Merge pull request #4581 from RolandMa1986/fix-istio

fix: the configuration of the Istio virtualservice is overwritten
This commit is contained in:
KubeSphere CI Bot
2022-01-05 15:38:31 +08:00
committed by GitHub
2 changed files with 201 additions and 51 deletions

View File

@@ -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
}

View File

@@ -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)
}
})
}
}