diff --git a/Makefile b/Makefile index f20bd8c..635f6c8 100644 --- a/Makefile +++ b/Makefile @@ -62,7 +62,7 @@ staticcheck: bin/$(OS)/staticcheck @$(GOBIN)/staticcheck github.com/epam/hubctl/... .PHONY: staticcheck -test: +test: deps go test -race -timeout 60s ./cmd/hub/... .PHONY: test diff --git a/cmd/hub/lifecycle/requirement.go b/cmd/hub/lifecycle/requirement.go index b3e3fe0..a343220 100644 --- a/cmd/hub/lifecycle/requirement.go +++ b/cmd/hub/lifecycle/requirement.go @@ -11,7 +11,6 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" "log" "os" "os/exec" @@ -28,7 +27,7 @@ import ( const ( providedByEnv = "*environment*" - gcpServiceAccountsHelp = "https://cloud.google.com/docs/authentication/getting-started" + gcpServiceAccountsHelp = "https://cloud.google.com/docs/authentication/provide-credentials-adc" azureGoSdkAuthHelp = "https://docs.microsoft.com/en-us/go/azure/azure-sdk-go-authorization" ) @@ -161,14 +160,15 @@ func setupRequirement(requirement string, provider string, } var bins = map[string][]string{ - "aws": {"aws", "s3", "ls", "--page-size", "5"}, + "aws": {"aws", "--version"}, "azure": {"az", "version"}, "gcp": {"gcloud", "version"}, "gcs": {"gsutil", "version"}, - "kubectl": {"kubectl", "version", "--client"}, - "kubernetes": {"kubectl", "version", "--client"}, - "helm": {"helm", "version", "--client"}, - "etcd": {"etcdctl", "--version"}, + "kubectl": {"kubectl", "version", "--client", "--output=json"}, + "kubernetes": {"kubectl", "version", "--client", "--output=json"}, + "vault": {"vault", "version"}, + "helm": {"helm", "version"}, + "terraform": {"terraform", "version"}, } type BinVersion struct { @@ -185,12 +185,14 @@ func semver(s string) *version.Version { } var binVersion = map[string]*BinVersion{ - "gcloud": {semver("246.0.0"), regexp.MustCompile(`Google Cloud SDK ([\d.]+)`)}, - "gsutil": {semver("4.38"), regexp.MustCompile(`version: ([\d.]+)`)}, - "vault": {semver("1.3.2"), regexp.MustCompile(`Vault v([\d.]+)`)}, - "kubectl": {semver("1.18.15"), regexp.MustCompile(`GitVersion:"v([\d.]+)`)}, - "helm": {semver("3.5.1"), regexp.MustCompile(`(?:SemVer|Version):"v([\d.]+)`)}, - "terraform": {semver("0.14.0"), regexp.MustCompile(`Terraform v([\d.]+)`)}, + "aws": {semver("2.10.0"), regexp.MustCompile(`aws-cli/([\d.]+)`)}, + "az": {semver("2.40.0"), regexp.MustCompile(`"azure-cli": "([\d.]+)"`)}, + "gcloud": {semver("400.0.0"), regexp.MustCompile(`Google Cloud SDK ([\d.]+)`)}, + "gsutil": {semver("5.0"), regexp.MustCompile(`version: ([\d.]+)`)}, + "vault": {semver("1.10"), regexp.MustCompile(`Vault v([\d.]+)`)}, + "kubectl": {semver("1.19"), regexp.MustCompile(`"gitVersion": "v([\d.]+)"`)}, + "helm": {semver("3.11"), regexp.MustCompile(`(?:SemVer|Version):"v([\d.]+)"`)}, + "terraform": {semver("1.0"), regexp.MustCompile(`Terraform v([\d.]+)`)}, } func checkStackRequires(requires []string, optional, requiresOfOptionalComponents map[string][]string) map[string][]string { @@ -290,7 +292,7 @@ func checkRequiresBin(bin ...string) ([]byte, error) { // validates binary version against minimum required version // // reqVer: required version and regexp to extract version string -// currVer: raw output from binary +// out: raw output from binary // // returns error version is not valid func checkRequiresBinVersion(reqVer *BinVersion, out []byte) error { @@ -395,7 +397,7 @@ func checkRequiresGcp() error { return err } - jsonData, err := ioutil.ReadFile(credsFile) + jsonData, err := os.ReadFile(credsFile) if err != nil { return fmt.Errorf("Unable to read `%s`: %v", credsFile, err) } diff --git a/cmd/hub/lifecycle/requirement_test.go b/cmd/hub/lifecycle/requirement_test.go index 980766f..eebe7e3 100644 --- a/cmd/hub/lifecycle/requirement_test.go +++ b/cmd/hub/lifecycle/requirement_test.go @@ -4,35 +4,89 @@ import ( "fmt" "testing" - "github.com/hashicorp/go-version" "github.com/stretchr/testify/assert" ) -var helmVerTpl = "version.BuildInfo{Version:\"%s\", GitCommit:\"50f003e5ee8704ec937a756c646870227d7c8b58\", GitTreeState:\"clean\", GoVersion:\"go1.18.8\"}" +var outputTemplates = map[string]string{ + "aws": "aws-cli/%s Python/3.11.5 Linux/5.15.90.1 source/x86_64.ubuntu.22 prompt/off", + "az": `{ + "azure-cli": "%s", + "azure-cli-core": "2.52.0", + "azure-cli-telemetry": "1.1.0", + "extensions": {} + }`, + "gcloud": `Google Cloud SDK %s +alpha 2023.09.13 +beta 2023.09.13 +bq 2.0.98 +bundled-python3-unix 3.9.16 +core 2023.09.13 +gcloud-crc32c 1.0.0 +gsutil 5.25`, + "gsutil": "version: %s", + "vault": "Vault v%s ('56debfa71653e72433345f23cd26276bc90629ce+CHANGES'), built 2023-09-11T21:23:55Z", + "kubectl": `{ + "clientVersion": { + "major": "1", + "minor": "28", + "gitVersion": "v%s", + "gitCommit": "8dc49c4b984b897d423aab4971090e1879eb4f23", + "gitTreeState": "clean", + "buildDate": "2023-08-24T11:16:29Z", + "goVersion": "go1.20.7", + "compiler": "gc", + "platform": "linux/amd64" + }, + "kustomizeVersion": "v5.0.4-0.20230601165947-6ce0bf390ce3" + }`, + "helm": "version.BuildInfo{Version:\"v%s\", GitCommit:\"3a31588ad33fe3b89af5a2a54ee1d25bfe6eaa5e\", GitTreeState:\"clean\", GoVersion:\"go1.20.7\"}", + "terraform": "Terraform v%s\non linux_amd64", +} + +func formatVersion(binary, version string) []byte { + return []byte(fmt.Sprintf(outputTemplates[binary], version)) +} + +func testRequiredbinaryVersion(binary, equalVersion, newerVersion, olderVersion, startFromTen string, t *testing.T) { + reqVer := binVersion[binary] + err := checkRequiresBinVersion(reqVer, formatVersion(binary, equalVersion)) + assert.NoError(t, err, "When version is equal with minimal required, checkRequiresBinVersion should not return validation error") + err = checkRequiresBinVersion(reqVer, formatVersion(binary, newerVersion)) + assert.NoError(t, err, "When version is greater than minimal required, checkRequiresBinVersion should not return validation error") + err = checkRequiresBinVersion(reqVer, formatVersion(binary, olderVersion)) + assert.Error(t, err, "When version is less than minimal required, checkRequiresBinVersion should return validation error") + err = checkRequiresBinVersion(reqVer, formatVersion(binary, startFromTen)) + assert.NoError(t, err, "When version number starts with 1 but actually is 10, checkRequiresBinVersion should not return validation error") +} + +func TestCheckAwsVersion(t *testing.T) { + testRequiredbinaryVersion("aws", "2.10", "2.13.19", "2.0.2", "10.1.1", t) +} -func formatHelmVersion(version string) []byte { - return []byte(fmt.Sprintf(helmVerTpl, version)) +func TestCheckAzureVersion(t *testing.T) { + testRequiredbinaryVersion("az", "2.40", "2.52.0", "2.0.2", "10.1.1", t) } -func TestCheckRequiresBinVersion(t *testing.T) { - min, _ := version.NewVersion("3.5.2") - helm := binVersion["helm"] - helm.minVersion = min +func TestCheckGcloudVersion(t *testing.T) { + testRequiredbinaryVersion("gcloud", "400.0.0", "446.0.1", "140.0.2", "1010.1.1", t) +} - raw_data := formatHelmVersion(min.String()) - err := checkRequiresBinVersion(helm, raw_data) - assert.Error(t, err, "When versions are equal, checkRequiresBinVersion should not return validation error") +func TestCheckGsutilVersion(t *testing.T) { + testRequiredbinaryVersion("gsutil", "5.0", "5.25", "3.52", "10.1.1", t) +} - raw_data = formatHelmVersion("v0.0.1") - err = checkRequiresBinVersion(helm, raw_data) - assert.Error(t, err, "When version is less than required, checkRequiresBinVersion should return validation error") +func TestCheckVaultVersion(t *testing.T) { + testRequiredbinaryVersion("vault", "1.10.0", "1.14.3", "1.9.10", "10.1.1", t) +} - raw_data = formatHelmVersion("v100.0.0") - err = checkRequiresBinVersion(helm, raw_data) - assert.NoError(t, err, "When version is greater than required, checkRequiresBinVersion should not return validation error") +func TestCheckKubectlVersion(t *testing.T) { + testRequiredbinaryVersion("kubectl", "1.19", "1.28.1", "1.18.15", "10.1.1", t) +} - raw_data = formatHelmVersion("v3.10.2") - err = checkRequiresBinVersion(helm, raw_data) - assert.NoError(t, err, "When version number starts with 1 but actually is 10 there should be no error") +func TestCheckHelmVersion(t *testing.T) { + testRequiredbinaryVersion("helm", "3.11", "3.12.3", "3.5.1", "10.1.1", t) +} +func TestCheckTerraformVersion(t *testing.T) { + testRequiredbinaryVersion("terraform", "1.0", "1.5.7", "0.14.1", "10.1.1", t) }