Skip to content

Commit

Permalink
refacto to have metadata (tags)
Browse files Browse the repository at this point in the history
  • Loading branch information
Martin Guibert committed Apr 12, 2021
1 parent 376a9eb commit b58ebc0
Show file tree
Hide file tree
Showing 70 changed files with 400 additions and 164 deletions.
57 changes: 4 additions & 53 deletions pkg/analyser/analyzer.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package analyser

import (
"reflect"
"sort"
"strings"

Expand Down Expand Up @@ -67,7 +66,6 @@ func (a Analyzer) Analyze(remoteResources, resourcesFromState []resource.Resourc
filteredRemoteResource = append(filteredRemoteResource, remoteRes)
}

normalizers := resource.Normalizers()
haveComputedDiff := false
for _, stateRes := range resourcesFromState {
i, remoteRes, found := findCorrespondingRes(filteredRemoteResource, stateRes)
Expand All @@ -85,16 +83,10 @@ func (a Analyzer) Analyze(remoteResources, resourcesFromState []resource.Resourc
filteredRemoteResource = removeResourceByIndex(i, filteredRemoteResource)
analysis.AddManaged(stateRes)

state := dctlcty.AsAttrs(stateRes.CtyValue())
rem := dctlcty.AsAttrs(remoteRes.CtyValue())
state := dctlcty.AsAttrs(stateRes.CtyValue(), stateRes.TerraformType())
rem := dctlcty.AsAttrs(remoteRes.CtyValue(), remoteRes.TerraformType())

normalize, exists := normalizers[stateRes.TerraformType()]
if exists {
normalize(&state)
normalize(&rem)
}

delta, _ := diff.Diff(state, rem)
delta, _ := diff.Diff(state.Attrs, rem.Attrs)
if len(delta) > 0 {
sort.Slice(delta, func(i, j int) bool {
return strings.Join(delta[i].Path, ".") < strings.Join(delta[j].Path, ".") || delta[i].Type < delta[j].Type
Expand All @@ -105,7 +97,7 @@ func (a Analyzer) Analyze(remoteResources, resourcesFromState []resource.Resourc
continue
}
c := Change{Change: change}
c.Computed = a.isComputedField(stateRes, c)
c.Computed = state.IsComputedField(c.Path)
if c.Computed {
haveComputedDiff = true
}
Expand Down Expand Up @@ -156,47 +148,6 @@ func removeResourceByIndex(i int, resources []resource.Resource) []resource.Reso
return append(resources[:i], resources[i+1:]...)
}

// isComputedField returns true if the field that generated the diff of a resource
// has a computed tag
func (a Analyzer) isComputedField(stateRes resource.Resource, change Change) bool {
if field, ok := a.getField(reflect.TypeOf(stateRes), change.Path); ok {
return field.Tag.Get("computed") == "true"
}
return false
}

// getField recursively finds the deepest field inside a resource depending on
// its path and its type
func (a Analyzer) getField(t reflect.Type, path []string) (reflect.StructField, bool) {
switch t.Kind() {
case reflect.Ptr:
return a.getField(t.Elem(), path)
case reflect.Slice:
return a.getField(t.Elem(), path[1:])
default:
{
if field, ok := t.FieldByName(path[0]); ok && a.hasNestedFields(field.Type) && len(path) > 1 {
return a.getField(field.Type, path[1:])
} else {
return field, ok
}
}
}
}

// hasNestedFields will return true if the current field is either a struct
// or a slice of struct
func (a Analyzer) hasNestedFields(t reflect.Type) bool {
switch t.Kind() {
case reflect.Ptr:
return a.hasNestedFields(t.Elem())
case reflect.Slice:
return t.Elem().Kind() == reflect.Struct
default:
return t.Kind() == reflect.Struct
}
}

// hasUnmanagedSecurityGroupRules returns true if we find at least one unmanaged
// security group rule
func (a Analyzer) hasUnmanagedSecurityGroupRules(unmanagedResources []resource.Resource) bool {
Expand Down
36 changes: 32 additions & 4 deletions pkg/analyser/analyzer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -781,11 +781,18 @@ func TestAnalyze(t *testing.T) {
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
for _, r := range c.cloud {
res, ok := r.(*testresource.FakeResource)
fres, ok := r.(*testresource.FakeResource)
if ok {
impliedType, _ := gocty.ImpliedType(res)
value, _ := gocty.ToCtyValue(res, impliedType)
res.CtyVal = &value
impliedType, _ := gocty.ImpliedType(fres)
value, _ := gocty.ToCtyValue(fres, impliedType)
fres.CtyVal = &value
continue
}
sgres, ok := r.(*aws.AwsSecurityGroup)
if ok {
impliedType, _ := gocty.ImpliedType(sgres)
value, _ := gocty.ToCtyValue(sgres, impliedType)
sgres.CtyVal = &value
continue
}
}
Expand All @@ -798,6 +805,13 @@ func TestAnalyze(t *testing.T) {
res.CtyVal = &value
continue
}
sgres, ok := r.(*aws.AwsSecurityGroup)
if ok {
impliedType, _ := gocty.ImpliedType(sgres)
value, _ := gocty.ToCtyValue(sgres, impliedType)
sgres.CtyVal = &value
continue
}
}

for _, r := range c.ignoredRes {
Expand All @@ -808,6 +822,13 @@ func TestAnalyze(t *testing.T) {
res.CtyVal = &value
continue
}
sgres, ok := r.(*aws.AwsSecurityGroup)
if ok {
impliedType, _ := gocty.ImpliedType(sgres)
value, _ := gocty.ToCtyValue(sgres, impliedType)
sgres.CtyVal = &value
continue
}
}

for _, r := range c.ignoredDrift {
Expand All @@ -818,6 +839,13 @@ func TestAnalyze(t *testing.T) {
res.CtyVal = &value
continue
}
sgres, ok := r.res.(*aws.AwsSecurityGroup)
if ok {
impliedType, _ := gocty.ImpliedType(sgres)
value, _ := gocty.ToCtyValue(sgres, impliedType)
sgres.CtyVal = &value
continue
}
}

filter := &mocks.Filter{}
Expand Down
45 changes: 40 additions & 5 deletions pkg/dctlcty/cty_attribute.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,47 @@ package dctlcty

import (
"encoding/json"
"reflect"
"strings"

"github.com/pkg/errors"
"github.com/zclconf/go-cty/cty"
ctyjson "github.com/zclconf/go-cty/cty/json"
)

func AsAttrs(val *cty.Value) CtyAttributes {
func AsAttrs(val *cty.Value, terraformType string) *CtyAttributes {
if val == nil {
return nil
}

metadata := resourcesMetadata[terraformType]

bytes, _ := ctyjson.Marshal(*val, val.Type())
var attrs map[string]interface{}
err := json.Unmarshal(bytes, &attrs)
if err != nil {
panic(err)
}
return attrs

attributes := &CtyAttributes{
attrs,
&metadata,
}

if metadata.normalizer != nil {
metadata.normalizer(attributes)
}

return attributes
}

type CtyAttributes map[string]interface{}
type CtyAttributes struct {
Attrs map[string]interface{}
metadata *Metadata
}

func (a *CtyAttributes) SafeDelete(path []string) {
val := *a
val := a.Attrs
for i, key := range path {
if i == len(path)-1 {
delete(val, key)
Expand All @@ -45,7 +62,7 @@ func (a *CtyAttributes) SafeDelete(path []string) {
}

func (a *CtyAttributes) SafeSet(path []string, value interface{}) error {
val := *a
val := a.Attrs
for i, key := range path {
if i == len(path)-1 {
val[key] = value
Expand All @@ -66,3 +83,21 @@ func (a *CtyAttributes) SafeSet(path []string, value interface{}) error {
}
return errors.New("Error setting value") // should not happen ?
}

func (a *CtyAttributes) Tags(path []string) reflect.StructTag {
if a.metadata == nil {
return ""
}

fieldTags, exists := a.metadata.tags[strings.Join(path, ".")]
if !exists {
return ""
}

return reflect.StructTag(fieldTags)
}

func (a *CtyAttributes) IsComputedField(path []string) bool {
tags := a.Tags(path)
return tags.Get("computed") == "true"
}
111 changes: 105 additions & 6 deletions pkg/dctlcty/cty_attribute_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dctlcty

import (
"reflect"
"testing"

"github.com/pkg/errors"
Expand All @@ -10,9 +11,9 @@ import (
func TestCtyAttributes_SafeDelete(t *testing.T) {
tests := []struct {
name string
attr CtyAttributes
attr map[string]interface{}
path []string
expected CtyAttributes
expected map[string]interface{}
}{
{
name: "Delete existing",
Expand Down Expand Up @@ -95,7 +96,10 @@ func TestCtyAttributes_SafeDelete(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.attr.SafeDelete(tt.path)
attr := CtyAttributes{
Attrs: tt.attr,
}
attr.SafeDelete(tt.path)
assert.Equal(t, tt.expected, tt.attr)
})
}
Expand All @@ -104,10 +108,10 @@ func TestCtyAttributes_SafeDelete(t *testing.T) {
func TestCtyAttributes_SafeSet(t *testing.T) {
tests := []struct {
name string
attr CtyAttributes
attr map[string]interface{}
path []string
value interface{}
expected CtyAttributes
expected map[string]interface{}
error error
}{
{
Expand Down Expand Up @@ -201,7 +205,10 @@ func TestCtyAttributes_SafeSet(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.attr.SafeSet(tt.path, tt.value)
attr := CtyAttributes{
Attrs: tt.attr,
}
err := attr.SafeSet(tt.path, tt.value)
if tt.error != nil {
assert.NotNil(t, err)
assert.Equal(t, tt.error.Error(), err.Error())
Expand All @@ -212,3 +219,95 @@ func TestCtyAttributes_SafeSet(t *testing.T) {
})
}
}

func TestCtyAttributes_Tags(t *testing.T) {
tests := []struct {
name string
metadata *Metadata
path []string
want reflect.StructTag
}{
{
"Found tags",
&Metadata{
tags: map[string]string{
"test.has.tags": "cty:\"instance_tenancy\" computed:\"true\"",
},
},
[]string{"test", "has", "tags"},
reflect.StructTag("cty:\"instance_tenancy\" computed:\"true\""),
},
{
"No tags found",
&Metadata{
tags: map[string]string{
"test.has.tags": "cty:\"instance_tenancy\" computed:\"true\"",
},
},
[]string{"test", "has", "no", "tags"},
reflect.StructTag(""),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := &CtyAttributes{
Attrs: nil,
metadata: tt.metadata,
}
if got := a.Tags(tt.path); got != tt.want {
t.Errorf("Tags() = %v, want %v", got, tt.want)
}
})
}
}

func TestCtyAttributes_IsComputedField(t *testing.T) {
tests := []struct {
name string
metadata *Metadata
path []string
want bool
}{
{
"Is computed",
&Metadata{
tags: map[string]string{
"test.has.tags": "cty:\"instance_tenancy\" computed:\"true\"",
},
},
[]string{"test", "has", "tags"},
true,
},
{
"Not computed",
&Metadata{
tags: map[string]string{
"test.has.tags": "cty:\"instance_tenancy\"",
},
},
[]string{"test", "has", "tags"},
false,
},
{
"No tags",
&Metadata{
tags: map[string]string{
"test.has.tags": "cty:\"instance_tenancy\"",
},
},
[]string{"test", "has", "no", "tags"},
false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := &CtyAttributes{
Attrs: nil,
metadata: tt.metadata,
}
if got := a.IsComputedField(tt.path); got != tt.want {
t.Errorf("IsComputedField() = %v, want %v", got, tt.want)
}
})
}
}
Loading

0 comments on commit b58ebc0

Please sign in to comment.