knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/leaderelection/config.go (about) 1 /* 2 Copyright 2020 The Knative Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package leaderelection 18 19 import ( 20 "context" 21 "fmt" 22 "os" 23 "strconv" 24 "strings" 25 "time" 26 27 "github.com/kelseyhightower/envconfig" 28 corev1 "k8s.io/api/core/v1" 29 "k8s.io/apimachinery/pkg/util/uuid" 30 "k8s.io/client-go/tools/leaderelection/resourcelock" 31 32 cm "knative.dev/pkg/configmap" 33 ) 34 35 const ( 36 configMapNameEnv = "CONFIG_LEADERELECTION_NAME" 37 // knativeResourceLock is the only supported lock mechanism for Knative. 38 knativeResourceLock = resourcelock.LeasesResourceLock 39 ) 40 41 // MaxBuckets is the maximum number of buckets to allow users to define. 42 // This is a variable so that it may be customized in the binary entrypoint. 43 var MaxBuckets uint32 = 10 44 45 // NewConfigFromMap returns a Config for the given map, or an error. 46 func NewConfigFromMap(data map[string]string) (*Config, error) { 47 config := defaultConfig() 48 49 if err := cm.Parse(data, 50 // Parse legacy keys first 51 cm.AsDuration("leaseDuration", &config.LeaseDuration), 52 cm.AsDuration("renewDeadline", &config.RenewDeadline), 53 cm.AsDuration("retryPeriod", &config.RetryPeriod), 54 55 cm.AsDuration("lease-duration", &config.LeaseDuration), 56 cm.AsDuration("renew-deadline", &config.RenewDeadline), 57 cm.AsDuration("retry-period", &config.RetryPeriod), 58 59 cm.AsUint32("buckets", &config.Buckets), 60 61 cm.CollectMapEntriesWithPrefix("map-lease-prefix", &config.LeaseNamesPrefixMapping), 62 ); err != nil { 63 return nil, err 64 } 65 66 if config.Buckets < 1 || config.Buckets > MaxBuckets { 67 return nil, fmt.Errorf("buckets: value must be between %d <= %d <= %d", 1, config.Buckets, MaxBuckets) 68 } 69 return config, nil 70 } 71 72 // NewConfigFromConfigMap returns a new Config from the given ConfigMap. 73 func NewConfigFromConfigMap(configMap *corev1.ConfigMap) (*Config, error) { 74 if configMap == nil { 75 config := defaultConfig() 76 return config, nil 77 } 78 return NewConfigFromMap(configMap.Data) 79 } 80 81 // Config represents the leader election config for a set of components 82 // contained within a single namespace. Typically these will correspond to a 83 // single source repository, viz: serving or eventing. 84 type Config struct { 85 Buckets uint32 86 LeaseDuration time.Duration 87 RenewDeadline time.Duration 88 RetryPeriod time.Duration 89 LeaseNamesPrefixMapping map[string]string 90 } 91 92 type lecfg struct{} 93 94 // WithConfig associates a leader election configuration with the 95 // context. 96 func WithConfig(ctx context.Context, cfg *Config) context.Context { 97 return context.WithValue(ctx, lecfg{}, cfg) 98 } 99 100 // GetConfig gets the leader election config from the provided 101 // context. 102 func GetConfig(ctx context.Context) *Config { 103 untyped := ctx.Value(lecfg{}) 104 if untyped == nil { 105 return nil 106 } 107 return untyped.(*Config) 108 } 109 110 func (c *Config) GetComponentConfig(name string) ComponentConfig { 111 return ComponentConfig{ 112 Component: name, 113 Buckets: c.Buckets, 114 LeaseDuration: c.LeaseDuration, 115 RenewDeadline: c.RenewDeadline, 116 RetryPeriod: c.RetryPeriod, 117 LeaseNamesPrefixMapping: c.LeaseNamesPrefixMapping, 118 } 119 } 120 121 func defaultConfig() *Config { 122 return &Config{ 123 Buckets: 1, 124 LeaseDuration: 60 * time.Second, 125 RenewDeadline: 40 * time.Second, 126 RetryPeriod: 10 * time.Second, 127 } 128 } 129 130 // ComponentConfig represents the leader election config for a single component. 131 type ComponentConfig struct { 132 Component string 133 Buckets uint32 134 LeaseDuration time.Duration 135 RenewDeadline time.Duration 136 RetryPeriod time.Duration 137 // LeaseName is a function to customize the lease name given the index i. 138 // If not present, a name in format {Component}.{queue-name}.{i}-of-{Buckets} 139 // will be use. 140 // Autoscaler need to know the Lease names to filter out Leases which are not 141 // used for Autoscaler. Instead of exposing the names from leadelection package, 142 // we let Autoscaler to pass them in. 143 LeaseName func(i uint32) string `json:"-"` 144 // Identity is the unique string identifying a resource lock holder across 145 // all participants in an election. If not present, a new unique string will 146 // be generated to be used as identity for each BuildElector call. 147 // Autoscaler uses the pod IP as identity. 148 Identity string 149 150 // LeaseNamesPrefixMapping maps lease prefixes 151 // from <component>.<package>.<reconciler_type_name> to the 152 // associated value when using standardBuilder. 153 LeaseNamesPrefixMapping map[string]string 154 } 155 156 // statefulSetID is a envconfig Decodable controller ordinal and name. 157 type statefulSetID struct { 158 ssName string 159 ordinal int 160 } 161 162 func (ssID *statefulSetID) Decode(v string) error { 163 if i := strings.LastIndex(v, "-"); i != -1 { 164 ui, err := strconv.Atoi(v[i+1:]) 165 ssID.ordinal = ui 166 ssID.ssName = v[:i] 167 return err 168 } 169 return fmt.Errorf("%q is not a valid stateful set controller ordinal", v) 170 } 171 172 var _ envconfig.Decoder = (*statefulSetID)(nil) 173 174 // statefulSetConfig represents the required information for a StatefulSet service. 175 type statefulSetConfig struct { 176 StatefulSetID statefulSetID `envconfig:"STATEFUL_CONTROLLER_ORDINAL" required:"true"` 177 ServiceName string `envconfig:"STATEFUL_SERVICE_NAME" required:"true"` 178 Port string `envconfig:"STATEFUL_SERVICE_PORT" default:"80"` 179 Protocol string `envconfig:"STATEFUL_SERVICE_PROTOCOL" default:"http"` 180 } 181 182 // newStatefulSetConfig builds a stateful set LE config. 183 func newStatefulSetConfig() (*statefulSetConfig, error) { 184 ssc := &statefulSetConfig{} 185 if err := envconfig.Process("", ssc); err != nil { 186 return nil, err 187 } 188 return ssc, nil 189 } 190 191 // ConfigMapName returns the name of the configmap to read for leader election 192 // settings. 193 func ConfigMapName() string { 194 cm := os.Getenv(configMapNameEnv) 195 if cm == "" { 196 return "config-leader-election" 197 } 198 return cm 199 } 200 201 // UniqueID returns a unique ID for use with a leader elector that prevents from 202 // pods running on the same host from colliding with one another. 203 func UniqueID() (string, error) { 204 id, err := os.Hostname() 205 if err != nil { 206 return "", err 207 } 208 209 return id + "_" + string(uuid.NewUUID()), nil 210 }