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  }