Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support canary services in multiple routes of the same proxy #76

Merged
merged 1 commit into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 48 additions & 34 deletions pkg/plugin/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,14 +177,16 @@ func createPatch(httpProxy *contourv1.HTTPProxy, rollout *v1alpha1.Rollout, cana
return nil, "", fmt.Errorf("failed to marshal the current configuration: %w", err)
}

canarySvc, stableSvc, totalWeight, err := getRouteServices(httpProxy, rollout)
canarySvcs, stableSvcs, totalWeights, err := getRouteServices(httpProxy, rollout)
if err != nil {
return nil, types.MergePatchType, err
}
slog.Debug("old weight", slog.Int64("canary", canarySvc.Weight), slog.Int64("stable", stableSvc.Weight))

canarySvc.Weight, stableSvc.Weight = utils.CalcWeight(totalWeight, float32(canaryWeightPercent))
slog.Debug("new weight", slog.Int64("canary", canarySvc.Weight), slog.Int64("stable", stableSvc.Weight))
for i := range canarySvcs {
slog.Debug("old weight", slog.Int64("canary", canarySvcs[i].Weight), slog.Int64("stable", stableSvcs[i].Weight))
canarySvcs[i].Weight, stableSvcs[i].Weight = utils.CalcWeight(totalWeights[i], float32(canaryWeightPercent))
slog.Debug("new weight", slog.Int64("canary", canarySvcs[i].Weight), slog.Int64("stable", stableSvcs[i].Weight))
}

newData, err := json.Marshal(httpProxy)
if err != nil {
Expand Down Expand Up @@ -221,53 +223,64 @@ func (r *RpcPlugin) verifyHTTPProxy(
return false, nil
}

canarySvc, stableSvc, totalWeight, err := getRouteServices(httpProxy, rollout)
canarySvcs, stableSvcs, totalWeights, err := getRouteServices(httpProxy, rollout)
if err != nil {
return false, err
}

canaryWeight, stableWeight := utils.CalcWeight(totalWeight, float32(canaryWeightPercent))
if canarySvc.Weight != canaryWeight || stableSvc.Weight != stableWeight {
slog.Debug(fmt.Sprintf("expected weights are canary=%d and stable=%d, but got canary=%d and stable=%d", canaryWeight, stableWeight, canarySvc.Weight, stableSvc.Weight), slog.String("name", httpProxyName))
return false, nil
for i := range canarySvcs {
canaryWeight, stableWeight := utils.CalcWeight(totalWeights[i], float32(canaryWeightPercent))
if canarySvcs[i].Weight != canaryWeight || stableSvcs[i].Weight != stableWeight {
slog.Debug(fmt.Sprintf("expected weights are canary=%d and stable=%d, but got canary=%d and stable=%d", canaryWeight, stableWeight, canarySvcs[i].Weight, stableSvcs[i].Weight), slog.String("name", httpProxyName))
return false, nil
}
}

return true, nil
}

func getRouteServices(httpProxy *contourv1.HTTPProxy, rollout *v1alpha1.Rollout) (
*contourv1.Service, *contourv1.Service, int64, error) {
[]*contourv1.Service, []*contourv1.Service, []int64, error) {
canarySvcName := rollout.Spec.Strategy.Canary.CanaryService
stableSvcName := rollout.Spec.Strategy.Canary.StableService

slog.Debug("the services name", slog.String("stable", stableSvcName), slog.String("canary", canarySvcName))

svcMap := getServiceMap(httpProxy, canarySvcName)
svcMaps := getRouteServiceMaps(httpProxy, canarySvcName)
canarySvcs := []*contourv1.Service{}
stableSvcs := []*contourv1.Service{}
totalWeights := []int64{}

canarySvc, err := getService(canarySvcName, svcMap)
if err != nil {
return nil, nil, 0, err
}
for _, svcMap := range svcMaps {
canarySvc, err := getService(canarySvcName, svcMap)
if err != nil {
return nil, nil, nil, err
}

stableSvc, err := getService(stableSvcName, svcMap)
if err != nil {
return nil, nil, 0, err
}
stableSvc, err := getService(stableSvcName, svcMap)
if err != nil {
return nil, nil, nil, err
}

otherWeight := int64(0)
for name, svc := range svcMap {
if name == stableSvcName || name == canarySvcName {
continue
otherWeight := int64(0)
for name, svc := range svcMap {
if name == stableSvcName || name == canarySvcName {
continue
}
otherWeight += svc.Weight
}
otherWeight += svc.Weight
}

// the total weight must equals to 100
if otherWeight+canarySvc.Weight+stableSvc.Weight != 100 {
return nil, nil, 0, fmt.Errorf("the total weight must equals to 100")
// the total weight must equals to 100
if otherWeight+canarySvc.Weight+stableSvc.Weight != 100 {
return nil, nil, nil, fmt.Errorf("the total weight must equals to 100")
}

canarySvcs = append(canarySvcs, canarySvc)
stableSvcs = append(stableSvcs, stableSvc)
totalWeights = append(totalWeights, 100-otherWeight)
}

return canarySvc, stableSvc, 100 - otherWeight, nil
return canarySvcs, stableSvcs, totalWeights, nil
}

func getContourTrafficRouting(rollout *v1alpha1.Rollout) (*ContourTrafficRouting, error) {
Expand All @@ -286,9 +299,8 @@ func getService(name string, svcMap map[string]*contourv1.Service) (*contourv1.S
return svc, nil
}

func getServiceMap(httpProxy *contourv1.HTTPProxy, canarySvcName string) map[string]*contourv1.Service {
svcMap := make(map[string]*contourv1.Service)

func getRouteServiceMaps(httpProxy *contourv1.HTTPProxy, canarySvcName string) []map[string]*contourv1.Service {
svcMaps := []map[string]*contourv1.Service{}
// filter the services by canary service name
filter := func(services []contourv1.Service) bool {
for _, svc := range services {
Expand All @@ -298,17 +310,19 @@ func getServiceMap(httpProxy *contourv1.HTTPProxy, canarySvcName string) map[str
}
return false
}
// TODO: same service in multi conditions

for _, r := range httpProxy.Spec.Routes {
svcMap := make(map[string]*contourv1.Service)
if filter(r.Services) {
svcMaps = append(svcMaps, svcMap)
for i := range r.Services {
s := &r.Services[i]
svcMap[s.Name] = s
}
}

}
return svcMap
return svcMaps
}

func validateRolloutParameters(rollout *v1alpha1.Rollout) error {
Expand Down
62 changes: 61 additions & 1 deletion pkg/plugin/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,66 @@ func Test_createPatch(t *testing.T) {
wantPatchType: k8stypes.MergePatchType,
wantErr: false,
},
{
name: "test create http proxy patch",
args: args{
httpProxy: &contourv1.HTTPProxy{
ObjectMeta: metav1.ObjectMeta{
Name: mocks.HTTPProxyName,
},
Spec: contourv1.HTTPProxySpec{
Routes: []contourv1.Route{
{
Services: []contourv1.Service{
{
Name: mocks.StableServiceName,
Weight: 70,
},
{
Name: mocks.CanaryServiceName,
Weight: 20,
},
{
Name: "others-service",
Weight: 10,
},
},
},
{
Services: []contourv1.Service{
{
Name: mocks.StableServiceName,
Weight: 70,
},
{
Name: mocks.CanaryServiceName,
Weight: 10,
},
{
Name: "others-service",
Weight: 20,
},
},
},
},
},
},
rollout: &v1alpha1.Rollout{
Spec: v1alpha1.RolloutSpec{
Strategy: v1alpha1.RolloutStrategy{
Canary: &v1alpha1.CanaryStrategy{
StableService: mocks.StableServiceName,
CanaryService: mocks.CanaryServiceName,
},
},
},
},
desiredWeight: 50,
},
want: []byte(`{"spec":{"routes":[{"services":[{"name":"argo-rollouts-stable","port":0,"weight":45},{"name":"argo-rollouts-canary","port":0,"weight":45},{"name":"others-service","port":0,"weight":10}]},{"services":[{"name":"argo-rollouts-stable","port":0,"weight":40},{"name":"argo-rollouts-canary","port":0,"weight":40},{"name":"others-service","port":0,"weight":20}]}]}}`),
wantPatchType: k8stypes.MergePatchType,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -292,7 +352,7 @@ func Test_createPatch(t *testing.T) {
t.Errorf("createPatch() gotPatchType = %v, want %v", gotPatchType, tt.wantPatchType)
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("createPatch() got = %v, want %v", got, tt.want)
t.Errorf("createPatch() got = %s, want %s", got, tt.want)
}
})
}
Expand Down
Loading