blob: 19fe3cf5fd03e2949a0cf6ffbbcb2714614590b7 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package translation
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
discoveryv1 "k8s.io/api/discovery/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake"
"k8s.io/client-go/tools/cache"
"github.com/apache/apisix-ingress-controller/pkg/kube"
configv1 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v1"
apisixv1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1"
)
func TestTranslateUpstreamConfig(t *testing.T) {
tr := &translator{}
au := &configv1.ApisixUpstreamConfig{
LoadBalancer: nil,
Scheme: apisixv1.SchemeGRPC,
}
ups, err := tr.TranslateUpstreamConfig(au)
assert.Nil(t, err, "checking upstream config translating")
assert.Equal(t, ups.Type, apisixv1.LbRoundRobin)
assert.Equal(t, ups.Scheme, apisixv1.SchemeGRPC)
au = &configv1.ApisixUpstreamConfig{
LoadBalancer: &configv1.LoadBalancer{
Type: apisixv1.LbConsistentHash,
HashOn: apisixv1.HashOnHeader,
Key: "user-agent",
},
Scheme: apisixv1.SchemeHTTP,
}
ups, err = tr.TranslateUpstreamConfig(au)
assert.Nil(t, err, "checking upstream config translating")
assert.Equal(t, ups.Type, apisixv1.LbConsistentHash)
assert.Equal(t, ups.Key, "user-agent")
assert.Equal(t, ups.HashOn, apisixv1.HashOnHeader)
assert.Equal(t, ups.Scheme, apisixv1.SchemeHTTP)
au = &configv1.ApisixUpstreamConfig{
LoadBalancer: &configv1.LoadBalancer{
Type: apisixv1.LbConsistentHash,
HashOn: apisixv1.HashOnHeader,
Key: "user-agent",
},
Scheme: "dns",
}
_, err = tr.TranslateUpstreamConfig(au)
assert.Error(t, err, &translateError{
field: "scheme",
reason: "invalid value",
})
au = &configv1.ApisixUpstreamConfig{
LoadBalancer: &configv1.LoadBalancer{
Type: "hash",
},
}
_, err = tr.TranslateUpstreamConfig(au)
assert.Error(t, err, &translateError{
field: "loadbalancer.type",
reason: "invalid value",
})
au = &configv1.ApisixUpstreamConfig{
LoadBalancer: &configv1.LoadBalancer{
Type: apisixv1.LbConsistentHash,
HashOn: "arg",
},
}
_, err = tr.TranslateUpstreamConfig(au)
assert.Error(t, err, &translateError{
field: "loadbalancer.hashOn",
reason: "invalid value",
})
}
func TestTranslateUpstreamNodes(t *testing.T) {
svc := &corev1.Service{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{
Name: "svc",
Namespace: "test",
},
Spec: corev1.ServiceSpec{
Ports: []corev1.ServicePort{
{
Name: "port1",
Port: 80,
TargetPort: intstr.IntOrString{
Type: intstr.Int,
IntVal: 9080,
},
},
{
Name: "port2",
Port: 443,
TargetPort: intstr.IntOrString{
Type: intstr.Int,
IntVal: 9443,
},
},
},
},
}
endpoints := &corev1.Endpoints{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{
Name: "svc",
Namespace: "test",
},
Subsets: []corev1.EndpointSubset{
{
Ports: []corev1.EndpointPort{
{
Name: "port1",
Port: 9080,
},
{
Name: "port2",
Port: 9443,
},
},
Addresses: []corev1.EndpointAddress{
{IP: "192.168.1.1"},
{IP: "192.168.1.2"},
},
},
},
}
client := fake.NewSimpleClientset()
informersFactory := informers.NewSharedInformerFactory(client, 0)
svcInformer := informersFactory.Core().V1().Services().Informer()
svcLister := informersFactory.Core().V1().Services().Lister()
processCh := make(chan struct{})
svcInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
processCh <- struct{}{}
},
})
stopCh := make(chan struct{})
defer close(stopCh)
go svcInformer.Run(stopCh)
cache.WaitForCacheSync(stopCh, svcInformer.HasSynced)
_, err := client.CoreV1().Services("test").Create(context.Background(), svc, metav1.CreateOptions{})
assert.Nil(t, err)
tr := &translator{&TranslatorOptions{
ServiceLister: svcLister,
}}
<-processCh
nodes, err := tr.TranslateUpstreamNodes(kube.NewEndpoint(endpoints), 10080, nil)
assert.Nil(t, nodes)
assert.Equal(t, err, &translateError{
field: "service.spec.ports",
reason: "port not defined",
})
nodes, err = tr.TranslateUpstreamNodes(kube.NewEndpoint(endpoints), 80, nil)
assert.Nil(t, err)
assert.Equal(t, nodes, apisixv1.UpstreamNodes{
{
Host: "192.168.1.1",
Port: 9080,
Weight: 100,
},
{
Host: "192.168.1.2",
Port: 9080,
Weight: 100,
},
})
nodes, err = tr.TranslateUpstreamNodes(kube.NewEndpoint(endpoints), 443, nil)
assert.Nil(t, err)
assert.Equal(t, nodes, apisixv1.UpstreamNodes{
{
Host: "192.168.1.1",
Port: 9443,
Weight: 100,
},
{
Host: "192.168.1.2",
Port: 9443,
Weight: 100,
},
})
}
func TestTranslateUpstreamNodesWithEndpointSlices(t *testing.T) {
svc := &corev1.Service{
TypeMeta: metav1.TypeMeta{},
ObjectMeta: metav1.ObjectMeta{
Name: "svc",
Namespace: "test",
},
Spec: corev1.ServiceSpec{
Ports: []corev1.ServicePort{
{
Name: "port1",
Port: 80,
TargetPort: intstr.IntOrString{
Type: intstr.Int,
IntVal: 9080,
},
},
{
Name: "port2",
Port: 443,
TargetPort: intstr.IntOrString{
Type: intstr.Int,
IntVal: 9443,
},
},
},
},
}
isTrue := true
port1 := int32(9080)
port2 := int32(9443)
port1Name := "port1"
port2Name := "port2"
ep := &discoveryv1.EndpointSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "svc",
Namespace: "test",
Labels: map[string]string{
discoveryv1.LabelManagedBy: "endpointslice-controller.k8s.io",
discoveryv1.LabelServiceName: "svc",
},
},
AddressType: discoveryv1.AddressTypeIPv4,
Endpoints: []discoveryv1.Endpoint{
{
Addresses: []string{
"192.168.1.1",
"192.168.1.2",
},
Conditions: discoveryv1.EndpointConditions{
Ready: &isTrue,
},
},
},
Ports: []discoveryv1.EndpointPort{
{
Name: &port1Name,
Port: &port1,
},
{
Name: &port2Name,
Port: &port2,
},
},
}
client := fake.NewSimpleClientset()
informersFactory := informers.NewSharedInformerFactory(client, 0)
svcInformer := informersFactory.Core().V1().Services().Informer()
svcLister := informersFactory.Core().V1().Services().Lister()
processCh := make(chan struct{})
svcInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
processCh <- struct{}{}
},
})
stopCh := make(chan struct{})
defer close(stopCh)
go svcInformer.Run(stopCh)
cache.WaitForCacheSync(stopCh, svcInformer.HasSynced)
_, err := client.CoreV1().Services("test").Create(context.Background(), svc, metav1.CreateOptions{})
assert.Nil(t, err)
tr := &translator{&TranslatorOptions{
ServiceLister: svcLister,
}}
<-processCh
nodes, err := tr.TranslateUpstreamNodes(kube.NewEndpointWithSlice(ep), 10080, nil)
assert.Nil(t, nodes)
assert.Equal(t, err, &translateError{
field: "service.spec.ports",
reason: "port not defined",
})
nodes, err = tr.TranslateUpstreamNodes(kube.NewEndpointWithSlice(ep), 80, nil)
assert.Nil(t, err)
assert.Equal(t, nodes, apisixv1.UpstreamNodes{
{
Host: "192.168.1.1",
Port: 9080,
Weight: 100,
},
{
Host: "192.168.1.2",
Port: 9080,
Weight: 100,
},
})
nodes, err = tr.TranslateUpstreamNodes(kube.NewEndpointWithSlice(ep), 443, nil)
assert.Nil(t, err)
assert.Equal(t, nodes, apisixv1.UpstreamNodes{
{
Host: "192.168.1.1",
Port: 9443,
Weight: 100,
},
{
Host: "192.168.1.2",
Port: 9443,
Weight: 100,
},
})
}