feat(az): use template for more flexibility

BREAKING CHANGE: this makes it so people have to remove the switches
in favor of the new template.
This commit is contained in:
Jan De Dobbeleer 2021-09-18 11:30:52 +02:00
parent c6a3dd4ee8
commit 2ed5d31f4f
No known key found for this signature in database
GPG key ID: F6CC273CE5BA9AEE
7 changed files with 265 additions and 291 deletions

View file

@ -33,7 +33,23 @@ To enable this, set `$env:AZ_ENABLED = $true` in your `$PROFILE`.
## Properties
- display_account: `boolean` - display the subscription account name or not - defaults to `false`
- display_name: `boolean` - display the subscription name or not - defaults to `true`
- display_id: `boolean` - display the subscription ID or not - defaults to `false`
- info_separator: `string` - text/icon to put in between the values - defaults to ` | `
- template: `string` - A go [text/template][go-text-template] template extended with [sprig][sprig] utilizing the
properties below. Defaults to `{{.Name}}`
## Template Properties
:::caution
When using the PowerShell module, only `.EnvironmentName`, `.ID`, `.Name` and `.User.Name` are available.
:::
- `.EnvironmentName`: `string` - the account environment name
- `.HomeTenantID`: `string` - the home tenant id
- `.ID`: `string` - the account/subscription id
- `.IsDefault`: `boolean` - is the default account or not
- `.Name`: `string` - the account name
- `.State`: `string` - the account state
- `.TenantID`: `string` - the tenant id
- `.User.Name`: `string` - the user name
[go-text-template]: https://golang.org/pkg/text/template/
[sprig]: https://masterminds.github.io/sprig/

View file

@ -50,16 +50,19 @@ function global:Initialize-ModuleSupport {
$env:POSH_GIT_STATUS = Write-GitStatus -Status $global:GitStatus
}
$env:AZ_SUBSCRIPTION_NAME = $null
$env:AZ_ENVIRONMENT_NAME = $null
$env:AZ_USER_NAME = $null
$env:AZ_SUBSCRIPTION_ID = $null
$env:AZ_ACCOUNT_NAME = $null
if ($env:AZ_ENABLED -eq $true) {
try {
$subscription = Get-AzContext | Select-Object -ExpandProperty "Subscription" | Select-Object "Name", "Id", "Account"
$context = Get-AzContext
if ($null -ne $subscription) {
$env:AZ_SUBSCRIPTION_NAME = $subscription.Name
$env:AZ_SUBSCRIPTION_ID = $subscription.Id
$env:AZ_SUBSCRIPTION_ACCOUNT = $subscription.Account
$env:AZ_ENVIRONMENT_NAME = $context.Environment.Name
$env:AZ_USER_NAME = $context.Account.Id
$env:AZ_SUBSCRIPTION_ID = $context.Subscription.Id
$env:AZ_ACCOUNT_NAME = $context.Subscription.Name
}
}
catch {}

View file

@ -1,57 +1,53 @@
package main
import (
"encoding/json"
"strings"
)
type az struct {
props *properties
env environmentInfo
name string
id string
account string
builder strings.Builder
separator string
props *properties
env environmentInfo
AZ *AzureAccount
}
const (
// SubscriptionInfoSeparator is put between the name and ID
SubscriptionInfoSeparator Property = "info_separator"
// DisplaySubscriptionID hides or show the subscription GUID
DisplaySubscriptionID Property = "display_id"
// DisplaySubscriptionName hides or shows the subscription display name
DisplaySubscriptionName Property = "display_name"
// DisplaySubscriptionAccount hides or shows the subscription account name
DisplaySubscriptionAccount Property = "display_account"
updateConsentNeeded = "Do you want to continue?"
updateMessage = "AZ CLI: Update needed!"
updateForeground = "#ffffff"
updateBackground = "#ff5349"
)
func (a *az) string() string {
a.separator = a.props.getString(SubscriptionInfoSeparator, " | ")
writeValue := func(value string) {
if len(value) == 0 {
return
}
if a.builder.Len() > 0 {
a.builder.WriteString(a.separator)
}
a.builder.WriteString(value)
}
if a.props.getBool(DisplaySubscriptionAccount, false) {
writeValue(a.account)
}
if a.props.getBool(DisplaySubscriptionName, true) {
writeValue(a.name)
}
if a.props.getBool(DisplaySubscriptionID, false) {
writeValue(a.id)
}
type AzureUser struct {
Name string `json:"name"`
}
return a.builder.String()
type AzureAccount struct {
EnvironmentName string `json:"environmentName"`
HomeTenantID string `json:"homeTenantId"`
ID string `json:"id"`
IsDefault bool `json:"isDefault"`
Name string `json:"name"`
State string `json:"state"`
TenantID string `json:"tenantId"`
User *AzureUser `json:"user"`
}
func (a *az) string() string {
if a.AZ != nil && a.AZ.Name == updateMessage {
return updateMessage
}
segmentTemplate := a.props.getString(SegmentTemplate, "{{.Name}}")
template := &textTemplate{
Template: segmentTemplate,
Context: a.AZ,
Env: a.env,
}
text, err := template.render()
if err != nil {
return err.Error()
}
return text
}
func (a *az) init(props *properties, env environmentInfo) {
@ -68,14 +64,24 @@ func (a *az) enabled() bool {
}
func (a *az) getFromEnvVars() bool {
a.name = a.env.getenv("AZ_SUBSCRIPTION_NAME")
a.id = a.env.getenv("AZ_SUBSCRIPTION_ID")
a.account = a.env.getenv("AZ_SUBSCRIPTION_ACCOUNT")
environmentName := a.env.getenv("AZ_ENVIRONMENT_NAME")
userName := a.env.getenv("AZ_USER_NAME")
id := a.env.getenv("AZ_SUBSCRIPTION_ID")
accountName := a.env.getenv("AZ_ACCOUNT_NAME")
if a.name == "" && a.id == "" {
if userName == "" && environmentName == "" {
return false
}
a.AZ = &AzureAccount{
EnvironmentName: environmentName,
Name: accountName,
ID: id,
User: &AzureUser{
Name: userName,
},
}
return true
}
@ -85,7 +91,7 @@ func (a *az) getFromAzCli() bool {
return false
}
output, _ := a.env.runCommand(cmd, "account", "show", "--query=[name,id,user.name]", "-o=tsv")
output, _ := a.env.runCommand(cmd, "account", "show")
if len(output) == 0 {
return false
}
@ -93,17 +99,13 @@ func (a *az) getFromAzCli() bool {
if strings.Contains(output, updateConsentNeeded) {
a.props.foreground = updateForeground
a.props.background = updateBackground
a.name = updateMessage
a.AZ = &AzureAccount{
Name: updateMessage,
}
return true
}
splittedOutput := strings.Split(output, "\n")
if len(splittedOutput) < 3 {
return false
}
a.name = strings.TrimSpace(splittedOutput[0])
a.id = strings.TrimSpace(splittedOutput[1])
a.account = strings.TrimSpace(splittedOutput[2])
return true
a.AZ = &AzureAccount{}
err := json.Unmarshal([]byte(output), a.AZ)
return err == nil
}

View file

@ -9,162 +9,140 @@ import (
func TestAzSegment(t *testing.T) {
cases := []struct {
Case string
ExpectedEnabled bool
ExpectedString string
EnvSubName string
EnvSubID string
EnvSubAccount string
CliExists bool
CliSubName string
CliSubID string
CliSubAccount string
InfoSeparator string
DisplayID bool
DisplayName bool
DisplayAccount bool
Case string
ExpectedEnabled bool
ExpectedString string
EnvEnvironmentName string
EnvUserName string
AccountName string
EnvSubscriptionID string
CLIExists bool
CLIEnvironmentname string
CLISubscriptionID string
CLIAccountName string
CLIUserName string
Template string
}{
{
Case: "print only account",
ExpectedEnabled: true,
ExpectedString: "foobar",
CliExists: true,
CliSubName: "foo",
CliSubID: "bar",
CliSubAccount: "foobar",
InfoSeparator: "$",
DisplayID: false,
DisplayName: false,
DisplayAccount: true,
Case: "display account name",
ExpectedEnabled: true,
ExpectedString: "foobar",
CLIExists: true,
CLIEnvironmentname: "foo",
CLISubscriptionID: "bar",
CLIAccountName: "foobar",
Template: "{{.Name}}",
},
{
Case: "envvars present",
ExpectedEnabled: true,
ExpectedString: "foo$bar",
EnvSubName: "foo",
EnvSubID: "bar",
CliExists: false,
InfoSeparator: "$",
DisplayID: true,
DisplayName: true,
Case: "envvars present",
ExpectedEnabled: true,
ExpectedString: "foo$bar",
EnvEnvironmentName: "foo",
EnvUserName: "bar",
CLIExists: false,
Template: "{{.EnvironmentName}}${{.User.Name}}",
},
{
Case: "envvar name present",
ExpectedEnabled: true,
ExpectedString: "foo",
EnvSubName: "foo",
CliExists: false,
InfoSeparator: "$",
DisplayID: true,
DisplayName: true,
Case: "envvar environment name present",
ExpectedEnabled: true,
ExpectedString: "foo",
EnvEnvironmentName: "foo",
CLIExists: false,
Template: "{{.EnvironmentName}}",
},
{
Case: "envvar id present",
Case: "envvar user name present",
ExpectedEnabled: true,
ExpectedString: "bar",
EnvSubID: "bar",
CliExists: false,
InfoSeparator: "$",
DisplayID: true,
DisplayName: true,
EnvUserName: "bar",
CLIExists: false,
Template: "{{.User.Name}}",
},
{
Case: "envvar account present",
ExpectedEnabled: true,
ExpectedString: "foobar",
EnvSubAccount: "foobar",
EnvSubID: "bar",
CliExists: false,
InfoSeparator: "$",
DisplayAccount: true,
Case: "envvar subscription id",
ExpectedEnabled: true,
ExpectedString: "foobar",
EnvSubscriptionID: "foobar",
EnvUserName: "bar",
CLIExists: false,
Template: "{{.ID}}",
},
{
Case: "cli not found",
ExpectedEnabled: false,
ExpectedString: "",
CliExists: false,
InfoSeparator: "$",
DisplayID: true,
DisplayName: true,
CLIExists: false,
},
{
Case: "cli contains data",
ExpectedEnabled: true,
ExpectedString: "foo$bar",
CliExists: true,
CliSubName: "foo",
CliSubID: "bar",
InfoSeparator: "$",
DisplayID: true,
DisplayName: true,
Case: "cli contains data",
ExpectedEnabled: true,
ExpectedString: "foo$bar",
CLIExists: true,
CLIEnvironmentname: "foo",
CLISubscriptionID: "bar",
Template: "{{.EnvironmentName}}${{.ID}}",
},
{
Case: "print only name",
ExpectedEnabled: true,
ExpectedString: "foo",
CliExists: true,
CliSubName: "foo",
CliSubID: "bar",
InfoSeparator: "$",
DisplayID: false,
DisplayName: true,
Case: "print only environment ame",
ExpectedEnabled: true,
ExpectedString: "foo",
CLIExists: true,
CLIEnvironmentname: "foo",
CLISubscriptionID: "bar",
Template: "{{.EnvironmentName}}",
},
{
Case: "print only id",
ExpectedEnabled: true,
ExpectedString: "bar",
CliExists: true,
CliSubName: "foo",
CliSubID: "bar",
InfoSeparator: "$",
DisplayID: true,
DisplayName: false,
Case: "print only id",
ExpectedEnabled: true,
ExpectedString: "bar",
CLIExists: true,
CLIEnvironmentname: "foo",
CLISubscriptionID: "bar",
Template: "{{.ID}}",
},
{
Case: "print none",
ExpectedEnabled: true,
CliExists: true,
CliSubName: "foo",
CliSubID: "bar",
InfoSeparator: "$",
Case: "print none",
ExpectedEnabled: true,
CLIExists: true,
CLIEnvironmentname: "foo",
CLISubscriptionID: "bar",
},
{
Case: "update needed",
ExpectedEnabled: true,
ExpectedString: updateMessage,
CliExists: true,
CliSubName: "Do you want to continue? (Y/n): Visual Studio Enterprise",
DisplayID: false,
DisplayName: true,
},
{
Case: "account info",
ExpectedEnabled: true,
ExpectedString: updateMessage,
CliExists: true,
CliSubName: "Do you want to continue? (Y/n): Visual Studio Enterprise",
DisplayID: false,
DisplayName: true,
DisplayAccount: true,
Case: "update needed",
ExpectedEnabled: true,
ExpectedString: updateMessage,
CLIExists: true,
CLIEnvironmentname: "Do you want to continue? (Y/n): Visual Studio Enterprise",
},
}
for _, tc := range cases {
env := new(MockedEnvironment)
env.On("getenv", "AZ_SUBSCRIPTION_NAME").Return(tc.EnvSubName)
env.On("getenv", "AZ_SUBSCRIPTION_ID").Return(tc.EnvSubID)
env.On("getenv", "AZ_SUBSCRIPTION_ACCOUNT").Return(tc.EnvSubAccount)
env.On("hasCommand", "az").Return(tc.CliExists)
env.On("runCommand", "az", []string{"account", "show", "--query=[name,id,user.name]", "-o=tsv"}).Return(
fmt.Sprintf("%s\n%s\n%s\n", tc.CliSubName, tc.CliSubID, tc.CliSubAccount),
env.On("getenv", "AZ_ENVIRONMENT_NAME").Return(tc.EnvEnvironmentName)
env.On("getenv", "AZ_USER_NAME").Return(tc.EnvUserName)
env.On("getenv", "AZ_SUBSCRIPTION_ID").Return(tc.EnvSubscriptionID)
env.On("getenv", "AZ_ACCOUNT_NAME").Return(tc.AccountName)
env.On("hasCommand", "az").Return(tc.CLIExists)
env.On("runCommand", "az", []string{"account", "show"}).Return(
fmt.Sprintf(`{
"environmentName": "%s",
"homeTenantId": "8d934305-ac9f-46fe-b0e7-50fd32ad2acf",
"id": "%s",
"isDefault": true,
"managedByTenants": [],
"name": "%s",
"state": "Enabled",
"tenantId": "8d934305-ac9f-46fe-b0e7-50fd32ad2acf",
"user": {
"name": "%s",
"type": "user"
}
}`, tc.CLIEnvironmentname, tc.CLISubscriptionID, tc.CLIAccountName, tc.CLIUserName),
nil,
)
props := &properties{
values: map[Property]interface{}{
SubscriptionInfoSeparator: tc.InfoSeparator,
DisplaySubscriptionID: tc.DisplayID,
DisplaySubscriptionName: tc.DisplayName,
DisplaySubscriptionAccount: tc.DisplayAccount,
SegmentTemplate: tc.Template,
},
}

View file

@ -10,87 +10,84 @@
"alignment": "left",
"segments": [
{
"type": "text",
"style": "diamond",
"leading_diamond": "\ue0c2\ue0c2",
"foreground": "#000000",
"background": "#4707a8",
"properties": {
"text": "",
"postfix": "",
"prefix": ""
}
"type": "text",
"style": "diamond",
"leading_diamond": "\ue0c2\ue0c2",
"foreground": "#000000",
"background": "#4707a8",
"properties": {
"text": "",
"postfix": "",
"prefix": ""
}
},
{
"type": "session",
"style": "diamond",
"foreground": "#ffffff",
"background": "#4707a8",
"leading_diamond": "\uE0B6",
"trailing_diamond": "\uE0B0"
"type": "session",
"style": "diamond",
"foreground": "#ffffff",
"background": "#4707a8",
"leading_diamond": "\uE0B6",
"trailing_diamond": "\uE0B0"
},
{
"type": "az",
"style": "powerline",
"powerline_symbol": "\uE0B0",
"foreground": "#ffffff",
"background": "#1a1af5",
"properties": {
"display_id": false,
"display_name": true,
"info_separator": " @ ",
"prefix": " \uFD03 "
}
{
"type": "az",
"style": "powerline",
"powerline_symbol": "\uE0B0",
"foreground": "#ffffff",
"background": "#1a1af5",
"properties": {
"prefix": " \uFD03 "
}
},
{
"type": "exit",
"style": "diamond",
"foreground": "#ffffff",
"background": "#4707a8",
"leading_diamond": "",
"trailing_diamond": "\uE0B4",
"properties": {
"display_exit_code": false,
"always_enabled": true,
"error_color": "#f1184c",
"color_background": true,
"prefix": "<transparent>\uE0B0</> \uE23A"
}
{
"type": "exit",
"style": "diamond",
"foreground": "#ffffff",
"background": "#4707a8",
"leading_diamond": "",
"trailing_diamond": "\uE0B4",
"properties": {
"display_exit_code": false,
"always_enabled": true,
"error_color": "#f1184c",
"color_background": true,
"prefix": "<transparent>\uE0B0</> \uE23A"
}
}
]
},
{
"type": "prompt",
"alignment": "left",
"newline": true,
"segments": [
{
"type": "os",
"style": "diamond",
"leading_diamond": "\ue0c2\ue0c2",
"foreground": "#000000",
"background": "#ffffff",
"properties": {
"alpine": "\uf300",
"arch": "\uf303",
"centos": "\uf304",
"debian": "\uf306",
"elementary": "\uf309",
"fedora": "\uf30a",
"gentoo": "\uf30d",
"linux": "\ue712",
"macos": "\ue711",
"manjaro": "\uf312",
"mint": "\uf30f",
"opensuse": "\uf314",
"raspbian": "\uf315",
"ubuntu": "\uf31c",
"wsl": "\ue712",
"wsl_separator": " on ",
"windows": "\ue70f",
"postfix": "<#000000> \ue0b1</>"
}
},
{
"type": "prompt",
"alignment": "left",
"newline": true,
"segments": [
{
"type": "os",
"style": "diamond",
"leading_diamond": "\ue0c2\ue0c2",
"foreground": "#000000",
"background": "#ffffff",
"properties": {
"alpine": "\uf300",
"arch": "\uf303",
"centos": "\uf304",
"debian": "\uf306",
"elementary": "\uf309",
"fedora": "\uf30a",
"gentoo": "\uf30d",
"linux": "\ue712",
"macos": "\ue711",
"manjaro": "\uf312",
"mint": "\uf30f",
"opensuse": "\uf314",
"raspbian": "\uf315",
"ubuntu": "\uf31c",
"wsl": "\ue712",
"wsl_separator": " on ",
"windows": "\ue70f",
"postfix": "<#000000> \ue0b1</>"
}
},
{
@ -136,13 +133,13 @@
"prefix": "<#ffffff>\ue0b1 </>"
}
},
{
"type": "terraform",
"style": "powerline",
"powerline_symbol": "",
"foreground": "#000000",
"background": "#ffffff"
},
{
"type": "terraform",
"style": "powerline",
"powerline_symbol": "",
"foreground": "#000000",
"background": "#ffffff"
},
{
"type": "text",
"style": "diamond",
@ -150,9 +147,9 @@
"foreground": "#000000",
"background": "#ffffff",
"properties": {
"text": "",
"postfix": "",
"prefix": ""
"text": "",
"postfix": "",
"prefix": ""
}
}
]
@ -166,7 +163,7 @@
"style": "plain",
"foreground": "#1a1af5",
"properties": {
"text": "\u276F"
"text": "\u276F"
}
}
]

View file

@ -39,9 +39,6 @@
"foreground": "#000000",
"background": "#008AD7",
"properties": {
"display_id": false,
"display_name": true,
"info_separator": " @ ",
"prefix": " \uFD03 "
}
}

View file

@ -290,23 +290,8 @@
"properties": {
"properties": {
"properties": {
"display_id": {
"type": "boolean",
"title": "Display ID",
"description": "Display the subscription ID or not",
"default": false
},
"display_name": {
"type": "boolean",
"title": "Display Name",
"description": "Display the subscription name or not",
"default": true
},
"info_separator": {
"type": "string",
"title": "Display Name",
"description": "Text/icon to put in between the subscription name and ID",
"default": " | "
"template": {
"$ref": "#/definitions/template"
}
}
}
@ -1597,11 +1582,7 @@
"title": "units",
"description": "Units of measurement. Available values are standard (kelvin), metric (celsius), and imperial (fahrenheit). Default is standard",
"default": "standard",
"enum": [
"standard",
"metric",
"imperial"
]
"enum": ["standard", "metric", "imperial"]
},
"enable_hyperlink": {
"$ref": "#/definitions/enable_hyperlink"