pkg/annotations/ingress_annotations.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.
// --------------------------------------------------------------------------------------------
package annotations
import (
"strconv"
"strings"
"github.com/knative/pkg/apis/istio/v1alpha3"
networking "k8s.io/api/networking/v1"
"github.com/Azure/application-gateway-kubernetes-ingress/pkg/controllererrors"
)
const (
// ApplicationGatewayPrefix defines the prefix for all keys associated with Application Gateway Ingress controller.
ApplicationGatewayPrefix = "appgw.ingress.kubernetes.io"
// BackendPathPrefixKey defines the key for Path which should be used as a prefix for all HTTP requests.
// Null means no path will be prefixed. Default value is null.
BackendPathPrefixKey = ApplicationGatewayPrefix + "/backend-path-prefix"
// BackendHostNameKey defines the key for Host which should be used as when making a connection to the backend.
// Null means Host specified in the request to Application Gateway is used to connect to the backend.
BackendHostNameKey = ApplicationGatewayPrefix + "/backend-hostname"
// HealthProbeHostKey defines the key for Host which should be used as a target for health probe.
HealthProbeHostKey = ApplicationGatewayPrefix + "/health-probe-hostname"
// HealthProbePortKey defines the key for port that should be used as a target for health probe.
HealthProbePortKey = ApplicationGatewayPrefix + "/health-probe-port"
// HealthProbePathKey defines the key for URL path which should be used as a target for health probe.
HealthProbePathKey = ApplicationGatewayPrefix + "/health-probe-path"
// HealthProbeStatusCodesKey defines status codes returned by the probe to be interpreted as healty service
HealthProbeStatusCodesKey = ApplicationGatewayPrefix + "/health-probe-status-codes"
// HealthProbeIntervalKey defines the probe interval in seconds
HealthProbeIntervalKey = ApplicationGatewayPrefix + "/health-probe-interval"
// HealthProbeTimeoutKey defines the probe timeout in seconds
HealthProbeTimeoutKey = ApplicationGatewayPrefix + "/health-probe-timeout"
// HealthProbeUnhealthyThresholdKey defines threshold for marking backend server as unhealthy
HealthProbeUnhealthyThresholdKey = ApplicationGatewayPrefix + "/health-probe-unhealthy-threshold"
// CookieBasedAffinityKey defines the key to enable/disable cookie based affinity for client connection.
CookieBasedAffinityKey = ApplicationGatewayPrefix + "/cookie-based-affinity"
// CookieBasedAffinityDistinctNameKey defines the key to enable/disable distinct cookie names per backend for client connection.
CookieBasedAffinityDistinctNameKey = ApplicationGatewayPrefix + "/cookie-based-affinity-distinct-name"
// RequestTimeoutKey defines the request timeout to the backend.
RequestTimeoutKey = ApplicationGatewayPrefix + "/request-timeout"
// ConnectionDrainingKey defines the key to enable/disable connection draining.
ConnectionDrainingKey = ApplicationGatewayPrefix + "/connection-draining"
// ConnectionDrainingTimeoutKey defines the drain timeout for the backends.
ConnectionDrainingTimeoutKey = ApplicationGatewayPrefix + "/connection-draining-timeout"
// SslRedirectKey defines the key for defining with SSL redirect should be turned on for an HTTP endpoint.
SslRedirectKey = ApplicationGatewayPrefix + "/ssl-redirect"
// UsePrivateIPKey defines the key to determine whether to use private ip with the ingress.
UsePrivateIPKey = ApplicationGatewayPrefix + "/use-private-ip"
// OverrideFrontendPortKey defines the key to define a custom fronend port
OverrideFrontendPortKey = ApplicationGatewayPrefix + "/override-frontend-port"
// BackendProtocolKey defines the key to determine whether to use private ip with the ingress.
BackendProtocolKey = ApplicationGatewayPrefix + "/backend-protocol"
// HostNameExtensionKey defines the key to add multiple hostnames to ingress rules including wildcard hostnames
// annotation will be appgw.ingress.kubernetes.io/hostname-extension : "hostname1, hostname2"
// The extended hostnames will be appended to ingress host for a rule if specified
HostNameExtensionKey = ApplicationGatewayPrefix + "/hostname-extension"
// IngressClassKey defines the key of the annotation which needs to be set in order to specify
// that this is an ingress resource meant for the application gateway ingress controller.
IngressClassKey = "kubernetes.io/ingress.class"
// IstioGatewayKey defines the key of the annotation which needs to be set in order to specify
// that this is a gateway meant for the application gateway ingress controller.
IstioGatewayKey = "appgw.ingress.istio.io/v1alpha3"
// FirewallPolicy is the key part of a key/value Ingress annotation.
// The value of this is an ID of a Firewall Policy. The Firewall Policy must be already defined in Azure.
// The policy will be attached to all URL paths declared in the annotated Ingress resource.
FirewallPolicy = ApplicationGatewayPrefix + "/waf-policy-for-path"
// AppGwSslCertificate indicates the name of ssl certificate installed by AppGw
AppGwSslCertificate = ApplicationGatewayPrefix + "/appgw-ssl-certificate"
// AppGwSslProfile indicates the name of the ssl profile installed by AppGw
AppGwSslProfile = ApplicationGatewayPrefix + "/appgw-ssl-profile"
// AppGwTrustedRootCertificate indicates the names of trusted root certificates
// Multiple root certificates separated by comma, e.g. "cert1,cert2"
AppGwTrustedRootCertificate = ApplicationGatewayPrefix + "/appgw-trusted-root-certificate"
// RewriteRuleSetKey indicates the name of the rule set to overwrite HTTP headers.
RewriteRuleSetKey = ApplicationGatewayPrefix + "/rewrite-rule-set"
// RewriteRuleSetCustomResourceKey indicates the name of the rule set CRD to use for header CRD and URL Config.
RewriteRuleSetCustomResourceKey = ApplicationGatewayPrefix + "/rewrite-rule-set-custom-resource"
// RequestRoutingRulePriority indicates the priority of the Request Routing Rules.
RequestRoutingRulePriority = ApplicationGatewayPrefix + "/rule-priority"
)
// ProtocolEnum is the type for protocol
type ProtocolEnum int
const (
// HTTP is enum for http protocol
HTTP ProtocolEnum = iota + 1
// HTTPS is enum for https protocol
HTTPS
)
// ProtocolEnumLookup is a reverse map of the EventType enums; used for logging purposes
var ProtocolEnumLookup = map[string]ProtocolEnum{
"http": HTTP,
"https": HTTPS,
}
// IngressClass returns ingress class annotation value if set
func IngressClass(ing *networking.Ingress) (string, error) {
return parseString(ing, IngressClassKey)
}
// IngressClass returns istio ingress class annotation value if set
func IstioGatewayIngressClass(gateway *v1alpha3.Gateway) (string, error) {
val, ok := gateway.Annotations[IstioGatewayKey]
if ok {
return val, nil
}
return "", controllererrors.NewErrorf(
controllererrors.ErrorMissingAnnotation,
"%s is not set in Ingress %s/%s", IstioGatewayKey, gateway.Namespace, gateway.Name,
)
}
// IsSslRedirect for HTTP end points.
func IsSslRedirect(ing *networking.Ingress) (bool, error) {
return parseBool(ing, SslRedirectKey)
}
// BackendPathPrefix override path
func BackendPathPrefix(ing *networking.Ingress) (string, error) {
return parseString(ing, BackendPathPrefixKey)
}
// BackendHostName override hostname
func BackendHostName(ing *networking.Ingress) (string, error) {
return parseString(ing, BackendHostNameKey)
}
// HealthProbeHostName probe hostname override
func HealthProbeHostName(ing *networking.Ingress) (string, error) {
return parseString(ing, HealthProbeHostKey)
}
// HealthProbePort probe port override
func HealthProbePort(ing *networking.Ingress) (int32, error) {
return parseInt32(ing, HealthProbePortKey)
}
// HealthProbePath probe path override
func HealthProbePath(ing *networking.Ingress) (string, error) {
return parseString(ing, HealthProbePathKey)
}
// HealthProbeStatusCodes probe status codes
func HealthProbeStatusCodes(ing *networking.Ingress) ([]string, error) {
value, err := parseString(ing, HealthProbeStatusCodesKey)
if value != "" {
codesArray := strings.Split(value, ",")
for index, element := range codesArray {
codesArray[index] = strings.TrimSpace(element)
}
return codesArray, err
}
return nil, err
}
// HealthProbeInterval probe interval
func HealthProbeInterval(ing *networking.Ingress) (int32, error) {
return parseInt32(ing, HealthProbeIntervalKey)
}
// HealthProbeTimeout probe timeout
func HealthProbeTimeout(ing *networking.Ingress) (int32, error) {
return parseInt32(ing, HealthProbeTimeoutKey)
}
// HealthProbeUnhealthyThreshold probe threshold
func HealthProbeUnhealthyThreshold(ing *networking.Ingress) (int32, error) {
return parseInt32(ing, HealthProbeUnhealthyThresholdKey)
}
// GetAppGwSslCertificate refer to appgw installed certificate
func GetAppGwSslCertificate(ing *networking.Ingress) (string, error) {
return parseString(ing, AppGwSslCertificate)
}
// GetAppGwTrustedRootCertificate refer to appgw installed root certificate
func GetAppGwTrustedRootCertificate(ing *networking.Ingress) (string, error) {
return parseString(ing, AppGwTrustedRootCertificate)
}
// GetAppGwSslProfile refer to appgw installed certificate
func GetAppGwSslProfile(ing *networking.Ingress) (string, error) {
return parseString(ing, AppGwSslProfile)
}
// RequestTimeout provides value for request timeout on the backend connection
func RequestTimeout(ing *networking.Ingress) (int32, error) {
return parseInt32(ing, RequestTimeoutKey)
}
// IsConnectionDraining provides whether connection draining is enabled or not.
func IsConnectionDraining(ing *networking.Ingress) (bool, error) {
return parseBool(ing, ConnectionDrainingKey)
}
// ConnectionDrainingTimeout provides value for draining timeout for backends.
func ConnectionDrainingTimeout(ing *networking.Ingress) (int32, error) {
return parseInt32(ing, ConnectionDrainingTimeoutKey)
}
// IsCookieBasedAffinity provides value to enable/disable cookie based affinity for client connection.
func IsCookieBasedAffinity(ing *networking.Ingress) (bool, error) {
return parseBool(ing, CookieBasedAffinityKey)
}
// IsCookieBasedAffinityDistinctName provides value to enable/disable distinct cookie name based affinity for client connection.
func IsCookieBasedAffinityDistinctName(ing *networking.Ingress) (bool, error) {
return parseBool(ing, CookieBasedAffinityDistinctNameKey)
}
// UsePrivateIP determines whether to use private IP with the ingress
func UsePrivateIP(ing *networking.Ingress) (bool, error) {
return parseBool(ing, UsePrivateIPKey)
}
// OverrideFrontendPort determines whether to use a custom Frontend port
func OverrideFrontendPort(ing *networking.Ingress) (int32, error) {
return parseInt32(ing, OverrideFrontendPortKey)
}
// BackendProtocol provides value for protocol to be used with the backend
func BackendProtocol(ing *networking.Ingress) (ProtocolEnum, error) {
protocol, err := parseString(ing, BackendProtocolKey)
if err != nil {
return HTTP, err
}
if protocolEnum, ok := ProtocolEnumLookup[strings.ToLower(protocol)]; ok {
return protocolEnum, nil
}
return HTTP, controllererrors.NewErrorf(controllererrors.ErrorInvalidContent,
"annotation %v does not contain a valid value (%v)", BackendProtocolKey, protocol,
)
}
// GetHostNameExtensions from a given ingress
func GetHostNameExtensions(ing *networking.Ingress) ([]string, error) {
val, err := parseString(ing, HostNameExtensionKey)
if err == nil {
var hostnames []string
for _, hostname := range strings.Split(val, ",") {
trimmed := strings.TrimSpace(hostname)
if len(trimmed) > 0 {
hostnames = append(hostnames, trimmed)
}
}
return hostnames, nil
}
return nil, err
}
// WAFPolicy override path
func WAFPolicy(ing *networking.Ingress) (string, error) {
return parseString(ing, FirewallPolicy)
}
// RewriteRuleSet name
func RewriteRuleSet(ing *networking.Ingress) (string, error) {
return parseString(ing, RewriteRuleSetKey)
}
// RewriteRuleSetCustomResource name
func RewriteRuleSetCustomResource(ing *networking.Ingress) (string, error) {
return parseString(ing, RewriteRuleSetCustomResourceKey)
}
// GetRequestRoutingRulePriority gets the request routing rule priority
func GetRequestRoutingRulePriority(ing *networking.Ingress) (*int32, error) {
min := int32(1)
max := int32(20000)
val, err := parseInt32(ing, RequestRoutingRulePriority)
if err == nil {
if val >= min && val <= max {
return &val, nil
} else {
val = 0
return &val, controllererrors.NewErrorf(controllererrors.ErrorInvalidContent,
"Priority must be a value from %d to %d", min, max)
}
}
return nil, err
}
func parseBool(ing *networking.Ingress, name string) (bool, error) {
if val, ok := ing.Annotations[name]; ok {
if boolVal, err := strconv.ParseBool(val); err == nil {
return boolVal, nil
}
return false, controllererrors.NewErrorf(controllererrors.ErrorInvalidContent,
"annotation %v does not contain a valid value (%v)", name, val,
)
}
return false, controllererrors.NewErrorf(
controllererrors.ErrorMissingAnnotation,
"%s is not set in Ingress %s/%s", name, ing.Namespace, ing.Name,
)
}
func parseString(ing *networking.Ingress, name string) (string, error) {
if val, ok := ing.Annotations[name]; ok {
return val, nil
}
return "", controllererrors.NewErrorf(
controllererrors.ErrorMissingAnnotation,
"%s is not set in Ingress %s/%s", name, ing.Namespace, ing.Name,
)
}
func parseInt32(ing *networking.Ingress, name string) (int32, error) {
if val, ok := ing.Annotations[name]; ok {
if intVal, err := strconv.Atoi(val); err == nil {
return int32(intVal), nil
}
return 0, controllererrors.NewErrorf(controllererrors.ErrorInvalidContent,
"annotation %v does not contain a valid value (%v)", name, val,
)
}
return 0, controllererrors.NewErrorf(
controllererrors.ErrorMissingAnnotation,
"%s is not set in Ingress %s/%s", name, ing.Namespace, ing.Name,
)
}