package awsinv import ( "context" "errors" "strings" "testing" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ec2" ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/carlosframework/platform/internal/infraview" ) // fakeEC2 answers whatever the test hands it; a non-nil err field for a // call makes that ONE call fail, which is how the partial-failure rule // below is proven. type fakeEC2 struct { instances *ec2.DescribeInstancesOutput volumes *ec2.DescribeVolumesOutput addresses *ec2.DescribeAddressesOutput groups *ec2.DescribeSecurityGroupsOutput vpcs *ec2.DescribeVpcsOutput subnets *ec2.DescribeSubnetsOutput regions *ec2.DescribeRegionsOutput errVols error } func (f *fakeEC2) DescribeInstances(context.Context, *ec2.DescribeInstancesInput, ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error) { return f.instances, nil } func (f *fakeEC2) DescribeVolumes(context.Context, *ec2.DescribeVolumesInput, ...func(*ec2.Options)) (*ec2.DescribeVolumesOutput, error) { if f.errVols != nil { return nil, f.errVols } return f.volumes, nil } func (f *fakeEC2) DescribeAddresses(context.Context, *ec2.DescribeAddressesInput, ...func(*ec2.Options)) (*ec2.DescribeAddressesOutput, error) { return f.addresses, nil } func (f *fakeEC2) DescribeSecurityGroups(context.Context, *ec2.DescribeSecurityGroupsInput, ...func(*ec2.Options)) (*ec2.DescribeSecurityGroupsOutput, error) { return f.groups, nil } func (f *fakeEC2) DescribeVpcs(context.Context, *ec2.DescribeVpcsInput, ...func(*ec2.Options)) (*ec2.DescribeVpcsOutput, error) { return f.vpcs, nil } func (f *fakeEC2) DescribeSubnets(context.Context, *ec2.DescribeSubnetsInput, ...func(*ec2.Options)) (*ec2.DescribeSubnetsOutput, error) { return f.subnets, nil } func (f *fakeEC2) DescribeRegions(context.Context, *ec2.DescribeRegionsInput, ...func(*ec2.Options)) (*ec2.DescribeRegionsOutput, error) { return f.regions, nil } func find(rs []infraview.Resource, kind, id string) *infraview.Resource { for i := range rs { if rs[i].Kind == kind && rs[i].ID == id { return &rs[i] } } return nil } func TestEC2InstanceMapping(t *testing.T) { f := &fakeEC2{instances: &ec2.DescribeInstancesOutput{ Reservations: []ec2types.Reservation{{Instances: []ec2types.Instance{{ InstanceId: aws.String("i-123"), InstanceType: ec2types.InstanceTypeT4gMedium, State: &ec2types.InstanceState{Name: ec2types.InstanceStateNameRunning}, Placement: &ec2types.Placement{AvailabilityZone: aws.String("eu-west-1a")}, PublicIpAddress: aws.String("52.0.0.1"), PrivateIpAddress: aws.String("10.0.0.5"), Tags: []ec2types.Tag{{Key: aws.String("Name"), Value: aws.String("carlos-eu-west-1")}}, }}}}, }} rs, partial := ec2Resources(context.Background(), f, "eu-west-1") if len(partial) != 0 { t.Fatalf("partial = %v, want none", partial) } m := find(rs, infraview.KindMachine, "i-123") if m == nil { t.Fatalf("no machine in %+v", rs) } if m.Name != "carlos-eu-west-1" { t.Errorf("Name = %q, want the Name tag", m.Name) } if m.Region != "eu-west-1" || m.NativeType != "ec2:instance" { t.Errorf("region/type = %q/%q", m.Region, m.NativeType) } for k, want := range map[string]string{ "type": "t4g.medium", "state": "running", "az": "eu-west-1a", "public_ip": "52.0.0.1", "private_ip": "10.0.0.5", } { if got := m.Attr(k); got != want { t.Errorf("attr %s = %q, want %q", k, got, want) } } } // One failing call costs its own resources and nothing else — the rule the // whole inventory depends on, since a page that shows eleven of twelve // classes is worth far more than one that shows none. func TestEC2PartialFailureCostsOnlyItsOwnCall(t *testing.T) { f := &fakeEC2{ instances: &ec2.DescribeInstancesOutput{Reservations: []ec2types.Reservation{{ Instances: []ec2types.Instance{{InstanceId: aws.String("i-123")}}, }}}, errVols: errors.New("Throttling: rate exceeded"), } rs, partial := ec2Resources(context.Background(), f, "eu-west-1") if find(rs, infraview.KindMachine, "i-123") == nil { t.Fatalf("a failed volume read must not cost the instances") } if len(partial) != 1 { t.Fatalf("partial = %v, want exactly the volumes line", partial) } if !strings.Contains(partial[0], "volumes") || !strings.Contains(partial[0], "eu-west-1") { t.Errorf("partial line %q must name what failed and where", partial[0]) } }