mirror of
https://github.com/fluxcd/flagger.git
synced 2026-04-15 06:57:34 +00:00
Use targetRef.name as the Kubernetes service name prefix only if service name is not specified Warn about routing conflicts when service name changes
198 lines
5.9 KiB
Go
198 lines
5.9 KiB
Go
package router
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
gloov1 "github.com/weaveworks/flagger/pkg/apis/gloo/v1"
|
|
"go.uber.org/zap"
|
|
"k8s.io/apimachinery/pkg/api/errors"
|
|
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/v1alpha3"
|
|
clientset "github.com/weaveworks/flagger/pkg/client/clientset/versioned"
|
|
)
|
|
|
|
// GlooRouter is managing Istio virtual services
|
|
type GlooRouter struct {
|
|
kubeClient kubernetes.Interface
|
|
glooClient clientset.Interface
|
|
flaggerClient clientset.Interface
|
|
logger *zap.SugaredLogger
|
|
upstreamDiscoveryNs string
|
|
}
|
|
|
|
// Reconcile creates or updates the Istio virtual service
|
|
func (gr *GlooRouter) Reconcile(canary *flaggerv1.Canary) error {
|
|
apexName, _, _ := canary.GetServiceNames()
|
|
canaryName := fmt.Sprintf("%s-%s-canary-%v", canary.Namespace, apexName, canary.Spec.Service.Port)
|
|
primaryName := fmt.Sprintf("%s-%s-primary-%v", canary.Namespace, apexName, canary.Spec.Service.Port)
|
|
|
|
newSpec := gloov1.UpstreamGroupSpec{
|
|
Destinations: []gloov1.WeightedDestination{
|
|
{
|
|
Destination: gloov1.Destination{
|
|
Upstream: gloov1.ResourceRef{
|
|
Name: primaryName,
|
|
Namespace: gr.upstreamDiscoveryNs,
|
|
},
|
|
},
|
|
Weight: 100,
|
|
},
|
|
{
|
|
Destination: gloov1.Destination{
|
|
Upstream: gloov1.ResourceRef{
|
|
Name: canaryName,
|
|
Namespace: gr.upstreamDiscoveryNs,
|
|
},
|
|
},
|
|
Weight: 0,
|
|
},
|
|
},
|
|
}
|
|
|
|
upstreamGroup, err := gr.glooClient.GlooV1().UpstreamGroups(canary.Namespace).Get(apexName, metav1.GetOptions{})
|
|
if errors.IsNotFound(err) {
|
|
upstreamGroup = &gloov1.UpstreamGroup{
|
|
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,
|
|
}),
|
|
},
|
|
},
|
|
Spec: newSpec,
|
|
}
|
|
|
|
_, err = gr.glooClient.GlooV1().UpstreamGroups(canary.Namespace).Create(upstreamGroup)
|
|
if err != nil {
|
|
return fmt.Errorf("UpstreamGroup %s.%s create error %v", apexName, canary.Namespace, err)
|
|
}
|
|
gr.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).
|
|
Infof("UpstreamGroup %s.%s created", upstreamGroup.GetName(), canary.Namespace)
|
|
return nil
|
|
}
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("UpstreamGroup %s.%s query error %v", apexName, canary.Namespace, err)
|
|
}
|
|
|
|
// update upstreamGroup but keep the original destination weights
|
|
if upstreamGroup != nil {
|
|
if diff := cmp.Diff(
|
|
newSpec,
|
|
upstreamGroup.Spec,
|
|
cmpopts.IgnoreFields(gloov1.WeightedDestination{}, "Weight"),
|
|
); diff != "" {
|
|
clone := upstreamGroup.DeepCopy()
|
|
clone.Spec = newSpec
|
|
|
|
_, err = gr.glooClient.GlooV1().UpstreamGroups(canary.Namespace).Update(clone)
|
|
if err != nil {
|
|
return fmt.Errorf("UpstreamGroup %s.%s update error %v", apexName, canary.Namespace, err)
|
|
}
|
|
gr.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).
|
|
Infof("UpstreamGroup %s.%s updated", upstreamGroup.GetName(), canary.Namespace)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetRoutes returns the destinations weight for primary and canary
|
|
func (gr *GlooRouter) GetRoutes(canary *flaggerv1.Canary) (
|
|
primaryWeight int,
|
|
canaryWeight int,
|
|
mirrored bool,
|
|
err error,
|
|
) {
|
|
apexName := canary.Spec.TargetRef.Name
|
|
primaryName := fmt.Sprintf("%s-%s-primary-%v", canary.Namespace, canary.Spec.TargetRef.Name, canary.Spec.Service.Port)
|
|
|
|
upstreamGroup, err := gr.glooClient.GlooV1().UpstreamGroups(canary.Namespace).Get(apexName, metav1.GetOptions{})
|
|
if err != nil {
|
|
if errors.IsNotFound(err) {
|
|
err = fmt.Errorf("UpstreamGroup %s.%s not found", apexName, canary.Namespace)
|
|
return
|
|
}
|
|
err = fmt.Errorf("UpstreamGroup %s.%s query error %v", apexName, canary.Namespace, err)
|
|
return
|
|
}
|
|
|
|
if len(upstreamGroup.Spec.Destinations) < 2 {
|
|
err = fmt.Errorf("UpstreamGroup %s.%s destinations not found", apexName, canary.Namespace)
|
|
return
|
|
}
|
|
|
|
for _, dst := range upstreamGroup.Spec.Destinations {
|
|
if dst.Destination.Upstream.Name == primaryName {
|
|
primaryWeight = int(dst.Weight)
|
|
canaryWeight = 100 - primaryWeight
|
|
return
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// SetRoutes updates the destinations weight for primary and canary
|
|
func (gr *GlooRouter) SetRoutes(
|
|
canary *flaggerv1.Canary,
|
|
primaryWeight int,
|
|
canaryWeight int,
|
|
mirrored bool,
|
|
) error {
|
|
apexName, _, _ := canary.GetServiceNames()
|
|
canaryName := fmt.Sprintf("%s-%s-canary-%v", canary.Namespace, apexName, canary.Spec.Service.Port)
|
|
primaryName := fmt.Sprintf("%s-%s-primary-%v", canary.Namespace, apexName, canary.Spec.Service.Port)
|
|
|
|
if primaryWeight == 0 && canaryWeight == 0 {
|
|
return fmt.Errorf("RoutingRule %s.%s update failed: no valid weights", apexName, canary.Namespace)
|
|
}
|
|
|
|
upstreamGroup, err := gr.glooClient.GlooV1().UpstreamGroups(canary.Namespace).Get(apexName, metav1.GetOptions{})
|
|
if err != nil {
|
|
if errors.IsNotFound(err) {
|
|
return fmt.Errorf("UpstreamGroup %s.%s not found", apexName, canary.Namespace)
|
|
|
|
}
|
|
return fmt.Errorf("UpstreamGroup %s.%s query error %v", apexName, canary.Namespace, err)
|
|
}
|
|
|
|
upstreamGroup.Spec = gloov1.UpstreamGroupSpec{
|
|
Destinations: []gloov1.WeightedDestination{
|
|
{
|
|
Destination: gloov1.Destination{
|
|
Upstream: gloov1.ResourceRef{
|
|
Name: primaryName,
|
|
Namespace: gr.upstreamDiscoveryNs,
|
|
},
|
|
},
|
|
Weight: uint32(primaryWeight),
|
|
},
|
|
{
|
|
Destination: gloov1.Destination{
|
|
Upstream: gloov1.ResourceRef{
|
|
Name: canaryName,
|
|
Namespace: gr.upstreamDiscoveryNs,
|
|
},
|
|
},
|
|
Weight: uint32(canaryWeight),
|
|
},
|
|
},
|
|
}
|
|
|
|
_, err = gr.glooClient.GlooV1().UpstreamGroups(canary.Namespace).Update(upstreamGroup)
|
|
if err != nil {
|
|
return fmt.Errorf("UpstreamGroup %s.%s update error %v", apexName, canary.Namespace, err)
|
|
}
|
|
return nil
|
|
}
|