Files
flagger/pkg/router/smi.go
T
Tanner Altares 92937a8f48 kubectl annotation support
rebase and squash

fix fmt issues

revert Dockerfile

revert go.mod and go.sum

introduction of finalizer

introduction of finalizer

remove test for finalizer add istio tests

fix fmt issues

revert go.mod and go.sum

revert Dockerfile and main.go

fmt deployment controller

introduction of finalizer

rebase and squash

fix fmt issues

revert Dockerfile

revert go.mod and go.sum

introduction of finalizer

introduction of finalizer

remove test for finalizer add istio tests

fix fmt issues

revert go.mod and go.sum

revert Dockerfile and main.go

fmt deployment controller

add unit tests for finalizing

introduction of finalizer

rebase and squash

fix fmt issues

revert Dockerfile

revert go.mod and go.sum

introduction of finalizer

introduction of finalizer

remove test for finalizer add istio tests

fix fmt issues

revert go.mod and go.sum

revert Dockerfile and main.go

fmt deployment controller

run fmt to clean up formatting

review changes

add kubectl annotation

add kubectl annotation support

introduction of finalizer

introduction of finalizer

rebase and squash

fix fmt issues

revert Dockerfile

revert go.mod and go.sum

introduction of finalizer

introduction of finalizer

remove test for finalizer add istio tests

fix fmt issues

revert go.mod and go.sum

revert Dockerfile and main.go

fmt deployment controller

introduction of finalizer

rebase and squash

fix fmt issues

revert Dockerfile

revert go.mod and go.sum

introduction of finalizer

introduction of finalizer

remove test for finalizer add istio tests

fix fmt issues

revert go.mod and go.sum

revert Dockerfile and main.go

fmt deployment controller

add unit tests for finalizing

introduction of finalizer

rebase and squash

fix fmt issues

revert Dockerfile

revert go.mod and go.sum

introduction of finalizer

introduction of finalizer

remove test for finalizer add istio tests

fix fmt issues

revert go.mod and go.sum

revert Dockerfile and main.go

fmt deployment controller

run fmt to clean up formatting

review changes

introduction of finalizer

introduction of finalizer

rebase and squash

fix fmt issues

revert Dockerfile

revert go.mod and go.sum

introduction of finalizer

introduction of finalizer

remove test for finalizer add istio tests

fix fmt issues

revert go.mod and go.sum

revert Dockerfile and main.go

fmt deployment controller

introduction of finalizer

rebase and squash

fix fmt issues

revert Dockerfile

revert go.mod and go.sum

introduction of finalizer

introduction of finalizer

remove test for finalizer add istio tests

fix fmt issues

revert go.mod and go.sum

revert Dockerfile and main.go

fmt deployment controller

add unit tests for finalizing

introduction of finalizer

rebase and squash

fix fmt issues

revert Dockerfile

revert go.mod and go.sum

introduction of finalizer

introduction of finalizer

remove test for finalizer add istio tests

fix fmt issues

revert go.mod and go.sum

revert Dockerfile and main.go

fmt deployment controller

run fmt to clean up formatting

review changes
2020-03-20 15:13:51 -05:00

231 lines
6.9 KiB
Go

package router
import (
"encoding/json"
"fmt"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"go.uber.org/zap"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/kubernetes"
flaggerv1 "github.com/weaveworks/flagger/pkg/apis/flagger/v1beta1"
smiv1alpha1 "github.com/weaveworks/flagger/pkg/apis/smi/v1alpha1"
smiv1alpha2 "github.com/weaveworks/flagger/pkg/apis/smi/v1alpha2"
clientset "github.com/weaveworks/flagger/pkg/client/clientset/versioned"
)
type SmiRouter struct {
kubeClient kubernetes.Interface
flaggerClient clientset.Interface
smiClient clientset.Interface
logger *zap.SugaredLogger
targetMesh string
}
// Reconcile creates or updates the SMI traffic split
func (sr *SmiRouter) Reconcile(canary *flaggerv1.Canary) error {
apexName, primaryName, canaryName := canary.GetServiceNames()
var host string
if len(canary.Spec.Service.Hosts) > 0 {
host = canary.Spec.Service.Hosts[0]
} else {
host = apexName
}
tsSpec := smiv1alpha1.TrafficSplitSpec{
Service: host,
Backends: []smiv1alpha1.TrafficSplitBackend{
{
Service: canaryName,
Weight: resource.NewQuantity(0, resource.DecimalExponent),
},
{
Service: primaryName,
Weight: resource.NewQuantity(100, resource.DecimalExponent),
},
},
}
ts, err := sr.smiClient.SplitV1alpha1().TrafficSplits(canary.Namespace).Get(apexName, metav1.GetOptions{})
// create traffic split
if errors.IsNotFound(err) {
t := &smiv1alpha1.TrafficSplit{
ObjectMeta: metav1.ObjectMeta{
Name: apexName,
Namespace: canary.Namespace,
OwnerReferences: []metav1.OwnerReference{
*metav1.NewControllerRef(canary, schema.GroupVersionKind{
Group: flaggerv1.SchemeGroupVersion.Group,
Version: flaggerv1.SchemeGroupVersion.Version,
Kind: flaggerv1.CanaryKind,
}),
},
Annotations: sr.makeAnnotations(canary.Spec.Service.Gateways),
},
Spec: tsSpec,
}
_, err := sr.smiClient.SplitV1alpha1().TrafficSplits(canary.Namespace).Create(t)
if err != nil {
return fmt.Errorf("TrafficSplit %s.%s create error: %w", apexName, canary.Namespace, err)
}
sr.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).
Infof("TrafficSplit %s.%s created", t.GetName(), canary.Namespace)
return nil
} else if err != nil {
return fmt.Errorf("TrafficSplit %s.%s get query error: %w", apexName, canary.Namespace, err)
}
// update traffic split
if diff := cmp.Diff(tsSpec, ts.Spec, cmpopts.IgnoreTypes(resource.Quantity{})); diff != "" {
tsClone := ts.DeepCopy()
tsClone.Spec = tsSpec
_, err := sr.smiClient.SplitV1alpha1().TrafficSplits(canary.Namespace).Update(tsClone)
if err != nil {
return fmt.Errorf("TrafficSplit %s.%s update error: %w", apexName, canary.Namespace, err)
}
sr.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).
Infof("TrafficSplit %s.%s updated", apexName, canary.Namespace)
return nil
}
return nil
}
// GetRoutes returns the destinations weight for primary and canary
func (sr *SmiRouter) GetRoutes(canary *flaggerv1.Canary) (
primaryWeight int,
canaryWeight int,
mirrored bool,
err error,
) {
apexName, primaryName, canaryName := canary.GetServiceNames()
ts, err := sr.smiClient.SplitV1alpha1().TrafficSplits(canary.Namespace).Get(apexName, metav1.GetOptions{})
if err != nil {
err = fmt.Errorf("TrafficSplit %s.%s get query error %v", apexName, canary.Namespace, err)
return
}
for _, r := range ts.Spec.Backends {
w, _ := r.Weight.AsInt64()
if r.Service == primaryName {
primaryWeight = int(w)
}
if r.Service == canaryName {
canaryWeight = int(w)
}
}
if primaryWeight == 0 && canaryWeight == 0 {
err = fmt.Errorf("TrafficSplit %s.%s does not contain routes for %s and %s",
apexName, canary.Namespace, primaryName, canaryName)
}
mirrored = false
return
}
// SetRoutes updates the destinations weight for primary and canary
func (sr *SmiRouter) SetRoutes(
canary *flaggerv1.Canary,
primaryWeight int,
canaryWeight int,
_ bool,
) error {
apexName, primaryName, canaryName := canary.GetServiceNames()
ts, err := sr.smiClient.SplitV1alpha1().TrafficSplits(canary.Namespace).Get(apexName, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("TrafficSplit %s.%s get query error %v", apexName, canary.Namespace, err)
}
backends := []smiv1alpha1.TrafficSplitBackend{
{
Service: canaryName,
Weight: resource.NewQuantity(int64(canaryWeight), resource.DecimalExponent),
},
{
Service: primaryName,
Weight: resource.NewQuantity(int64(primaryWeight), resource.DecimalExponent),
},
}
tsClone := ts.DeepCopy()
tsClone.Spec.Backends = backends
_, err = sr.smiClient.SplitV1alpha1().TrafficSplits(canary.Namespace).Update(tsClone)
if err != nil {
return fmt.Errorf("TrafficSplit %s.%s update error %v", apexName, canary.Namespace, err)
}
return nil
}
func (sr *SmiRouter) makeAnnotations(gateways []string) map[string]string {
res := make(map[string]string)
if sr.targetMesh == "istio" && len(gateways) > 0 {
g, _ := json.Marshal(gateways)
res["VirtualService.v1alpha3.networking.istio.io/spec.gateways"] = string(g)
}
return res
}
// getWithConvert overrides invalid traffic split and sets weight based on the canary status
func (sr *SmiRouter) getWithConvert(canary *flaggerv1.Canary, host string) (*smiv1alpha2.TrafficSplit, error) {
apexName, primaryName, canaryName := canary.GetServiceNames()
ts, err := sr.smiClient.SplitV1alpha2().TrafficSplits(canary.Namespace).Get(apexName, metav1.GetOptions{})
if errors.IsInvalid(err) {
t := &smiv1alpha2.TrafficSplit{
ObjectMeta: metav1.ObjectMeta{
Name: apexName,
Namespace: canary.Namespace,
OwnerReferences: []metav1.OwnerReference{
*metav1.NewControllerRef(canary, schema.GroupVersionKind{
Group: flaggerv1.SchemeGroupVersion.Group,
Version: flaggerv1.SchemeGroupVersion.Version,
Kind: flaggerv1.CanaryKind,
}),
},
Annotations: sr.makeAnnotations(canary.Spec.Service.Gateways),
},
Spec: smiv1alpha2.TrafficSplitSpec{
Service: host,
Backends: []smiv1alpha2.TrafficSplitBackend{
{
Service: canaryName,
Weight: canary.Status.CanaryWeight,
},
{
Service: primaryName,
Weight: 100 - canary.Status.CanaryWeight,
},
},
},
}
_, err := sr.smiClient.SplitV1alpha2().TrafficSplits(canary.Namespace).Update(t)
if err != nil {
return nil, fmt.Errorf("TrafficSplit %s.%s update error: %w", apexName, canary.Namespace, err)
}
sr.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).
Infof("TrafficSplit %s.%s converted", t.GetName(), canary.Namespace)
} else if err != nil {
return nil, fmt.Errorf("TrafficSplit %s.%s get query error %v", apexName, canary.Namespace, err)
}
return ts, nil
}
func (sr *SmiRouter) Finalize(canary *flaggerv1.Canary) error {
return nil
}