diff --git a/.github/workflows/ci.lint_test.yml b/.github/workflows/ci.lint_test.yml index b650e7f..15d5edf 100644 --- a/.github/workflows/ci.lint_test.yml +++ b/.github/workflows/ci.lint_test.yml @@ -19,12 +19,13 @@ jobs: fetch-depth: 0 - name: Lint Code Base - uses: docker://ghcr.io/github/super-linter:slim-v4 + uses: docker://ghcr.io/github/super-linter:v4 env: VALIDATE_ALL_CODEBASE: true DEFAULT_BRANCH: master GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} VALIDATE_GO: true + FILTER_REGEX_EXCLUDE: ".*test/.*" Test: name: "CI: Go Tests" diff --git a/README.md b/README.md index 3af6edc..ae8c3f9 100644 --- a/README.md +++ b/README.md @@ -29,10 +29,19 @@ ## Install +### Source + ```sh go install github.com/riweston/aztx ``` +### brew + +```sh +brew tap riweston/aztx +brew install aztx +``` + ## Usage ```sh diff --git a/cmd/aztx/contexts.go b/cmd/aztx/contexts.go index 713c819..7810347 100644 --- a/cmd/aztx/contexts.go +++ b/cmd/aztx/contexts.go @@ -17,74 +17,93 @@ limitations under the License. package aztx import ( + "bytes" "encoding/json" "fmt" - "os/exec" + "io/ioutil" + "os" + "github.com/google/uuid" "github.com/ktr0731/go-fuzzyfinder" ) -type Account struct { - CloudName string `json:"cloudName"` - HomeTenantID string `json:"homeTenantId"` - ID string `json:"id"` - IsDefault bool `json:"isDefault"` - ManagedByTenants []string `json:"managedByTenants"` - Name string `json:"name"` - State string `json:"state"` - TenantID string `json:"tenantId"` +type Subscription struct { + EnvironmentName string `json:"environmentName"` + HomeTenantID uuid.UUID `json:"homeTenantId"` + ID uuid.UUID `json:"id"` + IsDefault bool `json:"isDefault"` + ManagedByTenants []string `json:"managedByTenants"` + Name string `json:"name"` + State string `json:"state"` + TenantID uuid.UUID `json:"tenantId"` User struct { Name string `json:"name"` AccountType string `json:"type"` } `json:"user"` } -func GetAzureAccounts() []byte { - binary, errLook := exec.LookPath("az") - if errLook != nil { - panic(errLook) - } +type File struct { + InstallationID uuid.UUID `json:"installationId"` + Subscriptions []Subscription `json:"subscriptions"` +} - args := []string{"account", "list", "-o", "json"} +func SelectAzureAccountsDisplayName() { + home, errHome := os.UserHomeDir() + if errHome != nil { + panic(errHome) + } + azureProfile := home + "/.azure/azureProfile.json" + d := ReadAzureProfile(azureProfile) - out, err := exec.Command(binary, args...).CombinedOutput() - // TODO: This currently breaks when selecting context - if err != nil { - panic(err) + idx, errFind := fuzzyfinder.Find( + d.Subscriptions, + func(i int) string { + return d.Subscriptions[i].Name + }) + if errFind != nil { + panic(errFind) } - return out + errWrite := WriteAzureProfile(d, d.Subscriptions[idx].ID, azureProfile) + if errWrite != nil { + panic(errWrite) + } + fmt.Print(d.Subscriptions[idx].Name, "\n", d.Subscriptions[idx].ID, "\n") } -func SetAzureAccountContext(accountname string) { - binary, errLook := exec.LookPath("az") - if errLook != nil { - panic(errLook) +func ReadAzureProfile(file string) File { + jsonFile, err := os.Open(file) + if err != nil { + fmt.Println(err) + } + byteValue, errByte := ioutil.ReadAll(jsonFile) + if errByte != nil { + fmt.Println(errByte) + } + byteValue = bytes.TrimPrefix(byteValue, []byte("\xef\xbb\xbf")) + var jsonData File + errJSON := json.Unmarshal(byteValue, &jsonData) + if errJSON != nil { + fmt.Println(err) } - args := []string{"account", "set", "--subscription", accountname} + return jsonData +} - _, err := exec.Command(binary, args...).Output() - if err != nil { - panic(err.Error()) +func WriteAzureProfile(file File, id uuid.UUID, outFile string) error { + for idx := range file.Subscriptions { + if file.Subscriptions[idx].ID == id { + file.Subscriptions[idx].IsDefault = true + } else { + file.Subscriptions[idx].IsDefault = false + } } -} -func SelectAzureAccountsDisplayName() { - d := GetAzureAccounts() - var Accounts []Account - err := json.Unmarshal(d, &Accounts) + byteValue, err := json.Marshal(&file) if err != nil { - panic(err) - } - idx, errFind := fuzzyfinder.Find( - Accounts, - func(i int) string { - return Accounts[i].Name - }) - if errFind != nil { - panic(errFind) + return err } - SetAzureAccountContext(Accounts[idx].Name) - fmt.Print(Accounts[idx].Name, "\n", Accounts[idx].ID, "\n") + + err = ioutil.WriteFile(outFile, byteValue, 0600) + return err } diff --git a/go.mod b/go.mod index 128462f..4a6edbd 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,8 @@ module github.com/riweston/aztx go 1.16 -require github.com/ktr0731/go-fuzzyfinder v0.4.0 +require ( + github.com/BurntSushi/toml v0.3.1 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/ktr0731/go-fuzzyfinder v0.4.0 +) diff --git a/go.sum b/go.sum index 05b35e8..acfbee8 100644 --- a/go.sum +++ b/go.sum @@ -82,6 +82,7 @@ github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYX github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= @@ -372,6 +373,8 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/wire v0.5.0/go.mod h1:ngWDr9Qvq3yZA10YrxfyGELY/AFWGVpy9c1LTRi1EoU= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= diff --git a/test/aztx_function_test.go b/test/aztx_function_test.go new file mode 100644 index 0000000..873e3e1 --- /dev/null +++ b/test/aztx_function_test.go @@ -0,0 +1,29 @@ +// +build !integration + +package aztx_test + +import ( + "testing" + + "github.com/google/uuid" + "github.com/riweston/aztx/cmd/aztx" +) + +func TestReadFile(t *testing.T) { + value := aztx.ReadAzureProfile("azureProfile.json") + equals(t, "Test Subscription 1", value.Subscriptions[0].Name) +} + +func TestWriteFile(t *testing.T) { + inputFile := aztx.ReadAzureProfile("azureProfile.json") + uuid, err := uuid.Parse("9e7969ef-4cb8-4a2d-959f-bfdaae452a3d") + if err != nil { + panic(err) + } + outFile := aztx.WriteAzureProfile(inputFile, uuid, "azureProfile_test.json") + ok(t, outFile) + value := aztx.ReadAzureProfile("azureProfile_test.json") + equals(t, true, value.Subscriptions[0].IsDefault) + equals(t, false, value.Subscriptions[1].IsDefault) + equals(t, false, value.Subscriptions[2].IsDefault) +} diff --git a/test/azureProfile.json b/test/azureProfile.json new file mode 100644 index 0000000..e97e14a --- /dev/null +++ b/test/azureProfile.json @@ -0,0 +1,47 @@ +{ + "installationId": "e960b7cc-c5d9-11ea-a6f5-00155d82a4f4", + "subscriptions": [ + { + "id": "9e7969ef-4cb8-4a2d-959f-bfdaae452a3d", + "name": "Test Subscription 1", + "state": "Enabled", + "user": { + "name": "First Last", + "type": "user" + }, + "isDefault": false, + "tenantId": "9e7969ef-4cb8-4a2d-959f-bfdaae452a3d", + "environmentName": "AzureCloud", + "homeTenantId": "9e7969ef-4cb8-4a2d-959f-bfdaae452a3d", + "managedByTenants": [] + }, + { + "id": "9bb28eee-ebaa-442a-83ba-5511810fb151", + "name": "Test Subscription 2", + "state": "Enabled", + "user": { + "name": "First Last", + "type": "user" + }, + "isDefault": false, + "tenantId": "9bb28eee-ebaa-442a-83ba-5511810fb151", + "environmentName": "AzureCloud", + "homeTenantId": "9bb28eee-ebaa-442a-83ba-5511810fb151", + "managedByTenants": [] + }, + { + "id": "8fff24dd-2842-4dbb-8a66-1410c7bc231f", + "name": "Test Subscription 3", + "state": "Enabled", + "user": { + "name": "First Last", + "type": "user" + }, + "isDefault": false, + "tenantId": "8fff24dd-2842-4dbb-8a66-1410c7bc231f", + "environmentName": "AzureCloud", + "homeTenantId": "8fff24dd-2842-4dbb-8a66-1410c7bc231f", + "managedByTenants": [] + } + ] +} diff --git a/test/helpers_test.go b/test/helpers_test.go new file mode 100644 index 0000000..19adf7f --- /dev/null +++ b/test/helpers_test.go @@ -0,0 +1,36 @@ +package aztx_test + +import ( + "fmt" + "path/filepath" + "reflect" + "runtime" + "testing" +) + +// assert fails the test if the condition is false. +func assert(tb testing.TB, condition bool, msg string, v ...interface{}) { + if !condition { + _, file, line, _ := runtime.Caller(1) + fmt.Printf("\033[31m%s:%d: "+msg+"\033[39m\n\n", append([]interface{}{filepath.Base(file), line}, v...)...) + tb.FailNow() + } +} + +// ok fails the test if an err is not nil. +func ok(tb testing.TB, err error) { + if err != nil { + _, file, line, _ := runtime.Caller(1) + fmt.Printf("\033[31m%s:%d: unexpected error: %s\033[39m\n\n", filepath.Base(file), line, err.Error()) + tb.FailNow() + } +} + +// equals fails the test if exp is not equal to act. +func equals(tb testing.TB, exp interface{}, act interface{}) { + if !reflect.DeepEqual(exp, act) { + _, file, line, _ := runtime.Caller(1) + fmt.Printf("\033[31m%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\033[39m\n\n", filepath.Base(file), line, exp, act) + tb.FailNow() + } +}