github.com/verrazzano/verrazzano@v1.7.1/tools/oam-converter/pkg/resources/gateway/gateway.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 gateway 5 6 import ( 7 "fmt" 8 certapiv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" 9 certv1 "github.com/cert-manager/cert-manager/pkg/apis/meta/v1" 10 vzapi "github.com/verrazzano/verrazzano/application-operator/apis/oam/v1alpha1" 11 coallateHosts "github.com/verrazzano/verrazzano/pkg/ingresstrait" 12 consts "github.com/verrazzano/verrazzano/tools/oam-converter/pkg/constants" 13 "github.com/verrazzano/verrazzano/tools/oam-converter/pkg/types" 14 istio "istio.io/api/networking/v1beta1" 15 vsapi "istio.io/client-go/pkg/apis/networking/v1beta1" 16 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 17 18 "sigs.k8s.io/controller-runtime/pkg/client" 19 ) 20 21 // The function createGatewayCertificate generates a certificate that the cert manager can use to generate a certificate 22 // that is embedded in a secret. The gateway will use the secret to offer TLS/HTTPS endpoints for installed applications. 23 // Each application will generate a single gateway. 24 func createGatewayCertificate(trait *vzapi.IngressTrait, hostsForTrait []string, appNamespace string) string { 25 26 certName := buildCertificateName(trait, appNamespace) 27 secretName := buildCertificateSecretName(trait, appNamespace) 28 29 certificate := &certapiv1.Certificate{ 30 TypeMeta: metav1.TypeMeta{ 31 Kind: "Certificate", 32 APIVersion: consts.CertificateAPIVersion, 33 }, 34 ObjectMeta: metav1.ObjectMeta{ 35 Namespace: "istio-system", 36 Name: certName, 37 }} 38 certificate.Spec = certapiv1.CertificateSpec{ 39 DNSNames: hostsForTrait, 40 SecretName: secretName, 41 IssuerRef: certv1.ObjectReference{ 42 Name: consts.VerrazzanoClusterIssuer, 43 Kind: "ClusterIssuer", 44 }, 45 } 46 47 return secretName 48 } 49 50 // buildCertificateSecretName get a cert secret name from the trait appending namespace name with trait name 51 func buildCertificateSecretName(trait *vzapi.IngressTrait, appNamespace string) string { 52 return fmt.Sprintf("%s-%s-cert-secret", appNamespace, trait.Name) 53 } 54 55 // buildCertificateName get a cert name from the trait appending namespace name with trait name 56 func buildCertificateName(trait *vzapi.IngressTrait, appNamespace string) string { 57 return fmt.Sprintf("%s-%s-cert", appNamespace, trait.Name) 58 } 59 60 // buildLegacyCertificateName will generate a cert name 61 func buildLegacyCertificateName(trait *vzapi.IngressTrait, appNamespace string, appName string) string { 62 return fmt.Sprintf("%s-%s-cert", appNamespace, trait.Name) 63 } 64 65 // buildLegacyCertificateSecretName will generate a cert secret name 66 func buildLegacyCertificateSecretName(trait *vzapi.IngressTrait, appNamespace string, appName string) string { 67 return fmt.Sprintf("%s-%s-cert-secret", appNamespace, trait.Name) 68 } 69 70 // validateConfiguredSecret ensures that a secret is specified and the trait rules specify a "hosts" setting. The 71 // specification of a secret implies that a certificate was created for specific hosts that differ than the host names 72 // generated by the runtime (when no hosts are specified). 73 func validateConfiguredSecret(trait *vzapi.IngressTrait) string { 74 secretName := trait.Spec.TLS.SecretName 75 return secretName 76 } 77 78 // BuildGatewayName will generate a gateway name from the namespace and application name of the provided trait. Returns 79 func BuildGatewayName(appNamespace string) (string, error) { 80 81 gwName := fmt.Sprintf("%s-%s-gw", appNamespace, appNamespace) 82 83 return gwName, nil 84 } 85 86 // formatGatewayServerPortName will generate a sever port name by appending https with trait name 87 func formatGatewayServerPortName(traitName string) string { 88 return fmt.Sprintf("https-%s", traitName) 89 } 90 91 // updateGatewayServersList Update/add the Server entry for the IngressTrait to the gateway servers list 92 // Each gateway server entry has a TLS field for the certificate. This corresponds to the IngressTrait TLS field. 93 func updateGatewayServersList(servers []*istio.Server, server *istio.Server) []*istio.Server { 94 if len(servers) == 0 { 95 servers = append(servers, server) 96 return servers 97 } 98 if len(servers) == 1 && len(servers[0].Name) == 0 && servers[0].Port.Name == "https" { 99 100 // - replace the empty name server with the named one 101 servers[0] = server 102 103 return servers 104 } 105 for index, existingServer := range servers { 106 if existingServer.Name == server.Name { 107 108 servers[index] = server 109 return servers 110 } 111 } 112 servers = append(servers, server) 113 return servers 114 } 115 116 func CreateGatewayResource(cli client.Client, conversionComponents []*types.ConversionComponents) (*vsapi.Gateway, []string, error) { 117 gateway, allHostsForTrait, err := CreateCertificateAndSecretGateway(cli, conversionComponents) 118 119 if err != nil { 120 return nil, nil, err 121 } 122 123 return gateway, allHostsForTrait, nil 124 125 } 126 127 func CreateCertificateAndSecretGateway(cli client.Client, conversionComponents []*types.ConversionComponents) (*vsapi.Gateway, []string, error) { 128 129 var gateway *vsapi.Gateway 130 var allHostsForTrait []string 131 var err error 132 for _, conversionComponent := range conversionComponents { 133 134 allHostsForTrait, err = coallateHosts.CoallateAllHostsForTrait(cli, conversionComponent.IngressTrait, conversionComponent.AppName, conversionComponent.AppNamespace) 135 if err != nil { 136 return nil, nil, err 137 138 } 139 140 } 141 secretNames := CreateGatewaySecret(conversionComponents, allHostsForTrait) 142 if len(secretNames) == len(conversionComponents) && secretNames != nil { 143 gwName, err := BuildGatewayName(conversionComponents[0].AppNamespace) 144 if err != nil { 145 146 return nil, nil, err 147 148 } 149 gateway, err = CreateGateway(conversionComponents, allHostsForTrait, gwName, secretNames) 150 if err != nil { 151 152 return nil, nil, err 153 154 } 155 } 156 return gateway, allHostsForTrait, nil 157 } 158 func CreateGatewaySecret(conversionComponents []*types.ConversionComponents, hostsForTrait []string) []string { 159 var secrets []string 160 for _, conversionComponent := range conversionComponents { 161 var secretName string 162 if conversionComponent.IngressTrait.Spec.TLS != (vzapi.IngressSecurity{}) { 163 secretName = validateConfiguredSecret(conversionComponent.IngressTrait) 164 } else { 165 166 buildLegacyCertificateName(conversionComponent.IngressTrait, conversionComponent.AppNamespace, conversionComponent.AppName) 167 buildLegacyCertificateSecretName(conversionComponent.IngressTrait, conversionComponent.AppNamespace, conversionComponent.AppName) 168 secretName = createGatewayCertificate(conversionComponent.IngressTrait, hostsForTrait, conversionComponent.AppNamespace) 169 170 } 171 secrets = append(secrets, secretName) 172 } 173 174 return secrets 175 } 176 177 // CreateGateway creates the Gateway child resource of the trait. 178 func CreateGateway(conversionComponents []*types.ConversionComponents, hostsForTrait []string, gwName string, secretNames []string) (*vsapi.Gateway, error) { 179 // Create a gateway populating only gwName metadata. 180 // This is used as default if the gateway needs to be created. 181 gateway := &vsapi.Gateway{ 182 TypeMeta: metav1.TypeMeta{ 183 APIVersion: consts.GatewayAPIVersion, 184 Kind: "Gateway"}, 185 ObjectMeta: metav1.ObjectMeta{ 186 Namespace: conversionComponents[0].AppNamespace, 187 Name: gwName}} 188 189 var err error 190 gateway, err = mutateGateway(conversionComponents, gateway, hostsForTrait, secretNames) 191 if err != nil { 192 return nil, err 193 } 194 195 return gateway, nil 196 } 197 198 // mutate Gateway mutates or changes gateway according to the trait configuration 199 func mutateGateway(conversionComponents []*types.ConversionComponents, gateway *vsapi.Gateway, hostsForTrait []string, secretNames []string) (*vsapi.Gateway, error) { 200 for i := range conversionComponents { 201 // perform an operation 202 server := &istio.Server{ 203 Name: conversionComponents[i].IngressTrait.Name, 204 Hosts: hostsForTrait, 205 Port: &istio.Port{ 206 Name: formatGatewayServerPortName(conversionComponents[i].IngressTrait.Name), 207 Number: 443, 208 Protocol: consts.HTTPSProtocol, 209 }, 210 Tls: &istio.ServerTLSSettings{ 211 Mode: istio.ServerTLSSettings_SIMPLE, 212 CredentialName: secretNames[i], 213 }, 214 } 215 gateway.Spec.Servers = updateGatewayServersList(gateway.Spec.Servers, server) 216 } 217 gateway.Spec.Selector = map[string]string{"istio": "ingressgateway"} 218 return gateway, nil 219 } 220 221 // CreateListGateway Create a list of gateways 222 func CreateListGateway(gateway *vsapi.Gateway) (map[string]interface{}, error) { 223 gatewayData := make(map[string]interface{}) 224 gatewayData["apiVersion"] = "v1" 225 gatewayData["items"] = []*vsapi.Gateway{gateway} 226 gatewayData["kind"] = "List" 227 gatewayData["metadata"] = struct { 228 ResourceVersion string `yaml:"resourceVersion"` 229 }{ResourceVersion: ""} 230 231 return gatewayData, nil 232 }