Skip to content

Commit

Permalink
updated canary CRD and query rendering
Browse files Browse the repository at this point in the history
Signed-off-by: Nelson Johnstone <[email protected]>
  • Loading branch information
Nelson Johnstone authored and njohnstone2 committed Feb 8, 2023
1 parent 27eb21e commit 6786668
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 3 deletions.
5 changes: 5 additions & 0 deletions artifacts/flagger/crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,11 @@ spec:
namespace:
description: Namespace of this metric template
type: string
templateVariables:
description: Additional variables to be used in the metrics query (key-value pairs)
type: object
additionalProperties:
type: string
alerts:
description: Alert list for this canary analysis
type: array
Expand Down
5 changes: 5 additions & 0 deletions charts/flagger/crds/crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,11 @@ spec:
namespace:
description: Namespace of this metric template
type: string
templateVariables:
description: Additional variables to be used in the metrics query (key-value pairs)
type: object
additionalProperties:
type: string
alerts:
description: Alert list for this canary analysis
type: array
Expand Down
5 changes: 5 additions & 0 deletions kustomize/base/flagger/crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,11 @@ spec:
namespace:
description: Namespace of this metric template
type: string
templateVariables:
description: Additional variables to be used in the metrics query (key-value pairs)
type: object
additionalProperties:
type: string
alerts:
description: Alert list for this canary analysis
type: array
Expand Down
4 changes: 2 additions & 2 deletions pkg/apis/flagger/v1beta1/canary.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,9 +305,9 @@ type CanaryMetric struct {
// +optional
TemplateRef *CrossNamespaceObjectReference `json:"templateRef,omitempty"`

// TemplateVariables provides a map of key/value pairs to be used on a metric template object
// TemplateVariables provides a map of key/value pairs that can be used to inject variables into a metric query.
// +optional
TemplateVariables map[string]string `json:"variables,omitempty"`
TemplateVariables map[string]string `json:"templateVariables,omitempty"`
}

// CanaryThresholdRange defines the range used for metrics validation
Expand Down
14 changes: 14 additions & 0 deletions pkg/apis/flagger/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pkg/controller/scheduler_metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ func (c *Controller) runMetricChecks(canary *flaggerv1.Canary) bool {
}

query, err := observers.RenderQuery(template.Spec.Query, toMetricModel(canary, metric.Interval, metric.TemplateVariables))
c.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, namespace)).
Debugf("Metric template %s.%s query: %s", metric.TemplateRef.Name, namespace, query)
if err != nil {
c.recordEventErrorf(canary, "Metric template %s.%s query render error: %v",
metric.TemplateRef.Name, namespace, err)
Expand Down
2 changes: 1 addition & 1 deletion pkg/metrics/observers/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
)

func RenderQuery(queryTemplate string, model flaggerv1.MetricTemplateModel) (string, error) {
t, err := template.New("tmpl").Funcs(model.TemplateFunctions()).Parse(queryTemplate)
t, err := template.New("tmpl").Option("missingkey=error").Funcs(model.TemplateFunctions()).Parse(queryTemplate)
if err != nil {
return "", fmt.Errorf("template parsing failed: %w", err)
}
Expand Down
82 changes: 82 additions & 0 deletions pkg/metrics/observers/render_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
Copyright 2020 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package observers

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

flaggerv1 "github.com/fluxcd/flagger/pkg/apis/flagger/v1beta1"
)

func Test_RenderQuery(t *testing.T) {
t.Run("ok_without_variables", func(t *testing.T) {
expected := `sum(envoy_cluster_upstream_rq{envoy_cluster_name=~"default_myapp"})`
templateQuery := `sum(envoy_cluster_upstream_rq{envoy_cluster_name=~"{{ namespace }}_{{ target }}"})`

model := &flaggerv1.MetricTemplateModel{
Name: "standard",
Namespace: "default",
Target: "myapp",
Interval: "1m",
}

actual, err := RenderQuery(templateQuery, *model)
require.NoError(t, err)

assert.Equal(t, expected, actual)
})

t.Run("ok_with_variables", func(t *testing.T) {
expected := `delta(max by (consumer_group) (kafka_consumer_current_offset{cluster="dev", consumer_group="my_consumer"}[1m]))`
templateQuery := `delta(max by (consumer_group) (kafka_consumer_current_offset{cluster="{{ variables.cluster }}", consumer_group="{{ variables.consumer_group }}"}[{{ interval }}]))`

model := &flaggerv1.MetricTemplateModel{
Name: "kafka_consumer_offset",
Namespace: "default",
Interval: "1m",
Variables: map[string]string{
"cluster": "dev",
"consumer_group": "my_consumer",
},
}

actual, err := RenderQuery(templateQuery, *model)
require.NoError(t, err)

assert.Equal(t, expected, actual)
})

t.Run("missing_variable_key", func(t *testing.T) {
templateQuery := `delta(max by (consumer_group) (kafka_consumer_current_offset{cluster="{{ variables.cluster }}", consumer_group="{{ variables.consumer_group }}"}[{{ interval }}]))`

model := &flaggerv1.MetricTemplateModel{
Name: "kafka_consumer_offset",
Namespace: "default",
Interval: "1m",
Variables: map[string]string{
"invalid": "dev",
"consumer_group": "my_consumer",
},
}

_, err := RenderQuery(templateQuery, *model)
require.Error(t, err)
})
}

0 comments on commit 6786668

Please sign in to comment.