├── PROJECT
├── example-2pod.png
├── .gitignore
├── pkg
└── mtime
│ └── mtime.go
├── .github
└── workflows
│ └── go.yml
├── config
├── rbac
│ └── role_binding.yaml
└── manager
│ └── manager.yaml
├── Dockerfile
├── go.mod
├── controllers
└── events
│ ├── object_test.go
│ ├── recent.go
│ ├── outgoing.go
│ ├── pending.go
│ ├── playback.go
│ ├── mocks_for_test.go
│ ├── object.go
│ ├── event.go
│ ├── event_controller_test.go
│ ├── event_controller.go
│ └── testdata
│ └── deployment-2-pods.yaml
├── Makefile
├── README.md
├── main.go
├── LICENSE
└── go.sum
/PROJECT:
--------------------------------------------------------------------------------
1 | domain: weave.works
2 | repo: github.com/weaveworks-experiments/kspan
3 | version: "2"
4 |
--------------------------------------------------------------------------------
/example-2pod.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/weaveworks-experiments/kspan/HEAD/example-2pod.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Binaries for programs and plugins
3 | *.exe
4 | *.exe~
5 | *.dll
6 | *.so
7 | *.dylib
8 | bin
9 |
10 | # Test binary, build with `go test -c`
11 | *.test
12 |
13 | # Output of the go coverage tool, specifically when used with LiteIDE
14 | *.out
15 |
16 | # Kubernetes Generated files - skip generated files, except for vendored files
17 |
18 | !vendor/**/zz_generated.*
19 |
20 | # editor and IDE paraphernalia
21 | .idea
22 | *.swp
23 | *.swo
24 | *~
25 |
--------------------------------------------------------------------------------
/pkg/mtime/mtime.go:
--------------------------------------------------------------------------------
1 | // Tiny utility to mock time.Now() for testing
2 | package mtime
3 |
4 | import "time"
5 |
6 | // Now returns the current time.
7 | var Now = func() time.Time { return time.Now() }
8 |
9 | // NowForce sets the time returned by Now to t.
10 | func NowForce(t time.Time) {
11 | Now = func() time.Time { return t }
12 | }
13 |
14 | // NowReset makes Now returns the current time again.
15 | func NowReset() {
16 | Now = func() time.Time { return time.Now() }
17 | }
18 |
--------------------------------------------------------------------------------
/.github/workflows/go.yml:
--------------------------------------------------------------------------------
1 | name: Go
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | jobs:
10 |
11 | build:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v2
15 |
16 | - name: Set up Go
17 | uses: actions/setup-go@v2
18 | with:
19 | go-version: 1.16
20 |
21 | - name: Lint
22 | uses: golangci/golangci-lint-action@v2
23 | with:
24 | version: latest
25 | args: --timeout=5m
26 |
27 | - name: Build
28 | run: make
29 |
30 | - name: Test
31 | run: make test
32 |
--------------------------------------------------------------------------------
/config/rbac/role_binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | name: kspan
5 | ---
6 | apiVersion: v1
7 | kind: ServiceAccount
8 | metadata:
9 | name: kspan
10 | namespace: kspan
11 | ---
12 | apiVersion: rbac.authorization.k8s.io/v1
13 | kind: ClusterRole
14 | metadata:
15 | name: kspan
16 | namespace: kspan
17 | rules:
18 | - apiGroups:
19 | - "*"
20 | resources:
21 | - "*"
22 | verbs:
23 | - "*"
24 | ---
25 | apiVersion: rbac.authorization.k8s.io/v1
26 | kind: ClusterRoleBinding
27 | metadata:
28 | name: kspan
29 | namespace: kspan
30 | roleRef:
31 | apiGroup: rbac.authorization.k8s.io
32 | kind: ClusterRole
33 | name: kspan
34 | subjects:
35 | - kind: ServiceAccount
36 | name: kspan
37 | namespace: kspan
38 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Build the manager binary
2 | FROM golang:1.16.2 as builder
3 |
4 | WORKDIR /workspace
5 | # Copy the Go Modules manifests
6 | COPY go.mod go.mod
7 | COPY go.sum go.sum
8 | # cache deps before building and copying source so that we don't need to re-download as much
9 | # and so that source changes don't invalidate our downloaded layer
10 | RUN go mod download
11 |
12 | # Copy the go source
13 | COPY main.go main.go
14 | COPY controllers/ controllers/
15 | COPY pkg/ pkg/
16 |
17 | # Build
18 | RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager main.go
19 |
20 | # Use distroless as minimal base image to package the manager binary
21 | # Refer to https://github.com/GoogleContainerTools/distroless for more details
22 | FROM gcr.io/distroless/static:nonroot
23 | WORKDIR /
24 | COPY --from=builder /workspace/manager .
25 | USER nonroot:nonroot
26 |
27 | ENTRYPOINT ["/manager"]
28 |
--------------------------------------------------------------------------------
/config/manager/manager.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: controller-manager
5 | namespace: kspan
6 | labels:
7 | control-plane: controller-manager
8 | spec:
9 | selector:
10 | matchLabels:
11 | control-plane: controller-manager
12 | replicas: 1
13 | template:
14 | metadata:
15 | labels:
16 | control-plane: controller-manager
17 | spec:
18 | containers:
19 | - command:
20 | - /manager
21 | args:
22 | - --otlp-addr=otel-collector.default:4317
23 | image: weaveworks/kspan:main-12eefbe6
24 | name: manager
25 | resources:
26 | limits:
27 | cpu: 100m
28 | memory: 30Mi
29 | requests:
30 | cpu: 100m
31 | memory: 20Mi
32 | terminationGracePeriodSeconds: 10
33 | serviceAccountName: kspan
34 | automountServiceAccountToken: true
35 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/weaveworks-experiments/kspan
2 |
3 | go 1.13
4 |
5 | require (
6 | github.com/go-logr/logr v0.1.0
7 | github.com/imdario/mergo v0.3.9 // indirect
8 | github.com/kr/text v0.2.0 // indirect
9 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
10 | github.com/onsi/ginkgo v1.14.0 // indirect
11 | github.com/onsi/gomega v1.10.1
12 | github.com/pkg/errors v0.9.1
13 | github.com/prometheus/client_golang v1.0.0
14 | github.com/prometheus/procfs v0.0.5 // indirect
15 | go.opentelemetry.io/otel v0.19.0
16 | go.opentelemetry.io/otel/exporters/otlp v0.19.0
17 | go.opentelemetry.io/otel/sdk v0.19.0
18 | go.opentelemetry.io/otel/trace v0.19.0
19 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
20 | google.golang.org/grpc v1.36.0
21 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
22 | k8s.io/api v0.18.2
23 | k8s.io/apimachinery v0.18.6
24 | k8s.io/client-go v0.18.2
25 | k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29 // indirect
26 | sigs.k8s.io/controller-runtime v0.6.0
27 | sigs.k8s.io/yaml v1.2.0
28 | )
29 |
--------------------------------------------------------------------------------
/controllers/events/object_test.go:
--------------------------------------------------------------------------------
1 | package events
2 |
3 | import (
4 | "testing"
5 | "time"
6 |
7 | o "github.com/onsi/gomega"
8 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
9 | "sigs.k8s.io/yaml"
10 | )
11 |
12 | func Test_getUpdateSource(t *testing.T) {
13 | g := o.NewWithT(t)
14 |
15 | tests := []struct {
16 | name string
17 | objYaml string
18 | wantSource string
19 | wantOperation string
20 | wantTs time.Time
21 | }{
22 | {
23 | name: "managedFields-kubeadm",
24 | wantSource: "kubeadm",
25 | wantOperation: "Update",
26 | wantTs: time.Date(2020, time.November, 24, 16, 52, 40, 0, time.UTC),
27 | objYaml: deploymentMFkubeadm,
28 | },
29 | {
30 | name: "managedFields-other",
31 | wantSource: "other",
32 | wantOperation: "Update",
33 | wantTs: time.Date(2020, time.November, 24, 16, 53, 56, 0, time.UTC),
34 | objYaml: deploymentMFother,
35 | },
36 | {
37 | name: "noOwner",
38 | wantSource: "unknown",
39 | wantOperation: "unknown",
40 | objYaml: deploymentNoOwner,
41 | },
42 | }
43 | for _, tt := range tests {
44 | t.Run(tt.name, func(t *testing.T) {
45 | var obj unstructured.Unstructured
46 | g.Expect(yaml.Unmarshal([]byte(tt.objYaml), &obj)).To(o.Succeed())
47 | gotSource, gotOperation, gotTs := getUpdateSource(&obj, "f:spec")
48 | g.Expect(gotSource).To(o.BeIdenticalTo(tt.wantSource))
49 | g.Expect(gotOperation).To(o.BeIdenticalTo(tt.wantOperation))
50 | g.Expect(gotTs.Equal(tt.wantTs)).To(o.BeTrue())
51 | })
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/controllers/events/recent.go:
--------------------------------------------------------------------------------
1 | package events
2 |
3 | import (
4 | "sync"
5 | "time"
6 |
7 | "go.opentelemetry.io/otel/trace"
8 |
9 | "github.com/weaveworks-experiments/kspan/pkg/mtime"
10 | )
11 |
12 | const (
13 | defaultRecentWindow = time.Second * 5
14 | defaultExpireAfter = time.Minute * 5
15 | )
16 |
17 | type recentInfoStore struct {
18 | sync.Mutex
19 | recentWindow time.Duration // events within this window are considered likely to belong together
20 | expireAfter time.Duration // how long to keep events in the recent cache
21 |
22 | info map[actionReference]recentInfo
23 | }
24 |
25 | // Info about what happened recently with an object
26 | type recentInfo struct {
27 | lastUsed time.Time
28 | spanContext trace.SpanContext
29 | parentContext trace.SpanContext
30 | }
31 |
32 | func newRecentInfoStore() *recentInfoStore {
33 | return &recentInfoStore{
34 | recentWindow: defaultRecentWindow,
35 | expireAfter: defaultExpireAfter,
36 | info: make(map[actionReference]recentInfo),
37 | }
38 | }
39 |
40 | func (r *recentInfoStore) store(key actionReference, parentContext, spanContext trace.SpanContext) {
41 | r.Lock()
42 | defer r.Unlock()
43 | r.info[key] = recentInfo{
44 | lastUsed: mtime.Now(),
45 | spanContext: spanContext,
46 | parentContext: parentContext,
47 | }
48 | }
49 |
50 | func (r *recentInfoStore) lookupSpanContext(key actionReference) (trace.SpanContext, trace.SpanContext, bool) {
51 | now := mtime.Now()
52 | r.Lock()
53 | defer r.Unlock()
54 | value, ok := r.info[key]
55 | if !ok {
56 | return noTrace, noTrace, false
57 | }
58 | //nolint:staticcheck
59 | if value.lastUsed.Before(now.Add(-r.recentWindow)) {
60 | // fmt.Printf("key %v info too old %s, %s\n", key, value.lastUsed, now.Add(-r.recentWindow))
61 | //return noTrace, false
62 | }
63 | value.lastUsed = now
64 | r.info[key] = value
65 | return value.spanContext, value.parentContext, ok
66 | }
67 |
68 | func (r *recentInfoStore) expire() {
69 | now := mtime.Now()
70 | expiry := now.Add(-r.expireAfter)
71 | r.Lock()
72 | defer r.Unlock()
73 | for k, v := range r.info {
74 | if v.lastUsed.Before(expiry) {
75 | delete(r.info, k)
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 |
2 | # Image URL to use all building/pushing image targets
3 | IMG ?= weaveworks/kspan:dev
4 | # Produce CRDs that work back to Kubernetes 1.11 (no version conversion)
5 | CRD_OPTIONS ?= "crd:trivialVersions=true"
6 |
7 | # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
8 | ifeq (,$(shell go env GOBIN))
9 | GOBIN=$(shell go env GOPATH)/bin
10 | else
11 | GOBIN=$(shell go env GOBIN)
12 | endif
13 |
14 | all: manager
15 |
16 | # Run tests
17 | test: generate fmt vet manifests
18 | go test ./... -coverprofile cover.out
19 |
20 | # Build manager binary
21 | manager: generate
22 | go build -o bin/manager main.go
23 |
24 | # Run against the configured Kubernetes cluster in ~/.kube/config
25 | run: generate fmt vet manifests
26 | go run ./main.go
27 |
28 | # Install CRDs into a cluster
29 | install: manifests
30 | kustomize build config/crd | kubectl apply -f -
31 |
32 | # Uninstall CRDs from a cluster
33 | uninstall: manifests
34 | kustomize build config/crd | kubectl delete -f -
35 |
36 | # Deploy controller in the configured Kubernetes cluster in ~/.kube/config
37 | deploy: manifests
38 | cd config/manager && kustomize edit set image controller=${IMG}
39 | kustomize build config/default | kubectl apply -f -
40 |
41 | # Generate manifests e.g. CRD, RBAC etc.
42 | manifests: controller-gen
43 | $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
44 |
45 | # Run go fmt against code
46 | fmt:
47 | go fmt ./...
48 |
49 | # Run go vet against code
50 | vet:
51 | go vet ./...
52 |
53 | # Generate code
54 | generate: controller-gen
55 | $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
56 |
57 | # Build the docker image
58 | docker-build: test
59 | docker build . -t ${IMG}
60 |
61 | # Push the docker image
62 | docker-push: docker-build
63 | docker push ${IMG}
64 |
65 | # find or download controller-gen
66 | # download controller-gen if necessary
67 | controller-gen:
68 | ifeq (, $(shell which controller-gen))
69 | @{ \
70 | set -e ;\
71 | CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\
72 | cd $$CONTROLLER_GEN_TMP_DIR ;\
73 | go mod init tmp ;\
74 | go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.2.5 ;\
75 | rm -rf $$CONTROLLER_GEN_TMP_DIR ;\
76 | }
77 | CONTROLLER_GEN=$(GOBIN)/controller-gen
78 | else
79 | CONTROLLER_GEN=$(shell which controller-gen)
80 | endif
81 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # kspan - Turning Kubernetes Events into spans
2 |
3 | ## This project is a Work In Progress, under active evolution.
4 |
5 | Most Kubernetes components produce Events when something interesting happens.
6 | This program turns those Events into OpenTelemetry Spans, joining them up
7 | by causality and grouping them together into Traces.
8 |
9 | Example: rollout of a Deployment of two Pods:
10 |
11 | 
12 |
13 | The picture was generated by `kspan` and Jaeger; it is a visualisation of the
14 | events generated from `kubectl apply`. `kspan` has identified five different
15 | sources of events, and has joined up the events for starting each Pod underneath
16 | the ReplicaSet scaling operation that created it.
17 |
18 | We start with this concrete information:
19 | * Each Event has an Involved Object, e.g. when Kubelet sends a "Started" event,
20 | the Involved Object is a Pod.
21 | * Every Kubernetes object can have one or more Owner References. So for instance
22 | we can walk from the Pod up to a Deployment that caused it to be created.
23 |
24 | Complications:
25 | * We cannot expect events to arrive in the ideal order; we need to delay handling some until their "parent" arrives to make sense.
26 |
27 | Heuristics:
28 | * If we recently saw an event from an owner, that probably caused this event
29 | in the owned object. We set the child-of relationship on the new span.
30 | * A couple of specific events, from ReplicationSet and StatefulSet, are reported on
31 | the owner but make more sense as events on the sub-object they mention.
32 | * An event can be marked in its annotations as the start of a trace.
33 | * If we have walked the owner chain up to an object with no owner, no recent event,
34 | then start a new trace.
35 | * Trace ID is hashed from UID of this object + its generation
36 |
37 | For future consideration:
38 | * We can match up resourceVersion between event and object.
39 | * do we need to?
40 |
41 | To run Jaeger locally:
42 | ```
43 | docker run -d --name jaeger -p 16686:16686 -p 55680:55680 jaegertracing/opentelemetry-all-in-one
44 | ```
45 |
46 | ## Join in the fun!
47 |
48 | If you have any questions about, or feedback on `kspan`:
49 |
50 | - Invite yourself to the Weave Users Slack.
51 | - Ask a question on the [#general](https://weave-community.slack.com/messages/general/) slack channel.
52 | - [File an issue](https://github.com/weaveworks-experiments/kspan/issues/new).
53 |
54 | Your feedback is always welcome!
55 |
--------------------------------------------------------------------------------
/controllers/events/outgoing.go:
--------------------------------------------------------------------------------
1 | package events
2 |
3 | import (
4 | "context"
5 | "sync"
6 | "time"
7 |
8 | tracesdk "go.opentelemetry.io/otel/sdk/export/trace"
9 | apitrace "go.opentelemetry.io/otel/trace"
10 | )
11 |
12 | type outgoing struct {
13 | sync.Mutex
14 |
15 | byRef map[objectReference]*tracesdk.SpanSnapshot
16 | bySpanID map[apitrace.SpanID]*tracesdk.SpanSnapshot
17 | }
18 |
19 | func newOutgoing() *outgoing {
20 | return &outgoing{
21 | byRef: make(map[objectReference]*tracesdk.SpanSnapshot),
22 | bySpanID: make(map[apitrace.SpanID]*tracesdk.SpanSnapshot),
23 | }
24 | }
25 |
26 | const timeFmt = "15:04:05.000"
27 |
28 | // note we do not return errors, just log them here, because the one place it
29 | // can happen refers to a previous span, so not something the caller can react to.
30 | func (r *EventWatcher) emitSpan(ctx context.Context, ref objectReference, span *tracesdk.SpanSnapshot) {
31 | r.Log.Info("adding span", "ref", ref, "name", span.Name, "start", span.StartTime.Format(timeFmt), "end", span.EndTime.Format(timeFmt))
32 | r.outgoing.Lock()
33 | defer r.outgoing.Unlock()
34 |
35 | if prev, found := r.outgoing.byRef[ref]; found {
36 | if !prev.StartTime.After(span.StartTime) {
37 | prev.EndTime = span.StartTime
38 | } else {
39 | r.Log.Info("New span before old span", "oldSpan", prev.Name, "oldTime", prev.StartTime.Format(timeFmt), "newSpan", span.Name, "newTime", span.StartTime.Format(timeFmt))
40 | }
41 | r.Log.Info("emitting span", "ref", ref, "name", prev.Name)
42 | err := r.Exporter.ExportSpans(ctx, []*tracesdk.SpanSnapshot{prev})
43 | if err != nil {
44 | r.Log.Error(err, "failed to emit span", "ref", ref, "name", prev.Name)
45 | }
46 | // We do not remove from bySpanID at this time, in case it is needed for parent chains
47 | }
48 | r.outgoing.byRef[ref] = span
49 | r.outgoing.bySpanID[span.SpanContext.SpanID()] = span
50 |
51 | for parentID := span.ParentSpanID; parentID.IsValid(); {
52 | if parent, found := r.outgoing.bySpanID[parentID]; found {
53 | if span.EndTime.After(parent.EndTime) {
54 | //r.Log.Info("adjusting endtime", "parent", parent.Name, "from", parent.EndTime.Format(timeFmt), "to", span.EndTime.Format(timeFmt))
55 | parent.EndTime = span.EndTime
56 | }
57 | if parentID == parent.ParentSpanID {
58 | r.Log.Info("infinite loop!", "span", span.Name, "parent", parent.Name, "parentid", parentID)
59 | break
60 | }
61 | parentID = parent.ParentSpanID
62 | } else {
63 | break
64 | }
65 | }
66 | }
67 |
68 | func (r *EventWatcher) flushOutgoing(ctx context.Context, threshold time.Time) {
69 | r.outgoing.Lock()
70 | defer r.outgoing.Unlock()
71 | for k, span := range r.outgoing.byRef {
72 | if !span.EndTime.After(threshold) {
73 | r.Log.Info("deferred emit", "ref", k, "name", span.Name, "endTime", span.EndTime, "threshold", threshold)
74 | err := r.Exporter.ExportSpans(ctx, []*tracesdk.SpanSnapshot{span})
75 | if err != nil {
76 | r.Log.Error(err, "failed to emit span", "ref", k, "name", span.Name)
77 | }
78 | delete(r.outgoing.byRef, k)
79 | delete(r.outgoing.bySpanID, span.SpanContext.SpanID())
80 | }
81 | }
82 | // Now clear out anything old that is still in bySpanID
83 | for k, span := range r.outgoing.bySpanID {
84 | if !span.EndTime.After(threshold) {
85 | delete(r.outgoing.bySpanID, k)
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/controllers/events/pending.go:
--------------------------------------------------------------------------------
1 | package events
2 |
3 | import (
4 | "context"
5 | "time"
6 |
7 | "go.opentelemetry.io/otel/trace"
8 | corev1 "k8s.io/api/core/v1"
9 | "k8s.io/apimachinery/pkg/runtime"
10 | "sigs.k8s.io/controller-runtime/pkg/client"
11 | )
12 |
13 | func (r *EventWatcher) checkPending(ctx context.Context) error {
14 | log := r.Log
15 | // Take everything off the pending queue before walking it, so nobody else messes with those events
16 | r.Lock()
17 | pending := make([]*corev1.Event, len(r.pending))
18 | copy(pending, r.pending)
19 | r.pending = r.pending[:0]
20 | r.Unlock()
21 | for { // repeat if we do generate any new spans
22 | anyEmitted := false
23 | for i := 0; i < len(pending); {
24 | ev := pending[i]
25 | emitted, err := r.emitSpanFromEvent(ctx, log, ev)
26 | if err != nil {
27 | return err
28 | }
29 | if emitted {
30 | // delete entry from pending
31 | pending = append(pending[:i], pending[i+1:]...)
32 | anyEmitted = true
33 | } else {
34 | i++
35 | }
36 | }
37 | if !anyEmitted {
38 | break
39 | }
40 | }
41 | // Now copy back what we didn't process, adding any new items on r.pending after the ones that were there before
42 | r.Lock()
43 | r.pending = append(pending, r.pending...)
44 | r.Unlock()
45 | return nil
46 | }
47 |
48 | // After we've given up waiting, walk further up the owner chain to look for recent activity;
49 | // if necessary create a new span based off the topmost owner.
50 | func (r *EventWatcher) checkOlderPending(ctx context.Context, threshold time.Time) error {
51 | r.Lock()
52 | var olderPending []*corev1.Event
53 | // Collect older events and remove them from pending, which we unlock before calling any other methods
54 | for i := 0; i < len(r.pending); {
55 | event := r.pending[i]
56 | if eventTime(event).Before(threshold) {
57 | olderPending = append(olderPending, event)
58 | r.pending = append(r.pending[:i], r.pending[i+1:]...)
59 | } else {
60 | i++
61 | }
62 | }
63 | r.Unlock()
64 | // Now go through the older events; if we can't map at this point we give up and drop them
65 | for _, event := range olderPending {
66 | success, ref, remoteContext, err := r.makeSpanContextFromEvent(ctx, r.Client, event)
67 | if err != nil {
68 | if !isNotFound(err) {
69 | r.Log.Error(err, "dropping span", "name", event.UID)
70 | }
71 | continue
72 | }
73 | if success {
74 | span := r.eventToSpan(event, remoteContext)
75 | r.emitSpan(ctx, ref.object, span)
76 | if !(ref.IsTopLevel() && remoteContext.HasSpanID()) { // Only store for top-level object if top-level span
77 | r.recent.store(ref, remoteContext, span.SpanContext)
78 | }
79 | }
80 | }
81 | return nil
82 | }
83 |
84 | // Map the topmost owning object to a span, perhaps creating a new trace
85 | func (r *EventWatcher) makeSpanContextFromEvent(ctx context.Context, client client.Client, event *corev1.Event) (success bool, ref actionReference, remoteContext trace.SpanContext, err error) {
86 | var apiVersion string
87 | ref, apiVersion, err = objectFromEvent(ctx, client, event)
88 | if err != nil {
89 | return
90 | }
91 |
92 | if ref.actor.Name != "" {
93 | // See if we have a recent event matching exactly this ref
94 | _, remoteContext, success = r.recent.lookupSpanContext(ref)
95 | if !success {
96 | // Try the owner on its own, and if found use that as the parent
97 | remoteContext, _, success = r.recent.lookupSpanContext(actionReference{object: ref.actor})
98 | }
99 | }
100 | if !success {
101 | var involved runtime.Object
102 | involved, err = getObject(ctx, r.Client, apiVersion, ref.object.Kind, ref.object.Namespace, ref.object.Name)
103 | if err != nil {
104 | if isNotFound(err) { // TODO: could apply naming heuristic to go from a deleted pod to its ReplicaSet
105 | err = nil
106 | }
107 | return
108 | }
109 | r.captureObject(involved, "initial")
110 |
111 | // See if we can map this object to a trace
112 | remoteContext, err = r.makeSpanContextFromObject(ctx, involved, eventTime(event))
113 | if err != nil {
114 | return
115 | }
116 | success = remoteContext.HasTraceID()
117 | }
118 | return
119 | }
120 |
--------------------------------------------------------------------------------
/controllers/events/playback.go:
--------------------------------------------------------------------------------
1 | package events
2 |
3 | import (
4 | "bytes"
5 | "context"
6 | gojson "encoding/json"
7 | "errors"
8 | "fmt"
9 | "io"
10 | "os"
11 | "time"
12 |
13 | v1 "k8s.io/api/core/v1"
14 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
15 | "k8s.io/apimachinery/pkg/runtime"
16 | k8sserializer "k8s.io/apimachinery/pkg/runtime/serializer"
17 | "k8s.io/apimachinery/pkg/runtime/serializer/json"
18 | "k8s.io/apimachinery/pkg/util/yaml"
19 | clientgoscheme "k8s.io/client-go/kubernetes/scheme"
20 |
21 | "github.com/weaveworks-experiments/kspan/pkg/mtime"
22 | )
23 |
24 | type captureDetails struct {
25 | Timestamp time.Time `json:"time"`
26 | Style string `json:"style"`
27 | Kind string `json:"kind"`
28 | }
29 |
30 | // Capture details on an object, so we can play back sequences for testing
31 | func (r *EventWatcher) captureObject(obj runtime.Object, style string) {
32 | if r.Capture == nil {
33 | return
34 | }
35 | fmt.Fprintln(r.Capture, "---")
36 | d := captureDetails{Timestamp: mtime.Now(), Style: style, Kind: obj.GetObjectKind().GroupVersionKind().Kind}
37 | buf, _ := gojson.Marshal(&d)
38 | fmt.Fprintln(r.Capture, "#", string(buf))
39 | cf := k8sserializer.NewCodecFactory(r.scheme)
40 | serializerInfo, _ := runtime.SerializerInfoForMediaType(cf.SupportedMediaTypes(), runtime.ContentTypeYAML)
41 | encoder := serializerInfo.Serializer
42 | err := encoder.Encode(obj, r.Capture)
43 | _ = err // TODO error handling
44 | }
45 |
46 | func walkFile(ctx context.Context, filename string, callback func(captureDetails, []byte) error) error {
47 | file, err := os.Open(filename)
48 | if err != nil {
49 | return fmt.Errorf("error opening %q: %v", filename, err)
50 | }
51 | defer file.Close()
52 | fr := json.YAMLFramer.NewFrameReader(file)
53 | buf := make([]byte, 64*1024) // "should be enough for anyone"
54 | for {
55 | n, err := fr.Read(buf)
56 | if err != nil {
57 | if errors.Is(err, io.EOF) {
58 | break
59 | }
60 | return fmt.Errorf("error: %v", err)
61 | }
62 |
63 | doc := buf[:n]
64 | if n < 10 {
65 | return fmt.Errorf("doc too short: %q", string(doc))
66 | }
67 | if bytes.HasPrefix(doc, []byte("---\n")) {
68 | doc = doc[4:]
69 | }
70 |
71 | // we expect the top line of each doc to have metadata written out by capture
72 | line := firstLine(doc)
73 | var details captureDetails
74 | err = gojson.Unmarshal(line[2:], &details)
75 | if err != nil {
76 | return fmt.Errorf("error parsing first line %q: %v", string(line), err)
77 | }
78 |
79 | err = callback(details, doc)
80 | if err != nil {
81 | return err
82 | }
83 | }
84 | return nil
85 | }
86 |
87 | func firstLine(buf []byte) []byte {
88 | p := bytes.IndexByte(buf, '\n')
89 | if p == -1 {
90 | return nil
91 | }
92 | return buf[:p]
93 | }
94 |
95 | func getInitialObjects(filename string) ([]runtime.Object, time.Time, error) {
96 | scheme := runtime.NewScheme()
97 | _ = clientgoscheme.AddToScheme(scheme)
98 | //s := serializer.NewSerializer(scheme, nil)
99 | var maxTimestamp time.Time
100 | var objects []runtime.Object
101 |
102 | err := walkFile(context.Background(), filename, func(details captureDetails, doc []byte) error {
103 | if details.Timestamp.After(maxTimestamp) {
104 | maxTimestamp = details.Timestamp
105 | }
106 | switch details.Style {
107 | case "initial":
108 | dec := yaml.NewYAMLToJSONDecoder(bytes.NewBuffer(doc))
109 | var u unstructured.Unstructured
110 | err := dec.Decode(&u)
111 | if err != nil {
112 | return err
113 | }
114 | objects = append(objects, &u)
115 | }
116 | return nil
117 | })
118 | return objects, maxTimestamp, err
119 | }
120 |
121 | func playback(ctx context.Context, r *EventWatcher, filename string) error {
122 | return walkFile(ctx, filename, func(details captureDetails, doc []byte) error {
123 | switch details.Style {
124 | case "initial":
125 | // no-op on playback
126 | case "event":
127 | dec := yaml.NewYAMLToJSONDecoder(bytes.NewBuffer(doc))
128 | var ev v1.Event
129 | err := dec.Decode(&ev)
130 | if err != nil {
131 | return fmt.Errorf("error parsing: %v", err)
132 | }
133 | mtime.NowForce(details.Timestamp)
134 | err = r.handleEvent(ctx, &ev)
135 | mtime.NowReset()
136 | return err
137 | default:
138 | return fmt.Errorf("style not recognized: %q", string(details.Style))
139 | }
140 | return nil
141 | })
142 | }
143 |
--------------------------------------------------------------------------------
/controllers/events/mocks_for_test.go:
--------------------------------------------------------------------------------
1 | package events
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "sort"
7 |
8 | "github.com/go-logr/logr"
9 | "go.opentelemetry.io/otel/attribute"
10 | tracesdk "go.opentelemetry.io/otel/sdk/export/trace"
11 | "go.opentelemetry.io/otel/semconv"
12 | "go.opentelemetry.io/otel/trace"
13 | "k8s.io/apimachinery/pkg/runtime"
14 | clientgoscheme "k8s.io/client-go/kubernetes/scheme"
15 | "sigs.k8s.io/controller-runtime/pkg/client/fake" //nolint:staticcheck
16 | "sigs.k8s.io/controller-runtime/pkg/log/zap"
17 | )
18 |
19 | // Initialize an EventWatcher, context and logger ready for testing
20 | func newTestEventWatcher(initObjs ...runtime.Object) (context.Context, *EventWatcher, *fakeExporter, logr.Logger) {
21 | ctx := context.Background()
22 | scheme := runtime.NewScheme()
23 | _ = clientgoscheme.AddToScheme(scheme)
24 | log := zap.New(zap.UseDevMode(true))
25 |
26 | fakeClient := fake.NewFakeClientWithScheme(scheme, initObjs...)
27 | exporter := newFakeExporter()
28 |
29 | r := &EventWatcher{
30 | Client: fakeClient,
31 | Log: log,
32 | Exporter: exporter,
33 | }
34 |
35 | r.initialize(scheme)
36 |
37 | return ctx, r, exporter, log
38 | }
39 |
40 | func newFakeExporter() *fakeExporter {
41 | return &fakeExporter{}
42 | }
43 |
44 | // records spans sent to it, for testing purposes
45 | type fakeExporter struct {
46 | SpanSnapshot []*tracesdk.SpanSnapshot
47 | }
48 |
49 | func (f *fakeExporter) dump() []string {
50 | f.sort()
51 | spanMap := make(map[trace.SpanID]int)
52 | for i, d := range f.SpanSnapshot {
53 | spanMap[d.SpanContext.SpanID()] = i
54 | }
55 | var ret []string
56 | for i, d := range f.SpanSnapshot {
57 | parent, found := spanMap[d.ParentSpanID]
58 | var parentStr string
59 | if found {
60 | parentStr = fmt.Sprintf(" (%d)", parent)
61 | }
62 | message := attributeValue(d.Attributes, attribute.Key("message"))
63 | resourceName := attributeValue(d.Resource.Attributes(), semconv.ServiceNameKey)
64 | ret = append(ret, fmt.Sprintf("%d: %s %s%s %s", i, resourceName, d.Name, parentStr, message))
65 | }
66 | return ret
67 | }
68 |
69 | // ExportSpans implements trace.SpanExporter
70 | func (f *fakeExporter) ExportSpans(ctx context.Context, SpanSnapshot []*tracesdk.SpanSnapshot) error {
71 | f.SpanSnapshot = append(f.SpanSnapshot, SpanSnapshot...)
72 | return nil
73 | }
74 |
75 | // Shutdown implements trace.SpanExporter
76 | func (f *fakeExporter) Shutdown(ctx context.Context) error {
77 | return nil
78 | }
79 |
80 | func attributeValue(attributes []attribute.KeyValue, key attribute.Key) string {
81 | for _, lbl := range attributes {
82 | if lbl.Key == key {
83 | return lbl.Value.AsString()
84 | }
85 | }
86 | return ""
87 | }
88 |
89 | // Sort the captured spans so they are in a predictable order to check expected output.
90 | // Use depth-first search, with edges ordered by start-time where those differ.
91 | func (f *fakeExporter) sort() {
92 | sort.Stable(SortableSpans(f.SpanSnapshot))
93 |
94 | // Make a map from span-id to index in the set
95 | spanMap := make(map[trace.SpanID]int)
96 | for i, d := range f.SpanSnapshot {
97 | spanMap[d.SpanContext.SpanID()] = i
98 | }
99 |
100 | // Prepare vertexes for depth-first sort
101 | v := make([]*vertex, len(f.SpanSnapshot))
102 | for i := range f.SpanSnapshot {
103 | v[i] = &vertex{value: i}
104 | }
105 | topSpan := -1
106 | for i, s := range f.SpanSnapshot {
107 | if s.ParentSpanID.IsValid() {
108 | p := spanMap[s.ParentSpanID]
109 | v[p].connect(v[i])
110 | } else {
111 | if topSpan != -1 {
112 | panic("More than one top span")
113 | }
114 | topSpan = i
115 | }
116 | }
117 | if topSpan == -1 { // no top span found; can't do DFS
118 | return
119 | }
120 |
121 | sortedSpans := make([]*tracesdk.SpanSnapshot, 0, len(f.SpanSnapshot))
122 | t := dfs{
123 | visit: func(v *vertex) {
124 | sortedSpans = append(sortedSpans, f.SpanSnapshot[v.value])
125 | },
126 | }
127 | t.walk(v[topSpan])
128 |
129 | f.SpanSnapshot = sortedSpans
130 | }
131 |
132 | // SortableSpans attaches the methods of sort.Interface to []*tracesdk.SpanSnapshot, sorting by start time.
133 | type SortableSpans []*tracesdk.SpanSnapshot
134 |
135 | func (x SortableSpans) Len() int { return len(x) }
136 | func (x SortableSpans) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
137 | func (x SortableSpans) Less(i, j int) bool { return x[i].StartTime.Before(x[j].StartTime) }
138 |
139 | // Single-use DFS implementation.
140 | // Not using one from a library; see https://github.com/gonum/gonum/issues/1595
141 | type vertex struct {
142 | visited bool
143 | value int
144 | neighbours []*vertex
145 | }
146 |
147 | func (v *vertex) connect(vertex *vertex) {
148 | v.neighbours = append(v.neighbours, vertex)
149 | }
150 |
151 | type dfs struct {
152 | visit func(*vertex)
153 | }
154 |
155 | func (d *dfs) walk(vertex *vertex) {
156 | if vertex.visited {
157 | return
158 | }
159 | vertex.visited = true
160 | d.visit(vertex)
161 | for _, v := range vertex.neighbours {
162 | d.walk(v)
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/controllers/events/object.go:
--------------------------------------------------------------------------------
1 | package events
2 |
3 | import (
4 | "context"
5 | "encoding/binary"
6 | "encoding/json"
7 | "fmt"
8 | "hash/fnv"
9 | "strings"
10 | "time"
11 |
12 | "github.com/pkg/errors"
13 | "go.opentelemetry.io/otel/attribute"
14 | tracesdk "go.opentelemetry.io/otel/sdk/export/trace"
15 | "go.opentelemetry.io/otel/trace"
16 | corev1 "k8s.io/api/core/v1"
17 | "k8s.io/apimachinery/pkg/api/meta"
18 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
19 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
20 | "k8s.io/apimachinery/pkg/runtime"
21 | "sigs.k8s.io/controller-runtime/pkg/client"
22 | )
23 |
24 | // This is how we refer to objects - it's a subset of corev1.ObjectReference
25 | type objectReference struct {
26 | Kind string
27 | Namespace string
28 | Name string
29 | }
30 |
31 | func (r objectReference) String() string {
32 | return fmt.Sprintf("%s:%s/%s", r.Kind, r.Namespace, r.Name)
33 | }
34 |
35 | func (r objectReference) Blank() bool {
36 | return r == objectReference{}
37 | }
38 |
39 | // Given an object, come up with some source for the change, and the time it happened
40 | func getUpdateSource(obj v1.Object, subFields ...string) (source string, operation string, ts time.Time) {
41 | // If it has managed fields, return the newest change that updated the spec
42 | for _, mf := range obj.GetManagedFields() {
43 | var fields map[string]interface{}
44 | err := json.Unmarshal(mf.FieldsV1.Raw, &fields)
45 | if err != nil {
46 | continue
47 | }
48 | if _, found, _ := unstructured.NestedFieldNoCopy(fields, subFields...); found && mf.Time.Time.After(ts) {
49 | ts = mf.Time.Time
50 | source = mf.Manager
51 | operation = string(mf.Operation)
52 | }
53 | }
54 | if !ts.IsZero() {
55 | return source, operation, ts
56 | }
57 | // TODO: try some other ways
58 | return "unknown", "unknown", ts
59 | }
60 |
61 | // If we reach an object with no owner and no recent events, start a new trace.
62 | // Trace ID is a hash of object UID + generation.
63 | func (r *EventWatcher) createTraceFromTopLevelObject(ctx context.Context, obj runtime.Object, eventTime time.Time) (*tracesdk.SpanSnapshot, error) {
64 | m, err := meta.Accessor(obj)
65 | if err != nil {
66 | return nil, err
67 | }
68 |
69 | updateSource, operation, updateTime := getUpdateSource(m, "f:spec")
70 | res := r.getResource(source{name: updateSource})
71 |
72 | if updateTime.IsZero() { // We didn't find a time in the object
73 | updateTime = eventTime
74 | }
75 |
76 | kind := obj.GetObjectKind().GroupVersionKind().Kind
77 | attrs := []attribute.KeyValue{
78 | attribute.String("k8s.namespace.name", m.GetNamespace()),
79 | attribute.String("k8s."+strings.ToLower(kind)+".name", m.GetName()),
80 | attribute.String("kind", kind),
81 | attribute.Int64("generation", m.GetGeneration()),
82 | }
83 |
84 | spanData := &tracesdk.SpanSnapshot{
85 | SpanContext: trace.NewSpanContext(trace.SpanContextConfig{
86 | TraceID: objectToTraceID(m),
87 | SpanID: objectToSpanID(m),
88 | }),
89 | SpanKind: trace.SpanKindInternal,
90 | Name: fmt.Sprintf("%s.%s", obj.GetObjectKind().GroupVersionKind().Kind, operation),
91 | StartTime: updateTime,
92 | EndTime: updateTime,
93 | Attributes: attrs,
94 | Resource: res,
95 | }
96 |
97 | return spanData, nil
98 | }
99 |
100 | // create a spanID that will be consistent for a particular object
101 | func objectToSpanID(m v1.Object) trace.SpanID {
102 | f := fnv.New64a()
103 | _, _ = f.Write([]byte(m.GetUID()))
104 | _ = binary.Write(f, binary.LittleEndian, m.GetGeneration())
105 | var h trace.SpanID
106 | _ = f.Sum(h[:0])
107 | return h
108 | }
109 |
110 | // we include the generation, so changes to the spec will start a new trace.
111 | func objectToTraceID(m v1.Object) trace.TraceID {
112 | f := fnv.New128a()
113 | _, _ = f.Write([]byte(m.GetUID()))
114 | _ = binary.Write(f, binary.LittleEndian, m.GetGeneration())
115 | var h trace.TraceID
116 | _ = f.Sum(h[:0])
117 | return h
118 | }
119 |
120 | func getObject(ctx context.Context, c client.Client, apiVersion, kind, namespace, name string) (runtime.Object, error) {
121 | obj := &unstructured.Unstructured{}
122 | if apiVersion == "" { // this happens with Node references
123 | apiVersion = "v1" // TODO: find a more general solution
124 | }
125 | obj.SetAPIVersion(apiVersion)
126 | obj.SetKind(kind)
127 | key := client.ObjectKey{Namespace: namespace, Name: name}
128 | err := c.Get(ctx, key, obj)
129 | return obj, errors.Wrap(err, "unable to get object")
130 | }
131 |
132 | // Canonicalise strings via lowercase - Kubernetes is case-independent.
133 | // (we don't canonicalise Kind)
134 | func lc(s string) string {
135 | return strings.ToLower(s)
136 | }
137 |
138 | func refFromObjRef(oRef corev1.ObjectReference) objectReference {
139 | return objectReference{
140 | Kind: oRef.Kind,
141 | Namespace: lc(oRef.Namespace),
142 | Name: lc(oRef.Name),
143 | }
144 | }
145 |
146 | func refFromOwner(oRef v1.OwnerReference, namespace string) objectReference {
147 | return objectReference{
148 | Kind: oRef.Kind,
149 | Namespace: lc(namespace),
150 | Name: lc(oRef.Name),
151 | }
152 | }
153 |
154 | func refFromObject(obj v1.Object) objectReference {
155 | ty := obj.(v1.Type)
156 | return objectReference{
157 | Kind: ty.GetKind(),
158 | Namespace: lc(obj.GetNamespace()),
159 | Name: lc(obj.GetName()),
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | /*
2 |
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 main
18 |
19 | import (
20 | "context"
21 | "flag"
22 | "io"
23 | "os"
24 | "strings"
25 |
26 | "github.com/pkg/errors"
27 | "go.opentelemetry.io/otel"
28 | "go.opentelemetry.io/otel/exporters/otlp"
29 | "go.opentelemetry.io/otel/exporters/otlp/otlpgrpc"
30 | "go.opentelemetry.io/otel/propagation"
31 | tracesdk "go.opentelemetry.io/otel/sdk/export/trace"
32 | "google.golang.org/grpc/credentials"
33 | "k8s.io/apimachinery/pkg/runtime"
34 | clientgoscheme "k8s.io/client-go/kubernetes/scheme"
35 | _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
36 | ctrl "sigs.k8s.io/controller-runtime"
37 | "sigs.k8s.io/controller-runtime/pkg/log/zap"
38 | "sigs.k8s.io/controller-runtime/pkg/manager"
39 |
40 | "github.com/weaveworks-experiments/kspan/controllers/events"
41 | // +kubebuilder:scaffold:imports
42 | )
43 |
44 | var (
45 | scheme = runtime.NewScheme()
46 | setupLog = ctrl.Log.WithName("setup")
47 | )
48 |
49 | func init() {
50 | _ = clientgoscheme.AddToScheme(scheme)
51 |
52 | // +kubebuilder:scaffold:scheme
53 | }
54 |
55 | func setupOTLP(ctx context.Context, addr string, headers string, secured bool) (tracesdk.SpanExporter, error) {
56 | setupLog.Info("Setting up OTLP Exporter", "addr", addr)
57 |
58 | var exp *otlp.Exporter
59 | var err error
60 |
61 | headersMap := make(map[string]string)
62 | if headers != "" {
63 | ha := strings.Split(headers, ",")
64 | for _, h := range ha {
65 | parts := strings.Split(h, "=")
66 | if len(parts) != 2 {
67 | setupLog.Error(errors.New("Error parsing OTLP header"), "header parts length is not 2", "header", h)
68 | continue
69 | }
70 | headersMap[parts[0]] = parts[1]
71 | }
72 | }
73 |
74 | if secured {
75 | exp, err = otlp.NewExporter(
76 | ctx,
77 | otlpgrpc.NewDriver(
78 | otlpgrpc.WithEndpoint(addr),
79 | otlpgrpc.WithHeaders(headersMap),
80 | otlpgrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, "")),
81 | ),
82 | )
83 | } else {
84 | exp, err = otlp.NewExporter(
85 | ctx,
86 | otlpgrpc.NewDriver(
87 | otlpgrpc.WithEndpoint(addr),
88 | otlpgrpc.WithHeaders(headersMap),
89 | otlpgrpc.WithInsecure(),
90 | ),
91 | )
92 | }
93 | if err != nil {
94 | return nil, err
95 | }
96 |
97 | otel.SetTextMapPropagator(propagation.TraceContext{})
98 | return exp, err
99 | }
100 |
101 | func main() {
102 | var metricsAddr string
103 | var otlpAddr string
104 | var otlpHeaders string
105 | var otlpSecured bool
106 | var captureFile string
107 | flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
108 | flag.StringVar(&otlpAddr, "otlp-addr", "otlp-collector.default:55680", "Address to send traces to")
109 | flag.StringVar(&otlpHeaders, "otlp-headers", "", "Add headers key/values pairs to OTLP communication")
110 | flag.BoolVar(&otlpSecured, "otlp-secured", false, "Use TLS for OTLP export")
111 | flag.StringVar(&captureFile, "capture-to", "", "Write out all updates received to this file")
112 | flag.Parse()
113 |
114 | ctrl.SetLogger(zap.New(zap.UseDevMode(true)))
115 |
116 | ctx := context.Background()
117 | spanExporter, err := setupOTLP(ctx, otlpAddr, otlpHeaders, otlpSecured)
118 | if err != nil {
119 | setupLog.Error(err, "unable to set up tracing")
120 | os.Exit(1)
121 | }
122 | defer func() {
123 | err := spanExporter.Shutdown(ctx)
124 | if err != nil {
125 | setupLog.Error(err, "unable to gracefully shutdown exporter")
126 | }
127 | }()
128 |
129 | mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
130 | Scheme: scheme,
131 | MetricsBindAddress: metricsAddr,
132 | Port: 9443,
133 | })
134 | if err != nil {
135 | setupLog.Error(err, "unable to start manager")
136 | os.Exit(1)
137 | }
138 |
139 | var capture io.WriteCloser
140 | if captureFile != "" {
141 | capture, err = os.Create(captureFile)
142 | if err != nil {
143 | setupLog.Error(err, "unable to open capture file")
144 | os.Exit(1)
145 | }
146 | }
147 | if err = (&events.EventWatcher{
148 | Client: mgr.GetClient(),
149 | Log: ctrl.Log,
150 | Exporter: spanExporter,
151 | Capture: capture,
152 | }).SetupWithManager(mgr); err != nil {
153 | setupLog.Error(err, "unable to create controller", "controller", "Events")
154 | os.Exit(1)
155 | }
156 | // +kubebuilder:scaffold:builder
157 |
158 | // Close capture file when program shuts down
159 | if err := mgr.Add(manager.RunnableFunc(func(stop <-chan struct{}) error {
160 | <-stop
161 | return capture.Close()
162 | })); err != nil {
163 | setupLog.Error(err, "unable to add close function")
164 | os.Exit(1)
165 | }
166 |
167 | setupLog.Info("starting manager")
168 | if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
169 | setupLog.Error(err, "problem running manager")
170 | os.Exit(1)
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/controllers/events/event.go:
--------------------------------------------------------------------------------
1 | package events
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "hash/fnv"
7 | "strings"
8 | "time"
9 |
10 | "go.opentelemetry.io/otel/attribute"
11 | "go.opentelemetry.io/otel/codes"
12 | tracesdk "go.opentelemetry.io/otel/sdk/export/trace"
13 | "go.opentelemetry.io/otel/trace"
14 | corev1 "k8s.io/api/core/v1"
15 | "sigs.k8s.io/controller-runtime/pkg/client"
16 | )
17 |
18 | // bundle up two object references to track who did what to whom
19 | type actionReference struct {
20 | actor objectReference // this is what did something (may be blank if not known)
21 | object objectReference // this is what it was done to
22 | }
23 |
24 | func (p actionReference) String() string {
25 | return fmt.Sprintf("actor: %s, object: %s", p.actor, p.object) // TODO improve this
26 | }
27 |
28 | func (p actionReference) IsTopLevel() bool {
29 | return p.actor.Blank()
30 | }
31 |
32 | // look for the string 'marker' in 'message' and return the following space-separated word.
33 | // if anything goes wrong, return an empty string.
34 | func extractWordAfter(message, marker string) string {
35 | pos := strings.Index(message, marker)
36 | if pos == -1 {
37 | return ""
38 | }
39 | pos += len(marker)
40 | end := strings.IndexByte(message[pos:], ' ')
41 | if end == -1 {
42 | return message[pos:]
43 | }
44 | return message[pos : pos+end]
45 | }
46 |
47 | // Get the object relating to an event, after applying some heuristics
48 | // or a blank struct if this can't be done
49 | func objectFromEvent(ctx context.Context, client client.Client, event *corev1.Event) (actionReference, string, error) {
50 | if event.InvolvedObject.Name == "" {
51 | return actionReference{}, "", fmt.Errorf("no involved object")
52 | }
53 |
54 | objRef := refFromObjRef(event.InvolvedObject)
55 | ret := actionReference{
56 | object: objRef,
57 | }
58 | apiVersion := event.InvolvedObject.APIVersion
59 |
60 | // TODO: generalise these, take away source- and kind-specific checks
61 | switch {
62 | case event.Source.Component == "deployment-controller" && event.InvolvedObject.Kind == "Deployment":
63 | // if we have a message like "Scaled down replica set foobar-7ff854f459 to 0"; extract the ReplicaSet name
64 | name := extractWordAfter(event.Message, "replica set ")
65 | if name == "" {
66 | break
67 | }
68 | ret.actor = ret.object
69 | ret.object = objectReference{Kind: "ReplicaSet", Namespace: lc(ret.object.Namespace), Name: lc(name)}
70 | case event.Source.Component == "replicaset-controller" && event.InvolvedObject.Kind == "ReplicaSet":
71 | // if we have a message like "Created pod: foo-5c5df9754b-4w2hj"; extract the Pod name
72 | name := extractWordAfter(event.Message, "pod: ")
73 | if name == "" {
74 | break
75 | }
76 | ret.actor = ret.object
77 | ret.object = objectReference{Kind: "Pod", Namespace: lc(ret.object.Namespace), Name: lc(name)}
78 | apiVersion = "v1"
79 | case event.Source.Component == "statefulset-controller" && event.InvolvedObject.Kind == "StatefulSet":
80 | // if we have a message like "create Pod ingester-3 in StatefulSet ingester successful"; extract the Pod name
81 | name := extractWordAfter(event.Message, "Pod ")
82 | if name == "" {
83 | break
84 | }
85 | ret.actor = ret.object
86 | ret.object = objectReference{Kind: "Pod", Namespace: lc(ret.object.Namespace), Name: lc(name)}
87 | apiVersion = "v1"
88 | }
89 |
90 | return ret, apiVersion, nil
91 | }
92 |
93 | func (r *EventWatcher) eventToSpan(event *corev1.Event, remoteContext trace.SpanContext) *tracesdk.SpanSnapshot {
94 | // resource says which component the span is seen as coming from
95 | res := r.getResource(eventSource(event))
96 |
97 | attrs := []attribute.KeyValue{
98 | attribute.String("kind", event.InvolvedObject.Kind),
99 | attribute.String("k8s."+strings.ToLower(event.InvolvedObject.Kind)+".name", event.InvolvedObject.Name),
100 | }
101 |
102 | if event.Reason != "" {
103 | attrs = append(attrs, attribute.String("reason", event.Reason))
104 | }
105 | if event.Message != "" {
106 | attrs = append(attrs, attribute.String("message", event.Message)) // maybe this should be a log?
107 | }
108 | if event.Name != "" {
109 | attrs = append(attrs, attribute.String("eventID", event.Namespace+"/"+event.Name))
110 | }
111 | if event.InvolvedObject.Namespace != "" {
112 | attrs = append(attrs, attribute.String("k8s.namespace.name", event.InvolvedObject.Namespace))
113 | }
114 |
115 | statusCode := codes.Ok
116 | if event.Type != corev1.EventTypeNormal {
117 | statusCode = codes.Error
118 | }
119 |
120 | return &tracesdk.SpanSnapshot{
121 | SpanContext: trace.NewSpanContext(trace.SpanContextConfig{
122 | TraceID: remoteContext.TraceID(),
123 | SpanID: eventToSpanID(event),
124 | }),
125 | ParentSpanID: remoteContext.SpanID(),
126 | SpanKind: trace.SpanKindInternal,
127 | Name: fmt.Sprintf("%s.%s", event.InvolvedObject.Kind, event.Reason),
128 | StartTime: eventTime(event),
129 | EndTime: eventTime(event),
130 | Attributes: attrs,
131 | StatusCode: statusCode,
132 | HasRemoteParent: true,
133 | Resource: res,
134 | //InstrumentationLibrary instrumentation.Library
135 | }
136 | }
137 |
138 | // generate a spanID from an event. The first time this event is issued has a span ID that can be derived from the event UID
139 | func eventToSpanID(event *corev1.Event) trace.SpanID {
140 | f := fnv.New64a()
141 | _, _ = f.Write([]byte(event.UID))
142 | if event.Count > 0 {
143 | fmt.Fprint(f, event.Count)
144 | }
145 | var h trace.SpanID
146 | _ = f.Sum(h[:0])
147 | return h
148 | }
149 |
150 | // If time has zero ms, and is close to wall-clock time, use wall-clock time
151 | func adjustEventTime(event *corev1.Event, now time.Time) {
152 | if event.LastTimestamp.Time.IsZero() {
153 | return
154 | }
155 | if event.LastTimestamp.Time.Nanosecond() == 0 && now.Sub(event.LastTimestamp.Time) < time.Second {
156 | event.LastTimestamp.Time = now
157 | }
158 | }
159 |
160 | // Some events have just an EventTime; if LastTimestamp is present we prefer that.
161 | func eventTime(event *corev1.Event) time.Time {
162 | if !event.LastTimestamp.Time.IsZero() {
163 | return event.LastTimestamp.Time
164 | }
165 | return event.EventTime.Time
166 | }
167 |
168 | func eventSource(event *corev1.Event) source {
169 | if event.Source.Component != "" {
170 | return source{
171 | name: event.Source.Component,
172 | instance: event.Source.Host,
173 | }
174 | }
175 | return source{
176 | name: event.ReportingController,
177 | instance: event.ReportingInstance,
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/controllers/events/event_controller_test.go:
--------------------------------------------------------------------------------
1 | package events
2 |
3 | import (
4 | "testing"
5 | "time"
6 |
7 | o "github.com/onsi/gomega"
8 | corev1 "k8s.io/api/core/v1"
9 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
10 | )
11 |
12 | func TestDeploymentRolloutWithManagedFields(t *testing.T) {
13 | g := o.NewWithT(t)
14 |
15 | var (
16 | deploy1 unstructured.Unstructured
17 | rs1, rs2 unstructured.Unstructured
18 | pod0, pod1 unstructured.Unstructured
19 | )
20 | mustParse(t, deploy1str, &deploy1)
21 | mustParse(t, replicaSet1str, &rs1)
22 | mustParse(t, replicaSet2str, &rs2)
23 | mustParse(t, pod0str, &pod0)
24 | mustParse(t, pod1str, &pod1)
25 |
26 | // There is no 'top-level' event here; the controller must synthesise one from the managed fields of the Deployment.
27 |
28 | tests := []struct {
29 | name string
30 | perm []int
31 | wantTraces []string
32 | }{
33 | {
34 | name: "scaledown-later",
35 | perm: []int{0, 1, 2, 3, 4, 5, 6, 7, 8},
36 | wantTraces: []string{
37 | "0: kubectl Deployment.Update ",
38 | "1: deployment-controller Deployment.ScalingReplicaSet (0) Scaled up replica set hello-world-6b9d85fbd6 to 1",
39 | "2: replicaset-controller ReplicaSet.SuccessfulCreate (1) Created pod: hello-world-6b9d85fbd6-klpv2",
40 | "3: default-scheduler Pod.Scheduled (2) Successfully assigned default/hello-world-6b9d85fbd6-klpv2 to kind-control-plane",
41 | "4: kubelet Pod.Pulled (2) Container image \"nginx:1.19.2-alpine\" already present on machine",
42 | "5: kubelet Pod.Created (2) Created container hello-world",
43 | "6: kubelet Pod.Started (2) Started container hello-world",
44 | "7: deployment-controller Deployment.ScalingReplicaSet (0) Scaled down replica set hello-world-7ff854f459 to 0",
45 | "8: kubelet Pod.Killing (7) Stopping container hello-world",
46 | "9: replicaset-controller ReplicaSet.SuccessfulDelete (7) Deleted pod: hello-world-7ff854f459-kl4hq",
47 | },
48 | },
49 | {
50 | name: "scaledown-earlier",
51 | perm: []int{0, 6, 1, 2, 3, 4, 5, 7, 8},
52 | wantTraces: []string{
53 | "0: kubectl Deployment.Update ",
54 | "1: deployment-controller Deployment.ScalingReplicaSet (0) Scaled up replica set hello-world-6b9d85fbd6 to 1",
55 | "2: replicaset-controller ReplicaSet.SuccessfulCreate (1) Created pod: hello-world-6b9d85fbd6-klpv2",
56 | "3: default-scheduler Pod.Scheduled (2) Successfully assigned default/hello-world-6b9d85fbd6-klpv2 to kind-control-plane",
57 | "4: kubelet Pod.Pulled (2) Container image \"nginx:1.19.2-alpine\" already present on machine",
58 | "5: kubelet Pod.Created (2) Created container hello-world",
59 | "6: kubelet Pod.Started (2) Started container hello-world",
60 | "7: deployment-controller Deployment.ScalingReplicaSet (0) Scaled down replica set hello-world-7ff854f459 to 0",
61 | "8: kubelet Pod.Killing (7) Stopping container hello-world",
62 | "9: replicaset-controller ReplicaSet.SuccessfulDelete (7) Deleted pod: hello-world-7ff854f459-kl4hq",
63 | },
64 | },
65 | }
66 |
67 | threshold, err := time.Parse(time.RFC3339, deploymentUpdateEventsThresholdStr)
68 | g.Expect(err).NotTo(o.HaveOccurred())
69 |
70 | for _, tt := range tests {
71 | t.Run(tt.name, func(t *testing.T) {
72 | ctx, r, exporter, _ := newTestEventWatcher(&deploy1, &rs1, &rs2, &pod0, &pod1)
73 | defer r.stop()
74 | for _, index := range tt.perm {
75 | var event corev1.Event
76 | mustParse(t, deploymentUpdateEvents[index], &event)
77 | g.Expect(r.handleEvent(ctx, &event)).To(o.Succeed())
78 | }
79 | g.Expect(r.checkOlderPending(ctx, threshold)).To(o.Succeed())
80 | r.flushOutgoing(ctx, threshold)
81 | g.Expect(exporter.dump()).To(o.Equal(tt.wantTraces))
82 | })
83 | }
84 | }
85 |
86 | func Test2PodDeploymentRollout(t *testing.T) {
87 | g := o.NewWithT(t)
88 |
89 | tests := []struct {
90 | filename string
91 | wantTraces []string
92 | }{
93 | {
94 | filename: "testdata/deployment-2-pods.yaml",
95 | wantTraces: []string{
96 | "0: kubectl-client-side-apply Deployment.Update ",
97 | "1: deployment-controller Deployment.ScalingReplicaSet (0) Scaled up replica set px-5d567cc74c to 1",
98 | "2: replicaset-controller ReplicaSet.SuccessfulCreate (1) Created pod: px-5d567cc74c-ss4lb",
99 | "3: default-scheduler Pod.Scheduled (2) Successfully assigned default/px-5d567cc74c-ss4lb to kind-control-plane",
100 | "4: kubelet Pod.Pulling (2) Pulling image \"ghcr.io/stefanprodan/podinfo:5.0.0\"",
101 | "5: kubelet Pod.Pulled (2) Successfully pulled image \"ghcr.io/stefanprodan/podinfo:5.0.0\" in 5.870196872s",
102 | "6: kubelet Pod.Created (2) Created container podinfo",
103 | "7: kubelet Pod.Started (2) Started container podinfo",
104 | "8: replicaset-controller ReplicaSet.SuccessfulDelete (0) Deleted pod: px-7df978b9bf-jm22q",
105 | "9: kubelet Pod.Killing (8) Stopping container podinfo",
106 | "10: deployment-controller Deployment.ScalingReplicaSet (0) Scaled down replica set px-7df978b9bf to 1",
107 | "11: deployment-controller Deployment.ScalingReplicaSet (0) Scaled up replica set px-5d567cc74c to 2",
108 | "12: replicaset-controller ReplicaSet.SuccessfulCreate (11) Created pod: px-5d567cc74c-pmvzr",
109 | "13: kubelet Pod.Pulled (12) Container image \"ghcr.io/stefanprodan/podinfo:5.0.0\" already present on machine",
110 | "14: kubelet Pod.Created (12) Created container podinfo",
111 | "15: kubelet Pod.Started (12) Started container podinfo",
112 | "16: default-scheduler Pod.Scheduled (12) Successfully assigned default/px-5d567cc74c-pmvzr to kind-control-plane",
113 | "17: deployment-controller Deployment.ScalingReplicaSet (0) Scaled down replica set px-7df978b9bf to 0",
114 | "18: replicaset-controller ReplicaSet.SuccessfulDelete (17) Deleted pod: px-7df978b9bf-bfdrj",
115 | "19: kubelet Pod.Killing (18) Stopping container podinfo",
116 | },
117 | },
118 | }
119 |
120 | for _, tt := range tests {
121 | t.Run(tt.filename, func(t *testing.T) {
122 | objs, maxTimestamp, err := getInitialObjects(tt.filename)
123 | g.Expect(err).NotTo(o.HaveOccurred())
124 | ctx, r, exporter, _ := newTestEventWatcher(objs...)
125 | defer r.stop()
126 | g.Expect(playback(ctx, r, tt.filename)).To(o.Succeed())
127 | threshold := maxTimestamp.Add(time.Second * 10)
128 | g.Expect(r.checkOlderPending(ctx, threshold)).To(o.Succeed())
129 | r.flushOutgoing(ctx, threshold)
130 | g.Expect(exporter.dump()).To(o.Equal(tt.wantTraces))
131 | })
132 | }
133 | }
134 |
135 | func TestDeploymentRolloutFromFlux(t *testing.T) {
136 | g := o.NewWithT(t)
137 |
138 | var (
139 | deploy1 unstructured.Unstructured
140 | rs1, rs2 unstructured.Unstructured
141 | pod1 unstructured.Unstructured
142 | )
143 | mustParse(t, fluxDeploymentStr, &deploy1)
144 | mustParse(t, fluxReplicaSet1astr, &rs1)
145 | mustParse(t, fluxReplicaSet1bstr, &rs2)
146 | mustParse(t, fluxPod1astr, &pod1)
147 |
148 | tests := []struct {
149 | name string
150 | perm []int
151 | wantTraces []string
152 | }{
153 | {
154 | name: "flux-event-later",
155 | perm: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
156 | wantTraces: []string{
157 | "0: flux Deployment.Sync Commit e332e7bac962: Update nginx",
158 | "1: deployment-controller Deployment.ScalingReplicaSet (0) Scaled up replica set hello-world-f77b4f6c8 to 1",
159 | "2: replicaset-controller ReplicaSet.SuccessfulCreate (1) Created pod: hello-world-f77b4f6c8-6tcj2",
160 | "3: default-scheduler Pod.Scheduled (2) Successfully assigned default/hello-world-f77b4f6c8-6tcj2 to node2",
161 | "4: kubelet Pod.Pulling (2) Pulling image \"nginx:1.19.3-alpine\"",
162 | "5: kubelet Pod.Pulled (2) Successfully pulled image \"nginx:1.19.3-alpine\"",
163 | "6: kubelet Pod.Created (2) Created container hello-world",
164 | "7: kubelet Pod.Started (2) Started container hello-world",
165 | "8: deployment-controller Deployment.ScalingReplicaSet (0) Scaled down replica set hello-world-779cbf9f67 to 0",
166 | "9: replicaset-controller ReplicaSet.SuccessfulDelete (8) Deleted pod: hello-world-779cbf9f67-nbwfm",
167 | },
168 | },
169 | }
170 | threshold, err := time.Parse(time.RFC3339, fluxDeploymentUpdateEventsThresholdStr)
171 | g.Expect(err).NotTo(o.HaveOccurred())
172 |
173 | for _, tt := range tests {
174 | t.Run(tt.name, func(t *testing.T) {
175 | ctx, r, exporter, _ := newTestEventWatcher(&deploy1, &rs1, &rs2, &pod1)
176 | defer r.stop()
177 | for _, index := range tt.perm {
178 | var event corev1.Event
179 | mustParse(t, fluxDeploymentUpdateEvents[index], &event)
180 | g.Expect(r.handleEvent(ctx, &event)).To(o.Succeed())
181 | }
182 | g.Expect(r.checkOlderPending(ctx, threshold)).To(o.Succeed())
183 | r.flushOutgoing(ctx, threshold)
184 | g.Expect(exporter.dump()).To(o.Equal(tt.wantTraces))
185 | })
186 | }
187 | }
188 |
189 | func TestStsRolloutFromFlux(t *testing.T) {
190 | g := o.NewWithT(t)
191 |
192 | var (
193 | sts1 unstructured.Unstructured
194 | pod2, pod3 unstructured.Unstructured
195 | )
196 | mustParse(t, sts1str, &sts1)
197 | mustParse(t, stsPod2str, &pod2)
198 | mustParse(t, stsPod3str, &pod3)
199 |
200 | tests := []struct {
201 | name string
202 | perm []int
203 | wantTraces []string
204 | }{
205 | {
206 | name: "flux-sts",
207 | perm: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14},
208 | wantTraces: []string{
209 | "0: flux StatefulSet.Sync Commit fc4e825b46ac: Update ingester to latest, in dev",
210 | "1: statefulset-controller StatefulSet.SuccessfulDelete (0) delete Pod ingester-3 in StatefulSet ingester successful",
211 | "2: kubelet Pod.Killing (1) Stopping container ingester",
212 | "3: statefulset-controller StatefulSet.SuccessfulCreate (0) create Pod ingester-3 in StatefulSet ingester successful",
213 | "4: default-scheduler Pod.Scheduled (3) Successfully assigned cortex/ingester-3 to ip-172-20-2-25.ec2.internal",
214 | "5: kubelet Pod.Pulled (3) Container image \"cortexproject/cortex:master-a2be3d8\" already present on machine",
215 | "6: kubelet Pod.Created (3) Created container ingester",
216 | "7: kubelet Pod.Started (3) Started container ingester",
217 | "8: kubelet Pod.Killing (0) Stopping container ingester",
218 | "9: statefulset-controller StatefulSet.SuccessfulDelete (0) delete Pod ingester-2 in StatefulSet ingester successful",
219 | "10: default-scheduler Pod.Scheduled (9) Successfully assigned cortex/ingester-2 to ip-172-20-2-22.ec2.internal",
220 | "11: statefulset-controller StatefulSet.SuccessfulCreate (0) create Pod ingester-2 in StatefulSet ingester successful",
221 | "12: kubelet Pod.Pulled (11) Container image \"cortexproject/cortex:master-a2be3d8\" already present on machine",
222 | "13: kubelet Pod.Created (11) Created container ingester",
223 | "14: kubelet Pod.Started (11) Started container ingester",
224 | },
225 | },
226 | }
227 | threshold, err := time.Parse(time.RFC3339, stsUpdateEventsThresholdStr)
228 | g.Expect(err).NotTo(o.HaveOccurred())
229 |
230 | for _, tt := range tests {
231 | t.Run(tt.name, func(t *testing.T) {
232 | ctx, r, exporter, _ := newTestEventWatcher(&sts1, &pod2, &pod3)
233 | defer r.stop()
234 | for _, index := range tt.perm {
235 | var event corev1.Event
236 | mustParse(t, stsUpdateEvents[index], &event)
237 | g.Expect(r.handleEvent(ctx, &event)).To(o.Succeed())
238 | }
239 | g.Expect(r.checkOlderPending(ctx, threshold)).To(o.Succeed())
240 | r.flushOutgoing(ctx, threshold)
241 | g.Expect(exporter.dump()).To(o.Equal(tt.wantTraces))
242 | })
243 | }
244 | }
245 |
--------------------------------------------------------------------------------
/controllers/events/event_controller.go:
--------------------------------------------------------------------------------
1 | package events
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "io"
7 | "sync"
8 | "time"
9 |
10 | "github.com/go-logr/logr"
11 | "github.com/prometheus/client_golang/prometheus"
12 | tracesdk "go.opentelemetry.io/otel/sdk/export/trace"
13 | "go.opentelemetry.io/otel/sdk/resource"
14 | "go.opentelemetry.io/otel/semconv"
15 | "go.opentelemetry.io/otel/trace"
16 | corev1 "k8s.io/api/core/v1"
17 | apierrors "k8s.io/apimachinery/pkg/api/errors"
18 | "k8s.io/apimachinery/pkg/api/meta"
19 | "k8s.io/apimachinery/pkg/runtime"
20 | ctrl "sigs.k8s.io/controller-runtime"
21 | "sigs.k8s.io/controller-runtime/pkg/client"
22 | "sigs.k8s.io/controller-runtime/pkg/metrics"
23 |
24 | "github.com/weaveworks-experiments/kspan/pkg/mtime"
25 | )
26 |
27 | var (
28 | noTrace = trace.NewSpanContext(trace.SpanContextConfig{})
29 | )
30 |
31 | // EventWatcher listens to Events
32 | type EventWatcher struct {
33 | sync.Mutex
34 | Client client.Client
35 | Log logr.Logger
36 | Exporter tracesdk.SpanExporter
37 | Capture io.Writer
38 | ticker *time.Ticker
39 | startTime time.Time
40 | recent *recentInfoStore
41 | pending []*corev1.Event
42 | resources map[source]*resource.Resource
43 | outgoing *outgoing
44 | scheme *runtime.Scheme
45 | }
46 |
47 | // Info about the source of an event, e.g. kubelet
48 | type source struct {
49 | name string
50 | instance string
51 | }
52 |
53 | var (
54 | totalEventsNum = prometheus.NewCounterVec(
55 | prometheus.CounterOpts{
56 | Namespace: "kspan",
57 | Subsystem: "events",
58 | Name: "total",
59 | Help: "The total number of events.",
60 | },
61 | []string{"type", "involved_object", "reason"})
62 | )
63 |
64 | func init() {
65 | // Register custom metrics with the global prometheus registry
66 | metrics.Registry.MustRegister(totalEventsNum)
67 | }
68 |
69 | // Reconcile gets called every time an Event changes
70 | func (r *EventWatcher) Reconcile(req ctrl.Request) (ctrl.Result, error) {
71 | ctx := context.Background()
72 | log := r.Log.WithValues("event", req.NamespacedName)
73 |
74 | // Fetch the Event object
75 | var event corev1.Event
76 | if err := r.Client.Get(ctx, req.NamespacedName, &event); err != nil {
77 | if isNotFound(err) {
78 | // we get this on deleted events, which happen all the time; just ignore it.
79 | return ctrl.Result{}, nil
80 | }
81 | log.Error(err, "unable to fetch Event")
82 | return ctrl.Result{}, err
83 | }
84 |
85 | if eventTime(&event).Before(r.startTime.Add(-r.recent.recentWindow)) {
86 | // too old - ignore
87 | return ctrl.Result{}, nil
88 | }
89 |
90 | r.captureObject(&event, "event")
91 |
92 | // Bump Prometheus metrics
93 | totalEventsNum.WithLabelValues(event.Type, event.InvolvedObject.Kind, event.Reason).Inc()
94 |
95 | adjustEventTime(&event, mtime.Now())
96 |
97 | err := r.handleEvent(ctx, &event)
98 | if err != nil {
99 | log.Error(err, "unable to handle event")
100 | }
101 |
102 | return ctrl.Result{}, nil
103 | }
104 |
105 | func isNotFound(err error) bool {
106 | var unwrapped apierrors.APIStatus
107 | if errors.As(err, &unwrapped) {
108 | return apierrors.IsNotFound(unwrapped.(error))
109 | }
110 | return apierrors.IsNotFound(err)
111 | }
112 |
113 | // handleEvent is the meat of Reconcile, broken out for ease of testing.
114 | func (r *EventWatcher) handleEvent(ctx context.Context, event *corev1.Event) error {
115 | log := r.Log.WithValues("event", event.Namespace+"/"+event.Name)
116 | log.Info("event", "kind", event.InvolvedObject.Kind, "reason", event.Reason, "source", event.Source.Component)
117 |
118 | emitted, err := r.emitSpanFromEvent(ctx, log, event)
119 | if err != nil {
120 | if isNotFound(err) { // can't find something - suppress reporting because this happens often
121 | err = nil
122 | }
123 | return err
124 | }
125 | if emitted { // a new span may allow us to map one that was saved from earlier
126 | err := r.checkPending(ctx)
127 | if err != nil && !isNotFound(err) {
128 | log.Error(err, "while checking pending events")
129 | }
130 | } else {
131 | // keep this event pending for a bit, see if something shows up that will let us map it.
132 | r.Lock()
133 | r.pending = append(r.pending, event)
134 | r.Unlock()
135 | }
136 | return nil
137 | }
138 |
139 | // If our rules tell us to map this event immediately to a context, do that.
140 | func mapEventDirectlyToContext(ctx context.Context, client client.Client, event *corev1.Event, involved runtime.Object) (success bool, remoteContext trace.SpanContext, err error) {
141 | // The controller that issued this event has marked it as top-level
142 | if event.Annotations["topLevelSpan"] == "true" {
143 | m, _ := meta.Accessor(involved)
144 | remoteContext = trace.NewSpanContext(trace.SpanContextConfig{
145 | TraceID: objectToTraceID(m),
146 | })
147 | success = true
148 | }
149 | return
150 | }
151 |
152 | // attempt to map an Event to one or more Spans; return true if a Span was emitted
153 | func (r *EventWatcher) emitSpanFromEvent(ctx context.Context, log logr.Logger, event *corev1.Event) (bool, error) {
154 | ref, apiVersion, err := objectFromEvent(ctx, r.Client, event)
155 | if err != nil {
156 | return false, err
157 | }
158 |
159 | var remoteContext trace.SpanContext
160 | var success bool
161 | if ref.actor.Name != "" {
162 | // See if we have a recent event matching exactly this ref, and use its parent if found
163 | _, remoteContext, success = r.recent.lookupSpanContext(ref)
164 | if !success {
165 | // Try the owner on its own, and if found use that as the parent
166 | remoteContext, _, success = r.recent.lookupSpanContext(actionReference{object: ref.actor})
167 | }
168 | }
169 | var involved runtime.Object
170 | if !success {
171 | involved, err = getObject(ctx, r.Client, apiVersion, ref.object.Kind, ref.object.Namespace, ref.object.Name)
172 | if err == nil {
173 | r.captureObject(involved, "initial")
174 | // If our rules tell us to map this event immediately to a context, do that.
175 | success, remoteContext, err = mapEventDirectlyToContext(ctx, r.Client, event, involved)
176 | if err != nil {
177 | return false, err
178 | }
179 | }
180 | }
181 | if !success {
182 | // If the involved object (or its owner) maps to recent activity, make a span parented off that.
183 | remoteContext, err = recentSpanContextFromObject(ctx, involved, r.recent)
184 | if err != nil {
185 | return false, err
186 | }
187 | success = remoteContext.HasTraceID()
188 | }
189 | if !success {
190 | // If we have an actor distinct from the object, try the actor
191 | if ref.actor.Name != "" {
192 | involved, err = getObject(ctx, r.Client, event.InvolvedObject.APIVersion, ref.actor.Kind, ref.actor.Namespace, ref.actor.Name)
193 | if err == nil {
194 | r.captureObject(involved, "initial")
195 | remoteContext, err = recentSpanContextFromObject(ctx, involved, r.recent)
196 | if err != nil {
197 | return false, err
198 | }
199 | success = remoteContext.HasTraceID()
200 | }
201 | }
202 | }
203 | if !success {
204 | return false, nil
205 | }
206 |
207 | // Send out a span from the event details
208 | span := r.eventToSpan(event, remoteContext)
209 | r.emitSpan(ctx, ref.object, span)
210 | r.recent.store(ref, remoteContext, span.SpanContext)
211 |
212 | return true, nil
213 | }
214 |
215 | func (r *EventWatcher) getResource(s source) *resource.Resource {
216 | r.Lock()
217 | defer r.Unlock()
218 | res, found := r.resources[s]
219 | if !found {
220 | // Make a new resource and cache for later. TODO: cache eviction
221 | res = resource.NewWithAttributes(semconv.ServiceNameKey.String(s.name), semconv.ServiceInstanceIDKey.String(s.instance))
222 | r.resources[s] = res
223 | }
224 | return res
225 | }
226 |
227 | func recentSpanContextFromObject(ctx context.Context, obj runtime.Object, recent *recentInfoStore) (trace.SpanContext, error) {
228 | m, err := meta.Accessor(obj)
229 | if err != nil {
230 | return noTrace, err
231 | }
232 | // If no owners, this is a top-level object
233 | if len(m.GetOwnerReferences()) == 0 {
234 | objRef := refFromObject(m)
235 | if spanContext, _, found := recent.lookupSpanContext(actionReference{object: objRef}); found {
236 | return spanContext, nil
237 | }
238 | }
239 | // See if we have any recent event for an owner
240 | for _, ownerRef := range m.GetOwnerReferences() {
241 | ref := actionReference{
242 | actor: refFromOwner(ownerRef, m.GetNamespace()),
243 | object: refFromObject(m),
244 | }
245 | if spanContext, _, found := recent.lookupSpanContext(ref); found {
246 | return spanContext, nil
247 | }
248 | // See if we can find a sibling event for the object on its own
249 | if _, parentContext, found := recent.lookupSpanContext(actionReference{object: ref.object}); found {
250 | return parentContext, nil
251 | }
252 | // Try the owner on its own; make that the parent if found
253 | if spanContext, _, found := recent.lookupSpanContext(actionReference{object: ref.actor}); found {
254 | return spanContext, nil
255 | }
256 | }
257 | return noTrace, err
258 | }
259 |
260 | func (r *EventWatcher) makeSpanContextFromObject(ctx context.Context, obj runtime.Object, eventTime time.Time) (trace.SpanContext, error) {
261 | // See if we have any recent relevant event
262 | if sc, err := recentSpanContextFromObject(ctx, obj, r.recent); err != nil || sc.HasTraceID() {
263 | return sc, err
264 | }
265 |
266 | m, err := meta.Accessor(obj)
267 | if err != nil {
268 | return noTrace, err
269 | }
270 | // If no recent event, recurse over owners
271 | for _, ownerRef := range m.GetOwnerReferences() {
272 | owner, err := getObject(ctx, r.Client, ownerRef.APIVersion, ownerRef.Kind, m.GetNamespace(), ownerRef.Name)
273 | if err != nil {
274 | return noTrace, err
275 | }
276 | r.captureObject(owner, "initial")
277 | remoteContext, err := r.makeSpanContextFromObject(ctx, owner, eventTime)
278 | if err != nil {
279 | return noTrace, err
280 | }
281 | if remoteContext.HasTraceID() {
282 | return remoteContext, nil
283 | }
284 | }
285 | // If no owners and no recent data, create a span based off this top-level object
286 | if len(m.GetOwnerReferences()) == 0 {
287 | ref := actionReference{
288 | object: refFromObject(m),
289 | }
290 | spanData, err := r.createTraceFromTopLevelObject(ctx, obj, eventTime)
291 |
292 | if err != nil {
293 | return noTrace, err
294 | }
295 | r.emitSpan(ctx, ref.object, spanData)
296 | r.recent.store(ref, noTrace, spanData.SpanContext)
297 | return spanData.SpanContext, nil
298 | }
299 | return noTrace, nil
300 | }
301 |
302 | func (r *EventWatcher) runTicker() {
303 | // Need to check more often than the window, otherwise things will be too old.
304 | r.ticker = time.NewTicker(r.recent.recentWindow / 2)
305 | for range r.ticker.C {
306 | err := r.checkOlderPending(context.Background(), mtime.Now().Add(-r.recent.recentWindow))
307 | if err != nil {
308 | r.Log.Error(err, "from checkOlderPending")
309 | }
310 | r.recent.expire()
311 | r.flushOutgoing(context.Background(), mtime.Now().Add(-2*r.recent.recentWindow))
312 | }
313 | }
314 |
315 | func (r *EventWatcher) initialize(scheme *runtime.Scheme) {
316 | r.Lock()
317 | r.startTime = mtime.Now()
318 | r.scheme = scheme
319 | r.recent = newRecentInfoStore()
320 | r.resources = make(map[source]*resource.Resource)
321 | r.outgoing = newOutgoing()
322 | r.Unlock()
323 | go r.runTicker()
324 | }
325 |
326 | func (r *EventWatcher) stop() {
327 | if r.ticker != nil {
328 | r.ticker.Stop()
329 | }
330 | }
331 |
332 | // SetupWithManager to set up the watcher
333 | func (r *EventWatcher) SetupWithManager(mgr ctrl.Manager) error {
334 | r.initialize(mgr.GetScheme())
335 | return ctrl.NewControllerManagedBy(mgr).
336 | For(&corev1.Event{}).
337 | Complete(r)
338 | }
339 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
3 | cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
4 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
5 | github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
6 | github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
7 | github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
8 | github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
9 | github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
10 | github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
11 | github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
12 | github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
13 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
14 | github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
15 | github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
16 | github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
17 | github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
18 | github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
19 | github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
20 | github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
21 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
22 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
23 | github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
24 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
25 | github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
26 | github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
27 | github.com/benbjohnson/clock v1.0.3 h1:vkLuvpK4fmtSCuo60+yC63p7y0BmQ8gm5ZXGuBCJyXg=
28 | github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
29 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
30 | github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
31 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
32 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
33 | github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
34 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
35 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
36 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
37 | github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
38 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
39 | github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
40 | github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
41 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
42 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
43 | github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
44 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
45 | github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
46 | github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
47 | github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
48 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
49 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
50 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
51 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
52 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
53 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
54 | github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
55 | github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
56 | github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
57 | github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
58 | github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
59 | github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
60 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
61 | github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
62 | github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
63 | github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
64 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
65 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
66 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
67 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
68 | github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
69 | github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
70 | github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
71 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
72 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
73 | github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
74 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
75 | github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
76 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
77 | github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
78 | github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
79 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
80 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
81 | github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
82 | github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
83 | github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54=
84 | github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
85 | github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
86 | github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
87 | github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
88 | github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
89 | github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
90 | github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
91 | github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
92 | github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
93 | github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
94 | github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
95 | github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
96 | github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
97 | github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
98 | github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
99 | github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
100 | github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
101 | github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
102 | github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
103 | github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
104 | github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
105 | github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
106 | github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
107 | github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk=
108 | github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
109 | github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
110 | github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
111 | github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
112 | github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
113 | github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
114 | github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
115 | github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
116 | github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
117 | github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
118 | github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
119 | github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
120 | github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
121 | github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
122 | github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
123 | github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
124 | github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
125 | github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
126 | github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
127 | github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
128 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
129 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
130 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
131 | github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
132 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
133 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
134 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
135 | github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
136 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
137 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
138 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
139 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
140 | github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
141 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
142 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
143 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
144 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
145 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
146 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
147 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
148 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
149 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
150 | github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
151 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
152 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
153 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
154 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
155 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
156 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
157 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
158 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
159 | github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
160 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
161 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
162 | github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
163 | github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
164 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
165 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
166 | github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
167 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
168 | github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
169 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
170 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
171 | github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
172 | github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
173 | github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk=
174 | github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
175 | github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
176 | github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
177 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
178 | github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
179 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
180 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
181 | github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
182 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
183 | github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
184 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
185 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
186 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
187 | github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
188 | github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
189 | github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
190 | github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
191 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
192 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
193 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
194 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
195 | github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
196 | github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
197 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
198 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
199 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
200 | github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
201 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
202 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
203 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
204 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
205 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
206 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
207 | github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
208 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
209 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
210 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
211 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
212 | github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
213 | github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
214 | github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
215 | github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
216 | github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
217 | github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
218 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
219 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
220 | github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
221 | github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
222 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
223 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
224 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
225 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
226 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
227 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
228 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
229 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
230 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
231 | github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
232 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
233 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
234 | github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
235 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
236 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
237 | github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
238 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
239 | github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
240 | github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
241 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
242 | github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
243 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
244 | github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
245 | github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
246 | github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
247 | github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
248 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
249 | github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
250 | github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
251 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
252 | github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
253 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
254 | github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
255 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
256 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
257 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
258 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
259 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
260 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
261 | github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
262 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
263 | github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM=
264 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
265 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
266 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
267 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
268 | github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
269 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
270 | github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw=
271 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
272 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
273 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
274 | github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
275 | github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
276 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
277 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
278 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
279 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
280 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
281 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
282 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
283 | github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
284 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
285 | github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
286 | github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
287 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
288 | github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
289 | github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
290 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
291 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
292 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
293 | github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
294 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
295 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
296 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
297 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
298 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
299 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
300 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
301 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
302 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
303 | github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
304 | github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
305 | github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
306 | github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
307 | github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
308 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
309 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
310 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
311 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
312 | go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
313 | go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
314 | go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
315 | go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
316 | go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
317 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
318 | go.opentelemetry.io/otel v0.19.0 h1:Lenfy7QHRXPZVsw/12CWpxX6d/JkrX8wrx2vO8G80Ng=
319 | go.opentelemetry.io/otel v0.19.0/go.mod h1:j9bF567N9EfomkSidSfmMwIwIBuP37AMAIzVW85OxSg=
320 | go.opentelemetry.io/otel/exporters/otlp v0.19.0 h1:ez8agFGbFJJgBU9H3lfX0rxWhZlXqurgZKL4aDcOdqY=
321 | go.opentelemetry.io/otel/exporters/otlp v0.19.0/go.mod h1:MY1xDqVxZmOlEYbMxUHLbg0uKlnmg4XSC6Qvh6XmPZk=
322 | go.opentelemetry.io/otel/metric v0.19.0 h1:dtZ1Ju44gkJkYvo+3qGqVXmf88tc+a42edOywypengg=
323 | go.opentelemetry.io/otel/metric v0.19.0/go.mod h1:8f9fglJPRnXuskQmKpnad31lcLJ2VmNNqIsx/uIwBSc=
324 | go.opentelemetry.io/otel/oteltest v0.19.0 h1:YVfA0ByROYqTwOxqHVZYZExzEpfZor+MU1rU+ip2v9Q=
325 | go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA=
326 | go.opentelemetry.io/otel/sdk v0.19.0 h1:13pQquZyGbIvGxBWcVzUqe8kg5VGbTBiKKKXpYCylRM=
327 | go.opentelemetry.io/otel/sdk v0.19.0/go.mod h1:ouO7auJYMivDjywCHA6bqTI7jJMVQV1HdKR5CmH8DGo=
328 | go.opentelemetry.io/otel/sdk/export/metric v0.19.0 h1:9A1PC2graOx3epRLRWbq4DPCdpMUYK8XeCrdAg6ycbI=
329 | go.opentelemetry.io/otel/sdk/export/metric v0.19.0/go.mod h1:exXalzlU6quLTXiv29J+Qpj/toOzL3H5WvpbbjouTBo=
330 | go.opentelemetry.io/otel/sdk/metric v0.19.0 h1:fka1Zc/lpRMS+KlTP/TRXZuaFtSjUg/maHV3U8rt1Mc=
331 | go.opentelemetry.io/otel/sdk/metric v0.19.0/go.mod h1:t12+Mqmj64q1vMpxHlCGXGggo0sadYxEG6U+Us/9OA4=
332 | go.opentelemetry.io/otel/trace v0.19.0 h1:1ucYlenXIDA1OlHVLDZKX0ObXV5RLaq06DtUKz5e5zc=
333 | go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg=
334 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
335 | go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
336 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
337 | go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
338 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
339 | go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
340 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
341 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
342 | golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
343 | golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
344 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
345 | golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
346 | golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
347 | golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
348 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
349 | golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
350 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
351 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
352 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
353 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
354 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
355 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
356 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
357 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
358 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
359 | golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
360 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
361 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
362 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
363 | golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
364 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
365 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
366 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
367 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
368 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
369 | golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
370 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
371 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
372 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
373 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
374 | golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
375 | golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
376 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
377 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
378 | golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
379 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
380 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
381 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
382 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
383 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
384 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
385 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
386 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
387 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
388 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
389 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
390 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
391 | golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
392 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
393 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
394 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
395 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
396 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
397 | golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
398 | golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
399 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
400 | golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
401 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
402 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
403 | golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
404 | golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
405 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
406 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
407 | golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
408 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
409 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
410 | golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
411 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
412 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
413 | golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
414 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
415 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
416 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
417 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
418 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
419 | golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
420 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
421 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
422 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
423 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
424 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
425 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
426 | golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
427 | golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
428 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
429 | golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
430 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
431 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
432 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
433 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
434 | golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
435 | golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
436 | golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
437 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
438 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
439 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
440 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
441 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
442 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
443 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
444 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
445 | gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0=
446 | gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
447 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
448 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
449 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
450 | google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
451 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
452 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
453 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
454 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
455 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
456 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
457 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
458 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
459 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
460 | google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
461 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
462 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
463 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
464 | google.golang.org/grpc v1.36.0 h1:o1bcQ6imQMIOpdrO3SWf2z5RV72WbDwdXuK0MDlc8As=
465 | google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
466 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
467 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
468 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
469 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
470 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
471 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
472 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
473 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
474 | google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
475 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
476 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
477 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
478 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
479 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
480 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
481 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
482 | gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
483 | gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
484 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
485 | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
486 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
487 | gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
488 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
489 | gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
490 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
491 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
492 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
493 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
494 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
495 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
496 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
497 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
498 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
499 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
500 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
501 | gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
502 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
503 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
504 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
505 | k8s.io/api v0.18.2 h1:wG5g5ZmSVgm5B+eHMIbI9EGATS2L8Z72rda19RIEgY8=
506 | k8s.io/api v0.18.2/go.mod h1:SJCWI7OLzhZSvbY7U8zwNl9UA4o1fizoug34OV/2r78=
507 | k8s.io/apiextensions-apiserver v0.18.2 h1:I4v3/jAuQC+89L3Z7dDgAiN4EOjN6sbm6iBqQwHTah8=
508 | k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY=
509 | k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
510 | k8s.io/apimachinery v0.18.6 h1:RtFHnfGNfd1N0LeSrKCUznz5xtUP1elRGvHJbL3Ntag=
511 | k8s.io/apimachinery v0.18.6/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
512 | k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw=
513 | k8s.io/client-go v0.18.2 h1:aLB0iaD4nmwh7arT2wIn+lMnAq7OswjaejkQ8p9bBYE=
514 | k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU=
515 | k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
516 | k8s.io/component-base v0.18.2/go.mod h1:kqLlMuhJNHQ9lz8Z7V5bxUUtjFZnrypArGl58gmDfUM=
517 | k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
518 | k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
519 | k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
520 | k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
521 | k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
522 | k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
523 | k8s.io/kube-openapi v0.0.0-20200121204235-bf4fb3bd569c/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
524 | k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E=
525 | k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29 h1:NeQXVJ2XFSkRoPzRo8AId01ZER+j8oV4SZADT4iBOXQ=
526 | k8s.io/kube-openapi v0.0.0-20200410145947-bcb3869e6f29/go.mod h1:F+5wygcW0wmRTnM3cOgIqGivxkwSWIWT5YdsDbeAOaU=
527 | k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89 h1:d4vVOjXm687F1iLSP2q3lyPPuyvTUt3aVoBpi2DqRsU=
528 | k8s.io/utils v0.0.0-20200324210504-a9aa75ae1b89/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
529 | sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0=
530 | sigs.k8s.io/controller-runtime v0.6.0 h1:Fzna3DY7c4BIP6KwfSlrfnj20DJ+SeMBK8HSFvOk9NM=
531 | sigs.k8s.io/controller-runtime v0.6.0/go.mod h1:CpYf5pdNY/B352A1TFLAS2JVSlnGQ5O2cftPHndTroo=
532 | sigs.k8s.io/structured-merge-diff/v2 v2.0.1/go.mod h1:Wb7vfKAodbKgf6tn1Kl0VvGj7mRH6DGaRcixXEJXTsE=
533 | sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
534 | sigs.k8s.io/structured-merge-diff/v3 v3.0.0 h1:dOmIZBMfhcHS09XZkMyUgkq5trg3/jRyJYFZUiaOp8E=
535 | sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw=
536 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
537 | sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
538 | sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
539 |
--------------------------------------------------------------------------------
/controllers/events/testdata/deployment-2-pods.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | # {"time":"2021-05-19T09:42:58.640117041Z","style":"event","kind":"Event"}
3 | apiVersion: v1
4 | count: 1
5 | eventTime: null
6 | firstTimestamp: "2021-05-19T09:42:58Z"
7 | involvedObject:
8 | apiVersion: apps/v1
9 | kind: Deployment
10 | name: px
11 | namespace: default
12 | resourceVersion: "5361638"
13 | uid: 71c42017-61a7-418e-8e2c-897cf237d6e3
14 | kind: Event
15 | lastTimestamp: "2021-05-19T09:42:58Z"
16 | message: Scaled up replica set px-5d567cc74c to 1
17 | metadata:
18 | creationTimestamp: "2021-05-19T09:42:58Z"
19 | managedFields:
20 | - apiVersion: v1
21 | fieldsType: FieldsV1
22 | fieldsV1:
23 | f:count: {}
24 | f:firstTimestamp: {}
25 | f:involvedObject:
26 | f:apiVersion: {}
27 | f:kind: {}
28 | f:name: {}
29 | f:namespace: {}
30 | f:resourceVersion: {}
31 | f:uid: {}
32 | f:lastTimestamp: {}
33 | f:message: {}
34 | f:reason: {}
35 | f:source:
36 | f:component: {}
37 | f:type: {}
38 | manager: kube-controller-manager
39 | operation: Update
40 | time: "2021-05-19T09:42:58Z"
41 | name: px.16806e83e5b82d63
42 | namespace: default
43 | resourceVersion: "5361642"
44 | selfLink: /api/v1/namespaces/default/events/px.16806e83e5b82d63
45 | uid: e8acdb85-7a28-4ce1-9c5d-1d89467a17b6
46 | reason: ScalingReplicaSet
47 | reportingComponent: ""
48 | reportingInstance: ""
49 | source:
50 | component: deployment-controller
51 | type: Normal
52 | ---
53 | # {"time":"2021-05-19T09:42:58.650959062Z","style":"initial","kind":"ReplicaSet"}
54 | apiVersion: apps/v1
55 | kind: ReplicaSet
56 | metadata:
57 | annotations:
58 | deployment.kubernetes.io/desired-replicas: "2"
59 | deployment.kubernetes.io/max-replicas: "3"
60 | deployment.kubernetes.io/revision: "3"
61 | creationTimestamp: "2021-05-19T09:42:58Z"
62 | generation: 1
63 | labels:
64 | app: podinfo
65 | pod-template-hash: 5d567cc74c
66 | managedFields:
67 | - apiVersion: apps/v1
68 | fieldsType: FieldsV1
69 | fieldsV1:
70 | f:metadata:
71 | f:annotations:
72 | .: {}
73 | f:deployment.kubernetes.io/desired-replicas: {}
74 | f:deployment.kubernetes.io/max-replicas: {}
75 | f:deployment.kubernetes.io/revision: {}
76 | f:labels:
77 | .: {}
78 | f:app: {}
79 | f:pod-template-hash: {}
80 | f:ownerReferences:
81 | .: {}
82 | k:{"uid":"71c42017-61a7-418e-8e2c-897cf237d6e3"}:
83 | .: {}
84 | f:apiVersion: {}
85 | f:blockOwnerDeletion: {}
86 | f:controller: {}
87 | f:kind: {}
88 | f:name: {}
89 | f:uid: {}
90 | f:spec:
91 | f:replicas: {}
92 | f:selector:
93 | f:matchLabels:
94 | .: {}
95 | f:app: {}
96 | f:pod-template-hash: {}
97 | f:template:
98 | f:metadata:
99 | f:labels:
100 | .: {}
101 | f:app: {}
102 | f:pod-template-hash: {}
103 | f:spec:
104 | f:containers:
105 | k:{"name":"podinfo"}:
106 | .: {}
107 | f:image: {}
108 | f:imagePullPolicy: {}
109 | f:name: {}
110 | f:resources: {}
111 | f:terminationMessagePath: {}
112 | f:terminationMessagePolicy: {}
113 | f:dnsPolicy: {}
114 | f:restartPolicy: {}
115 | f:schedulerName: {}
116 | f:securityContext: {}
117 | f:terminationGracePeriodSeconds: {}
118 | f:status:
119 | f:observedGeneration: {}
120 | manager: kube-controller-manager
121 | operation: Update
122 | time: "2021-05-19T09:42:58Z"
123 | name: px-5d567cc74c
124 | namespace: default
125 | ownerReferences:
126 | - apiVersion: apps/v1
127 | blockOwnerDeletion: true
128 | controller: true
129 | kind: Deployment
130 | name: px
131 | uid: 71c42017-61a7-418e-8e2c-897cf237d6e3
132 | resourceVersion: "5361644"
133 | selfLink: /apis/apps/v1/namespaces/default/replicasets/px-5d567cc74c
134 | uid: 0d562fec-2485-4f88-b09e-596783c0d0a2
135 | spec:
136 | replicas: 1
137 | selector:
138 | matchLabels:
139 | app: podinfo
140 | pod-template-hash: 5d567cc74c
141 | template:
142 | metadata:
143 | creationTimestamp: null
144 | labels:
145 | app: podinfo
146 | pod-template-hash: 5d567cc74c
147 | spec:
148 | containers:
149 | - image: ghcr.io/stefanprodan/podinfo:5.0.0
150 | imagePullPolicy: IfNotPresent
151 | name: podinfo
152 | resources: {}
153 | terminationMessagePath: /dev/termination-log
154 | terminationMessagePolicy: File
155 | dnsPolicy: ClusterFirst
156 | restartPolicy: Always
157 | schedulerName: default-scheduler
158 | securityContext: {}
159 | terminationGracePeriodSeconds: 30
160 | status:
161 | observedGeneration: 1
162 | replicas: 0
163 | ---
164 | # {"time":"2021-05-19T09:42:58.655577144Z","style":"initial","kind":"Deployment"}
165 | apiVersion: apps/v1
166 | kind: Deployment
167 | metadata:
168 | annotations:
169 | deployment.kubernetes.io/revision: "3"
170 | kubectl.kubernetes.io/last-applied-configuration: |
171 | {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"name":"px","namespace":"default"},"spec":{"replicas":2,"selector":{"matchLabels":{"app":"podinfo"}},"template":{"metadata":{"labels":{"app":"podinfo"}},"spec":{"containers":[{"image":"ghcr.io/stefanprodan/podinfo:5.0.0","name":"podinfo"}]}}}}
172 | creationTimestamp: "2021-05-05T16:22:15Z"
173 | generation: 3
174 | managedFields:
175 | - apiVersion: apps/v1
176 | fieldsType: FieldsV1
177 | fieldsV1:
178 | f:metadata:
179 | f:annotations:
180 | f:deployment.kubernetes.io/revision: {}
181 | f:status:
182 | f:availableReplicas: {}
183 | f:conditions:
184 | .: {}
185 | k:{"type":"Available"}:
186 | .: {}
187 | f:lastTransitionTime: {}
188 | f:lastUpdateTime: {}
189 | f:message: {}
190 | f:reason: {}
191 | f:status: {}
192 | f:type: {}
193 | k:{"type":"Progressing"}:
194 | .: {}
195 | f:lastTransitionTime: {}
196 | f:lastUpdateTime: {}
197 | f:message: {}
198 | f:reason: {}
199 | f:status: {}
200 | f:type: {}
201 | f:observedGeneration: {}
202 | f:readyReplicas: {}
203 | f:replicas: {}
204 | f:unavailableReplicas: {}
205 | manager: kube-controller-manager
206 | operation: Update
207 | time: "2021-05-19T09:42:58Z"
208 | - apiVersion: apps/v1
209 | fieldsType: FieldsV1
210 | fieldsV1:
211 | f:metadata:
212 | f:annotations:
213 | .: {}
214 | f:kubectl.kubernetes.io/last-applied-configuration: {}
215 | f:spec:
216 | f:progressDeadlineSeconds: {}
217 | f:replicas: {}
218 | f:revisionHistoryLimit: {}
219 | f:selector:
220 | f:matchLabels:
221 | .: {}
222 | f:app: {}
223 | f:strategy:
224 | f:rollingUpdate:
225 | .: {}
226 | f:maxSurge: {}
227 | f:maxUnavailable: {}
228 | f:type: {}
229 | f:template:
230 | f:metadata:
231 | f:labels:
232 | .: {}
233 | f:app: {}
234 | f:spec:
235 | f:containers:
236 | k:{"name":"podinfo"}:
237 | .: {}
238 | f:image: {}
239 | f:imagePullPolicy: {}
240 | f:name: {}
241 | f:resources: {}
242 | f:terminationMessagePath: {}
243 | f:terminationMessagePolicy: {}
244 | f:dnsPolicy: {}
245 | f:restartPolicy: {}
246 | f:schedulerName: {}
247 | f:securityContext: {}
248 | f:terminationGracePeriodSeconds: {}
249 | manager: kubectl-client-side-apply
250 | operation: Update
251 | time: "2021-05-19T09:42:58Z"
252 | name: px
253 | namespace: default
254 | resourceVersion: "5361645"
255 | selfLink: /apis/apps/v1/namespaces/default/deployments/px
256 | uid: 71c42017-61a7-418e-8e2c-897cf237d6e3
257 | spec:
258 | progressDeadlineSeconds: 600
259 | replicas: 2
260 | revisionHistoryLimit: 10
261 | selector:
262 | matchLabels:
263 | app: podinfo
264 | strategy:
265 | rollingUpdate:
266 | maxSurge: 25%
267 | maxUnavailable: 25%
268 | type: RollingUpdate
269 | template:
270 | metadata:
271 | creationTimestamp: null
272 | labels:
273 | app: podinfo
274 | spec:
275 | containers:
276 | - image: ghcr.io/stefanprodan/podinfo:5.0.0
277 | imagePullPolicy: IfNotPresent
278 | name: podinfo
279 | resources: {}
280 | terminationMessagePath: /dev/termination-log
281 | terminationMessagePolicy: File
282 | dnsPolicy: ClusterFirst
283 | restartPolicy: Always
284 | schedulerName: default-scheduler
285 | securityContext: {}
286 | terminationGracePeriodSeconds: 30
287 | status:
288 | availableReplicas: 2
289 | conditions:
290 | - lastTransitionTime: "2021-05-05T16:22:23Z"
291 | lastUpdateTime: "2021-05-05T16:22:23Z"
292 | message: Deployment has minimum availability.
293 | reason: MinimumReplicasAvailable
294 | status: "True"
295 | type: Available
296 | - lastTransitionTime: "2021-05-05T16:22:15Z"
297 | lastUpdateTime: "2021-05-19T09:42:58Z"
298 | message: Created new replica set "px-5d567cc74c"
299 | reason: NewReplicaSetCreated
300 | status: "True"
301 | type: Progressing
302 | observedGeneration: 3
303 | readyReplicas: 2
304 | replicas: 2
305 | unavailableReplicas: 1
306 | ---
307 | # {"time":"2021-05-19T09:42:58.656696998Z","style":"event","kind":"Event"}
308 | apiVersion: v1
309 | count: 1
310 | eventTime: null
311 | firstTimestamp: "2021-05-19T09:42:58Z"
312 | involvedObject:
313 | apiVersion: apps/v1
314 | kind: ReplicaSet
315 | name: px-5d567cc74c
316 | namespace: default
317 | resourceVersion: "5361639"
318 | uid: 0d562fec-2485-4f88-b09e-596783c0d0a2
319 | kind: Event
320 | lastTimestamp: "2021-05-19T09:42:58Z"
321 | message: 'Created pod: px-5d567cc74c-ss4lb'
322 | metadata:
323 | creationTimestamp: "2021-05-19T09:42:58Z"
324 | managedFields:
325 | - apiVersion: v1
326 | fieldsType: FieldsV1
327 | fieldsV1:
328 | f:count: {}
329 | f:firstTimestamp: {}
330 | f:involvedObject:
331 | f:apiVersion: {}
332 | f:kind: {}
333 | f:name: {}
334 | f:namespace: {}
335 | f:resourceVersion: {}
336 | f:uid: {}
337 | f:lastTimestamp: {}
338 | f:message: {}
339 | f:reason: {}
340 | f:source:
341 | f:component: {}
342 | f:type: {}
343 | manager: kube-controller-manager
344 | operation: Update
345 | time: "2021-05-19T09:42:58Z"
346 | name: px-5d567cc74c.16806e83e61d2e11
347 | namespace: default
348 | resourceVersion: "5361646"
349 | selfLink: /api/v1/namespaces/default/events/px-5d567cc74c.16806e83e61d2e11
350 | uid: 487fd125-37da-4f78-aff7-914868d6ee16
351 | reason: SuccessfulCreate
352 | reportingComponent: ""
353 | reportingInstance: ""
354 | source:
355 | component: replicaset-controller
356 | type: Normal
357 | ---
358 | # {"time":"2021-05-19T09:42:58.664843487Z","style":"initial","kind":"Pod"}
359 | apiVersion: v1
360 | kind: Pod
361 | metadata:
362 | creationTimestamp: "2021-05-19T09:42:58Z"
363 | generateName: px-5d567cc74c-
364 | labels:
365 | app: podinfo
366 | pod-template-hash: 5d567cc74c
367 | managedFields:
368 | - apiVersion: v1
369 | fieldsType: FieldsV1
370 | fieldsV1:
371 | f:metadata:
372 | f:generateName: {}
373 | f:labels:
374 | .: {}
375 | f:app: {}
376 | f:pod-template-hash: {}
377 | f:ownerReferences:
378 | .: {}
379 | k:{"uid":"0d562fec-2485-4f88-b09e-596783c0d0a2"}:
380 | .: {}
381 | f:apiVersion: {}
382 | f:blockOwnerDeletion: {}
383 | f:controller: {}
384 | f:kind: {}
385 | f:name: {}
386 | f:uid: {}
387 | f:spec:
388 | f:containers:
389 | k:{"name":"podinfo"}:
390 | .: {}
391 | f:image: {}
392 | f:imagePullPolicy: {}
393 | f:name: {}
394 | f:resources: {}
395 | f:terminationMessagePath: {}
396 | f:terminationMessagePolicy: {}
397 | f:dnsPolicy: {}
398 | f:enableServiceLinks: {}
399 | f:restartPolicy: {}
400 | f:schedulerName: {}
401 | f:securityContext: {}
402 | f:terminationGracePeriodSeconds: {}
403 | manager: kube-controller-manager
404 | operation: Update
405 | time: "2021-05-19T09:42:58Z"
406 | name: px-5d567cc74c-ss4lb
407 | namespace: default
408 | ownerReferences:
409 | - apiVersion: apps/v1
410 | blockOwnerDeletion: true
411 | controller: true
412 | kind: ReplicaSet
413 | name: px-5d567cc74c
414 | uid: 0d562fec-2485-4f88-b09e-596783c0d0a2
415 | resourceVersion: "5361643"
416 | selfLink: /api/v1/namespaces/default/pods/px-5d567cc74c-ss4lb
417 | uid: 18eddc1d-87e8-4f02-8a13-f1e52db52e61
418 | spec:
419 | containers:
420 | - image: ghcr.io/stefanprodan/podinfo:5.0.0
421 | imagePullPolicy: IfNotPresent
422 | name: podinfo
423 | resources: {}
424 | terminationMessagePath: /dev/termination-log
425 | terminationMessagePolicy: File
426 | volumeMounts:
427 | - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
428 | name: default-token-kx75m
429 | readOnly: true
430 | dnsPolicy: ClusterFirst
431 | enableServiceLinks: true
432 | nodeName: kind-control-plane
433 | preemptionPolicy: PreemptLowerPriority
434 | priority: 0
435 | restartPolicy: Always
436 | schedulerName: default-scheduler
437 | securityContext: {}
438 | serviceAccount: default
439 | serviceAccountName: default
440 | terminationGracePeriodSeconds: 30
441 | tolerations:
442 | - effect: NoExecute
443 | key: node.kubernetes.io/not-ready
444 | operator: Exists
445 | tolerationSeconds: 300
446 | - effect: NoExecute
447 | key: node.kubernetes.io/unreachable
448 | operator: Exists
449 | tolerationSeconds: 300
450 | volumes:
451 | - name: default-token-kx75m
452 | secret:
453 | defaultMode: 420
454 | secretName: default-token-kx75m
455 | status:
456 | conditions:
457 | - lastProbeTime: null
458 | lastTransitionTime: "2021-05-19T09:42:58Z"
459 | status: "True"
460 | type: PodScheduled
461 | phase: Pending
462 | qosClass: BestEffort
463 | ---
464 | # {"time":"2021-05-19T09:42:58.675382292Z","style":"event","kind":"Event"}
465 | action: Binding
466 | apiVersion: v1
467 | eventTime: "2021-05-19T09:42:58.638469Z"
468 | firstTimestamp: null
469 | involvedObject:
470 | apiVersion: v1
471 | kind: Pod
472 | name: px-5d567cc74c-ss4lb
473 | namespace: default
474 | resourceVersion: "5361641"
475 | uid: 18eddc1d-87e8-4f02-8a13-f1e52db52e61
476 | kind: Event
477 | lastTimestamp: null
478 | message: Successfully assigned default/px-5d567cc74c-ss4lb to kind-control-plane
479 | metadata:
480 | creationTimestamp: "2021-05-19T09:42:58Z"
481 | managedFields:
482 | - apiVersion: events.k8s.io/v1
483 | fieldsType: FieldsV1
484 | fieldsV1:
485 | f:action: {}
486 | f:eventTime: {}
487 | f:note: {}
488 | f:reason: {}
489 | f:regarding:
490 | f:apiVersion: {}
491 | f:kind: {}
492 | f:name: {}
493 | f:namespace: {}
494 | f:resourceVersion: {}
495 | f:uid: {}
496 | f:reportingController: {}
497 | f:reportingInstance: {}
498 | f:type: {}
499 | manager: kube-scheduler
500 | operation: Update
501 | time: "2021-05-19T09:42:58Z"
502 | name: px-5d567cc74c-ss4lb.16806e83e6642682
503 | namespace: default
504 | resourceVersion: "5361647"
505 | selfLink: /api/v1/namespaces/default/events/px-5d567cc74c-ss4lb.16806e83e6642682
506 | uid: 264fc251-9856-4f85-b282-5306c9baa65d
507 | reason: Scheduled
508 | reportingComponent: default-scheduler
509 | reportingInstance: default-scheduler-kind-control-plane
510 | source: {}
511 | type: Normal
512 | ---
513 | # {"time":"2021-05-19T09:42:59.20271856Z","style":"event","kind":"Event"}
514 | apiVersion: v1
515 | count: 1
516 | eventTime: null
517 | firstTimestamp: "2021-05-19T09:42:59Z"
518 | involvedObject:
519 | apiVersion: v1
520 | fieldPath: spec.containers{podinfo}
521 | kind: Pod
522 | name: px-5d567cc74c-ss4lb
523 | namespace: default
524 | resourceVersion: "5361643"
525 | uid: 18eddc1d-87e8-4f02-8a13-f1e52db52e61
526 | kind: Event
527 | lastTimestamp: "2021-05-19T09:42:59Z"
528 | message: Pulling image "ghcr.io/stefanprodan/podinfo:5.0.0"
529 | metadata:
530 | creationTimestamp: "2021-05-19T09:42:59Z"
531 | managedFields:
532 | - apiVersion: v1
533 | fieldsType: FieldsV1
534 | fieldsV1:
535 | f:count: {}
536 | f:firstTimestamp: {}
537 | f:involvedObject:
538 | f:apiVersion: {}
539 | f:fieldPath: {}
540 | f:kind: {}
541 | f:name: {}
542 | f:namespace: {}
543 | f:resourceVersion: {}
544 | f:uid: {}
545 | f:lastTimestamp: {}
546 | f:message: {}
547 | f:reason: {}
548 | f:source:
549 | f:component: {}
550 | f:host: {}
551 | f:type: {}
552 | manager: kubelet
553 | operation: Update
554 | time: "2021-05-19T09:42:59Z"
555 | name: px-5d567cc74c-ss4lb.16806e8407b4b2a2
556 | namespace: default
557 | resourceVersion: "5361653"
558 | selfLink: /api/v1/namespaces/default/events/px-5d567cc74c-ss4lb.16806e8407b4b2a2
559 | uid: 8d24b1a4-c688-411c-9bea-ecfe0dea987c
560 | reason: Pulling
561 | reportingComponent: ""
562 | reportingInstance: ""
563 | source:
564 | component: kubelet
565 | host: kind-control-plane
566 | type: Normal
567 | ---
568 | # {"time":"2021-05-19T09:43:05.070173525Z","style":"event","kind":"Event"}
569 | apiVersion: v1
570 | count: 1
571 | eventTime: null
572 | firstTimestamp: "2021-05-19T09:43:05Z"
573 | involvedObject:
574 | apiVersion: v1
575 | fieldPath: spec.containers{podinfo}
576 | kind: Pod
577 | name: px-5d567cc74c-ss4lb
578 | namespace: default
579 | resourceVersion: "5361643"
580 | uid: 18eddc1d-87e8-4f02-8a13-f1e52db52e61
581 | kind: Event
582 | lastTimestamp: "2021-05-19T09:43:05Z"
583 | message: Successfully pulled image "ghcr.io/stefanprodan/podinfo:5.0.0" in 5.870196872s
584 | metadata:
585 | creationTimestamp: "2021-05-19T09:43:05Z"
586 | managedFields:
587 | - apiVersion: v1
588 | fieldsType: FieldsV1
589 | fieldsV1:
590 | f:count: {}
591 | f:firstTimestamp: {}
592 | f:involvedObject:
593 | f:apiVersion: {}
594 | f:fieldPath: {}
595 | f:kind: {}
596 | f:name: {}
597 | f:namespace: {}
598 | f:resourceVersion: {}
599 | f:uid: {}
600 | f:lastTimestamp: {}
601 | f:message: {}
602 | f:reason: {}
603 | f:source:
604 | f:component: {}
605 | f:host: {}
606 | f:type: {}
607 | manager: kubelet
608 | operation: Update
609 | time: "2021-05-19T09:43:05Z"
610 | name: px-5d567cc74c-ss4lb.16806e856598efd4
611 | namespace: default
612 | resourceVersion: "5361669"
613 | selfLink: /api/v1/namespaces/default/events/px-5d567cc74c-ss4lb.16806e856598efd4
614 | uid: ee3767ba-caae-4d37-a7d5-4e0975d0d327
615 | reason: Pulled
616 | reportingComponent: ""
617 | reportingInstance: ""
618 | source:
619 | component: kubelet
620 | host: kind-control-plane
621 | type: Normal
622 | ---
623 | # {"time":"2021-05-19T09:43:05.278784076Z","style":"event","kind":"Event"}
624 | apiVersion: v1
625 | count: 1
626 | eventTime: null
627 | firstTimestamp: "2021-05-19T09:43:05Z"
628 | involvedObject:
629 | apiVersion: v1
630 | fieldPath: spec.containers{podinfo}
631 | kind: Pod
632 | name: px-5d567cc74c-ss4lb
633 | namespace: default
634 | resourceVersion: "5361643"
635 | uid: 18eddc1d-87e8-4f02-8a13-f1e52db52e61
636 | kind: Event
637 | lastTimestamp: "2021-05-19T09:43:05Z"
638 | message: Created container podinfo
639 | metadata:
640 | creationTimestamp: "2021-05-19T09:43:05Z"
641 | managedFields:
642 | - apiVersion: v1
643 | fieldsType: FieldsV1
644 | fieldsV1:
645 | f:count: {}
646 | f:firstTimestamp: {}
647 | f:involvedObject:
648 | f:apiVersion: {}
649 | f:fieldPath: {}
650 | f:kind: {}
651 | f:name: {}
652 | f:namespace: {}
653 | f:resourceVersion: {}
654 | f:uid: {}
655 | f:lastTimestamp: {}
656 | f:message: {}
657 | f:reason: {}
658 | f:source:
659 | f:component: {}
660 | f:host: {}
661 | f:type: {}
662 | manager: kubelet
663 | operation: Update
664 | time: "2021-05-19T09:43:05Z"
665 | name: px-5d567cc74c-ss4lb.16806e8572001c74
666 | namespace: default
667 | resourceVersion: "5361670"
668 | selfLink: /api/v1/namespaces/default/events/px-5d567cc74c-ss4lb.16806e8572001c74
669 | uid: 68fe3453-bea6-4998-8fca-c64776de2ce5
670 | reason: Created
671 | reportingComponent: ""
672 | reportingInstance: ""
673 | source:
674 | component: kubelet
675 | host: kind-control-plane
676 | type: Normal
677 | ---
678 | # {"time":"2021-05-19T09:43:05.385591121Z","style":"event","kind":"Event"}
679 | apiVersion: v1
680 | count: 1
681 | eventTime: null
682 | firstTimestamp: "2021-05-19T09:43:05Z"
683 | involvedObject:
684 | apiVersion: v1
685 | fieldPath: spec.containers{podinfo}
686 | kind: Pod
687 | name: px-5d567cc74c-ss4lb
688 | namespace: default
689 | resourceVersion: "5361643"
690 | uid: 18eddc1d-87e8-4f02-8a13-f1e52db52e61
691 | kind: Event
692 | lastTimestamp: "2021-05-19T09:43:05Z"
693 | message: Started container podinfo
694 | metadata:
695 | creationTimestamp: "2021-05-19T09:43:05Z"
696 | managedFields:
697 | - apiVersion: v1
698 | fieldsType: FieldsV1
699 | fieldsV1:
700 | f:count: {}
701 | f:firstTimestamp: {}
702 | f:involvedObject:
703 | f:apiVersion: {}
704 | f:fieldPath: {}
705 | f:kind: {}
706 | f:name: {}
707 | f:namespace: {}
708 | f:resourceVersion: {}
709 | f:uid: {}
710 | f:lastTimestamp: {}
711 | f:message: {}
712 | f:reason: {}
713 | f:source:
714 | f:component: {}
715 | f:host: {}
716 | f:type: {}
717 | manager: kubelet
718 | operation: Update
719 | time: "2021-05-19T09:43:05Z"
720 | name: px-5d567cc74c-ss4lb.16806e857858a068
721 | namespace: default
722 | resourceVersion: "5361671"
723 | selfLink: /api/v1/namespaces/default/events/px-5d567cc74c-ss4lb.16806e857858a068
724 | uid: ff6a4c2b-37af-45aa-9604-69e0f1711001
725 | reason: Started
726 | reportingComponent: ""
727 | reportingInstance: ""
728 | source:
729 | component: kubelet
730 | host: kind-control-plane
731 | type: Normal
732 | ---
733 | # {"time":"2021-05-19T09:43:06.201341985Z","style":"event","kind":"Event"}
734 | apiVersion: v1
735 | count: 1
736 | eventTime: null
737 | firstTimestamp: "2021-05-19T09:43:06Z"
738 | involvedObject:
739 | apiVersion: apps/v1
740 | kind: ReplicaSet
741 | name: px-7df978b9bf
742 | namespace: default
743 | resourceVersion: "5361678"
744 | uid: e8a3abe0-ab69-48f7-9d18-6c0d6c562aab
745 | kind: Event
746 | lastTimestamp: "2021-05-19T09:43:06Z"
747 | message: 'Deleted pod: px-7df978b9bf-jm22q'
748 | metadata:
749 | creationTimestamp: "2021-05-19T09:43:06Z"
750 | managedFields:
751 | - apiVersion: v1
752 | fieldsType: FieldsV1
753 | fieldsV1:
754 | f:count: {}
755 | f:firstTimestamp: {}
756 | f:involvedObject:
757 | f:apiVersion: {}
758 | f:kind: {}
759 | f:name: {}
760 | f:namespace: {}
761 | f:resourceVersion: {}
762 | f:uid: {}
763 | f:lastTimestamp: {}
764 | f:message: {}
765 | f:reason: {}
766 | f:source:
767 | f:component: {}
768 | f:type: {}
769 | manager: kube-controller-manager
770 | operation: Update
771 | time: "2021-05-19T09:43:06Z"
772 | name: px-7df978b9bf.16806e85a8636752
773 | namespace: default
774 | resourceVersion: "5361682"
775 | selfLink: /api/v1/namespaces/default/events/px-7df978b9bf.16806e85a8636752
776 | uid: 009a57af-14cb-4110-9f1b-28737fa14f5b
777 | reason: SuccessfulDelete
778 | reportingComponent: ""
779 | reportingInstance: ""
780 | source:
781 | component: replicaset-controller
782 | type: Normal
783 | ---
784 | # {"time":"2021-05-19T09:43:06.205375953Z","style":"initial","kind":"Pod"}
785 | apiVersion: v1
786 | kind: Pod
787 | metadata:
788 | creationTimestamp: "2021-05-18T17:10:50Z"
789 | deletionGracePeriodSeconds: 30
790 | deletionTimestamp: "2021-05-19T09:43:36Z"
791 | generateName: px-7df978b9bf-
792 | labels:
793 | app: podinfo
794 | pod-template-hash: 7df978b9bf
795 | managedFields:
796 | - apiVersion: v1
797 | fieldsType: FieldsV1
798 | fieldsV1:
799 | f:metadata:
800 | f:generateName: {}
801 | f:labels:
802 | .: {}
803 | f:app: {}
804 | f:pod-template-hash: {}
805 | f:ownerReferences:
806 | .: {}
807 | k:{"uid":"e8a3abe0-ab69-48f7-9d18-6c0d6c562aab"}:
808 | .: {}
809 | f:apiVersion: {}
810 | f:blockOwnerDeletion: {}
811 | f:controller: {}
812 | f:kind: {}
813 | f:name: {}
814 | f:uid: {}
815 | f:spec:
816 | f:containers:
817 | k:{"name":"podinfo"}:
818 | .: {}
819 | f:image: {}
820 | f:imagePullPolicy: {}
821 | f:name: {}
822 | f:resources: {}
823 | f:terminationMessagePath: {}
824 | f:terminationMessagePolicy: {}
825 | f:dnsPolicy: {}
826 | f:enableServiceLinks: {}
827 | f:restartPolicy: {}
828 | f:schedulerName: {}
829 | f:securityContext: {}
830 | f:terminationGracePeriodSeconds: {}
831 | manager: kube-controller-manager
832 | operation: Update
833 | time: "2021-05-18T17:10:50Z"
834 | - apiVersion: v1
835 | fieldsType: FieldsV1
836 | fieldsV1:
837 | f:status:
838 | f:conditions:
839 | k:{"type":"ContainersReady"}:
840 | .: {}
841 | f:lastProbeTime: {}
842 | f:lastTransitionTime: {}
843 | f:status: {}
844 | f:type: {}
845 | k:{"type":"Initialized"}:
846 | .: {}
847 | f:lastProbeTime: {}
848 | f:lastTransitionTime: {}
849 | f:status: {}
850 | f:type: {}
851 | k:{"type":"Ready"}:
852 | .: {}
853 | f:lastProbeTime: {}
854 | f:lastTransitionTime: {}
855 | f:status: {}
856 | f:type: {}
857 | f:containerStatuses: {}
858 | f:hostIP: {}
859 | f:phase: {}
860 | f:podIP: {}
861 | f:podIPs:
862 | .: {}
863 | k:{"ip":"10.244.0.8"}:
864 | .: {}
865 | f:ip: {}
866 | f:startTime: {}
867 | manager: kubelet
868 | operation: Update
869 | time: "2021-05-18T17:10:51Z"
870 | name: px-7df978b9bf-jm22q
871 | namespace: default
872 | ownerReferences:
873 | - apiVersion: apps/v1
874 | blockOwnerDeletion: true
875 | controller: true
876 | kind: ReplicaSet
877 | name: px-7df978b9bf
878 | uid: e8a3abe0-ab69-48f7-9d18-6c0d6c562aab
879 | resourceVersion: "5361679"
880 | selfLink: /api/v1/namespaces/default/pods/px-7df978b9bf-jm22q
881 | uid: ac780302-1895-43b1-89e0-80f85c8e5457
882 | spec:
883 | containers:
884 | - image: ghcr.io/stefanprodan/podinfo:5.1.3
885 | imagePullPolicy: IfNotPresent
886 | name: podinfo
887 | resources: {}
888 | terminationMessagePath: /dev/termination-log
889 | terminationMessagePolicy: File
890 | volumeMounts:
891 | - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
892 | name: default-token-kx75m
893 | readOnly: true
894 | dnsPolicy: ClusterFirst
895 | enableServiceLinks: true
896 | nodeName: kind-control-plane
897 | preemptionPolicy: PreemptLowerPriority
898 | priority: 0
899 | restartPolicy: Always
900 | schedulerName: default-scheduler
901 | securityContext: {}
902 | serviceAccount: default
903 | serviceAccountName: default
904 | terminationGracePeriodSeconds: 30
905 | tolerations:
906 | - effect: NoExecute
907 | key: node.kubernetes.io/not-ready
908 | operator: Exists
909 | tolerationSeconds: 300
910 | - effect: NoExecute
911 | key: node.kubernetes.io/unreachable
912 | operator: Exists
913 | tolerationSeconds: 300
914 | volumes:
915 | - name: default-token-kx75m
916 | secret:
917 | defaultMode: 420
918 | secretName: default-token-kx75m
919 | status:
920 | conditions:
921 | - lastProbeTime: null
922 | lastTransitionTime: "2021-05-18T17:10:50Z"
923 | status: "True"
924 | type: Initialized
925 | - lastProbeTime: null
926 | lastTransitionTime: "2021-05-18T17:10:51Z"
927 | status: "True"
928 | type: Ready
929 | - lastProbeTime: null
930 | lastTransitionTime: "2021-05-18T17:10:51Z"
931 | status: "True"
932 | type: ContainersReady
933 | - lastProbeTime: null
934 | lastTransitionTime: "2021-05-18T17:10:50Z"
935 | status: "True"
936 | type: PodScheduled
937 | containerStatuses:
938 | - containerID: containerd://4f0072b3f51e88b2da583f0fc039968865ae60c60a81452a37828f6238a2e359
939 | image: ghcr.io/stefanprodan/podinfo:5.1.3
940 | imageID: ghcr.io/stefanprodan/podinfo@sha256:1cf7ad3ac4b7fc05773ec5deebbdf325566ee04483390688e09b53163ebb5bfd
941 | lastState: {}
942 | name: podinfo
943 | ready: true
944 | restartCount: 0
945 | started: true
946 | state:
947 | running:
948 | startedAt: "2021-05-18T17:10:51Z"
949 | hostIP: 172.18.0.2
950 | phase: Running
951 | podIP: 10.244.0.8
952 | podIPs:
953 | - ip: 10.244.0.8
954 | qosClass: BestEffort
955 | startTime: "2021-05-18T17:10:50Z"
956 | ---
957 | # {"time":"2021-05-19T09:43:06.225314506Z","style":"initial","kind":"ReplicaSet"}
958 | apiVersion: apps/v1
959 | kind: ReplicaSet
960 | metadata:
961 | annotations:
962 | deployment.kubernetes.io/desired-replicas: "2"
963 | deployment.kubernetes.io/max-replicas: "3"
964 | deployment.kubernetes.io/revision: "2"
965 | creationTimestamp: "2021-05-18T17:10:49Z"
966 | generation: 3
967 | labels:
968 | app: podinfo
969 | pod-template-hash: 7df978b9bf
970 | managedFields:
971 | - apiVersion: apps/v1
972 | fieldsType: FieldsV1
973 | fieldsV1:
974 | f:metadata:
975 | f:annotations:
976 | .: {}
977 | f:deployment.kubernetes.io/desired-replicas: {}
978 | f:deployment.kubernetes.io/max-replicas: {}
979 | f:deployment.kubernetes.io/revision: {}
980 | f:labels:
981 | .: {}
982 | f:app: {}
983 | f:pod-template-hash: {}
984 | f:ownerReferences:
985 | .: {}
986 | k:{"uid":"71c42017-61a7-418e-8e2c-897cf237d6e3"}:
987 | .: {}
988 | f:apiVersion: {}
989 | f:blockOwnerDeletion: {}
990 | f:controller: {}
991 | f:kind: {}
992 | f:name: {}
993 | f:uid: {}
994 | f:spec:
995 | f:replicas: {}
996 | f:selector:
997 | f:matchLabels:
998 | .: {}
999 | f:app: {}
1000 | f:pod-template-hash: {}
1001 | f:template:
1002 | f:metadata:
1003 | f:labels:
1004 | .: {}
1005 | f:app: {}
1006 | f:pod-template-hash: {}
1007 | f:spec:
1008 | f:containers:
1009 | k:{"name":"podinfo"}:
1010 | .: {}
1011 | f:image: {}
1012 | f:imagePullPolicy: {}
1013 | f:name: {}
1014 | f:resources: {}
1015 | f:terminationMessagePath: {}
1016 | f:terminationMessagePolicy: {}
1017 | f:dnsPolicy: {}
1018 | f:restartPolicy: {}
1019 | f:schedulerName: {}
1020 | f:securityContext: {}
1021 | f:terminationGracePeriodSeconds: {}
1022 | f:status:
1023 | f:availableReplicas: {}
1024 | f:fullyLabeledReplicas: {}
1025 | f:observedGeneration: {}
1026 | f:readyReplicas: {}
1027 | f:replicas: {}
1028 | manager: kube-controller-manager
1029 | operation: Update
1030 | time: "2021-05-19T09:43:06Z"
1031 | name: px-7df978b9bf
1032 | namespace: default
1033 | ownerReferences:
1034 | - apiVersion: apps/v1
1035 | blockOwnerDeletion: true
1036 | controller: true
1037 | kind: Deployment
1038 | name: px
1039 | uid: 71c42017-61a7-418e-8e2c-897cf237d6e3
1040 | resourceVersion: "5361680"
1041 | selfLink: /apis/apps/v1/namespaces/default/replicasets/px-7df978b9bf
1042 | uid: e8a3abe0-ab69-48f7-9d18-6c0d6c562aab
1043 | spec:
1044 | replicas: 1
1045 | selector:
1046 | matchLabels:
1047 | app: podinfo
1048 | pod-template-hash: 7df978b9bf
1049 | template:
1050 | metadata:
1051 | creationTimestamp: null
1052 | labels:
1053 | app: podinfo
1054 | pod-template-hash: 7df978b9bf
1055 | spec:
1056 | containers:
1057 | - image: ghcr.io/stefanprodan/podinfo:5.1.3
1058 | imagePullPolicy: IfNotPresent
1059 | name: podinfo
1060 | resources: {}
1061 | terminationMessagePath: /dev/termination-log
1062 | terminationMessagePolicy: File
1063 | dnsPolicy: ClusterFirst
1064 | restartPolicy: Always
1065 | schedulerName: default-scheduler
1066 | securityContext: {}
1067 | terminationGracePeriodSeconds: 30
1068 | status:
1069 | availableReplicas: 2
1070 | fullyLabeledReplicas: 2
1071 | observedGeneration: 3
1072 | readyReplicas: 2
1073 | replicas: 2
1074 | ---
1075 | # {"time":"2021-05-19T09:43:06.227373365Z","style":"event","kind":"Event"}
1076 | apiVersion: v1
1077 | count: 1
1078 | eventTime: null
1079 | firstTimestamp: "2021-05-19T09:43:06Z"
1080 | involvedObject:
1081 | apiVersion: apps/v1
1082 | kind: Deployment
1083 | name: px
1084 | namespace: default
1085 | resourceVersion: "5361649"
1086 | uid: 71c42017-61a7-418e-8e2c-897cf237d6e3
1087 | kind: Event
1088 | lastTimestamp: "2021-05-19T09:43:06Z"
1089 | message: Scaled down replica set px-7df978b9bf to 1
1090 | metadata:
1091 | creationTimestamp: "2021-05-19T09:43:06Z"
1092 | managedFields:
1093 | - apiVersion: v1
1094 | fieldsType: FieldsV1
1095 | fieldsV1:
1096 | f:count: {}
1097 | f:firstTimestamp: {}
1098 | f:involvedObject:
1099 | f:apiVersion: {}
1100 | f:kind: {}
1101 | f:name: {}
1102 | f:namespace: {}
1103 | f:resourceVersion: {}
1104 | f:uid: {}
1105 | f:lastTimestamp: {}
1106 | f:message: {}
1107 | f:reason: {}
1108 | f:source:
1109 | f:component: {}
1110 | f:type: {}
1111 | manager: kube-controller-manager
1112 | operation: Update
1113 | time: "2021-05-19T09:43:06Z"
1114 | name: px.16806e85a868c41d
1115 | namespace: default
1116 | resourceVersion: "5361683"
1117 | selfLink: /api/v1/namespaces/default/events/px.16806e85a868c41d
1118 | uid: e5614efd-c63c-43f2-bb57-3823aa0d841d
1119 | reason: ScalingReplicaSet
1120 | reportingComponent: ""
1121 | reportingInstance: ""
1122 | source:
1123 | component: deployment-controller
1124 | type: Normal
1125 | ---
1126 | # {"time":"2021-05-19T09:43:06.228091735Z","style":"event","kind":"Event"}
1127 | apiVersion: v1
1128 | count: 1
1129 | eventTime: null
1130 | firstTimestamp: "2021-05-19T09:43:06Z"
1131 | involvedObject:
1132 | apiVersion: v1
1133 | fieldPath: spec.containers{podinfo}
1134 | kind: Pod
1135 | name: px-7df978b9bf-jm22q
1136 | namespace: default
1137 | resourceVersion: "5201920"
1138 | uid: ac780302-1895-43b1-89e0-80f85c8e5457
1139 | kind: Event
1140 | lastTimestamp: "2021-05-19T09:43:06Z"
1141 | message: Stopping container podinfo
1142 | metadata:
1143 | creationTimestamp: "2021-05-19T09:43:06Z"
1144 | managedFields:
1145 | - apiVersion: v1
1146 | fieldsType: FieldsV1
1147 | fieldsV1:
1148 | f:count: {}
1149 | f:firstTimestamp: {}
1150 | f:involvedObject:
1151 | f:apiVersion: {}
1152 | f:fieldPath: {}
1153 | f:kind: {}
1154 | f:name: {}
1155 | f:namespace: {}
1156 | f:resourceVersion: {}
1157 | f:uid: {}
1158 | f:lastTimestamp: {}
1159 | f:message: {}
1160 | f:reason: {}
1161 | f:source:
1162 | f:component: {}
1163 | f:host: {}
1164 | f:type: {}
1165 | manager: kubelet
1166 | operation: Update
1167 | time: "2021-05-19T09:43:06Z"
1168 | name: px-7df978b9bf-jm22q.16806e85a86a0e56
1169 | namespace: default
1170 | resourceVersion: "5361684"
1171 | selfLink: /api/v1/namespaces/default/events/px-7df978b9bf-jm22q.16806e85a86a0e56
1172 | uid: c5a00f97-3191-4f3f-a9be-ef7f0f58370e
1173 | reason: Killing
1174 | reportingComponent: ""
1175 | reportingInstance: ""
1176 | source:
1177 | component: kubelet
1178 | host: kind-control-plane
1179 | type: Normal
1180 | ---
1181 | # {"time":"2021-05-19T09:43:06.245058316Z","style":"event","kind":"Event"}
1182 | apiVersion: v1
1183 | count: 1
1184 | eventTime: null
1185 | firstTimestamp: "2021-05-19T09:43:06Z"
1186 | involvedObject:
1187 | apiVersion: apps/v1
1188 | kind: Deployment
1189 | name: px
1190 | namespace: default
1191 | resourceVersion: "5361681"
1192 | uid: 71c42017-61a7-418e-8e2c-897cf237d6e3
1193 | kind: Event
1194 | lastTimestamp: "2021-05-19T09:43:06Z"
1195 | message: Scaled up replica set px-5d567cc74c to 2
1196 | metadata:
1197 | creationTimestamp: "2021-05-19T09:43:06Z"
1198 | managedFields:
1199 | - apiVersion: v1
1200 | fieldsType: FieldsV1
1201 | fieldsV1:
1202 | f:count: {}
1203 | f:firstTimestamp: {}
1204 | f:involvedObject:
1205 | f:apiVersion: {}
1206 | f:kind: {}
1207 | f:name: {}
1208 | f:namespace: {}
1209 | f:resourceVersion: {}
1210 | f:uid: {}
1211 | f:lastTimestamp: {}
1212 | f:message: {}
1213 | f:reason: {}
1214 | f:source:
1215 | f:component: {}
1216 | f:type: {}
1217 | manager: kube-controller-manager
1218 | operation: Update
1219 | time: "2021-05-19T09:43:06Z"
1220 | name: px.16806e85a9bbc38b
1221 | namespace: default
1222 | resourceVersion: "5361687"
1223 | selfLink: /api/v1/namespaces/default/events/px.16806e85a9bbc38b
1224 | uid: 67716c18-4c32-40d4-9ca1-d78c3819b2c5
1225 | reason: ScalingReplicaSet
1226 | reportingComponent: ""
1227 | reportingInstance: ""
1228 | source:
1229 | component: deployment-controller
1230 | type: Normal
1231 | ---
1232 | # {"time":"2021-05-19T09:43:06.245873474Z","style":"event","kind":"Event"}
1233 | apiVersion: v1
1234 | count: 1
1235 | eventTime: null
1236 | firstTimestamp: "2021-05-19T09:43:06Z"
1237 | involvedObject:
1238 | apiVersion: apps/v1
1239 | kind: ReplicaSet
1240 | name: px-5d567cc74c
1241 | namespace: default
1242 | resourceVersion: "5361685"
1243 | uid: 0d562fec-2485-4f88-b09e-596783c0d0a2
1244 | kind: Event
1245 | lastTimestamp: "2021-05-19T09:43:06Z"
1246 | message: 'Created pod: px-5d567cc74c-pmvzr'
1247 | metadata:
1248 | creationTimestamp: "2021-05-19T09:43:06Z"
1249 | managedFields:
1250 | - apiVersion: v1
1251 | fieldsType: FieldsV1
1252 | fieldsV1:
1253 | f:count: {}
1254 | f:firstTimestamp: {}
1255 | f:involvedObject:
1256 | f:apiVersion: {}
1257 | f:kind: {}
1258 | f:name: {}
1259 | f:namespace: {}
1260 | f:resourceVersion: {}
1261 | f:uid: {}
1262 | f:lastTimestamp: {}
1263 | f:message: {}
1264 | f:reason: {}
1265 | f:source:
1266 | f:component: {}
1267 | f:type: {}
1268 | manager: kube-controller-manager
1269 | operation: Update
1270 | time: "2021-05-19T09:43:06Z"
1271 | name: px-5d567cc74c.16806e85a9ffbf53
1272 | namespace: default
1273 | resourceVersion: "5361690"
1274 | selfLink: /api/v1/namespaces/default/events/px-5d567cc74c.16806e85a9ffbf53
1275 | uid: 94b6e861-4c67-43c9-9c54-e829dec8637d
1276 | reason: SuccessfulCreate
1277 | reportingComponent: ""
1278 | reportingInstance: ""
1279 | source:
1280 | component: replicaset-controller
1281 | type: Normal
1282 | ---
1283 | # {"time":"2021-05-19T09:43:06.253973869Z","style":"initial","kind":"Pod"}
1284 | apiVersion: v1
1285 | kind: Pod
1286 | metadata:
1287 | creationTimestamp: "2021-05-19T09:43:06Z"
1288 | generateName: px-5d567cc74c-
1289 | labels:
1290 | app: podinfo
1291 | pod-template-hash: 5d567cc74c
1292 | managedFields:
1293 | - apiVersion: v1
1294 | fieldsType: FieldsV1
1295 | fieldsV1:
1296 | f:metadata:
1297 | f:generateName: {}
1298 | f:labels:
1299 | .: {}
1300 | f:app: {}
1301 | f:pod-template-hash: {}
1302 | f:ownerReferences:
1303 | .: {}
1304 | k:{"uid":"0d562fec-2485-4f88-b09e-596783c0d0a2"}:
1305 | .: {}
1306 | f:apiVersion: {}
1307 | f:blockOwnerDeletion: {}
1308 | f:controller: {}
1309 | f:kind: {}
1310 | f:name: {}
1311 | f:uid: {}
1312 | f:spec:
1313 | f:containers:
1314 | k:{"name":"podinfo"}:
1315 | .: {}
1316 | f:image: {}
1317 | f:imagePullPolicy: {}
1318 | f:name: {}
1319 | f:resources: {}
1320 | f:terminationMessagePath: {}
1321 | f:terminationMessagePolicy: {}
1322 | f:dnsPolicy: {}
1323 | f:enableServiceLinks: {}
1324 | f:restartPolicy: {}
1325 | f:schedulerName: {}
1326 | f:securityContext: {}
1327 | f:terminationGracePeriodSeconds: {}
1328 | manager: kube-controller-manager
1329 | operation: Update
1330 | time: "2021-05-19T09:43:06Z"
1331 | - apiVersion: v1
1332 | fieldsType: FieldsV1
1333 | fieldsV1:
1334 | f:status:
1335 | f:conditions:
1336 | k:{"type":"ContainersReady"}:
1337 | .: {}
1338 | f:lastProbeTime: {}
1339 | f:lastTransitionTime: {}
1340 | f:message: {}
1341 | f:reason: {}
1342 | f:status: {}
1343 | f:type: {}
1344 | k:{"type":"Initialized"}:
1345 | .: {}
1346 | f:lastProbeTime: {}
1347 | f:lastTransitionTime: {}
1348 | f:status: {}
1349 | f:type: {}
1350 | k:{"type":"Ready"}:
1351 | .: {}
1352 | f:lastProbeTime: {}
1353 | f:lastTransitionTime: {}
1354 | f:message: {}
1355 | f:reason: {}
1356 | f:status: {}
1357 | f:type: {}
1358 | f:containerStatuses: {}
1359 | f:hostIP: {}
1360 | f:startTime: {}
1361 | manager: kubelet
1362 | operation: Update
1363 | time: "2021-05-19T09:43:06Z"
1364 | name: px-5d567cc74c-pmvzr
1365 | namespace: default
1366 | ownerReferences:
1367 | - apiVersion: apps/v1
1368 | blockOwnerDeletion: true
1369 | controller: true
1370 | kind: ReplicaSet
1371 | name: px-5d567cc74c
1372 | uid: 0d562fec-2485-4f88-b09e-596783c0d0a2
1373 | resourceVersion: "5361695"
1374 | selfLink: /api/v1/namespaces/default/pods/px-5d567cc74c-pmvzr
1375 | uid: 57a7a797-6d5b-45cd-8f54-8fa73f62316f
1376 | spec:
1377 | containers:
1378 | - image: ghcr.io/stefanprodan/podinfo:5.0.0
1379 | imagePullPolicy: IfNotPresent
1380 | name: podinfo
1381 | resources: {}
1382 | terminationMessagePath: /dev/termination-log
1383 | terminationMessagePolicy: File
1384 | volumeMounts:
1385 | - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
1386 | name: default-token-kx75m
1387 | readOnly: true
1388 | dnsPolicy: ClusterFirst
1389 | enableServiceLinks: true
1390 | nodeName: kind-control-plane
1391 | preemptionPolicy: PreemptLowerPriority
1392 | priority: 0
1393 | restartPolicy: Always
1394 | schedulerName: default-scheduler
1395 | securityContext: {}
1396 | serviceAccount: default
1397 | serviceAccountName: default
1398 | terminationGracePeriodSeconds: 30
1399 | tolerations:
1400 | - effect: NoExecute
1401 | key: node.kubernetes.io/not-ready
1402 | operator: Exists
1403 | tolerationSeconds: 300
1404 | - effect: NoExecute
1405 | key: node.kubernetes.io/unreachable
1406 | operator: Exists
1407 | tolerationSeconds: 300
1408 | volumes:
1409 | - name: default-token-kx75m
1410 | secret:
1411 | defaultMode: 420
1412 | secretName: default-token-kx75m
1413 | status:
1414 | conditions:
1415 | - lastProbeTime: null
1416 | lastTransitionTime: "2021-05-19T09:43:06Z"
1417 | status: "True"
1418 | type: Initialized
1419 | - lastProbeTime: null
1420 | lastTransitionTime: "2021-05-19T09:43:06Z"
1421 | message: 'containers with unready status: [podinfo]'
1422 | reason: ContainersNotReady
1423 | status: "False"
1424 | type: Ready
1425 | - lastProbeTime: null
1426 | lastTransitionTime: "2021-05-19T09:43:06Z"
1427 | message: 'containers with unready status: [podinfo]'
1428 | reason: ContainersNotReady
1429 | status: "False"
1430 | type: ContainersReady
1431 | - lastProbeTime: null
1432 | lastTransitionTime: "2021-05-19T09:43:06Z"
1433 | status: "True"
1434 | type: PodScheduled
1435 | containerStatuses:
1436 | - image: ghcr.io/stefanprodan/podinfo:5.0.0
1437 | imageID: ""
1438 | lastState: {}
1439 | name: podinfo
1440 | ready: false
1441 | restartCount: 0
1442 | started: false
1443 | state:
1444 | waiting:
1445 | reason: ContainerCreating
1446 | hostIP: 172.18.0.2
1447 | phase: Pending
1448 | qosClass: BestEffort
1449 | startTime: "2021-05-19T09:43:06Z"
1450 | ---
1451 | # {"time":"2021-05-19T09:43:06.257797688Z","style":"event","kind":"Event"}
1452 | action: Binding
1453 | apiVersion: v1
1454 | eventTime: "2021-05-19T09:43:06.232051Z"
1455 | firstTimestamp: null
1456 | involvedObject:
1457 | apiVersion: v1
1458 | kind: Pod
1459 | name: px-5d567cc74c-pmvzr
1460 | namespace: default
1461 | resourceVersion: "5361686"
1462 | uid: 57a7a797-6d5b-45cd-8f54-8fa73f62316f
1463 | kind: Event
1464 | lastTimestamp: null
1465 | message: Successfully assigned default/px-5d567cc74c-pmvzr to kind-control-plane
1466 | metadata:
1467 | creationTimestamp: "2021-05-19T09:43:06Z"
1468 | managedFields:
1469 | - apiVersion: events.k8s.io/v1
1470 | fieldsType: FieldsV1
1471 | fieldsV1:
1472 | f:action: {}
1473 | f:eventTime: {}
1474 | f:note: {}
1475 | f:reason: {}
1476 | f:regarding:
1477 | f:apiVersion: {}
1478 | f:kind: {}
1479 | f:name: {}
1480 | f:namespace: {}
1481 | f:resourceVersion: {}
1482 | f:uid: {}
1483 | f:reportingController: {}
1484 | f:reportingInstance: {}
1485 | f:type: {}
1486 | manager: kube-scheduler
1487 | operation: Update
1488 | time: "2021-05-19T09:43:06Z"
1489 | name: px-5d567cc74c-pmvzr.16806e85ab010be0
1490 | namespace: default
1491 | resourceVersion: "5361693"
1492 | selfLink: /api/v1/namespaces/default/events/px-5d567cc74c-pmvzr.16806e85ab010be0
1493 | uid: 2e881a1f-b194-41c1-a19e-feba8d816ffd
1494 | reason: Scheduled
1495 | reportingComponent: default-scheduler
1496 | reportingInstance: default-scheduler-kind-control-plane
1497 | source: {}
1498 | type: Normal
1499 | ---
1500 | # {"time":"2021-05-19T09:43:06.698881964Z","style":"event","kind":"Event"}
1501 | apiVersion: v1
1502 | count: 1
1503 | eventTime: null
1504 | firstTimestamp: "2021-05-19T09:43:06Z"
1505 | involvedObject:
1506 | apiVersion: v1
1507 | fieldPath: spec.containers{podinfo}
1508 | kind: Pod
1509 | name: px-5d567cc74c-pmvzr
1510 | namespace: default
1511 | resourceVersion: "5361691"
1512 | uid: 57a7a797-6d5b-45cd-8f54-8fa73f62316f
1513 | kind: Event
1514 | lastTimestamp: "2021-05-19T09:43:06Z"
1515 | message: Container image "ghcr.io/stefanprodan/podinfo:5.0.0" already present on machine
1516 | metadata:
1517 | creationTimestamp: "2021-05-19T09:43:06Z"
1518 | managedFields:
1519 | - apiVersion: v1
1520 | fieldsType: FieldsV1
1521 | fieldsV1:
1522 | f:count: {}
1523 | f:firstTimestamp: {}
1524 | f:involvedObject:
1525 | f:apiVersion: {}
1526 | f:fieldPath: {}
1527 | f:kind: {}
1528 | f:name: {}
1529 | f:namespace: {}
1530 | f:resourceVersion: {}
1531 | f:uid: {}
1532 | f:lastTimestamp: {}
1533 | f:message: {}
1534 | f:reason: {}
1535 | f:source:
1536 | f:component: {}
1537 | f:host: {}
1538 | f:type: {}
1539 | manager: kubelet
1540 | operation: Update
1541 | time: "2021-05-19T09:43:06Z"
1542 | name: px-5d567cc74c-pmvzr.16806e85c6a00dfe
1543 | namespace: default
1544 | resourceVersion: "5361698"
1545 | selfLink: /api/v1/namespaces/default/events/px-5d567cc74c-pmvzr.16806e85c6a00dfe
1546 | uid: a2f78777-39fc-4059-a4c7-52231ae1ba93
1547 | reason: Pulled
1548 | reportingComponent: ""
1549 | reportingInstance: ""
1550 | source:
1551 | component: kubelet
1552 | host: kind-control-plane
1553 | type: Normal
1554 | ---
1555 | # {"time":"2021-05-19T09:43:06.73097871Z","style":"event","kind":"Event"}
1556 | apiVersion: v1
1557 | count: 1
1558 | eventTime: null
1559 | firstTimestamp: "2021-05-19T09:43:06Z"
1560 | involvedObject:
1561 | apiVersion: v1
1562 | fieldPath: spec.containers{podinfo}
1563 | kind: Pod
1564 | name: px-5d567cc74c-pmvzr
1565 | namespace: default
1566 | resourceVersion: "5361691"
1567 | uid: 57a7a797-6d5b-45cd-8f54-8fa73f62316f
1568 | kind: Event
1569 | lastTimestamp: "2021-05-19T09:43:06Z"
1570 | message: Created container podinfo
1571 | metadata:
1572 | creationTimestamp: "2021-05-19T09:43:06Z"
1573 | managedFields:
1574 | - apiVersion: v1
1575 | fieldsType: FieldsV1
1576 | fieldsV1:
1577 | f:count: {}
1578 | f:firstTimestamp: {}
1579 | f:involvedObject:
1580 | f:apiVersion: {}
1581 | f:fieldPath: {}
1582 | f:kind: {}
1583 | f:name: {}
1584 | f:namespace: {}
1585 | f:resourceVersion: {}
1586 | f:uid: {}
1587 | f:lastTimestamp: {}
1588 | f:message: {}
1589 | f:reason: {}
1590 | f:source:
1591 | f:component: {}
1592 | f:host: {}
1593 | f:type: {}
1594 | manager: kubelet
1595 | operation: Update
1596 | time: "2021-05-19T09:43:06Z"
1597 | name: px-5d567cc74c-pmvzr.16806e85c88e64f1
1598 | namespace: default
1599 | resourceVersion: "5361699"
1600 | selfLink: /api/v1/namespaces/default/events/px-5d567cc74c-pmvzr.16806e85c88e64f1
1601 | uid: c200779e-2483-470a-8e6c-d9f99012da04
1602 | reason: Created
1603 | reportingComponent: ""
1604 | reportingInstance: ""
1605 | source:
1606 | component: kubelet
1607 | host: kind-control-plane
1608 | type: Normal
1609 | ---
1610 | # {"time":"2021-05-19T09:43:06.808479495Z","style":"event","kind":"Event"}
1611 | apiVersion: v1
1612 | count: 1
1613 | eventTime: null
1614 | firstTimestamp: "2021-05-19T09:43:06Z"
1615 | involvedObject:
1616 | apiVersion: v1
1617 | fieldPath: spec.containers{podinfo}
1618 | kind: Pod
1619 | name: px-5d567cc74c-pmvzr
1620 | namespace: default
1621 | resourceVersion: "5361691"
1622 | uid: 57a7a797-6d5b-45cd-8f54-8fa73f62316f
1623 | kind: Event
1624 | lastTimestamp: "2021-05-19T09:43:06Z"
1625 | message: Started container podinfo
1626 | metadata:
1627 | creationTimestamp: "2021-05-19T09:43:06Z"
1628 | managedFields:
1629 | - apiVersion: v1
1630 | fieldsType: FieldsV1
1631 | fieldsV1:
1632 | f:count: {}
1633 | f:firstTimestamp: {}
1634 | f:involvedObject:
1635 | f:apiVersion: {}
1636 | f:fieldPath: {}
1637 | f:kind: {}
1638 | f:name: {}
1639 | f:namespace: {}
1640 | f:resourceVersion: {}
1641 | f:uid: {}
1642 | f:lastTimestamp: {}
1643 | f:message: {}
1644 | f:reason: {}
1645 | f:source:
1646 | f:component: {}
1647 | f:host: {}
1648 | f:type: {}
1649 | manager: kubelet
1650 | operation: Update
1651 | time: "2021-05-19T09:43:06Z"
1652 | name: px-5d567cc74c-pmvzr.16806e85cd2f0f0a
1653 | namespace: default
1654 | resourceVersion: "5361700"
1655 | selfLink: /api/v1/namespaces/default/events/px-5d567cc74c-pmvzr.16806e85cd2f0f0a
1656 | uid: e88edc6d-666b-4f4c-a332-0deb1b264c82
1657 | reason: Started
1658 | reportingComponent: ""
1659 | reportingInstance: ""
1660 | source:
1661 | component: kubelet
1662 | host: kind-control-plane
1663 | type: Normal
1664 | ---
1665 | # {"time":"2021-05-19T09:43:07.176274411Z","style":"event","kind":"Event"}
1666 | apiVersion: v1
1667 | count: 1
1668 | eventTime: null
1669 | firstTimestamp: "2021-05-19T09:43:07Z"
1670 | involvedObject:
1671 | apiVersion: apps/v1
1672 | kind: Deployment
1673 | name: px
1674 | namespace: default
1675 | resourceVersion: "5361696"
1676 | uid: 71c42017-61a7-418e-8e2c-897cf237d6e3
1677 | kind: Event
1678 | lastTimestamp: "2021-05-19T09:43:07Z"
1679 | message: Scaled down replica set px-7df978b9bf to 0
1680 | metadata:
1681 | creationTimestamp: "2021-05-19T09:43:07Z"
1682 | managedFields:
1683 | - apiVersion: v1
1684 | fieldsType: FieldsV1
1685 | fieldsV1:
1686 | f:count: {}
1687 | f:firstTimestamp: {}
1688 | f:involvedObject:
1689 | f:apiVersion: {}
1690 | f:kind: {}
1691 | f:name: {}
1692 | f:namespace: {}
1693 | f:resourceVersion: {}
1694 | f:uid: {}
1695 | f:lastTimestamp: {}
1696 | f:message: {}
1697 | f:reason: {}
1698 | f:source:
1699 | f:component: {}
1700 | f:type: {}
1701 | manager: kube-controller-manager
1702 | operation: Update
1703 | time: "2021-05-19T09:43:07Z"
1704 | name: px.16806e85e3233164
1705 | namespace: default
1706 | resourceVersion: "5361705"
1707 | selfLink: /api/v1/namespaces/default/events/px.16806e85e3233164
1708 | uid: 396a77cf-b74a-4f8e-98d5-41a0756b6b31
1709 | reason: ScalingReplicaSet
1710 | reportingComponent: ""
1711 | reportingInstance: ""
1712 | source:
1713 | component: deployment-controller
1714 | type: Normal
1715 | ---
1716 | # {"time":"2021-05-19T09:43:07.190240066Z","style":"event","kind":"Event"}
1717 | apiVersion: v1
1718 | count: 1
1719 | eventTime: null
1720 | firstTimestamp: "2021-05-19T09:43:07Z"
1721 | involvedObject:
1722 | apiVersion: apps/v1
1723 | kind: ReplicaSet
1724 | name: px-7df978b9bf
1725 | namespace: default
1726 | resourceVersion: "5361704"
1727 | uid: e8a3abe0-ab69-48f7-9d18-6c0d6c562aab
1728 | kind: Event
1729 | lastTimestamp: "2021-05-19T09:43:07Z"
1730 | message: 'Deleted pod: px-7df978b9bf-bfdrj'
1731 | metadata:
1732 | creationTimestamp: "2021-05-19T09:43:07Z"
1733 | managedFields:
1734 | - apiVersion: v1
1735 | fieldsType: FieldsV1
1736 | fieldsV1:
1737 | f:count: {}
1738 | f:firstTimestamp: {}
1739 | f:involvedObject:
1740 | f:apiVersion: {}
1741 | f:kind: {}
1742 | f:name: {}
1743 | f:namespace: {}
1744 | f:resourceVersion: {}
1745 | f:uid: {}
1746 | f:lastTimestamp: {}
1747 | f:message: {}
1748 | f:reason: {}
1749 | f:source:
1750 | f:component: {}
1751 | f:type: {}
1752 | manager: kube-controller-manager
1753 | operation: Update
1754 | time: "2021-05-19T09:43:07Z"
1755 | name: px-7df978b9bf.16806e85e3cf5f3c
1756 | namespace: default
1757 | resourceVersion: "5361709"
1758 | selfLink: /api/v1/namespaces/default/events/px-7df978b9bf.16806e85e3cf5f3c
1759 | uid: 7ffcfc99-a056-46a0-a3c0-78dd8aa49172
1760 | reason: SuccessfulDelete
1761 | reportingComponent: ""
1762 | reportingInstance: ""
1763 | source:
1764 | component: replicaset-controller
1765 | type: Normal
1766 | ---
1767 | # {"time":"2021-05-19T09:43:07.199181099Z","style":"initial","kind":"Pod"}
1768 | apiVersion: v1
1769 | kind: Pod
1770 | metadata:
1771 | creationTimestamp: "2021-05-18T17:10:49Z"
1772 | deletionGracePeriodSeconds: 30
1773 | deletionTimestamp: "2021-05-19T09:43:37Z"
1774 | generateName: px-7df978b9bf-
1775 | labels:
1776 | app: podinfo
1777 | pod-template-hash: 7df978b9bf
1778 | managedFields:
1779 | - apiVersion: v1
1780 | fieldsType: FieldsV1
1781 | fieldsV1:
1782 | f:metadata:
1783 | f:generateName: {}
1784 | f:labels:
1785 | .: {}
1786 | f:app: {}
1787 | f:pod-template-hash: {}
1788 | f:ownerReferences:
1789 | .: {}
1790 | k:{"uid":"e8a3abe0-ab69-48f7-9d18-6c0d6c562aab"}:
1791 | .: {}
1792 | f:apiVersion: {}
1793 | f:blockOwnerDeletion: {}
1794 | f:controller: {}
1795 | f:kind: {}
1796 | f:name: {}
1797 | f:uid: {}
1798 | f:spec:
1799 | f:containers:
1800 | k:{"name":"podinfo"}:
1801 | .: {}
1802 | f:image: {}
1803 | f:imagePullPolicy: {}
1804 | f:name: {}
1805 | f:resources: {}
1806 | f:terminationMessagePath: {}
1807 | f:terminationMessagePolicy: {}
1808 | f:dnsPolicy: {}
1809 | f:enableServiceLinks: {}
1810 | f:restartPolicy: {}
1811 | f:schedulerName: {}
1812 | f:securityContext: {}
1813 | f:terminationGracePeriodSeconds: {}
1814 | manager: kube-controller-manager
1815 | operation: Update
1816 | time: "2021-05-18T17:10:49Z"
1817 | - apiVersion: v1
1818 | fieldsType: FieldsV1
1819 | fieldsV1:
1820 | f:status:
1821 | f:conditions:
1822 | k:{"type":"ContainersReady"}:
1823 | .: {}
1824 | f:lastProbeTime: {}
1825 | f:lastTransitionTime: {}
1826 | f:status: {}
1827 | f:type: {}
1828 | k:{"type":"Initialized"}:
1829 | .: {}
1830 | f:lastProbeTime: {}
1831 | f:lastTransitionTime: {}
1832 | f:status: {}
1833 | f:type: {}
1834 | k:{"type":"Ready"}:
1835 | .: {}
1836 | f:lastProbeTime: {}
1837 | f:lastTransitionTime: {}
1838 | f:status: {}
1839 | f:type: {}
1840 | f:containerStatuses: {}
1841 | f:hostIP: {}
1842 | f:phase: {}
1843 | f:podIP: {}
1844 | f:podIPs:
1845 | .: {}
1846 | k:{"ip":"10.244.0.7"}:
1847 | .: {}
1848 | f:ip: {}
1849 | f:startTime: {}
1850 | manager: kubelet
1851 | operation: Update
1852 | time: "2021-05-18T17:10:50Z"
1853 | name: px-7df978b9bf-bfdrj
1854 | namespace: default
1855 | ownerReferences:
1856 | - apiVersion: apps/v1
1857 | blockOwnerDeletion: true
1858 | controller: true
1859 | kind: ReplicaSet
1860 | name: px-7df978b9bf
1861 | uid: e8a3abe0-ab69-48f7-9d18-6c0d6c562aab
1862 | resourceVersion: "5361707"
1863 | selfLink: /api/v1/namespaces/default/pods/px-7df978b9bf-bfdrj
1864 | uid: 50418ee7-d53b-4c03-93ac-444b94fb98d2
1865 | spec:
1866 | containers:
1867 | - image: ghcr.io/stefanprodan/podinfo:5.1.3
1868 | imagePullPolicy: IfNotPresent
1869 | name: podinfo
1870 | resources: {}
1871 | terminationMessagePath: /dev/termination-log
1872 | terminationMessagePolicy: File
1873 | volumeMounts:
1874 | - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
1875 | name: default-token-kx75m
1876 | readOnly: true
1877 | dnsPolicy: ClusterFirst
1878 | enableServiceLinks: true
1879 | nodeName: kind-control-plane
1880 | preemptionPolicy: PreemptLowerPriority
1881 | priority: 0
1882 | restartPolicy: Always
1883 | schedulerName: default-scheduler
1884 | securityContext: {}
1885 | serviceAccount: default
1886 | serviceAccountName: default
1887 | terminationGracePeriodSeconds: 30
1888 | tolerations:
1889 | - effect: NoExecute
1890 | key: node.kubernetes.io/not-ready
1891 | operator: Exists
1892 | tolerationSeconds: 300
1893 | - effect: NoExecute
1894 | key: node.kubernetes.io/unreachable
1895 | operator: Exists
1896 | tolerationSeconds: 300
1897 | volumes:
1898 | - name: default-token-kx75m
1899 | secret:
1900 | defaultMode: 420
1901 | secretName: default-token-kx75m
1902 | status:
1903 | conditions:
1904 | - lastProbeTime: null
1905 | lastTransitionTime: "2021-05-18T17:10:49Z"
1906 | status: "True"
1907 | type: Initialized
1908 | - lastProbeTime: null
1909 | lastTransitionTime: "2021-05-18T17:10:50Z"
1910 | status: "True"
1911 | type: Ready
1912 | - lastProbeTime: null
1913 | lastTransitionTime: "2021-05-18T17:10:50Z"
1914 | status: "True"
1915 | type: ContainersReady
1916 | - lastProbeTime: null
1917 | lastTransitionTime: "2021-05-18T17:10:49Z"
1918 | status: "True"
1919 | type: PodScheduled
1920 | containerStatuses:
1921 | - containerID: containerd://cf553c322fd2165e0bd0ab36bc970c7c5786aeafbd8e2c28807d9d93aff1ed54
1922 | image: ghcr.io/stefanprodan/podinfo:5.1.3
1923 | imageID: ghcr.io/stefanprodan/podinfo@sha256:1cf7ad3ac4b7fc05773ec5deebbdf325566ee04483390688e09b53163ebb5bfd
1924 | lastState: {}
1925 | name: podinfo
1926 | ready: true
1927 | restartCount: 0
1928 | started: true
1929 | state:
1930 | running:
1931 | startedAt: "2021-05-18T17:10:49Z"
1932 | hostIP: 172.18.0.2
1933 | phase: Running
1934 | podIP: 10.244.0.7
1935 | podIPs:
1936 | - ip: 10.244.0.7
1937 | qosClass: BestEffort
1938 | startTime: "2021-05-18T17:10:49Z"
1939 | ---
1940 | # {"time":"2021-05-19T09:43:07.204574676Z","style":"event","kind":"Event"}
1941 | apiVersion: v1
1942 | count: 1
1943 | eventTime: null
1944 | firstTimestamp: "2021-05-19T09:43:07Z"
1945 | involvedObject:
1946 | apiVersion: v1
1947 | fieldPath: spec.containers{podinfo}
1948 | kind: Pod
1949 | name: px-7df978b9bf-bfdrj
1950 | namespace: default
1951 | resourceVersion: "5201896"
1952 | uid: 50418ee7-d53b-4c03-93ac-444b94fb98d2
1953 | kind: Event
1954 | lastTimestamp: "2021-05-19T09:43:07Z"
1955 | message: Stopping container podinfo
1956 | metadata:
1957 | creationTimestamp: "2021-05-19T09:43:07Z"
1958 | managedFields:
1959 | - apiVersion: v1
1960 | fieldsType: FieldsV1
1961 | fieldsV1:
1962 | f:count: {}
1963 | f:firstTimestamp: {}
1964 | f:involvedObject:
1965 | f:apiVersion: {}
1966 | f:fieldPath: {}
1967 | f:kind: {}
1968 | f:name: {}
1969 | f:namespace: {}
1970 | f:resourceVersion: {}
1971 | f:uid: {}
1972 | f:lastTimestamp: {}
1973 | f:message: {}
1974 | f:reason: {}
1975 | f:source:
1976 | f:component: {}
1977 | f:host: {}
1978 | f:type: {}
1979 | manager: kubelet
1980 | operation: Update
1981 | time: "2021-05-19T09:43:07Z"
1982 | name: px-7df978b9bf-bfdrj.16806e85e3b71865
1983 | namespace: default
1984 | resourceVersion: "5361710"
1985 | selfLink: /api/v1/namespaces/default/events/px-7df978b9bf-bfdrj.16806e85e3b71865
1986 | uid: da54dfde-35b5-4e29-817b-fdbfa03302b5
1987 | reason: Killing
1988 | reportingComponent: ""
1989 | reportingInstance: ""
1990 | source:
1991 | component: kubelet
1992 | host: kind-control-plane
1993 | type: Normal
1994 |
--------------------------------------------------------------------------------