mirror of
https://github.com/fluxcd/flagger.git
synced 2026-04-15 06:57:34 +00:00
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
231 lines
6.9 KiB
Go
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
|
|
}
|