pkg/providers/apisix/translation/apisix_upstream.go (118 lines of code) (raw):

// 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 ( "fmt" corev1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" "github.com/apache/apisix-ingress-controller/pkg/id" v2 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v2" "github.com/apache/apisix-ingress-controller/pkg/providers/translation" "github.com/apache/apisix-ingress-controller/pkg/providers/utils" "github.com/apache/apisix-ingress-controller/pkg/types" apisixv1 "github.com/apache/apisix-ingress-controller/pkg/types/apisix/v1" ) // generateUpstreamDeleteMark translates Upstream nodes with a loose way, only generate ID and Name for delete Event. func (t *translator) generateUpstreamDeleteMark(namespace, svcName, subset string, svcPort int32, resolveGranularity string) (*apisixv1.Upstream, error) { ups := &apisixv1.Upstream{} ups.Name = apisixv1.ComposeUpstreamName(namespace, svcName, subset, svcPort, resolveGranularity) ups.ID = id.GenID(ups.Name) return ups, nil } func (t *translator) translateService(namespace, svcName, subset, svcResolveGranularity, svcClusterIP string, svcPort int32) (*apisixv1.Upstream, error) { ups, err := t.TranslateService(namespace, svcName, subset, svcPort) if err != nil { return nil, err } if svcResolveGranularity == types.ResolveGranularity.Service { ups.Nodes = apisixv1.UpstreamNodes{ { Host: svcClusterIP, Port: int(svcPort), Weight: translation.DefaultWeight, }, } } ups.Name = apisixv1.ComposeUpstreamName(namespace, svcName, subset, svcPort, svcResolveGranularity) ups.ID = id.GenID(ups.Name) return ups, nil } func (t *translator) TranslateApisixUpstreamExternalNodes(au *v2.ApisixUpstream) ([]apisixv1.UpstreamNode, error) { var nodes []apisixv1.UpstreamNode for i, node := range au.Spec.ExternalNodes { if node.Type == v2.ExternalTypeDomain { weight := translation.DefaultWeight if node.Weight != nil { weight = *node.Weight } if !utils.MatchHostDef(node.Name) { return nil, fmt.Errorf("ApisixUpstream %s/%s ExternalNodes[%v]'s name %s as Domain must match lowercase RFC 1123 subdomain. "+ "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character", au.Namespace, au.Name, i, node.Name) } n := apisixv1.UpstreamNode{ Host: node.Name, Weight: weight, } if node.Port != nil { n.Port = *node.Port } else { n.Port = utils.SchemeToPort(au.Spec.Scheme) } nodes = append(nodes, n) } else if node.Type == v2.ExternalTypeService { svc, err := t.ServiceLister.Services(au.Namespace).Get(node.Name) if err != nil { // In theory, ApisixRoute now watches all service add event, a not found error is already handled if k8serrors.IsNotFound(err) { // TODO: Should retry return nil, err } return nil, err } if svc.Spec.Type != corev1.ServiceTypeExternalName { return nil, fmt.Errorf("ApisixUpstream %s/%s ExternalNodes[%v] must refers to a ExternalName service: %s", au.Namespace, au.Name, i, node.Name) } weight := translation.DefaultWeight if node.Weight != nil { weight = *node.Weight } n := apisixv1.UpstreamNode{ Host: svc.Spec.ExternalName, Weight: weight, } if node.Port != nil { n.Port = *node.Port } else { n.Port = utils.SchemeToPort(au.Spec.Scheme) } nodes = append(nodes, n) } } return nodes, nil } // TODO: Retry when ApisixUpstream/ExternalName service not found func (t *translator) translateExternalApisixUpstream(namespace, upstream string) (*apisixv1.Upstream, error) { multiVersioned, err := t.ApisixUpstreamLister.V2(namespace, upstream) if err != nil { if k8serrors.IsNotFound(err) { // TODO: Should retry return nil, err } return nil, err } au := multiVersioned.V2() if len(au.Spec.ExternalNodes) == 0 && au.Spec.Discovery == nil { // should do further resolve return nil, fmt.Errorf("%s/%s has empty ExternalNodes or Discovery configuration", namespace, upstream) } ups, err := t.TranslateUpstreamConfigV2(&au.Spec.ApisixUpstreamConfig) if err != nil { return nil, err } for k, v := range au.ObjectMeta.Labels { ups.Metadata.Labels[k] = v } ups.Name = apisixv1.ComposeExternalUpstreamName(namespace, upstream) ups.ID = id.GenID(ups.Name) // APISIX does not allow discovery_type and nodes to exist at the same time. // https://github.com/apache/apisix/blob/01b4b49eb2ba642b337f7a1fbe1894a77942910b/apisix/schema_def.lua#L501-L504 if len(au.Spec.ExternalNodes) != 0 { externalNodes, err := t.TranslateApisixUpstreamExternalNodes(au) if err != nil { return nil, err } ups.Nodes = append(ups.Nodes, externalNodes...) } return ups, nil }