github.com/cilium/cilium@v1.16.2/operator/pkg/model/translation/ingress/dedicated_ingress.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package ingress 5 6 import ( 7 "fmt" 8 9 corev1 "k8s.io/api/core/v1" 10 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 "k8s.io/apimachinery/pkg/types" 12 13 "github.com/cilium/cilium/operator/pkg/model" 14 "github.com/cilium/cilium/operator/pkg/model/translation" 15 ciliumv2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2" 16 slim_networkingv1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/api/networking/v1" 17 "github.com/cilium/cilium/pkg/logging/logfields" 18 ) 19 20 const ( 21 ciliumIngressPrefix = "cilium-ingress" 22 ciliumIngressLabelKey = "cilium.io/ingress" 23 ) 24 25 var _ translation.Translator = (*dedicatedIngressTranslator)(nil) 26 27 type dedicatedIngressTranslator struct { 28 cecTranslator translation.CECTranslator 29 hostNetworkEnabled bool 30 } 31 32 func NewDedicatedIngressTranslator(cecTranslator translation.CECTranslator, hostNetworkEnabled bool) *dedicatedIngressTranslator { 33 return &dedicatedIngressTranslator{ 34 cecTranslator: cecTranslator, 35 hostNetworkEnabled: hostNetworkEnabled, 36 } 37 } 38 39 func (d *dedicatedIngressTranslator) Translate(m *model.Model) (*ciliumv2.CiliumEnvoyConfig, *corev1.Service, *corev1.Endpoints, error) { 40 if m == nil || (len(m.HTTP) == 0 && len(m.TLSPassthrough) == 0) { 41 return nil, nil, nil, fmt.Errorf("model source can't be empty") 42 } 43 44 var name string 45 var namespace string 46 var sourceResource model.FullyQualifiedResource 47 var modelService *model.Service 48 var cecName string 49 var tlsOnly bool 50 51 if len(m.HTTP) == 0 { 52 name = fmt.Sprintf("%s-%s", ciliumIngressPrefix, m.TLSPassthrough[0].Sources[0].Name) 53 namespace = m.TLSPassthrough[0].Sources[0].Namespace 54 sourceResource = m.TLSPassthrough[0].Sources[0] 55 modelService = m.TLSPassthrough[0].Service 56 cecName = fmt.Sprintf("%s-%s-%s", ciliumIngressPrefix, namespace, m.TLSPassthrough[0].Sources[0].Name) 57 tlsOnly = true 58 } else { 59 name = fmt.Sprintf("%s-%s", ciliumIngressPrefix, m.HTTP[0].Sources[0].Name) 60 namespace = m.HTTP[0].Sources[0].Namespace 61 sourceResource = m.HTTP[0].Sources[0] 62 modelService = m.HTTP[0].Service 63 cecName = fmt.Sprintf("%s-%s-%s", ciliumIngressPrefix, namespace, m.HTTP[0].Sources[0].Name) 64 } 65 66 // The logic is same as what we have with default cecTranslator, but with a different model 67 // (i.e. the HTTP listeners are just belonged to one Ingress resource). 68 cec, err := d.cecTranslator.Translate(namespace, name, m) 69 if err != nil { 70 return nil, nil, nil, err 71 } 72 73 // Set the name to avoid any breaking change during upgrade. 74 cec.Name = cecName 75 76 dedicatedService := d.getService(sourceResource, modelService, tlsOnly) 77 78 return cec, dedicatedService, getEndpoints(sourceResource), err 79 } 80 81 func (d *dedicatedIngressTranslator) getService(resource model.FullyQualifiedResource, service *model.Service, tlsOnly bool) *corev1.Service { 82 serviceType := corev1.ServiceTypeLoadBalancer 83 clusterIP := "" 84 if d.hostNetworkEnabled { 85 serviceType = corev1.ServiceTypeClusterIP 86 } 87 88 var ports []corev1.ServicePort 89 if tlsOnly { 90 ports = []corev1.ServicePort{ 91 { 92 Name: "https", 93 Protocol: "TCP", 94 Port: 443, 95 }, 96 } 97 } else { 98 ports = []corev1.ServicePort{ 99 { 100 Name: "http", 101 Protocol: "TCP", 102 Port: 80, 103 }, 104 { 105 Name: "https", 106 Protocol: "TCP", 107 Port: 443, 108 }, 109 } 110 } 111 112 if service != nil { 113 switch service.Type { 114 case string(corev1.ServiceTypeNodePort): 115 serviceType = corev1.ServiceTypeNodePort 116 if service.InsecureNodePort != nil { 117 ports[0].NodePort = int32(*service.InsecureNodePort) 118 } 119 if service.SecureNodePort != nil { 120 ports[1].NodePort = int32(*service.SecureNodePort) 121 } 122 case string(corev1.ServiceTypeLoadBalancer): 123 // Do nothing as the port number is allocated by the cloud provider. 124 default: 125 log.WithField(logfields.ServiceType, service.Type). 126 Warn("only LoadBalancer and NodePort are supported. Defaulting to LoadBalancer") 127 } 128 } 129 130 return &corev1.Service{ 131 ObjectMeta: metav1.ObjectMeta{ 132 Name: fmt.Sprintf("%s-%s", ciliumIngressPrefix, resource.Name), 133 Namespace: resource.Namespace, 134 Labels: map[string]string{ciliumIngressLabelKey: "true"}, 135 OwnerReferences: []metav1.OwnerReference{ 136 { 137 APIVersion: slim_networkingv1.SchemeGroupVersion.String(), 138 Kind: "Ingress", 139 Name: resource.Name, 140 UID: types.UID(resource.UID), 141 Controller: model.AddressOf(true), 142 }, 143 }, 144 }, 145 Spec: corev1.ServiceSpec{ 146 Type: serviceType, 147 ClusterIP: clusterIP, 148 Ports: ports, 149 }, 150 } 151 } 152 153 func getEndpoints(resource model.FullyQualifiedResource) *corev1.Endpoints { 154 return &corev1.Endpoints{ 155 ObjectMeta: metav1.ObjectMeta{ 156 Name: fmt.Sprintf("%s-%s", ciliumIngressPrefix, resource.Name), 157 Namespace: resource.Namespace, 158 Labels: map[string]string{ciliumIngressLabelKey: "true"}, 159 OwnerReferences: []metav1.OwnerReference{ 160 { 161 APIVersion: slim_networkingv1.SchemeGroupVersion.String(), 162 Kind: "Ingress", 163 Name: resource.Name, 164 UID: types.UID(resource.UID), 165 Controller: model.AddressOf(true), 166 }, 167 }, 168 }, 169 Subsets: []corev1.EndpointSubset{ 170 { 171 // This dummy endpoint is required as agent refuses to push service entry 172 // to the lb map when the service has no backends. 173 // Related github issue https://github.com/cilium/cilium/issues/19262 174 Addresses: []corev1.EndpointAddress{{IP: "192.192.192.192"}}, // dummy 175 Ports: []corev1.EndpointPort{{Port: 9999}}, // dummy 176 }, 177 }, 178 } 179 }