diff --git a/Makefile b/Makefile index 2d1280ed..dce3fac1 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,20 @@ else GOBIN=$(shell go env GOBIN) endif +GOOS ?= linux +GOARCH ?= amd64 + +ifeq ($(shell uname -s),Darwin) + GOOS = darwin +endif + +ifeq ($(shell uname -m),arm64) + GOARCH = arm64 +endif +ifeq ($(shell uname -m), aarch64) + GOARCH = arm64 +endif + # CONTAINER_TOOL defines the container tool to be used for building images. # Be aware that the target commands are only tested with Docker which is # scaffolded by default. However, you might want to replace it to use other @@ -142,7 +156,7 @@ pull-infra-images: .PHONY: build build: manifests generate fmt vet ## Build manager binary. - CGO_ENABLED=0 go build -o bin/api7-ingress-controller -ldflags $(GO_LDFLAGS) cmd/main.go + GOOS=$(GOOS) GOARCH=$(GOARCH) CGO_ENABLED=0 go build -o bin/api7-ingress-controller -ldflags $(GO_LDFLAGS) cmd/main.go linux-build: GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o bin/api7-ingress-controller -ldflags $(GO_LDFLAGS) cmd/main.go @@ -158,7 +172,7 @@ run: manifests generate fmt vet ## Run a controller from your host. # (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it. # More info: https://docs.docker.com/develop/develop-images/build_enhancements/ .PHONY: docker-build -docker-build: build ## Build docker image with the manager. +docker-build: set-e2e-goos build ## Build docker image with the manager. $(CONTAINER_TOOL) build -t ${IMG} -f Dockerfile . .PHONY: docker-push @@ -268,6 +282,11 @@ gofmt: ## Apply go fmt @go fmt ./... .PHONY: gofmt +set-e2e-goos: + $(eval GOOS=linux) + @echo "e2e GOOS: $(GOOS)" +.PHONY: set-e2e-goos + # go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist # $1 - target path with name of binary # $2 - package url which can be installed diff --git a/api/adc/types.go b/api/adc/types.go index a5577efa..095bc5cf 100644 --- a/api/adc/types.go +++ b/api/adc/types.go @@ -148,7 +148,7 @@ type Upstream struct { HashOn string `json:"hash_on,omitempty" yaml:"hash_on,omitempty"` Key string `json:"key,omitempty" yaml:"key,omitempty"` - Nodes UpstreamNodes `json:"nodes,omitempty" yaml:"nodes,omitempty"` + Nodes UpstreamNodes `json:"nodes" yaml:"nodes"` PassHost *PassHost `json:"pass_host,omitempty" yaml:"pass_host,omitempty"` Retries *int64 `json:"retries,omitempty" yaml:"retries,omitempty"` RetryTimeout *float64 `json:"retry_timeout,omitempty" yaml:"retry_timeout,omitempty"` diff --git a/config/samples/config.yaml b/config/samples/config.yaml index 2543d85f..3bfe2f3a 100644 --- a/config/samples/config.yaml +++ b/config/samples/config.yaml @@ -4,14 +4,11 @@ log_level: "info" # The log level of the API7 Ingr controller_name: gateway.api7.io/api7-ingress-controller # The controller name of the API7 Ingress Controller, # which is used to identify the controller in the GatewayClass. # The default value is "gateway.api7.io/api7-ingress-controller". - leader_election_id: "api7-ingress-controller-leader" # The leader election ID for the API7 Ingress Controller. # The default value is "api7-ingress-controller-leader". - ingress_class: api7 # The ingress class name of the API7 Ingress Controller. ingress_publish_service: "" # The service name of the ingress publish service. ingress_status_address: [] # The status address of the ingress. - gateway_configs: # The configuration of the API7 Gateway. - name: api7 # The name of the Gateway in the Gateway API. control_plane: diff --git a/internal/controller/ingress_controller.go b/internal/controller/ingress_controller.go index d5fc617f..2e1636ee 100644 --- a/internal/controller/ingress_controller.go +++ b/internal/controller/ingress_controller.go @@ -10,6 +10,7 @@ import ( "github.com/api7/api7-ingress-controller/internal/provider" "github.com/api7/gopkg/pkg/log" "github.com/go-logr/logr" + "go.uber.org/zap" corev1 "k8s.io/api/core/v1" discoveryv1 "k8s.io/api/discovery/v1" networkingv1 "k8s.io/api/networking/v1" @@ -41,7 +42,12 @@ func (r *IngressReconciler) SetupWithManager(mgr ctrl.Manager) error { predicate.NewPredicateFuncs(r.checkIngressClass), ), ). - WithEventFilter(predicate.GenerationChangedPredicate{}). + WithEventFilter( + predicate.Or( + predicate.GenerationChangedPredicate{}, + predicate.AnnotationChangedPredicate{}, + ), + ). Watches( &networkingv1.IngressClass{}, handler.EnqueueRequestsFromMapFunc(r.listIngressForIngressClass), @@ -134,16 +140,19 @@ func (r *IngressReconciler) checkIngressClass(obj client.Object) bool { // find the ingress class that is marked as default for _, ic := range ingressClassList.Items { if IsDefaultIngressClass(&ic) && matchesController(ic.Spec.Controller) { + log.Debugw("match the default ingress class") return true } } + log.Debugw("no default ingress class found") return false } configuredClass := config.GetIngressClass() // if the ingress class name matches the configured ingress class name, return true if *ingress.Spec.IngressClassName == configuredClass { + log.Debugw("match the configured ingress class name") return true } @@ -168,25 +177,55 @@ func (r *IngressReconciler) matchesIngressController(obj client.Object) bool { } // listIngressForIngressClass list all ingresses that use a specific ingress class -func (r *IngressReconciler) listIngressForIngressClass(ctx context.Context, ingressClass client.Object) []reconcile.Request { - ingressList := &networkingv1.IngressList{} - if err := r.List(ctx, ingressList, client.MatchingFields{ - indexer.IngressClassRef: ingressClass.GetName(), - }); err != nil { - r.Log.Error(err, "failed to list ingresses for ingress class", "ingressclass", ingressClass.GetName()) +func (r *IngressReconciler) listIngressForIngressClass(ctx context.Context, obj client.Object) []reconcile.Request { + ingressClass, ok := obj.(*networkingv1.IngressClass) + if !ok { + r.Log.Error(fmt.Errorf("unexpected object type"), "failed to convert object to IngressClass") return nil } - requests := make([]reconcile.Request, 0, len(ingressList.Items)) - for _, ingress := range ingressList.Items { - requests = append(requests, reconcile.Request{ - NamespacedName: client.ObjectKey{ - Namespace: ingress.Namespace, - Name: ingress.Name, - }, - }) + // check if the ingress class is the default ingress class + if IsDefaultIngressClass(ingressClass) { + ingressList := &networkingv1.IngressList{} + if err := r.List(ctx, ingressList); err != nil { + r.Log.Error(err, "failed to list ingresses for ingress class", "ingressclass", ingressClass.GetName()) + return nil + } + + requests := make([]reconcile.Request, 0, len(ingressList.Items)) + for _, ingress := range ingressList.Items { + if ingress.Spec.IngressClassName == nil || *ingress.Spec.IngressClassName == "" || + *ingress.Spec.IngressClassName == ingressClass.GetName() { + requests = append(requests, reconcile.Request{ + NamespacedName: client.ObjectKey{ + Namespace: ingress.Namespace, + Name: ingress.Name, + }, + }) + } + } + return requests + } else { + ingressList := &networkingv1.IngressList{} + if err := r.List(ctx, ingressList, client.MatchingFields{ + indexer.IngressClassRef: ingressClass.GetName(), + }); err != nil { + r.Log.Error(err, "failed to list ingresses for ingress class", "ingressclass", ingressClass.GetName()) + return nil + } + + requests := make([]reconcile.Request, 0, len(ingressList.Items)) + for _, ingress := range ingressList.Items { + requests = append(requests, reconcile.Request{ + NamespacedName: client.ObjectKey{ + Namespace: ingress.Namespace, + Name: ingress.Name, + }, + }) + } + + return requests } - return requests } // listIngressesByService list all ingresses that use a specific service @@ -210,12 +249,14 @@ func (r *IngressReconciler) listIngressesByService(ctx context.Context, obj clie requests := make([]reconcile.Request, 0, len(ingressList.Items)) for _, ingress := range ingressList.Items { - requests = append(requests, reconcile.Request{ - NamespacedName: client.ObjectKey{ - Namespace: ingress.Namespace, - Name: ingress.Name, - }, - }) + if r.checkIngressClass(&ingress) { + requests = append(requests, reconcile.Request{ + NamespacedName: client.ObjectKey{ + Namespace: ingress.Namespace, + Name: ingress.Name, + }, + }) + } } return requests } @@ -241,12 +282,14 @@ func (r *IngressReconciler) listIngressesBySecret(ctx context.Context, obj clien requests := make([]reconcile.Request, 0, len(ingressList.Items)) for _, ingress := range ingressList.Items { - requests = append(requests, reconcile.Request{ - NamespacedName: client.ObjectKey{ - Namespace: ingress.Namespace, - Name: ingress.Name, - }, - }) + if r.checkIngressClass(&ingress) { + requests = append(requests, reconcile.Request{ + NamespacedName: client.ObjectKey{ + Namespace: ingress.Namespace, + Name: ingress.Name, + }, + }) + } } return requests } @@ -267,6 +310,11 @@ func (r *IngressReconciler) processTLS(ctx context.Context, tctx *provider.Trans return err } + if secret.Data == nil { + log.Warnw("secret data is nil", zap.String("secret", secret.Namespace+"/"+secret.Name)) + continue + } + // add the secret to the translate context tctx.Secrets[types.NamespacedName{Namespace: ingress.Namespace, Name: tls.SecretName}] = &secret } @@ -283,19 +331,16 @@ func (r *IngressReconciler) processBackends(ctx context.Context, tctx *provider. if rule.HTTP == nil { continue } - for _, path := range rule.HTTP.Paths { if path.Backend.Service == nil { continue } - service := path.Backend.Service if err := r.processBackendService(ctx, tctx, ingress.Namespace, service); err != nil { terr = err } } } - return terr } @@ -356,6 +401,11 @@ func (r *IngressReconciler) processBackendService(ctx context.Context, tctx *pro Name: backendService.Name, }] = endpointSliceList.Items + tctx.Services[client.ObjectKey{ + Namespace: namespace, + Name: backendService.Name, + }] = &service + return nil } @@ -383,6 +433,7 @@ func (r *IngressReconciler) updateStatus(ctx context.Context, ingress *networkin if err != nil { return fmt.Errorf("invalid ingress-publish-service format: %s, expected format: namespace/name", publishService) } + // if the namespace is not specified, use the ingress namespace if namespace == "" { namespace = ingress.Namespace } diff --git a/internal/provider/adc/adc.go b/internal/provider/adc/adc.go index ddf5779e..bec7ab17 100644 --- a/internal/provider/adc/adc.go +++ b/internal/provider/adc/adc.go @@ -101,6 +101,9 @@ func (d *adcClient) Delete(ctx context.Context, obj client.Object) error { labels = label.GenLabel(obj) case *gatewayv1.Gateway: // delete all resources + case *networkingv1.Ingress: + resourceTypes = append(resourceTypes, "service", "ssl") + labels = label.GenLabel(obj) case *v1alpha1.Consumer: resourceTypes = append(resourceTypes, "consumer") labels = label.GenLabel(obj) diff --git a/internal/provider/adc/translator/ingress.go b/internal/provider/adc/translator/ingress.go index 38b3d5e7..19cc0903 100644 --- a/internal/provider/adc/translator/ingress.go +++ b/internal/provider/adc/translator/ingress.go @@ -1,10 +1,220 @@ package translator import ( + "fmt" + "strings" + + adctypes "github.com/api7/api7-ingress-controller/api/adc" + "github.com/api7/api7-ingress-controller/internal/controller/label" + "github.com/api7/api7-ingress-controller/internal/id" "github.com/api7/api7-ingress-controller/internal/provider" + corev1 "k8s.io/api/core/v1" + discoveryv1 "k8s.io/api/discovery/v1" networkingv1 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/types" ) +func (t *Translator) translateIngressTLS(ingressTLS *networkingv1.IngressTLS, secret *corev1.Secret, labels map[string]string) (*adctypes.SSL, error) { + // extract the key pair from the secret + cert, key, err := extractKeyPair(secret, true) + if err != nil { + return nil, err + } + + hosts := ingressTLS.Hosts + if len(hosts) == 0 { + certHosts, err := extractHost(cert) + if err != nil { + return nil, err + } + hosts = append(hosts, certHosts...) + } + if len(hosts) == 0 { + return nil, fmt.Errorf("no hosts found in ingress TLS") + } + + ssl := &adctypes.SSL{ + Metadata: adctypes.Metadata{ + Labels: labels, + }, + Certificates: []adctypes.Certificate{ + { + Certificate: string(cert), + Key: string(key), + }, + }, + Snis: hosts, + } + ssl.ID = id.GenID(string(cert)) + + return ssl, nil +} + func (t *Translator) TranslateIngress(tctx *provider.TranslateContext, obj *networkingv1.Ingress) (*TranslateResult, error) { - return nil, nil + result := &TranslateResult{} + + labels := label.GenLabel(obj) + + // handle TLS configuration, convert to SSL objects + for _, tls := range obj.Spec.TLS { + if tls.SecretName == "" { + continue + } + secret := tctx.Secrets[types.NamespacedName{ + Namespace: obj.Namespace, + Name: tls.SecretName, + }] + if secret == nil { + continue + } + ssl, err := t.translateIngressTLS(&tls, secret, labels) + if err != nil { + return nil, err + } + + result.SSL = append(result.SSL, ssl) + } + + // process Ingress rules, convert to Service and Route objects + for i, rule := range obj.Spec.Rules { + // extract hostnames + var hosts []string + if rule.Host != "" { + hosts = append(hosts, rule.Host) + } + // if there is no HTTP path, skip + if rule.HTTP == nil { + continue + } + + // create a service for each path + for j, path := range rule.HTTP.Paths { + if path.Backend.Service == nil { + continue + } + + service := adctypes.NewDefaultService() + service.Labels = labels + service.Name = adctypes.ComposeServiceNameWithRule(obj.Namespace, obj.Name, fmt.Sprintf("%d-%d", i, j)) + service.ID = id.GenID(service.Name) + service.Hosts = hosts + + // create an upstream + upstream := adctypes.NewDefaultUpstream() + + // get the EndpointSlice of the backend service + backendService := path.Backend.Service + endpointSlices := tctx.EndpointSlices[types.NamespacedName{ + Namespace: obj.Namespace, + Name: backendService.Name, + }] + + // get the service port configuration + var servicePort int32 = 0 + var servicePortName string + if backendService.Port.Number != 0 { + servicePort = backendService.Port.Number + } else if backendService.Port.Name != "" { + servicePortName = backendService.Port.Name + } + + getService := tctx.Services[types.NamespacedName{ + Namespace: obj.Namespace, + Name: backendService.Name, + }] + if getService == nil { + continue + } + + var getServicePort *corev1.ServicePort + for _, port := range getService.Spec.Ports { + port := port + if servicePort > 0 && port.Port == servicePort { + getServicePort = &port + break + } + if servicePortName != "" && port.Name == servicePortName { + getServicePort = &port + break + } + } + + // convert the EndpointSlice to upstream nodes + if len(endpointSlices) > 0 { + upstream.Nodes = t.translateEndpointSliceForIngress(1, endpointSlices, getServicePort) + } + + // if there is no upstream node, create a placeholder node + if len(upstream.Nodes) == 0 { + upstream.Nodes = adctypes.UpstreamNodes{} + } + + service.Upstream = upstream + + // create a route + route := adctypes.NewDefaultRoute() + route.Name = adctypes.ComposeRouteName(obj.Namespace, obj.Name, fmt.Sprintf("%d-%d", i, j)) + route.ID = id.GenID(route.Name) + route.Labels = labels + + uris := []string{path.Path} + if path.PathType != nil { + if *path.PathType == networkingv1.PathTypePrefix { + // As per the specification of Ingress path matching rule: + // if the last element of the path is a substring of the + // last element in request path, it is not a match, e.g. /foo/bar + // matches /foo/bar/baz, but does not match /foo/barbaz. + // While in APISIX, /foo/bar matches both /foo/bar/baz and + // /foo/barbaz. + // In order to be conformant with Ingress specification, here + // we create two paths here, the first is the path itself + // (exact match), the other is path + "/*" (prefix match). + prefix := path.Path + if strings.HasSuffix(prefix, "/") { + prefix += "*" + } else { + prefix += "/*" + } + uris = append(uris, prefix) + } else if *path.PathType == networkingv1.PathTypeImplementationSpecific { + uris = []string{"/*"} + } + } + route.Uris = uris + + service.Routes = []*adctypes.Route{route} + result.Services = append(result.Services, service) + } + } + + return result, nil +} + +// translateEndpointSliceForIngress create upstream nodes from EndpointSlice +func (t *Translator) translateEndpointSliceForIngress(weight int, endpointSlices []discoveryv1.EndpointSlice, servicePort *corev1.ServicePort) adctypes.UpstreamNodes { + var nodes adctypes.UpstreamNodes + if len(endpointSlices) == 0 { + return nodes + } + + for _, endpointSlice := range endpointSlices { + for _, port := range endpointSlice.Ports { + // if the port number is specified, only use the matching port + if servicePort != nil && port.Name != nil && *port.Name != servicePort.Name { + continue + } + for _, endpoint := range endpointSlice.Endpoints { + for _, addr := range endpoint.Addresses { + node := adctypes.UpstreamNode{ + Host: addr, + Port: int(*port.Port), + Weight: weight, + } + nodes = append(nodes, node) + } + } + } + } + + return nodes } diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 1aca4a18..a57675f2 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -25,6 +25,7 @@ type TranslateContext struct { EndpointSlices map[types.NamespacedName][]discoveryv1.EndpointSlice Secrets map[types.NamespacedName]*corev1.Secret PluginConfigs map[types.NamespacedName]*v1alpha1.PluginConfig + Services map[types.NamespacedName]*corev1.Service } func NewDefaultTranslateContext() *TranslateContext { @@ -32,5 +33,6 @@ func NewDefaultTranslateContext() *TranslateContext { EndpointSlices: make(map[types.NamespacedName][]discoveryv1.EndpointSlice), Secrets: make(map[types.NamespacedName]*corev1.Secret), PluginConfigs: make(map[types.NamespacedName]*v1alpha1.PluginConfig), + Services: make(map[types.NamespacedName]*corev1.Service), } } diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index 8cff31d2..309db981 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -27,6 +27,7 @@ import ( _ "github.com/api7/api7-ingress-controller/test/e2e/crds" "github.com/api7/api7-ingress-controller/test/e2e/framework" _ "github.com/api7/api7-ingress-controller/test/e2e/gatewayapi" + _ "github.com/api7/api7-ingress-controller/test/e2e/ingress" ) // Run e2e tests using the Ginkgo runner. diff --git a/test/e2e/framework/consts.go b/test/e2e/framework/consts.go index 68e8aa68..33fd3361 100644 --- a/test/e2e/framework/consts.go +++ b/test/e2e/framework/consts.go @@ -31,6 +31,13 @@ var ( DefaultGatewayGroupKeyPrefix = fmt.Sprintf("/gateway_groups/%s", "default") ) +var ( + //go:embed manifests/cert.pem + TestServerCert string + //go:embed manifests/key.pem + TestServerKey string +) + const ( TestCACert = `-----BEGIN CERTIFICATE----- MIIDdzCCAl+gAwIBAgIUBB5PHXyymeboPDVdYeYihYnm5XIwDQYJKoZIhvcNAQEL diff --git a/test/e2e/framework/manifests/cert.pem b/test/e2e/framework/manifests/cert.pem new file mode 100644 index 00000000..2dbe89dd --- /dev/null +++ b/test/e2e/framework/manifests/cert.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFcjCCA1qgAwIBAgIJALDqPppBVXQ3MA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNV +BAYTAkNOMRAwDgYDVQQIDAdKaWFuZ3N1MQ8wDQYDVQQHDAZTdXpob3UxEDAOBgNV +BAoMB2FwaTcuYWkxEDAOBgNVBAsMB2FwaTcuYWkxDzANBgNVBAMMBmp3LmNvbTAg +Fw0yMTA0MDkwNzEyMDBaGA8yMDUxMDQwMjA3MTIwMFowZTELMAkGA1UEBhMCQ04x +EDAOBgNVBAgMB0ppYW5nc3UxDzANBgNVBAcMBlN1emhvdTEQMA4GA1UECgwHYXBp +Ny5haTEQMA4GA1UECwwHYXBpNy5haTEPMA0GA1UEAwwGancuY29tMIICIjANBgkq +hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuEPPnUMlSw41CTdxUNxkQ4gAZ7cPotwY +Y6sVLGtWoR8gKFSZImQIor3UF+8HhN/ZOFRv5tSeeQ/MTE72cs2T5mp6eRU8OrSV +0npk/TpZfaCx7zobsfXB4YU1NZcVWKthFF5X8p//l5gxUMU8V4a01P0aDTmZ67wG +3Fhr0AC067GVYvdwp1yRt6TUUk8ha7JsiySchUIFhX5QMWmrSNhc1bDnHetejMFl +itFFPZkeYG89O/7Ca1K3ca/VVu+/IJ4h7fbF3rt4uP182cJdHl1L94dQSKCJukaW +v+xauWnm4hxOzBK7ImpYB/2eP2D33tmuCLeSv4S+bTG1l7hIN9C/xrYPzfun415h +M2jMK69aAkQL71xa+66kVxioJyNYogYz3ss5ruzDL8K/7bkdO0Zzqldd2+j8lkTl +X4csA+aMHF3v/U7hL/4Wdwi8ziwToRMq9KK9vuh+mPgcdtFGFml+sU+NQfJNm/BN +7fRMZKDIHLacSPE0GUkfW+x3dXOP2lWSZe/iOBZ0NOGNdrOnxTRTr7IH7DYU8aXF +w2GqfAFEQbD4wazCh1AI8lkZr6mPGB1q+HnF2IF7kkgXBHtI5U2KErgcX5BirIVe +v0Yg/OxbbymeTh/hNCcY1kJ1YUCbm9U3U6ZV+d8lj7dQHtugcAzWxSTwpBLVUPrO +eFuhSMLVblUCAwEAAaMjMCEwHwYDVR0RBBgwFoIIYXBpNi5jb22CCiouYXBpNi5j +b20wDQYJKoZIhvcNAQELBQADggIBAFgeSuMPpriYUrQByO3taiY1+56s+KoAmsyA +LH15n2+thAgorusX2g1Zd7UNBHBtVO8c+ihpQb+2PnmgTTGT70ndpRbV5F6124Mt +Hui/X0kjm76RYd1QKN1VFp0Zo+JVdRa+VhDsXWjO0VetINmFhNINFEJctyeHB8oi +aaDL0wZrevHh47hBqtnrmLl+QVG34aLBRhZ5953leiNvXHUJNaT0nLgf0j9p4esS +b2bx9uP4pFI1T9wcv/TE3K0rQbu/uqGY6MgznXHyi4qIK/I+WCa3fF2UZ5P/5EUM +k2ptQneYkLLUVwwmj8C04bYhYe7Z6jkYYp17ojxIP+ejOY1eiE8SYKNjKinmphrM +5aceqIyfbE4TPqvicNzIggA4yEMPbTA0PC/wqbCf61oMc15hwacdeIaQGiwsM+pf +DTk+GBxp3megx/+0XwTQbguleTlHnaaES+ma0vbl6a1rUK0YAUDUrcfFLv6EFtGD +6EHxFf7gH9sTfc2RiGhKxUhRbyEree+6INAvXy+AymVYmQmKuAFqkDQJ+09bTfm8 +bDs+00FijzHFBvC8cIhNffj0qqiv35g+9FTwnE9qpunlrtKG/sMgEXX6m8kL1YQ8 +m5DPGhyEZyt5Js2kzzo8TyINPKmrqriYuiD4p4EH13eSRs3ayanQh6ckC7lb+WXq +3IrSc5hO +-----END CERTIFICATE----- diff --git a/test/e2e/framework/manifests/key.pem b/test/e2e/framework/manifests/key.pem new file mode 100644 index 00000000..b70cfbbf --- /dev/null +++ b/test/e2e/framework/manifests/key.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/test/e2e/gatewayapi/gateway.go b/test/e2e/gatewayapi/gateway.go index cee455dc..6320e5c9 100644 --- a/test/e2e/gatewayapi/gateway.go +++ b/test/e2e/gatewayapi/gateway.go @@ -3,101 +3,22 @@ package gatewayapi import ( "context" "fmt" + "strings" "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/stretchr/testify/assert" + "github.com/api7/api7-ingress-controller/test/e2e/framework" "github.com/api7/api7-ingress-controller/test/e2e/scaffold" ) const _secretName = "test-apisix-tls" -var Cert = `-----BEGIN CERTIFICATE----- -MIIFcjCCA1qgAwIBAgIJALDqPppBVXQ3MA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNV -BAYTAkNOMRAwDgYDVQQIDAdKaWFuZ3N1MQ8wDQYDVQQHDAZTdXpob3UxEDAOBgNV -BAoMB2FwaTcuYWkxEDAOBgNVBAsMB2FwaTcuYWkxDzANBgNVBAMMBmp3LmNvbTAg -Fw0yMTA0MDkwNzEyMDBaGA8yMDUxMDQwMjA3MTIwMFowZTELMAkGA1UEBhMCQ04x -EDAOBgNVBAgMB0ppYW5nc3UxDzANBgNVBAcMBlN1emhvdTEQMA4GA1UECgwHYXBp -Ny5haTEQMA4GA1UECwwHYXBpNy5haTEPMA0GA1UEAwwGancuY29tMIICIjANBgkq -hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuEPPnUMlSw41CTdxUNxkQ4gAZ7cPotwY -Y6sVLGtWoR8gKFSZImQIor3UF+8HhN/ZOFRv5tSeeQ/MTE72cs2T5mp6eRU8OrSV -0npk/TpZfaCx7zobsfXB4YU1NZcVWKthFF5X8p//l5gxUMU8V4a01P0aDTmZ67wG -3Fhr0AC067GVYvdwp1yRt6TUUk8ha7JsiySchUIFhX5QMWmrSNhc1bDnHetejMFl -itFFPZkeYG89O/7Ca1K3ca/VVu+/IJ4h7fbF3rt4uP182cJdHl1L94dQSKCJukaW -v+xauWnm4hxOzBK7ImpYB/2eP2D33tmuCLeSv4S+bTG1l7hIN9C/xrYPzfun415h -M2jMK69aAkQL71xa+66kVxioJyNYogYz3ss5ruzDL8K/7bkdO0Zzqldd2+j8lkTl -X4csA+aMHF3v/U7hL/4Wdwi8ziwToRMq9KK9vuh+mPgcdtFGFml+sU+NQfJNm/BN -7fRMZKDIHLacSPE0GUkfW+x3dXOP2lWSZe/iOBZ0NOGNdrOnxTRTr7IH7DYU8aXF -w2GqfAFEQbD4wazCh1AI8lkZr6mPGB1q+HnF2IF7kkgXBHtI5U2KErgcX5BirIVe -v0Yg/OxbbymeTh/hNCcY1kJ1YUCbm9U3U6ZV+d8lj7dQHtugcAzWxSTwpBLVUPrO -eFuhSMLVblUCAwEAAaMjMCEwHwYDVR0RBBgwFoIIYXBpNi5jb22CCiouYXBpNi5j -b20wDQYJKoZIhvcNAQELBQADggIBAFgeSuMPpriYUrQByO3taiY1+56s+KoAmsyA -LH15n2+thAgorusX2g1Zd7UNBHBtVO8c+ihpQb+2PnmgTTGT70ndpRbV5F6124Mt -Hui/X0kjm76RYd1QKN1VFp0Zo+JVdRa+VhDsXWjO0VetINmFhNINFEJctyeHB8oi -aaDL0wZrevHh47hBqtnrmLl+QVG34aLBRhZ5953leiNvXHUJNaT0nLgf0j9p4esS -b2bx9uP4pFI1T9wcv/TE3K0rQbu/uqGY6MgznXHyi4qIK/I+WCa3fF2UZ5P/5EUM -k2ptQneYkLLUVwwmj8C04bYhYe7Z6jkYYp17ojxIP+ejOY1eiE8SYKNjKinmphrM -5aceqIyfbE4TPqvicNzIggA4yEMPbTA0PC/wqbCf61oMc15hwacdeIaQGiwsM+pf -DTk+GBxp3megx/+0XwTQbguleTlHnaaES+ma0vbl6a1rUK0YAUDUrcfFLv6EFtGD -6EHxFf7gH9sTfc2RiGhKxUhRbyEree+6INAvXy+AymVYmQmKuAFqkDQJ+09bTfm8 -bDs+00FijzHFBvC8cIhNffj0qqiv35g+9FTwnE9qpunlrtKG/sMgEXX6m8kL1YQ8 -m5DPGhyEZyt5Js2kzzo8TyINPKmrqriYuiD4p4EH13eSRs3ayanQh6ckC7lb+WXq -3IrSc5hO ------END CERTIFICATE-----` +var Cert = strings.TrimSpace(framework.TestServerCert) -var Key = `-----BEGIN RSA PRIVATE KEY----- -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 ------END RSA PRIVATE KEY-----` +var Key = strings.TrimSpace(framework.TestServerKey) func createSecret(s *scaffold.Scaffold, secretName string) { err := s.NewKubeTlsSecret(secretName, Cert, Key) diff --git a/test/e2e/ingress/ingress.go b/test/e2e/ingress/ingress.go new file mode 100644 index 00000000..c5ebb119 --- /dev/null +++ b/test/e2e/ingress/ingress.go @@ -0,0 +1,191 @@ +package ingress + +import ( + "context" + "fmt" + "strings" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/stretchr/testify/assert" + + "github.com/api7/api7-ingress-controller/test/e2e/framework" + "github.com/api7/api7-ingress-controller/test/e2e/scaffold" +) + +const _secretName = "test-ingress-tls" + +var Cert = strings.TrimSpace(framework.TestServerCert) + +var Key = strings.TrimSpace(framework.TestServerKey) + +func createSecret(s *scaffold.Scaffold, secretName string) { + err := s.NewKubeTlsSecret(secretName, Cert, Key) + assert.Nil(GinkgoT(), err, "create secret error") +} + +var _ = Describe("Test Ingress", func() { + s := scaffold.NewScaffold(&scaffold.Options{ + ControllerName: "gateway.api7.io/api7-ingress-controller", + }) + + Context("Basic Ingress Functionality", func() { + var defaultIngressClass = ` +apiVersion: networking.k8s.io/v1 +kind: IngressClass +metadata: + name: api7 +spec: + controller: "gateway.api7.io/api7-ingress-controller" +` + + var defaultIngress = ` +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: api7-ingress +spec: + ingressClassName: api7 + rules: + - host: example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: httpbin-service-e2e-test + port: + number: 80 +` + + It("Create Ingress", func() { + By("create IngressClass") + err := s.CreateResourceFromStringWithNamespace(defaultIngressClass, "") + Expect(err).NotTo(HaveOccurred(), "creating IngressClass") + time.Sleep(5 * time.Second) + + By("create Ingress") + err = s.CreateResourceFromString(defaultIngress) + Expect(err).NotTo(HaveOccurred(), "creating Ingress") + time.Sleep(5 * time.Second) + + By("verify HTTP request") + s.NewAPISIXClient(). + GET("/get"). + WithHost("example.com"). + Expect(). + Status(200) + }) + }) + + Context("Ingress TLS", func() { + It("Check if SSL resource was created", func() { + secretName := _secretName + host := "api6.com" + createSecret(s, secretName) + + var defaultIngressClass = ` +apiVersion: networking.k8s.io/v1 +kind: IngressClass +metadata: + name: api7 +spec: + controller: "gateway.api7.io/api7-ingress-controller" +` + + var tlsIngress = fmt.Sprintf(` +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: api7-ingress-tls +spec: + ingressClassName: api7 + tls: + - hosts: + - %s + secretName: %s + rules: + - host: %s + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: httpbin-service-e2e-test + port: + number: 80 +`, host, secretName, host) + + By("create IngressClass") + err := s.CreateResourceFromStringWithNamespace(defaultIngressClass, "") + Expect(err).NotTo(HaveOccurred(), "creating IngressClass") + time.Sleep(5 * time.Second) + + By("create Ingress with TLS") + err = s.CreateResourceFromString(tlsIngress) + Expect(err).NotTo(HaveOccurred(), "creating Ingress with TLS") + time.Sleep(5 * time.Second) + + By("check TLS configuration") + tls, err := s.DefaultDataplaneResource().SSL().List(context.Background()) + assert.Nil(GinkgoT(), err, "list tls error") + assert.Len(GinkgoT(), tls, 1, "tls number not expect") + assert.Equal(GinkgoT(), Cert, tls[0].Cert, "tls cert not expect") + assert.ElementsMatch(GinkgoT(), []string{host}, tls[0].Snis) + }) + }) + + Context("IngressClass Selection", func() { + var defaultIngressClass = ` +apiVersion: networking.k8s.io/v1 +kind: IngressClass +metadata: + name: api7-default + annotations: + ingressclass.kubernetes.io/is-default-class: "true" +spec: + controller: "gateway.api7.io/api7-ingress-controller" +` + + var defaultIngress = ` +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: api7-ingress-default +spec: + rules: + - host: default.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: httpbin-service-e2e-test + port: + number: 80 +` + + It("Test IngressClass Selection", func() { + By("create Default IngressClass") + err := s.CreateResourceFromStringWithNamespace(defaultIngressClass, "") + Expect(err).NotTo(HaveOccurred(), "creating Default IngressClass") + time.Sleep(5 * time.Second) + + By("create Ingress without IngressClass") + err = s.CreateResourceFromString(defaultIngress) + Expect(err).NotTo(HaveOccurred(), "creating Ingress without IngressClass") + time.Sleep(5 * time.Second) + + By("verify default ingress") + s.NewAPISIXClient(). + GET("/get"). + WithHost("default.example.com"). + Expect(). + Status(200) + }) + }) +})