github.com/verrazzano/verrazzano@v1.7.1/tools/oam-converter/pkg/resources/workloads/destinationrule.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 workloads
     5  
     6  import (
     7  	"errors"
     8  	vzapi "github.com/verrazzano/verrazzano/application-operator/apis/oam/v1alpha1"
     9  	consts "github.com/verrazzano/verrazzano/tools/oam-converter/pkg/constants"
    10  	destination "github.com/verrazzano/verrazzano/tools/oam-converter/pkg/resources/destinationrule"
    11  	serviceDestination "github.com/verrazzano/verrazzano/tools/oam-converter/pkg/services"
    12  	"google.golang.org/protobuf/types/known/durationpb"
    13  	istionet "istio.io/api/networking/v1alpha3"
    14  	istio "istio.io/api/networking/v1beta1"
    15  	istioclient "istio.io/client-go/pkg/apis/networking/v1alpha3"
    16  	corev1 "k8s.io/api/core/v1"
    17  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    18  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    19  	"reflect"
    20  	"time"
    21  )
    22  
    23  // creates or updates the DestinationRule.
    24  func createDestinationRuleFromWorkload(trait *vzapi.IngressTrait, rule vzapi.IngressRule, name string, helidonWorkload *unstructured.Unstructured, service *corev1.Service) (*istioclient.DestinationRule, error) {
    25  	if rule.Destination.HTTPCookie != nil {
    26  		destinationRule := &istioclient.DestinationRule{
    27  			TypeMeta: metav1.TypeMeta{
    28  				APIVersion: consts.DestinationRuleAPIVersion,
    29  				Kind:       "DestinationRule"},
    30  			ObjectMeta: metav1.ObjectMeta{
    31  				Namespace: trait.Namespace,
    32  				Name:      name},
    33  		}
    34  		namespace := &corev1.Namespace{}
    35  		return mutateDestinationRuleFromWorkload(destinationRule, rule, namespace, helidonWorkload, service)
    36  
    37  	}
    38  	return nil, nil
    39  }
    40  
    41  // mutateDestinationRule changes the destination rule based upon a traits configuration
    42  func mutateDestinationRuleFromWorkload(destinationRule *istioclient.DestinationRule, rule vzapi.IngressRule, namespace *corev1.Namespace, helidonWorkload *unstructured.Unstructured, service *corev1.Service) (*istioclient.DestinationRule, error) {
    43  	dest, err := createDestinationFromRuleOrService(rule, helidonWorkload, service)
    44  	if err != nil {
    45  		print(err)
    46  		return nil, err
    47  	}
    48  
    49  	mode := istionet.ClientTLSSettings_DISABLE
    50  	value, ok := namespace.Labels["istio-injection"]
    51  	if ok && value == "enabled" {
    52  		mode = istionet.ClientTLSSettings_ISTIO_MUTUAL
    53  	}
    54  	destinationRule.Spec = istionet.DestinationRule{
    55  		Host: dest.Destination.Host,
    56  		TrafficPolicy: &istionet.TrafficPolicy{
    57  			Tls: &istionet.ClientTLSSettings{
    58  				Mode: mode,
    59  			},
    60  			LoadBalancer: &istionet.LoadBalancerSettings{
    61  				LbPolicy: &istionet.LoadBalancerSettings_ConsistentHash{
    62  					ConsistentHash: &istionet.LoadBalancerSettings_ConsistentHashLB{
    63  						HashKey: &istionet.LoadBalancerSettings_ConsistentHashLB_HttpCookie{
    64  							HttpCookie: &istionet.LoadBalancerSettings_ConsistentHashLB_HTTPCookie{
    65  								Name: rule.Destination.HTTPCookie.Name,
    66  								Path: rule.Destination.HTTPCookie.Path,
    67  								Ttl:  durationpb.New(rule.Destination.HTTPCookie.TTL * time.Second)},
    68  						},
    69  					},
    70  				},
    71  			},
    72  		},
    73  	}
    74  
    75  	return destinationRule, nil
    76  }
    77  
    78  // createDestinationFromRuleOrService creates a destination from the rule or workload
    79  // If the rule contains destination information that is used otherwise workload information is used
    80  func createDestinationFromRuleOrService(rule vzapi.IngressRule, helidonWorkload *unstructured.Unstructured, service *corev1.Service) (*istio.HTTPRouteDestination, error) {
    81  	if len(rule.Destination.Host) > 0 {
    82  
    83  		dest, err := destination.CreateDestinationFromRule(rule)
    84  		if err != nil {
    85  			return nil, err
    86  		}
    87  		return dest, err
    88  
    89  	}
    90  	if rule.Destination.Port != 0 {
    91  		return serviceDestination.CreateDestinationMatchRulePort(service, rule.Destination.Port)
    92  	}
    93  	if helidonWorkload == nil && service != nil {
    94  		return serviceDestination.CreateDestinationFromService(service)
    95  	}
    96  
    97  	return createDestinationFromHelidonWorkload(helidonWorkload)
    98  
    99  }
   100  
   101  // creates a destination in the virtual service from helidon workload if port details not present in trait
   102  func createDestinationFromHelidonWorkload(helidonWorkload *unstructured.Unstructured) (*istio.HTTPRouteDestination, error) {
   103  
   104  	helidonWorkloadStruct := helidonWorkload.UnstructuredContent()
   105  	spec, found, err := unstructured.NestedMap(helidonWorkloadStruct, "spec")
   106  	if !found || err != nil {
   107  		return nil, errors.New("spec key in a component doesn't exist or not found in the specified type")
   108  	}
   109  	deploymentTemplate, found, err := unstructured.NestedMap(spec, "deploymentTemplate")
   110  	if !found || err != nil {
   111  		return nil, errors.New("deployment template key in a component doesn't exist or not found in the specified type")
   112  	}
   113  	podSpec, found, err := unstructured.NestedMap(deploymentTemplate, "podSpec")
   114  	if !found || err != nil {
   115  		return nil, errors.New("pod spec in a component doesn't exist or not found in the specified type")
   116  	}
   117  	container, found, err := unstructured.NestedSlice(podSpec, "containers")
   118  	if !found || err != nil {
   119  		return nil, errors.New("container in a component doesn't exist or not found in the specified type")
   120  	}
   121  	metaData, found, err := unstructured.NestedMap(deploymentTemplate, "metadata")
   122  	if !found || err != nil {
   123  		return nil, errors.New("metadata in a component doesn't exist or not found in the specified type")
   124  	}
   125  	name, found, err := unstructured.NestedString(metaData, "name")
   126  	if !found || err != nil {
   127  		return nil, errors.New("name in a component doesn't exist or not found in the specified type")
   128  	}
   129  
   130  	// Iterate over the container slice
   131  	for _, item := range container {
   132  
   133  		// Type assertion to convert the item back to map[string]interface{}
   134  		if itemMap, ok := item.(map[string]interface{}); ok {
   135  
   136  			// Check if the "ports" key exists in the map
   137  			if ports, exists := itemMap["ports"]; exists {
   138  
   139  				// Type assertion to convert "ports" to []interface{}
   140  				if portsSlice, ok := ports.([]interface{}); ok {
   141  
   142  					// Iterate over the ports slice
   143  					for _, port := range portsSlice {
   144  
   145  						// Type assertion to convert each port to map[string]interface{}
   146  						if portMap, ok := port.(map[string]interface{}); ok {
   147  
   148  							// Check if the "containerPort" key exists in the portMap
   149  							if containerPort, exists := portMap["containerPort"]; exists {
   150  								var int32ContainerPort uint32
   151  								if isInt64(containerPort) {
   152  									int32ContainerPort = uint32(containerPort.(int64))
   153  								}
   154  								if isFloat64(containerPort) {
   155  									int32ContainerPort = uint32(containerPort.(float64))
   156  								}
   157  								// Access the value of "containerPort" and convert into uint32
   158  
   159  								dest := istio.HTTPRouteDestination{
   160  
   161  									Destination: &istio.Destination{Host: name}}
   162  
   163  								// Set the port to rule destination port
   164  								dest.Destination.Port = &istio.PortSelector{Number: int32ContainerPort}
   165  								return &dest, nil
   166  
   167  							}
   168  						}
   169  					}
   170  				}
   171  			}
   172  			return nil, errors.New("Port does not exist")
   173  		}
   174  
   175  	}
   176  	return nil, errors.New("unable to select data for specified destination port")
   177  }
   178  
   179  func isInt64(val interface{}) bool {
   180  	return reflect.TypeOf(val).Kind() == reflect.Int64
   181  }
   182  
   183  func isFloat64(val interface{}) bool {
   184  	return reflect.TypeOf(val).Kind() == reflect.Float64
   185  }