Cloudwatch: Fix parsing for ec2 resource attributes (#96501)

This commit is contained in:
Isabella Siu 2024-11-15 15:17:50 -05:00 committed by GitHub
parent 420db99d16
commit fcd88d356c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 133 additions and 31 deletions

View File

@ -97,39 +97,13 @@ func (e *cloudWatchExecutor) handleGetEc2InstanceAttribute(ctx context.Context,
dupCheck := make(map[string]bool)
for _, reservation := range instances.Reservations {
for _, instance := range reservation.Instances {
tags := make(map[string]string)
for _, tag := range instance.Tags {
tags[*tag.Key] = *tag.Value
data, found, err := getInstanceAttributeValue(attributeName, instance)
if err != nil {
return nil, err
}
var data string
if strings.Index(attributeName, "Tags.") == 0 {
tagName := attributeName[5:]
data = tags[tagName]
} else {
attributePath := strings.Split(attributeName, ".")
v := reflect.ValueOf(instance)
for _, key := range attributePath {
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() != reflect.Struct {
return nil, errors.New("invalid attribute path")
}
v = v.FieldByName(key)
if !v.IsValid() {
return nil, errors.New("invalid attribute path")
}
}
if attr, ok := v.Interface().(*string); ok {
data = *attr
} else if attr, ok := v.Interface().(*time.Time); ok {
data = attr.String()
} else {
return nil, errors.New("invalid attribute path")
}
if !found {
continue
}
if _, exists := dupCheck[data]; exists {
continue
}
@ -146,6 +120,54 @@ func (e *cloudWatchExecutor) handleGetEc2InstanceAttribute(ctx context.Context,
return result, nil
}
func getInstanceAttributeValue(attributeName string, instance *ec2.Instance) (value string, found bool, err error) {
tags := make(map[string]string)
for _, tag := range instance.Tags {
tags[*tag.Key] = *tag.Value
}
var data string
if strings.Index(attributeName, "Tags.") == 0 {
tagName := attributeName[5:]
data = tags[tagName]
} else {
attributePath := strings.Split(attributeName, ".")
v := reflect.ValueOf(instance)
for _, key := range attributePath {
if v.Kind() == reflect.Ptr {
if v.IsNil() {
return "", false, nil
}
v = v.Elem()
}
if v.Kind() != reflect.Struct {
return "", false, errors.New("invalid attribute path")
}
v = v.FieldByName(key)
if !v.IsValid() {
return "", false, errors.New("invalid attribute path")
}
}
if v.Kind() == reflect.Ptr && v.IsNil() {
return "", false, nil
}
if attr, ok := v.Interface().(*string); ok {
data = *attr
} else if attr, ok := v.Interface().(*time.Time); ok {
data = attr.String()
} else if _, ok := v.Interface().(*bool); ok {
data = fmt.Sprint(v.Elem().Bool())
} else if v.Kind() == reflect.Ptr && v.Elem().CanInt() {
data = fmt.Sprint(v.Elem().Int())
} else {
return "", false, errors.New("cannot parse attribute")
}
}
return data, true, nil
}
func (e *cloudWatchExecutor) handleGetResourceArns(ctx context.Context, pluginCtx backend.PluginContext, parameters url.Values) ([]suggestData, error) {
region := parameters.Get("region")
resourceType := parameters.Get("resourceType")

View File

@ -80,6 +80,86 @@ func TestQuery_InstanceAttributes(t *testing.T) {
}
assert.Equal(t, expResponse, resp)
})
t.Run("Get different types", func(t *testing.T) {
var expectedInt int64 = 3
var expectedBool = true
var expectedArn = "arn"
cli = oldEC2Client{
reservations: []*ec2.Reservation{
{
Instances: []*ec2.Instance{
{
AmiLaunchIndex: &expectedInt,
EbsOptimized: &expectedBool,
IamInstanceProfile: &ec2.IamInstanceProfile{
Arn: &expectedArn,
},
},
},
},
},
}
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil
})
executor := newExecutor(im, log.NewNullLogger())
testcases := []struct {
name string
attributeName string
expResponse []suggestData
}{
{
"int field",
"AmiLaunchIndex",
[]suggestData{
{Text: "3", Value: "3", Label: "3"},
},
},
{
"bool field",
"EbsOptimized",
[]suggestData{
{Text: "true", Value: "true", Label: "true"},
},
},
{
"nested field",
"IamInstanceProfile.Arn",
[]suggestData{
{Text: expectedArn, Value: expectedArn, Label: expectedArn},
},
},
{
"nil field",
"InstanceLifecycle",
[]suggestData{},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
filterMap := map[string][]string{}
filterJson, err := json.Marshal(filterMap)
require.NoError(t, err)
resp, err := executor.handleGetEc2InstanceAttribute(
context.Background(),
backend.PluginContext{
DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{},
}, url.Values{
"region": []string{"us-east-1"},
"attributeName": []string{tc.attributeName},
"filters": []string{string(filterJson)},
},
)
require.NoError(t, err)
assert.Equal(t, tc.expResponse, resp)
})
}
})
}
func TestQuery_EBSVolumeIDs(t *testing.T) {