Skip to content

Commit

Permalink
move cty manipulation to dctlcty
Browse files Browse the repository at this point in the history
  • Loading branch information
Martin Guibert committed Apr 6, 2021
1 parent 7518020 commit 15e8699
Show file tree
Hide file tree
Showing 57 changed files with 482 additions and 180 deletions.
22 changes: 3 additions & 19 deletions pkg/analyser/analyzer.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
package analyser

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

"github.com/zclconf/go-cty/cty"
ctyjson "github.com/zclconf/go-cty/cty/json"

"github.com/cloudskiff/driftctl/pkg/dctlcty"
resourceaws "github.com/cloudskiff/driftctl/pkg/resource/aws"

"github.com/r3labs/diff/v2"
Expand Down Expand Up @@ -88,8 +85,8 @@ func (a Analyzer) Analyze(remoteResources, resourcesFromState []resource.Resourc
filteredRemoteResource = removeResourceByIndex(i, filteredRemoteResource)
analysis.AddManaged(stateRes)

state := a.getAsAttrs(stateRes.CtyValue())
rem := a.getAsAttrs(remoteRes.CtyValue())
state := dctlcty.AsAttrs(stateRes.CtyValue())
rem := dctlcty.AsAttrs(remoteRes.CtyValue())

normalize, exists := normalizers[stateRes.TerraformType()]
if exists {
Expand Down Expand Up @@ -210,16 +207,3 @@ func (a Analyzer) hasUnmanagedSecurityGroupRules(unmanagedResources []resource.R
}
return false
}

func (a *Analyzer) getAsAttrs(val *cty.Value) map[string]interface{} {
if val == nil {
return nil
}
bytes, _ := ctyjson.Marshal(*val, val.Type())
var attrs map[string]interface{}
err := json.Unmarshal(bytes, &attrs)
if err != nil {
panic(err)
}
return attrs
}
68 changes: 68 additions & 0 deletions pkg/dctlcty/cty_attribute.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package dctlcty

import (
"encoding/json"
"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 {
if val == nil {
return nil
}
bytes, _ := ctyjson.Marshal(*val, val.Type())
var attrs map[string]interface{}
err := json.Unmarshal(bytes, &attrs)
if err != nil {
panic(err)
}
return attrs
}

type CtyAttributes map[string]interface{}

func (a *CtyAttributes) SafeDelete(path []string) {
val := *a
for i, key := range path {
if i == len(path)-1 {
delete(val, key)
return
}

v, exists := val[key]
if !exists {
return
}
m, ok := v.(map[string]interface{})
if !ok {
return
}
val = m
}
}

func (a *CtyAttributes) SafeSet(path []string, value interface{}) error {
val := *a
for i, key := range path {
if i == len(path)-1 {
val[key] = value
return nil
}

v, exists := val[key]
if !exists {
val[key] = map[string]interface{}{}
v = val[key]
}

m, ok := v.(map[string]interface{})
if !ok {
return errors.Errorf("Path %s cannot be set: %s is not a nested struct", strings.Join(path, "."), key)
}
val = m
}
return errors.New("Error setting value") // should not happen ?
}
214 changes: 214 additions & 0 deletions pkg/dctlcty/cty_attribute_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
package dctlcty

import (
"testing"

"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)

func TestCtyAttributes_SafeDelete(t *testing.T) {
tests := []struct {
name string
attr CtyAttributes
path []string
expected CtyAttributes
}{
{
name: "Delete existing",
attr: map[string]interface{}{
"test": "exists",
"nested": map[string]interface{}{
"testNested": "exists",
},
},
path: []string{"test"},
expected: map[string]interface{}{
"nested": map[string]interface{}{
"testNested": "exists",
},
},
},
{
name: "Delete existing nested",
attr: map[string]interface{}{
"test": "exists",
"nested": map[string]interface{}{
"testNested": "exists",
},
},
path: []string{"nested", "testNested"},
expected: map[string]interface{}{
"test": "exists",

"nested": map[string]interface{}{},
},
},
{
name: "Delete not existing",
attr: map[string]interface{}{
"test": "exists",
"nested": map[string]interface{}{
"testNested": "exists",
},
},
path: []string{"test1"},
expected: map[string]interface{}{
"test": "exists",
"nested": map[string]interface{}{
"testNested": "exists",
},
},
},
{
name: "Delete not existing nested",
attr: map[string]interface{}{
"test": "exists",
"nested": map[string]interface{}{
"testNested": "exists",
},
},
path: []string{"nested", "testNest"},
expected: map[string]interface{}{
"test": "exists",
"nested": map[string]interface{}{
"testNested": "exists",
},
},
},
{
name: "Delete not real nested",
attr: map[string]interface{}{
"test": "exists",
"nested": map[string]interface{}{
"testNested": "exists",
},
},
path: []string{"test", "testNest"},
expected: map[string]interface{}{
"test": "exists",
"nested": map[string]interface{}{
"testNested": "exists",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.attr.SafeDelete(tt.path)
assert.Equal(t, tt.expected, tt.attr)
})
}
}

func TestCtyAttributes_SafeSet(t *testing.T) {
tests := []struct {
name string
attr CtyAttributes
path []string
value interface{}
expected CtyAttributes
error error
}{
{
name: "set existing",
attr: map[string]interface{}{
"test": "exists",
"nested": map[string]interface{}{
"testNested": "exists",
},
},
path: []string{"test"},
value: "CHANGED",
expected: map[string]interface{}{
"test": "CHANGED",
"nested": map[string]interface{}{
"testNested": "exists",
},
},
},
{
name: "set existing nested",
attr: map[string]interface{}{
"test": "exists",
"nested": map[string]interface{}{
"testNested": "exists",
},
},
path: []string{"nested", "testNested"},
value: "CHANGED",
expected: map[string]interface{}{
"test": "exists",
"nested": map[string]interface{}{
"testNested": "CHANGED",
},
},
},
{
name: "Set not existing",
attr: map[string]interface{}{
"test": "exists",
"nested": map[string]interface{}{
"testNested": "exists",
},
},
path: []string{"test1"},
value: "SET",
expected: map[string]interface{}{
"test1": "SET",
"test": "exists",
"nested": map[string]interface{}{
"testNested": "exists",
},
},
},
{
name: "SET not existing nested",
attr: map[string]interface{}{
"test": "exists",
"nested": map[string]interface{}{
"testNested": "exists",
},
},
path: []string{"nested", "testNest"},
value: "SET",
expected: map[string]interface{}{
"test": "exists",
"nested": map[string]interface{}{
"testNested": "exists",
"testNest": "SET",
},
},
},
{
name: "Delete not real nested",
attr: map[string]interface{}{
"test": "exists",
"nested": map[string]interface{}{
"testNested": "exists",
},
},
path: []string{"test", "testNest"},
value: "NOK",
expected: map[string]interface{}{
"test": "exists",
"nested": map[string]interface{}{
"testNested": "exists",
},
},
error: errors.New("Path test.testNest cannot be set: test is not a nested struct"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.attr.SafeSet(tt.path, tt.value)
if tt.error != nil {
assert.NotNil(t, err)
assert.Equal(t, tt.error.Error(), err.Error())
} else {
assert.Nil(t, err)
}
assert.Equal(t, tt.expected, tt.attr)
})
}
}
14 changes: 0 additions & 14 deletions pkg/helpers/normalizers.go

This file was deleted.

6 changes: 3 additions & 3 deletions pkg/resource/aws/aws_ami.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package aws
import (
"github.com/zclconf/go-cty/cty"

"github.com/cloudskiff/driftctl/pkg/helpers"
"github.com/cloudskiff/driftctl/pkg/dctlcty"
)

const AwsAmiResourceType = "aws_ami"
Expand Down Expand Up @@ -58,6 +58,6 @@ func (r *AwsAmi) CtyValue() *cty.Value {
return r.CtyVal
}

func awsAmiNormalizer(val *map[string]interface{}) {
helpers.SafeDelete(val, []string{"timeouts"})
func awsAmiNormalizer(val *dctlcty.CtyAttributes) {
val.SafeDelete([]string{"timeouts"})
}
14 changes: 7 additions & 7 deletions pkg/resource/aws/aws_cloudfront_distribution.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package aws
import (
"github.com/zclconf/go-cty/cty"

"github.com/cloudskiff/driftctl/pkg/helpers"
"github.com/cloudskiff/driftctl/pkg/dctlcty"
)

const AwsCloudfrontDistributionResourceType = "aws_cloudfront_distribution"
Expand Down Expand Up @@ -160,10 +160,10 @@ func (r *AwsCloudfrontDistribution) CtyValue() *cty.Value {
return r.CtyVal
}

func awsCloudfrontDistributionNormalizer(val *map[string]interface{}) {
helpers.SafeDelete(val, []string{"etag"})
helpers.SafeDelete(val, []string{"last_modified_time"})
helpers.SafeDelete(val, []string{"retain_on_delete"})
helpers.SafeDelete(val, []string{"status"})
helpers.SafeDelete(val, []string{"wait_for_deployment"})
func awsCloudfrontDistributionNormalizer(val *dctlcty.CtyAttributes) {
val.SafeDelete([]string{"etag"})
val.SafeDelete([]string{"last_modified_time"})
val.SafeDelete([]string{"retain_on_delete"})
val.SafeDelete([]string{"status"})
val.SafeDelete([]string{"wait_for_deployment"})
}
Loading

0 comments on commit 15e8699

Please sign in to comment.