pkg/config/config.go (143 lines of code) (raw):
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
package config
import (
"errors"
"flag"
"fmt"
"os"
"strings"
"time"
"github.com/Azure/go-autorest/autorest/azure"
)
const (
// DefaultNs is the default namespace for the resources deployed by this operator
DefaultNs = "app-routing-system"
PublicZoneType = "dnszones"
PrivateZoneType = "privatednszones"
defaultDnsSyncInterval = 3 * time.Minute
)
var (
Flags = &Config{}
dnsZonesString string
)
func init() {
flag.Var(&Flags.DefaultController, "default-controller", "kind of default controller to use. should be one of 'standard', 'public', 'private', or 'off'.")
flag.StringVar(&Flags.NS, "namespace", DefaultNs, "namespace for managed resources")
flag.StringVar(&Flags.Registry, "registry", "mcr.microsoft.com", "container image registry to use for managed components")
flag.StringVar(&Flags.MSIClientID, "msi", "", "client ID of the MSI to use when accessing Azure resources")
flag.StringVar(&Flags.TenantID, "tenant-id", "", "AAD tenant ID to use when accessing Azure resources")
flag.StringVar(&Flags.Cloud, "cloud", "AzurePublicCloud", "azure cloud name")
flag.StringVar(&Flags.Location, "location", "", "azure region name")
flag.StringVar(&dnsZonesString, "dns-zone-ids", "", "dns zone resource IDs")
flag.BoolVar(&Flags.DisableKeyvault, "disable-keyvault", false, "disable the keyvault integration")
flag.Float64Var(&Flags.ConcurrencyWatchdogThres, "concurrency-watchdog-threshold", 200, "percentage of concurrent connections above mean required to vote for load shedding")
flag.IntVar(&Flags.ConcurrencyWatchdogVotes, "concurrency-watchdog-votes", 4, "number of votes required for a pod to be considered for load shedding")
flag.BoolVar(&Flags.DisableOSM, "disable-osm", false, "enable Open Service Mesh integration")
flag.StringVar(&Flags.ServiceAccountTokenPath, "service-account-token-path", "", "optionally override the default token path")
flag.StringVar(&Flags.MetricsAddr, "metrics-addr", "0.0.0.0:8081", "address to serve Prometheus metrics on")
flag.StringVar(&Flags.ProbeAddr, "probe-addr", "0.0.0.0:8080", "address to serve readiness/liveness probes on")
flag.StringVar(&Flags.OperatorDeployment, "operator-deployment", "app-routing-operator", "name of the operator's k8s deployment")
flag.StringVar(&Flags.ClusterUid, "cluster-uid", "", "unique identifier of the cluster the add-on belongs to")
flag.DurationVar(&Flags.DnsSyncInterval, "dns-sync-interval", defaultDnsSyncInterval, "interval at which to sync DNS records")
flag.StringVar(&Flags.CrdPath, "crd", "/crd", "location of the CRD manifests. manifests should be directly in this directory, not in a subdirectory")
flag.BoolVar(&Flags.EnableGateway, "enable-gateway", false, "whether or not to support and create controllers for Gateway API resources")
flag.BoolVar(&Flags.DisableExpensiveCache, "disable-expensive-cache", false, "disable the cache for expensive resources that aren't core to App Routing like Pods and Events")
flag.BoolVar(&Flags.EnableManagedCertificates, "enable-managed-certificates", false, "enable the managed certificates feature")
}
func (c *Config) Validate() error {
if c.NS == "" {
return errors.New("--namespace is required")
}
if c.Registry == "" {
return errors.New("--registry is required")
}
if c.MSIClientID == "" {
return errors.New("--msi is required")
}
if c.TenantID == "" {
return errors.New("--tenant-id is required")
}
if c.Cloud == "" {
return errors.New("--cloud is required")
}
if c.Location == "" {
return errors.New("--location is required")
}
if c.ConcurrencyWatchdogThres <= 100 {
return errors.New("--concurrency-watchdog-threshold must be greater than 100")
}
if c.ConcurrencyWatchdogVotes < 1 {
return errors.New("--concurrency-watchdog-votes must be a positive number")
}
if c.OperatorDeployment == "" {
return errors.New("--operator-deployment is required")
}
if c.ClusterUid == "" {
return errors.New("--cluster-uid is required")
}
if dnsZonesString != "" {
if err := c.ParseAndValidateZoneIDs(dnsZonesString); err != nil {
return err
}
}
if c.DnsSyncInterval <= 0 {
c.DnsSyncInterval = defaultDnsSyncInterval
}
crdPathStat, err := os.Stat(c.CrdPath)
if os.IsNotExist(err) {
return fmt.Errorf("crd path %s does not exist", c.CrdPath)
}
if err != nil {
return fmt.Errorf("checking crd path %s: %s", c.CrdPath, err)
}
if !crdPathStat.IsDir() {
return fmt.Errorf("crd path %s is not a directory", c.CrdPath)
}
return nil
}
func (c *Config) ParseAndValidateZoneIDs(zonesString string) error {
c.PrivateZoneConfig = DnsZoneConfig{}
c.PublicZoneConfig = DnsZoneConfig{}
DNSZoneIDs := strings.Split(zonesString, ",")
for _, zoneId := range DNSZoneIDs {
parsedZone, err := azure.ParseResourceID(zoneId)
if err != nil {
return fmt.Errorf("while parsing dns zone resource ID %s: %s", zoneId, err)
}
switch strings.ToLower(parsedZone.ResourceType) {
case PrivateZoneType:
// it's a private zone
if err := ValidateProviderSubAndRg(parsedZone, c.PrivateZoneConfig.Subscription, c.PrivateZoneConfig.ResourceGroup); err != nil {
return err
}
c.PrivateZoneConfig.Subscription = parsedZone.SubscriptionID
c.PrivateZoneConfig.ResourceGroup = parsedZone.ResourceGroup
if c.PrivateZoneConfig.ZoneIds == nil {
c.PrivateZoneConfig.ZoneIds = map[string]struct{}{}
}
c.PrivateZoneConfig.ZoneIds[strings.ToLower(zoneId)] = struct{}{} // azure resource names are case insensitive
case PublicZoneType:
// it's a public zone
if err := ValidateProviderSubAndRg(parsedZone, c.PublicZoneConfig.Subscription, c.PublicZoneConfig.ResourceGroup); err != nil {
return err
}
c.PublicZoneConfig.Subscription = parsedZone.SubscriptionID
c.PublicZoneConfig.ResourceGroup = parsedZone.ResourceGroup
if c.PublicZoneConfig.ZoneIds == nil {
c.PublicZoneConfig.ZoneIds = map[string]struct{}{}
}
c.PublicZoneConfig.ZoneIds[strings.ToLower(zoneId)] = struct{}{} // azure resource names are case insensitive
default:
return fmt.Errorf("while parsing dns zone resource ID %s: detected invalid resource type %s", zoneId, parsedZone.ResourceType)
}
}
return nil
}
func ValidateProviderSubAndRg(parsedZone azure.Resource, subscription, resourceGroup string) error {
if !strings.EqualFold(parsedZone.Provider, "Microsoft.Network") {
return fmt.Errorf("invalid resource provider %s from zone %s: resource ID must be a public or private DNS Zone resource ID from provider Microsoft.Network", parsedZone.Provider, parsedZone.String())
}
if subscription != "" && !strings.EqualFold(parsedZone.SubscriptionID, subscription) {
return fmt.Errorf("while parsing resource IDs for %s: detected multiple subscriptions %s and %s", parsedZone.ResourceType, parsedZone.SubscriptionID, subscription)
}
if resourceGroup != "" && !strings.EqualFold(parsedZone.ResourceGroup, resourceGroup) {
return fmt.Errorf("while parsing resource IDs for %s: detected multiple resource groups %s and %s", parsedZone.ResourceType, parsedZone.ResourceGroup, resourceGroup)
}
return nil
}