From 5a4e4aa316ecd5f486f0551871414fc1aa70a526 Mon Sep 17 00:00:00 2001 From: "Roland.Ma" Date: Thu, 30 Dec 2021 06:26:11 +0000 Subject: [PATCH] =?UTF-8?q?fix=204287=EF=BC=9Athe=20configuration=20of=20t?= =?UTF-8?q?he=20Istio=20virtualservice=20is=20overwritten?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Roland.Ma --- .../virtualservice_controller.go | 125 ++++++++++------- .../virtualservice_controller_test.go | 127 +++++++++++++++++- 2 files changed, 201 insertions(+), 51 deletions(-) diff --git a/pkg/controller/virtualservice/virtualservice_controller.go b/pkg/controller/virtualservice/virtualservice_controller.go index 902959fec..6e0d8e229 100644 --- a/pkg/controller/virtualservice/virtualservice_controller.go +++ b/pkg/controller/virtualservice/virtualservice_controller.go @@ -316,61 +316,12 @@ func (v *VirtualServiceController) syncService(key string) error { } vs := currentVirtualService.DeepCopy() - // create a whole new virtualservice - - // TODO(jeff): use FQDN to replace service name - vs.Spec.Hosts = []string{name} - - vs.Spec.Http = []*apinetworkingv1alpha3.HTTPRoute{} - vs.Spec.Tcp = []*apinetworkingv1alpha3.TCPRoute{} - - // check if service has TCP protocol ports - for _, port := range service.Spec.Ports { - var route apinetworkingv1alpha3.HTTPRouteDestination - var match apinetworkingv1alpha3.HTTPMatchRequest - if port.Protocol == v1.ProtocolTCP { - route = apinetworkingv1alpha3.HTTPRouteDestination{ - Destination: &apinetworkingv1alpha3.Destination{ - Host: name, - Subset: subsets[0].Name, - Port: &apinetworkingv1alpha3.PortSelector{ - Number: uint32(port.Port), - }, - }, - Weight: 100, - } - - match = apinetworkingv1alpha3.HTTPMatchRequest{Port: uint32(port.Port)} - - // a http port, add to HTTPRoute - - if servicemesh.SupportHttpProtocol(port.Name) { - httpRoute := apinetworkingv1alpha3.HTTPRoute{ - Route: []*apinetworkingv1alpha3.HTTPRouteDestination{&route}, - Match: []*apinetworkingv1alpha3.HTTPMatchRequest{&match}, - } - vs.Spec.Http = append(vs.Spec.Http, &httpRoute) - } else { - // everything else treated as TCPRoute - tcpRoute := apinetworkingv1alpha3.TCPRoute{ - Route: []*apinetworkingv1alpha3.RouteDestination{ - { - Destination: route.Destination, - Weight: route.Weight, - }, - }, - Match: []*apinetworkingv1alpha3.L4MatchAttributes{{Port: match.Port}}, - } - vs.Spec.Tcp = append(vs.Spec.Tcp, &tcpRoute) - } - } - } - if len(strategies) > 0 { // apply strategy spec to virtualservice switch strategies[0].Spec.StrategyPolicy { case servicemeshv1alpha2.PolicyPause: + vs.Spec = v.generateDefaultVirtualServiceSpec(name, subsets, service).Spec break case servicemeshv1alpha2.PolicyWaitForWorkloadReady: set := v.getSubsets(strategies[0]) @@ -388,6 +339,7 @@ func (v *VirtualServiceController) syncService(key string) error { } // strategy has subset that are not ready if nonExist { + vs.Spec = v.generateDefaultVirtualServiceSpec(name, subsets, service).Spec break } else { vs.Spec = v.generateVirtualServiceSpec(strategies[0], service).Spec @@ -397,8 +349,12 @@ func (v *VirtualServiceController) syncService(key string) error { default: vs.Spec = v.generateVirtualServiceSpec(strategies[0], service).Spec } + } else { + vs.Spec = v.generateDefaultVirtualServiceSpec(name, subsets, service).Spec } + v.patchHTTPRoute(currentVirtualService.Spec.Http, vs.Spec.Http) + createVirtualService := len(currentVirtualService.ResourceVersion) == 0 if !createVirtualService && @@ -632,3 +588,72 @@ func (v *VirtualServiceController) generateVirtualServiceSpec(strategy *servicem servicemesh.FillDestinationPort(vs, service) return vs } + +// create a whole new virtualservice +func (v *VirtualServiceController) generateDefaultVirtualServiceSpec(name string, subsets []*apinetworkingv1alpha3.Subset, service *v1.Service) *clientgonetworkingv1alpha3.VirtualService { + vs := &clientgonetworkingv1alpha3.VirtualService{} + // TODO(jeff): use FQDN to replace service name + vs.Spec.Hosts = []string{name} + // check if service has TCP protocol ports + for _, port := range service.Spec.Ports { + var route apinetworkingv1alpha3.HTTPRouteDestination + var match apinetworkingv1alpha3.HTTPMatchRequest + if port.Protocol == v1.ProtocolTCP { + route = apinetworkingv1alpha3.HTTPRouteDestination{ + Destination: &apinetworkingv1alpha3.Destination{ + Host: name, + Subset: subsets[0].Name, + Port: &apinetworkingv1alpha3.PortSelector{ + Number: uint32(port.Port), + }, + }, + Weight: 100, + } + + match = apinetworkingv1alpha3.HTTPMatchRequest{Port: uint32(port.Port)} + + // a http port, add to HTTPRoute + + if servicemesh.SupportHttpProtocol(port.Name) { + httpRoute := apinetworkingv1alpha3.HTTPRoute{ + Name: port.Name, + Route: []*apinetworkingv1alpha3.HTTPRouteDestination{&route}, + Match: []*apinetworkingv1alpha3.HTTPMatchRequest{&match}, + } + vs.Spec.Http = append(vs.Spec.Http, &httpRoute) + } else { + // everything else treated as TCPRoute + tcpRoute := apinetworkingv1alpha3.TCPRoute{ + Route: []*apinetworkingv1alpha3.RouteDestination{ + { + Destination: route.Destination, + Weight: route.Weight, + }, + }, + Match: []*apinetworkingv1alpha3.L4MatchAttributes{{Port: match.Port}}, + } + vs.Spec.Tcp = append(vs.Spec.Tcp, &tcpRoute) + } + } + } + return vs +} + +// patchHTTPRoute copy all properties from origin to the target HTTPRoute except Match and Route +func (v *VirtualServiceController) patchHTTPRoute(origin, target []*apinetworkingv1alpha3.HTTPRoute) []*apinetworkingv1alpha3.HTTPRoute { + originMap := map[string]*apinetworkingv1alpha3.HTTPRoute{} + for _, o := range origin { + originMap[o.Name] = o + } + + for _, t := range target { + if o, ok := originMap[t.Name]; ok { + match := t.Match + route := t.Route + *t = *o + t.Match = match + t.Route = route + } + } + return target +} diff --git a/pkg/controller/virtualservice/virtualservice_controller_test.go b/pkg/controller/virtualservice/virtualservice_controller_test.go index cd4a459ad..d590d7d42 100644 --- a/pkg/controller/virtualservice/virtualservice_controller_test.go +++ b/pkg/controller/virtualservice/virtualservice_controller_test.go @@ -19,8 +19,10 @@ package virtualservice import ( "context" "fmt" + "reflect" "testing" + apinetworkingv1alpha3 "istio.io/api/networking/v1alpha3" apiv1alpha3 "istio.io/api/networking/v1alpha3" "istio.io/client-go/pkg/apis/networking/v1alpha3" istiofake "istio.io/client-go/pkg/clientset/versioned/fake" @@ -34,7 +36,6 @@ import ( kubefake "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/record" - "kubesphere.io/api/servicemesh/v1alpha2" "kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake" @@ -329,6 +330,7 @@ func TestInitialStrategyCreate(t *testing.T) { for _, port := range svc.Spec.Ports { if servicemesh.SupportHttpProtocol(port.Name) { httpRoute := apiv1alpha3.HTTPRoute{ + Name: port.Name, Route: []*apiv1alpha3.HTTPRouteDestination{ { Destination: &apiv1alpha3.Destination{ @@ -578,3 +580,126 @@ func TestStrategies(t *testing.T) { }) } + +func TestVirtualServiceController_patchHTTPRoute(t *testing.T) { + + target := []*apiv1alpha3.HTTPRoute{ + { + Name: "http-1", + Match: []*apiv1alpha3.HTTPMatchRequest{ + { + Port: uint32(80), + }, + }, + Route: []*apiv1alpha3.HTTPRouteDestination{ + { + Destination: &apiv1alpha3.Destination{ + Host: "service1", + Subset: "v1", + Port: &apinetworkingv1alpha3.PortSelector{ + Number: uint32(80), + }, + }, + }, + }, + }, + } + + type args struct { + origin []*apiv1alpha3.HTTPRoute + target []*apiv1alpha3.HTTPRoute + } + tests := []struct { + name string + + args args + want []*apiv1alpha3.HTTPRoute + }{ + { + name: "empty", + args: args{ + origin: []*apiv1alpha3.HTTPRoute{}, + target: target, + }, + want: []*apiv1alpha3.HTTPRoute{ + { + Name: "http-1", + Match: []*apiv1alpha3.HTTPMatchRequest{ + { + Port: uint32(80), + }, + }, + Route: []*apiv1alpha3.HTTPRouteDestination{ + { + Destination: &apiv1alpha3.Destination{ + Host: "service1", + Subset: "v1", + Port: &apinetworkingv1alpha3.PortSelector{ + Number: uint32(80), + }, + }, + }, + }, + }, + }, + }, + { + name: "override", + args: args{ + origin: []*apiv1alpha3.HTTPRoute{ + { + Name: "http-1", + Match: []*apiv1alpha3.HTTPMatchRequest{ + { + Port: uint32(80), + }, + { + Port: uint32(81), + }, + }, + Fault: &apiv1alpha3.HTTPFaultInjection{ + Delay: &apiv1alpha3.HTTPFaultInjection_Delay{ + Percent: 10, + }, + }, + }, + }, + target: target, + }, + want: []*apiv1alpha3.HTTPRoute{ + { + Name: "http-1", + Match: []*apiv1alpha3.HTTPMatchRequest{ + { + Port: uint32(80), + }, + }, + Route: []*apiv1alpha3.HTTPRouteDestination{ + { + Destination: &apiv1alpha3.Destination{ + Host: "service1", + Subset: "v1", + Port: &apinetworkingv1alpha3.PortSelector{ + Number: uint32(80), + }, + }, + }, + }, + Fault: &apiv1alpha3.HTTPFaultInjection{ + Delay: &apiv1alpha3.HTTPFaultInjection_Delay{ + Percent: 10, + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v := &VirtualServiceController{} + if got := v.patchHTTPRoute(tt.args.origin, tt.args.target); !reflect.DeepEqual(got, tt.want) { + t.Errorf("VirtualServiceController.patchHTTPRoute() = %v, want %v", got, tt.want) + } + }) + } +}