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 }