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  }