Migrate ServerStatusRequest controller and resource to kubebuilder (#2838)

* Convert ServerStatusRequest controller to controller-runtime

Signed-off-by: Carlisia <carlisia@vmware.com>

* Add select stm

Signed-off-by: Carlisia <carlisia@vmware.com>

* Fixed status patch bug

Signed-off-by: Carlisia <carlisia@vmware.com>

* Add mgr start

Signed-off-by: Carlisia <carlisia@vmware.com>

* Trying to sync

Signed-off-by: Carlisia <carlisia@vmware.com>

* Clean async now

Signed-off-by: Carlisia <carlisia@vmware.com>

* Clean up + move context out

Signed-off-by: Carlisia <carlisia@vmware.com>

* Bug: not closing the channel

Signed-off-by: Carlisia <carlisia@vmware.com>

* Clean up some tests

Signed-off-by: Carlisia <carlisia@vmware.com>

* Much better way to fetch an update using a backoff loop

Signed-off-by: Carlisia <carlisia@vmware.com>

* Even better way to retry: use apimachinery lib

Signed-off-by: Carlisia <carlisia@vmware.com>

* Refactor controller + add test

Signed-off-by: Carlisia <carlisia@vmware.com>

* partially fix unit tests

Signed-off-by: Ashish Amarnath <ashisham@vmware.com>

* Fix and add tests

Signed-off-by: Carlisia <carlisia@vmware.com>

* Add changelog

Signed-off-by: Carlisia <carlisia@vmware.com>

* Add ability to disable the controller + cleanups

Signed-off-by: Carlisia <carlisia@vmware.com>

* Fix bug w/ disabling controllers + fix test + clean up

Signed-off-by: Carlisia <carlisia@vmware.com>

* Move role.yaml to the correct folder

Signed-off-by: Carlisia <carlisia@vmware.com>

* Add sample serverstatusrequest.yaml

Signed-off-by: Carlisia <carlisia@vmware.com>

* Add requeue + better formatting

Signed-off-by: Carlisia <carlisia@vmware.com>

* Increase # of max concurrent reconciles

Signed-off-by: Carlisia <carlisia@vmware.com>

Co-authored-by: Ashish Amarnath <ashisham@vmware.com>
This commit is contained in:
Carlisia Campos
2020-09-01 14:15:23 -07:00
committed by GitHub
co-authored by Ashish Amarnath
parent aed504a0fd
commit c952932f1b
19 changed files with 760 additions and 383 deletions
+12 -12
View File
@@ -17,6 +17,7 @@ limitations under the License.
package plugin
import (
"context"
"fmt"
"os"
"time"
@@ -30,9 +31,7 @@ import (
)
func NewGetCommand(f client.Factory, use string) *cobra.Command {
serverStatusGetter := &serverstatus.DefaultServerStatusGetter{
Timeout: 5 * time.Second,
}
timeout := 5 * time.Second
c := &cobra.Command{
Use: use,
@@ -41,17 +40,18 @@ func NewGetCommand(f client.Factory, use string) *cobra.Command {
err := output.ValidateFlags(c)
cmd.CheckError(err)
serverStatusGetter := &serverstatus.DefaultServerStatusGetter{
Namespace: f.Namespace(),
Timeout: 5 * time.Second,
}
client, err := f.Client()
kbClient, err := f.KubebuilderClient()
cmd.CheckError(err)
veleroClient := client.VeleroV1()
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
serverStatus, err := serverStatusGetter.GetServerStatus(veleroClient)
serverStatusGetter := &serverstatus.DefaultServerStatusGetter{
Namespace: f.Namespace(),
Context: ctx,
}
serverStatus, err := serverStatusGetter.GetServerStatus(kbClient)
if err != nil {
fmt.Fprintf(os.Stdout, "<error getting plugin information: %s>\n", err)
return
@@ -62,7 +62,7 @@ func NewGetCommand(f client.Factory, use string) *cobra.Command {
},
}
c.Flags().DurationVar(&serverStatusGetter.Timeout, "timeout", serverStatusGetter.Timeout, "Maximum time to wait for plugin information to be reported.")
c.Flags().DurationVar(&timeout, "timeout", timeout, "maximum time to wait for plugin information to be reported. Default is 5 seconds.")
output.BindFlagsSimple(c.Flags())
return c
+29 -54
View File
@@ -1,5 +1,5 @@
/*
Copyright 2019 the Velero contributors.
Copyright 2020 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -21,78 +21,53 @@ import (
"time"
"github.com/pkg/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/builder"
velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1"
)
type ServerStatusGetter interface {
GetServerStatus(client velerov1client.ServerStatusRequestsGetter) (*velerov1api.ServerStatusRequest, error)
GetServerStatus(kbClient kbclient.Client) (*velerov1api.ServerStatusRequest, error)
}
type DefaultServerStatusGetter struct {
Namespace string
Timeout time.Duration
Context context.Context
}
func (g *DefaultServerStatusGetter) GetServerStatus(client velerov1client.ServerStatusRequestsGetter) (*velerov1api.ServerStatusRequest, error) {
req := builder.ForServerStatusRequest(g.Namespace, "").
ObjectMeta(
builder.WithGenerateName("velero-cli-"),
).Result()
func (g *DefaultServerStatusGetter) GetServerStatus(kbClient kbclient.Client) (*velerov1api.ServerStatusRequest, error) {
created := builder.ForServerStatusRequest(g.Namespace, "", "0").ObjectMeta(builder.WithGenerateName("velero-cli-")).Result()
created, err := client.ServerStatusRequests(g.Namespace).Create(context.TODO(), req, metav1.CreateOptions{})
if err != nil {
if err := kbClient.Create(context.Background(), created, &kbclient.CreateOptions{}); err != nil {
return nil, errors.WithStack(err)
}
defer client.ServerStatusRequests(g.Namespace).Delete(context.TODO(), created.Name, metav1.DeleteOptions{})
listOptions := metav1.ListOptions{
// TODO: once the minimum supported Kubernetes version is v1.9.0, uncomment the following line.
ctx, cancel := context.WithCancel(g.Context)
defer cancel()
key := client.ObjectKey{Name: created.Name, Namespace: g.Namespace}
checkFunc := func() {
updated := &velerov1api.ServerStatusRequest{}
if err := kbClient.Get(ctx, key, updated); err != nil {
return
}
// TODO: once the minimum supported Kubernetes version is v1.9.0, remove the following check.
// See http://issue.k8s.io/51046 for details.
//FieldSelector: "metadata.name=" + req.Name
ResourceVersion: created.ResourceVersion,
}
watcher, err := client.ServerStatusRequests(g.Namespace).Watch(context.TODO(), listOptions)
if err != nil {
return nil, errors.WithStack(err)
}
defer watcher.Stop()
if updated.Name != created.Name {
return
}
expired := time.NewTimer(g.Timeout)
defer expired.Stop()
Loop:
for {
select {
case <-expired.C:
return nil, errors.New("timed out waiting for server status request to be processed")
case e := <-watcher.ResultChan():
updated, ok := e.Object.(*velerov1api.ServerStatusRequest)
if !ok {
return nil, errors.Errorf("unexpected type %T", e.Object)
}
// TODO: once the minimum supported Kubernetes version is v1.9.0, remove the following check.
// See http://issue.k8s.io/51046 for details.
if updated.Name != created.Name {
continue
}
switch e.Type {
case watch.Deleted:
return nil, errors.New("server status request was unexpectedly deleted")
case watch.Modified:
if updated.Status.Phase == velerov1api.ServerStatusRequestPhaseProcessed {
req = updated
break Loop
}
}
if updated.Status.Phase == velerov1api.ServerStatusRequestPhaseProcessed {
created = updated
cancel()
}
}
return req, nil
wait.Until(checkFunc, 250*time.Millisecond, ctx.Done())
return created, nil
}
+20 -17
View File
@@ -17,51 +17,54 @@ limitations under the License.
package version
import (
"context"
"fmt"
"io"
"os"
"time"
"github.com/spf13/cobra"
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
"github.com/vmware-tanzu/velero/pkg/buildinfo"
"github.com/vmware-tanzu/velero/pkg/client"
"github.com/vmware-tanzu/velero/pkg/cmd"
"github.com/vmware-tanzu/velero/pkg/cmd/cli/serverstatus"
velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1"
)
func NewCommand(f client.Factory) *cobra.Command {
clientOnly := false
serverStatusGetter := &serverstatus.DefaultServerStatusGetter{
Namespace: f.Namespace(),
Timeout: 5 * time.Second,
}
var clientOnly bool
timeout := 5 * time.Second
c := &cobra.Command{
Use: "version",
Short: "Print the velero version and associated image",
Run: func(c *cobra.Command, args []string) {
var veleroClient velerov1client.ServerStatusRequestsGetter
var kbClient kbclient.Client
if !clientOnly {
client, err := f.Client()
var err error
kbClient, err = f.KubebuilderClient()
cmd.CheckError(err)
veleroClient = client.VeleroV1()
}
serverStatusGetter.Namespace = f.Namespace()
printVersion(os.Stdout, clientOnly, veleroClient, serverStatusGetter)
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
serverStatusGetter := &serverstatus.DefaultServerStatusGetter{
Namespace: f.Namespace(),
Context: ctx,
}
printVersion(os.Stdout, clientOnly, kbClient, serverStatusGetter)
},
}
c.Flags().DurationVar(&serverStatusGetter.Timeout, "timeout", serverStatusGetter.Timeout, "Maximum time to wait for server version to be reported")
c.Flags().BoolVar(&clientOnly, "client-only", clientOnly, "Only get velero client version, not server version")
c.Flags().DurationVar(&timeout, "timeout", timeout, "maximum time to wait for server version to be reported. Default is 5 seconds.")
c.Flags().BoolVar(&clientOnly, "client-only", clientOnly, "only get velero client version, not server version")
return c
}
func printVersion(w io.Writer, clientOnly bool, client velerov1client.ServerStatusRequestsGetter, serverStatusGetter serverstatus.ServerStatusGetter) {
func printVersion(w io.Writer, clientOnly bool, kbClient kbclient.Client, serverStatusGetter serverstatus.ServerStatusGetter) {
fmt.Fprintln(w, "Client:")
fmt.Fprintf(w, "\tVersion: %s\n", buildinfo.Version)
fmt.Fprintf(w, "\tGit commit: %s\n", buildinfo.FormattedGitSHA())
@@ -70,7 +73,7 @@ func printVersion(w io.Writer, clientOnly bool, client velerov1client.ServerStat
return
}
serverStatus, err := serverStatusGetter.GetServerStatus(client)
serverStatus, err := serverStatusGetter.GetServerStatus(kbClient)
if err != nil {
fmt.Fprintf(w, "<error getting server version: %s>\n", err)
return
+17 -15
View File
@@ -1,5 +1,5 @@
/*
Copyright 2019 the Velero contributors.
Copyright 2020 the Velero contributors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -24,12 +24,13 @@ import (
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/builder"
"github.com/vmware-tanzu/velero/pkg/buildinfo"
"github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/fake"
v1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1"
"github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme"
)
func TestPrintVersion(t *testing.T) {
@@ -73,7 +74,7 @@ func TestPrintVersion(t *testing.T) {
{
name: "server status getter returns normally",
clientOnly: false,
serverStatusRequest: builder.ForServerStatusRequest("velero", "ssr-1").ServerVersion("v1.0.1").Result(),
serverStatusRequest: builder.ForServerStatusRequest("velero", "ssr-1", "0").ServerVersion("v1.0.1").Result(),
getterError: nil,
want: clientVersion + "Server:\n\tVersion: v1.0.1\n",
},
@@ -82,36 +83,37 @@ func TestPrintVersion(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var (
kbClient = fake.NewFakeClientWithScheme(scheme.Scheme)
serverStatusGetter = new(mockServerStatusGetter)
buf = new(bytes.Buffer)
client = fake.NewSimpleClientset()
)
defer serverStatusGetter.AssertExpectations(t)
// GetServerStatus should only be called when clientOnly = false
if !tc.clientOnly {
serverStatusGetter.On("GetServerStatus", client.VeleroV1()).Return(tc.serverStatusRequest, tc.getterError)
serverStatusGetter.On("GetServerStatus", kbClient).Return(tc.serverStatusRequest, tc.getterError)
}
printVersion(buf, tc.clientOnly, client.VeleroV1(), serverStatusGetter)
printVersion(buf, tc.clientOnly, kbClient, serverStatusGetter)
assert.Equal(t, tc.want, buf.String())
})
}
}
// serverStatusGetter is an autogenerated mock type for the serverStatusGetter type
// mockServerStatusGetter is an autogenerated mock type for the serverStatusGetter type
// Code generated by mockery v2.2.1.
type mockServerStatusGetter struct {
mock.Mock
}
// GetServerStatus provides a mock function with given fields: client
func (_m *mockServerStatusGetter) GetServerStatus(client v1.ServerStatusRequestsGetter) (*velerov1.ServerStatusRequest, error) {
ret := _m.Called(client)
// GetServerStatus provides a mock function with given fields: mgr
func (_m *mockServerStatusGetter) GetServerStatus(kbClient kbclient.Client) (*velerov1.ServerStatusRequest, error) {
ret := _m.Called(kbClient)
var r0 *velerov1.ServerStatusRequest
if rf, ok := ret.Get(0).(func(v1.ServerStatusRequestsGetter) *velerov1.ServerStatusRequest); ok {
r0 = rf(client)
if rf, ok := ret.Get(0).(func(kbclient.Client) *velerov1.ServerStatusRequest); ok {
r0 = rf(kbClient)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*velerov1.ServerStatusRequest)
@@ -119,8 +121,8 @@ func (_m *mockServerStatusGetter) GetServerStatus(client v1.ServerStatusRequests
}
var r1 error
if rf, ok := ret.Get(1).(func(v1.ServerStatusRequestsGetter) error); ok {
r1 = rf(client)
if rf, ok := ret.Get(1).(func(kbclient.Client) error); ok {
r1 = rf(kbClient)
} else {
r1 = ret.Error(1)
}