Skip to content

Commit 855e529

Browse files
committed
Support watch for get
Signed-off-by: Shiming Zhang <[email protected]>
1 parent d345af1 commit 855e529

File tree

5 files changed

+123
-6
lines changed

5 files changed

+123
-6
lines changed

augerctl/README.md

+8
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ augerctl get leases -n kube-system
8686
kubectl get leases -n kube-system -o yaml
8787
```
8888

89+
Watch all leases with namespace `kube-system`
90+
91+
``` bash
92+
augerctl get leases -n kube-system -w
93+
# Nearly equivalent
94+
kubectl get leases -n kube-system -w -o yaml
95+
```
96+
8997
List a single resource of type `apiservices.apiregistration.k8s.io` and name `v1.apps`
9098

9199
``` bash

augerctl/command/get_command.go

+33-6
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ type getFlagpole struct {
3131
Output string
3232
ChunkSize int64
3333
Prefix string
34+
35+
Watch bool
36+
WatchOnly bool
3437
}
3538

3639
var (
@@ -50,6 +53,11 @@ var (
5053
# Nearly equivalent
5154
kubectl get leases -n kube-system -o yaml
5255
56+
# Watch all leases with namespace "kube-system"
57+
augerctl get leases -n kube-system -w
58+
# Nearly equivalent
59+
kubectl get leases -n kube-system -w -o yaml
60+
5361
# List a single resource of type "apiservices.apiregistration.k8s.io" and name "v1.apps"
5462
augerctl get apiservices.apiregistration.k8s.io v1.apps
5563
# Nearly equivalent
@@ -89,6 +97,8 @@ func newCtlGetCommand(f *flagpole) *cobra.Command {
8997
cmd.Flags().Int64Var(&flags.ChunkSize, "chunk-size", 500, "chunk size of the list pager")
9098
cmd.Flags().StringVar(&flags.Prefix, "prefix", "/registry", "prefix to prepend to the resource")
9199

100+
cmd.Flags().BoolVarP(&flags.Watch, "watch", "w", false, "after listing/getting the requested object, watch for changes")
101+
cmd.Flags().BoolVar(&flags.WatchOnly, "watch-only", false, "watch for changes to the requested object(s), without listing/getting first")
92102
return cmd
93103
}
94104

@@ -124,13 +134,30 @@ func getCommand(ctx context.Context, etcdclient client.Client, flags *getFlagpol
124134
client.WithResponse(printer.Print),
125135
}
126136

127-
// TODO: Support watch
137+
if flags.Watch {
138+
if !flags.WatchOnly {
139+
rev, err := etcdclient.Get(ctx, flags.Prefix,
140+
opOpts...,
141+
)
142+
if err != nil {
143+
return err
144+
}
145+
opOpts = append(opOpts, client.WithRevision(rev))
146+
}
128147

129-
_, err := etcdclient.Get(ctx, flags.Prefix,
130-
opOpts...,
131-
)
132-
if err != nil {
133-
return err
148+
err := etcdclient.Watch(ctx, flags.Prefix,
149+
opOpts...,
150+
)
151+
if err != nil {
152+
return err
153+
}
154+
} else {
155+
_, err := etcdclient.Get(ctx, flags.Prefix,
156+
opOpts...,
157+
)
158+
if err != nil {
159+
return err
160+
}
134161
}
135162

136163
return nil

augerctl/command/printer.go

+3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ func NewPrinter(w io.Writer, printerType string) Printer {
4040

4141
func formatResponse(w io.Writer, outMediaType string, kv *client.KeyValue) error {
4242
value := kv.Value
43+
if kv.PrevValue != nil {
44+
value = kv.PrevValue
45+
}
4346
inMediaType, _, err := encoding.DetectAndExtract(value)
4447
if err != nil {
4548
_, err0 := fmt.Fprintf(w, "---\n# %s | raw | %v\n# %s\n", kv.Key, err, value)

pkg/client/client.go

+13
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ type Client interface {
3131
// Get is a method that retrieves a key-value pair from the etcd server.
3232
// It returns the revision of the key-value pair
3333
Get(ctx context.Context, prefix string, opOpts ...OpOption) (rev int64, err error)
34+
35+
// Watch is a method that watches for changes to a key-value pair on the etcd server.
36+
Watch(ctx context.Context, prefix string, opOpts ...OpOption) error
3437
}
3538

3639
// client is the etcd client.
@@ -89,6 +92,13 @@ func WithChunkSize(chunkSize int64) OpOption {
8992
}
9093
}
9194

95+
// WithRevision sets the revision for the target.
96+
func WithRevision(revision int64) OpOption {
97+
return func(o *op) {
98+
o.revision = revision
99+
}
100+
}
101+
92102
func opOption(opts []OpOption) op {
93103
var opt op
94104
for _, o := range opts {
@@ -101,6 +111,9 @@ func opOption(opts []OpOption) op {
101111
type KeyValue struct {
102112
Key []byte
103113
Value []byte
114+
115+
// For delete event
116+
PrevValue []byte
104117
}
105118

106119
func iterateList(kvs []*mvccpb.KeyValue, callback func(kv *KeyValue) error) error {

pkg/client/client_watch.go

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package client
18+
19+
import (
20+
"context"
21+
"fmt"
22+
23+
clientv3 "go.etcd.io/etcd/client/v3"
24+
)
25+
26+
func (c *client) Watch(ctx context.Context, prefix string, opOpts ...OpOption) error {
27+
opt := opOption(opOpts)
28+
if opt.response == nil {
29+
return fmt.Errorf("response is required")
30+
}
31+
32+
path, single, err := getPrefix(prefix, opt.gr, opt.name, opt.namespace)
33+
if err != nil {
34+
return err
35+
}
36+
37+
opts := make([]clientv3.OpOption, 0, 3)
38+
39+
if !single {
40+
opts = append(opts, clientv3.WithPrefix())
41+
}
42+
43+
if opt.revision != 0 {
44+
opts = append(opts, clientv3.WithRev(opt.revision))
45+
}
46+
47+
opts = append(opts, clientv3.WithPrevKV())
48+
49+
watchChan := c.client.Watch(ctx, path, opts...)
50+
for watchResp := range watchChan {
51+
for _, event := range watchResp.Events {
52+
r := &KeyValue{
53+
Key: event.Kv.Key,
54+
Value: event.Kv.Value,
55+
}
56+
if event.PrevKv != nil {
57+
r.PrevValue = event.PrevKv.Value
58+
}
59+
err := opt.response(r)
60+
if err != nil {
61+
return err
62+
}
63+
}
64+
}
65+
return nil
66+
}

0 commit comments

Comments
 (0)