Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 63 additions & 32 deletions pkg/job/maxdimassociator/associator.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,21 +174,42 @@ func (assoc Associator) AssociateMetricToResource(cwMetric *model.Metric) (*mode

// A regex mapping has been found. The metric has all (and possibly more)
// the dimensions computed for the mapping. Now compute a signature
// of the labels (names and values) of the dimensions of this mapping.
// of the labels (names and values) of the dimensions of this mapping, and try to
// find a resource match.
// This loop can run up to two times:
// On the first iteration, special-case dimension value
// fixes to match the value up with the resource ARN are applied to particular namespaces.
// The second iteration will only run if a fix was applied for one of the special-case
// namespaces and no match was found. It will try to find a match without applying the fixes.
// This covers cases where the dimension value does line up with the resource ARN.
mappingFound = true
labels := buildLabelsMap(cwMetric, regexpMapping)
signature := prom_model.LabelsToSignature(labels)
dimFixApplied := false
shouldTryFixDimension := true
for {
if !dimFixApplied && !shouldTryFixDimension {
// If no dimension fixes were applied, no need to try running again without the fixer
break
}

var labels map[string]string
labels, dimFixApplied = buildLabelsMap(cwMetric, regexpMapping, shouldTryFixDimension)
signature := prom_model.LabelsToSignature(labels)

// Check if there's an entry for the labels (names and values) of the metric,
// and return the resource in case.
if resource, ok := regexpMapping.dimensionsMapping[signature]; ok {
logger.Debug("resource matched", "signature", signature)
return resource, false
}

// Check if there's an entry for the labels (names and values) of the metric,
// and return the resource in case.
if resource, ok := regexpMapping.dimensionsMapping[signature]; ok {
logger.Debug("resource matched", "signature", signature)
return resource, false
// No resource was matched for the current signature.
logger.Debug("resource signature attempt not matched", "signature", signature)
shouldTryFixDimension = false
}

// Otherwise, continue iterating across the rest of regex mappings
// to attempt to find another one with fewer dimensions.
logger.Debug("resource not matched", "signature", signature)
// No resource was matched for any signature, continue iterating across the
// rest of regex mappings to attempt to find another one with fewer dimensions.
logger.Debug("resource not matched")
}
}

Expand All @@ -204,38 +225,48 @@ func (assoc Associator) AssociateMetricToResource(cwMetric *model.Metric) (*mode
return nil, mappingFound
}

// buildLabelsMap returns a map of labels names and values.
// buildLabelsMap returns a map of labels names and values, as well as whether the dimension fixer was applied.
// For some namespaces, values might need to be modified in order
// to match the dimension value extracted from ARN.
func buildLabelsMap(cwMetric *model.Metric, regexpMapping *dimensionsRegexpMapping) map[string]string {
func buildLabelsMap(cwMetric *model.Metric, regexpMapping *dimensionsRegexpMapping, shouldTryFixDimension bool) (map[string]string, bool) {
labels := make(map[string]string, len(cwMetric.Dimensions))
dimFixApplied := false
for _, rDimension := range regexpMapping.dimensions {
for _, mDimension := range cwMetric.Dimensions {
name := mDimension.Name
value := mDimension.Value

// AmazonMQ is special - for active/standby ActiveMQ brokers,
// the value of the "Broker" dimension contains a number suffix
// that is not part of the resource ARN
if cwMetric.Namespace == "AWS/AmazonMQ" && name == "Broker" {
if amazonMQBrokerSuffix.MatchString(value) {
value = amazonMQBrokerSuffix.ReplaceAllString(value, "")
}
}

// AWS Sagemaker endpoint name may have upper case characters
// Resource ARN is only in lower case, hence transforming
// endpoint name value to be able to match the resource ARN
if cwMetric.Namespace == "AWS/SageMaker" && name == "EndpointName" {
value = strings.ToLower(value)
if shouldTryFixDimension {
mDimension, dimFixApplied = fixDimension(cwMetric.Namespace, mDimension)
}

if rDimension == mDimension.Name {
labels[name] = value
labels[mDimension.Name] = mDimension.Value
}
}
}
return labels
return labels, dimFixApplied
}

// fixDimension modifies the dimension value to accommodate special cases where
// the dimension value doesn't match the resource ARN.
func fixDimension(namespace string, dim model.Dimension) (model.Dimension, bool) {
// AmazonMQ is special - for active/standby ActiveMQ brokers,
// the value of the "Broker" dimension contains a number suffix
// that is not part of the resource ARN
if namespace == "AWS/AmazonMQ" && dim.Name == "Broker" {
if amazonMQBrokerSuffix.MatchString(dim.Value) {
dim.Value = amazonMQBrokerSuffix.ReplaceAllString(dim.Value, "")
return dim, true
}
}

// AWS Sagemaker endpoint name may have upper case characters
// Resource ARN is only in lower case, hence transforming
// endpoint name value to be able to match the resource ARN
if namespace == "AWS/SageMaker" && dim.Name == "EndpointName" {
dim.Value = strings.ToLower(dim.Value)
return dim, true
}

return dim, false
}

// containsAll returns true if a contains all elements of b
Expand Down
23 changes: 22 additions & 1 deletion pkg/job/maxdimassociator/associator_mq_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ var rabbitMQBroker = &model.TaggedResource{
Namespace: "AWS/AmazonMQ",
}

var rabbitMQBrokerWithActiveStyleName = &model.TaggedResource{
ARN: "arn:aws:mq:us-east-2:123456789012:broker:rabbitmq-broker-0:b-000-111-222-333",
Namespace: "AWS/AmazonMQ",
}

var activeMQBroker = &model.TaggedResource{
ARN: "arn:aws:mq:us-east-2:123456789012:broker:activemq-broker:b-000-111-222-333",
Namespace: "AWS/AmazonMQ",
Expand Down Expand Up @@ -63,11 +68,27 @@ func TestAssociatorMQ(t *testing.T) {
expectedSkip: false,
expectedResource: rabbitMQBroker,
},
{
name: "should match with Broker dimension when broker name has a number suffix and does match ARN",
args: args{
dimensionRegexps: config.SupportedServices.GetService("AWS/AmazonMQ").ToModelDimensionsRegexp(),
resources: []*model.TaggedResource{rabbitMQBrokerWithActiveStyleName},
metric: &model.Metric{
MetricName: "ProducerCount",
Namespace: "AWS/AmazonMQ",
Dimensions: []model.Dimension{
{Name: "Broker", Value: "rabbitmq-broker-0"},
},
},
},
expectedSkip: false,
expectedResource: rabbitMQBrokerWithActiveStyleName,
},
{
// ActiveMQ allows active/standby modes where the `Broker` dimension has values
// like `brokername-1` and `brokername-2` which don't match the ARN (the dimension
// regex will extract `Broker` as `brokername` from ARN)
name: "should match with Broker dimension when broker name has a number suffix",
name: "should match with Broker dimension when broker name has a number suffix and doesn't match ARN",
args: args{
dimensionRegexps: config.SupportedServices.GetService("AWS/AmazonMQ").ToModelDimensionsRegexp(),
resources: []*model.TaggedResource{activeMQBroker},
Expand Down
Loading