Skip to content

Commit

Permalink
Merge pull request #218 from daviddelucca/feat/flagger-canary-support
Browse files Browse the repository at this point in the history
feat: add flagger canary support
  • Loading branch information
distorhead authored Sep 2, 2021
2 parents 233cb8d + 4583352 commit 0d36ebc
Show file tree
Hide file tree
Showing 9 changed files with 805 additions and 114 deletions.
12 changes: 10 additions & 2 deletions doc/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ Kubedog CLI utility is a tool that can be used to track what is going on with th

**NOTE:** While kubedog includes a CLI, it provides a *minimal* interface to access library functions. CLI was created to check library features and for debug purposes. Currently, we have no plans on further improvement of CLI.

There are 3 modes of resource tracking:
There are 4 modes of resource tracking:
* multitrack
* follow
* rollout
* canary

The commands are `kubedog multitrack ...`, `kubedog follow ...` and `kubedog rollout track ...` respectively.

Expand Down Expand Up @@ -118,14 +119,15 @@ Multitrack(
- `specs` — description of objects to track
- `opts` — multitrack specific options

`specs` argument describes what `Deployments`, `StatefulSets`, `DaemonSets` and `Jobs` to track using `MultitrackSpec` structure. `MultitrackSpec` allows to specify different modes of tracking per-resource (such as allowed failures count, log regexp and other):
`specs` argument describes what `Deployments`, `StatefulSets`, `DaemonSets`, `Jobs` and `Canaries` to track using `MultitrackSpec` structure. `MultitrackSpec` allows to specify different modes of tracking per-resource (such as allowed failures count, log regexp and other):

```
type MultitrackSpecs struct {
Deployments []MultitrackSpec
StatefulSets []MultitrackSpec
DaemonSets []MultitrackSpec
Jobs []MultitrackSpec
Canaries []MultitrackSpec
}
type MultitrackSpec struct {
Expand All @@ -150,6 +152,12 @@ type MultitrackSpec struct {

`Multitrack` function is a blocking call, which will return on error or when all resources are ready accordingly to the specified specs options.

#### Canaries

For now, we only support Canary resource from [Flagger](https://github.com/fluxcd/flagger).

It will watch resource Canary while canary promotion is rolling and will break after a successful or failed result.

### Follow tracker (DEPRECATED)

Follow tracker simply prints to the screen all resource related events. Follow tracker can be used as simple `tail -f` tool, but for kubernetes resources. This tracker used to implement follow mode of the CLI.
Expand Down
14 changes: 5 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,17 @@ module github.com/werf/kubedog

require (
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 // indirect
github.com/fluxcd/flagger v1.8.0
github.com/gookit/color v1.3.5
github.com/gophercloud/gophercloud v0.1.0 // indirect
github.com/spf13/cobra v1.1.1
github.com/ugorji/go v1.1.4 // indirect
github.com/werf/logboek v0.5.1
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 // indirect
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0
k8s.io/api v0.20.2
k8s.io/apimachinery v0.20.2
k8s.io/api v0.20.4
k8s.io/apimachinery v0.20.4
k8s.io/cli-runtime v0.20.2
k8s.io/client-go v0.20.2
k8s.io/client-go v0.20.4
k8s.io/klog v1.0.0
k8s.io/klog/v2 v2.4.0 // indirect
sigs.k8s.io/structured-merge-diff/v3 v3.0.0 // indirect
k8s.io/klog/v2 v2.4.0
)

go 1.14
309 changes: 207 additions & 102 deletions go.sum

Large diffs are not rendered by default.

162 changes: 162 additions & 0 deletions pkg/tracker/canary/feed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package canary

import (
"context"
"sync"

"github.com/werf/kubedog/pkg/tracker"
"k8s.io/client-go/kubernetes"

watchtools "k8s.io/client-go/tools/watch"
)

type Feed interface {
OnAdded(func() error)
OnSucceeded(func() error)
OnFailed(func(reason string) error)
OnEventMsg(func(msg string) error)
OnStatus(func(CanaryStatus) error)

GetStatus() CanaryStatus
Track(name, namespace string, kube kubernetes.Interface, opts tracker.Options) error
}

func NewFeed() Feed {
return &feed{}
}

type feed struct {
OnAddedFunc func() error
OnSucceededFunc func() error
OnFailedFunc func(string) error
OnEventMsgFunc func(string) error
OnStatusFunc func(CanaryStatus) error

statusMux sync.Mutex
status CanaryStatus
}

func (f *feed) OnAdded(function func() error) {
f.OnAddedFunc = function
}
func (f *feed) OnSucceeded(function func() error) {
f.OnSucceededFunc = function
}
func (f *feed) OnFailed(function func(string) error) {
f.OnFailedFunc = function
}
func (f *feed) OnEventMsg(function func(string) error) {
f.OnEventMsgFunc = function
}
func (f *feed) OnStatus(function func(CanaryStatus) error) {
f.OnStatusFunc = function
}

func (f *feed) Track(name, namespace string, kube kubernetes.Interface, opts tracker.Options) error {
errorChan := make(chan error, 0)
doneChan := make(chan struct{}, 0)

parentContext := opts.ParentContext
if parentContext == nil {
parentContext = context.Background()
}
ctx, cancel := watchtools.ContextWithOptionalTimeout(parentContext, opts.Timeout)
defer cancel()

canary := NewTracker(name, namespace, kube, opts)

go func() {
err := canary.Track(ctx)
if err != nil {
errorChan <- err
} else {
doneChan <- struct{}{}
}
}()

for {
select {
case status := <-canary.Added:
f.setStatus(status)

if f.OnAddedFunc != nil {
err := f.OnAddedFunc()
if err == tracker.StopTrack {
return nil
}
if err != nil {
return err
}
}
case status := <-canary.Succeeded:
f.setStatus(status)

if f.OnSucceededFunc != nil {
err := f.OnSucceededFunc()
if err == tracker.StopTrack {
return nil
}
if err != nil {
return err
}
}

case status := <-canary.Failed:
f.setStatus(status)

if f.OnFailedFunc != nil {
err := f.OnFailedFunc(status.FailedReason)
if err == tracker.StopTrack {
return nil
}
if err != nil {
return err
}
}

case msg := <-canary.EventMsg:
if f.OnEventMsgFunc != nil {
err := f.OnEventMsgFunc(msg)
if err == tracker.StopTrack {
return nil
}
if err != nil {
return err
}
}

case status := <-canary.Status:
f.setStatus(status)

if f.OnStatusFunc != nil {
err := f.OnStatusFunc(status)
if err == tracker.StopTrack {
return nil
}
if err != nil {
return err
}
}

case err := <-errorChan:
return err
case <-doneChan:
return nil
}
}
}

func (f *feed) setStatus(status CanaryStatus) {
f.statusMux.Lock()
defer f.statusMux.Unlock()

if status.StatusGeneration > f.status.StatusGeneration {
f.status = status
}
}

func (f *feed) GetStatus() CanaryStatus {
f.statusMux.Lock()
defer f.statusMux.Unlock()
return f.status
}
54 changes: 54 additions & 0 deletions pkg/tracker/canary/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package canary

import (
v1beta1 "github.com/fluxcd/flagger/pkg/apis/flagger/v1beta1"
"github.com/werf/kubedog/pkg/tracker/indicators"
"github.com/werf/kubedog/pkg/utils"
)

type CanaryStatus struct {
v1beta1.CanaryStatus

StatusGeneration uint64

StatusIndicator *indicators.StringEqualConditionIndicator

Duration string
Age string

IsSucceeded bool
IsFailed bool
FailedReason string
}

func NewCanaryStatus(object *v1beta1.Canary, statusGeneration uint64, isTrackerFailed bool, trackerFailedReason string, canariesStatuses map[string]v1beta1.CanaryStatus) CanaryStatus {
res := CanaryStatus{
CanaryStatus: object.Status,
StatusGeneration: statusGeneration,
StatusIndicator: &indicators.StringEqualConditionIndicator{},
Age: utils.TranslateTimestampSince(object.CreationTimestamp),
}

switch object.Status.Phase {
case v1beta1.CanaryPhaseInitialized, v1beta1.CanaryPhaseSucceeded:
res.IsSucceeded = true
case v1beta1.CanaryPhaseFailed:
if !res.IsFailed {
errorMessage := "Failed - "
for _, condition := range object.Status.Conditions {
errorMessage += condition.Message
}
res.IsFailed = true
res.FailedReason = errorMessage
}
default:
res.StatusIndicator.Value = string(object.Status.Phase)
}

if !res.IsSucceeded && !res.IsFailed {
res.IsFailed = isTrackerFailed
res.FailedReason = trackerFailedReason
}

return res
}
Loading

0 comments on commit 0d36ebc

Please sign in to comment.