github.com/verrazzano/verrazzano@v1.7.1/tools/oam-converter/pkg/services/destination.go (about)

     1  // Copyright (c) 2023, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package services
     5  
     6  import (
     7  	"fmt"
     8  	"github.com/verrazzano/verrazzano/tools/oam-converter/pkg/constants"
     9  	istio "istio.io/api/networking/v1beta1"
    10  	corev1 "k8s.io/api/core/v1"
    11  	"strings"
    12  )
    13  
    14  // CreateDestinationFromService selects a Service and creates a virtual service destination for the selected service.
    15  // If the selected service does not have a port, it is not included in the destination. If the selected service
    16  // declares port(s), it selects the appropriate one and add it to the destination.
    17  func CreateDestinationFromService(service *corev1.Service) (*istio.HTTPRouteDestination, error) {
    18  
    19  	dest := istio.HTTPRouteDestination{
    20  		Destination: &istio.Destination{Host: service.Name}}
    21  	// If the selected service declares port(s), select the appropriate port and add it to the destination.
    22  	if len(service.Spec.Ports) > 0 {
    23  		selectedPort, err := selectPortForDestination(service)
    24  		if err != nil {
    25  			return nil, err
    26  		}
    27  		dest.Destination.Port = &istio.PortSelector{Number: uint32(selectedPort.Port)}
    28  	}
    29  	return &dest, nil
    30  }
    31  
    32  // selectPortForDestination selects a Service port to be used for virtual service destination port.
    33  // The port is selected based on the following logic:
    34  //   - If there is one port, return that port.
    35  //   - If there are multiple ports, select the http/WebLogic port.
    36  //   - If there are multiple ports and more than one http/WebLogic port, return an error.
    37  //   - If there are multiple ports and none of then are http/WebLogic ports, return an error.
    38  func selectPortForDestination(service *corev1.Service) (corev1.ServicePort, error) {
    39  	servicePorts := service.Spec.Ports
    40  	// If there is only one port, return that port
    41  	if len(servicePorts) == 1 {
    42  		return servicePorts[0], nil
    43  	}
    44  	allowedPorts := append(getHTTPPorts(service), getWebLogicPorts(service)...)
    45  	// If there are multiple ports and one http/WebLogic port, return that port
    46  	if len(servicePorts) > 1 && len(allowedPorts) == 1 {
    47  		return allowedPorts[0], nil
    48  	}
    49  	// If there are multiple ports and none of them are http/WebLogic ports, return an error
    50  	if len(servicePorts) > 1 && len(allowedPorts) < 1 {
    51  		return corev1.ServicePort{}, fmt.Errorf("unable to select the service port for destination. The service port " +
    52  			"should be named with prefix \"http\" if there are multiple ports OR the IngressTrait must specify the port")
    53  	}
    54  	// If there are multiple http/WebLogic ports, return an error
    55  	if len(allowedPorts) > 1 {
    56  		return corev1.ServicePort{}, fmt.Errorf("unable to select the service port for destination. Only one service " +
    57  			"port should be named with prefix \"http\" OR the IngressTrait must specify the port")
    58  	}
    59  	return corev1.ServicePort{}, fmt.Errorf("unable to select default port for destination")
    60  }
    61  
    62  // getHTTPPorts returns all the service ports having the prefix "http" in their names.
    63  func getHTTPPorts(service *corev1.Service) []corev1.ServicePort {
    64  	var httpPorts []corev1.ServicePort
    65  	for _, servicePort := range service.Spec.Ports {
    66  		// Check if service port name has the http prefix
    67  		if strings.HasPrefix(servicePort.Name, "http") {
    68  			httpPorts = append(httpPorts, servicePort)
    69  		}
    70  	}
    71  	return httpPorts
    72  }
    73  
    74  // getWebLogicPorts returns WebLogic ports if any present for the service. A port is evaluated as a WebLogic port if
    75  // the port name is from the known WebLogic non-http prefixed port names used by the WebLogic operator.
    76  func getWebLogicPorts(service *corev1.Service) []corev1.ServicePort {
    77  	var webLogicPorts []corev1.ServicePort
    78  	selectorMap := service.Spec.Selector
    79  	value, ok := selectorMap["weblogic.createdByOperator"]
    80  	if !ok || value == "false" {
    81  		return webLogicPorts
    82  	}
    83  	for _, servicePort := range service.Spec.Ports {
    84  		// Check if service port name is one of the predefined WebLogic port names
    85  		for _, webLogicPortName := range constants.WeblogicPortNames {
    86  			if servicePort.Name == webLogicPortName {
    87  				webLogicPorts = append(webLogicPorts, servicePort)
    88  			}
    89  		}
    90  	}
    91  	return webLogicPorts
    92  }
    93  
    94  // CreateDestinationMatchRulePort fetches a Service matching the specified rule port and creates virtual service destination.
    95  func CreateDestinationMatchRulePort(service *corev1.Service, rulePort uint32) (*istio.HTTPRouteDestination, error) {
    96  
    97  	dest := &istio.HTTPRouteDestination{
    98  		Destination: &istio.Destination{Host: service.Name}}
    99  	// Set the port to rule destination port
   100  	dest.Destination.Port = &istio.PortSelector{Number: rulePort}
   101  
   102  	return nil, fmt.Errorf("unable to select service for specified destination port %d", rulePort)
   103  }