use istio client-go library instead of knative (#1661)
use istio client-go library instead of knative bump kubernetes dependency version change code coverage to codecov
This commit is contained in:
@@ -17,7 +17,7 @@ limitations under the License.
|
||||
package controller
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/application/pkg/controller/application"
|
||||
"github.com/kubernetes-sigs/application/pkg/controller/application"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -104,7 +104,7 @@ func (r *ReconcileClusterRoleBinding) Reconcile(request reconcile.Request) (reco
|
||||
nsList := &corev1.NamespaceList{}
|
||||
options := client.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{constants.WorkspaceLabelKey: workspaceName})}
|
||||
|
||||
if err := r.List(context.TODO(), &options, nsList); err != nil {
|
||||
if err := r.List(context.TODO(), nsList, &options); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
for _, ns := range nsList.Items {
|
||||
|
||||
@@ -4,7 +4,8 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/knative/pkg/apis/istio/v1alpha3"
|
||||
apinetworkingv1alpha3 "istio.io/api/networking/v1alpha3"
|
||||
clientgonetworkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -22,9 +23,9 @@ import (
|
||||
|
||||
"time"
|
||||
|
||||
istioclientset "github.com/knative/pkg/client/clientset/versioned"
|
||||
istioinformers "github.com/knative/pkg/client/informers/externalversions/istio/v1alpha3"
|
||||
istiolisters "github.com/knative/pkg/client/listers/istio/v1alpha3"
|
||||
istioclientset "istio.io/client-go/pkg/clientset/versioned"
|
||||
istioinformers "istio.io/client-go/pkg/informers/externalversions/networking/v1alpha3"
|
||||
istiolisters "istio.io/client-go/pkg/listers/networking/v1alpha3"
|
||||
informersv1 "k8s.io/client-go/informers/apps/v1"
|
||||
coreinformers "k8s.io/client-go/informers/core/v1"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
@@ -244,7 +245,7 @@ func (v *DestinationRuleController) syncService(key string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
subsets := make([]v1alpha3.Subset, 0)
|
||||
subsets := make([]*apinetworkingv1alpha3.Subset, 0)
|
||||
for _, deployment := range deployments {
|
||||
|
||||
// not a valid deployment we required
|
||||
@@ -262,7 +263,7 @@ func (v *DestinationRuleController) syncService(key string) error {
|
||||
continue
|
||||
}
|
||||
|
||||
subset := v1alpha3.Subset{
|
||||
subset := &apinetworkingv1alpha3.Subset{
|
||||
Name: util.NormalizeVersionName(version),
|
||||
Labels: map[string]string{
|
||||
util.VersionLabel: version,
|
||||
@@ -275,12 +276,12 @@ func (v *DestinationRuleController) syncService(key string) error {
|
||||
currentDestinationRule, err := v.destinationRuleLister.DestinationRules(namespace).Get(name)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
currentDestinationRule = &v1alpha3.DestinationRule{
|
||||
currentDestinationRule = &clientgonetworkingv1alpha3.DestinationRule{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: service.Name,
|
||||
Labels: service.Labels,
|
||||
},
|
||||
Spec: v1alpha3.DestinationRuleSpec{
|
||||
Spec: apinetworkingv1alpha3.DestinationRule{
|
||||
Host: name,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,58 +1,363 @@
|
||||
package destinationrule
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
apiv1alpha3 "istio.io/api/networking/v1alpha3"
|
||||
"istio.io/client-go/pkg/apis/networking/v1alpha3"
|
||||
istiofake "istio.io/client-go/pkg/clientset/versioned/fake"
|
||||
istioinformers "istio.io/client-go/pkg/informers/externalversions"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
kubeinformers "k8s.io/client-go/informers"
|
||||
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
|
||||
"kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake"
|
||||
informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
|
||||
"kubesphere.io/kubesphere/pkg/controller/virtualservice/util"
|
||||
"kubesphere.io/kubesphere/pkg/utils/reflectutils"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TODO(jeff): add test cases
|
||||
var (
|
||||
alwaysReady = func() bool { return true }
|
||||
replicas = int32(2)
|
||||
)
|
||||
|
||||
var namespace = "default"
|
||||
var lbs = map[string]string{
|
||||
"app.kubernetes.io/name": "bookinfo",
|
||||
"servicemesh.kubesphere.io/enabled": "",
|
||||
"app": "reviews",
|
||||
}
|
||||
func newDeployments(service *corev1.Service, version string) *appsv1.Deployment {
|
||||
lbs := service.Labels
|
||||
lbs["version"] = version
|
||||
|
||||
var service = corev1.Service{}
|
||||
|
||||
var deployments = []appsv1.Deployment{
|
||||
{
|
||||
deployment := &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: namespace,
|
||||
Name: "deploy-v1",
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/name": "bookinfo",
|
||||
"servicemesh.kubesphere.io/enabled": "",
|
||||
"app": "reviews",
|
||||
"version": "v1",
|
||||
},
|
||||
Name: fmt.Sprintf("%s-%s", service.Name, version),
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
Labels: lbs,
|
||||
Annotations: service.Annotations,
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Replicas: &replicas,
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"app.kubernetes.io/name": "bookinfo",
|
||||
"servicemesh.kubesphere.io/enabled": "",
|
||||
"app": "reviews",
|
||||
"version": "v1",
|
||||
},
|
||||
MatchLabels: lbs,
|
||||
},
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/name": "bookinfo",
|
||||
"servicemesh.kubesphere.io/enabled": "",
|
||||
"app": "reviews",
|
||||
"version": "v1",
|
||||
},
|
||||
Labels: lbs,
|
||||
Annotations: service.Annotations,
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{},
|
||||
{
|
||||
Name: "c1",
|
||||
Image: "nginx:latest",
|
||||
Ports: []corev1.ContainerPort{
|
||||
{
|
||||
Name: "http",
|
||||
ContainerPort: 80,
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
},
|
||||
{
|
||||
Name: "https",
|
||||
ContainerPort: 443,
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
},
|
||||
{
|
||||
Name: "mysql",
|
||||
ContainerPort: 3306,
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: appsv1.DeploymentStatus{
|
||||
AvailableReplicas: replicas,
|
||||
ReadyReplicas: replicas,
|
||||
Replicas: replicas,
|
||||
},
|
||||
}
|
||||
|
||||
return deployment
|
||||
}
|
||||
|
||||
func newDestinationRule(service *corev1.Service, deployments ...*appsv1.Deployment) *v1alpha3.DestinationRule {
|
||||
dr := &v1alpha3.DestinationRule{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: service.Name,
|
||||
Namespace: service.Namespace,
|
||||
Labels: service.Labels,
|
||||
Annotations: make(map[string]string),
|
||||
},
|
||||
Spec: apiv1alpha3.DestinationRule{
|
||||
Host: service.Name,
|
||||
},
|
||||
}
|
||||
|
||||
dr.Spec.Subsets = []*apiv1alpha3.Subset{}
|
||||
for _, deployment := range deployments {
|
||||
subset := &apiv1alpha3.Subset{
|
||||
Name: util.GetComponentVersion(&deployment.ObjectMeta),
|
||||
Labels: map[string]string{
|
||||
"version": util.GetComponentVersion(&deployment.ObjectMeta),
|
||||
},
|
||||
}
|
||||
dr.Spec.Subsets = append(dr.Spec.Subsets, subset)
|
||||
}
|
||||
|
||||
return dr
|
||||
}
|
||||
|
||||
func newService(name string) *corev1.Service {
|
||||
service := &corev1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/name": "bookinfo",
|
||||
"app.kubernetes.io/version": "1",
|
||||
"app": "foo",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"servicemesh.kubesphere.io/enabled": "true",
|
||||
},
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 80,
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
},
|
||||
{
|
||||
Name: "https",
|
||||
Port: 443,
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
},
|
||||
{
|
||||
Name: "mysql",
|
||||
Port: 3306,
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
},
|
||||
},
|
||||
Selector: map[string]string{
|
||||
"app.kubernetes.io/name": "bookinfo",
|
||||
"app.kubernetes.io/version": "1",
|
||||
"app": "foo",
|
||||
},
|
||||
Type: corev1.ServiceTypeClusterIP,
|
||||
},
|
||||
Status: corev1.ServiceStatus{},
|
||||
}
|
||||
|
||||
return service
|
||||
|
||||
}
|
||||
|
||||
func newServicePolicy(name string, service *corev1.Service, deployments ...*appsv1.Deployment) *v1alpha2.ServicePolicy {
|
||||
sp := &v1alpha2.ServicePolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
Labels: service.Labels,
|
||||
Annotations: service.Annotations,
|
||||
},
|
||||
Spec: v1alpha2.ServicePolicySpec{
|
||||
Template: v1alpha2.DestinationRuleSpecTemplate{
|
||||
Spec: apiv1alpha3.DestinationRule{
|
||||
Host: service.Name,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
sp.Spec.Template.Spec.Subsets = []*apiv1alpha3.Subset{}
|
||||
for _, deployment := range deployments {
|
||||
subset := &apiv1alpha3.Subset{
|
||||
Name: util.GetComponentVersion(&deployment.ObjectMeta),
|
||||
Labels: map[string]string{
|
||||
"version": util.GetComponentVersion(&deployment.ObjectMeta),
|
||||
},
|
||||
}
|
||||
sp.Spec.Template.Spec.Subsets = append(sp.Spec.Template.Spec.Subsets, subset)
|
||||
}
|
||||
|
||||
return sp
|
||||
}
|
||||
|
||||
type fixture struct {
|
||||
t testing.TB
|
||||
|
||||
kubeClient *kubefake.Clientset
|
||||
istioClient *istiofake.Clientset
|
||||
servicemeshClient *fake.Clientset
|
||||
|
||||
serviceLister []*corev1.Service
|
||||
deploymentLister []*appsv1.Deployment
|
||||
drLister []*v1alpha3.DestinationRule
|
||||
spLister []*v1alpha2.ServicePolicy
|
||||
|
||||
kubeObjects []runtime.Object
|
||||
istioObjects []runtime.Object
|
||||
servicemeshObjects []runtime.Object
|
||||
}
|
||||
|
||||
func newFixture(t testing.TB) *fixture {
|
||||
f := &fixture{}
|
||||
f.t = t
|
||||
f.kubeObjects = []runtime.Object{}
|
||||
f.istioObjects = []runtime.Object{}
|
||||
f.servicemeshObjects = []runtime.Object{}
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *fixture) newController() (*DestinationRuleController, kubeinformers.SharedInformerFactory, istioinformers.SharedInformerFactory, informers.SharedInformerFactory, error) {
|
||||
f.kubeClient = kubefake.NewSimpleClientset(f.kubeObjects...)
|
||||
f.servicemeshClient = fake.NewSimpleClientset(f.servicemeshObjects...)
|
||||
f.istioClient = istiofake.NewSimpleClientset(f.istioObjects...)
|
||||
kubeInformers := kubeinformers.NewSharedInformerFactory(f.kubeClient, 0)
|
||||
istioInformers := istioinformers.NewSharedInformerFactory(f.istioClient, 0)
|
||||
servicemeshInformers := informers.NewSharedInformerFactory(f.servicemeshClient, 0)
|
||||
|
||||
c := NewDestinationRuleController(kubeInformers.Apps().V1().Deployments(),
|
||||
istioInformers.Networking().V1alpha3().DestinationRules(),
|
||||
kubeInformers.Core().V1().Services(),
|
||||
servicemeshInformers.Servicemesh().V1alpha2().ServicePolicies(),
|
||||
f.kubeClient,
|
||||
f.istioClient,
|
||||
f.servicemeshClient)
|
||||
c.eventRecorder = &record.FakeRecorder{}
|
||||
c.destinationRuleSynced = alwaysReady
|
||||
c.deploymentSynced = alwaysReady
|
||||
c.servicePolicySynced = alwaysReady
|
||||
c.serviceSynced = alwaysReady
|
||||
|
||||
for _, s := range f.serviceLister {
|
||||
kubeInformers.Core().V1().Services().Informer().GetIndexer().Add(s)
|
||||
}
|
||||
|
||||
for _, d := range f.drLister {
|
||||
istioInformers.Networking().V1alpha3().DestinationRules().Informer().GetIndexer().Add(d)
|
||||
}
|
||||
|
||||
for _, d := range f.deploymentLister {
|
||||
kubeInformers.Apps().V1().Deployments().Informer().GetIndexer().Add(d)
|
||||
}
|
||||
|
||||
for _, s := range f.spLister {
|
||||
servicemeshInformers.Servicemesh().V1alpha2().ServicePolicies().Informer().GetIndexer().Add(s)
|
||||
}
|
||||
|
||||
return c, kubeInformers, istioInformers, servicemeshInformers, nil
|
||||
}
|
||||
|
||||
func (f *fixture) run(service *corev1.Service, expected *v1alpha3.DestinationRule, startInformers bool, expectedError bool) {
|
||||
c, kubeInformers, istioInformers, servicemeshInformers, err := f.newController()
|
||||
if err != nil {
|
||||
f.t.Fatal(err)
|
||||
}
|
||||
|
||||
if startInformers {
|
||||
stopCh := make(chan struct{})
|
||||
defer close(stopCh)
|
||||
kubeInformers.Start(stopCh)
|
||||
istioInformers.Start(stopCh)
|
||||
servicemeshInformers.Start(stopCh)
|
||||
}
|
||||
|
||||
key, err := cache.MetaNamespaceKeyFunc(service)
|
||||
if err != nil {
|
||||
f.t.Fatal(err)
|
||||
}
|
||||
|
||||
err = c.syncService(key)
|
||||
if !expectedError && err != nil {
|
||||
f.t.Fatalf("error syncing service: %v", err)
|
||||
} else if expectedError && err == nil {
|
||||
f.t.Fatal("expected error syncing service, got nil")
|
||||
}
|
||||
|
||||
got, err := c.destinationRuleClient.NetworkingV1alpha3().DestinationRules(service.Namespace).Get(service.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
f.t.Fatal(err)
|
||||
}
|
||||
|
||||
if unequals := reflectutils.Equal(got, expected); len(unequals) != 0 {
|
||||
f.t.Errorf("expected %#v, got %#v, unequal fields:", expected, got)
|
||||
for _, unequal := range unequals {
|
||||
f.t.Error(unequal)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func runServicePolicy(t *testing.T, service *corev1.Service, sp *v1alpha2.ServicePolicy, expected *v1alpha3.DestinationRule, expectedError bool, deployments ...*appsv1.Deployment) {
|
||||
f := newFixture(t)
|
||||
|
||||
f.kubeObjects = append(f.kubeObjects, service)
|
||||
f.serviceLister = append(f.serviceLister, service)
|
||||
for _, deployment := range deployments {
|
||||
f.kubeObjects = append(f.kubeObjects, deployment)
|
||||
f.deploymentLister = append(f.deploymentLister, deployment)
|
||||
}
|
||||
if sp != nil {
|
||||
f.servicemeshObjects = append(f.servicemeshObjects, sp)
|
||||
f.spLister = append(f.spLister, sp)
|
||||
}
|
||||
|
||||
f.run(service, expected, true, expectedError)
|
||||
}
|
||||
|
||||
func TestServicePolicy(t *testing.T) {
|
||||
defaultService := newService("foo")
|
||||
defaultDeploymentV1 := newDeployments(defaultService, "v1")
|
||||
defaultDeploymentV2 := newDeployments(defaultService, "v2")
|
||||
defaultServicePolicy := newServicePolicy("foo", defaultService, defaultDeploymentV1, defaultDeploymentV2)
|
||||
defaultExpected := newDestinationRule(defaultService, defaultDeploymentV1, defaultDeploymentV2)
|
||||
|
||||
t.Run("should create default destination rule", func(t *testing.T) {
|
||||
runServicePolicy(t, defaultService, nil, defaultExpected, false, defaultDeploymentV1, defaultDeploymentV2)
|
||||
})
|
||||
|
||||
t.Run("should create destination rule only to v1", func(t *testing.T) {
|
||||
deploymentV2 := defaultDeploymentV2.DeepCopy()
|
||||
deploymentV2.Status.AvailableReplicas = 0
|
||||
deploymentV2.Status.ReadyReplicas = 0
|
||||
|
||||
expected := defaultExpected.DeepCopy()
|
||||
expected.Spec.Subsets = expected.Spec.Subsets[:1]
|
||||
runServicePolicy(t, defaultService, nil, expected, false, defaultDeploymentV1, deploymentV2)
|
||||
})
|
||||
|
||||
t.Run("should create destination rule match service policy", func(t *testing.T) {
|
||||
sp := defaultServicePolicy.DeepCopy()
|
||||
sp.Spec.Template.Spec.TrafficPolicy = &apiv1alpha3.TrafficPolicy{
|
||||
LoadBalancer: &apiv1alpha3.LoadBalancerSettings{
|
||||
LbPolicy: &apiv1alpha3.LoadBalancerSettings_Simple{
|
||||
Simple: apiv1alpha3.LoadBalancerSettings_ROUND_ROBIN,
|
||||
},
|
||||
},
|
||||
ConnectionPool: &apiv1alpha3.ConnectionPoolSettings{
|
||||
Http: &apiv1alpha3.ConnectionPoolSettings_HTTPSettings{
|
||||
Http1MaxPendingRequests: 10,
|
||||
Http2MaxRequests: 20,
|
||||
MaxRequestsPerConnection: 5,
|
||||
MaxRetries: 4,
|
||||
},
|
||||
},
|
||||
OutlierDetection: &apiv1alpha3.OutlierDetection{
|
||||
ConsecutiveErrors: 5,
|
||||
MaxEjectionPercent: 10,
|
||||
MinHealthPercent: 20,
|
||||
},
|
||||
}
|
||||
|
||||
expected := defaultExpected.DeepCopy()
|
||||
expected.Spec.TrafficPolicy = sp.Spec.Template.Spec.TrafficPolicy
|
||||
runServicePolicy(t, defaultService, sp, expected, false, defaultDeploymentV1, defaultDeploymentV2)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"github.com/knative/pkg/apis/istio/v1alpha3"
|
||||
"istio.io/api/networking/v1alpha3"
|
||||
clientgonetworkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3"
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"strings"
|
||||
@@ -90,17 +91,22 @@ func IsApplicationComponent(lbs map[string]string) bool {
|
||||
}
|
||||
|
||||
// if virtualservice not specified with port number, then fill with service first port
|
||||
func FillDestinationPort(vs *v1alpha3.VirtualService, service *v1.Service) {
|
||||
func FillDestinationPort(vs *clientgonetworkingv1alpha3.VirtualService, service *v1.Service) {
|
||||
// fill http port
|
||||
for i := range vs.Spec.Http {
|
||||
for j := range vs.Spec.Http[i].Route {
|
||||
if vs.Spec.Http[i].Route[j].Destination.Port.Number == 0 {
|
||||
vs.Spec.Http[i].Route[j].Destination.Port.Number = uint32(service.Spec.Ports[0].Port)
|
||||
port := vs.Spec.Http[i].Route[j].Destination.Port
|
||||
if port == nil || port.Number == 0 {
|
||||
vs.Spec.Http[i].Route[j].Destination.Port = &v1alpha3.PortSelector{
|
||||
Number: uint32(service.Spec.Ports[0].Port),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if vs.Spec.Http[i].Mirror != nil && vs.Spec.Http[i].Mirror.Port.Number == 0 {
|
||||
vs.Spec.Http[i].Mirror.Port.Number = uint32(service.Spec.Ports[0].Port)
|
||||
if vs.Spec.Http[i].Mirror != nil && (vs.Spec.Http[i].Mirror.Port == nil || vs.Spec.Http[i].Mirror.Port.Number == 0) {
|
||||
vs.Spec.Http[i].Mirror.Port = &v1alpha3.PortSelector{
|
||||
Number: uint32(service.Spec.Ports[0].Port),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +114,9 @@ func FillDestinationPort(vs *v1alpha3.VirtualService, service *v1.Service) {
|
||||
for i := range vs.Spec.Tcp {
|
||||
for j := range vs.Spec.Tcp[i].Route {
|
||||
if vs.Spec.Tcp[i].Route[j].Destination.Port.Number == 0 {
|
||||
vs.Spec.Tcp[i].Route[j].Destination.Port.Number = uint32(service.Spec.Ports[0].Port)
|
||||
vs.Spec.Tcp[i].Route[j].Destination.Port = &v1alpha3.PortSelector{
|
||||
Number: uint32(service.Spec.Ports[0].Port),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/knative/pkg/apis/istio/v1alpha3"
|
||||
apinetworkingv1alpha3 "istio.io/api/networking/v1alpha3"
|
||||
clientgonetworkingv1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -19,9 +20,9 @@ import (
|
||||
log "k8s.io/klog"
|
||||
"kubesphere.io/kubesphere/pkg/controller/virtualservice/util"
|
||||
|
||||
istioclient "github.com/knative/pkg/client/clientset/versioned"
|
||||
istioinformers "github.com/knative/pkg/client/informers/externalversions/istio/v1alpha3"
|
||||
istiolisters "github.com/knative/pkg/client/listers/istio/v1alpha3"
|
||||
istioclient "istio.io/client-go/pkg/clientset/versioned"
|
||||
istioinformers "istio.io/client-go/pkg/informers/externalversions/networking/v1alpha3"
|
||||
istiolisters "istio.io/client-go/pkg/listers/networking/v1alpha3"
|
||||
coreinformers "k8s.io/client-go/informers/core/v1"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
corelisters "k8s.io/client-go/listers/core/v1"
|
||||
@@ -284,7 +285,7 @@ func (v *VirtualServiceController) syncService(key string) error {
|
||||
currentVirtualService, err := v.virtualServiceLister.VirtualServices(namespace).Get(appName)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
currentVirtualService = &v1alpha3.VirtualService{
|
||||
currentVirtualService = &clientgonetworkingv1alpha3.VirtualService{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
Namespace: namespace,
|
||||
@@ -305,13 +306,13 @@ func (v *VirtualServiceController) syncService(key string) error {
|
||||
|
||||
// check if service has TCP protocol ports
|
||||
for _, port := range service.Spec.Ports {
|
||||
var route v1alpha3.DestinationWeight
|
||||
var route apinetworkingv1alpha3.HTTPRouteDestination
|
||||
if port.Protocol == v1.ProtocolTCP {
|
||||
route = v1alpha3.DestinationWeight{
|
||||
Destination: v1alpha3.Destination{
|
||||
route = apinetworkingv1alpha3.HTTPRouteDestination{
|
||||
Destination: &apinetworkingv1alpha3.Destination{
|
||||
Host: name,
|
||||
Subset: subsets[0].Name,
|
||||
Port: v1alpha3.PortSelector{
|
||||
Port: &apinetworkingv1alpha3.PortSelector{
|
||||
Number: uint32(port.Port),
|
||||
},
|
||||
},
|
||||
@@ -320,12 +321,20 @@ func (v *VirtualServiceController) syncService(key string) error {
|
||||
|
||||
// a http port, add to HTTPRoute
|
||||
if len(port.Name) > 0 && (port.Name == "http" || strings.HasPrefix(port.Name, "http-")) {
|
||||
vs.Spec.Http = []v1alpha3.HTTPRoute{{Route: []v1alpha3.DestinationWeight{route}}}
|
||||
vs.Spec.Http = []*apinetworkingv1alpha3.HTTPRoute{{Route: []*apinetworkingv1alpha3.HTTPRouteDestination{&route}}}
|
||||
break
|
||||
}
|
||||
|
||||
// everything else treated as TCPRoute
|
||||
vs.Spec.Tcp = []v1alpha3.TCPRoute{{Route: []v1alpha3.DestinationWeight{route}}}
|
||||
tcpRoute := apinetworkingv1alpha3.TCPRoute{
|
||||
Route: []*apinetworkingv1alpha3.RouteDestination{
|
||||
{
|
||||
Destination: route.Destination,
|
||||
Weight: route.Weight,
|
||||
},
|
||||
},
|
||||
}
|
||||
vs.Spec.Tcp = []*apinetworkingv1alpha3.TCPRoute{&tcpRoute}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,7 +416,7 @@ func (v *VirtualServiceController) syncService(key string) error {
|
||||
// When a destinationrule is added, figure out which service it will be used
|
||||
// and enqueue it. obj must have *v1alpha3.DestinationRule type
|
||||
func (v *VirtualServiceController) addDestinationRule(obj interface{}) {
|
||||
dr := obj.(*v1alpha3.DestinationRule)
|
||||
dr := obj.(*clientgonetworkingv1alpha3.DestinationRule)
|
||||
service, err := v.serviceLister.Services(dr.Namespace).Get(dr.Name)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
@@ -523,17 +532,17 @@ func (v *VirtualServiceController) getSubsets(strategy *servicemeshv1alpha2.Stra
|
||||
return set
|
||||
}
|
||||
|
||||
func (v *VirtualServiceController) generateVirtualServiceSpec(strategy *servicemeshv1alpha2.Strategy, service *v1.Service) *v1alpha3.VirtualService {
|
||||
func (v *VirtualServiceController) generateVirtualServiceSpec(strategy *servicemeshv1alpha2.Strategy, service *v1.Service) *clientgonetworkingv1alpha3.VirtualService {
|
||||
|
||||
// Define VirtualService to be created
|
||||
vs := &v1alpha3.VirtualService{
|
||||
vs := &clientgonetworkingv1alpha3.VirtualService{
|
||||
Spec: strategy.Spec.Template.Spec,
|
||||
}
|
||||
|
||||
// one version rules them all
|
||||
if len(strategy.Spec.GovernorVersion) > 0 {
|
||||
governorDestinationWeight := v1alpha3.DestinationWeight{
|
||||
Destination: v1alpha3.Destination{
|
||||
governorDestinationWeight := apinetworkingv1alpha3.HTTPRouteDestination{
|
||||
Destination: &apinetworkingv1alpha3.Destination{
|
||||
Host: service.Name,
|
||||
Subset: strategy.Spec.GovernorVersion,
|
||||
},
|
||||
@@ -541,16 +550,26 @@ func (v *VirtualServiceController) generateVirtualServiceSpec(strategy *servicem
|
||||
}
|
||||
|
||||
if len(strategy.Spec.Template.Spec.Http) > 0 {
|
||||
governorRoute := v1alpha3.HTTPRoute{
|
||||
Route: []v1alpha3.DestinationWeight{governorDestinationWeight},
|
||||
governorRoute := apinetworkingv1alpha3.HTTPRoute{
|
||||
Route: []*apinetworkingv1alpha3.HTTPRouteDestination{&governorDestinationWeight},
|
||||
}
|
||||
|
||||
vs.Spec.Http = []v1alpha3.HTTPRoute{governorRoute}
|
||||
vs.Spec.Http = []*apinetworkingv1alpha3.HTTPRoute{&governorRoute}
|
||||
} else if len(strategy.Spec.Template.Spec.Tcp) > 0 {
|
||||
governorRoute := v1alpha3.TCPRoute{
|
||||
Route: []v1alpha3.DestinationWeight{governorDestinationWeight},
|
||||
tcpRoute := apinetworkingv1alpha3.TCPRoute{
|
||||
Route: []*apinetworkingv1alpha3.RouteDestination{
|
||||
{
|
||||
Destination: &apinetworkingv1alpha3.Destination{
|
||||
Host: governorDestinationWeight.Destination.Host,
|
||||
Subset: governorDestinationWeight.Destination.Subset,
|
||||
},
|
||||
Weight: governorDestinationWeight.Weight,
|
||||
},
|
||||
},
|
||||
}
|
||||
vs.Spec.Tcp = []v1alpha3.TCPRoute{governorRoute}
|
||||
|
||||
//governorRoute := v1alpha3.TCPRoute{tcpRoute}
|
||||
vs.Spec.Tcp = []*apinetworkingv1alpha3.TCPRoute{&tcpRoute}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1 +1,474 @@
|
||||
package virtualservice
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
apiv1alpha3 "istio.io/api/networking/v1alpha3"
|
||||
"istio.io/client-go/pkg/apis/networking/v1alpha3"
|
||||
istiofake "istio.io/client-go/pkg/clientset/versioned/fake"
|
||||
istioinformers "istio.io/client-go/pkg/informers/externalversions"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
kubeinformers "k8s.io/client-go/informers"
|
||||
kubefake "k8s.io/client-go/kubernetes/fake"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"kubesphere.io/kubesphere/pkg/apis/servicemesh/v1alpha2"
|
||||
"kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake"
|
||||
informers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
|
||||
"kubesphere.io/kubesphere/pkg/controller/virtualservice/util"
|
||||
"kubesphere.io/kubesphere/pkg/utils/reflectutils"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
alwaysReady = func() bool { return true }
|
||||
serviceName = "foo"
|
||||
applicationName = "bookinfo"
|
||||
namespace = metav1.NamespaceDefault
|
||||
subsets = []string{"v1", "v2"}
|
||||
)
|
||||
|
||||
type fixture struct {
|
||||
t testing.TB
|
||||
|
||||
kubeClient *kubefake.Clientset
|
||||
istioClient *istiofake.Clientset
|
||||
servicemeshClient *fake.Clientset
|
||||
|
||||
serviceLister []*v1.Service
|
||||
vrLister []*v1alpha3.VirtualService
|
||||
drLister []*v1alpha3.DestinationRule
|
||||
strategyLister []*v1alpha2.Strategy
|
||||
|
||||
kubeObjects []runtime.Object
|
||||
istioObjects []runtime.Object
|
||||
servicemeshObjects []runtime.Object
|
||||
}
|
||||
|
||||
type Labels map[string]string
|
||||
|
||||
func NewLabels() Labels {
|
||||
m := make(map[string]string)
|
||||
return m
|
||||
}
|
||||
|
||||
func (l Labels) WithApp(name string) Labels {
|
||||
l["app"] = name
|
||||
return l
|
||||
}
|
||||
|
||||
func (l Labels) WithVersion(version string) Labels {
|
||||
l["version"] = version
|
||||
return l
|
||||
}
|
||||
|
||||
func (l Labels) WithApplication(name string) Labels {
|
||||
l["app.kubernetes.io/name"] = name
|
||||
l["app.kubernetes.io/version"] = ""
|
||||
return l
|
||||
}
|
||||
|
||||
func (l Labels) WithServiceMeshEnabled(enabled bool) Labels {
|
||||
if enabled {
|
||||
l[util.ServiceMeshEnabledAnnotation] = "true"
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func newFixture(t testing.TB) *fixture {
|
||||
f := &fixture{}
|
||||
f.t = t
|
||||
f.kubeObjects = []runtime.Object{}
|
||||
f.istioObjects = []runtime.Object{}
|
||||
f.servicemeshObjects = []runtime.Object{}
|
||||
return f
|
||||
}
|
||||
|
||||
func newVirtualService(name string, host string, labels map[string]string) *v1alpha3.VirtualService {
|
||||
vr := v1alpha3.VirtualService{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Labels: labels,
|
||||
Annotations: make(map[string]string),
|
||||
},
|
||||
Spec: apiv1alpha3.VirtualService{
|
||||
Hosts: []string{host},
|
||||
Http: nil,
|
||||
Tls: nil,
|
||||
Tcp: nil,
|
||||
},
|
||||
}
|
||||
|
||||
return &vr
|
||||
}
|
||||
|
||||
func newService(name string, labels map[string]string, selector map[string]string, protocol string, port int) *v1.Service {
|
||||
svc := v1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: v1.ServiceSpec{
|
||||
Ports: []v1.ServicePort{
|
||||
{
|
||||
Protocol: v1.ProtocolTCP,
|
||||
Port: int32(port),
|
||||
Name: fmt.Sprintf("%s-aaa", protocol),
|
||||
TargetPort: intstr.FromInt(port),
|
||||
},
|
||||
},
|
||||
Selector: selector,
|
||||
Type: v1.ServiceTypeClusterIP,
|
||||
},
|
||||
}
|
||||
|
||||
return &svc
|
||||
}
|
||||
|
||||
func newDestinationRule(name string, host string, labels map[string]string, subsets ...string) *v1alpha3.DestinationRule {
|
||||
dr := v1alpha3.DestinationRule{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: apiv1alpha3.DestinationRule{
|
||||
Host: host,
|
||||
},
|
||||
}
|
||||
dr.Spec.Subsets = []*apiv1alpha3.Subset{}
|
||||
for _, subset := range subsets {
|
||||
dr.Spec.Subsets = append(dr.Spec.Subsets, &apiv1alpha3.Subset{
|
||||
Name: subset,
|
||||
Labels: labels,
|
||||
})
|
||||
}
|
||||
|
||||
return &dr
|
||||
}
|
||||
|
||||
func newStrategy(name string, service *v1.Service, principalVersion string) *v1alpha2.Strategy {
|
||||
st := v1alpha2.Strategy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: service.Namespace,
|
||||
Labels: NewLabels().WithApp(""),
|
||||
Annotations: nil,
|
||||
},
|
||||
Spec: v1alpha2.StrategySpec{
|
||||
Type: v1alpha2.CanaryType,
|
||||
PrincipalVersion: principalVersion,
|
||||
GovernorVersion: "",
|
||||
Template: v1alpha2.VirtualServiceTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{},
|
||||
Spec: apiv1alpha3.VirtualService{
|
||||
Hosts: []string{service.Name},
|
||||
Http: []*apiv1alpha3.HTTPRoute{
|
||||
{
|
||||
Route: []*apiv1alpha3.HTTPRouteDestination{
|
||||
{
|
||||
Destination: &apiv1alpha3.Destination{
|
||||
Host: service.Name,
|
||||
Subset: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
StrategyPolicy: v1alpha2.PolicyImmediately,
|
||||
},
|
||||
}
|
||||
|
||||
return &st
|
||||
}
|
||||
|
||||
func toHost(service *v1.Service) string {
|
||||
return fmt.Sprintf("%s.%s.svc", service.Name, service.Namespace)
|
||||
}
|
||||
|
||||
func (f *fixture) newController() (*VirtualServiceController, kubeinformers.SharedInformerFactory, istioinformers.SharedInformerFactory, informers.SharedInformerFactory, error) {
|
||||
f.kubeClient = kubefake.NewSimpleClientset(f.kubeObjects...)
|
||||
f.servicemeshClient = fake.NewSimpleClientset(f.servicemeshObjects...)
|
||||
f.istioClient = istiofake.NewSimpleClientset(f.istioObjects...)
|
||||
kubeInformers := kubeinformers.NewSharedInformerFactory(f.kubeClient, 0)
|
||||
istioInformers := istioinformers.NewSharedInformerFactory(f.istioClient, 0)
|
||||
servicemeshInformers := informers.NewSharedInformerFactory(f.servicemeshClient, 0)
|
||||
|
||||
c := NewVirtualServiceController(kubeInformers.Core().V1().Services(),
|
||||
istioInformers.Networking().V1alpha3().VirtualServices(),
|
||||
istioInformers.Networking().V1alpha3().DestinationRules(),
|
||||
servicemeshInformers.Servicemesh().V1alpha2().Strategies(),
|
||||
f.kubeClient,
|
||||
f.istioClient,
|
||||
f.servicemeshClient)
|
||||
c.eventRecorder = &record.FakeRecorder{}
|
||||
c.destinationRuleSynced = alwaysReady
|
||||
c.virtualServiceSynced = alwaysReady
|
||||
c.strategySynced = alwaysReady
|
||||
c.serviceSynced = alwaysReady
|
||||
|
||||
for _, s := range f.serviceLister {
|
||||
kubeInformers.Core().V1().Services().Informer().GetIndexer().Add(s)
|
||||
}
|
||||
|
||||
for _, d := range f.drLister {
|
||||
istioInformers.Networking().V1alpha3().DestinationRules().Informer().GetIndexer().Add(d)
|
||||
}
|
||||
|
||||
for _, v := range f.vrLister {
|
||||
istioInformers.Networking().V1alpha3().VirtualServices().Informer().GetIndexer().Add(v)
|
||||
}
|
||||
|
||||
for _, s := range f.strategyLister {
|
||||
servicemeshInformers.Servicemesh().V1alpha2().Strategies().Informer().GetIndexer().Add(s)
|
||||
}
|
||||
|
||||
return c, kubeInformers, istioInformers, servicemeshInformers, nil
|
||||
}
|
||||
|
||||
func (f *fixture) run(serviceKey string, expectedVirtualService *v1alpha3.VirtualService) {
|
||||
f.run_(serviceKey, expectedVirtualService, true, false)
|
||||
}
|
||||
|
||||
func (f *fixture) run_(serviceKey string, expectedVS *v1alpha3.VirtualService, startInformers bool, expectError bool) {
|
||||
namespace, name, err := cache.SplitMetaNamespaceKey(serviceKey)
|
||||
if err != nil {
|
||||
f.t.Fatalf("service key %s is not valid", serviceKey)
|
||||
}
|
||||
|
||||
c, kubeInformers, istioInformers, servicemeshInformers, err := f.newController()
|
||||
if err != nil {
|
||||
f.t.Fatal(err)
|
||||
}
|
||||
|
||||
if startInformers {
|
||||
stopCh := make(chan struct{})
|
||||
defer close(stopCh)
|
||||
kubeInformers.Start(stopCh)
|
||||
istioInformers.Start(stopCh)
|
||||
servicemeshInformers.Start(stopCh)
|
||||
}
|
||||
|
||||
err = c.syncService(serviceKey)
|
||||
if !expectError && err != nil {
|
||||
f.t.Errorf("error syncing service: %v", err)
|
||||
} else if expectError && err == nil {
|
||||
f.t.Error("expected error syncing service, got nil")
|
||||
}
|
||||
|
||||
if expectedVS != nil {
|
||||
got, err := c.virtualServiceClient.NetworkingV1alpha3().VirtualServices(namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
f.t.Errorf("error getting virtualservice: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if unequals := reflectutils.Equal(got, expectedVS); len(unequals) != 0 {
|
||||
f.t.Errorf("didn't get expected result, got %#v, unequal fields:", got)
|
||||
for _, unequal := range unequals {
|
||||
f.t.Errorf("%s", unequal)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitialStrategyCreate(t *testing.T) {
|
||||
f := newFixture(t)
|
||||
|
||||
svc := newService("foo", NewLabels().WithApplication(applicationName).WithApp(serviceName), NewLabels().WithApplication(serviceName).WithApp(applicationName), "http", 80)
|
||||
dr := newDestinationRule(svc.Name, toHost(svc), NewLabels().WithApp("foo").WithApplication(applicationName), subsets[0])
|
||||
svc.Annotations = NewLabels().WithServiceMeshEnabled(true)
|
||||
|
||||
f.kubeObjects = append(f.kubeObjects, svc)
|
||||
f.serviceLister = append(f.serviceLister, svc)
|
||||
f.istioObjects = append(f.istioObjects, dr)
|
||||
f.drLister = append(f.drLister, dr)
|
||||
|
||||
vs := newVirtualService(svc.Name, "foo", NewLabels().WithApplication("bookinfo").WithApp(svc.Name))
|
||||
vs.Annotations = make(map[string]string)
|
||||
vs.Spec.Http = []*apiv1alpha3.HTTPRoute{
|
||||
{
|
||||
Route: []*apiv1alpha3.HTTPRouteDestination{
|
||||
{
|
||||
Destination: &apiv1alpha3.Destination{
|
||||
Host: svc.Name,
|
||||
Subset: "v1",
|
||||
Port: &apiv1alpha3.PortSelector{
|
||||
Number: uint32(svc.Spec.Ports[0].Port),
|
||||
},
|
||||
},
|
||||
Weight: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
key, err := cache.MetaNamespaceKeyFunc(svc)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f.run(key, vs)
|
||||
}
|
||||
|
||||
func runStrategy(t *testing.T, svc *v1.Service, dr *v1alpha3.DestinationRule, strategy *v1alpha2.Strategy, expectedVS *v1alpha3.VirtualService) {
|
||||
key, err := cache.MetaNamespaceKeyFunc(svc)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
f := newFixture(t)
|
||||
|
||||
f.kubeObjects = append(f.kubeObjects, svc)
|
||||
f.serviceLister = append(f.serviceLister, svc)
|
||||
f.istioObjects = append(f.istioObjects, dr)
|
||||
f.drLister = append(f.drLister, dr)
|
||||
f.servicemeshObjects = append(f.servicemeshObjects, strategy)
|
||||
f.strategyLister = append(f.strategyLister, strategy)
|
||||
|
||||
f.run(key, expectedVS)
|
||||
}
|
||||
|
||||
func TestStrategies(t *testing.T) {
|
||||
|
||||
svc := newService(serviceName, NewLabels().WithApplication(applicationName).WithApp(serviceName), NewLabels().WithApplication(applicationName).WithApp(serviceName), "http", 80)
|
||||
defaultDr := newDestinationRule(svc.Name, toHost(svc), NewLabels().WithApp(serviceName).WithApplication(applicationName), subsets...)
|
||||
svc.Annotations = NewLabels().WithServiceMeshEnabled(true)
|
||||
defaultStrategy := &v1alpha2.Strategy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
Labels: NewLabels().WithApp(serviceName).WithApplication(applicationName),
|
||||
Annotations: make(map[string]string),
|
||||
},
|
||||
Spec: v1alpha2.StrategySpec{
|
||||
Type: v1alpha2.CanaryType,
|
||||
PrincipalVersion: "v1",
|
||||
Template: v1alpha2.VirtualServiceTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{},
|
||||
Spec: apiv1alpha3.VirtualService{
|
||||
Hosts: []string{serviceName},
|
||||
Http: []*apiv1alpha3.HTTPRoute{
|
||||
{
|
||||
Route: []*apiv1alpha3.HTTPRouteDestination{
|
||||
{
|
||||
Destination: &apiv1alpha3.Destination{
|
||||
Host: serviceName,
|
||||
Subset: "v1",
|
||||
Port: &apiv1alpha3.PortSelector{
|
||||
Number: 0,
|
||||
},
|
||||
},
|
||||
Weight: 80,
|
||||
},
|
||||
{
|
||||
Destination: &apiv1alpha3.Destination{
|
||||
Host: serviceName,
|
||||
Subset: "v2",
|
||||
Port: &apiv1alpha3.PortSelector{
|
||||
Number: 0,
|
||||
},
|
||||
},
|
||||
Weight: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
StrategyPolicy: v1alpha2.PolicyImmediately,
|
||||
},
|
||||
}
|
||||
|
||||
defaultExpected := newVirtualService(serviceName, serviceName, svc.Labels)
|
||||
defaultExpected.Spec.Http = []*apiv1alpha3.HTTPRoute{
|
||||
{
|
||||
Route: []*apiv1alpha3.HTTPRouteDestination{
|
||||
{
|
||||
Destination: &apiv1alpha3.Destination{
|
||||
Host: svc.Name,
|
||||
Subset: "v1",
|
||||
Port: &apiv1alpha3.PortSelector{
|
||||
Number: uint32(svc.Spec.Ports[0].Port),
|
||||
},
|
||||
},
|
||||
Weight: 80,
|
||||
},
|
||||
{
|
||||
Destination: &apiv1alpha3.Destination{
|
||||
Host: svc.Name,
|
||||
Subset: "v2",
|
||||
Port: &apiv1alpha3.PortSelector{
|
||||
Number: uint32(svc.Spec.Ports[0].Port),
|
||||
},
|
||||
},
|
||||
Weight: 20,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
t.Run("Canary: 80% v1 and 20% v2", func(t *testing.T) {
|
||||
runStrategy(t, svc, defaultDr, defaultStrategy, defaultExpected)
|
||||
})
|
||||
|
||||
t.Run("Canary: 0% v1 and 100% v2", func(t *testing.T) {
|
||||
strategy := defaultStrategy.DeepCopy()
|
||||
strategy.Spec.Template.Spec.Http[0].Route[0].Weight = 0
|
||||
strategy.Spec.Template.Spec.Http[0].Route[1].Weight = 100
|
||||
|
||||
expected := defaultExpected.DeepCopy()
|
||||
expected.Spec.Http[0].Route[0].Weight = 0
|
||||
expected.Spec.Http[0].Route[1].Weight = 100
|
||||
runStrategy(t, svc, defaultDr, strategy, expected)
|
||||
})
|
||||
|
||||
t.Run("Canary: v2 is governing", func(t *testing.T) {
|
||||
strategy := defaultStrategy.DeepCopy()
|
||||
strategy.Spec.GovernorVersion = "v2"
|
||||
|
||||
expected := defaultExpected.DeepCopy()
|
||||
expected.Spec.Http[0].Route[0].Weight = 100
|
||||
expected.Spec.Http[0].Route[0].Destination.Subset = "v2"
|
||||
expected.Spec.Http[0].Route = expected.Spec.Http[0].Route[:1]
|
||||
runStrategy(t, svc, defaultDr, strategy, expected)
|
||||
})
|
||||
|
||||
t.Run("Canary: http match route", func(t *testing.T) {
|
||||
strategy := defaultStrategy.DeepCopy()
|
||||
strategy.Spec.Template.Spec.Http[0].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"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expected := defaultExpected.DeepCopy()
|
||||
expected.Spec.Http[0].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"},
|
||||
},
|
||||
},
|
||||
}
|
||||
runStrategy(t, svc, defaultDr, strategy, expected)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ func Add(mgr manager.Manager) error {
|
||||
// newReconciler returns a new reconcile.Reconciler
|
||||
func newReconciler(mgr manager.Manager) reconcile.Reconciler {
|
||||
return &ReconcileWorkspace{Client: mgr.GetClient(), scheme: mgr.GetScheme(),
|
||||
recorder: mgr.GetRecorder("workspace-controller"), ksclient: cs.ClientSets().KubeSphere()}
|
||||
recorder: mgr.GetEventRecorderFor("workspace-controller"), ksclient: cs.ClientSets().KubeSphere()}
|
||||
}
|
||||
|
||||
// add adds a new Controller to mgr with r as the reconcile.Reconciler
|
||||
@@ -498,7 +498,7 @@ func (r *ReconcileWorkspace) bindNamespaces(instance *tenantv1alpha1.Workspace)
|
||||
|
||||
nsList := &corev1.NamespaceList{}
|
||||
options := client.ListOptions{LabelSelector: labels.SelectorFromSet(labels.Set{constants.WorkspaceLabelKey: instance.Name})}
|
||||
err := r.List(context.TODO(), &options, nsList)
|
||||
err := r.List(context.TODO(), nsList, &options)
|
||||
|
||||
if err != nil {
|
||||
log.Error(err, fmt.Sprintf("list workspace %s namespace failed", instance.Name))
|
||||
|
||||
Reference in New Issue
Block a user