Allow overriding lease name for a reconciler (#2435)

Add a `map-lease-prefix` prefix for config keys for
`config-leader-election` that is a map from a generated lease prefix
to a new prefix:

```yaml
map-lease-prefix.<component>.<package>.<reconciler_type_name>: <new_prefix>
map-lease-prefix.<component-x>.<package-x>.<reconciler_type_name-x>: <new_prefix>
```

Signed-off-by: Pierangelo Di Pilato <pierdipi@redhat.com>
This commit is contained in:
Pierangelo Di Pilato 2022-02-28 20:55:09 +01:00 committed by GitHub
parent 9b5c41135d
commit fe26417344
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 102 additions and 10 deletions

View File

@ -244,3 +244,16 @@ func Parse(data map[string]string, parsers ...ParseFunc) error {
}
return nil
}
// AsOptionalMap parses the data into the target as a map[string]string, if it exists.
// The map is represented as a list of key-value pairs with a common prefix.
func AsOptionalMap(prefix string, target map[string]string) ParseFunc {
return func(data map[string]string) error {
for k, v := range data {
if strings.HasPrefix(k, prefix) && len(k) > len(prefix)+1 {
target[k[len(prefix)+1: /* remove dot `.` */]] = v
}
}
return nil
}
}

View File

@ -42,6 +42,8 @@ type testConfig struct {
nsn types.NamespacedName
onsn *types.NamespacedName
dict map[string]string
}
func TestParse(t *testing.T) {
@ -70,6 +72,12 @@ func TestParse(t *testing.T) {
"test-namespaced-name": "some-namespace/some-name",
"test-optional-namespaced-name": "some-other-namespace/some-other-name",
"test-dict.k": "v",
"test-dict.k1": "v1",
},
conf: testConfig{
dict: map[string]string{},
},
want: testConfig{
str: "foo.bar",
@ -92,6 +100,10 @@ func TestParse(t *testing.T) {
Name: "some-other-name",
Namespace: "some-other-namespace",
},
dict: map[string]string{
"k": "v",
"k1": "v1",
},
},
}, {
name: "respect defaults",
@ -175,6 +187,18 @@ func TestParse(t *testing.T) {
"test-namespaced-name": "default/resource/whut",
},
expectErr: true,
}, {
name: "dict without key and dot",
data: map[string]string{
"test-dict": "v",
},
expectErr: false,
}, {
name: "dict without key",
data: map[string]string{
"test-dict.": "v",
},
expectErr: false,
}}
for _, test := range tests {
@ -194,6 +218,7 @@ func TestParse(t *testing.T) {
AsQuantity("test-quantity", &test.conf.qua),
AsNamespacedName("test-namespaced-name", &test.conf.nsn),
AsOptionalNamespacedName("test-optional-namespaced-name", &test.conf.onsn),
AsOptionalMap("test-dict", test.conf.dict),
); (err == nil) == test.expectErr {
t.Fatal("Failed to parse data:", err)
}

View File

@ -56,6 +56,8 @@ func NewConfigFromMap(data map[string]string) (*Config, error) {
cm.AsDuration("retry-period", &config.RetryPeriod),
cm.AsUint32("buckets", &config.Buckets),
cm.AsOptionalMap("map-lease-prefix", config.LeaseNamesPrefixMapping),
); err != nil {
return nil, err
}
@ -79,19 +81,21 @@ func NewConfigFromConfigMap(configMap *corev1.ConfigMap) (*Config, error) {
// contained within a single namespace. Typically these will correspond to a
// single source repository, viz: serving or eventing.
type Config struct {
Buckets uint32
LeaseDuration time.Duration
RenewDeadline time.Duration
RetryPeriod time.Duration
Buckets uint32
LeaseDuration time.Duration
RenewDeadline time.Duration
RetryPeriod time.Duration
LeaseNamesPrefixMapping map[string]string
}
func (c *Config) GetComponentConfig(name string) ComponentConfig {
return ComponentConfig{
Component: name,
Buckets: c.Buckets,
LeaseDuration: c.LeaseDuration,
RenewDeadline: c.RenewDeadline,
RetryPeriod: c.RetryPeriod,
Component: name,
Buckets: c.Buckets,
LeaseDuration: c.LeaseDuration,
RenewDeadline: c.RenewDeadline,
RetryPeriod: c.RetryPeriod,
LeaseNamesPrefixMapping: c.LeaseNamesPrefixMapping,
}
}
@ -123,6 +127,11 @@ type ComponentConfig struct {
// be generated to be used as identity for each BuildElector call.
// Autoscaler uses the pod IP as identity.
Identity string
// LeaseNamesPrefixMapping maps lease prefixes
// from <component>.<package>.<reconciler_type_name> to the
// associated value when using standardBuilder.
LeaseNamesPrefixMapping map[string]string
}
// statefulSetID is a envconfig Decodable controller ordinal and name.

View File

@ -27,6 +27,7 @@ import (
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/leaderelection"
"k8s.io/client-go/tools/leaderelection/resourcelock"
"knative.dev/pkg/hash"
"knative.dev/pkg/logging"
"knative.dev/pkg/network"
@ -192,7 +193,11 @@ func newStandardBuckets(queueName string, cc ComponentConfig) []reconciler.Bucke
}
func standardBucketName(ordinal uint32, queueName string, cc ComponentConfig) string {
return strings.ToLower(fmt.Sprintf("%s.%s.%02d-of-%02d", cc.Component, queueName, ordinal, cc.Buckets))
prefix := fmt.Sprintf("%s.%s", cc.Component, queueName)
if v, ok := cc.LeaseNamesPrefixMapping[prefix]; ok && len(v) > 0 {
prefix = v
}
return strings.ToLower(fmt.Sprintf("%s.%02d-of-%02d", prefix, ordinal, cc.Buckets))
}
type statefulSetBuilder struct {

View File

@ -29,6 +29,7 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
fakekube "k8s.io/client-go/kubernetes/fake"
ktesting "k8s.io/client-go/testing"
"knative.dev/pkg/reconciler"
_ "knative.dev/pkg/system/testing"
)
@ -355,3 +356,42 @@ func TestWithUnopposedElector(t *testing.T) {
// Wait to see if PromoteFunc is called with nil or our enq function.
<-time.After(time.Second)
}
func TestStandardBucketName(t *testing.T) {
tests := []struct {
name string
ordinal uint32
queueName string
cc ComponentConfig
want string
}{
{
name: "identity",
ordinal: 0,
queueName: "queue-queue",
cc: ComponentConfig{
Component: "my-comp",
},
want: "my-comp.queue-queue.00-of-00",
},
{
name: "remapping",
ordinal: 0,
queueName: "queue-queue",
cc: ComponentConfig{
Component: "my-comp",
LeaseNamesPrefixMapping: map[string]string{
"my-comp.queue-queue": "my-comp-2.queue",
},
},
want: "my-comp-2.queue.00-of-00",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := standardBucketName(tt.ordinal, tt.queueName, tt.cc); got != tt.want {
t.Errorf("got %v, want %v", got, tt.want)
}
})
}
}