Skip to content

Commit d086de1

Browse files
committed
Add a new "chaosduck" e2e test tool for leaderelection.
This adds a new main package under knative.dev/pkg/leaderelection/chaosduck, which decodes the leases in the system namespace to establish the set of components and their leader pod names and kills a leader pod for each component on a certain period.
1 parent a817277 commit d086de1

File tree

1 file changed

+130
-0
lines changed

1 file changed

+130
-0
lines changed

leaderelection/chaosduck/main.go

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
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+
// The chaosduck binary is an e2e testing tool for leader election, which loads
18+
// the leader election configuration within the system namespace and
19+
// periodically kills one of the leader pods for each HA component.
20+
package main
21+
22+
import (
23+
"context"
24+
"errors"
25+
"log"
26+
"strings"
27+
"time"
28+
29+
"golang.org/x/sync/errgroup"
30+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31+
"k8s.io/apimachinery/pkg/util/sets"
32+
"k8s.io/client-go/kubernetes"
33+
kubeclient "knative.dev/pkg/client/injection/kube/client"
34+
"knative.dev/pkg/controller"
35+
"knative.dev/pkg/injection"
36+
"knative.dev/pkg/injection/sharedmain"
37+
"knative.dev/pkg/signals"
38+
"knative.dev/pkg/system"
39+
)
40+
41+
// components is a mapping from component name to the collection of leader pod names.
42+
type components map[string]sets.String
43+
44+
// buildComponents crawls the list of leases and builds a mapping from component names
45+
// to the set pod names that hold one or more leases.
46+
func buildComponents(kc kubernetes.Interface) (components, error) {
47+
leases, err := kc.CoordinationV1().Leases(system.Namespace()).List(metav1.ListOptions{})
48+
if err != nil {
49+
return nil, err
50+
}
51+
52+
cs := make(components, 5)
53+
for _, lease := range leases.Items {
54+
if lease.Spec.HolderIdentity == nil {
55+
log.Printf("Found lease %q held by nobody!", lease.Name)
56+
continue
57+
}
58+
pod := strings.SplitN(*lease.Spec.HolderIdentity, "_", 2)[0]
59+
60+
parts := strings.Split(pod, "-")
61+
if len(parts) < 3 {
62+
continue
63+
}
64+
deploymentName := strings.Join(parts[:len(parts)-2], "-")
65+
66+
set, ok := cs[deploymentName]
67+
if !ok {
68+
set = sets.NewString()
69+
cs[deploymentName] = set
70+
}
71+
set.Insert(pod)
72+
}
73+
return cs, nil
74+
}
75+
76+
// quack will kill one of the components leader pods.
77+
func quack(ctx context.Context, kc kubernetes.Interface, component string, leaders sets.String) error {
78+
tribute, ok := leaders.PopAny()
79+
if !ok {
80+
return errors.New("this should not be possible, since components are only created when they have components.")
81+
}
82+
log.Printf("Quacking at %q leader %q", component, tribute)
83+
84+
err := kc.CoreV1().Pods(system.Namespace()).Delete(tribute, &metav1.DeleteOptions{})
85+
if err != nil {
86+
return err
87+
}
88+
return nil
89+
}
90+
91+
// frequency is the frequency with which we kill off leaders.
92+
const frequency = 30 * time.Second
93+
94+
func main() {
95+
ctx := signals.NewContext()
96+
97+
// We don't expect informers to be set up, but we do expect the client to get attached to ctx.
98+
ctx, informers := injection.Default.SetupInformers(ctx, sharedmain.ParseAndGetConfigOrDie())
99+
if err := controller.StartInformers(ctx.Done(), informers...); err != nil {
100+
log.Fatalf("Failed to start informers %v", err)
101+
}
102+
kc := kubeclient.Get(ctx)
103+
104+
// Until we are shutdown, build up an index of components and kill
105+
// of a leader at the specified frequency.
106+
for {
107+
select {
108+
case <-time.After(frequency):
109+
components, err := buildComponents(kc)
110+
if err != nil {
111+
log.Printf("Error building components: %v", err)
112+
}
113+
log.Printf("Got components: %#v", components)
114+
115+
eg, ctx := errgroup.WithContext(ctx)
116+
for name, leaders := range components {
117+
name, leaders := name, leaders
118+
eg.Go(func() error {
119+
return quack(ctx, kc, name, leaders)
120+
})
121+
}
122+
if err := eg.Wait(); err != nil {
123+
log.Printf("Ended iteration with err: %v", err)
124+
}
125+
126+
case <-ctx.Done():
127+
return
128+
}
129+
}
130+
}

0 commit comments

Comments
 (0)