1package format 2 3import ( 4 "github.com/zclconf/go-cty/cty" 5) 6 7// ObjectValueID takes a value that is assumed to be an object representation 8// of some resource instance object and attempts to heuristically find an 9// attribute of it that is likely to be a unique identifier in the remote 10// system that it belongs to which will be useful to the user. 11// 12// If such an attribute is found, its name and string value intended for 13// display are returned. Both returned strings are empty if no such attribute 14// exists, in which case the caller should assume that the resource instance 15// address within the Terraform configuration is the best available identifier. 16// 17// This is only a best-effort sort of thing, relying on naming conventions in 18// our resource type schemas. The result is not guaranteed to be unique, but 19// should generally be suitable for display to an end-user anyway. 20// 21// This function will panic if the given value is not of an object type. 22func ObjectValueID(obj cty.Value) (k, v string) { 23 if obj.IsNull() || !obj.IsKnown() { 24 return "", "" 25 } 26 27 atys := obj.Type().AttributeTypes() 28 29 switch { 30 31 case atys["id"] == cty.String: 32 v := obj.GetAttr("id") 33 if v.HasMark("sensitive") { 34 break 35 } 36 v, _ = v.Unmark() 37 38 if v.IsKnown() && !v.IsNull() { 39 return "id", v.AsString() 40 } 41 42 case atys["name"] == cty.String: 43 // "name" isn't always globally unique, but if there isn't also an 44 // "id" then it _often_ is, in practice. 45 v := obj.GetAttr("name") 46 if v.HasMark("sensitive") { 47 break 48 } 49 v, _ = v.Unmark() 50 51 if v.IsKnown() && !v.IsNull() { 52 return "name", v.AsString() 53 } 54 } 55 56 return "", "" 57} 58 59// ObjectValueName takes a value that is assumed to be an object representation 60// of some resource instance object and attempts to heuristically find an 61// attribute of it that is likely to be a human-friendly name in the remote 62// system that it belongs to which will be useful to the user. 63// 64// If such an attribute is found, its name and string value intended for 65// display are returned. Both returned strings are empty if no such attribute 66// exists, in which case the caller should assume that the resource instance 67// address within the Terraform configuration is the best available identifier. 68// 69// This is only a best-effort sort of thing, relying on naming conventions in 70// our resource type schemas. The result is not guaranteed to be unique, but 71// should generally be suitable for display to an end-user anyway. 72// 73// Callers that use both ObjectValueName and ObjectValueID at the same time 74// should be prepared to get the same attribute key and value from both in 75// some cases, since there is overlap betweek the id-extraction and 76// name-extraction heuristics. 77// 78// This function will panic if the given value is not of an object type. 79func ObjectValueName(obj cty.Value) (k, v string) { 80 if obj.IsNull() || !obj.IsKnown() { 81 return "", "" 82 } 83 84 atys := obj.Type().AttributeTypes() 85 86 switch { 87 88 case atys["name"] == cty.String: 89 v := obj.GetAttr("name") 90 if v.HasMark("sensitive") { 91 break 92 } 93 v, _ = v.Unmark() 94 95 if v.IsKnown() && !v.IsNull() { 96 return "name", v.AsString() 97 } 98 99 case atys["tags"].IsMapType() && atys["tags"].ElementType() == cty.String: 100 tags := obj.GetAttr("tags") 101 if tags.IsNull() || !tags.IsWhollyKnown() || tags.HasMark("sensitive") { 102 break 103 } 104 tags, _ = tags.Unmark() 105 106 switch { 107 case tags.HasIndex(cty.StringVal("name")).RawEquals(cty.True): 108 v := tags.Index(cty.StringVal("name")) 109 if v.HasMark("sensitive") { 110 break 111 } 112 v, _ = v.Unmark() 113 114 if v.IsKnown() && !v.IsNull() { 115 return "tags.name", v.AsString() 116 } 117 case tags.HasIndex(cty.StringVal("Name")).RawEquals(cty.True): 118 // AWS-style naming convention 119 v := tags.Index(cty.StringVal("Name")) 120 if v.HasMark("sensitive") { 121 break 122 } 123 v, _ = v.Unmark() 124 125 if v.IsKnown() && !v.IsNull() { 126 return "tags.Name", v.AsString() 127 } 128 } 129 } 130 131 return "", "" 132} 133 134// ObjectValueIDOrName is a convenience wrapper around both ObjectValueID 135// and ObjectValueName (in that preference order) to try to extract some sort 136// of human-friendly descriptive string value for an object as additional 137// context about an object when it is being displayed in a compact way (where 138// not all of the attributes are visible.) 139// 140// Just as with the two functions it wraps, it is a best-effort and may return 141// two empty strings if no suitable attribute can be found for a given object. 142func ObjectValueIDOrName(obj cty.Value) (k, v string) { 143 k, v = ObjectValueID(obj) 144 if k != "" { 145 return 146 } 147 k, v = ObjectValueName(obj) 148 return 149} 150