提交 676062d7 编写于 作者: Z zackzhangkai

support virtualservice multi port

Signed-off-by: Nzackzhangkai <zhangkaiamm@gmail.com>
上级 6f719e0a
...@@ -118,6 +118,7 @@ func FillDestinationPort(vs *v1alpha3.VirtualService, service *corev1.Service) { ...@@ -118,6 +118,7 @@ func FillDestinationPort(vs *v1alpha3.VirtualService, service *corev1.Service) {
vs.Spec.Http[i].Route[j].Destination.Port = &apiv1alpha3.PortSelector{ vs.Spec.Http[i].Route[j].Destination.Port = &apiv1alpha3.PortSelector{
Number: uint32(service.Spec.Ports[0].Port), Number: uint32(service.Spec.Ports[0].Port),
} }
vs.Spec.Http[i].Match[j].Port = uint32(service.Spec.Ports[0].Port)
} }
} }
...@@ -135,7 +136,18 @@ func FillDestinationPort(vs *v1alpha3.VirtualService, service *corev1.Service) { ...@@ -135,7 +136,18 @@ func FillDestinationPort(vs *v1alpha3.VirtualService, service *corev1.Service) {
vs.Spec.Tcp[i].Route[j].Destination.Port = &apiv1alpha3.PortSelector{ vs.Spec.Tcp[i].Route[j].Destination.Port = &apiv1alpha3.PortSelector{
Number: uint32(service.Spec.Ports[0].Port), Number: uint32(service.Spec.Ports[0].Port),
} }
vs.Spec.Tcp[i].Match[j].Port = uint32(service.Spec.Ports[0].Port)
} }
} }
} }
} }
func SupportHttpProtocol(protocol string) bool {
httpPro := []string{"http", "http2", "grpc"}
for _, i := range httpPro {
if strings.ToLower(protocol) == i || strings.HasPrefix(strings.ToLower(protocol), i+"-") {
return true
}
}
return false
}
package servicemesh
import "testing"
func TestIsHTTP(t *testing.T) {
if !SupportHttpProtocol("gRPC") {
t.Errorf("gRPC is HTTP protocol")
}
if !SupportHttpProtocol("HTTP") {
t.Errorf("HTTP is HTTP protocol")
}
if !SupportHttpProtocol("HTTP2") {
t.Errorf("HTTP2 is HTTP protocol")
}
if SupportHttpProtocol("Mysql") {
t.Errorf("mysql is not HTTP protocol")
}
if SupportHttpProtocol("udp") {
t.Errorf("UDP is not HTTP protocol")
}
}
...@@ -19,10 +19,6 @@ package virtualservice ...@@ -19,10 +19,6 @@ package virtualservice
import ( import (
"context" "context"
"fmt" "fmt"
"kubesphere.io/kubesphere/pkg/controller/utils/servicemesh"
"reflect"
"strings"
apinetworkingv1alpha3 "istio.io/api/networking/v1alpha3" apinetworkingv1alpha3 "istio.io/api/networking/v1alpha3"
clientgonetworkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3" clientgonetworkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3"
istioclient "istio.io/client-go/pkg/clientset/versioned" istioclient "istio.io/client-go/pkg/clientset/versioned"
...@@ -49,6 +45,8 @@ import ( ...@@ -49,6 +45,8 @@ import (
servicemeshclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned" servicemeshclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
servicemeshinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/servicemesh/v1alpha2" servicemeshinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions/servicemesh/v1alpha2"
servicemeshlisters "kubesphere.io/kubesphere/pkg/client/listers/servicemesh/v1alpha2" servicemeshlisters "kubesphere.io/kubesphere/pkg/client/listers/servicemesh/v1alpha2"
"kubesphere.io/kubesphere/pkg/controller/utils/servicemesh"
"reflect"
"time" "time"
) )
...@@ -320,9 +318,13 @@ func (v *VirtualServiceController) syncService(key string) error { ...@@ -320,9 +318,13 @@ func (v *VirtualServiceController) syncService(key string) error {
// TODO(jeff): use FQDN to replace service name // TODO(jeff): use FQDN to replace service name
vs.Spec.Hosts = []string{name} vs.Spec.Hosts = []string{name}
vs.Spec.Http = []*apinetworkingv1alpha3.HTTPRoute{}
vs.Spec.Tcp = []*apinetworkingv1alpha3.TCPRoute{}
// check if service has TCP protocol ports // check if service has TCP protocol ports
for _, port := range service.Spec.Ports { for _, port := range service.Spec.Ports {
var route apinetworkingv1alpha3.HTTPRouteDestination var route apinetworkingv1alpha3.HTTPRouteDestination
var match apinetworkingv1alpha3.HTTPMatchRequest
if port.Protocol == v1.ProtocolTCP { if port.Protocol == v1.ProtocolTCP {
route = apinetworkingv1alpha3.HTTPRouteDestination{ route = apinetworkingv1alpha3.HTTPRouteDestination{
Destination: &apinetworkingv1alpha3.Destination{ Destination: &apinetworkingv1alpha3.Destination{
...@@ -335,12 +337,17 @@ func (v *VirtualServiceController) syncService(key string) error { ...@@ -335,12 +337,17 @@ func (v *VirtualServiceController) syncService(key string) error {
Weight: 100, Weight: 100,
} }
match = apinetworkingv1alpha3.HTTPMatchRequest{Port: uint32(port.Port)}
// a http port, add to HTTPRoute // a http port, add to HTTPRoute
if len(port.Name) > 0 && (port.Name == "http" || strings.HasPrefix(port.Name, "http-")) {
vs.Spec.Http = []*apinetworkingv1alpha3.HTTPRoute{{Route: []*apinetworkingv1alpha3.HTTPRouteDestination{&route}}}
break
}
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 // everything else treated as TCPRoute
tcpRoute := apinetworkingv1alpha3.TCPRoute{ tcpRoute := apinetworkingv1alpha3.TCPRoute{
Route: []*apinetworkingv1alpha3.RouteDestination{ Route: []*apinetworkingv1alpha3.RouteDestination{
...@@ -349,8 +356,10 @@ func (v *VirtualServiceController) syncService(key string) error { ...@@ -349,8 +356,10 @@ func (v *VirtualServiceController) syncService(key string) error {
Weight: route.Weight, Weight: route.Weight,
}, },
}, },
Match: []*apinetworkingv1alpha3.L4MatchAttributes{{Port: match.Port}},
}
vs.Spec.Tcp = append(vs.Spec.Tcp, &tcpRoute)
} }
vs.Spec.Tcp = []*apinetworkingv1alpha3.TCPRoute{&tcpRoute}
} }
} }
...@@ -385,7 +394,6 @@ func (v *VirtualServiceController) syncService(key string) error { ...@@ -385,7 +394,6 @@ func (v *VirtualServiceController) syncService(key string) error {
default: default:
vs.Spec = v.generateVirtualServiceSpec(strategies[0], service).Spec vs.Spec = v.generateVirtualServiceSpec(strategies[0], service).Spec
} }
} }
createVirtualService := len(currentVirtualService.ResourceVersion) == 0 createVirtualService := len(currentVirtualService.ResourceVersion) == 0
...@@ -542,8 +550,40 @@ func (v *VirtualServiceController) getSubsets(strategy *servicemeshv1alpha2.Stra ...@@ -542,8 +550,40 @@ func (v *VirtualServiceController) getSubsets(strategy *servicemeshv1alpha2.Stra
func (v *VirtualServiceController) generateVirtualServiceSpec(strategy *servicemeshv1alpha2.Strategy, service *v1.Service) *clientgonetworkingv1alpha3.VirtualService { func (v *VirtualServiceController) generateVirtualServiceSpec(strategy *servicemeshv1alpha2.Strategy, service *v1.Service) *clientgonetworkingv1alpha3.VirtualService {
// Define VirtualService to be created // Define VirtualService to be created
vs := &clientgonetworkingv1alpha3.VirtualService{ vs := &clientgonetworkingv1alpha3.VirtualService{}
Spec: strategy.Spec.Template.Spec, vs.Spec.Hosts = strategy.Spec.Template.Spec.Hosts
// For multi-ports, apply the rules to each port matched http/tcp protocol
for _, port := range service.Spec.Ports {
s := strategy.DeepCopy()
strategyTempSpec := s.Spec.Template.Spec
// fill route.destination.port and match.port filed
if len(strategyTempSpec.Http) > 0 && servicemesh.SupportHttpProtocol(port.Name) {
for _, http := range strategyTempSpec.Http {
if len(http.Match) == 0 {
http.Match = []*apinetworkingv1alpha3.HTTPMatchRequest{{Port: uint32(port.Port)}}
} else {
for _, match := range http.Match {
match.Port = uint32(port.Port)
}
}
for _, route := range http.Route {
route.Destination.Port = &apinetworkingv1alpha3.PortSelector{
Number: uint32(port.Port),
}
}
}
vs.Spec.Http = append(vs.Spec.Http, strategyTempSpec.Http...)
}
if len(strategyTempSpec.Tcp) > 0 && !servicemesh.SupportHttpProtocol(port.Name) {
for _, tcp := range strategyTempSpec.Tcp {
tcp.Match = []*apinetworkingv1alpha3.L4MatchAttributes{{Port: uint32(port.Port)}}
for _, r := range tcp.Route {
r.Destination.Port = &apinetworkingv1alpha3.PortSelector{Number: uint32(port.Port)}
}
}
vs.Spec.Tcp = append(vs.Spec.Tcp, strategyTempSpec.Tcp...)
}
} }
// one version rules them all // one version rules them all
...@@ -556,13 +596,17 @@ func (v *VirtualServiceController) generateVirtualServiceSpec(strategy *servicem ...@@ -556,13 +596,17 @@ func (v *VirtualServiceController) generateVirtualServiceSpec(strategy *servicem
Weight: 100, Weight: 100,
} }
for _, port := range service.Spec.Ports {
match := apinetworkingv1alpha3.HTTPMatchRequest{Port: uint32(port.Port)}
if len(strategy.Spec.Template.Spec.Http) > 0 { if len(strategy.Spec.Template.Spec.Http) > 0 {
governorRoute := apinetworkingv1alpha3.HTTPRoute{ governorRoute := apinetworkingv1alpha3.HTTPRoute{
Route: []*apinetworkingv1alpha3.HTTPRouteDestination{&governorDestinationWeight}, Route: []*apinetworkingv1alpha3.HTTPRouteDestination{&governorDestinationWeight},
Match: []*apinetworkingv1alpha3.HTTPMatchRequest{&match},
} }
vs.Spec.Http = []*apinetworkingv1alpha3.HTTPRoute{&governorRoute} vs.Spec.Http = []*apinetworkingv1alpha3.HTTPRoute{&governorRoute}
} else if len(strategy.Spec.Template.Spec.Tcp) > 0 {
}
if len(strategy.Spec.Template.Spec.Tcp) > 0 {
tcpRoute := apinetworkingv1alpha3.TCPRoute{ tcpRoute := apinetworkingv1alpha3.TCPRoute{
Route: []*apinetworkingv1alpha3.RouteDestination{ Route: []*apinetworkingv1alpha3.RouteDestination{
{ {
...@@ -573,12 +617,13 @@ func (v *VirtualServiceController) generateVirtualServiceSpec(strategy *servicem ...@@ -573,12 +617,13 @@ func (v *VirtualServiceController) generateVirtualServiceSpec(strategy *servicem
Weight: governorDestinationWeight.Weight, Weight: governorDestinationWeight.Weight,
}, },
}, },
Match: []*apinetworkingv1alpha3.L4MatchAttributes{{Port: match.Port}},
} }
//governorRoute := v1alpha3.TCPRoute{tcpRoute} //governorRoute := v1alpha3.TCPRoute{tcpRoute}
vs.Spec.Tcp = []*apinetworkingv1alpha3.TCPRoute{&tcpRoute} vs.Spec.Tcp = []*apinetworkingv1alpha3.TCPRoute{&tcpRoute}
} }
}
} }
servicemesh.FillDestinationPort(vs, service) servicemesh.FillDestinationPort(vs, service)
......
...@@ -45,6 +45,9 @@ var ( ...@@ -45,6 +45,9 @@ var (
applicationName = "bookinfo" applicationName = "bookinfo"
namespace = metav1.NamespaceDefault namespace = metav1.NamespaceDefault
subsets = []string{"v1", "v2"} subsets = []string{"v1", "v2"}
httpPort = 80
grpcPort = 81
mysqlPort = 82
) )
type fixture struct { type fixture struct {
...@@ -123,7 +126,7 @@ func newVirtualService(name string, host string, labels map[string]string) *v1al ...@@ -123,7 +126,7 @@ func newVirtualService(name string, host string, labels map[string]string) *v1al
return &vr return &vr
} }
func newService(name string, labels map[string]string, selector map[string]string, protocol string, port int) *v1.Service { func newService(name string, labels map[string]string, selector map[string]string) *v1.Service {
svc := v1.Service{ svc := v1.Service{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
...@@ -134,9 +137,21 @@ func newService(name string, labels map[string]string, selector map[string]strin ...@@ -134,9 +137,21 @@ func newService(name string, labels map[string]string, selector map[string]strin
Ports: []v1.ServicePort{ Ports: []v1.ServicePort{
{ {
Protocol: v1.ProtocolTCP, Protocol: v1.ProtocolTCP,
Port: int32(port), Port: int32(httpPort),
Name: fmt.Sprintf("%s-aaa", protocol), Name: "HTTP-1",
TargetPort: intstr.FromInt(port), TargetPort: intstr.FromInt(httpPort),
},
{
Protocol: v1.ProtocolTCP,
Port: int32(grpcPort),
Name: "grpc-1",
TargetPort: intstr.FromInt(grpcPort),
},
{
Protocol: v1.ProtocolTCP,
Port: int32(mysqlPort),
Name: "mysql-1",
TargetPort: intstr.FromInt(mysqlPort),
}, },
}, },
Selector: selector, Selector: selector,
...@@ -299,7 +314,7 @@ func (f *fixture) run_(serviceKey string, expectedVS *v1alpha3.VirtualService, s ...@@ -299,7 +314,7 @@ func (f *fixture) run_(serviceKey string, expectedVS *v1alpha3.VirtualService, s
func TestInitialStrategyCreate(t *testing.T) { func TestInitialStrategyCreate(t *testing.T) {
f := newFixture(t) f := newFixture(t)
svc := newService("foo", NewLabels().WithApplication(applicationName).WithApp(serviceName), NewLabels().WithApplication(serviceName).WithApp(applicationName), "http", 80) svc := newService("foo", NewLabels().WithApplication(applicationName).WithApp(serviceName), NewLabels().WithApplication(serviceName).WithApp(applicationName))
dr := newDestinationRule(svc.Name, toHost(svc), NewLabels().WithApp("foo").WithApplication(applicationName), subsets[0]) dr := newDestinationRule(svc.Name, toHost(svc), NewLabels().WithApp("foo").WithApplication(applicationName), subsets[0])
svc.Annotations = NewLabels().WithServiceMeshEnabled(true) svc.Annotations = NewLabels().WithServiceMeshEnabled(true)
...@@ -310,22 +325,47 @@ func TestInitialStrategyCreate(t *testing.T) { ...@@ -310,22 +325,47 @@ func TestInitialStrategyCreate(t *testing.T) {
vs := newVirtualService(svc.Name, "foo", NewLabels().WithApplication("bookinfo").WithApp(svc.Name)) vs := newVirtualService(svc.Name, "foo", NewLabels().WithApplication("bookinfo").WithApp(svc.Name))
vs.Annotations = make(map[string]string) vs.Annotations = make(map[string]string)
vs.Spec.Http = []*apiv1alpha3.HTTPRoute{ for _, port := range svc.Spec.Ports {
{ if servicemesh.SupportHttpProtocol(port.Name) {
httpRoute := apiv1alpha3.HTTPRoute{
Route: []*apiv1alpha3.HTTPRouteDestination{ Route: []*apiv1alpha3.HTTPRouteDestination{
{ {
Destination: &apiv1alpha3.Destination{ Destination: &apiv1alpha3.Destination{
Host: svc.Name, Host: svc.Name,
Subset: "v1", Subset: "v1",
Port: &apiv1alpha3.PortSelector{ Port: &apiv1alpha3.PortSelector{
Number: uint32(svc.Spec.Ports[0].Port), Number: uint32(port.Port),
},
},
Weight: 100,
},
},
Match: []*apiv1alpha3.HTTPMatchRequest{
{Port: uint32(port.Port)},
},
}
vs.Spec.Http = append(vs.Spec.Http, &httpRoute)
} else {
tcpRoute := apiv1alpha3.TCPRoute{
Route: []*apiv1alpha3.RouteDestination{
{
Destination: &apiv1alpha3.Destination{
Host: svc.Name,
Subset: "v1",
Port: &apiv1alpha3.PortSelector{
Number: uint32(port.Port),
}, },
}, },
Weight: 100, Weight: 100,
}, },
}, },
Match: []*apiv1alpha3.L4MatchAttributes{
{Port: uint32(port.Port)},
}, },
} }
vs.Spec.Tcp = append(vs.Spec.Tcp, &tcpRoute)
}
}
key, err := cache.MetaNamespaceKeyFunc(svc) key, err := cache.MetaNamespaceKeyFunc(svc)
if err != nil { if err != nil {
...@@ -354,7 +394,7 @@ func runStrategy(t *testing.T, svc *v1.Service, dr *v1alpha3.DestinationRule, st ...@@ -354,7 +394,7 @@ func runStrategy(t *testing.T, svc *v1.Service, dr *v1alpha3.DestinationRule, st
func TestStrategies(t *testing.T) { func TestStrategies(t *testing.T) {
svc := newService(serviceName, NewLabels().WithApplication(applicationName).WithApp(serviceName), NewLabels().WithApplication(applicationName).WithApp(serviceName), "http", 80) svc := newService(serviceName, NewLabels().WithApplication(applicationName).WithApp(serviceName), NewLabels().WithApplication(applicationName).WithApp(serviceName))
defaultDr := newDestinationRule(svc.Name, toHost(svc), NewLabels().WithApp(serviceName).WithApplication(applicationName), subsets...) defaultDr := newDestinationRule(svc.Name, toHost(svc), NewLabels().WithApp(serviceName).WithApplication(applicationName), subsets...)
svc.Annotations = NewLabels().WithServiceMeshEnabled(true) svc.Annotations = NewLabels().WithServiceMeshEnabled(true)
defaultStrategy := &v1alpha2.Strategy{ defaultStrategy := &v1alpha2.Strategy{
...@@ -395,6 +435,9 @@ func TestStrategies(t *testing.T) { ...@@ -395,6 +435,9 @@ func TestStrategies(t *testing.T) {
Weight: 20, Weight: 20,
}, },
}, },
Match: []*apiv1alpha3.HTTPMatchRequest{
{Port: 0},
},
}, },
}, },
}, },
...@@ -412,7 +455,7 @@ func TestStrategies(t *testing.T) { ...@@ -412,7 +455,7 @@ func TestStrategies(t *testing.T) {
Host: svc.Name, Host: svc.Name,
Subset: "v1", Subset: "v1",
Port: &apiv1alpha3.PortSelector{ Port: &apiv1alpha3.PortSelector{
Number: uint32(svc.Spec.Ports[0].Port), Number: uint32(httpPort),
}, },
}, },
Weight: 80, Weight: 80,
...@@ -422,12 +465,38 @@ func TestStrategies(t *testing.T) { ...@@ -422,12 +465,38 @@ func TestStrategies(t *testing.T) {
Host: svc.Name, Host: svc.Name,
Subset: "v2", Subset: "v2",
Port: &apiv1alpha3.PortSelector{ Port: &apiv1alpha3.PortSelector{
Number: uint32(svc.Spec.Ports[0].Port), Number: uint32(httpPort),
}, },
}, },
Weight: 20, Weight: 20,
}, },
}, },
Match: []*apiv1alpha3.HTTPMatchRequest{{Port: uint32(httpPort)}},
},
{
Route: []*apiv1alpha3.HTTPRouteDestination{
{
Destination: &apiv1alpha3.Destination{
Host: svc.Name,
Subset: "v1",
Port: &apiv1alpha3.PortSelector{
Number: uint32(grpcPort),
},
},
Weight: 80,
},
{
Destination: &apiv1alpha3.Destination{
Host: svc.Name,
Subset: "v2",
Port: &apiv1alpha3.PortSelector{
Number: uint32(grpcPort),
},
},
Weight: 20,
},
},
Match: []*apiv1alpha3.HTTPMatchRequest{{Port: uint32(grpcPort)}},
}, },
} }
...@@ -443,6 +512,8 @@ func TestStrategies(t *testing.T) { ...@@ -443,6 +512,8 @@ func TestStrategies(t *testing.T) {
expected := defaultExpected.DeepCopy() expected := defaultExpected.DeepCopy()
expected.Spec.Http[0].Route[0].Weight = 0 expected.Spec.Http[0].Route[0].Weight = 0
expected.Spec.Http[0].Route[1].Weight = 100 expected.Spec.Http[0].Route[1].Weight = 100
expected.Spec.Http[1].Route[0].Weight = 0
expected.Spec.Http[1].Route[1].Weight = 100
runStrategy(t, svc, defaultDr, strategy, expected) runStrategy(t, svc, defaultDr, strategy, expected)
}) })
...@@ -454,6 +525,7 @@ func TestStrategies(t *testing.T) { ...@@ -454,6 +525,7 @@ func TestStrategies(t *testing.T) {
expected.Spec.Http[0].Route[0].Weight = 100 expected.Spec.Http[0].Route[0].Weight = 100
expected.Spec.Http[0].Route[0].Destination.Subset = "v2" expected.Spec.Http[0].Route[0].Destination.Subset = "v2"
expected.Spec.Http[0].Route = expected.Spec.Http[0].Route[:1] expected.Spec.Http[0].Route = expected.Spec.Http[0].Route[:1]
expected.Spec.Http = expected.Spec.Http[:1]
runStrategy(t, svc, defaultDr, strategy, expected) runStrategy(t, svc, defaultDr, strategy, expected)
}) })
...@@ -483,6 +555,20 @@ func TestStrategies(t *testing.T) { ...@@ -483,6 +555,20 @@ func TestStrategies(t *testing.T) {
Uri: &apiv1alpha3.StringMatch{ Uri: &apiv1alpha3.StringMatch{
MatchType: &apiv1alpha3.StringMatch_Prefix{Prefix: "/apis"}, MatchType: &apiv1alpha3.StringMatch_Prefix{Prefix: "/apis"},
}, },
Port: expected.Spec.Http[0].Route[0].Destination.Port.Number,
},
}
expected.Spec.Http[1].Match = []*apiv1alpha3.HTTPMatchRequest{
{
Headers: map[string]*apiv1alpha3.StringMatch{
"X-USER": {
MatchType: &apiv1alpha3.StringMatch_Regex{Regex: "users"},
},
},
Uri: &apiv1alpha3.StringMatch{
MatchType: &apiv1alpha3.StringMatch_Prefix{Prefix: "/apis"},
},
Port: expected.Spec.Http[1].Route[0].Destination.Port.Number,
}, },
} }
runStrategy(t, svc, defaultDr, strategy, expected) runStrategy(t, svc, defaultDr, strategy, expected)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册