mirror of
https://github.com/fluxcd/flagger.git
synced 2026-02-14 18:10:00 +00:00
Merge pull request #419 from weaveworks/metric-template
Implement metric templates for Prometheus
This commit is contained in:
@@ -42,6 +42,8 @@ rules:
|
||||
resources:
|
||||
- canaries
|
||||
- canaries/status
|
||||
- metrictemplates
|
||||
- metrictemplates/status
|
||||
verbs: ["*"]
|
||||
- apiGroups:
|
||||
- networking.istio.io
|
||||
|
||||
@@ -215,18 +215,29 @@ spec:
|
||||
required: ["name", "threshold"]
|
||||
properties:
|
||||
name:
|
||||
description: Name of the Prometheus metric
|
||||
description: Name of the metric
|
||||
type: string
|
||||
interval:
|
||||
description: Interval of the promql query
|
||||
description: Interval of the query
|
||||
type: string
|
||||
pattern: "^[0-9]+(m|s)"
|
||||
threshold:
|
||||
description: Max scalar value accepted for this metric
|
||||
description: Max value accepted for this metric
|
||||
type: number
|
||||
query:
|
||||
description: Prometheus query
|
||||
type: string
|
||||
templateRef:
|
||||
description: Metric template reference
|
||||
type: object
|
||||
required: ["name"]
|
||||
properties:
|
||||
name:
|
||||
description: Name of this metric template
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace of this metric template
|
||||
type: string
|
||||
webhooks:
|
||||
description: Webhook list for this canary
|
||||
type: array
|
||||
@@ -322,3 +333,62 @@ spec:
|
||||
type:
|
||||
description: Type of this condition
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: metrictemplates.flagger.app
|
||||
annotations:
|
||||
helm.sh/resource-policy: keep
|
||||
spec:
|
||||
group: flagger.app
|
||||
version: v1alpha1
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
names:
|
||||
plural: metrictemplates
|
||||
singular: metrictemplate
|
||||
kind: MetricTemplate
|
||||
categories:
|
||||
- all
|
||||
scope: Namespaced
|
||||
subresources:
|
||||
status: {}
|
||||
validation:
|
||||
openAPIV3Schema:
|
||||
properties:
|
||||
spec:
|
||||
required:
|
||||
- provider
|
||||
- query
|
||||
properties:
|
||||
provider:
|
||||
description: Provider of this metric template
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
- address
|
||||
properties:
|
||||
type:
|
||||
description: Type of this provider
|
||||
type: string
|
||||
enum:
|
||||
- prometheus
|
||||
- influxdb
|
||||
address:
|
||||
description: API address of this provider
|
||||
type: string
|
||||
secretRef:
|
||||
description: Kubernetes secret reference containing the provider credentials
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
properties:
|
||||
name:
|
||||
description: Name of the Kubernetes secret
|
||||
type: string
|
||||
query:
|
||||
description: Query of this metric template
|
||||
type: string
|
||||
|
||||
@@ -78,7 +78,7 @@ spec:
|
||||
targetRef:
|
||||
description: Deployment selector
|
||||
type: object
|
||||
required: ['apiVersion', 'kind', 'name']
|
||||
required: ["apiVersion", "kind", "name"]
|
||||
properties:
|
||||
apiVersion:
|
||||
type: string
|
||||
@@ -91,7 +91,7 @@ spec:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: object
|
||||
required: ['apiVersion', 'kind', 'name']
|
||||
required: ["apiVersion", "kind", "name"]
|
||||
properties:
|
||||
apiVersion:
|
||||
type: string
|
||||
@@ -104,7 +104,7 @@ spec:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: object
|
||||
required: ['apiVersion', 'kind', 'name']
|
||||
required: ["apiVersion", "kind", "name"]
|
||||
properties:
|
||||
apiVersion:
|
||||
type: string
|
||||
@@ -114,7 +114,7 @@ spec:
|
||||
type: string
|
||||
service:
|
||||
type: object
|
||||
required: ['port']
|
||||
required: ["port"]
|
||||
properties:
|
||||
name:
|
||||
description: Kubernetes service name
|
||||
@@ -213,21 +213,32 @@ spec:
|
||||
properties:
|
||||
items:
|
||||
type: object
|
||||
required: ['name', 'threshold']
|
||||
required: ["name", "threshold"]
|
||||
properties:
|
||||
name:
|
||||
description: Name of the Prometheus metric
|
||||
description: Name of the metric
|
||||
type: string
|
||||
interval:
|
||||
description: Interval of the promql query
|
||||
description: Interval of the query
|
||||
type: string
|
||||
pattern: "^[0-9]+(m|s)"
|
||||
threshold:
|
||||
description: Max scalar value accepted for this metric
|
||||
description: Max value accepted for this metric
|
||||
type: number
|
||||
query:
|
||||
description: Prometheus query
|
||||
type: string
|
||||
templateRef:
|
||||
description: Metric template reference
|
||||
type: object
|
||||
required: ["name"]
|
||||
properties:
|
||||
name:
|
||||
description: Name of this metric template
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace of this metric template
|
||||
type: string
|
||||
webhooks:
|
||||
description: Webhook list for this canary
|
||||
type: array
|
||||
@@ -301,7 +312,7 @@ spec:
|
||||
properties:
|
||||
items:
|
||||
type: object
|
||||
required: ['type', 'status', 'reason']
|
||||
required: ["type", "status", "reason"]
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: LastTransitionTime of this condition
|
||||
@@ -323,4 +334,63 @@ spec:
|
||||
type:
|
||||
description: Type of this condition
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: metrictemplates.flagger.app
|
||||
annotations:
|
||||
helm.sh/resource-policy: keep
|
||||
spec:
|
||||
group: flagger.app
|
||||
version: v1alpha1
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
names:
|
||||
plural: metrictemplates
|
||||
singular: metrictemplate
|
||||
kind: MetricTemplate
|
||||
categories:
|
||||
- all
|
||||
scope: Namespaced
|
||||
subresources:
|
||||
status: {}
|
||||
validation:
|
||||
openAPIV3Schema:
|
||||
properties:
|
||||
spec:
|
||||
required:
|
||||
- provider
|
||||
- query
|
||||
properties:
|
||||
provider:
|
||||
description: Provider of this metric template
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
- address
|
||||
properties:
|
||||
type:
|
||||
description: Type of this provider
|
||||
type: string
|
||||
enum:
|
||||
- prometheus
|
||||
- influxdb
|
||||
address:
|
||||
description: API address of this provider
|
||||
type: string
|
||||
secretRef:
|
||||
description: Kubernetes secret reference containing the provider credentials
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
properties:
|
||||
name:
|
||||
description: Name of the Kubernetes secret
|
||||
type: string
|
||||
query:
|
||||
description: Query of this metric template
|
||||
type: string
|
||||
{{- end }}
|
||||
|
||||
@@ -38,6 +38,8 @@ rules:
|
||||
resources:
|
||||
- canaries
|
||||
- canaries/status
|
||||
- metrictemplates
|
||||
- metrictemplates/status
|
||||
verbs: ["*"]
|
||||
- apiGroups:
|
||||
- networking.istio.io
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"go.uber.org/zap"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
||||
@@ -26,7 +27,7 @@ import (
|
||||
informers "github.com/weaveworks/flagger/pkg/client/informers/externalversions"
|
||||
"github.com/weaveworks/flagger/pkg/controller"
|
||||
"github.com/weaveworks/flagger/pkg/logger"
|
||||
"github.com/weaveworks/flagger/pkg/metrics"
|
||||
"github.com/weaveworks/flagger/pkg/metrics/observers"
|
||||
"github.com/weaveworks/flagger/pkg/notifier"
|
||||
"github.com/weaveworks/flagger/pkg/router"
|
||||
"github.com/weaveworks/flagger/pkg/server"
|
||||
@@ -102,6 +103,8 @@ func main() {
|
||||
|
||||
stopCh := signals.SetupSignalHandler()
|
||||
|
||||
logger.Infof("Starting flagger version %s revision %s mesh provider %s", version.VERSION, version.REVISION, meshProvider)
|
||||
|
||||
cfg, err := clientcmd.BuildConfigFromFlags(masterURL, kubeconfig)
|
||||
if err != nil {
|
||||
logger.Fatalf("Error building kubeconfig: %v", err)
|
||||
@@ -122,48 +125,19 @@ func main() {
|
||||
logger.Fatalf("Error building flagger clientset: %s", err.Error())
|
||||
}
|
||||
|
||||
flaggerInformerFactory := informers.NewSharedInformerFactoryWithOptions(flaggerClient, time.Second*30, informers.WithNamespace(namespace))
|
||||
|
||||
canaryInformer := flaggerInformerFactory.Flagger().V1alpha3().Canaries()
|
||||
|
||||
logger.Infof("Starting flagger version %s revision %s mesh provider %s", version.VERSION, version.REVISION, meshProvider)
|
||||
|
||||
ver, err := kubeClient.Discovery().ServerVersion()
|
||||
if err != nil {
|
||||
logger.Fatalf("Error calling Kubernetes API: %v", err)
|
||||
}
|
||||
|
||||
k8sVersionConstraint := "^1.11.0"
|
||||
|
||||
// We append -alpha.1 to the end of our version constraint so that prebuilds of later versions
|
||||
// are considered valid for our purposes, as well as some managed solutions like EKS where they provide
|
||||
// a version like `v1.12.6-eks-d69f1b`. It doesn't matter what the prelease value is here, just that it
|
||||
// exists in our constraint.
|
||||
semverConstraint, err := semver.NewConstraint(k8sVersionConstraint + "-alpha.1")
|
||||
if err != nil {
|
||||
logger.Fatalf("Error parsing kubernetes version constraint: %v", err)
|
||||
}
|
||||
|
||||
k8sSemver, err := semver.NewVersion(ver.GitVersion)
|
||||
if err != nil {
|
||||
logger.Fatalf("Error parsing kubernetes version as a semantic version: %v", err)
|
||||
}
|
||||
|
||||
if !semverConstraint.Check(k8sSemver) {
|
||||
logger.Fatalf("Unsupported version of kubernetes detected. Expected %s, got %v", k8sVersionConstraint, ver)
|
||||
}
|
||||
verifyCRDs(flaggerClient, logger)
|
||||
verifyKubernetesVersion(kubeClient, logger)
|
||||
|
||||
labels := strings.Split(selectorLabels, ",")
|
||||
if len(labels) < 1 {
|
||||
logger.Fatalf("At least one selector label is required")
|
||||
}
|
||||
|
||||
logger.Infof("Connected to Kubernetes API %s", ver)
|
||||
if namespace != "" {
|
||||
logger.Infof("Watching namespace %s", namespace)
|
||||
}
|
||||
|
||||
observerFactory, err := metrics.NewFactory(metricsServer, 5*time.Second)
|
||||
observerFactory, err := observers.NewFactory(metricsServer)
|
||||
if err != nil {
|
||||
logger.Fatalf("Error building prometheus client: %s", err.Error())
|
||||
}
|
||||
@@ -181,6 +155,23 @@ func main() {
|
||||
// start HTTP server
|
||||
go server.ListenAndServe(port, 3*time.Second, logger, stopCh)
|
||||
|
||||
// start informers
|
||||
flaggerInformerFactory := informers.NewSharedInformerFactoryWithOptions(flaggerClient, time.Second*30, informers.WithNamespace(namespace))
|
||||
|
||||
logger.Info("Waiting for canary informer cache to sync")
|
||||
canaryInformer := flaggerInformerFactory.Flagger().V1alpha3().Canaries()
|
||||
go canaryInformer.Informer().Run(stopCh)
|
||||
if ok := cache.WaitForNamedCacheSync("flagger", stopCh, canaryInformer.Informer().HasSynced); !ok {
|
||||
logger.Fatalf("failed to wait for cache to sync")
|
||||
}
|
||||
|
||||
logger.Info("Waiting for metric template informer cache to sync")
|
||||
metricInformer := flaggerInformerFactory.Flagger().V1alpha1().MetricTemplates()
|
||||
go metricInformer.Informer().Run(stopCh)
|
||||
if ok := cache.WaitForNamedCacheSync("flagger", stopCh, metricInformer.Informer().HasSynced); !ok {
|
||||
logger.Fatalf("failed to wait for cache to sync")
|
||||
}
|
||||
|
||||
routerFactory := router.NewFactory(cfg, kubeClient, flaggerClient, ingressAnnotationsPrefix, logger, meshClient)
|
||||
configTracker := canary.ConfigTracker{
|
||||
Logger: logger,
|
||||
@@ -205,17 +196,6 @@ func main() {
|
||||
eventWebhook,
|
||||
)
|
||||
|
||||
flaggerInformerFactory.Start(stopCh)
|
||||
|
||||
logger.Info("Waiting for informer caches to sync")
|
||||
for _, synced := range []cache.InformerSynced{
|
||||
canaryInformer.Informer().HasSynced,
|
||||
} {
|
||||
if ok := cache.WaitForCacheSync(stopCh, synced); !ok {
|
||||
logger.Fatalf("Failed to wait for cache sync")
|
||||
}
|
||||
}
|
||||
|
||||
// leader election context
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
@@ -322,3 +302,44 @@ func fromEnv(envVar string, defaultVal string) string {
|
||||
}
|
||||
return defaultVal
|
||||
}
|
||||
|
||||
func verifyCRDs(flaggerClient clientset.Interface, logger *zap.SugaredLogger) {
|
||||
_, err := flaggerClient.FlaggerV1alpha3().Canaries(namespace).List(metav1.ListOptions{Limit: 1})
|
||||
if err != nil {
|
||||
logger.Fatalf("Canary CRD is not registered %v", err)
|
||||
}
|
||||
|
||||
_, err = flaggerClient.FlaggerV1alpha1().MetricTemplates(namespace).List(metav1.ListOptions{Limit: 1})
|
||||
if err != nil {
|
||||
logger.Fatalf("MetricTemplate CRD is not registered %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func verifyKubernetesVersion(kubeClient kubernetes.Interface, logger *zap.SugaredLogger) {
|
||||
ver, err := kubeClient.Discovery().ServerVersion()
|
||||
if err != nil {
|
||||
logger.Fatalf("Error calling Kubernetes API: %v", err)
|
||||
}
|
||||
|
||||
k8sVersionConstraint := "^1.11.0"
|
||||
|
||||
// We append -alpha.1 to the end of our version constraint so that prebuilds of later versions
|
||||
// are considered valid for our purposes, as well as some managed solutions like EKS where they provide
|
||||
// a version like `v1.12.6-eks-d69f1b`. It doesn't matter what the prelease value is here, just that it
|
||||
// exists in our constraint.
|
||||
semverConstraint, err := semver.NewConstraint(k8sVersionConstraint + "-alpha.1")
|
||||
if err != nil {
|
||||
logger.Fatalf("Error parsing kubernetes version constraint: %v", err)
|
||||
}
|
||||
|
||||
k8sSemver, err := semver.NewVersion(ver.GitVersion)
|
||||
if err != nil {
|
||||
logger.Fatalf("Error parsing kubernetes version as a semantic version: %v", err)
|
||||
}
|
||||
|
||||
if !semverConstraint.Check(k8sSemver) {
|
||||
logger.Fatalf("Unsupported version of kubernetes detected. Expected %s, got %v", k8sVersionConstraint, ver)
|
||||
}
|
||||
|
||||
logger.Infof("Connected to Kubernetes API %s", ver)
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ chmod +x ${CODEGEN_PKG}/generate-groups.sh
|
||||
|
||||
${CODEGEN_PKG}/generate-groups.sh all \
|
||||
github.com/weaveworks/flagger/pkg/client github.com/weaveworks/flagger/pkg/apis \
|
||||
"flagger:v1alpha3 appmesh:v1beta1 istio:v1alpha3 smi:v1alpha1 gloo:v1 projectcontour:v1" \
|
||||
"flagger:v1alpha3 flagger:v1alpha1 appmesh:v1beta1 istio:v1alpha3 smi:v1alpha1 gloo:v1 projectcontour:v1" \
|
||||
--output-base "${TEMP_DIR}" \
|
||||
--go-header-file ${SCRIPT_ROOT}/hack/boilerplate.go.txt
|
||||
|
||||
|
||||
@@ -215,18 +215,29 @@ spec:
|
||||
required: ["name", "threshold"]
|
||||
properties:
|
||||
name:
|
||||
description: Name of the Prometheus metric
|
||||
description: Name of the metric
|
||||
type: string
|
||||
interval:
|
||||
description: Interval of the promql query
|
||||
description: Interval of the query
|
||||
type: string
|
||||
pattern: "^[0-9]+(m|s)"
|
||||
threshold:
|
||||
description: Max scalar value accepted for this metric
|
||||
description: Max value accepted for this metric
|
||||
type: number
|
||||
query:
|
||||
description: Prometheus query
|
||||
type: string
|
||||
templateRef:
|
||||
description: Metric template reference
|
||||
type: object
|
||||
required: ["name"]
|
||||
properties:
|
||||
name:
|
||||
description: Name of this metric template
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace of this metric template
|
||||
type: string
|
||||
webhooks:
|
||||
description: Webhook list for this canary
|
||||
type: array
|
||||
@@ -322,3 +333,62 @@ spec:
|
||||
type:
|
||||
description: Type of this condition
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: metrictemplates.flagger.app
|
||||
annotations:
|
||||
helm.sh/resource-policy: keep
|
||||
spec:
|
||||
group: flagger.app
|
||||
version: v1alpha1
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
names:
|
||||
plural: metrictemplates
|
||||
singular: metrictemplate
|
||||
kind: MetricTemplate
|
||||
categories:
|
||||
- all
|
||||
scope: Namespaced
|
||||
subresources:
|
||||
status: {}
|
||||
validation:
|
||||
openAPIV3Schema:
|
||||
properties:
|
||||
spec:
|
||||
required:
|
||||
- provider
|
||||
- query
|
||||
properties:
|
||||
provider:
|
||||
description: Provider of this metric template
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
- address
|
||||
properties:
|
||||
type:
|
||||
description: Type of this provider
|
||||
type: string
|
||||
enum:
|
||||
- prometheus
|
||||
- influxdb
|
||||
address:
|
||||
description: API address of this provider
|
||||
type: string
|
||||
secretRef:
|
||||
description: Kubernetes secret reference containing the provider credentials
|
||||
type: object
|
||||
required:
|
||||
- name
|
||||
properties:
|
||||
name:
|
||||
description: Name of the Kubernetes secret
|
||||
type: string
|
||||
query:
|
||||
description: Query of this metric template
|
||||
type: string
|
||||
|
||||
@@ -32,6 +32,8 @@ rules:
|
||||
resources:
|
||||
- canaries
|
||||
- canaries/status
|
||||
- metrictemplates
|
||||
- metrictemplates/status
|
||||
verbs: ["*"]
|
||||
- apiGroups:
|
||||
- networking.istio.io
|
||||
|
||||
21
pkg/apis/flagger/v1alpha1/doc.go
Executable file
21
pkg/apis/flagger/v1alpha1/doc.go
Executable file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
Copyright The Flagger 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.
|
||||
*/
|
||||
|
||||
// +k8s:deepcopy-gen=package
|
||||
|
||||
// Package v1alpha1 is the v1alpha1 version of the API.
|
||||
// +groupName=flagger.app
|
||||
package v1alpha1
|
||||
120
pkg/apis/flagger/v1alpha1/metric.go
Normal file
120
pkg/apis/flagger/v1alpha1/metric.go
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
Copyright The Flagger 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 v1alpha1
|
||||
|
||||
import (
|
||||
"text/template"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
MetricTemplateKind = "MetricTemplate"
|
||||
MetricInterval = "1m"
|
||||
)
|
||||
|
||||
// +genclient
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// MetricTemplate is a specification for a canary analysis metric
|
||||
type MetricTemplate struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec MetricTemplateSpec `json:"spec"`
|
||||
Status MetricTemplateStatus `json:"status"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// MetricTemplateList is a list of metric template resources
|
||||
type MetricTemplateList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata"`
|
||||
|
||||
Items []MetricTemplate `json:"items"`
|
||||
}
|
||||
|
||||
// MetricTemplateSpec is the spec for a metric template resource
|
||||
type MetricTemplateSpec struct {
|
||||
// Provider of this metric
|
||||
Provider MetricTemplateProvider `json:"provider,omitempty"`
|
||||
|
||||
// Query template for this metric
|
||||
Query string `json:"query,omitempty"`
|
||||
}
|
||||
|
||||
// MetricProvider is the spec for a MetricProvider resource
|
||||
type MetricTemplateProvider struct {
|
||||
// Type of provider
|
||||
Type string `json:"type,omitempty"`
|
||||
|
||||
// HTTP(S) address of this provider
|
||||
Address string `json:"address,omitempty"`
|
||||
|
||||
// Secret reference containing the provider credentials
|
||||
// +optional
|
||||
SecretRef *corev1.LocalObjectReference `json:"secretRef,omitempty"`
|
||||
}
|
||||
|
||||
// MetricTemplateModel is the query template model
|
||||
type MetricTemplateModel struct {
|
||||
Name string `json:"name"`
|
||||
Namespace string `json:"namespace"`
|
||||
Target string `json:"target"`
|
||||
Service string `json:"service"`
|
||||
Ingress string `json:"ingress"`
|
||||
Interval string `json:"interval"`
|
||||
}
|
||||
|
||||
// TemplateFunctions returns a map of functions, one for each model field
|
||||
func (mtm *MetricTemplateModel) TemplateFunctions() template.FuncMap {
|
||||
return template.FuncMap{
|
||||
"name": func() string { return mtm.Name },
|
||||
"namespace": func() string { return mtm.Namespace },
|
||||
"target": func() string { return mtm.Target },
|
||||
"service": func() string { return mtm.Service },
|
||||
"ingress": func() string { return mtm.Ingress },
|
||||
"interval": func() string { return mtm.Interval },
|
||||
}
|
||||
}
|
||||
|
||||
type MetricTemplateStatus struct {
|
||||
// Conditions of this status
|
||||
Conditions []MetricTemplateCondition `json:"conditions,omitempty"`
|
||||
}
|
||||
|
||||
type MetricTemplateCondition struct {
|
||||
// Type of this condition
|
||||
Type string `json:"type"`
|
||||
|
||||
// Status of this condition
|
||||
Status corev1.ConditionStatus `json:"status"`
|
||||
|
||||
// LastUpdateTime of this condition
|
||||
LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"`
|
||||
|
||||
// LastTransitionTime of this condition
|
||||
LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"`
|
||||
|
||||
// Reason for the current status of this condition
|
||||
Reason string `json:"reason,omitempty"`
|
||||
|
||||
// Message associated with this condition
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
53
pkg/apis/flagger/v1alpha1/register.go
Executable file
53
pkg/apis/flagger/v1alpha1/register.go
Executable file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
Copyright The Flagger 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 v1alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"github.com/weaveworks/flagger/pkg/apis/flagger"
|
||||
)
|
||||
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
var SchemeGroupVersion = schema.GroupVersion{Group: flagger.GroupName, Version: "v1alpha1"}
|
||||
|
||||
// Kind takes an unqualified kind and returns back a Group qualified GroupKind
|
||||
func Kind(kind string) schema.GroupKind {
|
||||
return SchemeGroupVersion.WithKind(kind).GroupKind()
|
||||
}
|
||||
|
||||
// Resource takes an unqualified resource and returns a Group qualified GroupResource
|
||||
func Resource(resource string) schema.GroupResource {
|
||||
return SchemeGroupVersion.WithResource(resource).GroupResource()
|
||||
}
|
||||
|
||||
var (
|
||||
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
|
||||
AddToScheme = SchemeBuilder.AddToScheme
|
||||
)
|
||||
|
||||
// Adds the list of known types to Scheme.
|
||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&MetricTemplate{},
|
||||
&MetricTemplateList{},
|
||||
)
|
||||
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
|
||||
return nil
|
||||
}
|
||||
182
pkg/apis/flagger/v1alpha1/zz_generated.deepcopy.go
Normal file
182
pkg/apis/flagger/v1alpha1/zz_generated.deepcopy.go
Normal file
@@ -0,0 +1,182 @@
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright The Flagger 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.
|
||||
*/
|
||||
|
||||
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
v1 "k8s.io/api/core/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *MetricTemplate) DeepCopyInto(out *MetricTemplate) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricTemplate.
|
||||
func (in *MetricTemplate) DeepCopy() *MetricTemplate {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(MetricTemplate)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *MetricTemplate) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *MetricTemplateCondition) DeepCopyInto(out *MetricTemplateCondition) {
|
||||
*out = *in
|
||||
in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime)
|
||||
in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricTemplateCondition.
|
||||
func (in *MetricTemplateCondition) DeepCopy() *MetricTemplateCondition {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(MetricTemplateCondition)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *MetricTemplateList) DeepCopyInto(out *MetricTemplateList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]MetricTemplate, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricTemplateList.
|
||||
func (in *MetricTemplateList) DeepCopy() *MetricTemplateList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(MetricTemplateList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *MetricTemplateList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *MetricTemplateModel) DeepCopyInto(out *MetricTemplateModel) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricTemplateModel.
|
||||
func (in *MetricTemplateModel) DeepCopy() *MetricTemplateModel {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(MetricTemplateModel)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *MetricTemplateProvider) DeepCopyInto(out *MetricTemplateProvider) {
|
||||
*out = *in
|
||||
if in.SecretRef != nil {
|
||||
in, out := &in.SecretRef, &out.SecretRef
|
||||
*out = new(v1.LocalObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricTemplateProvider.
|
||||
func (in *MetricTemplateProvider) DeepCopy() *MetricTemplateProvider {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(MetricTemplateProvider)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *MetricTemplateSpec) DeepCopyInto(out *MetricTemplateSpec) {
|
||||
*out = *in
|
||||
in.Provider.DeepCopyInto(&out.Provider)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricTemplateSpec.
|
||||
func (in *MetricTemplateSpec) DeepCopy() *MetricTemplateSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(MetricTemplateSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *MetricTemplateStatus) DeepCopyInto(out *MetricTemplateStatus) {
|
||||
*out = *in
|
||||
if in.Conditions != nil {
|
||||
in, out := &in.Conditions, &out.Conditions
|
||||
*out = make([]MetricTemplateCondition, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricTemplateStatus.
|
||||
func (in *MetricTemplateStatus) DeepCopy() *MetricTemplateStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(MetricTemplateStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
@@ -135,6 +135,14 @@ type CanaryMetric struct {
|
||||
Threshold float64 `json:"threshold"`
|
||||
// +optional
|
||||
Query string `json:"query,omitempty"`
|
||||
// +optional
|
||||
TemplateRef *MetricTemplateRef `json:"templateRef,omitempty"`
|
||||
}
|
||||
|
||||
type MetricTemplateRef struct {
|
||||
Name string `json:"name"`
|
||||
// +optional
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
}
|
||||
|
||||
// HookType can be pre, post or during rollout
|
||||
|
||||
@@ -60,7 +60,9 @@ func (in *CanaryAnalysis) DeepCopyInto(out *CanaryAnalysis) {
|
||||
if in.Metrics != nil {
|
||||
in, out := &in.Metrics, &out.Metrics
|
||||
*out = make([]CanaryMetric, len(*in))
|
||||
copy(*out, *in)
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Webhooks != nil {
|
||||
in, out := &in.Webhooks, &out.Webhooks
|
||||
@@ -143,6 +145,11 @@ func (in *CanaryList) DeepCopyObject() runtime.Object {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CanaryMetric) DeepCopyInto(out *CanaryMetric) {
|
||||
*out = *in
|
||||
if in.TemplateRef != nil {
|
||||
in, out := &in.TemplateRef, &out.TemplateRef
|
||||
*out = new(MetricTemplateRef)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -338,3 +345,19 @@ func (in *CanaryWebhookPayload) DeepCopy() *CanaryWebhookPayload {
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *MetricTemplateRef) DeepCopyInto(out *MetricTemplateRef) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetricTemplateRef.
|
||||
func (in *MetricTemplateRef) DeepCopy() *MetricTemplateRef {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(MetricTemplateRef)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
appmeshv1beta1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/appmesh/v1beta1"
|
||||
flaggerv1alpha1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/flagger/v1alpha1"
|
||||
flaggerv1alpha3 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/flagger/v1alpha3"
|
||||
gloov1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/gloo/v1"
|
||||
networkingv1alpha3 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/istio/v1alpha3"
|
||||
@@ -36,6 +37,7 @@ type Interface interface {
|
||||
Discovery() discovery.DiscoveryInterface
|
||||
AppmeshV1beta1() appmeshv1beta1.AppmeshV1beta1Interface
|
||||
FlaggerV1alpha3() flaggerv1alpha3.FlaggerV1alpha3Interface
|
||||
FlaggerV1alpha1() flaggerv1alpha1.FlaggerV1alpha1Interface
|
||||
GlooV1() gloov1.GlooV1Interface
|
||||
NetworkingV1alpha3() networkingv1alpha3.NetworkingV1alpha3Interface
|
||||
ProjectcontourV1() projectcontourv1.ProjectcontourV1Interface
|
||||
@@ -48,6 +50,7 @@ type Clientset struct {
|
||||
*discovery.DiscoveryClient
|
||||
appmeshV1beta1 *appmeshv1beta1.AppmeshV1beta1Client
|
||||
flaggerV1alpha3 *flaggerv1alpha3.FlaggerV1alpha3Client
|
||||
flaggerV1alpha1 *flaggerv1alpha1.FlaggerV1alpha1Client
|
||||
glooV1 *gloov1.GlooV1Client
|
||||
networkingV1alpha3 *networkingv1alpha3.NetworkingV1alpha3Client
|
||||
projectcontourV1 *projectcontourv1.ProjectcontourV1Client
|
||||
@@ -64,6 +67,11 @@ func (c *Clientset) FlaggerV1alpha3() flaggerv1alpha3.FlaggerV1alpha3Interface {
|
||||
return c.flaggerV1alpha3
|
||||
}
|
||||
|
||||
// FlaggerV1alpha1 retrieves the FlaggerV1alpha1Client
|
||||
func (c *Clientset) FlaggerV1alpha1() flaggerv1alpha1.FlaggerV1alpha1Interface {
|
||||
return c.flaggerV1alpha1
|
||||
}
|
||||
|
||||
// GlooV1 retrieves the GlooV1Client
|
||||
func (c *Clientset) GlooV1() gloov1.GlooV1Interface {
|
||||
return c.glooV1
|
||||
@@ -113,6 +121,10 @@ func NewForConfig(c *rest.Config) (*Clientset, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cs.flaggerV1alpha1, err = flaggerv1alpha1.NewForConfig(&configShallowCopy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cs.glooV1, err = gloov1.NewForConfig(&configShallowCopy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -143,6 +155,7 @@ func NewForConfigOrDie(c *rest.Config) *Clientset {
|
||||
var cs Clientset
|
||||
cs.appmeshV1beta1 = appmeshv1beta1.NewForConfigOrDie(c)
|
||||
cs.flaggerV1alpha3 = flaggerv1alpha3.NewForConfigOrDie(c)
|
||||
cs.flaggerV1alpha1 = flaggerv1alpha1.NewForConfigOrDie(c)
|
||||
cs.glooV1 = gloov1.NewForConfigOrDie(c)
|
||||
cs.networkingV1alpha3 = networkingv1alpha3.NewForConfigOrDie(c)
|
||||
cs.projectcontourV1 = projectcontourv1.NewForConfigOrDie(c)
|
||||
@@ -157,6 +170,7 @@ func New(c rest.Interface) *Clientset {
|
||||
var cs Clientset
|
||||
cs.appmeshV1beta1 = appmeshv1beta1.New(c)
|
||||
cs.flaggerV1alpha3 = flaggerv1alpha3.New(c)
|
||||
cs.flaggerV1alpha1 = flaggerv1alpha1.New(c)
|
||||
cs.glooV1 = gloov1.New(c)
|
||||
cs.networkingV1alpha3 = networkingv1alpha3.New(c)
|
||||
cs.projectcontourV1 = projectcontourv1.New(c)
|
||||
|
||||
@@ -22,6 +22,8 @@ import (
|
||||
clientset "github.com/weaveworks/flagger/pkg/client/clientset/versioned"
|
||||
appmeshv1beta1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/appmesh/v1beta1"
|
||||
fakeappmeshv1beta1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/appmesh/v1beta1/fake"
|
||||
flaggerv1alpha1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/flagger/v1alpha1"
|
||||
fakeflaggerv1alpha1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/flagger/v1alpha1/fake"
|
||||
flaggerv1alpha3 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/flagger/v1alpha3"
|
||||
fakeflaggerv1alpha3 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/flagger/v1alpha3/fake"
|
||||
gloov1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/gloo/v1"
|
||||
@@ -96,6 +98,11 @@ func (c *Clientset) FlaggerV1alpha3() flaggerv1alpha3.FlaggerV1alpha3Interface {
|
||||
return &fakeflaggerv1alpha3.FakeFlaggerV1alpha3{Fake: &c.Fake}
|
||||
}
|
||||
|
||||
// FlaggerV1alpha1 retrieves the FlaggerV1alpha1Client
|
||||
func (c *Clientset) FlaggerV1alpha1() flaggerv1alpha1.FlaggerV1alpha1Interface {
|
||||
return &fakeflaggerv1alpha1.FakeFlaggerV1alpha1{Fake: &c.Fake}
|
||||
}
|
||||
|
||||
// GlooV1 retrieves the GlooV1Client
|
||||
func (c *Clientset) GlooV1() gloov1.GlooV1Interface {
|
||||
return &fakegloov1.FakeGlooV1{Fake: &c.Fake}
|
||||
|
||||
@@ -20,6 +20,7 @@ package fake
|
||||
|
||||
import (
|
||||
appmeshv1beta1 "github.com/weaveworks/flagger/pkg/apis/appmesh/v1beta1"
|
||||
flaggerv1alpha1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
flaggerv1alpha3 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3"
|
||||
gloov1 "github.com/weaveworks/flagger/pkg/apis/gloo/v1"
|
||||
networkingv1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3"
|
||||
@@ -38,6 +39,7 @@ var parameterCodec = runtime.NewParameterCodec(scheme)
|
||||
var localSchemeBuilder = runtime.SchemeBuilder{
|
||||
appmeshv1beta1.AddToScheme,
|
||||
flaggerv1alpha3.AddToScheme,
|
||||
flaggerv1alpha1.AddToScheme,
|
||||
gloov1.AddToScheme,
|
||||
networkingv1alpha3.AddToScheme,
|
||||
projectcontourv1.AddToScheme,
|
||||
|
||||
@@ -20,6 +20,7 @@ package scheme
|
||||
|
||||
import (
|
||||
appmeshv1beta1 "github.com/weaveworks/flagger/pkg/apis/appmesh/v1beta1"
|
||||
flaggerv1alpha1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
flaggerv1alpha3 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3"
|
||||
gloov1 "github.com/weaveworks/flagger/pkg/apis/gloo/v1"
|
||||
networkingv1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3"
|
||||
@@ -38,6 +39,7 @@ var ParameterCodec = runtime.NewParameterCodec(Scheme)
|
||||
var localSchemeBuilder = runtime.SchemeBuilder{
|
||||
appmeshv1beta1.AddToScheme,
|
||||
flaggerv1alpha3.AddToScheme,
|
||||
flaggerv1alpha1.AddToScheme,
|
||||
gloov1.AddToScheme,
|
||||
networkingv1alpha3.AddToScheme,
|
||||
projectcontourv1.AddToScheme,
|
||||
|
||||
20
pkg/client/clientset/versioned/typed/flagger/v1alpha1/doc.go
Normal file
20
pkg/client/clientset/versioned/typed/flagger/v1alpha1/doc.go
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright The Flagger 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.
|
||||
*/
|
||||
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
// This package has the automatically generated typed clients.
|
||||
package v1alpha1
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright The Flagger 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.
|
||||
*/
|
||||
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
// Package fake has the automatically generated clients.
|
||||
package fake
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
Copyright The Flagger 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.
|
||||
*/
|
||||
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
v1alpha1 "github.com/weaveworks/flagger/pkg/client/clientset/versioned/typed/flagger/v1alpha1"
|
||||
rest "k8s.io/client-go/rest"
|
||||
testing "k8s.io/client-go/testing"
|
||||
)
|
||||
|
||||
type FakeFlaggerV1alpha1 struct {
|
||||
*testing.Fake
|
||||
}
|
||||
|
||||
func (c *FakeFlaggerV1alpha1) MetricTemplates(namespace string) v1alpha1.MetricTemplateInterface {
|
||||
return &FakeMetricTemplates{c, namespace}
|
||||
}
|
||||
|
||||
// RESTClient returns a RESTClient that is used to communicate
|
||||
// with API server by this client implementation.
|
||||
func (c *FakeFlaggerV1alpha1) RESTClient() rest.Interface {
|
||||
var ret *rest.RESTClient
|
||||
return ret
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
Copyright The Flagger 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.
|
||||
*/
|
||||
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package fake
|
||||
|
||||
import (
|
||||
v1alpha1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
testing "k8s.io/client-go/testing"
|
||||
)
|
||||
|
||||
// FakeMetricTemplates implements MetricTemplateInterface
|
||||
type FakeMetricTemplates struct {
|
||||
Fake *FakeFlaggerV1alpha1
|
||||
ns string
|
||||
}
|
||||
|
||||
var metrictemplatesResource = schema.GroupVersionResource{Group: "flagger.app", Version: "v1alpha1", Resource: "metrictemplates"}
|
||||
|
||||
var metrictemplatesKind = schema.GroupVersionKind{Group: "flagger.app", Version: "v1alpha1", Kind: "MetricTemplate"}
|
||||
|
||||
// Get takes name of the metricTemplate, and returns the corresponding metricTemplate object, and an error if there is any.
|
||||
func (c *FakeMetricTemplates) Get(name string, options v1.GetOptions) (result *v1alpha1.MetricTemplate, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewGetAction(metrictemplatesResource, c.ns, name), &v1alpha1.MetricTemplate{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1alpha1.MetricTemplate), err
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of MetricTemplates that match those selectors.
|
||||
func (c *FakeMetricTemplates) List(opts v1.ListOptions) (result *v1alpha1.MetricTemplateList, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewListAction(metrictemplatesResource, metrictemplatesKind, c.ns, opts), &v1alpha1.MetricTemplateList{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
label, _, _ := testing.ExtractFromListOptions(opts)
|
||||
if label == nil {
|
||||
label = labels.Everything()
|
||||
}
|
||||
list := &v1alpha1.MetricTemplateList{ListMeta: obj.(*v1alpha1.MetricTemplateList).ListMeta}
|
||||
for _, item := range obj.(*v1alpha1.MetricTemplateList).Items {
|
||||
if label.Matches(labels.Set(item.Labels)) {
|
||||
list.Items = append(list.Items, item)
|
||||
}
|
||||
}
|
||||
return list, err
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested metricTemplates.
|
||||
func (c *FakeMetricTemplates) Watch(opts v1.ListOptions) (watch.Interface, error) {
|
||||
return c.Fake.
|
||||
InvokesWatch(testing.NewWatchAction(metrictemplatesResource, c.ns, opts))
|
||||
|
||||
}
|
||||
|
||||
// Create takes the representation of a metricTemplate and creates it. Returns the server's representation of the metricTemplate, and an error, if there is any.
|
||||
func (c *FakeMetricTemplates) Create(metricTemplate *v1alpha1.MetricTemplate) (result *v1alpha1.MetricTemplate, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewCreateAction(metrictemplatesResource, c.ns, metricTemplate), &v1alpha1.MetricTemplate{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1alpha1.MetricTemplate), err
|
||||
}
|
||||
|
||||
// Update takes the representation of a metricTemplate and updates it. Returns the server's representation of the metricTemplate, and an error, if there is any.
|
||||
func (c *FakeMetricTemplates) Update(metricTemplate *v1alpha1.MetricTemplate) (result *v1alpha1.MetricTemplate, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewUpdateAction(metrictemplatesResource, c.ns, metricTemplate), &v1alpha1.MetricTemplate{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1alpha1.MetricTemplate), err
|
||||
}
|
||||
|
||||
// UpdateStatus was generated because the type contains a Status member.
|
||||
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
|
||||
func (c *FakeMetricTemplates) UpdateStatus(metricTemplate *v1alpha1.MetricTemplate) (*v1alpha1.MetricTemplate, error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewUpdateSubresourceAction(metrictemplatesResource, "status", c.ns, metricTemplate), &v1alpha1.MetricTemplate{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1alpha1.MetricTemplate), err
|
||||
}
|
||||
|
||||
// Delete takes name of the metricTemplate and deletes it. Returns an error if one occurs.
|
||||
func (c *FakeMetricTemplates) Delete(name string, options *v1.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(testing.NewDeleteAction(metrictemplatesResource, c.ns, name), &v1alpha1.MetricTemplate{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *FakeMetricTemplates) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
|
||||
action := testing.NewDeleteCollectionAction(metrictemplatesResource, c.ns, listOptions)
|
||||
|
||||
_, err := c.Fake.Invokes(action, &v1alpha1.MetricTemplateList{})
|
||||
return err
|
||||
}
|
||||
|
||||
// Patch applies the patch and returns the patched metricTemplate.
|
||||
func (c *FakeMetricTemplates) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.MetricTemplate, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(testing.NewPatchSubresourceAction(metrictemplatesResource, c.ns, name, pt, data, subresources...), &v1alpha1.MetricTemplate{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*v1alpha1.MetricTemplate), err
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
Copyright The Flagger 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.
|
||||
*/
|
||||
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
v1alpha1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
"github.com/weaveworks/flagger/pkg/client/clientset/versioned/scheme"
|
||||
rest "k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
type FlaggerV1alpha1Interface interface {
|
||||
RESTClient() rest.Interface
|
||||
MetricTemplatesGetter
|
||||
}
|
||||
|
||||
// FlaggerV1alpha1Client is used to interact with features provided by the flagger.app group.
|
||||
type FlaggerV1alpha1Client struct {
|
||||
restClient rest.Interface
|
||||
}
|
||||
|
||||
func (c *FlaggerV1alpha1Client) MetricTemplates(namespace string) MetricTemplateInterface {
|
||||
return newMetricTemplates(c, namespace)
|
||||
}
|
||||
|
||||
// NewForConfig creates a new FlaggerV1alpha1Client for the given config.
|
||||
func NewForConfig(c *rest.Config) (*FlaggerV1alpha1Client, error) {
|
||||
config := *c
|
||||
if err := setConfigDefaults(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err := rest.RESTClientFor(&config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &FlaggerV1alpha1Client{client}, nil
|
||||
}
|
||||
|
||||
// NewForConfigOrDie creates a new FlaggerV1alpha1Client for the given config and
|
||||
// panics if there is an error in the config.
|
||||
func NewForConfigOrDie(c *rest.Config) *FlaggerV1alpha1Client {
|
||||
client, err := NewForConfig(c)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
// New creates a new FlaggerV1alpha1Client for the given RESTClient.
|
||||
func New(c rest.Interface) *FlaggerV1alpha1Client {
|
||||
return &FlaggerV1alpha1Client{c}
|
||||
}
|
||||
|
||||
func setConfigDefaults(config *rest.Config) error {
|
||||
gv := v1alpha1.SchemeGroupVersion
|
||||
config.GroupVersion = &gv
|
||||
config.APIPath = "/apis"
|
||||
config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
|
||||
|
||||
if config.UserAgent == "" {
|
||||
config.UserAgent = rest.DefaultKubernetesUserAgent()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RESTClient returns a RESTClient that is used to communicate
|
||||
// with API server by this client implementation.
|
||||
func (c *FlaggerV1alpha1Client) RESTClient() rest.Interface {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.restClient
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
Copyright The Flagger 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.
|
||||
*/
|
||||
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
type MetricTemplateExpansion interface{}
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
Copyright The Flagger 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.
|
||||
*/
|
||||
|
||||
// Code generated by client-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
v1alpha1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
scheme "github.com/weaveworks/flagger/pkg/client/clientset/versioned/scheme"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
rest "k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
// MetricTemplatesGetter has a method to return a MetricTemplateInterface.
|
||||
// A group's client should implement this interface.
|
||||
type MetricTemplatesGetter interface {
|
||||
MetricTemplates(namespace string) MetricTemplateInterface
|
||||
}
|
||||
|
||||
// MetricTemplateInterface has methods to work with MetricTemplate resources.
|
||||
type MetricTemplateInterface interface {
|
||||
Create(*v1alpha1.MetricTemplate) (*v1alpha1.MetricTemplate, error)
|
||||
Update(*v1alpha1.MetricTemplate) (*v1alpha1.MetricTemplate, error)
|
||||
UpdateStatus(*v1alpha1.MetricTemplate) (*v1alpha1.MetricTemplate, error)
|
||||
Delete(name string, options *v1.DeleteOptions) error
|
||||
DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error
|
||||
Get(name string, options v1.GetOptions) (*v1alpha1.MetricTemplate, error)
|
||||
List(opts v1.ListOptions) (*v1alpha1.MetricTemplateList, error)
|
||||
Watch(opts v1.ListOptions) (watch.Interface, error)
|
||||
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.MetricTemplate, err error)
|
||||
MetricTemplateExpansion
|
||||
}
|
||||
|
||||
// metricTemplates implements MetricTemplateInterface
|
||||
type metricTemplates struct {
|
||||
client rest.Interface
|
||||
ns string
|
||||
}
|
||||
|
||||
// newMetricTemplates returns a MetricTemplates
|
||||
func newMetricTemplates(c *FlaggerV1alpha1Client, namespace string) *metricTemplates {
|
||||
return &metricTemplates{
|
||||
client: c.RESTClient(),
|
||||
ns: namespace,
|
||||
}
|
||||
}
|
||||
|
||||
// Get takes name of the metricTemplate, and returns the corresponding metricTemplate object, and an error if there is any.
|
||||
func (c *metricTemplates) Get(name string, options v1.GetOptions) (result *v1alpha1.MetricTemplate, err error) {
|
||||
result = &v1alpha1.MetricTemplate{}
|
||||
err = c.client.Get().
|
||||
Namespace(c.ns).
|
||||
Resource("metrictemplates").
|
||||
Name(name).
|
||||
VersionedParams(&options, scheme.ParameterCodec).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of MetricTemplates that match those selectors.
|
||||
func (c *metricTemplates) List(opts v1.ListOptions) (result *v1alpha1.MetricTemplateList, err error) {
|
||||
var timeout time.Duration
|
||||
if opts.TimeoutSeconds != nil {
|
||||
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
|
||||
}
|
||||
result = &v1alpha1.MetricTemplateList{}
|
||||
err = c.client.Get().
|
||||
Namespace(c.ns).
|
||||
Resource("metrictemplates").
|
||||
VersionedParams(&opts, scheme.ParameterCodec).
|
||||
Timeout(timeout).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested metricTemplates.
|
||||
func (c *metricTemplates) Watch(opts v1.ListOptions) (watch.Interface, error) {
|
||||
var timeout time.Duration
|
||||
if opts.TimeoutSeconds != nil {
|
||||
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
|
||||
}
|
||||
opts.Watch = true
|
||||
return c.client.Get().
|
||||
Namespace(c.ns).
|
||||
Resource("metrictemplates").
|
||||
VersionedParams(&opts, scheme.ParameterCodec).
|
||||
Timeout(timeout).
|
||||
Watch()
|
||||
}
|
||||
|
||||
// Create takes the representation of a metricTemplate and creates it. Returns the server's representation of the metricTemplate, and an error, if there is any.
|
||||
func (c *metricTemplates) Create(metricTemplate *v1alpha1.MetricTemplate) (result *v1alpha1.MetricTemplate, err error) {
|
||||
result = &v1alpha1.MetricTemplate{}
|
||||
err = c.client.Post().
|
||||
Namespace(c.ns).
|
||||
Resource("metrictemplates").
|
||||
Body(metricTemplate).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Update takes the representation of a metricTemplate and updates it. Returns the server's representation of the metricTemplate, and an error, if there is any.
|
||||
func (c *metricTemplates) Update(metricTemplate *v1alpha1.MetricTemplate) (result *v1alpha1.MetricTemplate, err error) {
|
||||
result = &v1alpha1.MetricTemplate{}
|
||||
err = c.client.Put().
|
||||
Namespace(c.ns).
|
||||
Resource("metrictemplates").
|
||||
Name(metricTemplate.Name).
|
||||
Body(metricTemplate).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateStatus was generated because the type contains a Status member.
|
||||
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
|
||||
|
||||
func (c *metricTemplates) UpdateStatus(metricTemplate *v1alpha1.MetricTemplate) (result *v1alpha1.MetricTemplate, err error) {
|
||||
result = &v1alpha1.MetricTemplate{}
|
||||
err = c.client.Put().
|
||||
Namespace(c.ns).
|
||||
Resource("metrictemplates").
|
||||
Name(metricTemplate.Name).
|
||||
SubResource("status").
|
||||
Body(metricTemplate).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete takes name of the metricTemplate and deletes it. Returns an error if one occurs.
|
||||
func (c *metricTemplates) Delete(name string, options *v1.DeleteOptions) error {
|
||||
return c.client.Delete().
|
||||
Namespace(c.ns).
|
||||
Resource("metrictemplates").
|
||||
Name(name).
|
||||
Body(options).
|
||||
Do().
|
||||
Error()
|
||||
}
|
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *metricTemplates) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
|
||||
var timeout time.Duration
|
||||
if listOptions.TimeoutSeconds != nil {
|
||||
timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second
|
||||
}
|
||||
return c.client.Delete().
|
||||
Namespace(c.ns).
|
||||
Resource("metrictemplates").
|
||||
VersionedParams(&listOptions, scheme.ParameterCodec).
|
||||
Timeout(timeout).
|
||||
Body(options).
|
||||
Do().
|
||||
Error()
|
||||
}
|
||||
|
||||
// Patch applies the patch and returns the patched metricTemplate.
|
||||
func (c *metricTemplates) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.MetricTemplate, err error) {
|
||||
result = &v1alpha1.MetricTemplate{}
|
||||
err = c.client.Patch(pt).
|
||||
Namespace(c.ns).
|
||||
Resource("metrictemplates").
|
||||
SubResource(subresources...).
|
||||
Name(name).
|
||||
Body(data).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
@@ -19,6 +19,7 @@ limitations under the License.
|
||||
package flagger
|
||||
|
||||
import (
|
||||
v1alpha1 "github.com/weaveworks/flagger/pkg/client/informers/externalversions/flagger/v1alpha1"
|
||||
v1alpha3 "github.com/weaveworks/flagger/pkg/client/informers/externalversions/flagger/v1alpha3"
|
||||
internalinterfaces "github.com/weaveworks/flagger/pkg/client/informers/externalversions/internalinterfaces"
|
||||
)
|
||||
@@ -27,6 +28,8 @@ import (
|
||||
type Interface interface {
|
||||
// V1alpha3 provides access to shared informers for resources in V1alpha3.
|
||||
V1alpha3() v1alpha3.Interface
|
||||
// V1alpha1 provides access to shared informers for resources in V1alpha1.
|
||||
V1alpha1() v1alpha1.Interface
|
||||
}
|
||||
|
||||
type group struct {
|
||||
@@ -44,3 +47,8 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList
|
||||
func (g *group) V1alpha3() v1alpha3.Interface {
|
||||
return v1alpha3.New(g.factory, g.namespace, g.tweakListOptions)
|
||||
}
|
||||
|
||||
// V1alpha1 returns a new v1alpha1.Interface.
|
||||
func (g *group) V1alpha1() v1alpha1.Interface {
|
||||
return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright The Flagger 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.
|
||||
*/
|
||||
|
||||
// Code generated by informer-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
internalinterfaces "github.com/weaveworks/flagger/pkg/client/informers/externalversions/internalinterfaces"
|
||||
)
|
||||
|
||||
// Interface provides access to all the informers in this group version.
|
||||
type Interface interface {
|
||||
// MetricTemplates returns a MetricTemplateInformer.
|
||||
MetricTemplates() MetricTemplateInformer
|
||||
}
|
||||
|
||||
type version struct {
|
||||
factory internalinterfaces.SharedInformerFactory
|
||||
namespace string
|
||||
tweakListOptions internalinterfaces.TweakListOptionsFunc
|
||||
}
|
||||
|
||||
// New returns a new Interface.
|
||||
func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface {
|
||||
return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions}
|
||||
}
|
||||
|
||||
// MetricTemplates returns a MetricTemplateInformer.
|
||||
func (v *version) MetricTemplates() MetricTemplateInformer {
|
||||
return &metricTemplateInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
Copyright The Flagger 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.
|
||||
*/
|
||||
|
||||
// Code generated by informer-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
time "time"
|
||||
|
||||
flaggerv1alpha1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
versioned "github.com/weaveworks/flagger/pkg/client/clientset/versioned"
|
||||
internalinterfaces "github.com/weaveworks/flagger/pkg/client/informers/externalversions/internalinterfaces"
|
||||
v1alpha1 "github.com/weaveworks/flagger/pkg/client/listers/flagger/v1alpha1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
watch "k8s.io/apimachinery/pkg/watch"
|
||||
cache "k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
// MetricTemplateInformer provides access to a shared informer and lister for
|
||||
// MetricTemplates.
|
||||
type MetricTemplateInformer interface {
|
||||
Informer() cache.SharedIndexInformer
|
||||
Lister() v1alpha1.MetricTemplateLister
|
||||
}
|
||||
|
||||
type metricTemplateInformer struct {
|
||||
factory internalinterfaces.SharedInformerFactory
|
||||
tweakListOptions internalinterfaces.TweakListOptionsFunc
|
||||
namespace string
|
||||
}
|
||||
|
||||
// NewMetricTemplateInformer constructs a new informer for MetricTemplate type.
|
||||
// Always prefer using an informer factory to get a shared informer instead of getting an independent
|
||||
// one. This reduces memory footprint and number of connections to the server.
|
||||
func NewMetricTemplateInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
|
||||
return NewFilteredMetricTemplateInformer(client, namespace, resyncPeriod, indexers, nil)
|
||||
}
|
||||
|
||||
// NewFilteredMetricTemplateInformer constructs a new informer for MetricTemplate type.
|
||||
// Always prefer using an informer factory to get a shared informer instead of getting an independent
|
||||
// one. This reduces memory footprint and number of connections to the server.
|
||||
func NewFilteredMetricTemplateInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
|
||||
return cache.NewSharedIndexInformer(
|
||||
&cache.ListWatch{
|
||||
ListFunc: func(options v1.ListOptions) (runtime.Object, error) {
|
||||
if tweakListOptions != nil {
|
||||
tweakListOptions(&options)
|
||||
}
|
||||
return client.FlaggerV1alpha1().MetricTemplates(namespace).List(options)
|
||||
},
|
||||
WatchFunc: func(options v1.ListOptions) (watch.Interface, error) {
|
||||
if tweakListOptions != nil {
|
||||
tweakListOptions(&options)
|
||||
}
|
||||
return client.FlaggerV1alpha1().MetricTemplates(namespace).Watch(options)
|
||||
},
|
||||
},
|
||||
&flaggerv1alpha1.MetricTemplate{},
|
||||
resyncPeriod,
|
||||
indexers,
|
||||
)
|
||||
}
|
||||
|
||||
func (f *metricTemplateInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
|
||||
return NewFilteredMetricTemplateInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
|
||||
}
|
||||
|
||||
func (f *metricTemplateInformer) Informer() cache.SharedIndexInformer {
|
||||
return f.factory.InformerFor(&flaggerv1alpha1.MetricTemplate{}, f.defaultInformer)
|
||||
}
|
||||
|
||||
func (f *metricTemplateInformer) Lister() v1alpha1.MetricTemplateLister {
|
||||
return v1alpha1.NewMetricTemplateLister(f.Informer().GetIndexer())
|
||||
}
|
||||
@@ -22,11 +22,12 @@ import (
|
||||
"fmt"
|
||||
|
||||
v1beta1 "github.com/weaveworks/flagger/pkg/apis/appmesh/v1beta1"
|
||||
v1alpha1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
v1alpha3 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3"
|
||||
v1 "github.com/weaveworks/flagger/pkg/apis/gloo/v1"
|
||||
istiov1alpha3 "github.com/weaveworks/flagger/pkg/apis/istio/v1alpha3"
|
||||
projectcontourv1 "github.com/weaveworks/flagger/pkg/apis/projectcontour/v1"
|
||||
v1alpha1 "github.com/weaveworks/flagger/pkg/apis/smi/v1alpha1"
|
||||
smiv1alpha1 "github.com/weaveworks/flagger/pkg/apis/smi/v1alpha1"
|
||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
cache "k8s.io/client-go/tools/cache"
|
||||
)
|
||||
@@ -65,6 +66,10 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
|
||||
case v1beta1.SchemeGroupVersion.WithResource("virtualservices"):
|
||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Appmesh().V1beta1().VirtualServices().Informer()}, nil
|
||||
|
||||
// Group=flagger.app, Version=v1alpha1
|
||||
case v1alpha1.SchemeGroupVersion.WithResource("metrictemplates"):
|
||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Flagger().V1alpha1().MetricTemplates().Informer()}, nil
|
||||
|
||||
// Group=flagger.app, Version=v1alpha3
|
||||
case v1alpha3.SchemeGroupVersion.WithResource("canaries"):
|
||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Flagger().V1alpha3().Canaries().Informer()}, nil
|
||||
@@ -84,7 +89,7 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
|
||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Projectcontour().V1().HTTPProxies().Informer()}, nil
|
||||
|
||||
// Group=split.smi-spec.io, Version=v1alpha1
|
||||
case v1alpha1.SchemeGroupVersion.WithResource("trafficsplits"):
|
||||
case smiv1alpha1.SchemeGroupVersion.WithResource("trafficsplits"):
|
||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Split().V1alpha1().TrafficSplits().Informer()}, nil
|
||||
|
||||
}
|
||||
|
||||
27
pkg/client/listers/flagger/v1alpha1/expansion_generated.go
Normal file
27
pkg/client/listers/flagger/v1alpha1/expansion_generated.go
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
Copyright The Flagger 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.
|
||||
*/
|
||||
|
||||
// Code generated by lister-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
// MetricTemplateListerExpansion allows custom methods to be added to
|
||||
// MetricTemplateLister.
|
||||
type MetricTemplateListerExpansion interface{}
|
||||
|
||||
// MetricTemplateNamespaceListerExpansion allows custom methods to be added to
|
||||
// MetricTemplateNamespaceLister.
|
||||
type MetricTemplateNamespaceListerExpansion interface{}
|
||||
94
pkg/client/listers/flagger/v1alpha1/metrictemplate.go
Normal file
94
pkg/client/listers/flagger/v1alpha1/metrictemplate.go
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
Copyright The Flagger 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.
|
||||
*/
|
||||
|
||||
// Code generated by lister-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
v1alpha1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
// MetricTemplateLister helps list MetricTemplates.
|
||||
type MetricTemplateLister interface {
|
||||
// List lists all MetricTemplates in the indexer.
|
||||
List(selector labels.Selector) (ret []*v1alpha1.MetricTemplate, err error)
|
||||
// MetricTemplates returns an object that can list and get MetricTemplates.
|
||||
MetricTemplates(namespace string) MetricTemplateNamespaceLister
|
||||
MetricTemplateListerExpansion
|
||||
}
|
||||
|
||||
// metricTemplateLister implements the MetricTemplateLister interface.
|
||||
type metricTemplateLister struct {
|
||||
indexer cache.Indexer
|
||||
}
|
||||
|
||||
// NewMetricTemplateLister returns a new MetricTemplateLister.
|
||||
func NewMetricTemplateLister(indexer cache.Indexer) MetricTemplateLister {
|
||||
return &metricTemplateLister{indexer: indexer}
|
||||
}
|
||||
|
||||
// List lists all MetricTemplates in the indexer.
|
||||
func (s *metricTemplateLister) List(selector labels.Selector) (ret []*v1alpha1.MetricTemplate, err error) {
|
||||
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
|
||||
ret = append(ret, m.(*v1alpha1.MetricTemplate))
|
||||
})
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// MetricTemplates returns an object that can list and get MetricTemplates.
|
||||
func (s *metricTemplateLister) MetricTemplates(namespace string) MetricTemplateNamespaceLister {
|
||||
return metricTemplateNamespaceLister{indexer: s.indexer, namespace: namespace}
|
||||
}
|
||||
|
||||
// MetricTemplateNamespaceLister helps list and get MetricTemplates.
|
||||
type MetricTemplateNamespaceLister interface {
|
||||
// List lists all MetricTemplates in the indexer for a given namespace.
|
||||
List(selector labels.Selector) (ret []*v1alpha1.MetricTemplate, err error)
|
||||
// Get retrieves the MetricTemplate from the indexer for a given namespace and name.
|
||||
Get(name string) (*v1alpha1.MetricTemplate, error)
|
||||
MetricTemplateNamespaceListerExpansion
|
||||
}
|
||||
|
||||
// metricTemplateNamespaceLister implements the MetricTemplateNamespaceLister
|
||||
// interface.
|
||||
type metricTemplateNamespaceLister struct {
|
||||
indexer cache.Indexer
|
||||
namespace string
|
||||
}
|
||||
|
||||
// List lists all MetricTemplates in the indexer for a given namespace.
|
||||
func (s metricTemplateNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.MetricTemplate, err error) {
|
||||
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
|
||||
ret = append(ret, m.(*v1alpha1.MetricTemplate))
|
||||
})
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// Get retrieves the MetricTemplate from the indexer for a given namespace and name.
|
||||
func (s metricTemplateNamespaceLister) Get(name string) (*v1alpha1.MetricTemplate, error) {
|
||||
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !exists {
|
||||
return nil, errors.NewNotFound(v1alpha1.Resource("metrictemplate"), name)
|
||||
}
|
||||
return obj.(*v1alpha1.MetricTemplate), nil
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
flaggerinformers "github.com/weaveworks/flagger/pkg/client/informers/externalversions/flagger/v1alpha3"
|
||||
flaggerlisters "github.com/weaveworks/flagger/pkg/client/listers/flagger/v1alpha3"
|
||||
"github.com/weaveworks/flagger/pkg/metrics"
|
||||
"github.com/weaveworks/flagger/pkg/metrics/observers"
|
||||
"github.com/weaveworks/flagger/pkg/notifier"
|
||||
"github.com/weaveworks/flagger/pkg/router"
|
||||
)
|
||||
@@ -49,7 +50,7 @@ type Controller struct {
|
||||
notifier notifier.Interface
|
||||
canaryFactory *canary.Factory
|
||||
routerFactory *router.Factory
|
||||
observerFactory *metrics.Factory
|
||||
observerFactory *observers.Factory
|
||||
meshProvider string
|
||||
eventWebhook string
|
||||
}
|
||||
@@ -64,7 +65,7 @@ func NewController(
|
||||
notifier notifier.Interface,
|
||||
canaryFactory *canary.Factory,
|
||||
routerFactory *router.Factory,
|
||||
observerFactory *metrics.Factory,
|
||||
observerFactory *observers.Factory,
|
||||
meshProvider string,
|
||||
version string,
|
||||
eventWebhook string,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"github.com/weaveworks/flagger/pkg/metrics/observers"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -73,7 +74,7 @@ func SetupMocks(c *flaggerv1.Canary) Mocks {
|
||||
rf := router.NewFactory(nil, kubeClient, flaggerClient, "annotationsPrefix", logger, flaggerClient)
|
||||
|
||||
// init observer
|
||||
observerFactory, _ := metrics.NewFactory("fake", 5*time.Second)
|
||||
observerFactory, _ := observers.NewFactory("fake")
|
||||
|
||||
// init canary factory
|
||||
configTracker := canary.ConfigTracker{
|
||||
|
||||
@@ -7,9 +7,11 @@ import (
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3"
|
||||
flaggerv1alpha1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
flaggerv1alpha3 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha3"
|
||||
"github.com/weaveworks/flagger/pkg/canary"
|
||||
"github.com/weaveworks/flagger/pkg/metrics"
|
||||
"github.com/weaveworks/flagger/pkg/metrics/observers"
|
||||
"github.com/weaveworks/flagger/pkg/metrics/providers"
|
||||
"github.com/weaveworks/flagger/pkg/router"
|
||||
)
|
||||
|
||||
@@ -25,7 +27,7 @@ func (c *Controller) scheduleCanaries() {
|
||||
stats := make(map[string]int)
|
||||
|
||||
c.canaries.Range(func(key interface{}, value interface{}) bool {
|
||||
canary := value.(*flaggerv1.Canary)
|
||||
canary := value.(*flaggerv1alpha3.Canary)
|
||||
|
||||
// format: <name>.<namespace>
|
||||
name := key.(string)
|
||||
@@ -196,8 +198,8 @@ func (c *Controller) advanceCanary(name string, namespace string, skipLivenessCh
|
||||
}
|
||||
|
||||
// reset status
|
||||
status := flaggerv1.CanaryStatus{
|
||||
Phase: flaggerv1.CanaryPhaseProgressing,
|
||||
status := flaggerv1alpha3.CanaryStatus{
|
||||
Phase: flaggerv1alpha3.CanaryPhaseProgressing,
|
||||
CanaryWeight: 0,
|
||||
FailedChecks: 0,
|
||||
Iterations: 0,
|
||||
@@ -225,8 +227,8 @@ func (c *Controller) advanceCanary(name string, namespace string, skipLivenessCh
|
||||
}
|
||||
|
||||
// check if we should rollback
|
||||
if cd.Status.Phase == flaggerv1.CanaryPhaseProgressing ||
|
||||
cd.Status.Phase == flaggerv1.CanaryPhaseWaiting {
|
||||
if cd.Status.Phase == flaggerv1alpha3.CanaryPhaseProgressing ||
|
||||
cd.Status.Phase == flaggerv1alpha3.CanaryPhaseWaiting {
|
||||
if ok := c.runRollbackHooks(cd, cd.Status.Phase); ok {
|
||||
c.recordEventWarningf(cd, "Rolling back %s.%s manual webhook invoked", cd.Name, cd.Namespace)
|
||||
c.sendNotification(cd, "Rolling back manual webhook invoked", false, true)
|
||||
@@ -236,7 +238,7 @@ func (c *Controller) advanceCanary(name string, namespace string, skipLivenessCh
|
||||
}
|
||||
|
||||
// route all traffic to primary if analysis has succeeded
|
||||
if cd.Status.Phase == flaggerv1.CanaryPhasePromoting {
|
||||
if cd.Status.Phase == flaggerv1alpha3.CanaryPhasePromoting {
|
||||
if provider != "kubernetes" {
|
||||
c.recordEventInfof(cd, "Routing all traffic to primary")
|
||||
if err := meshRouter.SetRoutes(cd, 100, 0, false); err != nil {
|
||||
@@ -247,7 +249,7 @@ func (c *Controller) advanceCanary(name string, namespace string, skipLivenessCh
|
||||
}
|
||||
|
||||
// update status phase
|
||||
if err := canaryController.SetStatusPhase(cd, flaggerv1.CanaryPhaseFinalising); err != nil {
|
||||
if err := canaryController.SetStatusPhase(cd, flaggerv1alpha3.CanaryPhaseFinalising); err != nil {
|
||||
c.recordEventWarningf(cd, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -256,19 +258,19 @@ func (c *Controller) advanceCanary(name string, namespace string, skipLivenessCh
|
||||
}
|
||||
|
||||
// scale canary to zero if promotion has finished
|
||||
if cd.Status.Phase == flaggerv1.CanaryPhaseFinalising {
|
||||
if cd.Status.Phase == flaggerv1alpha3.CanaryPhaseFinalising {
|
||||
if err := canaryController.Scale(cd, 0); err != nil {
|
||||
c.recordEventWarningf(cd, "%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// set status to succeeded
|
||||
if err := canaryController.SetStatusPhase(cd, flaggerv1.CanaryPhaseSucceeded); err != nil {
|
||||
if err := canaryController.SetStatusPhase(cd, flaggerv1alpha3.CanaryPhaseSucceeded); err != nil {
|
||||
c.recordEventWarningf(cd, "%v", err)
|
||||
return
|
||||
}
|
||||
c.recorder.SetStatus(cd, flaggerv1.CanaryPhaseSucceeded)
|
||||
c.runPostRolloutHooks(cd, flaggerv1.CanaryPhaseSucceeded)
|
||||
c.recorder.SetStatus(cd, flaggerv1alpha3.CanaryPhaseSucceeded)
|
||||
c.runPostRolloutHooks(cd, flaggerv1alpha3.CanaryPhaseSucceeded)
|
||||
c.recordEventInfof(cd, "Promotion completed! Scaling down %s.%s", cd.Spec.TargetRef.Name, cd.Namespace)
|
||||
c.sendNotification(cd, "Canary analysis completed successfully, promotion finished.",
|
||||
false, false)
|
||||
@@ -276,7 +278,7 @@ func (c *Controller) advanceCanary(name string, namespace string, skipLivenessCh
|
||||
}
|
||||
|
||||
// check if the number of failed checks reached the threshold
|
||||
if cd.Status.Phase == flaggerv1.CanaryPhaseProgressing &&
|
||||
if cd.Status.Phase == flaggerv1alpha3.CanaryPhaseProgressing &&
|
||||
(!retriable || cd.Status.FailedChecks >= cd.Spec.CanaryAnalysis.Threshold) {
|
||||
if !retriable {
|
||||
c.recordEventWarningf(cd, "Rolling back %s.%s progress deadline exceeded %v",
|
||||
@@ -349,7 +351,7 @@ func (c *Controller) advanceCanary(name string, namespace string, skipLivenessCh
|
||||
|
||||
}
|
||||
|
||||
func (c *Controller) runCanary(canary *flaggerv1.Canary, canaryController canary.Controller, meshRouter router.Interface, provider string, mirrored bool, canaryWeight int, primaryWeight int, maxWeight int) {
|
||||
func (c *Controller) runCanary(canary *flaggerv1alpha3.Canary, canaryController canary.Controller, meshRouter router.Interface, provider string, mirrored bool, canaryWeight int, primaryWeight int, maxWeight int) {
|
||||
primaryName := fmt.Sprintf("%s-primary", canary.Spec.TargetRef.Name)
|
||||
|
||||
// increase traffic weight
|
||||
@@ -412,14 +414,14 @@ func (c *Controller) runCanary(canary *flaggerv1.Canary, canaryController canary
|
||||
}
|
||||
|
||||
// update status phase
|
||||
if err := canaryController.SetStatusPhase(canary, flaggerv1.CanaryPhasePromoting); err != nil {
|
||||
if err := canaryController.SetStatusPhase(canary, flaggerv1alpha3.CanaryPhasePromoting); err != nil {
|
||||
c.recordEventWarningf(canary, "%v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Controller) runAB(canary *flaggerv1.Canary, canaryController canary.Controller, meshRouter router.Interface, provider string) {
|
||||
func (c *Controller) runAB(canary *flaggerv1alpha3.Canary, canaryController canary.Controller, meshRouter router.Interface, provider string) {
|
||||
primaryName := fmt.Sprintf("%s-primary", canary.Spec.TargetRef.Name)
|
||||
|
||||
// route traffic to canary and increment iterations
|
||||
@@ -454,14 +456,14 @@ func (c *Controller) runAB(canary *flaggerv1.Canary, canaryController canary.Con
|
||||
}
|
||||
|
||||
// update status phase
|
||||
if err := canaryController.SetStatusPhase(canary, flaggerv1.CanaryPhasePromoting); err != nil {
|
||||
if err := canaryController.SetStatusPhase(canary, flaggerv1alpha3.CanaryPhasePromoting); err != nil {
|
||||
c.recordEventWarningf(canary, "%v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Controller) runBlueGreen(canary *flaggerv1.Canary, canaryController canary.Controller, meshRouter router.Interface, provider string, mirrored bool) {
|
||||
func (c *Controller) runBlueGreen(canary *flaggerv1alpha3.Canary, canaryController canary.Controller, meshRouter router.Interface, provider string, mirrored bool) {
|
||||
primaryName := fmt.Sprintf("%s-primary", canary.Spec.TargetRef.Name)
|
||||
|
||||
// increment iterations
|
||||
@@ -522,7 +524,7 @@ func (c *Controller) runBlueGreen(canary *flaggerv1.Canary, canaryController can
|
||||
}
|
||||
|
||||
// update status phase
|
||||
if err := canaryController.SetStatusPhase(canary, flaggerv1.CanaryPhasePromoting); err != nil {
|
||||
if err := canaryController.SetStatusPhase(canary, flaggerv1alpha3.CanaryPhasePromoting); err != nil {
|
||||
c.recordEventWarningf(canary, "%v", err)
|
||||
return
|
||||
}
|
||||
@@ -530,7 +532,7 @@ func (c *Controller) runBlueGreen(canary *flaggerv1.Canary, canaryController can
|
||||
|
||||
}
|
||||
|
||||
func (c *Controller) shouldSkipAnalysis(canary *flaggerv1.Canary, canaryController canary.Controller, meshRouter router.Interface, primaryWeight int, canaryWeight int) bool {
|
||||
func (c *Controller) shouldSkipAnalysis(canary *flaggerv1alpha3.Canary, canaryController canary.Controller, meshRouter router.Interface, primaryWeight int, canaryWeight int) bool {
|
||||
if !canary.Spec.SkipAnalysis {
|
||||
return false
|
||||
}
|
||||
@@ -559,13 +561,13 @@ func (c *Controller) shouldSkipAnalysis(canary *flaggerv1.Canary, canaryControll
|
||||
}
|
||||
|
||||
// update status phase
|
||||
if err := canaryController.SetStatusPhase(canary, flaggerv1.CanaryPhaseSucceeded); err != nil {
|
||||
if err := canaryController.SetStatusPhase(canary, flaggerv1alpha3.CanaryPhaseSucceeded); err != nil {
|
||||
c.recordEventWarningf(canary, "%v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
// notify
|
||||
c.recorder.SetStatus(canary, flaggerv1.CanaryPhaseSucceeded)
|
||||
c.recorder.SetStatus(canary, flaggerv1alpha3.CanaryPhaseSucceeded)
|
||||
c.recordEventInfof(canary, "Promotion completed! Canary analysis was skipped for %s.%s",
|
||||
canary.Spec.TargetRef.Name, canary.Namespace)
|
||||
c.sendNotification(canary, "Canary analysis was skipped, promotion finished.",
|
||||
@@ -574,13 +576,13 @@ func (c *Controller) shouldSkipAnalysis(canary *flaggerv1.Canary, canaryControll
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Controller) shouldAdvance(canary *flaggerv1.Canary, canaryController canary.Controller) (bool, error) {
|
||||
func (c *Controller) shouldAdvance(canary *flaggerv1alpha3.Canary, canaryController canary.Controller) (bool, error) {
|
||||
if canary.Status.LastAppliedSpec == "" ||
|
||||
canary.Status.Phase == flaggerv1.CanaryPhaseInitializing ||
|
||||
canary.Status.Phase == flaggerv1.CanaryPhaseProgressing ||
|
||||
canary.Status.Phase == flaggerv1.CanaryPhaseWaiting ||
|
||||
canary.Status.Phase == flaggerv1.CanaryPhasePromoting ||
|
||||
canary.Status.Phase == flaggerv1.CanaryPhaseFinalising {
|
||||
canary.Status.Phase == flaggerv1alpha3.CanaryPhaseInitializing ||
|
||||
canary.Status.Phase == flaggerv1alpha3.CanaryPhaseProgressing ||
|
||||
canary.Status.Phase == flaggerv1alpha3.CanaryPhaseWaiting ||
|
||||
canary.Status.Phase == flaggerv1alpha3.CanaryPhasePromoting ||
|
||||
canary.Status.Phase == flaggerv1alpha3.CanaryPhaseFinalising {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -601,20 +603,20 @@ func (c *Controller) shouldAdvance(canary *flaggerv1.Canary, canaryController ca
|
||||
|
||||
}
|
||||
|
||||
func (c *Controller) checkCanaryStatus(canary *flaggerv1.Canary, canaryController canary.Controller, shouldAdvance bool) bool {
|
||||
func (c *Controller) checkCanaryStatus(canary *flaggerv1alpha3.Canary, canaryController canary.Controller, shouldAdvance bool) bool {
|
||||
c.recorder.SetStatus(canary, canary.Status.Phase)
|
||||
if canary.Status.Phase == flaggerv1.CanaryPhaseProgressing ||
|
||||
canary.Status.Phase == flaggerv1.CanaryPhasePromoting ||
|
||||
canary.Status.Phase == flaggerv1.CanaryPhaseFinalising {
|
||||
if canary.Status.Phase == flaggerv1alpha3.CanaryPhaseProgressing ||
|
||||
canary.Status.Phase == flaggerv1alpha3.CanaryPhasePromoting ||
|
||||
canary.Status.Phase == flaggerv1alpha3.CanaryPhaseFinalising {
|
||||
return true
|
||||
}
|
||||
|
||||
if canary.Status.Phase == "" || canary.Status.Phase == flaggerv1.CanaryPhaseInitializing {
|
||||
if err := canaryController.SyncStatus(canary, flaggerv1.CanaryStatus{Phase: flaggerv1.CanaryPhaseInitialized}); err != nil {
|
||||
if canary.Status.Phase == "" || canary.Status.Phase == flaggerv1alpha3.CanaryPhaseInitializing {
|
||||
if err := canaryController.SyncStatus(canary, flaggerv1alpha3.CanaryStatus{Phase: flaggerv1alpha3.CanaryPhaseInitialized}); err != nil {
|
||||
c.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).Errorf("%v", err)
|
||||
return false
|
||||
}
|
||||
c.recorder.SetStatus(canary, flaggerv1.CanaryPhaseInitialized)
|
||||
c.recorder.SetStatus(canary, flaggerv1alpha3.CanaryPhaseInitialized)
|
||||
c.recordEventInfof(canary, "Initialization done! %s.%s", canary.Name, canary.Namespace)
|
||||
c.sendNotification(canary, "New deployment detected, initialization completed.",
|
||||
true, false)
|
||||
@@ -623,7 +625,7 @@ func (c *Controller) checkCanaryStatus(canary *flaggerv1.Canary, canaryControlle
|
||||
|
||||
if shouldAdvance {
|
||||
canaryPhaseProgressing := canary.DeepCopy()
|
||||
canaryPhaseProgressing.Status.Phase = flaggerv1.CanaryPhaseProgressing
|
||||
canaryPhaseProgressing.Status.Phase = flaggerv1alpha3.CanaryPhaseProgressing
|
||||
c.recordEventInfof(canaryPhaseProgressing, "New revision detected! Scaling up %s.%s", canaryPhaseProgressing.Spec.TargetRef.Name, canaryPhaseProgressing.Namespace)
|
||||
c.sendNotification(canaryPhaseProgressing, "New revision detected, starting canary analysis.",
|
||||
true, false)
|
||||
@@ -632,18 +634,18 @@ func (c *Controller) checkCanaryStatus(canary *flaggerv1.Canary, canaryControlle
|
||||
c.recordEventErrorf(canary, "%v", err)
|
||||
return false
|
||||
}
|
||||
if err := canaryController.SyncStatus(canary, flaggerv1.CanaryStatus{Phase: flaggerv1.CanaryPhaseProgressing}); err != nil {
|
||||
if err := canaryController.SyncStatus(canary, flaggerv1alpha3.CanaryStatus{Phase: flaggerv1alpha3.CanaryPhaseProgressing}); err != nil {
|
||||
c.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).Errorf("%v", err)
|
||||
return false
|
||||
}
|
||||
c.recorder.SetStatus(canary, flaggerv1.CanaryPhaseProgressing)
|
||||
c.recorder.SetStatus(canary, flaggerv1alpha3.CanaryPhaseProgressing)
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Controller) hasCanaryRevisionChanged(canary *flaggerv1.Canary, canaryController canary.Controller) bool {
|
||||
if canary.Status.Phase == flaggerv1.CanaryPhaseProgressing {
|
||||
func (c *Controller) hasCanaryRevisionChanged(canary *flaggerv1alpha3.Canary, canaryController canary.Controller) bool {
|
||||
if canary.Status.Phase == flaggerv1alpha3.CanaryPhaseProgressing {
|
||||
if diff, _ := canaryController.HasTargetChanged(canary); diff {
|
||||
return true
|
||||
}
|
||||
@@ -654,13 +656,13 @@ func (c *Controller) hasCanaryRevisionChanged(canary *flaggerv1.Canary, canaryCo
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Controller) runConfirmRolloutHooks(canary *flaggerv1.Canary, canaryController canary.Controller) bool {
|
||||
func (c *Controller) runConfirmRolloutHooks(canary *flaggerv1alpha3.Canary, canaryController canary.Controller) bool {
|
||||
for _, webhook := range canary.Spec.CanaryAnalysis.Webhooks {
|
||||
if webhook.Type == flaggerv1.ConfirmRolloutHook {
|
||||
err := CallWebhook(canary.Name, canary.Namespace, flaggerv1.CanaryPhaseProgressing, webhook)
|
||||
if webhook.Type == flaggerv1alpha3.ConfirmRolloutHook {
|
||||
err := CallWebhook(canary.Name, canary.Namespace, flaggerv1alpha3.CanaryPhaseProgressing, webhook)
|
||||
if err != nil {
|
||||
if canary.Status.Phase != flaggerv1.CanaryPhaseWaiting {
|
||||
if err := canaryController.SetStatusPhase(canary, flaggerv1.CanaryPhaseWaiting); err != nil {
|
||||
if canary.Status.Phase != flaggerv1alpha3.CanaryPhaseWaiting {
|
||||
if err := canaryController.SetStatusPhase(canary, flaggerv1alpha3.CanaryPhaseWaiting); err != nil {
|
||||
c.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).Errorf("%v", err)
|
||||
}
|
||||
c.recordEventWarningf(canary, "Halt %s.%s advancement waiting for approval %s",
|
||||
@@ -669,8 +671,8 @@ func (c *Controller) runConfirmRolloutHooks(canary *flaggerv1.Canary, canaryCont
|
||||
}
|
||||
return false
|
||||
} else {
|
||||
if canary.Status.Phase == flaggerv1.CanaryPhaseWaiting {
|
||||
if err := canaryController.SetStatusPhase(canary, flaggerv1.CanaryPhaseProgressing); err != nil {
|
||||
if canary.Status.Phase == flaggerv1alpha3.CanaryPhaseWaiting {
|
||||
if err := canaryController.SetStatusPhase(canary, flaggerv1alpha3.CanaryPhaseProgressing); err != nil {
|
||||
c.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).Errorf("%v", err)
|
||||
return false
|
||||
}
|
||||
@@ -683,10 +685,10 @@ func (c *Controller) runConfirmRolloutHooks(canary *flaggerv1.Canary, canaryCont
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Controller) runConfirmPromotionHooks(canary *flaggerv1.Canary) bool {
|
||||
func (c *Controller) runConfirmPromotionHooks(canary *flaggerv1alpha3.Canary) bool {
|
||||
for _, webhook := range canary.Spec.CanaryAnalysis.Webhooks {
|
||||
if webhook.Type == flaggerv1.ConfirmPromotionHook {
|
||||
err := CallWebhook(canary.Name, canary.Namespace, flaggerv1.CanaryPhaseProgressing, webhook)
|
||||
if webhook.Type == flaggerv1alpha3.ConfirmPromotionHook {
|
||||
err := CallWebhook(canary.Name, canary.Namespace, flaggerv1alpha3.CanaryPhaseProgressing, webhook)
|
||||
if err != nil {
|
||||
c.recordEventWarningf(canary, "Halt %s.%s advancement waiting for promotion approval %s",
|
||||
canary.Name, canary.Namespace, webhook.Name)
|
||||
@@ -700,10 +702,10 @@ func (c *Controller) runConfirmPromotionHooks(canary *flaggerv1.Canary) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Controller) runPreRolloutHooks(canary *flaggerv1.Canary) bool {
|
||||
func (c *Controller) runPreRolloutHooks(canary *flaggerv1alpha3.Canary) bool {
|
||||
for _, webhook := range canary.Spec.CanaryAnalysis.Webhooks {
|
||||
if webhook.Type == flaggerv1.PreRolloutHook {
|
||||
err := CallWebhook(canary.Name, canary.Namespace, flaggerv1.CanaryPhaseProgressing, webhook)
|
||||
if webhook.Type == flaggerv1alpha3.PreRolloutHook {
|
||||
err := CallWebhook(canary.Name, canary.Namespace, flaggerv1alpha3.CanaryPhaseProgressing, webhook)
|
||||
if err != nil {
|
||||
c.recordEventWarningf(canary, "Halt %s.%s advancement pre-rollout check %s failed %v",
|
||||
canary.Name, canary.Namespace, webhook.Name, err)
|
||||
@@ -716,9 +718,9 @@ func (c *Controller) runPreRolloutHooks(canary *flaggerv1.Canary) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Controller) runPostRolloutHooks(canary *flaggerv1.Canary, phase flaggerv1.CanaryPhase) bool {
|
||||
func (c *Controller) runPostRolloutHooks(canary *flaggerv1alpha3.Canary, phase flaggerv1alpha3.CanaryPhase) bool {
|
||||
for _, webhook := range canary.Spec.CanaryAnalysis.Webhooks {
|
||||
if webhook.Type == flaggerv1.PostRolloutHook {
|
||||
if webhook.Type == flaggerv1alpha3.PostRolloutHook {
|
||||
err := CallWebhook(canary.Name, canary.Namespace, phase, webhook)
|
||||
if err != nil {
|
||||
c.recordEventWarningf(canary, "Post-rollout hook %s failed %v", webhook.Name, err)
|
||||
@@ -731,9 +733,9 @@ func (c *Controller) runPostRolloutHooks(canary *flaggerv1.Canary, phase flagger
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Controller) runRollbackHooks(canary *flaggerv1.Canary, phase flaggerv1.CanaryPhase) bool {
|
||||
func (c *Controller) runRollbackHooks(canary *flaggerv1alpha3.Canary, phase flaggerv1alpha3.CanaryPhase) bool {
|
||||
for _, webhook := range canary.Spec.CanaryAnalysis.Webhooks {
|
||||
if webhook.Type == flaggerv1.RollbackHook {
|
||||
if webhook.Type == flaggerv1alpha3.RollbackHook {
|
||||
err := CallWebhook(canary.Name, canary.Namespace, phase, webhook)
|
||||
if err != nil {
|
||||
c.recordEventInfof(canary, "Rollback hook %s not signaling a rollback", webhook.Name)
|
||||
@@ -746,11 +748,11 @@ func (c *Controller) runRollbackHooks(canary *flaggerv1.Canary, phase flaggerv1.
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Controller) runAnalysis(r *flaggerv1.Canary) bool {
|
||||
func (c *Controller) runAnalysis(r *flaggerv1alpha3.Canary) bool {
|
||||
// run external checks
|
||||
for _, webhook := range r.Spec.CanaryAnalysis.Webhooks {
|
||||
if webhook.Type == "" || webhook.Type == flaggerv1.RolloutHook {
|
||||
err := CallWebhook(r.Name, r.Namespace, flaggerv1.CanaryPhaseProgressing, webhook)
|
||||
if webhook.Type == "" || webhook.Type == flaggerv1alpha3.RolloutHook {
|
||||
err := CallWebhook(r.Name, r.Namespace, flaggerv1alpha3.CanaryPhaseProgressing, webhook)
|
||||
if err != nil {
|
||||
c.recordEventWarningf(r, "Halt %s.%s advancement external check %s failed %v",
|
||||
r.Name, r.Namespace, webhook.Name, err)
|
||||
@@ -759,6 +761,20 @@ func (c *Controller) runAnalysis(r *flaggerv1.Canary) bool {
|
||||
}
|
||||
}
|
||||
|
||||
ok := c.runBuiltinMetricChecks(r)
|
||||
if !ok {
|
||||
return ok
|
||||
}
|
||||
|
||||
ok = c.runMetricChecks(r)
|
||||
if !ok {
|
||||
return ok
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Controller) runBuiltinMetricChecks(r *flaggerv1alpha3.Canary) bool {
|
||||
// override the global provider if one is specified in the canary spec
|
||||
var metricsProvider string
|
||||
// set the metrics provider to Crossover Prometheus when Crossover is the mesh provider
|
||||
@@ -786,11 +802,9 @@ func (c *Controller) runAnalysis(r *flaggerv1.Canary) bool {
|
||||
observerFactory := c.observerFactory
|
||||
|
||||
// override the global metrics server if one is specified in the canary spec
|
||||
metricsServer := c.observerFactory.Client.GetMetricsServer()
|
||||
if r.Spec.MetricsServer != "" {
|
||||
metricsServer = r.Spec.MetricsServer
|
||||
var err error
|
||||
observerFactory, err = metrics.NewFactory(metricsServer, 5*time.Second)
|
||||
observerFactory, err = observers.NewFactory(r.Spec.MetricsServer)
|
||||
if err != nil {
|
||||
c.recordEventErrorf(r, "Error building Prometheus client for %s %v", r.Spec.MetricsServer, err)
|
||||
return false
|
||||
@@ -805,13 +819,13 @@ func (c *Controller) runAnalysis(r *flaggerv1.Canary) bool {
|
||||
}
|
||||
|
||||
if metric.Name == "request-success-rate" {
|
||||
val, err := observer.GetRequestSuccessRate(r.Spec.TargetRef.Name, r.Namespace, metric.Interval)
|
||||
val, err := observer.GetRequestSuccessRate(toMetricModel(r, metric.Interval))
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "no values found") {
|
||||
c.recordEventWarningf(r, "Halt advancement no values found for %s metric %s probably %s.%s is not receiving traffic",
|
||||
metricsProvider, metric.Name, r.Spec.TargetRef.Name, r.Namespace)
|
||||
} else {
|
||||
c.recordEventErrorf(r, "Metrics server %s query failed: %v", metricsServer, err)
|
||||
c.recordEventErrorf(r, "Prometheus query failed: %v", err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -825,13 +839,13 @@ func (c *Controller) runAnalysis(r *flaggerv1.Canary) bool {
|
||||
}
|
||||
|
||||
if metric.Name == "request-duration" {
|
||||
val, err := observer.GetRequestDuration(r.Spec.TargetRef.Name, r.Namespace, metric.Interval)
|
||||
val, err := observer.GetRequestDuration(toMetricModel(r, metric.Interval))
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "no values found") {
|
||||
c.recordEventWarningf(r, "Halt advancement no values found for %s metric %s probably %s.%s is not receiving traffic",
|
||||
metricsProvider, metric.Name, r.Spec.TargetRef.Name, r.Namespace)
|
||||
} else {
|
||||
c.recordEventErrorf(r, "Metrics server %s query failed: %v", metricsServer, err)
|
||||
c.recordEventErrorf(r, "Prometheus query failed: %v", err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -853,7 +867,7 @@ func (c *Controller) runAnalysis(r *flaggerv1.Canary) bool {
|
||||
c.recordEventWarningf(r, "Halt advancement no values found for custom metric: %s",
|
||||
metric.Name)
|
||||
} else {
|
||||
c.recordEventErrorf(r, "Metrics server %s query failed for %s: %v", metricsServer, metric.Name, err)
|
||||
c.recordEventErrorf(r, "Prometheus query failed for %s: %v", metric.Name, err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -868,7 +882,88 @@ func (c *Controller) runAnalysis(r *flaggerv1.Canary) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Controller) rollback(canary *flaggerv1.Canary, canaryController canary.Controller, meshRouter router.Interface) {
|
||||
func (c *Controller) runMetricChecks(r *flaggerv1alpha3.Canary) bool {
|
||||
for _, metric := range r.Spec.CanaryAnalysis.Metrics {
|
||||
if metric.TemplateRef != nil {
|
||||
namespace := r.Namespace
|
||||
if metric.TemplateRef.Namespace != "" {
|
||||
namespace = metric.TemplateRef.Namespace
|
||||
}
|
||||
|
||||
template, err := c.flaggerClient.FlaggerV1alpha1().MetricTemplates(namespace).Get(metric.TemplateRef.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
c.recordEventErrorf(r, "Metric template %s.%s error: %v", metric.TemplateRef.Name, namespace, err)
|
||||
return false
|
||||
}
|
||||
|
||||
var credentials map[string][]byte
|
||||
if template.Spec.Provider.SecretRef != nil {
|
||||
secret, err := c.kubeClient.CoreV1().Secrets(namespace).Get(template.Spec.Provider.SecretRef.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
c.recordEventErrorf(r, "Metric template %s.%s secret %s error: %v",
|
||||
metric.TemplateRef.Name, namespace, template.Spec.Provider.SecretRef.Name, err)
|
||||
return false
|
||||
}
|
||||
credentials = secret.Data
|
||||
}
|
||||
|
||||
factory := providers.Factory{}
|
||||
provider, err := factory.Provider(template.Spec.Provider, credentials)
|
||||
if err != nil {
|
||||
c.recordEventErrorf(r, "Metric template %s.%s provider %s error: %v",
|
||||
metric.TemplateRef.Name, namespace, template.Spec.Provider.Type, err)
|
||||
return false
|
||||
}
|
||||
|
||||
query, err := observers.RenderQuery(template.Spec.Query, toMetricModel(r, metric.Interval))
|
||||
if err != nil {
|
||||
c.recordEventErrorf(r, "Metric template %s.%s query render error: %v",
|
||||
metric.TemplateRef.Name, namespace, err)
|
||||
return false
|
||||
}
|
||||
|
||||
val, err := provider.RunQuery(query)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "no values found") {
|
||||
c.recordEventWarningf(r, "Halt advancement no values found for custom metric: %s",
|
||||
metric.Name)
|
||||
} else {
|
||||
c.recordEventErrorf(r, "Metric query failed for %s: %v", metric.Name, err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if val > metric.Threshold {
|
||||
c.recordEventWarningf(r, "Halt %s.%s advancement %s %.2f > %v",
|
||||
r.Name, r.Namespace, metric.Name, val, metric.Threshold)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func toMetricModel(r *flaggerv1alpha3.Canary, interval string) flaggerv1alpha1.MetricTemplateModel {
|
||||
service := r.Spec.TargetRef.Name
|
||||
if r.Spec.Service.Name != "" {
|
||||
service = r.Spec.Service.Name
|
||||
}
|
||||
ingress := r.Spec.TargetRef.Name
|
||||
if r.Spec.IngressRef != nil {
|
||||
ingress = r.Spec.IngressRef.Name
|
||||
}
|
||||
return flaggerv1alpha1.MetricTemplateModel{
|
||||
Name: r.Name,
|
||||
Namespace: r.Namespace,
|
||||
Target: r.Spec.TargetRef.Name,
|
||||
Service: service,
|
||||
Ingress: ingress,
|
||||
Interval: interval,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Controller) rollback(canary *flaggerv1alpha3.Canary, canaryController canary.Controller, meshRouter router.Interface) {
|
||||
if canary.Status.FailedChecks >= canary.Spec.CanaryAnalysis.Threshold {
|
||||
c.recordEventWarningf(canary, "Rolling back %s.%s failed checks threshold reached %v",
|
||||
canary.Name, canary.Namespace, canary.Status.FailedChecks)
|
||||
@@ -885,7 +980,7 @@ func (c *Controller) rollback(canary *flaggerv1.Canary, canaryController canary.
|
||||
}
|
||||
|
||||
canaryPhaseFailed := canary.DeepCopy()
|
||||
canaryPhaseFailed.Status.Phase = flaggerv1.CanaryPhaseFailed
|
||||
canaryPhaseFailed.Status.Phase = flaggerv1alpha3.CanaryPhaseFailed
|
||||
c.recordEventWarningf(canaryPhaseFailed, "Canary failed! Scaling down %s.%s",
|
||||
canaryPhaseFailed.Name, canaryPhaseFailed.Namespace)
|
||||
|
||||
@@ -898,11 +993,11 @@ func (c *Controller) rollback(canary *flaggerv1.Canary, canaryController canary.
|
||||
}
|
||||
|
||||
// mark canary as failed
|
||||
if err := canaryController.SyncStatus(canary, flaggerv1.CanaryStatus{Phase: flaggerv1.CanaryPhaseFailed, CanaryWeight: 0}); err != nil {
|
||||
if err := canaryController.SyncStatus(canary, flaggerv1alpha3.CanaryStatus{Phase: flaggerv1alpha3.CanaryPhaseFailed, CanaryWeight: 0}); err != nil {
|
||||
c.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).Errorf("%v", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.recorder.SetStatus(canary, flaggerv1.CanaryPhaseFailed)
|
||||
c.runPostRolloutHooks(canary, flaggerv1.CanaryPhaseFailed)
|
||||
c.recorder.SetStatus(canary, flaggerv1alpha3.CanaryPhaseFailed)
|
||||
c.runPostRolloutHooks(canary, flaggerv1alpha3.CanaryPhaseFailed)
|
||||
}
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var appMeshQueries = map[string]string{
|
||||
"request-success-rate": `
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq{
|
||||
kubernetes_namespace="{{ .Namespace }}",
|
||||
kubernetes_pod_name=~"{{ .Name }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)",
|
||||
envoy_response_code!~"5.*"
|
||||
}[{{ .Interval }}]
|
||||
)
|
||||
)
|
||||
/
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq{
|
||||
kubernetes_namespace="{{ .Namespace }}",
|
||||
kubernetes_pod_name=~"{{ .Name }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"
|
||||
}[{{ .Interval }}]
|
||||
)
|
||||
)
|
||||
* 100`,
|
||||
"request-duration": `
|
||||
histogram_quantile(
|
||||
0.99,
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq_time_bucket{
|
||||
kubernetes_namespace="{{ .Namespace }}",
|
||||
kubernetes_pod_name=~"{{ .Name }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"
|
||||
}[{{ .Interval }}]
|
||||
)
|
||||
) by (le)
|
||||
)`,
|
||||
}
|
||||
|
||||
type AppMeshObserver struct {
|
||||
client *PrometheusClient
|
||||
}
|
||||
|
||||
func (ob *AppMeshObserver) GetRequestSuccessRate(name string, namespace string, interval string) (float64, error) {
|
||||
query, err := ob.client.RenderQuery(name, namespace, interval, appMeshQueries["request-success-rate"])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (ob *AppMeshObserver) GetRequestDuration(name string, namespace string, interval string) (time.Duration, error) {
|
||||
query, err := ob.client.RenderQuery(name, namespace, interval, appMeshQueries["request-duration"])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
ms := time.Duration(int64(value)) * time.Millisecond
|
||||
return ms, nil
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestPrometheusClient_RunQuery(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
json := `{"status":"success","data":{"resultType":"vector","result":[{"metric":{},"value":[1545905245.458,"100"]}]}}`
|
||||
w.Write([]byte(json))
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
client, err := NewPrometheusClient(ts.URL, time.Second)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
query := `
|
||||
histogram_quantile(0.99,
|
||||
sum(
|
||||
rate(
|
||||
http_request_duration_seconds_bucket{
|
||||
kubernetes_namespace="test",
|
||||
kubernetes_pod_name=~"podinfo-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"
|
||||
}[1m]
|
||||
)
|
||||
) by (le)
|
||||
)`
|
||||
|
||||
val, err := client.RunQuery(query)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
if val != 100 {
|
||||
t.Errorf("Got %v wanted %v", val, 100)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrometheusClient_IsOnline(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
json := `{"status":"success","data":{"config.file":"/etc/prometheus/prometheus.yml"}}`
|
||||
w.Write([]byte(json))
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
client, err := NewPrometheusClient(ts.URL, time.Second)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ok, err := client.IsOnline()
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
if !ok {
|
||||
t.Errorf("Got %v wanted %v", ok, true)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrometheusClient_IsOffline(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusBadGateway)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
client, err := NewPrometheusClient(ts.URL, time.Second)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ok, err := client.IsOnline()
|
||||
if err == nil {
|
||||
t.Errorf("Got no error wanted %v", http.StatusBadGateway)
|
||||
}
|
||||
|
||||
if ok {
|
||||
t.Errorf("Got %v wanted %v", ok, false)
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var crossoverQueries = map[string]string{
|
||||
"request-success-rate": `
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq{
|
||||
kubernetes_namespace="{{ .Namespace }}",
|
||||
envoy_cluster_name=~"{{ .Name }}-canary",
|
||||
envoy_response_code!~"5.*"
|
||||
}[{{ .Interval }}]
|
||||
)
|
||||
)
|
||||
/
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq{
|
||||
kubernetes_namespace="{{ .Namespace }}",
|
||||
envoy_cluster_name=~"{{ .Name }}-canary"
|
||||
}[{{ .Interval }}]
|
||||
)
|
||||
)
|
||||
* 100`,
|
||||
"request-duration": `
|
||||
histogram_quantile(
|
||||
0.99,
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq_time_bucket{
|
||||
kubernetes_namespace="{{ .Namespace }}",
|
||||
envoy_cluster_name=~"{{ .Name }}-canary"
|
||||
}[{{ .Interval }}]
|
||||
)
|
||||
) by (le)
|
||||
)`,
|
||||
}
|
||||
|
||||
type CrossoverObserver struct {
|
||||
client *PrometheusClient
|
||||
}
|
||||
|
||||
func (ob *CrossoverObserver) GetRequestSuccessRate(name string, namespace string, interval string) (float64, error) {
|
||||
query, err := ob.client.RenderQuery(name, namespace, interval, crossoverQueries["request-success-rate"])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (ob *CrossoverObserver) GetRequestDuration(name string, namespace string, interval string) (time.Duration, error) {
|
||||
query, err := ob.client.RenderQuery(name, namespace, interval, crossoverQueries["request-duration"])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
ms := time.Duration(int64(value)) * time.Millisecond
|
||||
return ms, nil
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var crossoverServiceQueries = map[string]string{
|
||||
"request-success-rate": `
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq{
|
||||
kubernetes_namespace="{{ .Namespace }}",
|
||||
envoy_cluster_name="{{ .Name }}-canary",
|
||||
envoy_response_code!~"5.*"
|
||||
}[{{ .Interval }}]
|
||||
)
|
||||
)
|
||||
/
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq{
|
||||
kubernetes_namespace="{{ .Namespace }}",
|
||||
envoy_cluster_name="{{ .Name }}-canary"
|
||||
}[{{ .Interval }}]
|
||||
)
|
||||
)
|
||||
* 100`,
|
||||
"request-duration": `
|
||||
histogram_quantile(
|
||||
0.99,
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq_time_bucket{
|
||||
kubernetes_namespace="{{ .Namespace }}",
|
||||
envoy_cluster_name="{{ .Name }}-canary"
|
||||
}[{{ .Interval }}]
|
||||
)
|
||||
) by (le)
|
||||
)`,
|
||||
}
|
||||
|
||||
type CrossoverServiceObserver struct {
|
||||
client *PrometheusClient
|
||||
}
|
||||
|
||||
func (ob *CrossoverServiceObserver) GetRequestSuccessRate(name string, namespace string, interval string) (float64, error) {
|
||||
query, err := ob.client.RenderQuery(name, namespace, interval, crossoverServiceQueries["request-success-rate"])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (ob *CrossoverServiceObserver) GetRequestDuration(name string, namespace string, interval string) (time.Duration, error) {
|
||||
query, err := ob.client.RenderQuery(name, namespace, interval, crossoverServiceQueries["request-duration"])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
ms := time.Duration(int64(value)) * time.Millisecond
|
||||
return ms, nil
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package metrics
|
||||
|
||||
import "time"
|
||||
|
||||
var httpQueries = map[string]string{
|
||||
"request-success-rate": `
|
||||
sum(
|
||||
rate(
|
||||
http_request_duration_seconds_count{
|
||||
kubernetes_namespace="{{ .Namespace }}",
|
||||
kubernetes_pod_name=~"{{ .Name }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)",
|
||||
status!~"5.*"
|
||||
}[{{ .Interval }}]
|
||||
)
|
||||
)
|
||||
/
|
||||
sum(
|
||||
rate(
|
||||
http_request_duration_seconds_count{
|
||||
kubernetes_namespace="{{ .Namespace }}",
|
||||
kubernetes_pod_name=~"{{ .Name }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"
|
||||
}[{{ .Interval }}]
|
||||
)
|
||||
)
|
||||
* 100`,
|
||||
"request-duration": `
|
||||
histogram_quantile(
|
||||
0.99,
|
||||
sum(
|
||||
rate(
|
||||
http_request_duration_seconds_bucket{
|
||||
kubernetes_namespace="{{ .Namespace }}",
|
||||
kubernetes_pod_name=~"{{ .Name }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"
|
||||
}[{{ .Interval }}]
|
||||
)
|
||||
) by (le)
|
||||
)`,
|
||||
}
|
||||
|
||||
type HttpObserver struct {
|
||||
client *PrometheusClient
|
||||
}
|
||||
|
||||
func (ob *HttpObserver) GetRequestSuccessRate(name string, namespace string, interval string) (float64, error) {
|
||||
query, err := ob.client.RenderQuery(name, namespace, interval, httpQueries["request-success-rate"])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (ob *HttpObserver) GetRequestDuration(name string, namespace string, interval string) (time.Duration, error) {
|
||||
query, err := ob.client.RenderQuery(name, namespace, interval, httpQueries["request-duration"])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
ms := time.Duration(int64(value*1000)) * time.Millisecond
|
||||
return ms, nil
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var istioQueries = map[string]string{
|
||||
"request-success-rate": `
|
||||
sum(
|
||||
rate(
|
||||
istio_requests_total{
|
||||
reporter="destination",
|
||||
destination_workload_namespace="{{ .Namespace }}",
|
||||
destination_workload=~"{{ .Name }}",
|
||||
response_code!~"5.*"
|
||||
}[{{ .Interval }}]
|
||||
)
|
||||
)
|
||||
/
|
||||
sum(
|
||||
rate(
|
||||
istio_requests_total{
|
||||
reporter="destination",
|
||||
destination_workload_namespace="{{ .Namespace }}",
|
||||
destination_workload=~"{{ .Name }}"
|
||||
}[{{ .Interval }}]
|
||||
)
|
||||
)
|
||||
* 100`,
|
||||
"request-duration": `
|
||||
histogram_quantile(
|
||||
0.99,
|
||||
sum(
|
||||
rate(
|
||||
istio_request_duration_seconds_bucket{
|
||||
reporter="destination",
|
||||
destination_workload_namespace="{{ .Namespace }}",
|
||||
destination_workload=~"{{ .Name }}"
|
||||
}[{{ .Interval }}]
|
||||
)
|
||||
) by (le)
|
||||
)`,
|
||||
}
|
||||
|
||||
type IstioObserver struct {
|
||||
client *PrometheusClient
|
||||
}
|
||||
|
||||
func (ob *IstioObserver) GetRequestSuccessRate(name string, namespace string, interval string) (float64, error) {
|
||||
query, err := ob.client.RenderQuery(name, namespace, interval, istioQueries["request-success-rate"])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (ob *IstioObserver) GetRequestDuration(name string, namespace string, interval string) (time.Duration, error) {
|
||||
query, err := ob.client.RenderQuery(name, namespace, interval, istioQueries["request-duration"])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
ms := time.Duration(int64(value*1000)) * time.Millisecond
|
||||
return ms, nil
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var linkerdQueries = map[string]string{
|
||||
"request-success-rate": `
|
||||
sum(
|
||||
rate(
|
||||
response_total{
|
||||
namespace="{{ .Namespace }}",
|
||||
deployment=~"{{ .Name }}",
|
||||
classification!="failure",
|
||||
direction="inbound"
|
||||
}[{{ .Interval }}]
|
||||
)
|
||||
)
|
||||
/
|
||||
sum(
|
||||
rate(
|
||||
response_total{
|
||||
namespace="{{ .Namespace }}",
|
||||
deployment=~"{{ .Name }}",
|
||||
direction="inbound"
|
||||
}[{{ .Interval }}]
|
||||
)
|
||||
)
|
||||
* 100`,
|
||||
"request-duration": `
|
||||
histogram_quantile(
|
||||
0.99,
|
||||
sum(
|
||||
rate(
|
||||
response_latency_ms_bucket{
|
||||
namespace="{{ .Namespace }}",
|
||||
deployment=~"{{ .Name }}",
|
||||
direction="inbound"
|
||||
}[{{ .Interval }}]
|
||||
)
|
||||
) by (le)
|
||||
)`,
|
||||
}
|
||||
|
||||
type LinkerdObserver struct {
|
||||
client *PrometheusClient
|
||||
}
|
||||
|
||||
func (ob *LinkerdObserver) GetRequestSuccessRate(name string, namespace string, interval string) (float64, error) {
|
||||
query, err := ob.client.RenderQuery(name, namespace, interval, linkerdQueries["request-success-rate"])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (ob *LinkerdObserver) GetRequestDuration(name string, namespace string, interval string) (time.Duration, error) {
|
||||
query, err := ob.client.RenderQuery(name, namespace, interval, linkerdQueries["request-duration"])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
ms := time.Duration(int64(value)) * time.Millisecond
|
||||
return ms, nil
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var nginxQueries = map[string]string{
|
||||
"request-success-rate": `
|
||||
sum(
|
||||
rate(
|
||||
nginx_ingress_controller_requests{
|
||||
namespace="{{ .Namespace }}",
|
||||
ingress="{{ .Name }}",
|
||||
status!~"5.*"
|
||||
}[{{ .Interval }}]
|
||||
)
|
||||
)
|
||||
/
|
||||
sum(
|
||||
rate(
|
||||
nginx_ingress_controller_requests{
|
||||
namespace="{{ .Namespace }}",
|
||||
ingress="{{ .Name }}"
|
||||
}[{{ .Interval }}]
|
||||
)
|
||||
)
|
||||
* 100`,
|
||||
"request-duration": `
|
||||
sum(
|
||||
rate(
|
||||
nginx_ingress_controller_ingress_upstream_latency_seconds_sum{
|
||||
namespace="{{ .Namespace }}",
|
||||
ingress="{{ .Name }}"
|
||||
}[{{ .Interval }}]
|
||||
)
|
||||
)
|
||||
/
|
||||
sum(
|
||||
rate(
|
||||
nginx_ingress_controller_ingress_upstream_latency_seconds_count{
|
||||
namespace="{{ .Namespace }}",
|
||||
ingress="{{ .Name }}"
|
||||
}[{{ .Interval }}]
|
||||
)
|
||||
)
|
||||
* 1000`,
|
||||
}
|
||||
|
||||
type NginxObserver struct {
|
||||
client *PrometheusClient
|
||||
}
|
||||
|
||||
func (ob *NginxObserver) GetRequestSuccessRate(name string, namespace string, interval string) (float64, error) {
|
||||
query, err := ob.client.RenderQuery(name, namespace, interval, nginxQueries["request-success-rate"])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (ob *NginxObserver) GetRequestDuration(name string, namespace string, interval string) (time.Duration, error) {
|
||||
query, err := ob.client.RenderQuery(name, namespace, interval, nginxQueries["request-duration"])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
ms := time.Duration(int64(value)) * time.Millisecond
|
||||
return ms, nil
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
GetRequestSuccessRate(name string, namespace string, interval string) (float64, error)
|
||||
GetRequestDuration(name string, namespace string, interval string) (time.Duration, error)
|
||||
}
|
||||
76
pkg/metrics/observers/appmesh.go
Normal file
76
pkg/metrics/observers/appmesh.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package observers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
"github.com/weaveworks/flagger/pkg/metrics/providers"
|
||||
)
|
||||
|
||||
var appMeshQueries = map[string]string{
|
||||
"request-success-rate": `
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq{
|
||||
kubernetes_namespace="{{ namespace }}",
|
||||
kubernetes_pod_name=~"{{ target }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)",
|
||||
envoy_response_code!~"5.*"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
/
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq{
|
||||
kubernetes_namespace="{{ namespace }}",
|
||||
kubernetes_pod_name=~"{{ target }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
* 100`,
|
||||
"request-duration": `
|
||||
histogram_quantile(
|
||||
0.99,
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq_time_bucket{
|
||||
kubernetes_namespace="{{ namespace }}",
|
||||
kubernetes_pod_name=~"{{ target }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
) by (le)
|
||||
)`,
|
||||
}
|
||||
|
||||
type AppMeshObserver struct {
|
||||
client providers.Interface
|
||||
}
|
||||
|
||||
func (ob *AppMeshObserver) GetRequestSuccessRate(model flaggerv1.MetricTemplateModel) (float64, error) {
|
||||
query, err := RenderQuery(appMeshQueries["request-success-rate"], model)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (ob *AppMeshObserver) GetRequestDuration(model flaggerv1.MetricTemplateModel) (time.Duration, error) {
|
||||
query, err := RenderQuery(appMeshQueries["request-duration"], model)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
ms := time.Duration(int64(value)) * time.Millisecond
|
||||
return ms, nil
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
package metrics
|
||||
package observers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
"github.com/weaveworks/flagger/pkg/metrics/providers"
|
||||
)
|
||||
|
||||
func TestAppMeshObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
@@ -21,7 +24,11 @@ func TestAppMeshObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
client, err := NewPrometheusClient(ts.URL, time.Second)
|
||||
client, err := providers.NewPrometheusProvider(flaggerv1.MetricTemplateProvider{
|
||||
Type: "prometheus",
|
||||
Address: ts.URL,
|
||||
SecretRef: nil,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -30,7 +37,13 @@ func TestAppMeshObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
client: client,
|
||||
}
|
||||
|
||||
val, err := observer.GetRequestSuccessRate("podinfo", "default", "1m")
|
||||
val, err := observer.GetRequestSuccessRate(flaggerv1.MetricTemplateModel{
|
||||
Name: "podinfo",
|
||||
Namespace: "default",
|
||||
Target: "podinfo",
|
||||
Service: "podinfo",
|
||||
Interval: "1m",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
@@ -54,7 +67,11 @@ func TestAppMeshObserver_GetRequestDuration(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
client, err := NewPrometheusClient(ts.URL, time.Second)
|
||||
client, err := providers.NewPrometheusProvider(flaggerv1.MetricTemplateProvider{
|
||||
Type: "prometheus",
|
||||
Address: ts.URL,
|
||||
SecretRef: nil,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -63,7 +80,13 @@ func TestAppMeshObserver_GetRequestDuration(t *testing.T) {
|
||||
client: client,
|
||||
}
|
||||
|
||||
val, err := observer.GetRequestDuration("podinfo", "default", "1m")
|
||||
val, err := observer.GetRequestDuration(flaggerv1.MetricTemplateModel{
|
||||
Name: "podinfo",
|
||||
Namespace: "default",
|
||||
Target: "podinfo",
|
||||
Service: "podinfo",
|
||||
Interval: "1m",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
package metrics
|
||||
package observers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
"github.com/weaveworks/flagger/pkg/metrics/providers"
|
||||
)
|
||||
|
||||
//envoy_cluster_name="test_podinfo-canary_9898"
|
||||
@@ -11,17 +14,17 @@ var contourQueries = map[string]string{
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq{
|
||||
envoy_cluster_name=~"{{ .Namespace }}_{{ .Name }}-canary_[0-9a-zA-Z-]+",
|
||||
envoy_cluster_name=~"{{ namespace }}_{{ target }}-canary_[0-9a-zA-Z-]+",
|
||||
envoy_response_code!~"5.*"
|
||||
}[{{ .Interval }}]
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
/
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq{
|
||||
envoy_cluster_name=~"{{ .Namespace }}_{{ .Name }}-canary_[0-9a-zA-Z-]+",
|
||||
}[{{ .Interval }}]
|
||||
envoy_cluster_name=~"{{ namespace }}_{{ target }}-canary_[0-9a-zA-Z-]+",
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
* 100`,
|
||||
@@ -31,19 +34,19 @@ var contourQueries = map[string]string{
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq_time_bucket{
|
||||
envoy_cluster_name=~"{{ .Namespace }}_{{ .Name }}-canary_[0-9a-zA-Z-]+",
|
||||
}[{{ .Interval }}]
|
||||
envoy_cluster_name=~"{{ namespace }}_{{ target }}-canary_[0-9a-zA-Z-]+",
|
||||
}[{{ interval }}]
|
||||
)
|
||||
) by (le)
|
||||
)`,
|
||||
}
|
||||
|
||||
type ContourObserver struct {
|
||||
client *PrometheusClient
|
||||
client providers.Interface
|
||||
}
|
||||
|
||||
func (ob *ContourObserver) GetRequestSuccessRate(name string, namespace string, interval string) (float64, error) {
|
||||
query, err := ob.client.RenderQuery(name, namespace, interval, contourQueries["request-success-rate"])
|
||||
func (ob *ContourObserver) GetRequestSuccessRate(model flaggerv1.MetricTemplateModel) (float64, error) {
|
||||
query, err := RenderQuery(contourQueries["request-success-rate"], model)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -56,8 +59,8 @@ func (ob *ContourObserver) GetRequestSuccessRate(name string, namespace string,
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (ob *ContourObserver) GetRequestDuration(name string, namespace string, interval string) (time.Duration, error) {
|
||||
query, err := ob.client.RenderQuery(name, namespace, interval, contourQueries["request-duration"])
|
||||
func (ob *ContourObserver) GetRequestDuration(model flaggerv1.MetricTemplateModel) (time.Duration, error) {
|
||||
query, err := RenderQuery(contourQueries["request-duration"], model)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
package metrics
|
||||
package observers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
"github.com/weaveworks/flagger/pkg/metrics/providers"
|
||||
)
|
||||
|
||||
func TestContourObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
@@ -21,7 +24,11 @@ func TestContourObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
client, err := NewPrometheusClient(ts.URL, time.Second)
|
||||
client, err := providers.NewPrometheusProvider(flaggerv1.MetricTemplateProvider{
|
||||
Type: "prometheus",
|
||||
Address: ts.URL,
|
||||
SecretRef: nil,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -30,7 +37,13 @@ func TestContourObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
client: client,
|
||||
}
|
||||
|
||||
val, err := observer.GetRequestSuccessRate("podinfo", "default", "1m")
|
||||
val, err := observer.GetRequestSuccessRate(flaggerv1.MetricTemplateModel{
|
||||
Name: "podinfo",
|
||||
Namespace: "default",
|
||||
Target: "podinfo",
|
||||
Service: "podinfo",
|
||||
Interval: "1m",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
@@ -54,7 +67,11 @@ func TestContourObserver_GetRequestDuration(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
client, err := NewPrometheusClient(ts.URL, time.Second)
|
||||
client, err := providers.NewPrometheusProvider(flaggerv1.MetricTemplateProvider{
|
||||
Type: "prometheus",
|
||||
Address: ts.URL,
|
||||
SecretRef: nil,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -63,7 +80,13 @@ func TestContourObserver_GetRequestDuration(t *testing.T) {
|
||||
client: client,
|
||||
}
|
||||
|
||||
val, err := observer.GetRequestDuration("podinfo", "default", "1m")
|
||||
val, err := observer.GetRequestDuration(flaggerv1.MetricTemplateModel{
|
||||
Name: "podinfo",
|
||||
Namespace: "default",
|
||||
Target: "podinfo",
|
||||
Service: "podinfo",
|
||||
Interval: "1m",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
76
pkg/metrics/observers/crossover.go
Normal file
76
pkg/metrics/observers/crossover.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package observers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
"github.com/weaveworks/flagger/pkg/metrics/providers"
|
||||
)
|
||||
|
||||
var crossoverQueries = map[string]string{
|
||||
"request-success-rate": `
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq{
|
||||
kubernetes_namespace="{{ namespace }}",
|
||||
envoy_cluster_name=~"{{ target }}-canary",
|
||||
envoy_response_code!~"5.*"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
/
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq{
|
||||
kubernetes_namespace="{{ namespace }}",
|
||||
envoy_cluster_name=~"{{ target }}-canary"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
* 100`,
|
||||
"request-duration": `
|
||||
histogram_quantile(
|
||||
0.99,
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq_time_bucket{
|
||||
kubernetes_namespace="{{ namespace }}",
|
||||
envoy_cluster_name=~"{{ target }}-canary"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
) by (le)
|
||||
)`,
|
||||
}
|
||||
|
||||
type CrossoverObserver struct {
|
||||
client providers.Interface
|
||||
}
|
||||
|
||||
func (ob *CrossoverObserver) GetRequestSuccessRate(model flaggerv1.MetricTemplateModel) (float64, error) {
|
||||
query, err := RenderQuery(crossoverQueries["request-success-rate"], model)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (ob *CrossoverObserver) GetRequestDuration(model flaggerv1.MetricTemplateModel) (time.Duration, error) {
|
||||
query, err := RenderQuery(crossoverQueries["request-duration"], model)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
ms := time.Duration(int64(value)) * time.Millisecond
|
||||
return ms, nil
|
||||
}
|
||||
76
pkg/metrics/observers/crossover_service.go
Normal file
76
pkg/metrics/observers/crossover_service.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package observers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
"github.com/weaveworks/flagger/pkg/metrics/providers"
|
||||
)
|
||||
|
||||
var crossoverServiceQueries = map[string]string{
|
||||
"request-success-rate": `
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq{
|
||||
kubernetes_namespace="{{ namespace }}",
|
||||
envoy_cluster_name="{{ target }}-canary",
|
||||
envoy_response_code!~"5.*"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
/
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq{
|
||||
kubernetes_namespace="{{ namespace }}",
|
||||
envoy_cluster_name="{{ target }}-canary"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
* 100`,
|
||||
"request-duration": `
|
||||
histogram_quantile(
|
||||
0.99,
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq_time_bucket{
|
||||
kubernetes_namespace="{{ namespace }}",
|
||||
envoy_cluster_name="{{ target }}-canary"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
) by (le)
|
||||
)`,
|
||||
}
|
||||
|
||||
type CrossoverServiceObserver struct {
|
||||
client providers.Interface
|
||||
}
|
||||
|
||||
func (ob *CrossoverServiceObserver) GetRequestSuccessRate(model flaggerv1.MetricTemplateModel) (float64, error) {
|
||||
query, err := RenderQuery(crossoverServiceQueries["request-success-rate"], model)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (ob *CrossoverServiceObserver) GetRequestDuration(model flaggerv1.MetricTemplateModel) (time.Duration, error) {
|
||||
query, err := RenderQuery(crossoverServiceQueries["request-duration"], model)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
ms := time.Duration(int64(value)) * time.Millisecond
|
||||
return ms, nil
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
package metrics
|
||||
package observers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
"github.com/weaveworks/flagger/pkg/metrics/providers"
|
||||
)
|
||||
|
||||
func TestCrossoverServiceObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
@@ -21,7 +24,11 @@ func TestCrossoverServiceObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
client, err := NewPrometheusClient(ts.URL, time.Second)
|
||||
client, err := providers.NewPrometheusProvider(flaggerv1.MetricTemplateProvider{
|
||||
Type: "prometheus",
|
||||
Address: ts.URL,
|
||||
SecretRef: nil,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -30,7 +37,13 @@ func TestCrossoverServiceObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
client: client,
|
||||
}
|
||||
|
||||
val, err := observer.GetRequestSuccessRate("podinfo", "default", "1m")
|
||||
val, err := observer.GetRequestSuccessRate(flaggerv1.MetricTemplateModel{
|
||||
Name: "podinfo",
|
||||
Namespace: "default",
|
||||
Target: "podinfo",
|
||||
Service: "podinfo",
|
||||
Interval: "1m",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
@@ -54,7 +67,11 @@ func TestCrossoverServiceObserver_GetRequestDuration(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
client, err := NewPrometheusClient(ts.URL, time.Second)
|
||||
client, err := providers.NewPrometheusProvider(flaggerv1.MetricTemplateProvider{
|
||||
Type: "prometheus",
|
||||
Address: ts.URL,
|
||||
SecretRef: nil,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -63,7 +80,13 @@ func TestCrossoverServiceObserver_GetRequestDuration(t *testing.T) {
|
||||
client: client,
|
||||
}
|
||||
|
||||
val, err := observer.GetRequestDuration("podinfo", "default", "1m")
|
||||
val, err := observer.GetRequestDuration(flaggerv1.MetricTemplateModel{
|
||||
Name: "podinfo",
|
||||
Namespace: "default",
|
||||
Target: "podinfo",
|
||||
Service: "podinfo",
|
||||
Interval: "1m",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
package metrics
|
||||
package observers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
"github.com/weaveworks/flagger/pkg/metrics/providers"
|
||||
)
|
||||
|
||||
func TestCrossoverObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
@@ -21,7 +24,11 @@ func TestCrossoverObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
client, err := NewPrometheusClient(ts.URL, time.Second)
|
||||
client, err := providers.NewPrometheusProvider(flaggerv1.MetricTemplateProvider{
|
||||
Type: "prometheus",
|
||||
Address: ts.URL,
|
||||
SecretRef: nil,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -30,7 +37,13 @@ func TestCrossoverObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
client: client,
|
||||
}
|
||||
|
||||
val, err := observer.GetRequestSuccessRate("podinfo", "default", "1m")
|
||||
val, err := observer.GetRequestSuccessRate(flaggerv1.MetricTemplateModel{
|
||||
Name: "podinfo",
|
||||
Namespace: "default",
|
||||
Target: "podinfo",
|
||||
Service: "podinfo",
|
||||
Interval: "1m",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
@@ -54,7 +67,11 @@ func TestCrossoverObserver_GetRequestDuration(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
client, err := NewPrometheusClient(ts.URL, time.Second)
|
||||
client, err := providers.NewPrometheusProvider(flaggerv1.MetricTemplateProvider{
|
||||
Type: "prometheus",
|
||||
Address: ts.URL,
|
||||
SecretRef: nil,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -63,7 +80,13 @@ func TestCrossoverObserver_GetRequestDuration(t *testing.T) {
|
||||
client: client,
|
||||
}
|
||||
|
||||
val, err := observer.GetRequestDuration("podinfo", "default", "1m")
|
||||
val, err := observer.GetRequestDuration(flaggerv1.MetricTemplateModel{
|
||||
Name: "podinfo",
|
||||
Namespace: "default",
|
||||
Target: "podinfo",
|
||||
Service: "podinfo",
|
||||
Interval: "1m",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
@@ -1,16 +1,22 @@
|
||||
package metrics
|
||||
package observers
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
"github.com/weaveworks/flagger/pkg/metrics/providers"
|
||||
)
|
||||
|
||||
type Factory struct {
|
||||
Client *PrometheusClient
|
||||
Client providers.Interface
|
||||
}
|
||||
|
||||
func NewFactory(metricsServer string, timeout time.Duration) (*Factory, error) {
|
||||
client, err := NewPrometheusClient(metricsServer, timeout)
|
||||
func NewFactory(metricsServer string) (*Factory, error) {
|
||||
client, err := providers.NewPrometheusProvider(flaggerv1.MetricTemplateProvider{
|
||||
Type: "prometheus",
|
||||
Address: metricsServer,
|
||||
SecretRef: nil,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
package metrics
|
||||
package observers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
"github.com/weaveworks/flagger/pkg/metrics/providers"
|
||||
)
|
||||
|
||||
//envoy_cluster_name="test-podinfo-primary-9898_gloo-system"
|
||||
@@ -11,17 +14,17 @@ var glooQueries = map[string]string{
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq{
|
||||
envoy_cluster_name=~"{{ .Namespace }}-{{ .Name }}-canary-[0-9a-zA-Z-]+_[0-9a-zA-Z-]+",
|
||||
envoy_cluster_name=~"{{ namespace }}-{{ target }}-canary-[0-9a-zA-Z-]+_[0-9a-zA-Z-]+",
|
||||
envoy_response_code!~"5.*"
|
||||
}[{{ .Interval }}]
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
/
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq{
|
||||
envoy_cluster_name=~"{{ .Namespace }}-{{ .Name }}-canary-[0-9a-zA-Z-]+_[0-9a-zA-Z-]+",
|
||||
}[{{ .Interval }}]
|
||||
envoy_cluster_name=~"{{ namespace }}-{{ target }}-canary-[0-9a-zA-Z-]+_[0-9a-zA-Z-]+",
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
* 100`,
|
||||
@@ -31,19 +34,19 @@ var glooQueries = map[string]string{
|
||||
sum(
|
||||
rate(
|
||||
envoy_cluster_upstream_rq_time_bucket{
|
||||
envoy_cluster_name=~"{{ .Namespace }}-{{ .Name }}-canary-[0-9a-zA-Z-]+_[0-9a-zA-Z-]+",
|
||||
}[{{ .Interval }}]
|
||||
envoy_cluster_name=~"{{ namespace }}-{{ target }}-canary-[0-9a-zA-Z-]+_[0-9a-zA-Z-]+",
|
||||
}[{{ interval }}]
|
||||
)
|
||||
) by (le)
|
||||
)`,
|
||||
}
|
||||
|
||||
type GlooObserver struct {
|
||||
client *PrometheusClient
|
||||
client providers.Interface
|
||||
}
|
||||
|
||||
func (ob *GlooObserver) GetRequestSuccessRate(name string, namespace string, interval string) (float64, error) {
|
||||
query, err := ob.client.RenderQuery(name, namespace, interval, glooQueries["request-success-rate"])
|
||||
func (ob *GlooObserver) GetRequestSuccessRate(model flaggerv1.MetricTemplateModel) (float64, error) {
|
||||
query, err := RenderQuery(glooQueries["request-success-rate"], model)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -56,8 +59,8 @@ func (ob *GlooObserver) GetRequestSuccessRate(name string, namespace string, int
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (ob *GlooObserver) GetRequestDuration(name string, namespace string, interval string) (time.Duration, error) {
|
||||
query, err := ob.client.RenderQuery(name, namespace, interval, glooQueries["request-duration"])
|
||||
func (ob *GlooObserver) GetRequestDuration(model flaggerv1.MetricTemplateModel) (time.Duration, error) {
|
||||
query, err := RenderQuery(glooQueries["request-duration"], model)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
package metrics
|
||||
package observers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
"github.com/weaveworks/flagger/pkg/metrics/providers"
|
||||
)
|
||||
|
||||
func TestGlooObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
@@ -21,7 +24,11 @@ func TestGlooObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
client, err := NewPrometheusClient(ts.URL, time.Second)
|
||||
client, err := providers.NewPrometheusProvider(flaggerv1.MetricTemplateProvider{
|
||||
Type: "prometheus",
|
||||
Address: ts.URL,
|
||||
SecretRef: nil,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -30,7 +37,13 @@ func TestGlooObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
client: client,
|
||||
}
|
||||
|
||||
val, err := observer.GetRequestSuccessRate("podinfo", "default", "1m")
|
||||
val, err := observer.GetRequestSuccessRate(flaggerv1.MetricTemplateModel{
|
||||
Name: "podinfo",
|
||||
Namespace: "default",
|
||||
Target: "podinfo",
|
||||
Service: "podinfo",
|
||||
Interval: "1m",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
@@ -54,7 +67,11 @@ func TestGlooObserver_GetRequestDuration(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
client, err := NewPrometheusClient(ts.URL, time.Second)
|
||||
client, err := providers.NewPrometheusProvider(flaggerv1.MetricTemplateProvider{
|
||||
Type: "prometheus",
|
||||
Address: ts.URL,
|
||||
SecretRef: nil,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -63,7 +80,13 @@ func TestGlooObserver_GetRequestDuration(t *testing.T) {
|
||||
client: client,
|
||||
}
|
||||
|
||||
val, err := observer.GetRequestDuration("podinfo", "default", "1m")
|
||||
val, err := observer.GetRequestDuration(flaggerv1.MetricTemplateModel{
|
||||
Name: "podinfo",
|
||||
Namespace: "default",
|
||||
Target: "podinfo",
|
||||
Service: "podinfo",
|
||||
Interval: "1m",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
76
pkg/metrics/observers/http.go
Normal file
76
pkg/metrics/observers/http.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package observers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
"github.com/weaveworks/flagger/pkg/metrics/providers"
|
||||
)
|
||||
|
||||
var httpQueries = map[string]string{
|
||||
"request-success-rate": `
|
||||
sum(
|
||||
rate(
|
||||
http_request_duration_seconds_count{
|
||||
kubernetes_namespace="{{ namespace }}",
|
||||
kubernetes_pod_name=~"{{ target }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)",
|
||||
status!~"5.*"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
/
|
||||
sum(
|
||||
rate(
|
||||
http_request_duration_seconds_count{
|
||||
kubernetes_namespace="{{ namespace }}",
|
||||
kubernetes_pod_name=~"{{ target }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
* 100`,
|
||||
"request-duration": `
|
||||
histogram_quantile(
|
||||
0.99,
|
||||
sum(
|
||||
rate(
|
||||
http_request_duration_seconds_bucket{
|
||||
kubernetes_namespace="{{ namespace }}",
|
||||
kubernetes_pod_name=~"{{ target }}-[0-9a-zA-Z]+(-[0-9a-zA-Z]+)"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
) by (le)
|
||||
)`,
|
||||
}
|
||||
|
||||
type HttpObserver struct {
|
||||
client providers.Interface
|
||||
}
|
||||
|
||||
func (ob *HttpObserver) GetRequestSuccessRate(model flaggerv1.MetricTemplateModel) (float64, error) {
|
||||
query, err := RenderQuery(httpQueries["request-success-rate"], model)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (ob *HttpObserver) GetRequestDuration(model flaggerv1.MetricTemplateModel) (time.Duration, error) {
|
||||
query, err := RenderQuery(httpQueries["request-duration"], model)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
ms := time.Duration(int64(value*1000)) * time.Millisecond
|
||||
return ms, nil
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
package metrics
|
||||
package observers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
"github.com/weaveworks/flagger/pkg/metrics/providers"
|
||||
)
|
||||
|
||||
func TestHttpObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
@@ -21,7 +24,11 @@ func TestHttpObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
client, err := NewPrometheusClient(ts.URL, time.Second)
|
||||
client, err := providers.NewPrometheusProvider(flaggerv1.MetricTemplateProvider{
|
||||
Type: "prometheus",
|
||||
Address: ts.URL,
|
||||
SecretRef: nil,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -30,7 +37,13 @@ func TestHttpObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
client: client,
|
||||
}
|
||||
|
||||
val, err := observer.GetRequestSuccessRate("podinfo", "default", "1m")
|
||||
val, err := observer.GetRequestSuccessRate(flaggerv1.MetricTemplateModel{
|
||||
Name: "podinfo",
|
||||
Namespace: "default",
|
||||
Target: "podinfo",
|
||||
Service: "podinfo",
|
||||
Interval: "1m",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
@@ -54,7 +67,11 @@ func TestHttpObserver_GetRequestDuration(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
client, err := NewPrometheusClient(ts.URL, time.Second)
|
||||
client, err := providers.NewPrometheusProvider(flaggerv1.MetricTemplateProvider{
|
||||
Type: "prometheus",
|
||||
Address: ts.URL,
|
||||
SecretRef: nil,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -63,7 +80,13 @@ func TestHttpObserver_GetRequestDuration(t *testing.T) {
|
||||
client: client,
|
||||
}
|
||||
|
||||
val, err := observer.GetRequestDuration("podinfo", "default", "1m")
|
||||
val, err := observer.GetRequestDuration(flaggerv1.MetricTemplateModel{
|
||||
Name: "podinfo",
|
||||
Namespace: "default",
|
||||
Target: "podinfo",
|
||||
Service: "podinfo",
|
||||
Interval: "1m",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
79
pkg/metrics/observers/istio.go
Normal file
79
pkg/metrics/observers/istio.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package observers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
"github.com/weaveworks/flagger/pkg/metrics/providers"
|
||||
)
|
||||
|
||||
var istioQueries = map[string]string{
|
||||
"request-success-rate": `
|
||||
sum(
|
||||
rate(
|
||||
istio_requests_total{
|
||||
reporter="destination",
|
||||
destination_workload_namespace="{{ namespace }}",
|
||||
destination_workload=~"{{ target }}",
|
||||
response_code!~"5.*"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
/
|
||||
sum(
|
||||
rate(
|
||||
istio_requests_total{
|
||||
reporter="destination",
|
||||
destination_workload_namespace="{{ namespace }}",
|
||||
destination_workload=~"{{ target }}"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
* 100`,
|
||||
"request-duration": `
|
||||
histogram_quantile(
|
||||
0.99,
|
||||
sum(
|
||||
rate(
|
||||
istio_request_duration_seconds_bucket{
|
||||
reporter="destination",
|
||||
destination_workload_namespace="{{ namespace }}",
|
||||
destination_workload=~"{{ target }}"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
) by (le)
|
||||
)`,
|
||||
}
|
||||
|
||||
type IstioObserver struct {
|
||||
client providers.Interface
|
||||
}
|
||||
|
||||
func (ob *IstioObserver) GetRequestSuccessRate(model flaggerv1.MetricTemplateModel) (float64, error) {
|
||||
query, err := RenderQuery(istioQueries["request-success-rate"], model)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (ob *IstioObserver) GetRequestDuration(model flaggerv1.MetricTemplateModel) (time.Duration, error) {
|
||||
query, err := RenderQuery(istioQueries["request-duration"], model)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
ms := time.Duration(int64(value*1000)) * time.Millisecond
|
||||
return ms, nil
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
package metrics
|
||||
package observers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
"github.com/weaveworks/flagger/pkg/metrics/providers"
|
||||
)
|
||||
|
||||
func TestIstioObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
@@ -21,7 +24,11 @@ func TestIstioObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
client, err := NewPrometheusClient(ts.URL, time.Second)
|
||||
client, err := providers.NewPrometheusProvider(flaggerv1.MetricTemplateProvider{
|
||||
Type: "prometheus",
|
||||
Address: ts.URL,
|
||||
SecretRef: nil,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -30,7 +37,13 @@ func TestIstioObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
client: client,
|
||||
}
|
||||
|
||||
val, err := observer.GetRequestSuccessRate("podinfo", "default", "1m")
|
||||
val, err := observer.GetRequestSuccessRate(flaggerv1.MetricTemplateModel{
|
||||
Name: "podinfo",
|
||||
Namespace: "default",
|
||||
Target: "podinfo",
|
||||
Service: "podinfo",
|
||||
Interval: "1m",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
@@ -54,7 +67,11 @@ func TestIstioObserver_GetRequestDuration(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
client, err := NewPrometheusClient(ts.URL, time.Second)
|
||||
client, err := providers.NewPrometheusProvider(flaggerv1.MetricTemplateProvider{
|
||||
Type: "prometheus",
|
||||
Address: ts.URL,
|
||||
SecretRef: nil,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -63,7 +80,13 @@ func TestIstioObserver_GetRequestDuration(t *testing.T) {
|
||||
client: client,
|
||||
}
|
||||
|
||||
val, err := observer.GetRequestDuration("podinfo", "default", "1m")
|
||||
val, err := observer.GetRequestDuration(flaggerv1.MetricTemplateModel{
|
||||
Name: "podinfo",
|
||||
Namespace: "default",
|
||||
Target: "podinfo",
|
||||
Service: "podinfo",
|
||||
Interval: "1m",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
79
pkg/metrics/observers/linkerd.go
Normal file
79
pkg/metrics/observers/linkerd.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package observers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
"github.com/weaveworks/flagger/pkg/metrics/providers"
|
||||
)
|
||||
|
||||
var linkerdQueries = map[string]string{
|
||||
"request-success-rate": `
|
||||
sum(
|
||||
rate(
|
||||
response_total{
|
||||
namespace="{{ namespace }}",
|
||||
deployment=~"{{ target }}",
|
||||
classification!="failure",
|
||||
direction="inbound"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
/
|
||||
sum(
|
||||
rate(
|
||||
response_total{
|
||||
namespace="{{ namespace }}",
|
||||
deployment=~"{{ target }}",
|
||||
direction="inbound"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
* 100`,
|
||||
"request-duration": `
|
||||
histogram_quantile(
|
||||
0.99,
|
||||
sum(
|
||||
rate(
|
||||
response_latency_ms_bucket{
|
||||
namespace="{{ namespace }}",
|
||||
deployment=~"{{ target }}",
|
||||
direction="inbound"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
) by (le)
|
||||
)`,
|
||||
}
|
||||
|
||||
type LinkerdObserver struct {
|
||||
client providers.Interface
|
||||
}
|
||||
|
||||
func (ob *LinkerdObserver) GetRequestSuccessRate(model flaggerv1.MetricTemplateModel) (float64, error) {
|
||||
query, err := RenderQuery(linkerdQueries["request-success-rate"], model)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (ob *LinkerdObserver) GetRequestDuration(model flaggerv1.MetricTemplateModel) (time.Duration, error) {
|
||||
query, err := RenderQuery(linkerdQueries["request-duration"], model)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
ms := time.Duration(int64(value)) * time.Millisecond
|
||||
return ms, nil
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
package metrics
|
||||
package observers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
"github.com/weaveworks/flagger/pkg/metrics/providers"
|
||||
)
|
||||
|
||||
func TestLinkerdObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
@@ -21,7 +24,11 @@ func TestLinkerdObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
client, err := NewPrometheusClient(ts.URL, time.Second)
|
||||
client, err := providers.NewPrometheusProvider(flaggerv1.MetricTemplateProvider{
|
||||
Type: "prometheus",
|
||||
Address: ts.URL,
|
||||
SecretRef: nil,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -30,7 +37,13 @@ func TestLinkerdObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
client: client,
|
||||
}
|
||||
|
||||
val, err := observer.GetRequestSuccessRate("podinfo", "default", "1m")
|
||||
val, err := observer.GetRequestSuccessRate(flaggerv1.MetricTemplateModel{
|
||||
Name: "podinfo",
|
||||
Namespace: "default",
|
||||
Target: "podinfo",
|
||||
Service: "podinfo",
|
||||
Interval: "1m",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
@@ -54,7 +67,11 @@ func TestLinkerdObserver_GetRequestDuration(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
client, err := NewPrometheusClient(ts.URL, time.Second)
|
||||
client, err := providers.NewPrometheusProvider(flaggerv1.MetricTemplateProvider{
|
||||
Type: "prometheus",
|
||||
Address: ts.URL,
|
||||
SecretRef: nil,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -63,7 +80,13 @@ func TestLinkerdObserver_GetRequestDuration(t *testing.T) {
|
||||
client: client,
|
||||
}
|
||||
|
||||
val, err := observer.GetRequestDuration("podinfo", "default", "1m")
|
||||
val, err := observer.GetRequestDuration(flaggerv1.MetricTemplateModel{
|
||||
Name: "podinfo",
|
||||
Namespace: "default",
|
||||
Target: "podinfo",
|
||||
Service: "podinfo",
|
||||
Interval: "1m",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
83
pkg/metrics/observers/nginx.go
Normal file
83
pkg/metrics/observers/nginx.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package observers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
"github.com/weaveworks/flagger/pkg/metrics/providers"
|
||||
)
|
||||
|
||||
var nginxQueries = map[string]string{
|
||||
"request-success-rate": `
|
||||
sum(
|
||||
rate(
|
||||
nginx_ingress_controller_requests{
|
||||
namespace="{{ namespace }}",
|
||||
ingress="{{ ingress }}",
|
||||
status!~"5.*"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
/
|
||||
sum(
|
||||
rate(
|
||||
nginx_ingress_controller_requests{
|
||||
namespace="{{ namespace }}",
|
||||
ingress="{{ ingress }}"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
* 100`,
|
||||
"request-duration": `
|
||||
sum(
|
||||
rate(
|
||||
nginx_ingress_controller_ingress_upstream_latency_seconds_sum{
|
||||
namespace="{{ namespace }}",
|
||||
ingress="{{ ingress }}"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
/
|
||||
sum(
|
||||
rate(
|
||||
nginx_ingress_controller_ingress_upstream_latency_seconds_count{
|
||||
namespace="{{ namespace }}",
|
||||
ingress="{{ ingress }}"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
)
|
||||
* 1000`,
|
||||
}
|
||||
|
||||
type NginxObserver struct {
|
||||
client providers.Interface
|
||||
}
|
||||
|
||||
func (ob *NginxObserver) GetRequestSuccessRate(model flaggerv1.MetricTemplateModel) (float64, error) {
|
||||
query, err := RenderQuery(nginxQueries["request-success-rate"], model)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func (ob *NginxObserver) GetRequestDuration(model flaggerv1.MetricTemplateModel) (time.Duration, error) {
|
||||
query, err := RenderQuery(nginxQueries["request-duration"], model)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
value, err := ob.client.RunQuery(query)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
ms := time.Duration(int64(value)) * time.Millisecond
|
||||
return ms, nil
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
package metrics
|
||||
package observers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
"github.com/weaveworks/flagger/pkg/metrics/providers"
|
||||
)
|
||||
|
||||
func TestNginxObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
@@ -21,7 +24,11 @@ func TestNginxObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
client, err := NewPrometheusClient(ts.URL, time.Second)
|
||||
client, err := providers.NewPrometheusProvider(flaggerv1.MetricTemplateProvider{
|
||||
Type: "prometheus",
|
||||
Address: ts.URL,
|
||||
SecretRef: nil,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -30,7 +37,13 @@ func TestNginxObserver_GetRequestSuccessRate(t *testing.T) {
|
||||
client: client,
|
||||
}
|
||||
|
||||
val, err := observer.GetRequestSuccessRate("podinfo", "nginx", "1m")
|
||||
val, err := observer.GetRequestSuccessRate(flaggerv1.MetricTemplateModel{
|
||||
Name: "podinfo",
|
||||
Namespace: "nginx",
|
||||
Target: "podinfo",
|
||||
Ingress: "podinfo",
|
||||
Interval: "1m",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
@@ -54,7 +67,11 @@ func TestNginxObserver_GetRequestDuration(t *testing.T) {
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
client, err := NewPrometheusClient(ts.URL, time.Second)
|
||||
client, err := providers.NewPrometheusProvider(flaggerv1.MetricTemplateProvider{
|
||||
Type: "prometheus",
|
||||
Address: ts.URL,
|
||||
SecretRef: nil,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -63,7 +80,13 @@ func TestNginxObserver_GetRequestDuration(t *testing.T) {
|
||||
client: client,
|
||||
}
|
||||
|
||||
val, err := observer.GetRequestDuration("podinfo", "nginx", "1m")
|
||||
val, err := observer.GetRequestDuration(flaggerv1.MetricTemplateModel{
|
||||
Name: "podinfo",
|
||||
Namespace: "nginx",
|
||||
Target: "podinfo",
|
||||
Ingress: "podinfo",
|
||||
Interval: "1m",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
11
pkg/metrics/observers/observer.go
Normal file
11
pkg/metrics/observers/observer.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package observers
|
||||
|
||||
import (
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
GetRequestSuccessRate(model flaggerv1.MetricTemplateModel) (float64, error)
|
||||
GetRequestDuration(model flaggerv1.MetricTemplateModel) (time.Duration, error)
|
||||
}
|
||||
29
pkg/metrics/observers/render.go
Normal file
29
pkg/metrics/observers/render.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package observers
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"text/template"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
)
|
||||
|
||||
func RenderQuery(queryTemplate string, model flaggerv1.MetricTemplateModel) (string, error) {
|
||||
t, err := template.New("tmpl").Funcs(model.TemplateFunctions()).Parse(queryTemplate)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var data bytes.Buffer
|
||||
b := bufio.NewWriter(&data)
|
||||
|
||||
if err := t.Execute(b, nil); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = b.Flush()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return data.String(), nil
|
||||
}
|
||||
15
pkg/metrics/providers/factory.go
Normal file
15
pkg/metrics/providers/factory.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package providers
|
||||
|
||||
import flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
|
||||
type Factory struct {
|
||||
}
|
||||
|
||||
func (factory Factory) Provider(provider flaggerv1.MetricTemplateProvider, credentials map[string][]byte) (Interface, error) {
|
||||
switch {
|
||||
case provider.Type == "prometheus":
|
||||
return NewPrometheusProvider(provider, credentials)
|
||||
default:
|
||||
return NewPrometheusProvider(provider, credentials)
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
package metrics
|
||||
package providers
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -12,14 +10,17 @@ import (
|
||||
"path"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
)
|
||||
|
||||
// PrometheusClient is executing promql queries
|
||||
type PrometheusClient struct {
|
||||
timeout time.Duration
|
||||
url url.URL
|
||||
// PrometheusProvider executes promQL queries
|
||||
type PrometheusProvider struct {
|
||||
timeout time.Duration
|
||||
url url.URL
|
||||
username string
|
||||
password string
|
||||
}
|
||||
|
||||
type prometheusResponse struct {
|
||||
@@ -33,54 +34,44 @@ type prometheusResponse struct {
|
||||
}
|
||||
}
|
||||
|
||||
// NewPrometheusClient creates a Prometheus client for the provided URL address
|
||||
func NewPrometheusClient(address string, timeout time.Duration) (*PrometheusClient, error) {
|
||||
promURL, err := url.Parse(address)
|
||||
// NewPrometheusProvider takes a provider spec and the credentials map,
|
||||
// validates the address, extracts the username and password values if provided and
|
||||
// returns a Prometheus client ready to execute queries against the API
|
||||
func NewPrometheusProvider(provider flaggerv1.MetricTemplateProvider, credentials map[string][]byte) (*PrometheusProvider, error) {
|
||||
promURL, err := url.Parse(provider.Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("%s address %s is not a valid URL", provider.Type, provider.Address)
|
||||
}
|
||||
|
||||
return &PrometheusClient{timeout: timeout, url: *promURL}, nil
|
||||
prom := PrometheusProvider{
|
||||
timeout: 5 * time.Second,
|
||||
url: *promURL,
|
||||
}
|
||||
|
||||
if provider.SecretRef != nil {
|
||||
if username, ok := credentials["username"]; ok {
|
||||
prom.username = string(username)
|
||||
} else {
|
||||
return nil, fmt.Errorf("%s credentials does not contain a username", provider.Type)
|
||||
}
|
||||
|
||||
if password, ok := credentials["password"]; ok {
|
||||
prom.password = string(password)
|
||||
} else {
|
||||
return nil, fmt.Errorf("%s credentials does not contain a password", provider.Type)
|
||||
}
|
||||
}
|
||||
|
||||
return &prom, nil
|
||||
}
|
||||
|
||||
// RenderQuery renders the promql query using the provided text template
|
||||
func (p *PrometheusClient) RenderQuery(name string, namespace string, interval string, tmpl string) (string, error) {
|
||||
meta := struct {
|
||||
Name string
|
||||
Namespace string
|
||||
Interval string
|
||||
}{
|
||||
name,
|
||||
namespace,
|
||||
interval,
|
||||
}
|
||||
|
||||
t, err := template.New("tmpl").Parse(tmpl)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var data bytes.Buffer
|
||||
b := bufio.NewWriter(&data)
|
||||
|
||||
if err := t.Execute(b, meta); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = b.Flush()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return data.String(), nil
|
||||
}
|
||||
|
||||
// RunQuery executes the promql and converts the result to float64
|
||||
func (p *PrometheusClient) RunQuery(query string) (float64, error) {
|
||||
// RunQuery executes the promQL query and returns the the first result as float64
|
||||
func (p *PrometheusProvider) RunQuery(query string) (float64, error) {
|
||||
if p.url.Host == "fake" {
|
||||
return 100, nil
|
||||
}
|
||||
|
||||
query = url.QueryEscape(p.TrimQuery(query))
|
||||
query = url.QueryEscape(p.trimQuery(query))
|
||||
u, err := url.Parse(fmt.Sprintf("./api/v1/query?query=%s", query))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -94,6 +85,10 @@ func (p *PrometheusClient) RunQuery(query string) (float64, error) {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if p.username != "" && p.password != "" {
|
||||
req.SetBasicAuth(p.username, p.password)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(req.Context(), p.timeout)
|
||||
defer cancel()
|
||||
|
||||
@@ -137,14 +132,8 @@ func (p *PrometheusClient) RunQuery(query string) (float64, error) {
|
||||
return *value, nil
|
||||
}
|
||||
|
||||
// TrimQuery takes a promql query and removes whitespace
|
||||
func (p *PrometheusClient) TrimQuery(query string) string {
|
||||
space := regexp.MustCompile(`\s+`)
|
||||
return space.ReplaceAllString(query, " ")
|
||||
}
|
||||
|
||||
// IsOnline call Prometheus status endpoint and returns an error if the API is unreachable
|
||||
func (p *PrometheusClient) IsOnline() (bool, error) {
|
||||
// IsOnline calls the Prometheus status endpoint and returns an error if the API is unreachable
|
||||
func (p *PrometheusProvider) IsOnline() (bool, error) {
|
||||
u, err := url.Parse("./api/v1/status/flags")
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -158,6 +147,10 @@ func (p *PrometheusClient) IsOnline() (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if p.username != "" && p.password != "" {
|
||||
req.SetBasicAuth(p.username, p.password)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(req.Context(), p.timeout)
|
||||
defer cancel()
|
||||
|
||||
@@ -179,6 +172,8 @@ func (p *PrometheusClient) IsOnline() (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (p *PrometheusClient) GetMetricsServer() string {
|
||||
return p.url.String()
|
||||
// trimQuery takes a promql query and removes whitespace
|
||||
func (p *PrometheusProvider) trimQuery(query string) string {
|
||||
space := regexp.MustCompile(`\s+`)
|
||||
return space.ReplaceAllString(query, " ")
|
||||
}
|
||||
170
pkg/metrics/providers/prometheus_test.go
Normal file
170
pkg/metrics/providers/prometheus_test.go
Normal file
@@ -0,0 +1,170 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
|
||||
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1alpha1"
|
||||
clientset "github.com/weaveworks/flagger/pkg/client/clientset/versioned"
|
||||
fakeFlagger "github.com/weaveworks/flagger/pkg/client/clientset/versioned/fake"
|
||||
)
|
||||
|
||||
type fakeClients struct {
|
||||
kubeClient kubernetes.Interface
|
||||
flaggerClient clientset.Interface
|
||||
}
|
||||
|
||||
func prometheusFake() fakeClients {
|
||||
provider := flaggerv1.MetricTemplateProvider{
|
||||
Type: "prometheus",
|
||||
Address: "http://prometheus:9090",
|
||||
SecretRef: &corev1.LocalObjectReference{Name: "prometheus"},
|
||||
}
|
||||
|
||||
template := &flaggerv1.MetricTemplate{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: flaggerv1.SchemeGroupVersion.String()},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
Name: "prometheus",
|
||||
},
|
||||
Spec: flaggerv1.MetricTemplateSpec{
|
||||
Provider: provider,
|
||||
Query: "sum(envoy_cluster_upstream_rq)",
|
||||
},
|
||||
}
|
||||
|
||||
flaggerClient := fakeFlagger.NewSimpleClientset(template)
|
||||
|
||||
secret := &corev1.Secret{
|
||||
TypeMeta: metav1.TypeMeta{APIVersion: corev1.SchemeGroupVersion.String()},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "default",
|
||||
Name: "prometheus",
|
||||
},
|
||||
Type: corev1.SecretTypeOpaque,
|
||||
Data: map[string][]byte{
|
||||
"username": []byte("username"),
|
||||
"password": []byte("password"),
|
||||
},
|
||||
}
|
||||
|
||||
kubeClient := fake.NewSimpleClientset(secret)
|
||||
|
||||
return fakeClients{
|
||||
kubeClient: kubeClient,
|
||||
flaggerClient: flaggerClient,
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewPrometheusProvider(t *testing.T) {
|
||||
clients := prometheusFake()
|
||||
|
||||
template, err := clients.flaggerClient.FlaggerV1alpha1().MetricTemplates("default").Get("prometheus", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
secret, err := clients.kubeClient.CoreV1().Secrets("default").Get("prometheus", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
prom, err := NewPrometheusProvider(template.Spec.Provider, secret.Data)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
if prom.url.String() != "http://prometheus:9090" {
|
||||
t.Errorf("Got URL %s wanted %s", prom.url.String(), "http://prometheus:9090")
|
||||
}
|
||||
|
||||
if prom.password != "password" {
|
||||
t.Errorf("Got password %s wanted %s", prom.password, "password")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrometheusProvider_RunQueryWithBasicAuth(t *testing.T) {
|
||||
expected := `sum(envoy_cluster_upstream_rq)`
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
promql := r.URL.Query()["query"][0]
|
||||
if promql != expected {
|
||||
t.Errorf("\nGot %s \nWanted %s", promql, expected)
|
||||
}
|
||||
|
||||
if header, ok := r.Header["Authorization"]; ok {
|
||||
if !strings.Contains(header[0], "Basic") {
|
||||
t.Error("Basic authorization header not found")
|
||||
}
|
||||
} else {
|
||||
t.Error("Authorization header not found")
|
||||
}
|
||||
|
||||
json := `{"status":"success","data":{"resultType":"vector","result":[{"metric":{},"value":[1545905245.458,"100"]}]}}`
|
||||
w.Write([]byte(json))
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
clients := prometheusFake()
|
||||
|
||||
template, err := clients.flaggerClient.FlaggerV1alpha1().MetricTemplates("default").Get("prometheus", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
template.Spec.Provider.Address = ts.URL
|
||||
|
||||
secret, err := clients.kubeClient.CoreV1().Secrets("default").Get("prometheus", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
prom, err := NewPrometheusProvider(template.Spec.Provider, secret.Data)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
val, err := prom.RunQuery(template.Spec.Query)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
if val != 100 {
|
||||
t.Errorf("Got %v wanted %v", val, 100)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrometheusProvider_IsOnline(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusBadGateway)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
clients := prometheusFake()
|
||||
|
||||
template, err := clients.flaggerClient.FlaggerV1alpha1().MetricTemplates("default").Get("prometheus", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
template.Spec.Provider.Address = ts.URL
|
||||
template.Spec.Provider.SecretRef = nil
|
||||
|
||||
prom, err := NewPrometheusProvider(template.Spec.Provider, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
|
||||
ok, err := prom.IsOnline()
|
||||
if err == nil {
|
||||
t.Errorf("Got no error wanted %v", http.StatusBadGateway)
|
||||
}
|
||||
|
||||
if ok {
|
||||
t.Errorf("Got %v wanted %v", ok, false)
|
||||
}
|
||||
}
|
||||
9
pkg/metrics/providers/provider.go
Normal file
9
pkg/metrics/providers/provider.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package providers
|
||||
|
||||
type Interface interface {
|
||||
// RunQuery executes the query and converts the first result to float64
|
||||
RunQuery(query string) (float64, error)
|
||||
|
||||
// IsOnline calls the provider endpoint and returns an error if the API is unreachable
|
||||
IsOnline() (bool, error)
|
||||
}
|
||||
@@ -17,6 +17,31 @@ kubectl -n test rollout status deployment/flagger-loadtester
|
||||
echo '>>> Initialising canary'
|
||||
kubectl apply -f ${REPO_ROOT}/test/e2e-workload.yaml
|
||||
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: flagger.app/v1alpha1
|
||||
kind: MetricTemplate
|
||||
metadata:
|
||||
name: latency
|
||||
namespace: linkerd
|
||||
spec:
|
||||
provider:
|
||||
type: prometheus
|
||||
address: http://linkerd-prometheus.linkerd:9090
|
||||
query: |
|
||||
histogram_quantile(
|
||||
0.99,
|
||||
sum(
|
||||
rate(
|
||||
response_latency_ms_bucket{
|
||||
namespace="{{ namespace }}",
|
||||
deployment=~"{{ target }}",
|
||||
direction="inbound"
|
||||
}[{{ interval }}]
|
||||
)
|
||||
) by (le)
|
||||
)
|
||||
EOF
|
||||
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: flagger.app/v1alpha3
|
||||
kind: Canary
|
||||
@@ -45,6 +70,12 @@ spec:
|
||||
- name: request-duration
|
||||
threshold: 500
|
||||
interval: 30s
|
||||
- name: latency
|
||||
templateRef:
|
||||
name: latency
|
||||
namespace: linkerd
|
||||
threshold: 300
|
||||
interval: 1m
|
||||
webhooks:
|
||||
- name: http-acceptance-test
|
||||
type: pre-rollout
|
||||
|
||||
Reference in New Issue
Block a user