Compare commits

..

24 Commits

Author SHA1 Message Date
fe80f029b5 Merge pull request 'Add error handling and improve comments' (#7) from fix-1.9.x into master
Reviewed-on: #7
2024-09-12 00:38:13 +00:00
15742e8f3f Add error handling and improve comments
Added fmt.Errorf for better error messages during HTTP and file handling in InstallTerraformVersion. Also fixed indentation in comments to follow Go conventions. Made other small improvements for better readability and maintainability.
2024-09-11 20:27:10 -04:00
5251c15271 Merge pull request 'feature/fix-mac-arm' (#6) from feature/fix-mac-arm into master
Reviewed-on: #6
2023-10-05 21:36:42 +00:00
2d3d1f881e Merge pull request 'master' (#5) from master into feature/fix-mac-arm
Reviewed-on: #5
2023-10-05 21:05:43 +00:00
cde1201da5 Merge pull request 'Fixed make install command' (#4) from fix-makefile into master
Reviewed-on: #4
2023-10-05 21:05:02 +00:00
6df08053c6 Fixed make install command 2023-10-05 17:02:10 -04:00
8690a026b1 added fix to use x86 in for arm mac's where arm package is unavailable 2023-10-03 19:26:18 -04:00
a7cb1f8aa1 Updated configuration manager to correctly retain stableonly value. 2023-02-04 07:40:33 -05:00
1bc2fddc09 Merge pull request 'Updated to new circle ci image' (#3) from update-ci-image into master
Reviewed-on: #3
2022-10-25 19:21:09 +00:00
4ffb4abbcc Updated to new circle ci image 2022-10-25 09:45:32 -04:00
b4c95f7ce3 Merge pull request 'fixed exit code' (#2) from test_update into master
Reviewed-on: #2
2022-10-25 13:03:06 +00:00
727001e838 fixed exit code 2022-10-25 09:02:00 -04:00
9001410471 Merge pull request 'Added tests for VersionInSlice' (#1) from test_update into master
Reviewed-on: #1
2022-10-24 12:44:36 +00:00
af2855a18c Added tests for VersionInSlice 2022-10-24 08:40:50 -04:00
c0bacf3d52 changed docker image back 2022-10-22 12:00:53 -04:00
b02e04e27b Reduced resource_class size
changed from golang to new go docker image
2022-10-22 11:56:34 -04:00
5cfcabad4b Added coverage report 2022-10-22 11:51:13 -04:00
4c99d54c51 Added circleci badge 2022-10-22 11:46:18 -04:00
55da2682ef Added circleci config 2022-10-22 11:34:55 -04:00
2282560e7a Added exit code 2022-10-21 09:39:06 -04:00
mitch
ce4d829184 Added comments 2022-06-18 09:41:00 -04:00
mitch
98493c8d52 Added support for default terraform version
Added default behavior for > and >= to not use non-stable release versions
Added configuration option of `StableOnly` which defaults to true.  Setting this to false will permit the use of -alpha and -beta releases
2022-06-15 22:52:19 -04:00
mitch
aed9e15f6e Added space to README.md 2022-06-07 08:56:23 -04:00
mitch
3dc30db64c Fixed tests 2022-04-27 19:20:10 -04:00
27 changed files with 1205 additions and 118 deletions

49
.circleci/config.yml Normal file
View File

@ -0,0 +1,49 @@
# Use the latest 2.1 version of CircleCI pipeline process engine.
# See: https://circleci.com/docs/2.0/configuration-reference
version: 2.1
# Define a job to be invoked later in a workflow.
# See: https://circleci.com/docs/2.0/configuration-reference/#jobs
jobs:
build:
resource_class: small
working_directory: ~/repo
# Specify the execution environment. You can specify an image from Dockerhub or use one of our Convenience Images from CircleCI's Developer Hub.
# See: https://circleci.com/docs/2.0/configuration-reference/#docker-machine-macos-windows-executor
docker:
- image: cimg/go:1.17
# Add steps to the job
# See: https://circleci.com/docs/2.0/configuration-reference/#steps
steps:
- checkout
- restore_cache:
keys:
- go-mod-v4-{{ checksum "go.sum" }}
- run:
name: Install Dependencies
command: go mod download
- save_cache:
key: go-mod-v4-{{ checksum "go.sum" }}
paths:
- "/go/pkg/mod"
- run:
name: Run tests
command: |
mkdir -p /tmp/test-reports
mkdir -p /tmp/artifacts
go test -coverprofile=c.out
go tool cover -html=c.out -o coverage.html
mv coverage.html /tmp/artifacts
gotestsum --junitfile /tmp/test-reports/unit-tests.xml
- store_test_results:
path: /tmp/test-reports
- store_artifacts:
path: /tmp/artifacts
# Invoke jobs via workflows
# See: https://circleci.com/docs/2.0/configuration-reference/#workflows
workflows:
build: # This is the name of the workflow, feel free to change it to better match your workflow.
# Inside the workflow, you define the jobs you want to run.
jobs:
- build

View File

@ -7,12 +7,12 @@ build:
# Clean # Clean
#################################################### ####################################################
clean: clean:
rm -f ~/.local/bin/versionedTerraform rm -f $(shell go env GOPATH)/bin/versionedTerraform
#################################################### ####################################################
# Install # Install
#################################################### ####################################################
install: install:
mv versionedTerraform ~/.local/bin/ mv versionedTerraform $(shell go env GOPATH)/bin/
#################################################### ####################################################
# help feature # help feature
#################################################### ####################################################
@ -20,8 +20,8 @@ help:
@echo '' @echo ''
@echo 'Usage: make [TARGET]' @echo 'Usage: make [TARGET]'
@echo 'Targets:' @echo 'Targets:'
@echo ' build go build -o versionedTerraform ./cmd' @echo ' build go build -o versionedTerraform ./cmd'
@echo ' clean removes installed versionedTerraform file' @echo ' clean removes installed versionedTerraform file'
@echo ' install installs versionedTerraform to local user bin folder' @echo ' install installs versionedTerraform to bin folder in GOPATH'
@echo ' all Nothing to do.' @echo ' all Nothing to do.'
@echo '' @echo ''

View File

@ -1,9 +1,11 @@
#Versioned Terraform [![<ORG_NAME>](https://circleci.com/gh/mitch-thompson/versionedTerraform.svg?style=svg)](<LINK>)
# Versioned Terraform
A wrapper for terraform to detect the expected version of terraform, A wrapper for terraform to detect the expected version of terraform,
download, and execute that version download, and execute that version
## Requirements ## Requirements
- go compiler (only tested on go1.17) - go
## Install ## Install
`make build install` for installation to local user<br> `make build install` for installation to local user<br>
@ -14,7 +16,12 @@ download, and execute that version
All arguments are passed through to terraform All arguments are passed through to terraform
``` ```
## sample usage ## Sample usage
`versionedTerraform version` will display the terraform version executed in a folder `versionedTerraform version` will display the terraform version executed in a folder
## Configuration
A configuration file is created in `~/.versionedTerraform`<br><br>
`StableOnly` boolean values: <b>true</b>/false<br>
This value is used to restrict terraform to release versions only defaults to true
## Known Issues ## Known Issues

View File

@ -16,6 +16,7 @@ const (
type SemVersion struct { type SemVersion struct {
version string version string
isStable bool
majorVersion int majorVersion int
minorVersion int minorVersion int
patchVersion int patchVersion int
@ -29,6 +30,7 @@ type SemVersionInterface interface {
func NewSemVersion(v string) *SemVersion { func NewSemVersion(v string) *SemVersion {
s := new(SemVersion) s := new(SemVersion)
s.isStable = true
s.version = removeSpacesVersion(v) s.version = removeSpacesVersion(v)
s.setMajorVersion() s.setMajorVersion()
@ -37,12 +39,14 @@ func NewSemVersion(v string) *SemVersion {
return s return s
} }
//setMajorVersion setter for SemVersion.majorVersion
func (s *SemVersion) setMajorVersion() { func (s *SemVersion) setMajorVersion() {
version := s.version version := s.version
majorVersionString := strings.Split(version, ".")[0] majorVersionString := strings.Split(version, ".")[0]
s.majorVersion, _ = strconv.Atoi(majorVersionString) s.majorVersion, _ = strconv.Atoi(majorVersionString)
} }
//setMinorVersion setter for SemVersion.minorVersion
func (s *SemVersion) setMinorVersion() { func (s *SemVersion) setMinorVersion() {
version := s.version version := s.version
minorVersionString := strings.Split(version, ".")[1] minorVersionString := strings.Split(version, ".")[1]
@ -50,21 +54,30 @@ func (s *SemVersion) setMinorVersion() {
} }
//setPatchVersion setter for SemVersion.patchVersion
func (s *SemVersion) setPatchVersion() { func (s *SemVersion) setPatchVersion() {
version := s.version version := s.version
var err error
patchStringSlice := strings.Split(version, ".") patchStringSlice := strings.Split(version, ".")
if len(patchStringSlice) < 3 { if len(patchStringSlice) < 3 {
s.patchVersion = 0 s.patchVersion = 0
return return
} }
s.patchVersion, _ = strconv.Atoi(patchStringSlice[2]) s.patchVersion, err = strconv.Atoi(patchStringSlice[2])
if err != nil {
s.isStable = false
patchStringSlice = strings.Split(patchStringSlice[2], "-")
s.patchVersion, _ = strconv.Atoi(patchStringSlice[0])
}
} }
//ToString returns string of SemVersion
func (s *SemVersion) ToString() string { func (s *SemVersion) ToString() string {
return s.version return s.version
} }
//VersionInSlice iterates through slices of SemVersion to check if version is in slice
//Used by main.go to determine if terraform version is currently installed
func (s *SemVersion) VersionInSlice(sSem []SemVersion) bool { func (s *SemVersion) VersionInSlice(sSem []SemVersion) bool {
for _, ver := range sSem { for _, ver := range sSem {
if ver.ToString() == s.ToString() { if ver.ToString() == s.ToString() {
@ -73,3 +86,50 @@ func (s *SemVersion) VersionInSlice(sSem []SemVersion) bool {
} }
return false return false
} }
func (s *SemVersion) IsEqualTo(s2 SemVersion) bool {
if s.majorVersion == s2.majorVersion && s.minorVersion == s2.minorVersion && s.patchVersion == s2.patchVersion {
return true
}
return false
}
func (s *SemVersion) IsLessThan(s2 SemVersion) bool {
if s2.majorVersion > s.majorVersion {
return true
}
if s2.minorVersion > s.minorVersion && s2.majorVersion == s.majorVersion {
return true
}
if s2.patchVersion > s.patchVersion && s2.majorVersion == s.majorVersion && s2.minorVersion == s.minorVersion {
return true
}
return false
}
func (s *SemVersion) IsGreaterThan(s2 SemVersion) bool {
if s2.majorVersion < s.majorVersion {
return true
}
if s2.minorVersion < s.minorVersion && s2.majorVersion == s.majorVersion {
return true
}
if s2.patchVersion < s.patchVersion && s2.majorVersion == s.majorVersion && s2.minorVersion == s.minorVersion {
return true
}
return false
}
func (s *SemVersion) IsLessOrEqual(s2 SemVersion) bool {
if s.IsLessThan(s2) || s.IsEqualTo(s2) {
return true
}
return false
}
func (s *SemVersion) IsGreaterOrEqual(s2 SemVersion) bool {
if s.IsGreaterThan(s2) || s.IsEqualTo(s2) {
return true
}
return false
}

773
SemVersion_test.go Normal file
View File

@ -0,0 +1,773 @@
package versionedTerraform
import "testing"
func TestSemVersion_VersionInSlice_success(t *testing.T) {
want := SemVersion{
version: "1.1.9",
isStable: true,
majorVersion: 1,
minorVersion: 1,
patchVersion: 9,
}
ver1 := SemVersion{
version: "1.1.10",
isStable: true,
majorVersion: 1,
minorVersion: 1,
patchVersion: 10,
}
ver2 := SemVersion{
version: "0.1.9",
isStable: true,
majorVersion: 0,
minorVersion: 1,
patchVersion: 9,
}
ver3 := SemVersion{
version: "1.0.9",
isStable: true,
majorVersion: 1,
minorVersion: 0,
patchVersion: 9,
}
var semArray []SemVersion
semArray = append(semArray, ver1)
semArray = append(semArray, ver2)
semArray = append(semArray, ver3)
semArray = append(semArray, want)
if !want.VersionInSlice(semArray) {
t.Errorf("Expected Sem Version to be found in semArray")
}
}
func TestSemVersion_VersionInSlice_fail(t *testing.T) {
want := SemVersion{
version: "1.1.9",
isStable: true,
majorVersion: 1,
minorVersion: 1,
patchVersion: 9,
}
ver1 := SemVersion{
version: "1.1.10",
isStable: true,
majorVersion: 1,
minorVersion: 1,
patchVersion: 10,
}
ver2 := SemVersion{
version: "0.1.9",
isStable: true,
majorVersion: 0,
minorVersion: 1,
patchVersion: 9,
}
ver3 := SemVersion{
version: "1.0.9",
isStable: true,
majorVersion: 1,
minorVersion: 0,
patchVersion: 9,
}
var semArray []SemVersion
semArray = append(semArray, ver1)
semArray = append(semArray, ver2)
semArray = append(semArray, ver3)
if want.VersionInSlice(semArray) {
t.Errorf("Expected Sem Version to not be found in semArray")
}
}
func TestSemVersion_IsLessThan(t *testing.T) {
cases := []struct {
name string
want bool
s1, s2 SemVersion
}{
{"IsEqualTo",
false,
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
},
{"MajorVersionLess",
true,
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "2.2.3",
isStable: false,
majorVersion: 2,
minorVersion: 2,
patchVersion: 3,
},
},
{"MinorVersionLess",
true,
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "1.3.3",
isStable: false,
majorVersion: 1,
minorVersion: 3,
patchVersion: 3,
},
},
{"PatchVersionLess",
true,
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 2,
minorVersion: 2,
patchVersion: 3,
},
},
{"MajorVersionGreater",
false,
SemVersion{
version: "2.2.3",
isStable: false,
majorVersion: 2,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "1.2.4",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 4,
},
},
{"MinorVersionGreater",
false,
SemVersion{
version: "1.3.3",
isStable: false,
majorVersion: 1,
minorVersion: 3,
patchVersion: 3,
},
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
},
{"PatchVersionGreater",
false,
SemVersion{
version: "1.2.4",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 4,
},
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
},
}
for _, c := range cases {
t.Run("Test: "+c.name, func(t *testing.T) {
t.Parallel()
got := c.s1.IsLessThan(c.s2)
if c.want != got {
t.Errorf("Expected %+v got %+v", c.want, got)
}
})
}
}
func TestSemVersion_IsGreaterThan(t *testing.T) {
cases := []struct {
name string
want bool
s1, s2 SemVersion
}{
{"IsEqualTo",
false,
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
},
{"MajorVersionLess",
false,
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "2.2.3",
isStable: false,
majorVersion: 2,
minorVersion: 2,
patchVersion: 3,
},
},
{"MinorVersionLess",
false,
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "1.3.3",
isStable: false,
majorVersion: 1,
minorVersion: 3,
patchVersion: 3,
},
},
{"PatchVersionLess",
false,
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 2,
minorVersion: 2,
patchVersion: 3,
},
},
{"MajorVersionGreater",
true,
SemVersion{
version: "2.2.3",
isStable: false,
majorVersion: 2,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "1.2.4",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 4,
},
},
{"MinorVersionGreater",
true,
SemVersion{
version: "1.3.3",
isStable: false,
majorVersion: 1,
minorVersion: 3,
patchVersion: 3,
},
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
},
{"PatchVersionGreater",
true,
SemVersion{
version: "1.2.4",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 4,
},
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
},
}
for _, c := range cases {
t.Run("Test: "+c.name, func(t *testing.T) {
t.Parallel()
got := c.s1.IsGreaterThan(c.s2)
if c.want != got {
t.Errorf("Expected %+v got %+v", c.want, got)
}
})
}
}
func TestSemVersion_IsEqualTo(t *testing.T) {
cases := []struct {
name string
want bool
s1, s2 SemVersion
}{
{"IsEqualTo",
true,
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
},
{"MajorVersionLess",
false,
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "2.2.3",
isStable: false,
majorVersion: 2,
minorVersion: 2,
patchVersion: 3,
},
},
{"MinorVersionLess",
false,
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "1.3.3",
isStable: false,
majorVersion: 1,
minorVersion: 3,
patchVersion: 3,
},
},
{"PatchVersionLess",
false,
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 2,
minorVersion: 2,
patchVersion: 3,
},
},
{"MajorVersionGreater",
false,
SemVersion{
version: "2.2.3",
isStable: false,
majorVersion: 2,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "1.2.4",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 4,
},
},
{"MinorVersionGreater",
false,
SemVersion{
version: "1.3.3",
isStable: false,
majorVersion: 1,
minorVersion: 3,
patchVersion: 3,
},
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
},
{"PatchVersionGreater",
false,
SemVersion{
version: "1.2.4",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 4,
},
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
},
}
for _, c := range cases {
t.Run("Test: "+c.name, func(t *testing.T) {
t.Parallel()
got := c.s1.IsEqualTo(c.s2)
if c.want != got {
t.Errorf("Expected %+v got %+v", c.want, got)
}
})
}
}
func TestSemVersion_IsLessOrEqual(t *testing.T) {
cases := []struct {
name string
want bool
s1, s2 SemVersion
}{
{"IsEqualTo",
true,
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
},
{"MajorVersionLess",
true,
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "2.2.3",
isStable: false,
majorVersion: 2,
minorVersion: 2,
patchVersion: 3,
},
},
{"MinorVersionLess",
true,
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "1.3.3",
isStable: false,
majorVersion: 1,
minorVersion: 3,
patchVersion: 3,
},
},
{"PatchVersionLess",
true,
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 2,
minorVersion: 2,
patchVersion: 3,
},
},
{"MajorVersionGreater",
false,
SemVersion{
version: "2.2.3",
isStable: false,
majorVersion: 2,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "1.2.4",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 4,
},
},
{"MinorVersionGreater",
false,
SemVersion{
version: "1.3.3",
isStable: false,
majorVersion: 1,
minorVersion: 3,
patchVersion: 3,
},
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
},
{"PatchVersionGreater",
false,
SemVersion{
version: "1.2.4",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 4,
},
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
},
}
for _, c := range cases {
t.Run("Test: "+c.name, func(t *testing.T) {
t.Parallel()
got := c.s1.IsLessOrEqual(c.s2)
if c.want != got {
t.Errorf("Expected %+v got %+v", c.want, got)
}
})
}
}
func TestSemVersion_IsGreaterOrEqual(t *testing.T) {
cases := []struct {
name string
want bool
s1, s2 SemVersion
}{
{"IsEqualTo",
true,
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
},
{"MajorVersionLess",
false,
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "2.2.3",
isStable: false,
majorVersion: 2,
minorVersion: 2,
patchVersion: 3,
},
},
{"MinorVersionLess",
false,
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "1.3.3",
isStable: false,
majorVersion: 1,
minorVersion: 3,
patchVersion: 3,
},
},
{"PatchVersionLess",
false,
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 2,
minorVersion: 2,
patchVersion: 3,
},
},
{"MajorVersionGreater",
true,
SemVersion{
version: "2.2.3",
isStable: false,
majorVersion: 2,
minorVersion: 2,
patchVersion: 3,
},
SemVersion{
version: "1.2.4",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 4,
},
},
{"MinorVersionGreater",
true,
SemVersion{
version: "1.3.3",
isStable: false,
majorVersion: 1,
minorVersion: 3,
patchVersion: 3,
},
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
},
{"PatchVersionGreater",
true,
SemVersion{
version: "1.2.4",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 4,
},
SemVersion{
version: "1.2.3",
isStable: false,
majorVersion: 1,
minorVersion: 2,
patchVersion: 3,
},
},
}
for _, c := range cases {
t.Run("Test: "+c.name, func(t *testing.T) {
t.Parallel()
got := c.s1.IsGreaterOrEqual(c.s2)
if c.want != got {
t.Errorf("Expected %+v got %+v", c.want, got)
}
})
}
}

View File

@ -4,4 +4,4 @@ terraform {
output "hello" { output "hello" {
value = "world" value = "world"
} }

View File

@ -15,20 +15,25 @@ const (
terraformPrefix = "/terraform_" terraformPrefix = "/terraform_"
) )
var needsStable = true
func main() { func main() {
homeDir, _ := os.UserHomeDir() homeDir, _ := os.UserHomeDir()
configDirString := homeDir + shortConfigDirString configDirString := homeDir + shortConfigDirString
// Create configuration directory if it does not exist
_, err := os.Stat(configDirString) _, err := os.Stat(configDirString)
if os.IsNotExist(err) { if os.IsNotExist(err) {
err = versionedTerraform.CreateConfig(configDirString, configFileLocation) err = versionedTerraform.CreateConfig(configDirString, configFileLocation)
} }
// Create configuration file if it does not exist
_, err = os.Stat(configDirString + "/" + configFileLocation) _, err = os.Stat(configDirString + "/" + configFileLocation)
if os.IsNotExist(err) { if os.IsNotExist(err) {
err = versionedTerraform.CreateConfig(configDirString, configFileLocation) err = versionedTerraform.CreateConfig(configDirString, configFileLocation)
} }
// Let the user know if we couldn't create the config directory or file
if err != nil { if err != nil {
fmt.Printf("Unable to create config directory: %v", err) fmt.Printf("Unable to create config directory: %v", err)
} }
@ -40,24 +45,28 @@ func main() {
flag.Parse() flag.Parse()
args := flag.Args() args := flag.Args()
//Load available versions from configuration file
versionsFromConfig, err = versionedTerraform.LoadVersionsFromConfig(configDir, configFileLocation) versionsFromConfig, err = versionedTerraform.LoadVersionsFromConfig(configDir, configFileLocation)
if err != nil { if err != nil {
fmt.Printf("Unable to read config: %v", err) fmt.Printf("Unable to read config: %v\n", err)
os.Exit(1) os.Exit(1)
} }
//Check if we need to update available versions with terraform's website
//Then update configuration if we do
//todo move this above loading the config
needsUpdate, err := versionedTerraform.NeedToUpdateAvailableVersions(configDir, configFileLocation) needsUpdate, err := versionedTerraform.NeedToUpdateAvailableVersions(configDir, configFileLocation)
if os.ErrNotExist == err { if os.ErrNotExist == err {
fmt.Println("Unable to update version: %v", err) fmt.Printf("Unable to update version: %v\n", err)
} }
fileHandle, _ := os.OpenFile(configDirString+"/"+configFileLocation, os.O_RDWR, 0666)
defer fileHandle.Close()
if needsUpdate { if needsUpdate {
fileHandle, _ := os.OpenFile(configDirString+"/"+configFileLocation, os.O_RDWR, 0666)
defer fileHandle.Close()
versionedTerraform.UpdateConfig(*fileHandle) versionedTerraform.UpdateConfig(*fileHandle)
} }
// Load a slice of versions which have already been installed
installedVersions, err := versionedTerraform.LoadInstalledVersions(configDir) installedVersions, err := versionedTerraform.LoadInstalledVersions(configDir)
if err != nil { if err != nil {
fmt.Printf("Unable to verify installed verisons: %v", err) fmt.Printf("Unable to verify installed verisons: %v", err)
@ -69,7 +78,14 @@ func main() {
vSlice = append(vSlice, v.ToString()) vSlice = append(vSlice, v.ToString())
} }
ver, err := versionedTerraform.GetVersionFromFile(workingDir, vSlice) // Check if stable version of terraform is required
needsStable, err = versionedTerraform.ConfigRequiresStable(*fileHandle)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to open config file, defaulting to stable versions of terraform only")
}
// Load version required from terraform directory
ver, err := versionedTerraform.GetVersionFromFile(workingDir, vSlice, needsStable)
if err != nil { if err != nil {
fmt.Printf("Unable to retrieve terraform version from files: %v", err) fmt.Printf("Unable to retrieve terraform version from files: %v", err)
} }
@ -82,6 +98,7 @@ func main() {
} }
} }
// Execute terraform
terraformFile := configDirString + terraformPrefix + ver.VersionToString() terraformFile := configDirString + terraformPrefix + ver.VersionToString()
argsForTerraform := append([]string{""}, args...) argsForTerraform := append([]string{""}, args...)
cmd := exec.Cmd{ cmd := exec.Cmd{
@ -93,5 +110,9 @@ func main() {
Stdout: os.Stdout, Stdout: os.Stdout,
Stderr: os.Stderr, Stderr: os.Stderr,
} }
cmd.Run() if err := cmd.Run(); err != nil {
if exitError, ok := err.(*exec.ExitError); ok {
os.Exit(exitError.ExitCode())
}
}
} }

View File

@ -12,10 +12,36 @@ import (
) )
type configStruct struct { type configStruct struct {
StableOnly bool
LastUpdate int64 LastUpdate int64
AvailableVersions []string AvailableVersions []string
} }
//ConfigRequiresStable returns bool, error only false if StableOnly: false is set in configuration file
func ConfigRequiresStable(File os.File) (bool, error) {
fileHandle, err := os.Open(File.Name())
if err != nil {
return true, err
}
defer fileHandle.Close()
fileScanner := bufio.NewScanner(fileHandle)
fileScanner.Split(bufio.ScanLines)
for fileScanner.Scan() {
_line := fileScanner.Text()
if strings.Contains(_line, "StableOnly: ") {
isStable := strings.SplitAfter(_line, "StableOnly: ")[1]
if strings.EqualFold(isStable, "false") {
return false, nil
}
}
}
return true, nil
}
//NeedToUpdateAvailableVersions returns bool, error checks if last update was older than 1 day ago
// this prevents us from spamming the list of available terraform versions page
func NeedToUpdateAvailableVersions(fileSystem fs.FS, availableVersions string) (bool, error) { func NeedToUpdateAvailableVersions(fileSystem fs.FS, availableVersions string) (bool, error) {
//todo this is used a lot abstract it? //todo this is used a lot abstract it?
fileHandle, err := fileSystem.Open(availableVersions) fileHandle, err := fileSystem.Open(availableVersions)
@ -45,6 +71,8 @@ func NeedToUpdateAvailableVersions(fileSystem fs.FS, availableVersions string) (
return false, nil return false, nil
} }
//LoadVersionsFromConfig returns slice of SemVersions and an error from AvailableVersions in configuration file
//This is stored from GetVersionList()
func LoadVersionsFromConfig(fileSystem fs.FS, configFile string) ([]SemVersion, error) { func LoadVersionsFromConfig(fileSystem fs.FS, configFile string) ([]SemVersion, error) {
fileHandle, err := fileSystem.Open(configFile) fileHandle, err := fileSystem.Open(configFile)
removeOpenBracket := regexp.MustCompile("\\[") removeOpenBracket := regexp.MustCompile("\\[")
@ -74,6 +102,7 @@ func LoadVersionsFromConfig(fileSystem fs.FS, configFile string) ([]SemVersion,
return nil, nil return nil, nil
} }
//LoadInstalledVersions returns list of SemVersions and an error from the directory listing of the .versionedTerraform
func LoadInstalledVersions(fileSystem fs.FS) ([]SemVersion, error) { func LoadInstalledVersions(fileSystem fs.FS) ([]SemVersion, error) {
dir, err := fs.ReadDir(fileSystem, ".") dir, err := fs.ReadDir(fileSystem, ".")
var installedTerraformVersions []SemVersion var installedTerraformVersions []SemVersion
@ -92,24 +121,39 @@ func LoadInstalledVersions(fileSystem fs.FS) ([]SemVersion, error) {
return installedTerraformVersions, nil return installedTerraformVersions, nil
} }
func UpdateConfig(File os.File) error { //UpdateConfig returns an error, and updates configuration file
// adding:
// a new date to the last updated field
// the available versions listed on terraforms website
// the status of if the user wants only stable releases
func UpdateConfig(File os.File, timeNow ...time.Time) error {
configValues := new(configStruct) configValues := new(configStruct)
configValues.AvailableVersions, _ = GetVersionList() configValues.AvailableVersions, _ = GetVersionList()
configValues.StableOnly, _ = ConfigRequiresStable(File)
timeNow := time.Now() var t time.Time
configValues.LastUpdate = timeNow.Unix() if len(timeNow) > 0 {
t = timeNow[0]
} else {
t = time.Now()
}
configValues.LastUpdate = t.Unix()
File.Truncate(0) File.Truncate(0)
File.Seek(0, 0) File.Seek(0, 0)
lineToByte := []byte(fmt.Sprintf("LastUpdate: %d\n", configValues.LastUpdate)) lineToByte := []byte(fmt.Sprintf("StableOnly: %+v\n", configValues.StableOnly))
File.Write(lineToByte)
lineToByte = []byte(fmt.Sprintf("LastUpdate: %d\n", configValues.LastUpdate))
File.Write(lineToByte) File.Write(lineToByte)
lineToByte = []byte(fmt.Sprintf("AvailableVersions: %+v\n", configValues.AvailableVersions)) lineToByte = []byte(fmt.Sprintf("AvailableVersions: %+v\n", configValues.AvailableVersions))
File.Write(lineToByte) File.Write(lineToByte)
return nil return nil
} }
//CreateConfig returns error, creates a new configuration file
func CreateConfig(directory string, configFile string) error { func CreateConfig(directory string, configFile string) error {
configFileName := directory + "/" + configFile configFileName := directory + "/" + configFile
err := os.MkdirAll(directory, 0755) err := os.MkdirAll(directory, 0755)
@ -120,6 +164,8 @@ func CreateConfig(directory string, configFile string) error {
fileHandler, err := os.Create(configFileName) fileHandler, err := os.Create(configFileName)
defer fileHandler.Close() defer fileHandler.Close()
lineToByte := []byte(fmt.Sprintf("StableOnly: true\n"))
fileHandler.Write(lineToByte)
err = UpdateConfig(*fileHandler) err = UpdateConfig(*fileHandler)
return err return err
} }

View File

@ -2,8 +2,11 @@ package versionedTerraform
import ( import (
"fmt" "fmt"
"io"
"os"
"reflect" "reflect"
"sort" "sort"
"strings"
"testing" "testing"
"testing/fstest" "testing/fstest"
"time" "time"
@ -23,26 +26,26 @@ func TestUpdateAvailableVersions(t *testing.T) {
} }
t.Run("Test success last update time", func(t *testing.T) { t.Run("Test success last update time", func(t *testing.T) {
want := true want := false
got, err := NeedToUpdateAvailableVersions(fs, "successConfig.conf") got, err := NeedToUpdateAvailableVersions(fs, "successConfig.conf")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if got != want { if got != want {
t.Errorf("updateAvailableVersions had incorrect output expected %v got %v", got, want) t.Errorf("updateAvailableVersions had incorrect output expected %v got %v", want, got)
} }
}) })
t.Run("Test failed last update time", func(t *testing.T) { t.Run("Test failed last update time", func(t *testing.T) {
want := false want := true
got, err := NeedToUpdateAvailableVersions(fs, "failConfig.conf") got, err := NeedToUpdateAvailableVersions(fs, "failConfig.conf")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if got != want { if got != want {
t.Errorf("updateAvailableVersions had incorrect output expected %v got %v", got, want) t.Errorf("updateAvailableVersions had incorrect output expected %v got %v", want, got)
} }
}) })
} }
@ -79,23 +82,27 @@ func TestInstalledVersions(t *testing.T) {
} }
fs := fstest.MapFS{ fs := fstest.MapFS{
"terraform_0.12.31": {Data: []byte("")}, "terraform_0.12.31": {Data: []byte("")},
"terraform_0.12.30": {Data: []byte("")}, "terraform_0.12.30": {Data: []byte("")},
"terraform_0.11.10": {Data: []byte("")}, "terraform_0.11.10": {Data: []byte("")},
"terraform_0.11.15": {Data: []byte("")}, "terraform_0.11.15": {Data: []byte("")},
"terraform_1.0.1": {Data: []byte("")}, "terraform_1.0.1": {Data: []byte("")},
"terraform_1.0.12": {Data: []byte("")}, "terraform_1.0.12": {Data: []byte("")},
"terraform_1.1.1": {Data: []byte("")}, "terraform_1.2.23-alpha": {Data: []byte("")},
"terraform_1.1.2": {Data: []byte("")}, "terraform_1.1.1": {Data: []byte("")},
"terraform_1.1.3": {Data: []byte("")}, "terraform_1.1.2": {Data: []byte("")},
"terraform_1.1.4": {Data: []byte("")}, "terraform_1.1.3": {Data: []byte("")},
"terraform_1.1.5": {Data: []byte("")}, "terraform_1.1.4": {Data: []byte("")},
"terraform_1.1.6": {Data: []byte("")}, "terraform_1.1.5": {Data: []byte("")},
"terraform_1.1.7": {Data: []byte("")}, "terraform_1.1.6": {Data: []byte("")},
"terraform_1.1.8": {Data: []byte("")}, "terraform_1.1.7": {Data: []byte("")},
"terraform_1.1.9": {Data: []byte("")}, "terraform_1.1.8": {Data: []byte("")},
"terraform_1.1.10": {Data: []byte("")}, "terraform_1.1.9": {Data: []byte("")},
"terraform_1.1.11": {Data: []byte("")}, "terraform_1.1.10": {Data: []byte("")},
"terraform_1.1.11": {Data: []byte("")},
"terraform_0.14.0": {Data: []byte("")},
"terraform_0.13.1": {Data: []byte("")},
"terraform_0.13.0": {Data: []byte("")},
} }
t.Run("Test installed versions", func(t *testing.T) { t.Run("Test installed versions", func(t *testing.T) {
@ -110,3 +117,73 @@ func TestInstalledVersions(t *testing.T) {
}) })
} }
func TestConfigRequiresStable(t *testing.T) {
availableVersions, _ := GetVersionList()
versions := strings.Join(availableVersions, " ")
cases := []struct {
name, content, want string
timeNow time.Time
}{
{"StableOnly True", "StableOnly: true\n" +
"LastUpdate: 1674481203\n" +
"AvailableVersions: [1.3.7]",
"StableOnly: true\n" +
"LastUpdate: 1286705410\n" +
"AvailableVersions: [" +
versions + "]\n",
time.Date(2010, 10, 10, 10, 10, 10, 10, time.UTC)},
{"StableOnly False", "StableOnly: false\n" +
"LastUpdate: 1674481203\n" +
"AvailableVersions: [1.3.7]",
"StableOnly: false\n" +
"LastUpdate: 1286705410\n" +
"AvailableVersions: [" +
versions + "]\n",
time.Date(2010, 10, 10, 10, 10, 10, 10, time.UTC)},
{"StableOnly not found", "LastUpdate: 1674481203\n" +
"AvailableVersions: [1.3.7]",
"StableOnly: true\n" +
"LastUpdate: 1286705410\n" +
"AvailableVersions: [" +
versions + "]\n",
time.Date(2010, 10, 10, 10, 10, 10, 10, time.UTC)},
}
for _, c := range cases {
t.Run("Test: "+c.name, func(t *testing.T) {
t.Parallel()
tempDir := os.TempDir()
tempFile, err := os.Create(tempDir + "/config")
defer tempFile.Close()
if err != nil {
t.Errorf("Unable to execute test : %v", err)
}
UpdateConfig(*tempFile, c.timeNow)
tempFile.Seek(0, 0)
data := make([]byte, 1024)
var got string
for {
n, err := tempFile.Read(data)
if err == io.EOF {
break
}
if err != nil {
t.Errorf("File reading error : %v", err)
return
}
got += string(data[:n])
}
if !reflect.DeepEqual(got, c.want) {
t.Errorf("%v test failed to meet conditions", c.name)
fmt.Fprintf(os.Stdout, "%v\n", c.want)
fmt.Fprintf(os.Stdout, "%v\n", got)
}
})
}
}

View File

@ -4,5 +4,7 @@
package versionedTerraform package versionedTerraform
const ( const (
fileSuffix = "_darwin_amd64.zip" fileSuffix = "_darwin_amd64.zip"
minVersion = "0.0.0"
alternateSuffix = ""
) )

View File

@ -4,5 +4,7 @@
package versionedTerraform package versionedTerraform
const ( const (
fileSuffix = "_darwin_arm64.zip" fileSuffix = "_darwin_arm64.zip"
minVersion = "1.0.2"
alternateSuffix = "_darwin_amd64.zip"
) )

View File

@ -7,9 +7,15 @@ import (
"strings" "strings"
) )
var needsStable bool
//GetVersionFromFile returns Version pointer and error
//Iterates through files in current directory and sending to parseVersionFromFile
//todo this should be (Version) GetVers... //todo this should be (Version) GetVers...
func GetVersionFromFile(fileSystem fs.FS, versionList []string) (*Version, error) { func GetVersionFromFile(fileSystem fs.FS, versionList []string, needsStableValue bool) (*Version, error) {
needsStable = needsStableValue
var versionFinal Version var versionFinal Version
versionFinal = *NewVersion(">= 0.0.0", versionList)
dir, err := fs.ReadDir(fileSystem, ".") dir, err := fs.ReadDir(fileSystem, ".")
if err != nil { if err != nil {
return &versionFinal, err return &versionFinal, err
@ -29,6 +35,9 @@ func GetVersionFromFile(fileSystem fs.FS, versionList []string) (*Version, error
} }
//todo same here //todo same here
//parseVersionFromFile returns Version pointer, bool, and error
//bool returns true if required_version is found
//if required_version is found sends to NewVersion to determine required version for the application
func parseVersionFromFile(f fs.FS, fileName string, versionList []string) (*Version, bool, error) { func parseVersionFromFile(f fs.FS, fileName string, versionList []string) (*Version, bool, error) {
fileHandle, err := f.Open(fileName) fileHandle, err := f.Open(fileName)
regex := regexp.MustCompile("required_version\\s+?=") regex := regexp.MustCompile("required_version\\s+?=")

View File

@ -5,9 +5,8 @@ import (
"testing/fstest" "testing/fstest"
) )
func TestFileHandler(t *testing.T) { const (
const ( firstFile = `
firstFile = `
resource "aws_mq_broker" "sample" { resource "aws_mq_broker" "sample" {
depends_on = [aws_security_group.mq] depends_on = [aws_security_group.mq]
broker_name = var.name broker_name = var.name
@ -21,13 +20,14 @@ resource "aws_mq_broker" "sample" {
subnet_ids = ["10.0.0.0/24", "10.0.1.0/24"] subnet_ids = ["10.0.0.0/24", "10.0.1.0/24"]
} }
` `
secondFile = ` secondFile = `
terraform { terraform {
required_version = "~> 0.12.4" required_version = "~> 0.12.4"
} }
` `
) )
func TestFileHandler(t *testing.T) {
want := NewVersion("0.12.31", testVersionList()) want := NewVersion("0.12.31", testVersionList())
fs := fstest.MapFS{ fs := fstest.MapFS{
@ -35,7 +35,25 @@ terraform {
"versions.tf": {Data: []byte(secondFile)}, "versions.tf": {Data: []byte(secondFile)},
} }
version, err := GetVersionFromFile(fs, testVersionList()) version, err := GetVersionFromFile(fs, testVersionList(), true)
if err != nil {
t.Fatal(err)
}
got := *version
if got.Version != want.Version {
t.Errorf("Expected %v, got %v", want.Version, got.Version)
}
}
func TestEmptyTerraformVersion(t *testing.T) {
want := NewVersion("1.1.11", testVersionList())
fs := fstest.MapFS{"main.tf": {Data: []byte(firstFile)}}
version, err := GetVersionFromFile(fs, testVersionList(), true)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)

View File

@ -4,5 +4,7 @@
package versionedTerraform package versionedTerraform
const ( const (
fileSuffix = "_freebsd_386.zip" fileSuffix = "_freebsd_386.zip"
minVersion = "0.0.0"
alternateSuffix = ""
) )

View File

@ -4,5 +4,7 @@
package versionedTerraform package versionedTerraform
const ( const (
fileSuffix = "_freebsd_amd64.zip" fileSuffix = "_freebsd_amd64.zip"
minVersion = "0.0.0"
alternateSuffix = ""
) )

View File

@ -4,5 +4,7 @@
package versionedTerraform package versionedTerraform
const ( const (
fileSuffix = "_freebsd_arm.zip" fileSuffix = "_freebsd_arm.zip"
minVersion = "0.0.0"
alternateSuffix = ""
) )

View File

@ -4,5 +4,7 @@
package versionedTerraform package versionedTerraform
const ( const (
fileSuffix = "_linux_386.zip" fileSuffix = "_linux_386.zip"
minVersion = "0.0.0"
alternateSuffix = ""
) )

View File

@ -4,5 +4,7 @@
package versionedTerraform package versionedTerraform
const ( const (
fileSuffix = "_linux_amd64.zip" fileSuffix = "_linux_amd64.zip"
minVersion = "0.0.0"
alternateSuffix = ""
) )

View File

@ -4,5 +4,7 @@
package versionedTerraform package versionedTerraform
const ( const (
fileSuffix = "_linux_arm.zip" fileSuffix = "_linux_arm.zip"
minVersion = "0.0.0"
alternateSuffix = ""
) )

View File

@ -4,5 +4,7 @@
package versionedTerraform package versionedTerraform
const ( const (
fileSuffix = "_linux_arm64.zip" fileSuffix = "_linux_arm64.zip"
minVersion = "0.0.0"
alternateSuffix = ""
) )

View File

@ -4,5 +4,7 @@
package versionedTerraform package versionedTerraform
const ( const (
fileSuffix = "_openbsd_386.zip" fileSuffix = "_openbsd_386.zip"
minVersion = "0.0.0"
alternateSuffix = ""
) )

View File

@ -4,5 +4,7 @@
package versionedTerraform package versionedTerraform
const ( const (
fileSuffix = "_openbsd_amd64.zip" fileSuffix = "_openbsd_amd64.zip"
minVersion = "0.0.0"
alternateSuffix = ""
) )

View File

@ -4,5 +4,7 @@
package versionedTerraform package versionedTerraform
const ( const (
fileSuffix = "_solaris_amd64.zip" fileSuffix = "_solaris_amd64.zip"
minVersion = "0.0.0"
alternateSuffix = ""
) )

View File

@ -5,6 +5,7 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"errors" "errors"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
@ -13,15 +14,6 @@ import (
"strings" "strings"
) )
func (*Version) latestMajorVersion() {
}
func (*Version) latestMinorVersion() {
}
func (*Version) latestPatchVersion() {
}
type Version struct { type Version struct {
Version SemVersion Version SemVersion
availableVersions []SemVersion availableVersions []SemVersion
@ -34,19 +26,19 @@ const (
versionedTerraformFolder = "/.versionedTerraform" versionedTerraformFolder = "/.versionedTerraform"
) )
//getLatestMajorRelease() returns the latest major release from Version // getLatestMajorRelease() returns the latest major release from Version
func (v *Version) getLatestMajorRelease() { func (v *Version) getLatestMajorRelease() {
//todo clean up
for _, release := range v.availableVersions { for _, release := range v.availableVersions {
if release.majorVersion == v.Version.majorVersion && if release.majorVersion == v.Version.majorVersion &&
release.minorVersion == v.Version.minorVersion && release.minorVersion == v.Version.minorVersion &&
release.patchVersion >= v.Version.patchVersion { release.patchVersion >= v.Version.patchVersion &&
(v.Version.isStable || !needsStable) {
v.Version = release v.Version = release
} }
} }
} }
//getGreatestRelease() returns less than release // getGreatestRelease() returns less than release
func (v *Version) getOneLessRelease() { func (v *Version) getOneLessRelease() {
var vSlice []Version var vSlice []Version
@ -69,83 +61,89 @@ func (v *Version) getOneLessRelease() {
} }
} }
//getLatestRelease returns the latest release from Version // getLatestRelease returns the latest release from Version
func (v *Version) getLatestRelease() { func (v *Version) getLatestRelease() {
//todo clean up
for _, release := range v.availableVersions { for _, release := range v.availableVersions {
if release.majorVersion > v.Version.majorVersion { if release.majorVersion > v.Version.majorVersion &&
(release.isStable || !needsStable) {
v.Version = release v.Version = release
} }
if release.majorVersion >= v.Version.majorVersion && if release.majorVersion >= v.Version.majorVersion &&
release.minorVersion > v.Version.minorVersion { release.minorVersion > v.Version.minorVersion &&
(release.isStable || !needsStable) {
v.Version = release v.Version = release
} }
if release.majorVersion >= v.Version.majorVersion && if release.majorVersion >= v.Version.majorVersion &&
release.minorVersion >= v.Version.minorVersion && release.minorVersion >= v.Version.minorVersion &&
release.patchVersion >= v.Version.patchVersion { release.patchVersion >= v.Version.patchVersion &&
(release.isStable || !needsStable) {
v.Version = release v.Version = release
} }
} }
} }
//InstallTerraformVersion installs the defined terraform Version in the application // InstallTerraformVersion installs the defined terraform Version in the application
//configuration directory // configuration directory
func (v *Version) InstallTerraformVersion() error { func (v *Version) InstallTerraformVersion() error {
homeDir, _ := os.UserHomeDir() homeDir, _ := os.UserHomeDir()
resp, err := http.Get(hashicorpUrl + suffix := fileSuffix
minV := NewSemVersion(minVersion)
if v.Version.IsLessThan(*minV) {
suffix = alternateSuffix
}
url := hashicorpUrl +
v.Version.ToString() + v.Version.ToString() +
"/" + terraformPrefix + "/" + terraformPrefix +
v.Version.ToString() + v.Version.ToString() +
fileSuffix) suffix
resp, err := http.Get(url)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to download Terraform: %v", err)
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body) body, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to read response body: %v", err)
} }
zipReader, err := zip.NewReader(bytes.NewReader(body), int64(len(body))) zipReader, err := zip.NewReader(bytes.NewReader(body), int64(len(body)))
if err != nil { if err != nil {
return err return fmt.Errorf("failed to create zip reader: %v", err)
} }
versionedFileName := homeDir + versionedTerraformFolder + "/" + terraformPrefix + v.Version.ToString() versionedFileName := homeDir + versionedTerraformFolder + "/" + terraformPrefix + v.Version.ToString()
versionedFile, err := os.OpenFile(versionedFileName, os.O_WRONLY, 0755) versionedFile, err := os.OpenFile(versionedFileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
if os.IsNotExist(err) {
//_, err = os.Create(versionedFileName)
//if err != nil {
// return err
//}
versionedFile, err = os.OpenFile(versionedFileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
if err != nil {
return err
}
}
if err != nil { if err != nil {
return err return fmt.Errorf("failed to create output file: %v", err)
} }
defer versionedFile.Close() defer versionedFile.Close()
for _, zipFIle := range zipReader.File { for _, zipFile := range zipReader.File {
zr, err := zipFIle.Open() if zipFile.Name != "terraform" {
if err != nil { continue
return err
} }
unzippedFileBytes, _ := ioutil.ReadAll(zr)
_, err = versionedFile.Write(unzippedFileBytes) zr, err := zipFile.Open()
if err != nil { if err != nil {
return err return fmt.Errorf("failed to open zip file: %v", err)
} }
zr.Close() defer zr.Close()
_, err = io.Copy(versionedFile, zr)
if err != nil {
return fmt.Errorf("failed to write terraform binary: %v", err)
}
break
} }
return nil return nil
} }
//NewVersion creates a new Version using sem versioning for determining the // NewVersion creates a new Version using sem versioning for determining the
//latest release // latest release
func NewVersion(_version string, _vList []string) *Version { func NewVersion(_version string, _vList []string) *Version {
v := new(Version) v := new(Version)
v.Version = *NewSemVersion(_version) v.Version = *NewSemVersion(_version)
@ -208,7 +206,7 @@ func NewVersion(_version string, _vList []string) *Version {
return v return v
} }
//GetVersionList returns a list of available versions from hashicorp's release page // GetVersionList returns a list of available versions from hashicorp's release page
func GetVersionList() ([]string, error) { func GetVersionList() ([]string, error) {
var versionList []string var versionList []string
resp, err := http.Get(hashicorpUrl) resp, err := http.Get(hashicorpUrl)
@ -240,7 +238,7 @@ func GetVersionList() ([]string, error) {
return versionList, nil return versionList, nil
} }
//removeSpacesVersion removes spaces from Version string for parsing // removeSpacesVersion removes spaces from Version string for parsing
func removeSpacesVersion(v string) string { func removeSpacesVersion(v string) string {
splitV := strings.Split(v, " ") splitV := strings.Split(v, " ")
var returnString string var returnString string
@ -251,12 +249,12 @@ func removeSpacesVersion(v string) string {
return strings.TrimSpace(returnString) return strings.TrimSpace(returnString)
} }
//VersionToString returns string of a Version // VersionToString returns string of a Version
func (v *Version) VersionToString() string { func (v *Version) VersionToString() string {
return v.Version.ToString() return v.Version.ToString()
} }
//versionCompare returns true if v1 is greater than v2 // isVersionGreater returns true if v1 is greater than v2
func isVersionGreater(v1 Version, v2 Version) bool { func isVersionGreater(v1 Version, v2 Version) bool {
if v1.Version.majorVersion != v2.Version.majorVersion { if v1.Version.majorVersion != v2.Version.majorVersion {
if v1.Version.majorVersion > v2.Version.majorVersion { if v1.Version.majorVersion > v2.Version.majorVersion {

View File

@ -6,6 +6,7 @@ import (
func testVersionList() []string { func testVersionList() []string {
return []string{ return []string{
"1.2.23-alpha",
"1.1.11", "1.1.11",
"1.1.10", "1.1.10",
"1.1.9", "1.1.9",
@ -137,7 +138,7 @@ func TestIsVersionGreater(t *testing.T) {
func TestGetVersionList(t *testing.T) { func TestGetVersionList(t *testing.T) {
//todo write test for this //todo write test for this
//response, _ := getVersionList() //response, _ := GetVersionList()
//for _, Version := range response { //for _, Version := range response {
// t.Errorf("%v", Version) // t.Errorf("%v", Version)
//} //}

View File

@ -4,5 +4,7 @@
package versionedTerraform package versionedTerraform
const ( const (
fileSuffix = "_windows_386.zip" fileSuffix = "_windows_386.zip"
minVersion = "0.0.0"
alternateSuffix = ""
) )

View File

@ -4,5 +4,7 @@
package versionedTerraform package versionedTerraform
const ( const (
fileSuffix = "_windows_amd64.zip" fileSuffix = "_windows_amd64.zip"
minVersion = "0.0.0"
alternateSuffix = ""
) )