Skip to content
Open
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
108 changes: 108 additions & 0 deletions custom-metrics-stackdriver-adapter/docs/cross-project.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Fetching Metrics from a Different Google Cloud Project

This adapter supports fetching metrics from a Google Cloud project different from the one where the adapter is running. This is useful in scenarios where metrics are stored in a central project, but the Horizontal Pod Autoscaler (HPA) is defined in a cluster running in a different project.

## Configuration

To fetch a metric from a different project, you need to specify the target project ID using the label `resource.labels.metrics_host_project_id` within the metric selector in your HPA definition. This applies to all supported metric types: `Pods`, `Object`, and `External`.

The label should be added to the `matchLabels` section of the `metric.selector` .

### Examples

Below are examples for each metric type:

**1. Pods Metric:**

```yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: pods-metric-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 1
maxReplicas: 10
metrics:
- type: Pods
pods:
metric:
name: my-pod-metric
selector:
matchLabels:
"resource.labels.metrics_host_project_id": "my-metrics-project"
target:
type: AverageValue
averageValue: 10
```

**2. Object Metric:**

```yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: object-metric-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 1
maxReplicas: 10
metrics:
- type: Object
object:
metric:
name: my-object-metric
selector:
matchLabels:
"resource.labels.metrics_host_project_id": "my-metrics-project"
describedObject:
apiVersion: v1
kind: Service
name: my-service
target:
type: Value
value: 100
```

**3. External Metric:**

```yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: external-metric-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
minReplicas: 1
maxReplicas: 10
metrics:
- type: External
external:
metric:
name: my-external-metric
selector:
matchLabels:
"resource.labels.metrics_host_project_id": "my-metrics-project"
target:
type: Value
value: 10
```

### Backward Compatibility for External Metrics

For external metrics, the adapter also supports the legacy label `resource.labels.project_id` for backward compatibility. However, it is recommended to use the new label `resource.labels.metrics_host_project_id` for clarity and consistency across all metric types.

If both `resource.labels.metrics_host_project_id` and `resource.labels.project_id` are present for an External metric, the value from `resource.labels.metrics_host_project_id` will be used.

## Permissions

Ensure that the service account used by the Custom Metrics Stackdriver Adapter has the necessary permissions (e.g., `monitoring.viewer`) to read metrics from the target project (`my-metrics-project` in the examples above).
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,18 @@ func (p *StackdriverProvider) getRootScopedMetricByName(groupResource schema.Gro
if err != nil {
return nil, err
}
metricsProject, metricSelector, err := p.translator.MetricsHostProjectFromSelector(metricSelector, false)
if err != nil {
return nil, err
}
metricName := getCustomMetricName(escapedMetricName)
metricKind, metricValueType, err := p.translator.GetMetricKind(metricName, metricSelector)
metricKind, metricValueType, err := p.translator.GetMetricKind(metricsProject, metricName)
if err != nil {
return nil, err
}
stackdriverRequest, err := translator.NewQueryBuilder(p.translator, metricName).
WithNodes(&v1.NodeList{Items: []v1.Node{*matchingNode}}).
WithMetricsProject(metricsProject).
WithMetricKind(metricKind).
WithMetricValueType(metricValueType).
WithMetricSelector(metricSelector).
Expand Down Expand Up @@ -147,8 +152,12 @@ func (p *StackdriverProvider) getRootScopedMetricBySelector(groupResource schema
if err != nil {
return nil, err
}
metricsProject, metricSelector, err := p.translator.MetricsHostProjectFromSelector(metricSelector, false)
if err != nil {
return nil, err
}
metricName := getCustomMetricName(escapedMetricName)
metricKind, metricValueType, err := p.translator.GetMetricKind(metricName, metricSelector)
metricKind, metricValueType, err := p.translator.GetMetricKind(metricsProject, metricName)
if err != nil {
return nil, err
}
Expand All @@ -159,6 +168,7 @@ func (p *StackdriverProvider) getRootScopedMetricBySelector(groupResource schema
stackdriverRequest, err := translator.NewQueryBuilder(p.translator, metricName).
WithNodes(nodesSlice).
WithMetricKind(metricKind).
WithMetricsProject(metricsProject).
WithMetricValueType(metricValueType).
WithMetricSelector(metricSelector).
Build()
Expand Down Expand Up @@ -189,9 +199,12 @@ func (p *StackdriverProvider) getNamespacedMetricByName(groupResource schema.Gro
if err != nil {
return nil, err
}

metricsProject, metricSelector, err := p.translator.MetricsHostProjectFromSelector(metricSelector, false)
if err != nil {
return nil, err
}
metricName := getCustomMetricName(escapedMetricName)
metricKind, metricValueType, err := p.translator.GetMetricKind(metricName, metricSelector)
metricKind, metricValueType, err := p.translator.GetMetricKind(metricsProject, metricName)
if err != nil {
return nil, err
}
Expand All @@ -201,6 +214,7 @@ func (p *StackdriverProvider) getNamespacedMetricByName(groupResource schema.Gro
stackdriverRequest, err := queryBuilder.
WithPods(pods).
WithMetricKind(metricKind).
WithMetricsProject(metricsProject).
WithMetricValueType(metricValueType).
WithMetricSelector(metricSelector).
WithNamespace(namespace).
Expand All @@ -219,6 +233,7 @@ func (p *StackdriverProvider) getNamespacedMetricByName(groupResource schema.Gro
AsContainerType().
WithPods(pods).
WithMetricKind(metricKind).
WithMetricsProject(metricsProject).
WithMetricValueType(metricValueType).
WithMetricSelector(metricSelector).
WithNamespace(namespace).
Expand Down Expand Up @@ -249,17 +264,22 @@ func (p *StackdriverProvider) getNamespacedMetricBySelector(groupResource schema
if err != nil {
return nil, err
}
metricsProject, metricSelector, err := p.translator.MetricsHostProjectFromSelector(metricSelector, false)
if err != nil {
return nil, err
}
metricName := getCustomMetricName(escapedMetricName)
klog.V(4).Infof("Querying for metric: %s", metricName)

metricKind, metricValueType, err := p.translator.GetMetricKind(metricName, metricSelector)
metricKind, metricValueType, err := p.translator.GetMetricKind(metricsProject, metricName)
if err != nil {
return nil, err
}

queryBuilder := translator.NewQueryBuilder(p.translator, metricName).
WithMetricKind(metricKind).
WithMetricSelector(metricSelector).
WithMetricsProject(metricsProject).
WithMetricValueType(metricValueType).
WithNamespace(namespace)

Expand Down Expand Up @@ -290,6 +310,7 @@ func (p *StackdriverProvider) getNamespacedMetricBySelector(groupResource schema
AsContainerType().
WithPods(podsSlice).
WithMetricKind(metricKind).
WithMetricsProject(metricsProject).
WithMetricValueType(metricValueType).
WithMetricSelector(metricSelector).
WithNamespace(namespace).
Expand Down Expand Up @@ -335,17 +356,22 @@ func (p *StackdriverProvider) GetExternalMetric(ctx context.Context, namespace s
}
}

metricsProject, metricSelector, err := p.translator.MetricsHostProjectFromSelector(metricSelector, true)
if err != nil {
return nil, err
}

// Proceed to do a fresh fetch for metrics since one of the following is true at this point
// a) externalMetricCache is disabled
// b) the key was never added to the cache
// c) the key was in the cache, but its corrupt or its TTL has expired
metricNameEscaped := info.Metric
metricName := getExternalMetricName(metricNameEscaped)
metricKind, metricValueType, err := p.translator.GetMetricKind(metricName, metricSelector)
metricKind, metricValueType, err := p.translator.GetMetricKind(metricsProject, metricName)
if err != nil {
return nil, err
}
stackdriverRequest, err := p.translator.GetExternalMetricRequest(metricName, metricKind, metricValueType, metricSelector)
stackdriverRequest, err := p.translator.GetExternalMetricRequest(metricsProject, metricName, metricKind, metricValueType, metricSelector)
if err != nil {
return nil, err
}
Expand Down
Loading
Loading