Remove human readable func from resource

main
Elie 2021-06-02 13:56:18 +02:00
parent 3a544133e2
commit a373bb0744
No known key found for this signature in database
GPG Key ID: 399AF69092C727B6
6 changed files with 54 additions and 575 deletions

View File

@ -5,6 +5,7 @@ import (
"fmt"
"os"
"reflect"
"sort"
"strings"
"github.com/aws/aws-sdk-go/aws/awsutil"
@ -15,7 +16,6 @@ import (
"github.com/yudai/gojsondiff/formatter"
"github.com/cloudskiff/driftctl/pkg/analyser"
"github.com/cloudskiff/driftctl/pkg/output"
"github.com/cloudskiff/driftctl/pkg/remote"
"github.com/cloudskiff/driftctl/pkg/resource"
)
@ -41,10 +41,8 @@ func (c *Console) Write(analysis *analyser.Analysis) error {
fmt.Printf(" %s:\n", ty)
for _, res := range resources {
humanString := fmt.Sprintf(" - %s", res.TerraformId())
if humanizerRes, ok := res.(output.AttributesGetter); ok {
if humanAttrs := output.HumanizeAttribute(humanizerRes); humanAttrs != "" {
humanString += fmt.Sprintf("\n %s", humanAttrs)
}
if humanAttrs := formatResourceAttributes(res); humanAttrs != "" {
humanString += fmt.Sprintf("\n %s", humanAttrs)
}
fmt.Println(humanString)
}
@ -58,10 +56,8 @@ func (c *Console) Write(analysis *analyser.Analysis) error {
fmt.Printf(" %s:\n", ty)
for _, res := range resource {
humanString := fmt.Sprintf(" - %s", res.TerraformId())
if humanizerRes, ok := res.(output.AttributesGetter); ok {
if humanAttrs := output.HumanizeAttribute(humanizerRes); humanAttrs != "" {
humanString += fmt.Sprintf("\n %s", humanAttrs)
}
if humanAttrs := formatResourceAttributes(res); humanAttrs != "" {
humanString += fmt.Sprintf("\n %s", humanAttrs)
}
fmt.Println(humanString)
}
@ -73,11 +69,9 @@ func (c *Console) Write(analysis *analyser.Analysis) error {
for _, difference := range analysis.Differences() {
humanString := fmt.Sprintf(" - %s (%s):", difference.Res.TerraformId(), difference.Res.TerraformType())
whiteSpace := " "
if humanizerRes, ok := difference.Res.(output.AttributesGetter); ok {
if humanAttrs := output.HumanizeAttribute(humanizerRes); humanAttrs != "" {
humanString += fmt.Sprintf("\n %s", humanAttrs)
whiteSpace = " "
}
if humanAttrs := formatResourceAttributes(difference.Res); humanAttrs != "" {
humanString += fmt.Sprintf("\n %s", humanAttrs)
whiteSpace = " "
}
fmt.Println(humanString)
for _, change := range difference.Changelog {
@ -211,3 +205,28 @@ func jsonDiff(a, b interface{}, prefix string) string {
return diffStr
}
func formatResourceAttributes(res resource.Resource) string {
if res.Schema() == nil || res.Schema().HumanReadableAttributesFunc == nil {
return ""
}
attributes := res.Schema().HumanReadableAttributesFunc(res.(*resource.AbstractResource))
if len(attributes) <= 0 {
return ""
}
// sort attributes
keys := make([]string, 0, len(attributes))
for k := range attributes {
keys = append(keys, k)
}
sort.Strings(keys)
// retrieve stringer
attrString := ""
for _, k := range keys {
if attrString != "" {
attrString += ", "
}
attrString += fmt.Sprintf("%s: %s", k, attributes[k])
}
return attrString
}

View File

@ -160,32 +160,45 @@ func fakeAnalysisWithoutAttrs() *analyser.Analysis {
func fakeAnalysisWithStringerResources() *analyser.Analysis {
a := analyser.Analysis{}
fakeResourceStringerSchema := &resource.Schema{HumanReadableAttributesFunc: func(res *resource.AbstractResource) map[string]string {
return map[string]string{
"Name": (*res.Attrs)["name"].(string),
}
}}
a.AddDeleted(
&testresource.FakeResourceStringer{
Id: "dfjkgnbsgj",
&resource.AbstractResource{
Id: "dfjkgnbsgj",
Type: "FakeResourceStringer",
Sch: fakeResourceStringerSchema,
Attrs: &resource.Attributes{
"name": "deleted resource",
},
},
)
a.AddManaged(
&testresource.FakeResourceStringer{
Id: "usqyfsdbgjsdgjkdfg",
&resource.AbstractResource{
Id: "usqyfsdbgjsdgjkdfg",
Type: "FakeResourceStringer",
Sch: fakeResourceStringerSchema,
Attrs: &resource.Attributes{
"name": "managed resource",
},
},
)
a.AddUnmanaged(
&testresource.FakeResourceStringer{
Id: "duysgkfdjfdgfhd",
&resource.AbstractResource{
Id: "duysgkfdjfdgfhd",
Type: "FakeResourceStringer",
Sch: fakeResourceStringerSchema,
Attrs: &resource.Attributes{
"name": "unmanaged resource",
},
},
)
a.AddDifference(analyser.Difference{Res: &testresource.FakeResourceStringer{
Id: "gdsfhgkbn",
a.AddDifference(analyser.Difference{Res: &resource.AbstractResource{
Id: "gdsfhgkbn",
Type: "FakeResourceStringer",
Sch: fakeResourceStringerSchema,
Attrs: &resource.Attributes{
"name": "resource with diff",
},

View File

@ -1,32 +0,0 @@
package output
import (
"fmt"
"sort"
)
type AttributesGetter interface {
HumanReadableAttributes() map[string]string
}
func HumanizeAttribute(res AttributesGetter) string {
attributes := res.HumanReadableAttributes()
if len(attributes) <= 0 {
return ""
}
// sort attributes
keys := make([]string, 0, len(attributes))
for k := range attributes {
keys = append(keys, k)
}
sort.Strings(keys)
// retrieve stringer
attrString := ""
for _, k := range keys {
if attrString != "" {
attrString += ", "
}
attrString += fmt.Sprintf("%s: %s", k, attributes[k])
}
return attrString
}

View File

@ -1,504 +0,0 @@
package output
import (
"testing"
"github.com/cloudskiff/driftctl/pkg/resource"
"github.com/cloudskiff/driftctl/pkg/resource/aws"
"github.com/cloudskiff/driftctl/pkg/resource/github"
testresource "github.com/cloudskiff/driftctl/test/resource"
)
func TestHumanizeAttribute_AWS(t *testing.T) {
tests := []struct {
name string
res AttributesGetter
want string
}{
{
name: "test empty iam_access_key",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsIamAccessKeyResourceType,
Attrs: &resource.Attributes{},
},
want: "",
},
{
name: "test valid iam_access_key",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsIamAccessKeyResourceType,
Attrs: &resource.Attributes{
"user": "foo",
},
},
want: "User: foo",
},
{
name: "test empty aws_instance",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsInstanceResourceType,
Attrs: &resource.Attributes{},
},
want: "",
},
{
name: "test valid aws_instance",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsInstanceResourceType,
Attrs: &resource.Attributes{
"tags": map[string]interface{}{
"name": "foo",
},
},
},
want: "Name: foo",
},
{
name: "test empty aws_lambda_event_source_mapping",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsLambdaEventSourceMappingResourceType,
Attrs: &resource.Attributes{},
},
want: "",
},
{
name: "test with source aws_lambda_event_source_mapping",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsLambdaEventSourceMappingResourceType,
Attrs: &resource.Attributes{
"event_source_arn": "source-arn",
},
},
want: "",
},
{
name: "test with source and dest aws_lambda_event_source_mapping",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsLambdaEventSourceMappingResourceType,
Attrs: &resource.Attributes{
"event_source_arn": "source-arn",
"function_name": "function-name",
},
},
want: "Dest: function-name, Source: source-arn",
},
{
name: "test empty aws_route",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsRouteResourceType,
Attrs: &resource.Attributes{},
},
want: "",
},
{
name: "test with no destination aws_route",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsRouteResourceType,
Attrs: &resource.Attributes{
"route_table_id": "table-id",
},
},
want: "Table: table-id",
},
{
name: "test with ipv4 destination aws_route",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsRouteResourceType,
Attrs: &resource.Attributes{
"destination_cidr_block": "0.0.0.0/0",
"route_table_id": "table-id",
},
},
want: "Destination: 0.0.0.0/0, Table: table-id",
},
{
name: "test with ipv6 destination aws_route",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsRouteResourceType,
Attrs: &resource.Attributes{
"destination_ipv6_cidr_block": "::/0",
"route_table_id": "table-id",
},
},
want: "Destination: ::/0, Table: table-id",
},
{
name: "test empty aws_route53_health_check",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsRoute53HealthCheckResourceType,
Attrs: &resource.Attributes{},
},
want: "",
},
{
name: "test with name tag, fqdn and resource path aws_route53_health_check",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsRoute53HealthCheckResourceType,
Attrs: &resource.Attributes{
"tags": map[string]interface{}{
"name": "foo",
},
"fqdn": "fq.dn",
"resource_path": "/toto",
},
},
want: "Fqdn: fq.dn, Name: foo, Path: /toto",
},
{
name: "test with ip and port aws_route53_health_check",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsRoute53HealthCheckResourceType,
Attrs: &resource.Attributes{
"tags": map[string]interface{}{
"name": "foo",
},
"ip_address": "10.0.0.10",
"port": float64(443),
},
},
want: "IpAddress: 10.0.0.10, Name: foo, Port: 443",
},
{
name: "test with ip, port and resource path aws_route53_health_check",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsRoute53HealthCheckResourceType,
Attrs: &resource.Attributes{
"tags": map[string]interface{}{
"name": "foo",
},
"ip_address": "10.0.0.10",
"port": float64(443),
"resource_path": "/toto",
},
},
want: "IpAddress: 10.0.0.10, Name: foo, Path: /toto, Port: 443",
},
{
name: "test empty aws_route53_record",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsRoute53RecordResourceType,
Attrs: &resource.Attributes{},
},
want: "",
},
{
name: "test with fqdn, type and zoneId aws_route53_record",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsRoute53RecordResourceType,
Attrs: &resource.Attributes{
"fqdn": "_github-challenge-cloudskiff.cloudskiff.com",
"type": "TXT",
"zone_id": "ZOS30SFDAFTU9",
},
},
want: "Fqdn: _github-challenge-cloudskiff.cloudskiff.com, Type: TXT, ZoneId: ZOS30SFDAFTU9",
},
{
name: "test empty aws_route53_zone",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsRoute53ZoneResourceType,
Attrs: &resource.Attributes{},
},
want: "",
},
{
name: "test with name aws_route53_zone",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsRoute53ZoneResourceType,
Attrs: &resource.Attributes{
"name": "example.com",
},
},
want: "Name: example.com",
},
{
name: "test empty aws_route_table_association",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsRouteTableAssociationResourceType,
Attrs: &resource.Attributes{},
},
want: "",
},
{
name: "test with gateway aws_route_table_association",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsRouteTableAssociationResourceType,
Attrs: &resource.Attributes{
"route_table_id": "table-id",
"gateway_id": "gtw-id",
},
},
want: "Gateway: gtw-id, Table: table-id",
},
{
name: "test with subnet aws_route_table_association",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsRouteTableAssociationResourceType,
Attrs: &resource.Attributes{
"route_table_id": "table-id",
"subnet_id": "subnet-id",
},
},
want: "Subnet: subnet-id, Table: table-id",
},
{
name: "test empty aws_security_group_rule",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsSecurityGroupRuleResourceType,
Attrs: &resource.Attributes{},
},
want: "",
},
{
name: "test ingress_ssh_ipv4 aws_security_group_rule",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsSecurityGroupRuleResourceType,
Attrs: &resource.Attributes{
"security_group_id": "sg-12345",
"type": "ingress",
"protocol": "tcp",
"from_port": float64(22),
"to_port": float64(22),
"cidr_blocks": []interface{}{"0.0.0.0/0", "1.2.3.4/32"},
},
},
want: "Ports: 22, Protocol: tcp, SecurityGroup: sg-12345, Source: 0.0.0.0/0, 1.2.3.4/32, Type: ingress",
},
{
name: "test egress_ssh_ipv4 aws_security_group_rule",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsSecurityGroupRuleResourceType,
Attrs: &resource.Attributes{
"security_group_id": "sg-12345",
"type": "egress",
"protocol": "tcp",
"from_port": float64(22),
"to_port": float64(22),
"cidr_blocks": []interface{}{"0.0.0.0/0", "1.2.3.4/32"},
},
},
want: "Destination: 0.0.0.0/0, 1.2.3.4/32, Ports: 22, Protocol: tcp, SecurityGroup: sg-12345, Type: egress",
},
{
name: "test ingress_all aws_security_group_rule",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsSecurityGroupRuleResourceType,
Attrs: &resource.Attributes{
"security_group_id": "sg-12345",
"type": "ingress",
"protocol": "-1",
},
},
want: "Protocol: All, SecurityGroup: sg-12345, Type: ingress",
},
{
name: "test ingress_all_range_0 aws_security_group_rule",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsSecurityGroupRuleResourceType,
Attrs: &resource.Attributes{
"security_group_id": "sg-12345",
"type": "ingress",
"protocol": "-1",
"from_port": float64(0),
"to_port": float64(0),
},
},
want: "Ports: All, Protocol: All, SecurityGroup: sg-12345, Type: ingress",
},
{
name: "test ingress_all_ipv6 aws_security_group_rule",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsSecurityGroupRuleResourceType,
Attrs: &resource.Attributes{
"security_group_id": "sg-12345",
"type": "ingress",
"protocol": "-1",
"ipv6_cidr_blocks": []interface{}{"::/0"},
},
},
want: "Protocol: All, SecurityGroup: sg-12345, Source: ::/0, Type: ingress",
},
{
name: "test ingress_all_prefix aws_security_group_rule",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsSecurityGroupRuleResourceType,
Attrs: &resource.Attributes{
"security_group_id": "sg-12345",
"type": "ingress",
"protocol": "-1",
"prefix_list_ids": []interface{}{"pl-12345"},
},
},
want: "Protocol: All, SecurityGroup: sg-12345, Source: pl-12345, Type: ingress",
},
{
name: "test egress_all_source aws_security_group_rule",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsSecurityGroupRuleResourceType,
Attrs: &resource.Attributes{
"security_group_id": "sg-12345",
"type": "egress",
"protocol": "all",
"source_security_group_id": "sg-67890",
},
},
want: "Destination: sg-67890, Protocol: all, SecurityGroup: sg-12345, Type: egress",
},
{
name: "test empty aws_sns_topic",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsSnsTopicResourceType,
Attrs: &resource.Attributes{},
},
want: "",
},
{
name: "test with name and display name aws_sns_topic",
res: &resource.AbstractResource{
Id: "foo",
Type: aws.AwsSnsTopicResourceType,
Attrs: &resource.Attributes{
"name": "foo",
"display_name": "bar",
},
},
want: "DisplayName: bar, Name: foo",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
repo := testresource.InitFakeSchemaRepository("aws", "3.19.0")
aws.InitResourcesMetadata(repo)
abstractResource, ok := tt.res.(*resource.AbstractResource)
if ok {
schema, _ := repo.GetSchema(abstractResource.TerraformType())
abstractResource.Sch = schema
}
if got := HumanizeAttribute(tt.res); got != tt.want {
t.Errorf("HumanizeAttribute() = %v, want %v", got, tt.want)
}
})
}
}
func TestHumanizeAttribute_Github(t *testing.T) {
tests := []struct {
name string
res AttributesGetter
want string
}{
{
name: "test empty github_branch_protection",
res: &resource.AbstractResource{
Id: "foo",
Type: github.GithubBranchProtectionResourceType,
Attrs: &resource.Attributes{},
},
want: "Id: foo",
},
{
name: "test with pattern github_branch_protection",
res: &resource.AbstractResource{
Id: "foo",
Type: github.GithubBranchProtectionResourceType,
Attrs: &resource.Attributes{
"pattern": "my-branch",
},
},
want: "Branch: my-branch, Id: foo",
},
{
name: "test with pattern and invalid base64 repo_id github_branch_protection",
res: &resource.AbstractResource{
Id: "foo",
Type: github.GithubBranchProtectionResourceType,
Attrs: &resource.Attributes{
"pattern": "my-branch",
"repository_id": "invalid",
},
},
want: "Branch: my-branch, Id: foo",
},
{
name: "test with pattern and valid base64 repo_id github_branch_protection",
res: &resource.AbstractResource{
Id: "foo",
Type: github.GithubBranchProtectionResourceType,
Attrs: &resource.Attributes{
"pattern": "my-branch",
"repository_id": "MDEwOlJlcG9zaXRvcnkxMjM0NTY=",
},
},
want: "Branch: my-branch, RepoId: 010:Repository123456",
},
{
name: "test empty github_team",
res: &resource.AbstractResource{
Id: "foo",
Type: github.GithubTeamResourceType,
Attrs: &resource.Attributes{},
},
want: "Id: foo",
},
{
name: "test with name github_team",
res: &resource.AbstractResource{
Id: "foo",
Type: github.GithubTeamResourceType,
Attrs: &resource.Attributes{
"name": "my-org-name",
},
},
want: "Id: foo, Name: my-org-name",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
repo := testresource.InitFakeSchemaRepository("github", "4.4.0")
github.InitResourcesMetadata(repo)
abstractResource, ok := tt.res.(*resource.AbstractResource)
if ok {
schema, _ := repo.GetSchema(abstractResource.TerraformType())
abstractResource.Sch = schema
}
if got := HumanizeAttribute(tt.res); got != tt.want {
t.Errorf("HumanizeAttribute() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -40,15 +40,6 @@ func (a *AbstractResource) Attributes() *Attributes {
return a.Attrs
}
func (a *AbstractResource) HumanReadableAttributes() map[string]string {
var attrs map[string]string
schema := a.Schema()
if schema.HumanReadableAttributesFunc != nil {
attrs = schema.HumanReadableAttributesFunc(a)
}
return attrs
}
type ResourceFactory interface {
CreateAbstractResource(ty, id string, data map[string]interface{}) *AbstractResource
}

View File

@ -40,14 +40,6 @@ func (d *FakeResourceStringer) Schema() *resource.Schema {
return nil
}
func (d *FakeResourceStringer) HumanReadableAttributes() map[string]string {
attrs := make(map[string]string)
if name := d.Attributes().GetString("name"); name != nil && *name != "" {
attrs["Name"] = *name
}
return attrs
}
func (d *FakeResourceStringer) TerraformId() string {
return d.Id
}