Compare commits

...

22 Commits

Author SHA1 Message Date
stefanprodan
8c12cdb21d Release v0.1.0 2018-11-25 21:05:16 +02:00
stefanprodan
923799dce7 Keep CRD on Helm release delete 2018-11-25 20:13:40 +02:00
stefanprodan
ebc932fba5 Add Slack configuration to Helm readme 2018-11-25 20:07:32 +02:00
Stefan Prodan
3d8d30db47 Merge pull request #6 from stefanprodan/quay
Switch to Quay and Go 1.11
2018-11-25 19:35:53 +02:00
stefanprodan
1022c3438a Use go 1.11 for docker build 2018-11-25 19:21:42 +02:00
stefanprodan
9159855df2 Use Quay as container registry in Helm and YAML manifests 2018-11-25 19:20:29 +02:00
Stefan Prodan
7927ac0a5d Push container image to Quay 2018-11-25 18:52:18 +02:00
Stefan Prodan
f438e9a4b2 Merge pull request #4 from stefanprodan/slack
Add Slack notifications
2018-11-25 11:54:15 +02:00
Stefan Prodan
4c70a330d4 Add Slack notifications configuration to readme 2018-11-25 11:46:18 +02:00
Stefan Prodan
d8875a3da1 Add Slack flags to Helm chart 2018-11-25 11:45:38 +02:00
Stefan Prodan
769aff57cb Add Slack notifications for canary events 2018-11-25 11:44:45 +02:00
Stefan Prodan
4138f37f9a Add Slack notifier component 2018-11-25 11:40:35 +02:00
stefanprodan
583c9cc004 Rename Istio client set 2018-11-25 00:05:43 +02:00
Stefan Prodan
c5930e6f70 Update deployment strategy on promotion
- include spec strategy, min ready seconds and revision history limit to initialization and promotion
2018-11-24 20:03:02 +02:00
stefanprodan
423d9bbbb3 Use go 1.11 in Travis 2018-11-24 16:23:20 +02:00
Stefan Prodan
07771f500f Release 0.1.0-beta.7 2018-11-24 15:58:17 +02:00
Stefan Prodan
65bd77c88f Add last transition time to Canary CRD status 2018-11-24 15:48:35 +02:00
Stefan Prodan
82bf63f89b Change website URL 2018-11-15 12:20:53 +02:00
Stefan Prodan
7f735ead07 Set site banner 2018-11-15 10:50:58 +02:00
Stefan Prodan
56ffd618d6 Increase flagger probes timeout to 5s (containerd fix) 2018-11-15 10:38:20 +02:00
Stefan Prodan
19cb34479e Increase probes timeout to 5s (containerd fix) 2018-11-14 15:39:44 +02:00
Stefan Prodan
2d906f0b71 Add Grafana install to helm-up cmd 2018-11-14 15:38:35 +02:00
28 changed files with 275 additions and 76 deletions

View File

@@ -2,7 +2,7 @@ sudo: required
language: go
go:
- 1.10.x
- 1.11.x
services:
- docker
@@ -21,19 +21,18 @@ script:
after_success:
- if [ -z "$DOCKER_USER" ]; then
echo "PR build, skipping Docker Hub push";
echo "PR build, skipping image push";
else
docker tag stefanprodan/flagger:latest stefanprodan/flagger:${TRAVIS_COMMIT};
echo $DOCKER_PASS | docker login -u=$DOCKER_USER --password-stdin;
docker push stefanprodan/flagger:${TRAVIS_COMMIT};
docker tag stefanprodan/flagger:latest quay.io/stefanprodan/flagger:${TRAVIS_COMMIT};
echo $DOCKER_PASS | docker login -u=$DOCKER_USER --password-stdin quay.io;
docker push quay.io/stefanprodan/flagger:${TRAVIS_COMMIT};
fi
- if [ -z "$TRAVIS_TAG" ]; then
echo "Not a release, skipping Docker Hub push";
echo "Not a release, skipping image push";
else
docker tag stefanprodan/flagger:latest stefanprodan/flagger:$TRAVIS_TAG;
echo $DOCKER_PASS | docker login -u=$DOCKER_USER --password-stdin;
docker push stefanprodan/flagger:latest;
docker push stefanprodan/flagger:$TRAVIS_TAG;
docker tag stefanprodan/flagger:latest quay.io/stefanprodan/flagger:${TRAVIS_TAG};
echo $DOCKER_PASS | docker login -u=$DOCKER_USER --password-stdin quay.io;
docker push quay.io/stefanprodan/flagger:$TRAVIS_TAG;
fi
- bash <(curl -s https://codecov.io/bash)
- rm coverage.txt

View File

@@ -1,4 +1,4 @@
FROM golang:1.10
FROM golang:1.11
RUN mkdir -p /go/src/github.com/stefanprodan/flagger/

View File

@@ -4,7 +4,10 @@ VERSION_MINOR:=$(shell grep 'VERSION' pkg/version/version.go | awk '{ print $$4
PATCH:=$(shell grep 'VERSION' pkg/version/version.go | awk '{ print $$4 }' | tr -d '"' | awk -F. '{print $$NF}')
SOURCE_DIRS = cmd pkg/apis pkg/controller pkg/server pkg/logging pkg/version
run:
go run cmd/flagger/* -kubeconfig=$$HOME/.kube/config -log-level=info -metrics-server=https://prometheus.iowa.weavedx.com
go run cmd/flagger/* -kubeconfig=$$HOME/.kube/config -log-level=info \
-metrics-server=https://prometheus.iowa.weavedx.com \
-slack-url=https://hooks.slack.com/services/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 \
-slack-channel="devops-alerts"
build:
docker build -t stefanprodan/flagger:$(TAG) . -f Dockerfile
@@ -31,6 +34,7 @@ helm-package:
helm-up:
helm upgrade --install flagger ./charts/flagger --namespace=istio-system --set crd.create=false
helm upgrade --install flagger-grafana ./charts/grafana --namespace=istio-system
version-set:
@next="$(TAG)" && \
@@ -52,10 +56,10 @@ version-up:
dev-up: version-up
@echo "Starting build/push/deploy pipeline for $(VERSION)"
docker build -t stefanprodan/flagger:$(VERSION) . -f Dockerfile
docker push stefanprodan/flagger:$(VERSION)
docker build -t quay.io/stefanprodan/flagger:$(VERSION) . -f Dockerfile
docker push quay.io/stefanprodan/flagger:$(VERSION)
kubectl apply -f ./artifacts/flagger/crd.yaml
helm upgrade --install flagger ./charts/flagger --namespace=istio-system --set crd.create=false
helm upgrade -i flagger ./charts/flagger --namespace=istio-system --set crd.create=false
release:
git tag $(VERSION)

View File

@@ -8,8 +8,6 @@
Flagger is a Kubernetes operator that automates the promotion of canary deployments
using Istio routing for traffic shifting and Prometheus metrics for canary analysis.
The project is currently in experimental phase and it is expected that breaking changes
to the API will be made in the upcoming releases.
### Install
@@ -351,13 +349,28 @@ flagger_canary_duration_seconds_sum{name="podinfo",namespace="test"} 17.3561329
flagger_canary_duration_seconds_count{name="podinfo",namespace="test"} 6
```
### Alerting
Flagger can be configured to send Slack notifications:
```bash
helm upgrade -i flagger flagger/flagger \
--namespace=istio-system \
--set slack.url=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK \
--set slack.channel=general \
--set slack.user=flagger
```
Once configured with a Slack incoming webhook, Flagger will post messages when a canary deployment has been initialized,
when a new revision has been detected and if the canary analysis failed or succeeded.
![flagger-slack](https://raw.githubusercontent.com/stefanprodan/flagger/master/docs/screens/slack-notifications.png)
### Roadmap
* Extend the canary analysis and promotion to other types than Kubernetes deployments such as Flux Helm releases or OpenFaaS functions
* Extend the validation mechanism to support other metrics than HTTP success rate and latency
* Add support for comparing the canary metrics to the primary ones and do the validation based on the derivation between the two
* Alerting: trigger Alertmanager on successful or failed promotions
* Reporting: publish canary analysis results to Slack/Jira/etc
* Extend the canary analysis and promotion to other types than Kubernetes deployments such as Flux Helm releases or OpenFaaS functions
### Contributing

View File

@@ -22,7 +22,7 @@ spec:
- public-gateway.istio-system.svc.cluster.local
# Istio virtual service host names (optional)
hosts:
- podinfo.iowa.weavedx.com
- app.iowa.weavedx.com
canaryAnalysis:
# max number of failed metric checks before rollback
threshold: 10

View File

@@ -6,6 +6,8 @@ metadata:
labels:
app: podinfo
spec:
minReadySeconds: 5
revisionHistoryLimit: 5
strategy:
rollingUpdate:
maxUnavailable: 0
@@ -45,10 +47,7 @@ spec:
- http
- localhost:9898/healthz
initialDelaySeconds: 5
failureThreshold: 3
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
timeoutSeconds: 5
readinessProbe:
exec:
command:
@@ -57,10 +56,7 @@ spec:
- http
- localhost:9898/readyz
initialDelaySeconds: 5
failureThreshold: 3
periodSeconds: 3
successThreshold: 1
timeoutSeconds: 1
timeoutSeconds: 5
resources:
limits:
cpu: 2000m

View File

@@ -22,7 +22,7 @@ spec:
serviceAccountName: flagger
containers:
- name: flagger
image: stefanprodan/flagger:0.1.0-beta.6
image: quay.io/stefanprodan/flagger:0.1.0
imagePullPolicy: Always
ports:
- name: http
@@ -41,6 +41,7 @@ spec:
- --timeout=2
- --spider
- http://localhost:8080/healthz
timeoutSeconds: 5
readinessProbe:
exec:
command:
@@ -50,6 +51,7 @@ spec:
- --timeout=2
- --spider
- http://localhost:8080/healthz
timeoutSeconds: 5
resources:
limits:
memory: "512Mi"

View File

@@ -1,6 +1,6 @@
apiVersion: v1
name: flagger
version: 0.1.0
appVersion: 0.1.0-beta.6
appVersion: 0.1.0
description: Flagger is a Kubernetes operator that automates the promotion of canary deployments using Istio routing for traffic shifting and Prometheus metrics for canary analysis.
home: https://github.com/stefanprodan/flagger

View File

@@ -1,14 +1,20 @@
# Flagger
Flagger is a Kubernetes operator that automates the promotion of canary deployments
[Flagger](https://flagger.app) is a Kubernetes operator that automates the promotion of canary deployments
using Istio routing for traffic shifting and Prometheus metrics for canary analysis.
## Installing the Chart
Add Flagger Hel repository:
```console
helm repo add flagger https://flagger.app
```
To install the chart with the release name `flagger`:
```console
$ helm upgrade --install flagger ./charts/flagger --namespace=istio-system
$ helm install --name flagger --namespace istio-system flagger/flagger
```
The command deploys Flagger on the Kubernetes cluster in the istio-system namespace.
@@ -30,9 +36,16 @@ The following tables lists the configurable parameters of the Flagger chart and
Parameter | Description | Default
--- | --- | ---
`image.repository` | image repository | `stefanprodan/flagger`
`image.repository` | image repository | `quay.io/stefanprodan/flagger`
`image.tag` | image tag | `<VERSION>`
`image.pullPolicy` | image pull policy | `IfNotPresent`
`controlLoopInterval` | wait interval between checks | `10s`
`metricsServer` | Prometheus URL | `http://prometheus.istio-system:9090`
`slack.url` | Slack incoming webhook | None
`slack.channel` | Slack channel | None
`slack.user` | Slack username | `flagger`
`rbac.create` | if `true`, create and use RBAC resources | `true`
`crd.create` | if `true`, create Flagger's CRDs | `true`
`resources.requests/cpu` | pod CPU request | `10m`
`resources.requests/memory` | pod memory request | `32Mi`
`resources.limits/cpu` | pod CPU limit | `1000m`
@@ -44,16 +57,16 @@ Parameter | Description | Default
Specify each parameter using the `--set key=value[,key=value]` argument to `helm upgrade`. For example,
```console
$ helm upgrade --install flagger ./charts/flagger \
--namespace=istio-system \
--set=image.tag=0.0.2
$ helm upgrade -i flagger flagger/flagger \
--namespace istio-system \
--set controlLoopInterval=1m
```
Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example,
```console
$ helm upgrade --install flagger ./charts/flagger \
--namespace=istio-system \
$ helm upgrade -i flagger flagger/flagger \
--namespace istio-system \
-f values.yaml
```

View File

@@ -3,6 +3,8 @@ apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: canaries.flagger.app
annotations:
"helm.sh/resource-policy": keep
spec:
group: flagger.app
version: v1alpha1

View File

@@ -37,24 +37,31 @@ spec:
- -log-level=info
- -control-loop-interval={{ .Values.controlLoopInterval }}
- -metrics-server={{ .Values.metricsServer }}
{{- if .Values.slack.url }}
- -slack-url={{ .Values.slack.url }}
- -slack-user={{ .Values.slack.user }}
- -slack-channel={{ .Values.slack.channel }}
{{- end }}
livenessProbe:
exec:
command:
- wget
- --quiet
- --tries=1
- --timeout=2
- --timeout=4
- --spider
- http://localhost:8080/healthz
timeoutSeconds: 5
readinessProbe:
exec:
command:
- wget
- --quiet
- --tries=1
- --timeout=2
- --timeout=4
- --spider
- http://localhost:8080/healthz
timeoutSeconds: 5
resources:
{{ toYaml .Values.resources | indent 12 }}
{{- with .Values.nodeSelector }}

View File

@@ -1,13 +1,19 @@
# Default values for flagger.
image:
repository: stefanprodan/flagger
tag: 0.1.0-beta.6
repository: quay.io/stefanprodan/flagger
tag: 0.1.0
pullPolicy: IfNotPresent
controlLoopInterval: "10s"
metricsServer: "http://prometheus.istio-system.svc.cluster.local:9090"
slack:
user: flagger
channel:
# incoming webhook https://api.slack.com/incoming-webhooks
url:
crd:
create: true

View File

@@ -6,12 +6,13 @@ import (
"time"
_ "github.com/istio/glog"
sharedclientset "github.com/knative/pkg/client/clientset/versioned"
istioclientset "github.com/knative/pkg/client/clientset/versioned"
"github.com/knative/pkg/signals"
clientset "github.com/stefanprodan/flagger/pkg/client/clientset/versioned"
informers "github.com/stefanprodan/flagger/pkg/client/informers/externalversions"
"github.com/stefanprodan/flagger/pkg/controller"
"github.com/stefanprodan/flagger/pkg/logging"
"github.com/stefanprodan/flagger/pkg/notifier"
"github.com/stefanprodan/flagger/pkg/server"
"github.com/stefanprodan/flagger/pkg/version"
"k8s.io/client-go/kubernetes"
@@ -27,6 +28,9 @@ var (
controlLoopInterval time.Duration
logLevel string
port string
slackURL string
slackUser string
slackChannel string
)
func init() {
@@ -36,6 +40,9 @@ func init() {
flag.DurationVar(&controlLoopInterval, "control-loop-interval", 10*time.Second, "wait interval between rollouts")
flag.StringVar(&logLevel, "log-level", "debug", "Log level can be: debug, info, warning, error.")
flag.StringVar(&port, "port", "8080", "Port to listen on.")
flag.StringVar(&slackURL, "slack-url", "", "Slack hook URL.")
flag.StringVar(&slackUser, "slack-user", "flagger", "Slack user name.")
flag.StringVar(&slackChannel, "slack-channel", "", "Slack channel.")
}
func main() {
@@ -59,9 +66,9 @@ func main() {
logger.Fatalf("Error building kubernetes clientset: %v", err)
}
sharedClient, err := sharedclientset.NewForConfig(cfg)
istioClient, err := istioclientset.NewForConfig(cfg)
if err != nil {
logger.Fatalf("Error building shared clientset: %v", err)
logger.Fatalf("Error building istio clientset: %v", err)
}
flaggerClient, err := clientset.NewForConfig(cfg)
@@ -88,17 +95,28 @@ func main() {
logger.Errorf("Metrics server %s unreachable %v", metricsServer, err)
}
var slack *notifier.Slack
if slackURL != "" {
slack, err = notifier.NewSlack(slackURL, slackUser, slackChannel)
if err != nil {
logger.Errorf("Notifier %v", err)
} else {
logger.Infof("Slack notifications enabled for channel %s", slack.Channel)
}
}
// start HTTP server
go server.ListenAndServe(port, 3*time.Second, logger, stopCh)
c := controller.NewController(
kubeClient,
sharedClient,
istioClient,
flaggerClient,
canaryInformer,
controlLoopInterval,
metricsServer,
logger,
slack,
)
flaggerInformerFactory.Start(stopCh)

View File

@@ -8,8 +8,6 @@
Flagger is a Kubernetes operator that automates the promotion of canary deployments
using Istio routing for traffic shifting and Prometheus metrics for canary analysis.
The project is currently in experimental phase and it is expected that breaking changes
to the API will be made in the upcoming releases.
### Install
@@ -351,13 +349,28 @@ flagger_canary_duration_seconds_sum{name="podinfo",namespace="test"} 17.3561329
flagger_canary_duration_seconds_count{name="podinfo",namespace="test"} 6
```
### Alerting
Flagger can be configured to send Slack notifications:
```bash
helm upgrade -i flagger flagger/flagger \
--namespace=istio-system \
--set slack.url=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK \
--set slack.channel=general \
--set slack.user=flagger
```
Once configured with a Slack incoming webhook, Flagger will post messages when a canary deployment has been initialized,
when a new revision has been detected and if the canary analysis failed or succeeded.
![flagger-slack](https://raw.githubusercontent.com/stefanprodan/flagger/master/docs/screens/slack-notifications.png)
### Roadmap
* Extend the canary analysis and promotion to other types than Kubernetes deployments such as Flux Helm releases or OpenFaaS functions
* Extend the validation mechanism to support other metrics than HTTP success rate and latency
* Add support for comparing the canary metrics to the primary ones and do the validation based on the derivation between the two
* Alerting: trigger Alertmanager on successful or failed promotions
* Reporting: publish canary analysis results to Slack/Jira/etc
* Extend the canary analysis and promotion to other types than Kubernetes deployments such as Flux Helm releases or OpenFaaS functions
### Contributing

View File

@@ -4,7 +4,7 @@ remote_theme: errordeveloper/simple-project-homepage
repository: stefanprodan/flagger
by_weaveworks: true
url: "https://stefanprodan.github.io/flagger"
url: "https://flagger.app"
baseurl: "/"
twitter:
@@ -15,7 +15,7 @@ author:
# Set default og:image
defaults:
- scope: {path: ""}
values: {image: "logo/logo-flagger.png"}
values: {image: "diagrams/flagger-overview.png"}
# See: https://material.io/guidelines/style/color.html
# Use color-name-value, like pink-200 or deep-purple-100
@@ -51,14 +51,4 @@ plugins:
exclude:
- CNAME
- Dockerfile
- Gopkg.lock
- Gopkg.toml
- LICENSE
- Makefile
- add-model.sh
- build
- cmd
- pkg
- tag_release.sh
- vendor

Binary file not shown.

Binary file not shown.

View File

@@ -2,12 +2,12 @@ apiVersion: v1
entries:
flagger:
- apiVersion: v1
appVersion: 0.1.0-beta.6
created: 2018-10-29T21:46:00.29473+02:00
appVersion: 0.1.0
created: 2018-11-25T20:52:59.226156+02:00
description: Flagger is a Kubernetes operator that automates the promotion of
canary deployments using Istio routing for traffic shifting and Prometheus metrics
for canary analysis.
digest: c17380b0f4e08a9b1f76a0e52d53677248c5756eff6a1fcd5629d3465dd1ad58
digest: 03e05634149e13ddfddae6757266d65c271878a026c21c7d1429c16712bf3845
home: https://github.com/stefanprodan/flagger
name: flagger
urls:
@@ -16,13 +16,13 @@ entries:
grafana:
- apiVersion: v1
appVersion: 5.3.1
created: 2018-10-29T21:46:00.295247+02:00
created: 2018-11-25T20:52:59.226488+02:00
description: A Grafana Helm chart for monitoring progressive deployments powered
by Istio and Flagger
digest: 370aa2e6a0d4ab717f047658bdb02969b8f2a4d2e81c0bc96b90e3365229715f
digest: 12ad252512006e91b6eb359c4e0c73e7f01f74f3c07c85bb1e66780bed6747f5
home: https://github.com/stefanprodan/flagger
name: grafana
urls:
- https://stefanprodan.github.io/flagger/grafana-0.1.0.tgz
version: 0.1.0
generated: 2018-10-29T21:46:00.293821+02:00
generated: 2018-11-25T20:52:59.225755+02:00

Binary file not shown.

Before

Width:  |  Height:  |  Size: 620 KiB

After

Width:  |  Height:  |  Size: 442 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

View File

@@ -65,6 +65,8 @@ type CanaryStatus struct {
State string `json:"state"`
CanaryRevision string `json:"canaryRevision"`
FailedChecks int `json:"failedChecks"`
// +optional
LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"`
}
// CanaryService is used to create ClusterIP services

View File

@@ -30,7 +30,7 @@ func (in *Canary) DeepCopyInto(out *Canary) {
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
in.Status.DeepCopyInto(&out.Status)
return
}
@@ -171,6 +171,7 @@ func (in *CanarySpec) DeepCopy() *CanarySpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CanaryStatus) DeepCopyInto(out *CanaryStatus) {
*out = *in
in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime)
return
}

View File

@@ -12,6 +12,7 @@ import (
flaggerscheme "github.com/stefanprodan/flagger/pkg/client/clientset/versioned/scheme"
flaggerinformers "github.com/stefanprodan/flagger/pkg/client/informers/externalversions/flagger/v1alpha1"
flaggerlisters "github.com/stefanprodan/flagger/pkg/client/listers/flagger/v1alpha1"
"github.com/stefanprodan/flagger/pkg/notifier"
"go.uber.org/zap"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
@@ -43,6 +44,7 @@ type Controller struct {
router CanaryRouter
observer CanaryObserver
recorder CanaryRecorder
notifier *notifier.Slack
}
func NewController(
@@ -53,6 +55,7 @@ func NewController(
flaggerWindow time.Duration,
metricServer string,
logger *zap.SugaredLogger,
notifier *notifier.Slack,
) *Controller {
logger.Debug("Creating event broadcaster")
@@ -100,6 +103,7 @@ func NewController(
router: router,
observer: observer,
recorder: recorder,
notifier: notifier,
}
flaggerInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
@@ -256,6 +260,17 @@ func (c *Controller) recordEventWarningf(r *flaggerv1.Canary, template string, a
c.eventRecorder.Event(r, corev1.EventTypeWarning, "Synced", fmt.Sprintf(template, args...))
}
func (c *Controller) sendNotification(workload string, namespace string, message string, warn bool) {
if c.notifier == nil {
return
}
err := c.notifier.Post(workload, namespace, message, warn)
if err != nil {
c.logger.Error(err)
}
}
func int32p(i int32) *int32 {
return &i
}

View File

@@ -48,6 +48,9 @@ func (c *CanaryDeployer) Promote(cd *flaggerv1.Canary) error {
return fmt.Errorf("deployment %s.%s query error %v", primaryName, cd.Namespace, err)
}
primary.Spec.MinReadySeconds = canary.Spec.MinReadySeconds
primary.Spec.RevisionHistoryLimit = canary.Spec.RevisionHistoryLimit
primary.Spec.Strategy = canary.Spec.Strategy
primary.Spec.Template.Spec = canary.Spec.Template.Spec
_, err = c.kubeClient.AppsV1().Deployments(primary.Namespace).Update(primary)
if err != nil {
@@ -127,6 +130,7 @@ func (c *CanaryDeployer) IsNewSpec(cd *flaggerv1.Canary) (bool, error) {
// SetFailedChecks updates the canary failed checks counter
func (c *CanaryDeployer) SetFailedChecks(cd *flaggerv1.Canary, val int) error {
cd.Status.FailedChecks = val
cd.Status.LastTransitionTime = metav1.Now()
cd, err := c.flaggerClient.FlaggerV1alpha1().Canaries(cd.Namespace).Update(cd)
if err != nil {
return fmt.Errorf("deployment %s.%s update error %v", cd.Spec.TargetRef.Name, cd.Namespace, err)
@@ -137,6 +141,7 @@ func (c *CanaryDeployer) SetFailedChecks(cd *flaggerv1.Canary, val int) error {
// SetState updates the canary status state
func (c *CanaryDeployer) SetState(cd *flaggerv1.Canary, state string) error {
cd.Status.State = state
cd.Status.LastTransitionTime = metav1.Now()
cd, err := c.flaggerClient.FlaggerV1alpha1().Canaries(cd.Namespace).Update(cd)
if err != nil {
return fmt.Errorf("deployment %s.%s update error %v", cd.Spec.TargetRef.Name, cd.Namespace, err)
@@ -163,6 +168,7 @@ func (c *CanaryDeployer) SyncStatus(cd *flaggerv1.Canary, status flaggerv1.Canar
cd.Status.State = status.State
cd.Status.FailedChecks = status.FailedChecks
cd.Status.CanaryRevision = specEnc
cd.Status.LastTransitionTime = metav1.Now()
cd, err = c.flaggerClient.FlaggerV1alpha1().Canaries(cd.Namespace).Update(cd)
if err != nil {
return fmt.Errorf("deployment %s.%s update error %v", cd.Spec.TargetRef.Name, cd.Namespace, err)
@@ -238,8 +244,10 @@ func (c *CanaryDeployer) createPrimaryDeployment(cd *flaggerv1.Canary) error {
},
},
Spec: appsv1.DeploymentSpec{
Replicas: canaryDep.Spec.Replicas,
Strategy: canaryDep.Spec.Strategy,
MinReadySeconds: canaryDep.Spec.MinReadySeconds,
RevisionHistoryLimit: canaryDep.Spec.RevisionHistoryLimit,
Replicas: canaryDep.Spec.Replicas,
Strategy: canaryDep.Spec.Strategy,
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": primaryName,

View File

@@ -162,7 +162,7 @@ func newTestHPA() *hpav2.HorizontalPodAutoscaler {
{
Type: "Resource",
Resource: &hpav2.ResourceMetricSource{
Name: "cpu",
Name: "cpu",
TargetAverageUtilization: int32p(99),
},
},

View File

@@ -110,6 +110,8 @@ func (c *Controller) advanceCanary(name string, namespace string) {
return
}
c.recorder.SetStatus(cd)
c.sendNotification(cd.Spec.TargetRef.Name, cd.Namespace,
"Canary analysis failed, rollback finished.", true)
return
}
@@ -180,6 +182,8 @@ func (c *Controller) advanceCanary(name string, namespace string) {
return
}
c.recorder.SetStatus(cd)
c.sendNotification(cd.Spec.TargetRef.Name, cd.Namespace,
"Canary analysis completed successfully, promotion finished.", false)
}
}
@@ -196,11 +200,15 @@ func (c *Controller) checkCanaryStatus(cd *flaggerv1.Canary, deployer CanaryDepl
}
c.recorder.SetStatus(cd)
c.recordEventInfof(cd, "Initialization done! %s.%s", cd.Name, cd.Namespace)
c.sendNotification(cd.Spec.TargetRef.Name, cd.Namespace,
"New deployment detected, initialization completed.", false)
return false
}
if diff, err := deployer.IsNewSpec(cd); diff {
c.recordEventInfof(cd, "New revision detected! Scaling up %s.%s", cd.Spec.TargetRef.Name, cd.Namespace)
c.sendNotification(cd.Spec.TargetRef.Name, cd.Namespace,
"New revision detected, starting canary analysis.", false)
if err = deployer.Scale(cd, 1); err != nil {
c.recordEventErrorf(cd, "%v", err)
return false

102
pkg/notifier/slack.go Normal file
View File

@@ -0,0 +1,102 @@
package notifier
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
)
// Slack holds the hook URL
type Slack struct {
URL string
Username string
Channel string
IconEmoji string
}
// SlackPayload holds the channel and attachments
type SlackPayload struct {
Channel string `json:"channel"`
Username string `json:"username"`
IconUrl string `json:"icon_url"`
IconEmoji string `json:"icon_emoji"`
Text string `json:"text,omitempty"`
Attachments []SlackAttachment `json:"attachments,omitempty"`
}
// SlackAttachment holds the markdown message body
type SlackAttachment struct {
Color string `json:"color"`
AuthorName string `json:"author_name"`
Text string `json:"text"`
MrkdwnIn []string `json:"mrkdwn_in"`
}
// NewSlack validates the Slack URL and returns a Slack object
func NewSlack(hookURL string, username string, channel string) (*Slack, error) {
_, err := url.ParseRequestURI(hookURL)
if err != nil {
return nil, fmt.Errorf("invalid Slack hook URL %s", hookURL)
}
if username == "" {
return nil, errors.New("empty Slack username")
}
if channel == "" {
return nil, errors.New("empty Slack channel")
}
return &Slack{
Channel: channel,
URL: hookURL,
Username: username,
IconEmoji: ":rocket:",
}, nil
}
// Post Slack message
func (s *Slack) Post(workload string, namespace string, message string, warn bool) error {
payload := SlackPayload{
Channel: s.Channel,
Username: s.Username,
}
color := "good"
if warn {
color = "danger"
}
a := SlackAttachment{
Color: color,
AuthorName: fmt.Sprintf("%s.%s", workload, namespace),
Text: message,
MrkdwnIn: []string{"text"},
}
payload.Attachments = []SlackAttachment{a}
data, err := json.Marshal(payload)
if err != nil {
return fmt.Errorf("marshalling slack payload failed %v", err)
}
b := bytes.NewBuffer(data)
if res, err := http.Post(s.URL, "application/json", b); err != nil {
return fmt.Errorf("sending data to slack failed %v", err)
} else {
defer res.Body.Close()
statusCode := res.StatusCode
if statusCode != 200 {
body, _ := ioutil.ReadAll(res.Body)
return fmt.Errorf("sending data to slack failed %v", string(body))
}
}
return nil
}

View File

@@ -1,4 +1,4 @@
package version
var VERSION = "0.1.0-beta.6"
var VERSION = "0.1.0"
var REVISION = "unknown"