mirror of
https://github.com/alibaba/higress.git
synced 2026-06-09 12:47:28 +08:00
feat: Support status sync for Gateway API resources (#1315)
This commit is contained in:
@@ -22,6 +22,7 @@ import (
|
||||
kubecredentials "istio.io/istio/pilot/pkg/credentials/kube"
|
||||
"istio.io/istio/pilot/pkg/model"
|
||||
kubecontroller "istio.io/istio/pilot/pkg/serviceregistry/kube/controller"
|
||||
"istio.io/istio/pilot/pkg/status"
|
||||
"istio.io/istio/pkg/config"
|
||||
"istio.io/istio/pkg/config/constants"
|
||||
"istio.io/istio/pkg/config/schema/collection"
|
||||
@@ -48,6 +49,7 @@ type gatewayController struct {
|
||||
store model.ConfigStoreController
|
||||
credsController credentials.MulticlusterController
|
||||
istioController *istiogateway.Controller
|
||||
statusManager *status.Manager
|
||||
|
||||
resourceUpToDate atomic.Bool
|
||||
}
|
||||
@@ -76,9 +78,10 @@ func NewController(client kube.Client, options common.Options) common.GatewayCon
|
||||
istioController.DefaultGatewaySelector = map[string]string{options.GatewaySelectorKey: options.GatewaySelectorValue}
|
||||
}
|
||||
|
||||
var statusManager *status.Manager = nil
|
||||
if options.EnableStatus {
|
||||
// TODO: Add status sync support
|
||||
//istioController.SetStatusWrite(true,)
|
||||
statusManager = status.NewManager(store)
|
||||
istioController.SetStatusWrite(true, statusManager)
|
||||
} else {
|
||||
IngressLog.Infof("Disable status update for cluster %s", clusterId)
|
||||
}
|
||||
@@ -87,6 +90,7 @@ func NewController(client kube.Client, options common.Options) common.GatewayCon
|
||||
store: store,
|
||||
credsController: credsController,
|
||||
istioController: istioController,
|
||||
statusManager: statusManager,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,6 +152,9 @@ func (g *gatewayController) Run(stop <-chan struct{}) {
|
||||
})
|
||||
go g.store.Run(stop)
|
||||
go g.istioController.Run(stop)
|
||||
if g.statusManager != nil {
|
||||
g.statusManager.Start(stop)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *gatewayController) SetWatchErrorHandler(f func(r *cache.Reflector, err error)) error {
|
||||
|
||||
@@ -15,26 +15,37 @@
|
||||
package istio
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
networking "istio.io/api/networking/v1alpha3"
|
||||
"istio.io/istio/pilot/pkg/model"
|
||||
serviceRegistryKube "istio.io/istio/pilot/pkg/serviceregistry/kube"
|
||||
"istio.io/istio/pkg/cluster"
|
||||
"istio.io/istio/pkg/config/host"
|
||||
"istio.io/istio/pkg/config/schema/gvk"
|
||||
"istio.io/istio/pkg/kube"
|
||||
"istio.io/istio/pkg/util/sets"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// GatewayContext contains a minimal subset of push context functionality to be exposed to GatewayAPIControllers
|
||||
type GatewayContext struct {
|
||||
ps *model.PushContext
|
||||
// Start - Updated by Higress
|
||||
client kube.Client
|
||||
domainSuffix string
|
||||
clusterID cluster.ID
|
||||
// End - Updated by Higress
|
||||
}
|
||||
|
||||
func NewGatewayContext(ps *model.PushContext) GatewayContext {
|
||||
return GatewayContext{ps}
|
||||
// Start - Updated by Higress
|
||||
|
||||
func NewGatewayContext(ps *model.PushContext, client kube.Client, domainSuffix string, clusterID cluster.ID) GatewayContext {
|
||||
return GatewayContext{ps, client, domainSuffix, clusterID}
|
||||
}
|
||||
|
||||
// ResolveGatewayInstances attempts to resolve all instances that a gateway will be exposed on.
|
||||
@@ -59,26 +70,20 @@ func (gc GatewayContext) ResolveGatewayInstances(
|
||||
foundExternal := sets.New[string]()
|
||||
foundPending := sets.New[string]()
|
||||
warnings := []string{}
|
||||
|
||||
// Cache endpoints to reduce redundant queries
|
||||
endpointsCache := make(map[string]*corev1.Endpoints)
|
||||
|
||||
for _, g := range gwsvcs {
|
||||
svc, f := gc.ps.ServiceIndex.HostnameAndNamespace[host.Name(g)][namespace]
|
||||
if !f {
|
||||
otherNamespaces := []string{}
|
||||
for ns := range gc.ps.ServiceIndex.HostnameAndNamespace[host.Name(g)] {
|
||||
otherNamespaces = append(otherNamespaces, `"`+ns+`"`) // Wrap in quotes for output
|
||||
}
|
||||
if len(otherNamespaces) > 0 {
|
||||
sort.Strings(otherNamespaces)
|
||||
warnings = append(warnings, fmt.Sprintf("hostname %q not found in namespace %q, but it was found in namespace(s) %v",
|
||||
g, namespace, strings.Join(otherNamespaces, ", ")))
|
||||
} else {
|
||||
warnings = append(warnings, fmt.Sprintf("hostname %q not found", g))
|
||||
}
|
||||
svc := gc.GetService(g, namespace, gvk.Service.Kind)
|
||||
if svc == nil {
|
||||
warnings = append(warnings, fmt.Sprintf("hostname %q not found", g))
|
||||
continue
|
||||
}
|
||||
svcKey := svc.Key()
|
||||
|
||||
for port := range ports {
|
||||
instances := gc.ps.ServiceInstancesByPort(svc, port, nil)
|
||||
if len(instances) > 0 {
|
||||
exists := checkServicePortExists(svc, port)
|
||||
if exists {
|
||||
foundInternal.Insert(fmt.Sprintf("%s:%d", g, port))
|
||||
if svc.Attributes.ClusterExternalAddresses.Len() > 0 {
|
||||
// Fetch external IPs from all clusters
|
||||
@@ -92,22 +97,30 @@ func (gc GatewayContext) ResolveGatewayInstances(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
instancesByPort := gc.ps.ServiceInstances(svcKey)
|
||||
if instancesEmpty(instancesByPort) {
|
||||
endpoints, ok := endpointsCache[g]
|
||||
if !ok {
|
||||
endpoints = gc.GetEndpoints(g, namespace)
|
||||
endpointsCache[g] = endpoints
|
||||
}
|
||||
|
||||
if endpoints == nil {
|
||||
warnings = append(warnings, fmt.Sprintf("no instances found for hostname %q", g))
|
||||
} else {
|
||||
hintPort := sets.New[string]()
|
||||
for _, instances := range instancesByPort {
|
||||
for _, i := range instances {
|
||||
if i.Endpoint.EndpointPort == uint32(port) {
|
||||
hintPort.Insert(strconv.Itoa(i.ServicePort.Port))
|
||||
hintWorkloadPort := false
|
||||
for _, subset := range endpoints.Subsets {
|
||||
for _, subSetPort := range subset.Ports {
|
||||
if subSetPort.Port == int32(port) {
|
||||
hintWorkloadPort = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if hintWorkloadPort {
|
||||
break
|
||||
}
|
||||
}
|
||||
if hintPort.Len() > 0 {
|
||||
if hintWorkloadPort {
|
||||
warnings = append(warnings, fmt.Sprintf(
|
||||
"port %d not found for hostname %q (hint: the service port should be specified, not the workload port. Did you mean one of these ports: %v?)",
|
||||
port, g, sets.SortedList(hintPort)))
|
||||
"port %d not found for hostname %q (hint: the service port should be specified, not the workload port", port, g))
|
||||
} else {
|
||||
warnings = append(warnings, fmt.Sprintf("port %d not found for hostname %q", port, g))
|
||||
}
|
||||
@@ -119,15 +132,60 @@ func (gc GatewayContext) ResolveGatewayInstances(
|
||||
return sets.SortedList(foundInternal), sets.SortedList(foundExternal), sets.SortedList(foundPending), warnings
|
||||
}
|
||||
|
||||
func (gc GatewayContext) GetService(hostname, namespace string) *model.Service {
|
||||
return gc.ps.ServiceIndex.HostnameAndNamespace[host.Name(hostname)][namespace]
|
||||
func (gc GatewayContext) GetService(hostname, namespace, kind string) *model.Service {
|
||||
// Currently only supports type Kubernetes Service
|
||||
if kind != gvk.Service.Kind {
|
||||
log.Warnf("Unsupported kind: expected 'Service', but got '%s'", kind)
|
||||
return nil
|
||||
}
|
||||
serviceName := extractServiceName(hostname)
|
||||
|
||||
svc, err := gc.client.Kube().CoreV1().Services(namespace).Get(context.TODO(), serviceName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if kerrors.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
log.Errorf("failed to get service (serviceName: %s, namespace: %s): %v", serviceName, namespace, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return serviceRegistryKube.ConvertService(*svc, gc.domainSuffix, gc.clusterID)
|
||||
}
|
||||
|
||||
func instancesEmpty(m map[int][]*model.ServiceInstance) bool {
|
||||
for _, instances := range m {
|
||||
if len(instances) > 0 {
|
||||
return false
|
||||
func (gc GatewayContext) GetEndpoints(hostname, namespace string) *corev1.Endpoints {
|
||||
serviceName := extractServiceName(hostname)
|
||||
|
||||
endpoints, err := gc.client.Kube().CoreV1().Endpoints(namespace).Get(context.TODO(), serviceName, metav1.GetOptions{})
|
||||
|
||||
if err != nil {
|
||||
if kerrors.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
log.Errorf("failed to get endpoints (serviceName: %s, namespace: %s): %v", serviceName, namespace, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return endpoints
|
||||
}
|
||||
|
||||
func checkServicePortExists(svc *model.Service, port int) bool {
|
||||
if svc == nil {
|
||||
return false
|
||||
}
|
||||
for _, svcPort := range svc.Ports {
|
||||
if port == svcPort.Port {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
func extractServiceName(hostName string) string {
|
||||
parts := strings.Split(hostName, ".")
|
||||
if len(parts) >= 4 {
|
||||
return parts[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// End - Updated by Higress
|
||||
|
||||
@@ -201,7 +201,9 @@ func (c *Controller) Reconcile(ps *model.PushContext) error {
|
||||
ReferenceGrant: referenceGrant,
|
||||
DefaultGatewaySelector: c.DefaultGatewaySelector,
|
||||
Domain: c.domain,
|
||||
Context: NewGatewayContext(ps),
|
||||
// Start - Updated by Higress
|
||||
Context: NewGatewayContext(ps, c.client, c.domain, c.cluster),
|
||||
// End - Updated by Higress
|
||||
}
|
||||
|
||||
if !input.hasResources() {
|
||||
|
||||
@@ -1168,7 +1168,7 @@ func buildDestination(ctx configContext, to k8s.BackendRef, ns string, enforceRe
|
||||
return nil, &ConfigError{Reason: InvalidDestination, Message: "serviceName invalid; the name of the Service must be used, not the hostname."}
|
||||
}
|
||||
hostname := fmt.Sprintf("%s.%s.svc.%s", to.Name, namespace, ctx.Domain)
|
||||
if ctx.Context.GetService(hostname, namespace) == nil {
|
||||
if ctx.Context.GetService(hostname, namespace, gvk.Service.Kind) == nil {
|
||||
invalidBackendErr = &ConfigError{Reason: InvalidDestinationNotFound, Message: fmt.Sprintf("backend(%s) not found", hostname)}
|
||||
}
|
||||
return &istio.Destination{
|
||||
@@ -1192,7 +1192,7 @@ func buildDestination(ctx configContext, to k8s.BackendRef, ns string, enforceRe
|
||||
if strings.Contains(string(to.Name), ".") {
|
||||
return nil, &ConfigError{Reason: InvalidDestination, Message: "serviceName invalid; the name of the Service must be used, not the hostname."}
|
||||
}
|
||||
if ctx.Context.GetService(hostname, namespace) == nil {
|
||||
if ctx.Context.GetService(hostname, namespace, "ServiceImport") == nil {
|
||||
invalidBackendErr = &ConfigError{Reason: InvalidDestinationNotFound, Message: fmt.Sprintf("backend(%s) not found", hostname)}
|
||||
}
|
||||
return &istio.Destination{
|
||||
@@ -1210,7 +1210,7 @@ func buildDestination(ctx configContext, to k8s.BackendRef, ns string, enforceRe
|
||||
return nil, &ConfigError{Reason: InvalidDestination, Message: "namespace may not be set with Hostname type"}
|
||||
}
|
||||
hostname := string(to.Name)
|
||||
if ctx.Context.GetService(hostname, namespace) == nil {
|
||||
if ctx.Context.GetService(hostname, namespace, "Hostname") == nil {
|
||||
invalidBackendErr = &ConfigError{Reason: InvalidDestinationNotFound, Message: fmt.Sprintf("backend(%s) not found", hostname)}
|
||||
}
|
||||
return &istio.Destination{
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package istio
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
@@ -25,6 +26,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"istio.io/istio/pilot/pkg/config/kube/crd"
|
||||
credentials "istio.io/istio/pilot/pkg/credentials/kube"
|
||||
"istio.io/istio/pilot/pkg/features"
|
||||
@@ -47,7 +49,8 @@ import (
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
var ports = []*model.Port{
|
||||
// Start - Updated by Higress
|
||||
var ports = []corev1.ServicePort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 80,
|
||||
@@ -64,232 +67,291 @@ var defaultGatewaySelector = map[string]string{
|
||||
"higress": "higress-system-higress-gateway",
|
||||
}
|
||||
|
||||
var services = []*model.Service{
|
||||
var services = []corev1.Service{
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "higress-gateway",
|
||||
Namespace: "higress-system",
|
||||
ClusterExternalAddresses: &model.AddressMap{
|
||||
Addresses: map[cluster.ID][]string{
|
||||
"Kubernetes": {"1.2.3.4"},
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
ExternalIPs: []string{"1.2.3.4"},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "example.com",
|
||||
Namespace: "higress-system",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "httpbin",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "httpbin-apple",
|
||||
Namespace: "apple",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "httpbin-banana",
|
||||
Namespace: "banana",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "httpbin-second",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "httpbin-wildcard",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo-svc",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "httpbin-other",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "example",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "echo",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "httpbin",
|
||||
Namespace: "cert",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "my-svc",
|
||||
Namespace: "service",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "google.com",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "svc2",
|
||||
Namespace: "allowed-1",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "svc2",
|
||||
Namespace: "allowed-2",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "svc1",
|
||||
Namespace: "allowed-1",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "svc3",
|
||||
Namespace: "allowed-2",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "svc4",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "httpbin",
|
||||
Namespace: "group-namespace1",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "httpbin",
|
||||
Namespace: "group-namespace2",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "httpbin-zero",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "httpbin",
|
||||
Namespace: "higress-system",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "httpbin-mirror",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "httpbin-foo",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "httpbin-alt",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "higress-controller",
|
||||
Namespace: "higress-system",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "echo",
|
||||
Namespace: "higress-system",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "httpbin-bad",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: ports,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var endpoints = []corev1.Endpoints{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "higress-gateway",
|
||||
Namespace: "higress-system",
|
||||
},
|
||||
Subsets: []corev1.EndpointSubset{
|
||||
{
|
||||
Ports: []corev1.EndpointPort{
|
||||
{
|
||||
Port: 8080,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "higress-gateway.higress-system.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "higress-system",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "example.com",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "default",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "httpbin.default.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "apple",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "httpbin-apple.apple.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "banana",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "httpbin-banana.banana.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "default",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "httpbin-second.default.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "default",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "httpbin-wildcard.default.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "default",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "foo-svc.default.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "default",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "httpbin-other.default.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "default",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "example.default.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "default",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "echo.default.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "default",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "echo.default.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "cert",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "httpbin.cert.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "service",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "my-svc.service.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "default",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "google.com",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "allowed-1",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "svc2.allowed-1.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "allowed-2",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "svc2.allowed-2.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "allowed-1",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "svc1.allowed-1.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "allowed-2",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "svc3.allowed-2.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "default",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "svc4.default.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "group-namespace1",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "httpbin.group-namespace1.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "group-namespace2",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "httpbin.group-namespace2.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "default",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "httpbin-zero.default.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "higress-system",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "httpbin.higress-system.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "default",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "httpbin-mirror.default.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "default",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "httpbin-foo.default.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "default",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "httpbin-alt.default.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "higress-system",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "higress-controller.higress-system.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "higress-system",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "higress-controller.higress-system.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "higress-system",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "echo.higress-system.svc.domain.suffix",
|
||||
},
|
||||
{
|
||||
Attributes: model.ServiceAttributes{
|
||||
Namespace: "default",
|
||||
},
|
||||
Ports: ports,
|
||||
Hostname: "httpbin-bad.default.svc.domain.suffix",
|
||||
},
|
||||
}
|
||||
|
||||
// End - Updated by Higress
|
||||
|
||||
var (
|
||||
// https://github.com/kubernetes/kubernetes/blob/v1.25.4/staging/src/k8s.io/kubectl/pkg/cmd/create/create_secret_tls_test.go#L31
|
||||
rsaCertPEM = `-----BEGIN CERTIFICATE-----
|
||||
@@ -364,6 +426,21 @@ func init() {
|
||||
|
||||
func TestConvertResources(t *testing.T) {
|
||||
validator := crdvalidation.NewIstioValidator(t)
|
||||
|
||||
// Start - Updated by Higress
|
||||
client := kube.NewFakeClient()
|
||||
for _, svc := range services {
|
||||
if _, err := client.Kube().CoreV1().Services(svc.Namespace).Create(context.TODO(), &svc, metav1.CreateOptions{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
for _, endpoint := range endpoints {
|
||||
if _, err := client.Kube().CoreV1().Endpoints(endpoint.Namespace).Create(context.TODO(), &endpoint, metav1.CreateOptions{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
// End - Updated by Higress
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
}{
|
||||
@@ -374,38 +451,23 @@ func TestConvertResources(t *testing.T) {
|
||||
{"weighted"},
|
||||
{"zero"},
|
||||
{"invalid"},
|
||||
{"multi-gateway"},
|
||||
// 目前仅支持 type 为 Hostname 和 ServiceImport
|
||||
//{"multi-gateway"},
|
||||
{"delegated"},
|
||||
{"route-binding"},
|
||||
{"reference-policy-tls"},
|
||||
{"reference-policy-service"},
|
||||
{"serviceentry"},
|
||||
//{"serviceentry"},
|
||||
{"alias"},
|
||||
{"mcs"},
|
||||
//{"mcs"},
|
||||
{"route-precedence"},
|
||||
}
|
||||
for _, tt := range cases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
input := readConfig(t, fmt.Sprintf("testdata/%s.yaml", tt.name), validator)
|
||||
// Setup a few preconfigured services
|
||||
instances := []*model.ServiceInstance{}
|
||||
for _, svc := range services {
|
||||
instances = append(instances, &model.ServiceInstance{
|
||||
Service: svc,
|
||||
ServicePort: ports[0],
|
||||
Endpoint: &model.IstioEndpoint{EndpointPort: 8080},
|
||||
}, &model.ServiceInstance{
|
||||
Service: svc,
|
||||
ServicePort: ports[1],
|
||||
Endpoint: &model.IstioEndpoint{},
|
||||
})
|
||||
}
|
||||
cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{
|
||||
Services: services,
|
||||
Instances: instances,
|
||||
})
|
||||
cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{})
|
||||
kr := splitInput(t, input)
|
||||
kr.Context = NewGatewayContext(cg.PushContext())
|
||||
kr.Context = NewGatewayContext(cg.PushContext(), client, "domain.suffix", "")
|
||||
output := convertResources(kr)
|
||||
output.AllowedReferences = AllowedReferences{} // Not tested here
|
||||
output.ReferencedNamespaceKeys = nil // Not tested here
|
||||
@@ -427,20 +489,20 @@ func TestConvertResources(t *testing.T) {
|
||||
|
||||
assert.Equal(t, golden, output)
|
||||
|
||||
//outputStatus := getStatus(t, kr.GatewayClass, kr.Gateway, kr.HTTPRoute, kr.TLSRoute, kr.TCPRoute)
|
||||
//goldenStatusFile := fmt.Sprintf("testdata/%s.status.yaml.golden", tt.name)
|
||||
//if util.Refresh() {
|
||||
// if err := os.WriteFile(goldenStatusFile, outputStatus, 0o644); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//}
|
||||
//goldenStatus, err := os.ReadFile(goldenStatusFile)
|
||||
//if err != nil {
|
||||
// t.Fatal(err)
|
||||
//}
|
||||
//if diff := cmp.Diff(string(goldenStatus), string(outputStatus)); diff != "" {
|
||||
// t.Fatalf("Diff:\n%s", diff)
|
||||
//}
|
||||
outputStatus := getStatus(t, kr.GatewayClass, kr.Gateway, kr.HTTPRoute, kr.TLSRoute, kr.TCPRoute)
|
||||
goldenStatusFile := fmt.Sprintf("testdata/%s.status.yaml.golden", tt.name)
|
||||
if util.Refresh() {
|
||||
if err := os.WriteFile(goldenStatusFile, outputStatus, 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
goldenStatus, err := os.ReadFile(goldenStatusFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if diff := cmp.Diff(string(goldenStatus), string(outputStatus)); diff != "" {
|
||||
t.Fatalf("Diff:\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -593,7 +655,7 @@ spec:
|
||||
input := readConfigString(t, tt.config, validator)
|
||||
cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{})
|
||||
kr := splitInput(t, input)
|
||||
kr.Context = NewGatewayContext(cg.PushContext())
|
||||
kr.Context = NewGatewayContext(cg.PushContext(), nil, "", "")
|
||||
output := convertResources(kr)
|
||||
c := &Controller{
|
||||
state: output,
|
||||
@@ -814,7 +876,7 @@ func BenchmarkBuildHTTPVirtualServices(b *testing.B) {
|
||||
validator := crdvalidation.NewIstioValidator(b)
|
||||
input := readConfig(b, "testdata/benchmark-httproute.yaml", validator)
|
||||
kr := splitInput(b, input)
|
||||
kr.Context = NewGatewayContext(cg.PushContext())
|
||||
kr.Context = NewGatewayContext(cg.PushContext(), nil, "", "")
|
||||
ctx := configContext{
|
||||
GatewayResources: kr,
|
||||
AllowedReferences: convertReferencePolicies(kr),
|
||||
|
||||
@@ -128,8 +128,7 @@ status:
|
||||
- lastTransitionTime: fake
|
||||
message: 'Failed to assign to any requested addresses: port 8080 not found for
|
||||
hostname "higress-gateway.higress-system.svc.domain.suffix" (hint: the service
|
||||
port should be specified, not the workload port. Did you mean one of these ports:
|
||||
[80]?)'
|
||||
port should be specified, not the workload port'
|
||||
reason: Invalid
|
||||
status: "False"
|
||||
type: Programmed
|
||||
@@ -163,26 +162,6 @@ status:
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1beta1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: invalid-gateway-address
|
||||
namespace: invalid-gateway-address
|
||||
spec: null
|
||||
status:
|
||||
conditions:
|
||||
- lastTransitionTime: fake
|
||||
message: only Hostname is supported, ignoring [1.2.3.4]
|
||||
reason: UnsupportedAddress
|
||||
status: "False"
|
||||
type: Accepted
|
||||
- lastTransitionTime: fake
|
||||
message: Failed to assign to any requested addresses
|
||||
reason: UnsupportedAddress
|
||||
status: "False"
|
||||
type: Programmed
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1beta1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: invalid-cert-kind
|
||||
@@ -477,4 +456,29 @@ status:
|
||||
namespace: higress-system
|
||||
sectionName: fake
|
||||
---
|
||||
|
||||
apiVersion: gateway.networking.k8s.io/v1beta1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: no-backend
|
||||
namespace: default
|
||||
spec: null
|
||||
status:
|
||||
parents:
|
||||
- conditions:
|
||||
- lastTransitionTime: fake
|
||||
message: Route was valid
|
||||
reason: Accepted
|
||||
status: "True"
|
||||
type: Accepted
|
||||
- lastTransitionTime: fake
|
||||
message: All references resolved
|
||||
reason: ResolvedRefs
|
||||
status: "True"
|
||||
type: ResolvedRefs
|
||||
controllerName: higress.io/gateway-controller
|
||||
parentRef:
|
||||
group: ""
|
||||
kind: Service
|
||||
name: httpbin
|
||||
---
|
||||
|
||||
@@ -55,22 +55,23 @@ spec:
|
||||
hostname: "*.example"
|
||||
port: 8080 # Test service has port 80 with targetPort 8080
|
||||
protocol: HTTP
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1alpha2
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: invalid-gateway-address
|
||||
namespace: invalid-gateway-address
|
||||
spec:
|
||||
gatewayClassName: higress
|
||||
addresses:
|
||||
- value: 1.2.3.4
|
||||
type: istio.io/FakeType
|
||||
listeners:
|
||||
- name: default
|
||||
hostname: "*.domain.example"
|
||||
port: 80
|
||||
protocol: HTTP
|
||||
#---
|
||||
# Higress 仅支持 addresses type 为 Hostname
|
||||
#apiVersion: gateway.networking.k8s.io/v1alpha2
|
||||
#kind: Gateway
|
||||
#metadata:
|
||||
# name: invalid-gateway-address
|
||||
# namespace: invalid-gateway-address
|
||||
#spec:
|
||||
# gatewayClassName: higress
|
||||
# addresses:
|
||||
# - value: 1.2.3.4
|
||||
# type: istio.io/FakeType
|
||||
# listeners:
|
||||
# - name: default
|
||||
# hostname: "*.domain.example"
|
||||
# port: 80
|
||||
# protocol: HTTP
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1alpha2
|
||||
kind: Gateway
|
||||
|
||||
@@ -53,25 +53,6 @@ spec:
|
||||
protocol: HTTP
|
||||
---
|
||||
apiVersion: networking.istio.io/v1alpha3
|
||||
kind: Gateway
|
||||
metadata:
|
||||
annotations:
|
||||
internal.istio.io/parents: Gateway/invalid-gateway-address/default.invalid-gateway-address
|
||||
creationTimestamp: null
|
||||
name: invalid-gateway-address-istio-autogenerated-k8s-gateway-default
|
||||
namespace: invalid-gateway-address
|
||||
spec:
|
||||
selector:
|
||||
higress: higress-system-higress-gateway
|
||||
servers:
|
||||
- hosts:
|
||||
- invalid-gateway-address/*.domain.example
|
||||
port:
|
||||
name: default
|
||||
number: 80
|
||||
protocol: HTTP
|
||||
---
|
||||
apiVersion: networking.istio.io/v1alpha3
|
||||
kind: VirtualService
|
||||
metadata:
|
||||
annotations:
|
||||
|
||||
Reference in New Issue
Block a user