Skip to content

Commit cdd73ec

Browse files
authored
Merge pull request #268 from ninech/watch-fix-header
fix: initial watch list formatting
2 parents b8a1ee7 + d5d2026 commit cdd73ec

File tree

3 files changed

+51
-17
lines changed

3 files changed

+51
-17
lines changed

api/list.go

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -99,18 +99,7 @@ func (c *Client) ListObjects(ctx context.Context, list runtimeclient.ObjectList,
9999
return nil
100100
}
101101

102-
// we now need a bit of reflection code from the apimachinery package
103-
// as the ObjectList interface provides no way to get or set the list
104-
// items directly.
105-
106-
// we need to get a pointer to the items field of the list and turn it
107-
// into a reflect value so that we can change the items in case we want
108-
// to search in all projects.
109-
itemsPtr, err := meta.GetItemsPtr(list)
110-
if err != nil {
111-
return err
112-
}
113-
items, err := conversion.EnforcePtr(itemsPtr)
102+
items, err := itemsFromObjectList(list)
114103
if err != nil {
115104
return err
116105
}
@@ -261,3 +250,16 @@ func (c *Client) Projects(ctx context.Context, onlyName string) ([]management.Pr
261250
}
262251
return projectList.Items, nil
263252
}
253+
254+
func itemsFromObjectList(list runtimeclient.ObjectList) (reflect.Value, error) {
255+
// we need a bit of reflection code from the apimachinery package as the
256+
// ObjectList interface provides no way to get or set the list items
257+
// directly. We need to get a pointer to the items field of the list and
258+
// turn it into a reflect value so that we can change the items in case we
259+
// want to search in all projects.
260+
itemsPtr, err := meta.GetItemsPtr(list)
261+
if err != nil {
262+
return reflect.Value{}, err
263+
}
264+
return conversion.EnforcePtr(itemsPtr)
265+
}

api/watch.go

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66

77
"k8s.io/apimachinery/pkg/api/meta"
8+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
89
"k8s.io/apimachinery/pkg/runtime"
910
"k8s.io/apimachinery/pkg/watch"
1011
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
@@ -17,23 +18,52 @@ type WatchFunc func(list runtimeclient.ObjectList) error
1718
// All namespaces is easy to solve but all projects would mean a goroutine/watch
1819
// per project.
1920
func (c *Client) watch(ctx context.Context, list runtimeclient.ObjectList, options ...ListOpt) error {
20-
// this is a bit awkward: we need to extract some functional options. We do
21-
// this by executing each opt and checking the result.
21+
// this is a bit awkward: we need to extract some functional options and
22+
// make sure we don't pass the watch option down to c.ListObjects as that
23+
// will cause infinite recursion. We do this by executing each opt and
24+
// checking the result.
25+
newOptions := []ListOpt{}
2226
var watchFunc WatchFunc
2327
var clientListOptions []runtimeclient.ListOption
2428
for _, opt := range options {
2529
opts := &ListOpts{}
2630
opt(opts)
31+
if !opts.watch {
32+
newOptions = append(newOptions, opt)
33+
}
2734
if opts.watchFunc != nil {
2835
watchFunc = opts.watchFunc
2936
}
3037
if opts.clientListOptions != nil {
3138
clientListOptions = opts.clientListOptions
3239
}
3340
}
41+
// do an initial list call, this is to get immediate output of the current
42+
// list if it has any items. After that updates will be streamed in by the
43+
// watch. If we would just call watch immediately, the list wouldn't be
44+
// properly formatted as the tab writer and sort logic would not have the
45+
// full list to work with.
46+
if err := c.ListObjects(ctx, list, newOptions...); err != nil {
47+
return err
48+
}
49+
items, err := itemsFromObjectList(list)
50+
if err != nil {
51+
return err
52+
}
53+
if items.Len() > 0 {
54+
if err := watchFunc(list); err != nil {
55+
return err
56+
}
57+
}
3458

3559
wa, err := c.Watch(ctx, list, append(clientListOptions, &runtimeclient.ListOptions{
3660
Namespace: c.Project,
61+
Raw: &metav1.ListOptions{
62+
// in order to get around the initial list of items, we make a
63+
// previous list call and then set the resource version of the
64+
// returned list.
65+
ResourceVersion: list.GetResourceVersion(),
66+
},
3767
})...)
3868
if err != nil {
3969
return fmt.Errorf("watching resources: %w", err)

get/get_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,17 @@ func TestListPrint(t *testing.T) {
3535
wantLines: 2,
3636
},
3737
"watch": {
38-
out: full,
39-
existingResources: []client.Object{},
38+
out: full,
39+
existingResources: []client.Object{
40+
test.CloudVirtualMachine("foo", test.DefaultProject, "nine-es34", infrastructure.VirtualMachinePowerState("on")),
41+
},
4042
toCreate: []client.Object{
4143
test.CloudVirtualMachine("new", test.DefaultProject, "nine-es34", infrastructure.VirtualMachinePowerState("on")),
4244
test.CloudVirtualMachine("new2", test.DefaultProject, "nine-es34", infrastructure.VirtualMachinePowerState("on")),
4345
test.CloudVirtualMachine("new3", "other-project", "nine-es34", infrastructure.VirtualMachinePowerState("on")),
4446
},
4547
wantContain: []string{"new", "new2"},
46-
wantLines: 3,
48+
wantLines: 4,
4749
watch: true,
4850
},
4951
// TODO: watch currently does not support the all-projects or

0 commit comments

Comments
 (0)