command: show resource actions using resource addresses
Previously we were using the internal resource id syntax in the UI. Now we'll use the standard user-facing resource address syntax instead.
This commit is contained in:
parent
3ea159297c
commit
d4efc95191
|
@ -65,6 +65,7 @@ func (h *UiHook) PreApply(
|
||||||
}
|
}
|
||||||
|
|
||||||
id := n.HumanId()
|
id := n.HumanId()
|
||||||
|
addr := n.ResourceAddress()
|
||||||
|
|
||||||
op := uiResourceModify
|
op := uiResourceModify
|
||||||
if d.Destroy {
|
if d.Destroy {
|
||||||
|
@ -142,7 +143,7 @@ func (h *UiHook) PreApply(
|
||||||
|
|
||||||
h.ui.Output(h.Colorize.Color(fmt.Sprintf(
|
h.ui.Output(h.Colorize.Color(fmt.Sprintf(
|
||||||
"[reset][bold]%s: %s%s[reset]%s",
|
"[reset][bold]%s: %s%s[reset]%s",
|
||||||
id,
|
addr,
|
||||||
operation,
|
operation,
|
||||||
stateIdSuffix,
|
stateIdSuffix,
|
||||||
attrString)))
|
attrString)))
|
||||||
|
@ -210,6 +211,7 @@ func (h *UiHook) PostApply(
|
||||||
applyerr error) (terraform.HookAction, error) {
|
applyerr error) (terraform.HookAction, error) {
|
||||||
|
|
||||||
id := n.HumanId()
|
id := n.HumanId()
|
||||||
|
addr := n.ResourceAddress()
|
||||||
|
|
||||||
h.l.Lock()
|
h.l.Lock()
|
||||||
state := h.resources[id]
|
state := h.resources[id]
|
||||||
|
@ -244,7 +246,7 @@ func (h *UiHook) PostApply(
|
||||||
|
|
||||||
colorized := h.Colorize.Color(fmt.Sprintf(
|
colorized := h.Colorize.Color(fmt.Sprintf(
|
||||||
"[reset][bold]%s: %s after %s%s[reset]",
|
"[reset][bold]%s: %s after %s%s[reset]",
|
||||||
id, msg, time.Now().Round(time.Second).Sub(state.Start), stateIdSuffix))
|
addr, msg, time.Now().Round(time.Second).Sub(state.Start), stateIdSuffix))
|
||||||
|
|
||||||
h.ui.Output(colorized)
|
h.ui.Output(colorized)
|
||||||
|
|
||||||
|
@ -260,10 +262,10 @@ func (h *UiHook) PreDiff(
|
||||||
func (h *UiHook) PreProvision(
|
func (h *UiHook) PreProvision(
|
||||||
n *terraform.InstanceInfo,
|
n *terraform.InstanceInfo,
|
||||||
provId string) (terraform.HookAction, error) {
|
provId string) (terraform.HookAction, error) {
|
||||||
id := n.HumanId()
|
addr := n.ResourceAddress()
|
||||||
h.ui.Output(h.Colorize.Color(fmt.Sprintf(
|
h.ui.Output(h.Colorize.Color(fmt.Sprintf(
|
||||||
"[reset][bold]%s: Provisioning with '%s'...[reset]",
|
"[reset][bold]%s: Provisioning with '%s'...[reset]",
|
||||||
id, provId)))
|
addr, provId)))
|
||||||
return terraform.HookActionContinue, nil
|
return terraform.HookActionContinue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,11 +273,11 @@ func (h *UiHook) ProvisionOutput(
|
||||||
n *terraform.InstanceInfo,
|
n *terraform.InstanceInfo,
|
||||||
provId string,
|
provId string,
|
||||||
msg string) {
|
msg string) {
|
||||||
id := n.HumanId()
|
addr := n.ResourceAddress()
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
buf.WriteString(h.Colorize.Color("[reset]"))
|
buf.WriteString(h.Colorize.Color("[reset]"))
|
||||||
|
|
||||||
prefix := fmt.Sprintf("%s (%s): ", id, provId)
|
prefix := fmt.Sprintf("%s (%s): ", addr, provId)
|
||||||
s := bufio.NewScanner(strings.NewReader(msg))
|
s := bufio.NewScanner(strings.NewReader(msg))
|
||||||
s.Split(scanLines)
|
s.Split(scanLines)
|
||||||
for s.Scan() {
|
for s.Scan() {
|
||||||
|
@ -293,7 +295,7 @@ func (h *UiHook) PreRefresh(
|
||||||
s *terraform.InstanceState) (terraform.HookAction, error) {
|
s *terraform.InstanceState) (terraform.HookAction, error) {
|
||||||
h.once.Do(h.init)
|
h.once.Do(h.init)
|
||||||
|
|
||||||
id := n.HumanId()
|
addr := n.ResourceAddress()
|
||||||
|
|
||||||
var stateIdSuffix string
|
var stateIdSuffix string
|
||||||
// Data resources refresh before they have ids, whereas managed
|
// Data resources refresh before they have ids, whereas managed
|
||||||
|
@ -304,7 +306,7 @@ func (h *UiHook) PreRefresh(
|
||||||
|
|
||||||
h.ui.Output(h.Colorize.Color(fmt.Sprintf(
|
h.ui.Output(h.Colorize.Color(fmt.Sprintf(
|
||||||
"[reset][bold]%s: Refreshing state...%s",
|
"[reset][bold]%s: Refreshing state...%s",
|
||||||
id, stateIdSuffix)))
|
addr, stateIdSuffix)))
|
||||||
return terraform.HookActionContinue, nil
|
return terraform.HookActionContinue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -313,9 +315,10 @@ func (h *UiHook) PreImportState(
|
||||||
id string) (terraform.HookAction, error) {
|
id string) (terraform.HookAction, error) {
|
||||||
h.once.Do(h.init)
|
h.once.Do(h.init)
|
||||||
|
|
||||||
|
addr := n.ResourceAddress()
|
||||||
h.ui.Output(h.Colorize.Color(fmt.Sprintf(
|
h.ui.Output(h.Colorize.Color(fmt.Sprintf(
|
||||||
"[reset][bold]%s: Importing from ID %q...",
|
"[reset][bold]%s: Importing from ID %q...",
|
||||||
n.HumanId(), id)))
|
addr, id)))
|
||||||
return terraform.HookActionContinue, nil
|
return terraform.HookActionContinue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,9 +327,9 @@ func (h *UiHook) PostImportState(
|
||||||
s []*terraform.InstanceState) (terraform.HookAction, error) {
|
s []*terraform.InstanceState) (terraform.HookAction, error) {
|
||||||
h.once.Do(h.init)
|
h.once.Do(h.init)
|
||||||
|
|
||||||
id := n.HumanId()
|
addr := n.ResourceAddress()
|
||||||
h.ui.Output(h.Colorize.Color(fmt.Sprintf(
|
h.ui.Output(h.Colorize.Color(fmt.Sprintf(
|
||||||
"[reset][bold][green]%s: Import complete!", id)))
|
"[reset][bold][green]%s: Import complete!", addr)))
|
||||||
for _, s := range s {
|
for _, s := range s {
|
||||||
h.ui.Output(h.Colorize.Color(fmt.Sprintf(
|
h.ui.Output(h.Colorize.Color(fmt.Sprintf(
|
||||||
"[reset][green] Imported %s (ID: %s)",
|
"[reset][green] Imported %s (ID: %s)",
|
||||||
|
|
|
@ -88,6 +88,46 @@ func (i *InstanceInfo) HumanId() string {
|
||||||
i.Id)
|
i.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResourceAddress returns the address of the resource that the receiver is describing.
|
||||||
|
func (i *InstanceInfo) ResourceAddress() *ResourceAddress {
|
||||||
|
// GROSS: for tainted and deposed instances, their status gets appended
|
||||||
|
// to i.Id to create a unique id for the graph node. Historically these
|
||||||
|
// ids were displayed to the user, so it's designed to be human-readable:
|
||||||
|
// "aws_instance.bar.0 (deposed #0)"
|
||||||
|
//
|
||||||
|
// So here we detect such suffixes and try to interpret them back to
|
||||||
|
// their original meaning so we can then produce a ResourceAddress
|
||||||
|
// with a suitable InstanceType.
|
||||||
|
id := i.Id
|
||||||
|
instanceType := TypeInvalid
|
||||||
|
if idx := strings.Index(id, " ("); idx != -1 {
|
||||||
|
remain := id[idx:]
|
||||||
|
id = id[:idx]
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case strings.Contains(remain, "tainted"):
|
||||||
|
instanceType = TypeTainted
|
||||||
|
case strings.Contains(remain, "deposed"):
|
||||||
|
instanceType = TypeDeposed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addr, err := parseResourceAddressInternal(id)
|
||||||
|
if err != nil {
|
||||||
|
// should never happen, since that would indicate a bug in the
|
||||||
|
// code that constructed this InstanceInfo.
|
||||||
|
panic(fmt.Errorf("InstanceInfo has invalid Id %s", id))
|
||||||
|
}
|
||||||
|
if len(i.ModulePath) > 1 {
|
||||||
|
addr.Path = i.ModulePath[1:] // trim off "root" prefix, which is implied
|
||||||
|
}
|
||||||
|
if instanceType != TypeInvalid {
|
||||||
|
addr.InstanceTypeSet = true
|
||||||
|
addr.InstanceType = instanceType
|
||||||
|
}
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
func (i *InstanceInfo) uniqueId() string {
|
func (i *InstanceInfo) uniqueId() string {
|
||||||
prefix := i.HumanId()
|
prefix := i.HumanId()
|
||||||
if v := i.uniqueExtra; v != "" {
|
if v := i.uniqueExtra; v != "" {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package terraform
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/hil"
|
"github.com/hashicorp/hil"
|
||||||
|
@ -46,6 +47,63 @@ func TestInstanceInfo(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInstanceInfoResourceAddress(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
Input *InstanceInfo
|
||||||
|
Want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
&InstanceInfo{
|
||||||
|
Id: "test_resource.baz",
|
||||||
|
},
|
||||||
|
"test_resource.baz",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&InstanceInfo{
|
||||||
|
Id: "test_resource.baz",
|
||||||
|
ModulePath: rootModulePath,
|
||||||
|
},
|
||||||
|
"test_resource.baz",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&InstanceInfo{
|
||||||
|
Id: "test_resource.baz",
|
||||||
|
ModulePath: []string{"root", "foo"},
|
||||||
|
},
|
||||||
|
"module.foo.test_resource.baz",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&InstanceInfo{
|
||||||
|
Id: "test_resource.baz",
|
||||||
|
ModulePath: []string{"root", "foo", "bar"},
|
||||||
|
},
|
||||||
|
"module.foo.module.bar.test_resource.baz",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&InstanceInfo{
|
||||||
|
Id: "test_resource.baz (tainted)",
|
||||||
|
},
|
||||||
|
"test_resource.baz.tainted",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&InstanceInfo{
|
||||||
|
Id: "test_resource.baz (deposed #0)",
|
||||||
|
},
|
||||||
|
"test_resource.baz.deposed",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||||
|
gotAddr := test.Input.ResourceAddress()
|
||||||
|
got := gotAddr.String()
|
||||||
|
if got != test.Want {
|
||||||
|
t.Fatalf("wrong result\ngot: %s\nwant: %s", got, test.Want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestResourceConfigGet(t *testing.T) {
|
func TestResourceConfigGet(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
Config map[string]interface{}
|
Config map[string]interface{}
|
||||||
|
|
Loading…
Reference in New Issue