pkg/appgw/internaltypes.go (216 lines of code) (raw):

// ------------------------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. // -------------------------------------------------------------------------------------------- // A note on naming Application Gateway properties: // A constraint on the App Gateway property names - these must begin and end with a word character or an underscore // A word character is well defined here: https://docs.microsoft.com/en-us/dotnet/standard/base-types/character-classes-in-regular-expressions#WordCharacter package appgw import ( "crypto/md5" "fmt" "math" "regexp" "strings" n "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2021-03-01/network" "github.com/Azure/go-autorest/autorest/to" networking "k8s.io/api/networking/v1" "k8s.io/klog/v2" "github.com/Azure/application-gateway-kubernetes-ingress/pkg/environment" "github.com/Azure/application-gateway-kubernetes-ingress/pkg/utils" ) const ( prefixHTTPSettings = "bp" prefixProbe = "pb" prefixPool = "pool" prefixPort = "fp" prefixListener = "fl" prefixPathMap = "url" prefixRoutingRule = "rr" prefixRedirect = "sslr" prefixPathRule = "pr" prefixSslCertificate = "cert" ) const ( // MaxAllowedHostNames the maximum number of HostNames allowed for listener. MaxAllowedHostNames int = 5 // WildcardSpecialCharacters are characters that are allowed for wildcard HostNames. WildcardSpecialCharacters = "*?" ) type backendIdentifier struct { serviceIdentifier Ingress *networking.Ingress Rule *networking.IngressRule Path *networking.HTTPIngressPath Backend *networking.IngressBackend } type serviceBackendPortPair struct { ServicePort Port BackendPort Port } type listenerIdentifier struct { FrontendPort Port HostNames [MaxAllowedHostNames]string FrontendType FrontendType } type serviceIdentifier struct { Namespace string Name string } type secretIdentifier struct { Namespace string Name string } // Max length for a property name is 80 characters. We hash w/ MD5 when length is > 80, which is 32 characters var agPrefixValidator = regexp.MustCompile(`^[0-9a-zA-Z\-]{0,47}$`) var agPrefix = environment.GetEnvironmentVariable("APPGW_CONFIG_NAME_PREFIX", "", agPrefixValidator) // create xxx -> xxxconfiguration mappings to contain all the information type listenerAzConfig struct { Protocol n.ApplicationGatewayProtocol Secret secretIdentifier SslRedirectConfigurationName string SslProfile string FirewallPolicy string } // formatPropName ensures that the string generated is not longer than 80 characters. func formatPropName(val string) string { // App Gateway property name cannot be longer than 80 characters maxLen := 80 if len(val) <= maxLen { return val } hash := fmt.Sprintf("%x", md5.Sum([]byte(val))) separator := "-" prefix := val[0 : maxLen-len(hash)-len(separator)] finalVal := fmt.Sprintf("%s%s%s", prefix, separator, hash) klog.V(3).Infof("Prop name %s with length %d is longer than %d characters; Transformed to %s", val, len(val), maxLen, finalVal) return finalVal } func (s serviceIdentifier) serviceFullName() string { return fmt.Sprintf("%v-%v", s.Namespace, s.Name) } func (s serviceIdentifier) serviceFullNameHash() string { return fmt.Sprintf("%x", md5.Sum([]byte(s.serviceFullName()))) } func (s serviceIdentifier) serviceKey() string { return fmt.Sprintf("%v/%v", s.Namespace, s.Name) } func (s secretIdentifier) secretKey() string { return fmt.Sprintf("%v/%v", s.Namespace, s.Name) } func (s secretIdentifier) secretFullName() string { // in case of referring ssl certificate from agic annotation if len(s.Namespace) == 0 { return s.Name } return fmt.Sprintf("%v%v-%v-%v", agPrefix, prefixSslCertificate, s.Namespace, s.Name) } func getResourceKey(namespace, name string) string { return formatPropName(fmt.Sprintf("%v/%v", namespace, name)) } func generateHTTPSettingsName(serviceName string, servicePort string, backendPort Port, ingress string) string { return formatPropName(fmt.Sprintf("%s%s-%v-%v-%v-%s", agPrefix, prefixHTTPSettings, serviceName, servicePort, backendPort, ingress)) } func generateProbeName(serviceName string, servicePort string, ingress *networking.Ingress) string { return formatPropName(fmt.Sprintf("%s%s-%s-%v-%v-%s", agPrefix, prefixProbe, ingress.Namespace, serviceName, servicePort, ingress.Name)) } func generateAddressPoolName(serviceName string, servicePort string, backendPort Port) string { return formatPropName(fmt.Sprintf("%s%s-%v-%v-bp-%v", agPrefix, prefixPool, serviceName, servicePort, backendPort)) } func generateFrontendPortName(port Port) string { return formatPropName(fmt.Sprintf("%s%s-%v", agPrefix, prefixPort, port)) } func generateListenerName(listenerID listenerIdentifier) string { return formatPropName(fmt.Sprintf("%s%s-%s", agPrefix, prefixListener, utils.GetHashCode(listenerID))) } func generateURLPathMapName(listenerID listenerIdentifier) string { return formatPropName(fmt.Sprintf("%s%s-%s", agPrefix, prefixPathMap, utils.GetHashCode(listenerID))) } func generateRequestRoutingRuleName(listenerID listenerIdentifier) string { return formatPropName(fmt.Sprintf("%s%s-%s", agPrefix, prefixRoutingRule, utils.GetHashCode(listenerID))) } func generateSSLRedirectConfigurationName(targetListener listenerIdentifier) string { return formatPropName(fmt.Sprintf("%s%s-%s", agPrefix, prefixRedirect, generateListenerName(targetListener))) } func generatePathRuleName(namespace, ingress string, ruleIdx, pathIdx int) string { return formatPropName(fmt.Sprintf("%s%s-%s-%s-rule-%d-path-%d", agPrefix, prefixPathRule, namespace, ingress, ruleIdx, pathIdx)) } // DefaultBackendHTTPSettingsName is the name to be assigned to App Gateway's default HTTP settings resource. var DefaultBackendHTTPSettingsName = fmt.Sprintf("%sdefaulthttpsetting", agPrefix) // DefaultBackendAddressPoolName is the name to be assigned to App Gateway's default backend pool resource. var DefaultBackendAddressPoolName = fmt.Sprintf("%sdefaultaddresspool", agPrefix) func defaultProbeName(protocol n.ApplicationGatewayProtocol) string { return fmt.Sprintf("%sdefaultprobe-%s", agPrefix, protocol) } func defaultBackendHTTPSettings(appGWIdentifier Identifier, protocol n.ApplicationGatewayProtocol) n.ApplicationGatewayBackendHTTPSettings { defHTTPSettingsName := DefaultBackendHTTPSettingsName defHTTPSettingsPort := int32(80) return n.ApplicationGatewayBackendHTTPSettings{ Name: &defHTTPSettingsName, ID: to.StringPtr(appGWIdentifier.HTTPSettingsID(defHTTPSettingsName)), ApplicationGatewayBackendHTTPSettingsPropertiesFormat: &n.ApplicationGatewayBackendHTTPSettingsPropertiesFormat{ Protocol: protocol, Port: &defHTTPSettingsPort, Probe: resourceRef(appGWIdentifier.probeID(defaultProbeName(protocol))), // setting to default PickHostNameFromBackendAddress: to.BoolPtr(false), CookieBasedAffinity: n.ApplicationGatewayCookieBasedAffinityDisabled, RequestTimeout: to.Int32Ptr(30), }, } } func defaultProbe(appGWIdentifier Identifier, protocol n.ApplicationGatewayProtocol) n.ApplicationGatewayProbe { defProbeName := defaultProbeName(protocol) defHost := "localhost" defPath := "/" defInterval := int32(30) defTimeout := int32(30) defUnHealthyCount := int32(3) return n.ApplicationGatewayProbe{ Name: to.StringPtr(defProbeName), ID: to.StringPtr(appGWIdentifier.probeID(defProbeName)), ApplicationGatewayProbePropertiesFormat: &n.ApplicationGatewayProbePropertiesFormat{ Protocol: protocol, Host: &defHost, Path: &defPath, Interval: &defInterval, Timeout: &defTimeout, UnhealthyThreshold: &defUnHealthyCount, // setting to defaults Match: &n.ApplicationGatewayProbeHealthResponseMatch{}, PickHostNameFromBackendHTTPSettings: to.BoolPtr(false), MinServers: to.Int32Ptr(0), }, } } func defaultBackendAddressPool(appGWIdentifier Identifier) n.ApplicationGatewayBackendAddressPool { return n.ApplicationGatewayBackendAddressPool{ Name: &DefaultBackendAddressPoolName, ID: to.StringPtr(appGWIdentifier.AddressPoolID(DefaultBackendAddressPoolName)), ApplicationGatewayBackendAddressPoolPropertiesFormat: &n.ApplicationGatewayBackendAddressPoolPropertiesFormat{ BackendAddresses: &[]n.ApplicationGatewayBackendAddress{}, }, } } // defaultFrontendType determines which frontend to use by default // if UsePrivateIP environment variable is set, then use private IP // if public IP is present, then use public IP as that was the default behavior before // otherwise use private IP func defaultFrontendType(appGw n.ApplicationGateway, env environment.EnvVariables) FrontendType { if env.UsePrivateIP { return FrontendTypePrivate } publicIPPresent := LookupIPConfigurationByType(appGw.FrontendIPConfigurations, FrontendTypePublic) != nil if publicIPPresent { return FrontendTypePublic } return FrontendTypePrivate } func defaultFrontendListenerIdentifier(appGw n.ApplicationGateway, env environment.EnvVariables) listenerIdentifier { return listenerIdentifier{ FrontendPort: Port(80), FrontendType: defaultFrontendType(appGw, env), } } func (listenerID *listenerIdentifier) setHostNames(hostNames []string) { hostnameCount := int(math.Min(float64(len(hostNames)), float64(MaxAllowedHostNames))) for i := 0; i < hostnameCount; i++ { listenerID.HostNames[i] = hostNames[i] } } // Returns the HostNames as a slice func (listenerID *listenerIdentifier) getHostNames() []string { var hostNames []string for i := 0; i < len(listenerID.HostNames); i++ { if listenerID.HostNames[i] != "" { hostNames = append(hostNames, listenerID.HostNames[i]) } } return hostNames } // getHostNameForProbes returns the first hostname which doesn't have special chars. To be used for probes. func (listenerID *listenerIdentifier) getHostNameForProbes() *string { hostNames := listenerID.getHostNames() for _, hostName := range hostNames { if !strings.ContainsAny(hostName, WildcardSpecialCharacters) { return to.StringPtr(hostName) } } return nil }