-
Notifications
You must be signed in to change notification settings - Fork 2
feat: support ingress translator #78
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 4 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
3f18a63
feat: support ingress translator
ronething a2f6e64
feat: add translator
ronething 822d167
fix: r
ronething 6b5add7
fix: r
ronething cb8a115
fix: r
ronething 836c681
Merge remote-tracking branch 'origin/release-v2-dev' into feat/ingres…
ronething 2010ba8
fix: r
ronething d7ab910
fix: r
ronething 5e02d1b
Merge remote-tracking branch 'origin/release-v2-dev' into feat/ingres…
ronething 5edce5b
fix: review
ronething 31a5dca
fix: r
ronething 59e65c9
Update internal/provider/adc/translator/ingress.go
ronething a7a4f4d
Merge remote-tracking branch 'origin/release-v2-dev' into feat/ingres…
ronething 25bd15f
Merge remote-tracking branch 'origin/feat/ingress_translator' into fe…
ronething File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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, servciePort *corev1.ServicePort) adctypes.UpstreamNodes { | ||
AlinsRan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| 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 servciePort != nil && *port.Name != servciePort.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 | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.