Repository: knative/pkg
Pull request #1487 (2020-07-14 15:10:08)

Add flags to disable chaos per-component and configure period.

knative-prow-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/knative/pkg/pull/1487#" title="Author self-approved">mattmoor</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=knative%2Fpkg).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/knative/pkg/blob/master/OWNERS)~~ [mattmoor]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
knative-metrics-robot: The following is the coverage report on the affected files.
Say `/test pull-knative-pkg-go-coverage` to re-run this coverage report

File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
[kflag/set.go](https://storage.cloud.google.com/knative-prow/pr-logs/pull/knative_pkg/1487/pull-knative-pkg-go-coverage/1283056300007100417/artifacts/line-cov.html#file0) | Do not exist | 100.0% | 


Patch:
diff --git a/OWNERS_ALIASES b/OWNERS_ALIASES
index 867be5392..bade21344 100644
--- a/OWNERS_ALIASES
+++ b/OWNERS_ALIASES
@@ -40,6 +40,10 @@ aliases:
   controller-reviewers:
   - whaught
 
+  kflag-approvers:
+  - mattmoor
+  - vagababov
+
   kmeta-approvers:
   - mattmoor
   - vagababov
diff --git a/apis/duck/v1/addressable_types.go b/apis/duck/v1/addressable_types.go
index 3cfcac331..db39ab862 100644
--- a/apis/duck/v1/addressable_types.go
+++ b/apis/duck/v1/addressable_types.go
@@ -63,8 +63,10 @@ type AddressStatus struct {
 	Address *Addressable `json:"address,omitempty"`
 }
 
+// Verify AddressableType resources meet duck contracts.
 var (
-	_ apis.Listable = (*AddressableType)(nil)
+	_ apis.Listable         = (*AddressableType)(nil)
+	_ ducktypes.Populatable = (*AddressableType)(nil)
 )
 
 // GetFullType implements duck.Implementable
diff --git a/apis/duck/v1/kresource_type.go b/apis/duck/v1/kresource_type.go
index 78d8ece66..491c13f1f 100644
--- a/apis/duck/v1/kresource_type.go
+++ b/apis/duck/v1/kresource_type.go
@@ -23,6 +23,7 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/runtime/schema"
+	"knative.dev/pkg/apis/duck/ducktypes"
 
 	"knative.dev/pkg/apis"
 )
@@ -66,6 +67,12 @@ func (t *KResource) Populate() {
 	}}
 }
 
+// Verify KResource resources meet duck contracts.
+var (
+	_ apis.Listable         = (*KResource)(nil)
+	_ ducktypes.Populatable = (*KResource)(nil)
+)
+
 // GetListType implements apis.Listable
 func (*KResource) GetListType() runtime.Object {
 	return &KResourceList{}
diff --git a/apis/duck/v1/podspec_types.go b/apis/duck/v1/podspec_types.go
index 92ed7f1c4..a8d172416 100644
--- a/apis/duck/v1/podspec_types.go
+++ b/apis/duck/v1/podspec_types.go
@@ -31,6 +31,9 @@ import (
 // in the manner of ReplicaSet, Deployment, DaemonSet, StatefulSet.
 type PodSpecable corev1.PodTemplateSpec
 
+// PodSpecable is an Implementable duck type.
+var _ ducktypes.Implementable = (*PodSpecable)(nil)
+
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 
 // WithPod is the shell that demonstrates how PodSpecable types wrap
@@ -47,10 +50,10 @@ type WithPodSpec struct {
 	Template PodSpecable `json:"template,omitempty"`
 }
 
-// Assert that we implement the interfaces necessary to
-// use duck.VerifyType.
+// Verify WithPod resources meet duck contracts.
 var (
-	_ apis.Listable = (*WithPod)(nil)
+	_ apis.Listable         = (*WithPod)(nil)
+	_ ducktypes.Populatable = (*WithPod)(nil)
 )
 
 // GetFullType implements duck.Implementable
diff --git a/apis/duck/v1/source_types.go b/apis/duck/v1/source_types.go
index 03c4d40b9..4ce3eaf60 100644
--- a/apis/duck/v1/source_types.go
+++ b/apis/duck/v1/source_types.go
@@ -88,7 +88,6 @@ type SourceStatus struct {
 // CloudEventAttributes specifies the attributes that a Source
 // uses as part of its CloudEvents.
 type CloudEventAttributes struct {
-
 	// Type refers to the CloudEvent type attribute.
 	Type string `json:"type,omitempty"`
 
@@ -109,8 +108,11 @@ func (ss *SourceStatus) IsReady() bool {
 	return false
 }
 
+// Verify Source resources meet duck contracts.
 var (
-	_ apis.Listable = (*Source)(nil)
+	_ apis.Listable           = (*Source)(nil)
+	_ ducktypes.Implementable = (*Source)(nil)
+	_ ducktypes.Populatable   = (*Source)(nil)
 )
 
 const (
diff --git a/apis/duck/v1/status_types.go b/apis/duck/v1/status_types.go
index f25212e57..5ea757694 100644
--- a/apis/duck/v1/status_types.go
+++ b/apis/duck/v1/status_types.go
@@ -29,6 +29,9 @@ import (
 // Conditions is a simple wrapper around apis.Conditions to implement duck.Implementable.
 type Conditions apis.Conditions
 
+// Conditions is an Implementable duck type.
+var _ ducktypes.Implementable = (*Conditions)(nil)
+
 // Status shows how we expect folks to embed Conditions in
 // their Status field.
 // WARNING: Adding fields to this struct will add them to all Knative resources.
diff --git a/apis/duck/v1beta1/addressable_types.go b/apis/duck/v1beta1/addressable_types.go
index d8984d8ed..ba7bbc85d 100644
--- a/apis/duck/v1beta1/addressable_types.go
+++ b/apis/duck/v1beta1/addressable_types.go
@@ -43,6 +43,9 @@ type Addressable struct {
 var (
 	// Addressable is a Convertible type.
 	_ apis.Convertible = (*Addressable)(nil)
+
+	// Addressable is an Implementable "duck type".
+	_ ducktypes.Implementable = (*Addressable)(nil)
 )
 
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
@@ -64,8 +67,10 @@ type AddressStatus struct {
 	Address *Addressable `json:"address,omitempty"`
 }
 
+// Verify AddressableType resources meet duck contracts.
 var (
-	_ apis.Listable = (*AddressableType)(nil)
+	_ apis.Listable         = (*AddressableType)(nil)
+	_ ducktypes.Populatable = (*AddressableType)(nil)
 )
 
 // GetFullType implements duck.Implementable
diff --git a/apis/duck/v1beta1/binding_types.go b/apis/duck/v1beta1/binding_types.go
new file mode 100644
index 000000000..b9e970b3a
--- /dev/null
+++ b/apis/duck/v1beta1/binding_types.go
@@ -0,0 +1,92 @@
+/*
+Copyright 2020 The Knative Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1beta1
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+
+	"knative.dev/pkg/apis"
+	"knative.dev/pkg/apis/duck"
+	"knative.dev/pkg/tracker"
+)
+
+// +genduck
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+// Binding is a duck type that specifies the partial schema to which all
+// Binding implementations should adhere.
+type Binding struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	Spec BindingSpec `json:"spec"`
+}
+
+// Verify that Binding implements the appropriate interfaces.
+var (
+	_ duck.Implementable = (*Binding)(nil)
+	_ duck.Populatable   = (*Binding)(nil)
+	_ apis.Listable      = (*Binding)(nil)
+)
+
+// BindingSpec specifies the spec portion of the Binding partial-schema.
+type BindingSpec struct {
+	// Subject references the resource(s) whose "runtime contract" should be
+	// augmented by Binding implementations.
+	Subject tracker.Reference `json:"subject"`
+}
+
+// GetFullType implements duck.Implementable
+func (*Binding) GetFullType() duck.Populatable {
+	return &Binding{}
+}
+
+// Populate implements duck.Populatable
+func (t *Binding) Populate() {
+	t.Spec = BindingSpec{
+		Subject: tracker.Reference{
+			APIVersion: "apps/v1",
+			Kind:       "Deployment",
+			Namespace:  "default",
+			// Name and Selector are mutually exclusive,
+			// but we fill them both in for this test.
+			Name: "bazinga",
+			Selector: &metav1.LabelSelector{
+				MatchLabels: map[string]string{
+					"foo": "bar",
+					"baz": "blah",
+				},
+			},
+		},
+	}
+}
+
+// GetListType implements apis.Listable
+func (*Binding) GetListType() runtime.Object {
+	return &BindingList{}
+}
+
+// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
+
+// BindingList is a list of Binding resources
+type BindingList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata"`
+
+	Items []Binding `json:"items"`
+}
diff --git a/apis/duck/v1beta1/register.go b/apis/duck/v1beta1/register.go
index 324639c06..160eff56b 100644
--- a/apis/duck/v1beta1/register.go
+++ b/apis/duck/v1beta1/register.go
@@ -50,6 +50,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
 		(&KResource{}).GetListType(),
 		&AddressableType{},
 		(&AddressableType{}).GetListType(),
+		&Binding{},
+		(&Binding{}).GetListType(),
 	)
 	metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
 	return nil
diff --git a/apis/duck/v1beta1/source_types.go b/apis/duck/v1beta1/source_types.go
index 1a9be0d6c..60a502041 100644
--- a/apis/duck/v1beta1/source_types.go
+++ b/apis/duck/v1beta1/source_types.go
@@ -94,8 +94,11 @@ func (ss *SourceStatus) IsReady() bool {
 	return false
 }
 
+// Verify Source resources meet duck contracts.
 var (
-	_ apis.Listable = (*Source)(nil)
+	_ apis.Listable           = (*Source)(nil)
+	_ ducktypes.Implementable = (*Source)(nil)
+	_ ducktypes.Populatable   = (*Source)(nil)
 )
 
 const (
diff --git a/apis/duck/v1beta1/status_types.go b/apis/duck/v1beta1/status_types.go
index 8054c53be..ff7c68c26 100644
--- a/apis/duck/v1beta1/status_types.go
+++ b/apis/duck/v1beta1/status_types.go
@@ -34,6 +34,9 @@ import (
 // Conditions is a simple wrapper around apis.Conditions to implement duck.Implementable.
 type Conditions apis.Conditions
 
+// Conditions is an Implementable duck type.
+var _ ducktypes.Implementable = (*Conditions)(nil)
+
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 
 // KResource is a skeleton type wrapping Conditions in the manner we expect
@@ -81,8 +84,11 @@ func (s *Status) SetConditions(c apis.Conditions) {
 	s.Conditions = Conditions(c)
 }
 
-// Ensure KResource satisfies apis.Listable
-var _ apis.Listable = (*KResource)(nil)
+// Verify KResource resources meet duck contracts.
+var (
+	_ apis.Listable         = (*KResource)(nil)
+	_ ducktypes.Populatable = (*KResource)(nil)
+)
 
 // GetFullType implements duck.Implementable
 func (*Conditions) GetFullType() ducktypes.Populatable {
diff --git a/apis/duck/v1beta1/zz_generated.deepcopy.go b/apis/duck/v1beta1/zz_generated.deepcopy.go
index 152bb8828..d22c6724a 100644
--- a/apis/duck/v1beta1/zz_generated.deepcopy.go
+++ b/apis/duck/v1beta1/zz_generated.deepcopy.go
@@ -128,6 +128,83 @@ func (in *AddressableTypeList) DeepCopyObject() runtime.Object {
 	return nil
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Binding) DeepCopyInto(out *Binding) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	in.Spec.DeepCopyInto(&out.Spec)
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Binding.
+func (in *Binding) DeepCopy() *Binding {
+	if in == nil {
+		return nil
+	}
+	out := new(Binding)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *Binding) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BindingList) DeepCopyInto(out *BindingList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]Binding, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BindingList.
+func (in *BindingList) DeepCopy() *BindingList {
+	if in == nil {
+		return nil
+	}
+	out := new(BindingList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *BindingList) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BindingSpec) DeepCopyInto(out *BindingSpec) {
+	*out = *in
+	in.Subject.DeepCopyInto(&out.Subject)
+	return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BindingSpec.
+func (in *BindingSpec) DeepCopy() *BindingSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(BindingSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *CloudEventOverrides) DeepCopyInto(out *CloudEventOverrides) {
 	*out = *in
diff --git a/client/injection/ducks/duck/v1beta1/binding/binding.go b/client/injection/ducks/duck/v1beta1/binding/binding.go
new file mode 100644
index 000000000..ac6e4b937
--- /dev/null
+++ b/client/injection/ducks/duck/v1beta1/binding/binding.go
@@ -0,0 +1,60 @@
+/*
+Copyright 2020 The Knative Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by injection-gen. DO NOT EDIT.
+
+package binding
+
+import (
+	context "context"
+
+	duck "knative.dev/pkg/apis/duck"
+	v1beta1 "knative.dev/pkg/apis/duck/v1beta1"
+	controller "knative.dev/pkg/controller"
+	injection "knative.dev/pkg/injection"
+	dynamicclient "knative.dev/pkg/injection/clients/dynamicclient"
+	logging "knative.dev/pkg/logging"
+)
+
+func init() {
+	injection.Default.RegisterDuck(WithDuck)
+}
+
+// Key is used for associating the Informer inside the context.Context.
+type Key struct{}
+
+func WithDuck(ctx context.Context) context.Context {
+	dc := dynamicclient.Get(ctx)
+	dif := &duck.CachedInformerFactory{
+		Delegate: &duck.TypedInformerFactory{
+			Client:       dc,
+			Type:         (&v1beta1.Binding{}).GetFullType(),
+			ResyncPeriod: controller.GetResyncPeriod(ctx),
+			StopChannel:  ctx.Done(),
+		},
+	}
+	return context.WithValue(ctx, Key{}, dif)
+}
+
+// Get extracts the typed informer from the context.
+func Get(ctx context.Context) duck.InformerFactory {
+	untyped := ctx.Value(Key{})
+	if untyped == nil {
+		logging.FromContext(ctx).Panic(
+			"Unable to fetch knative.dev/pkg/apis/duck.InformerFactory from context.")
+	}
+	return untyped.(duck.InformerFactory)
+}
diff --git a/client/injection/ducks/duck/v1beta1/binding/fake/fake.go b/client/injection/ducks/duck/v1beta1/binding/fake/fake.go
new file mode 100644
index 000000000..b51111360
--- /dev/null
+++ b/client/injection/ducks/duck/v1beta1/binding/fake/fake.go
@@ -0,0 +1,30 @@
+/*
+Copyright 2020 The Knative Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by injection-gen. DO NOT EDIT.
+
+package fake
+
+import (
+	binding "knative.dev/pkg/client/injection/ducks/duck/v1beta1/binding"
+	injection "knative.dev/pkg/injection"
+)
+
+var Get = binding.Get
+
+func init() {
+	injection.Fake.RegisterDuck(binding.WithDuck)
+}
diff --git a/kflag/OWNERS b/kflag/OWNERS
new file mode 100644
index 000000000..0222d3f38
--- /dev/null
+++ b/kflag/OWNERS
@@ -0,0 +1,4 @@
+# The OWNERS file is used by prow to automatically merge approved PRs.
+
+approvers:
+- kflag-approvers
diff --git a/kflag/doc.go b/kflag/doc.go
new file mode 100644
index 000000000..1d47245b8
--- /dev/null
+++ b/kflag/doc.go
@@ -0,0 +1,18 @@
+/*
+Copyright 2020 The Knative Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package kflag contains a variety of non-standard command-line flag types
+package kflag
diff --git a/kflag/set.go b/kflag/set.go
new file mode 100644
index 000000000..b219021f2
--- /dev/null
+++ b/kflag/set.go
@@ -0,0 +1,42 @@
+/*
+Copyright 2020 The Knative Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package kflag
+
+import (
+	"flag"
+	"fmt"
+
+	"k8s.io/apimachinery/pkg/util/sets"
+)
+
+type StringSet struct {
+	Value sets.String
+}
+
+var _ flag.Value = (*StringSet)(nil)
+
+func (i *StringSet) String() string {
+	return fmt.Sprintf("%v", i.Value)
+}
+
+func (i *StringSet) Set(value string) error {
+	if i.Value == nil {
+		i.Value = make(sets.String, 1)
+	}
+	i.Value.Insert(value)
+	return nil
+}
diff --git a/leaderelection/chaosduck/main.go b/leaderelection/chaosduck/main.go
index 4f674638f..34145977e 100644
--- a/leaderelection/chaosduck/main.go
+++ b/leaderelection/chaosduck/main.go
@@ -22,6 +22,7 @@ package main
 import (
 	"context"
 	"errors"
+	"flag"
 	"log"
 	"strings"
 	"time"
@@ -34,6 +35,7 @@ import (
 	"knative.dev/pkg/controller"
 	"knative.dev/pkg/injection"
 	"knative.dev/pkg/injection/sharedmain"
+	"knative.dev/pkg/kflag"
 	"knative.dev/pkg/signals"
 	"knative.dev/pkg/system"
 )
@@ -41,6 +43,17 @@ import (
 // components is a mapping from component name to the collection of leader pod names.
 type components map[string]sets.String
 
+var (
+	disabledComponents kflag.StringSet
+	tributePeriod      time.Duration = 30 * time.Second
+)
+
+func init() {
+	// Note that we don't explicitly call flag.Parse() because ParseAndGetConfigOrDie below does this already.
+	flag.Var(&disabledComponents, "disable", "A repeatable flag to disable chaos for certain components.")
+	flag.DurationVar(&tributePeriod, "period", tributePeriod, "How frequently to terminate a leader pod per component.")
+}
+
 func countingRFind(wr rune, wc int) func(rune) bool {
 	cnt := 0
 	return func(r rune) bool {
@@ -101,9 +114,6 @@ func quack(ctx context.Context, kc kubernetes.Interface, component string, leade
 	return kc.CoreV1().Pods(system.Namespace()).Delete(tribute, &metav1.DeleteOptions{})
 }
 
-// frequency is the frequency with which we kill off leaders.
-const frequency = 30 * time.Second
-
 func main() {
 	ctx := signals.NewContext()
 
@@ -118,7 +128,7 @@ func main() {
 	// of a leader at the specified frequency.
 	for {
 		select {
-		case <-time.After(frequency):
+		case <-time.After(tributePeriod):
 			components, err := buildComponents(kc)
 			if err != nil {
 				log.Printf("Error building components: %v", err)
@@ -127,6 +137,9 @@ func main() {
 
 			eg, ctx := errgroup.WithContext(ctx)
 			for name, leaders := range components {
+				if disabledComponents.Value.Has(name) {
+					continue
+				}
 				name, leaders := name, leaders
 				eg.Go(func() error {
 					return quack(ctx, kc, name, leaders)
diff --git a/leaderelection/config.go b/leaderelection/config.go
index b694c4fd2..326956790 100644
--- a/leaderelection/config.go
+++ b/leaderelection/config.go
@@ -32,21 +32,21 @@ import (
 	cm "knative.dev/pkg/configmap"
 )
 
-const configMapNameEnv = "CONFIG_LEADERELECTION_NAME"
+const (
+	configMapNameEnv = "CONFIG_LEADERELECTION_NAME"
+	// KnativeResourceLock is the only supported lock mechanism for Knative.
+	KnativeResourceLock = resourcelock.LeasesResourceLock
+)
 
 // MaxBuckets is the maximum number of buckets to allow users to define.
 // This is a variable so that it may be customized in the binary entrypoint.
 var MaxBuckets uint32 = 10
 
-var validResourceLocks = sets.NewString(resourcelock.LeasesResourceLock)
-
 // NewConfigFromMap returns a Config for the given map, or an error.
 func NewConfigFromMap(data map[string]string) (*Config, error) {
 	config := defaultConfig()
 
 	if err := cm.Parse(data,
-		cm.AsString("resourceLock", &config.ResourceLock),
-
 		cm.AsDuration("leaseDuration", &config.LeaseDuration),
 		cm.AsDuration("renewDeadline", &config.RenewDeadline),
 		cm.AsDuration("retryPeriod", &config.RetryPeriod),
@@ -59,10 +59,6 @@ func NewConfigFromMap(data map[string]string) (*Config, error) {
 	if config.Buckets < 1 || config.Buckets > MaxBuckets {
 		return nil, fmt.Errorf("buckets: value must be between %d <= %d <= %d", 1, config.Buckets, MaxBuckets)
 	}
-	if !validResourceLocks.Has(config.ResourceLock) {
-		return nil, fmt.Errorf(`resourceLock: invalid value %q: valid values are "leases"`, config.ResourceLock)
-	}
-
 	return config, nil
 }
 
@@ -79,7 +75,6 @@ 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 {
-	ResourceLock  string
 	Buckets       uint32
 	LeaseDuration time.Duration
 	RenewDeadline time.Duration
@@ -95,7 +90,6 @@ func (c *Config) GetComponentConfig(name string) ComponentConfig {
 	return ComponentConfig{
 		Component:     name,
 		Buckets:       c.Buckets,
-		ResourceLock:  c.ResourceLock,
 		LeaseDuration: c.LeaseDuration,
 		RenewDeadline: c.RenewDeadline,
 		RetryPeriod:   c.RetryPeriod,
@@ -104,7 +98,6 @@ func (c *Config) GetComponentConfig(name string) ComponentConfig {
 
 func defaultConfig() *Config {
 	return &Config{
-		ResourceLock:  "leases",
 		Buckets:       1,
 		LeaseDuration: 15 * time.Second,
 		RenewDeadline: 10 * time.Second,
@@ -116,7 +109,6 @@ func defaultConfig() *Config {
 type ComponentConfig struct {
 	Component     string
 	Buckets       uint32
-	ResourceLock  string
 	LeaseDuration time.Duration
 	RenewDeadline time.Duration
 	RetryPeriod   time.Duration
diff --git a/leaderelection/context.go b/leaderelection/context.go
index 57b0586fb..18f7e23eb 100644
--- a/leaderelection/context.go
+++ b/leaderelection/context.go
@@ -123,7 +123,7 @@ func (b *standardBuilder) buildElector(ctx context.Context, la reconciler.Leader
 			total: b.lec.Buckets,
 		}
 
-		rl, err := resourcelock.New(b.lec.ResourceLock,
+		rl, err := resourcelock.New(KnativeResourceLock,
 			system.Namespace(), // use namespace we are running in
 			bkt.Name(),
 			b.kc.CoreV1(),


Test patch:
diff --git a/apis/duck/v1/implements_test.go b/apis/duck/v1/implements_test.go
new file mode 100644
index 000000000..a786f41f0
--- /dev/null
+++ b/apis/duck/v1/implements_test.go
@@ -0,0 +1,58 @@
+/*
+Copyright 2020 The Knative Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1
+
+import (
+	"testing"
+
+	appsv1 "k8s.io/api/apps/v1"
+	batchv1 "k8s.io/api/batch/v1"
+
+	"knative.dev/pkg/apis/duck"
+	"knative.dev/pkg/apis/duck/ducktypes"
+)
+
+func TestTypesImplements(t *testing.T) {
+	testCases := []struct {
+		instance interface{}
+		iface    ducktypes.Implementable
+	}{
+		{instance: &AddressableType{}, iface: &Addressable{}},
+		{instance: &KResource{}, iface: &Conditions{}},
+	}
+	for _, tc := range testCases {
+		if err := duck.VerifyType(tc.instance, tc.iface); err != nil {
+			t.Error(err)
+		}
+	}
+}
+
+func TestImplementsPodSpecable(t *testing.T) {
+	instances := []interface{}{
+		&WithPod{},
+		&appsv1.ReplicaSet{},
+		&appsv1.Deployment{},
+		&appsv1.StatefulSet{},
+		&appsv1.DaemonSet{},
+		&batchv1.Job{},
+	}
+	for _, instance := range instances {
+		if err := duck.VerifyType(instance, &PodSpecable{}); err != nil {
+			t.Error(err)
+		}
+	}
+}
diff --git a/apis/duck/v1_tests.go b/apis/duck/v1_tests.go
deleted file mode 100644
index ba56a994c..000000000
--- a/apis/duck/v1_tests.go
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
-Copyright 2020 The Knative Authors
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package duck
-
-import (
-	"testing"
-
-	appsv1 "k8s.io/api/apps/v1"
-	batchv1 "k8s.io/api/batch/v1"
-
-	v1 "knative.dev/pkg/apis/duck/v1"
-)
-
-// Conditions is an Implementable "duck type".
-var _ Implementable = (*v1.Conditions)(nil)
-
-// In order for Conditions to be Implementable, KResource must be Populatable.
-var _ Populatable = (*v1.KResource)(nil)
-
-// Source is an Implementable "duck type".
-var _ Implementable = (*v1.Source)(nil)
-
-// Verify Source resources meet duck contracts.
-var _ Populatable = (*v1.Source)(nil)
-
-var _ Populatable = (*v1.WithPod)(nil)
-var _ Implementable = (*v1.PodSpecable)(nil)
-
-func TestV1TypesImplements(t *testing.T) {
-	testCases := []struct {
-		instance interface{}
-		iface    Implementable
-	}{
-		{instance: &v1.AddressableType{}, iface: &v1.Addressable{}},
-		{instance: &v1.KResource{}, iface: &v1.Conditions{}},
-	}
-	for _, tc := range testCases {
-		if err := VerifyType(tc.instance, tc.iface); err != nil {
-			t.Error(err)
-		}
-	}
-}
-
-func TestV1ImplementsPodSpecable(t *testing.T) {
-	instances := []interface{}{
-		&v1.WithPod{},
-		&appsv1.ReplicaSet{},
-		&appsv1.Deployment{},
-		&appsv1.StatefulSet{},
-		&appsv1.DaemonSet{},
-		&batchv1.Job{},
-	}
-	for _, instance := range instances {
-		if err := VerifyType(instance, &v1.PodSpecable{}); err != nil {
-			t.Error(err)
-		}
-	}
-}
-
-// Addressable is an Implementable "duck type".
-var _ Implementable = (*v1.Addressable)(nil)
-
-// Verify AddressableType resources meet duck contracts.
-var _ Populatable = (*v1.AddressableType)(nil)
diff --git a/apis/duck/v1alpha1/implements_test.go b/apis/duck/v1alpha1/implements_test.go
index 60ea9689c..56a4250d6 100644
--- a/apis/duck/v1alpha1/implements_test.go
+++ b/apis/duck/v1alpha1/implements_test.go
@@ -20,7 +20,6 @@ import (
 	"testing"
 
 	"knative.dev/pkg/apis/duck"
-	"knative.dev/pkg/apis/duck/v1beta1"
 )
 
 func TestTypesImplements(t *testing.T) {
@@ -29,7 +28,6 @@ func TestTypesImplements(t *testing.T) {
 		iface    duck.Implementable
 	}{
 		{instance: &AddressableType{}, iface: &Addressable{}},
-		{instance: &AddressableType{}, iface: &v1beta1.Addressable{}},
 		{instance: &LegacyTarget{}, iface: &LegacyTargetable{}},
 		{instance: &Target{}, iface: &Targetable{}},
 		{instance: &Binding{}, iface: &Binding{}},
diff --git a/apis/duck/v1beta1/implements_test.go b/apis/duck/v1beta1/implements_test.go
new file mode 100644
index 000000000..3e6c890ae
--- /dev/null
+++ b/apis/duck/v1beta1/implements_test.go
@@ -0,0 +1,40 @@
+/*
+Copyright 2020 The Knative Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1beta1
+
+import (
+	"testing"
+
+	"knative.dev/pkg/apis/duck"
+	"knative.dev/pkg/apis/duck/ducktypes"
+)
+
+func TestTypesImplements(t *testing.T) {
+	testCases := []struct {
+		instance interface{}
+		iface    ducktypes.Implementable
+	}{
+		{instance: &AddressableType{}, iface: &Addressable{}},
+		{instance: &KResource{}, iface: &Conditions{}},
+		{instance: &Source{}, iface: &Conditions{}},
+	}
+	for _, tc := range testCases {
+		if err := duck.VerifyType(tc.instance, tc.iface); err != nil {
+			t.Error(err)
+		}
+	}
+}
diff --git a/apis/duck/v1beta1_tests.go b/apis/duck/v1beta1_tests.go
deleted file mode 100644
index 23731d8c7..000000000
--- a/apis/duck/v1beta1_tests.go
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
-Copyright 2020 The Knative Authors
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package duck
-
-import (
-	"testing"
-
-	"knative.dev/pkg/apis/duck/v1beta1"
-)
-
-// v1beta1.Conditions is an Implementable "duck type".
-var _ Implementable = (*v1beta1.Conditions)(nil)
-
-// In order for v1beta1.Conditions to be Implementable, v1beta1.KResource must be Populatable.
-var _ Populatable = (*v1beta1.KResource)(nil)
-
-// v1beta1.Source is an Implementable "duck type".
-var _ Implementable = (*v1beta1.Source)(nil)
-
-// Verify v1beta1.Source resources meet duck contracts.
-var _ Populatable = (*v1beta1.Source)(nil)
-
-// Addressable is an Implementable "duck type".
-var _ Implementable = (*v1beta1.Addressable)(nil)
-
-// Verify AddressableType resources meet duck contracts.
-var _ Populatable = (*v1beta1.AddressableType)(nil)
-
-func TestV1Beta1TypesImplements(t *testing.T) {
-	testCases := []struct {
-		instance interface{}
-		iface    Implementable
-	}{
-		{instance: &v1beta1.AddressableType{}, iface: &v1beta1.Addressable{}},
-		{instance: &v1beta1.KResource{}, iface: &v1beta1.Conditions{}},
-	}
-	for _, tc := range testCases {
-		if err := VerifyType(tc.instance, tc.iface); err != nil {
-			t.Error(err)
-		}
-	}
-}
diff --git a/controller/controller_test.go b/controller/controller_test.go
index f03afeedd..629ecc0f0 100644
--- a/controller/controller_test.go
+++ b/controller/controller_test.go
@@ -918,7 +918,6 @@ func TestStartAndShutdownWithLeaderAwareWithLostElection(t *testing.T) {
 	}
 	cc := leaderelection.ComponentConfig{
 		Component:     "component",
-		ResourceLock:  "leases",
 		LeaseDuration: 15 * time.Second,
 		RenewDeadline: 10 * time.Second,
 		RetryPeriod:   2 * time.Second,
diff --git a/kflag/set_test.go b/kflag/set_test.go
new file mode 100644
index 000000000..9d7f047c6
--- /dev/null
+++ b/kflag/set_test.go
@@ -0,0 +1,70 @@
+/*
+Copyright 2020 The Knative Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package kflag
+
+import (
+	"flag"
+	"testing"
+)
+
+func TestSet(t *testing.T) {
+	var f StringSet
+
+	fs := flag.NewFlagSet("blah", flag.ContinueOnError)
+	fs.Var(&f, "flag-name", "usage description")
+
+	if err := fs.Parse([]string{
+		"-flag-name=val1",
+		"-flag-name=val1",
+		"-flag-name=val2",
+		"-flag-name=val3",
+		"-flag-name=val1",
+	}); err != nil {
+		t.Fatalf("Parse() = %v", err)
+	}
+
+	if got, want := f.Value.Len(), 3; got != want {
+		t.Errorf("Len() = %d, wanted %d", want, got)
+	}
+	if !f.Value.Has("val1") {
+		t.Error("Has(val1) = false, wanted true")
+	}
+	if !f.Value.Has("val2") {
+		t.Error("Has(val1) = false, wanted true")
+	}
+	if !f.Value.Has("val3") {
+		t.Error("Has(val1) = false, wanted true")
+	}
+}
+
+func TestEmptySet(t *testing.T) {
+	var f StringSet
+
+	fs := flag.NewFlagSet("blah", flag.ContinueOnError)
+	fs.Var(&f, "flag-name", "usage description")
+
+	if err := fs.Parse([]string{}); err != nil {
+		t.Fatalf("Parse() = %v", err)
+	}
+
+	if got, want := f.Value.Len(), 0; got != want {
+		t.Errorf("Len() = %d, wanted %d", want, got)
+	}
+	if f.Value.Has("val1") {
+		t.Error("Has(val1) = true, wanted false")
+	}
+}
diff --git a/leaderelection/config_test.go b/leaderelection/config_test.go
index 44a1516a5..2dfdcd389 100644
--- a/leaderelection/config_test.go
+++ b/leaderelection/config_test.go
@@ -30,7 +30,6 @@ import (
 
 func okConfig() *Config {
 	return &Config{
-		ResourceLock:  "leases",
 		Buckets:       1,
 		LeaseDuration: 15 * time.Second,
 		RenewDeadline: 10 * time.Second,
@@ -40,8 +39,7 @@ func okConfig() *Config {
 
 func okData() map[string]string {
 	return map[string]string{
-		"resourceLock": "leases",
-		"buckets":      "1",
+		"buckets": "1",
 		// values in this data come from the defaults suggested in the
 		// code:
 		// https://github.com/kubernetes/client-go/blob/kubernetes-1.16.0/tools/leaderelection/leaderelection.go
@@ -71,12 +69,6 @@ func TestNewConfigMapFromData(t *testing.T) {
 			config.Buckets = 5
 			return config
 		}(),
-	}, {
-		name: "invalid resourceLock",
-		data: kmeta.UnionMaps(okData(), map[string]string{
-			"resourceLock": "flarps",
-		}),
-		err: errors.New(`resourceLock: invalid value "flarps": valid values are "leases"`),
 	}, {
 		name: "invalid leaseDuration",
 		data: kmeta.UnionMaps(okData(), map[string]string{
@@ -142,14 +134,12 @@ func TestGetComponentConfig(t *testing.T) {
 	}{{
 		name: "component enabled",
 		config: Config{
-			ResourceLock:  "leases",
 			LeaseDuration: 15 * time.Second,
 			RenewDeadline: 10 * time.Second,
 			RetryPeriod:   2 * time.Second,
 		},
 		expected: ComponentConfig{
 			Component:     expectedName,
-			ResourceLock:  "leases",
 			LeaseDuration: 15 * time.Second,
 			RenewDeadline: 10 * time.Second,
 			RetryPeriod:   2 * time.Second,
diff --git a/leaderelection/context_test.go b/leaderelection/context_test.go
index 0a66ae3f2..d46ebb628 100644
--- a/leaderelection/context_test.go
+++ b/leaderelection/context_test.go
@@ -38,7 +38,6 @@ func TestWithBuilder(t *testing.T) {
 	cc := ComponentConfig{
 		Component:     "the-component",
 		Buckets:       1,
-		ResourceLock:  "leases",
 		LeaseDuration: 15 * time.Second,
 		RenewDeadline: 10 * time.Second,
 		RetryPeriod:   2 * time.Second,