mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-01-07 13:55:20 +00:00
show server version in ark version output using ServerStatusRequest CRD
Signed-off-by: Steve Kriss <steve@heptio.com>
This commit is contained in:
@@ -62,7 +62,7 @@ operations can also be performed as 'ark backup get' and 'ark schedule create'.`
|
||||
schedule.NewCommand(f),
|
||||
restore.NewCommand(f),
|
||||
server.NewCommand(),
|
||||
version.NewCommand(),
|
||||
version.NewCommand(f),
|
||||
get.NewCommand(f),
|
||||
describe.NewCommand(f),
|
||||
create.NewCommand(f),
|
||||
|
||||
@@ -681,6 +681,17 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
serverStatusRequestController := controller.NewServerStatusRequestController(
|
||||
s.logger,
|
||||
s.arkClient.ArkV1(),
|
||||
s.sharedInformerFactory.Ark().V1().ServerStatusRequests(),
|
||||
)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
serverStatusRequestController.Run(ctx, 1)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
// SHARED INFORMERS HAVE TO BE STARTED AFTER ALL CONTROLLERS
|
||||
go s.sharedInformerFactory.Start(ctx.Done())
|
||||
|
||||
|
||||
@@ -18,21 +18,133 @@ package version
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
|
||||
arkv1api "github.com/heptio/ark/pkg/apis/ark/v1"
|
||||
"github.com/heptio/ark/pkg/buildinfo"
|
||||
"github.com/heptio/ark/pkg/client"
|
||||
"github.com/heptio/ark/pkg/cmd"
|
||||
arkv1client "github.com/heptio/ark/pkg/generated/clientset/versioned/typed/ark/v1"
|
||||
"github.com/heptio/ark/pkg/serverstatusrequest"
|
||||
)
|
||||
|
||||
func NewCommand() *cobra.Command {
|
||||
func NewCommand(f client.Factory) *cobra.Command {
|
||||
clientOnly := false
|
||||
serverStatusGetter := &defaultServerStatusGetter{
|
||||
namespace: f.Namespace(),
|
||||
timeout: 5 * time.Second,
|
||||
}
|
||||
|
||||
c := &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print the ark version and associated image",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("Version: %s\n", buildinfo.Version)
|
||||
fmt.Printf("Git commit: %s\n", buildinfo.FormattedGitSHA())
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
var arkClient arkv1client.ServerStatusRequestsGetter
|
||||
|
||||
if !clientOnly {
|
||||
client, err := f.Client()
|
||||
cmd.CheckError(err)
|
||||
|
||||
arkClient = client.ArkV1()
|
||||
}
|
||||
|
||||
printVersion(os.Stdout, clientOnly, arkClient, 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 ark client version, not server version")
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func printVersion(w io.Writer, clientOnly bool, client arkv1client.ServerStatusRequestsGetter, serverStatusGetter serverStatusGetter) {
|
||||
fmt.Fprintln(w, "Client:")
|
||||
fmt.Fprintf(w, "\tVersion: %s\n", buildinfo.Version)
|
||||
fmt.Fprintf(w, "\tGit commit: %s\n", buildinfo.FormattedGitSHA())
|
||||
|
||||
if clientOnly {
|
||||
return
|
||||
}
|
||||
|
||||
serverStatus, err := serverStatusGetter.getServerStatus(client)
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "<error getting server version: %s>\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintln(w, "Server:")
|
||||
fmt.Fprintf(w, "\tVersion: %s\n", serverStatus.Status.ServerVersion)
|
||||
}
|
||||
|
||||
type serverStatusGetter interface {
|
||||
getServerStatus(client arkv1client.ServerStatusRequestsGetter) (*arkv1api.ServerStatusRequest, error)
|
||||
}
|
||||
|
||||
type defaultServerStatusGetter struct {
|
||||
namespace string
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
func (g *defaultServerStatusGetter) getServerStatus(client arkv1client.ServerStatusRequestsGetter) (*arkv1api.ServerStatusRequest, error) {
|
||||
req := serverstatusrequest.NewBuilder().Namespace(g.namespace).GenerateName("ark-cli-").Build()
|
||||
|
||||
created, err := client.ServerStatusRequests(g.namespace).Create(req)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
defer client.ServerStatusRequests(g.namespace).Delete(created.Name, nil)
|
||||
|
||||
listOptions := metav1.ListOptions{
|
||||
// TODO: once the minimum supported Kubernetes version is v1.9.0, uncomment the following line.
|
||||
// See http://issue.k8s.io/51046 for details.
|
||||
//FieldSelector: "metadata.name=" + req.Name
|
||||
ResourceVersion: created.ResourceVersion,
|
||||
}
|
||||
watcher, err := client.ServerStatusRequests(g.namespace).Watch(listOptions)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
defer watcher.Stop()
|
||||
|
||||
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.(*arkv1api.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 == arkv1api.ServerStatusRequestPhaseProcessed {
|
||||
req = updated
|
||||
break Loop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
129
pkg/cmd/version/version_test.go
Normal file
129
pkg/cmd/version/version_test.go
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
Copyright 2019 the Heptio Ark contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package version
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
arkv1 "github.com/heptio/ark/pkg/apis/ark/v1"
|
||||
"github.com/heptio/ark/pkg/buildinfo"
|
||||
"github.com/heptio/ark/pkg/generated/clientset/versioned/fake"
|
||||
v1 "github.com/heptio/ark/pkg/generated/clientset/versioned/typed/ark/v1"
|
||||
"github.com/heptio/ark/pkg/serverstatusrequest"
|
||||
)
|
||||
|
||||
func TestPrintVersion(t *testing.T) {
|
||||
// set up some non-empty buildinfo values, but put them back to their
|
||||
// defaults at the end of the test
|
||||
var (
|
||||
origVersion = buildinfo.Version
|
||||
origGitSHA = buildinfo.GitSHA
|
||||
origGitTreeState = buildinfo.GitTreeState
|
||||
)
|
||||
defer func() {
|
||||
buildinfo.Version = origVersion
|
||||
buildinfo.GitSHA = origGitSHA
|
||||
buildinfo.GitTreeState = origGitTreeState
|
||||
}()
|
||||
buildinfo.Version = "v1.0.0"
|
||||
buildinfo.GitSHA = "somegitsha"
|
||||
buildinfo.GitTreeState = "dirty"
|
||||
|
||||
clientVersion := fmt.Sprintf("Client:\n\tVersion: %s\n\tGit commit: %s\n", buildinfo.Version, buildinfo.FormattedGitSHA())
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
clientOnly bool
|
||||
serverStatusRequest *arkv1.ServerStatusRequest
|
||||
getterError error
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "client-only",
|
||||
clientOnly: true,
|
||||
want: clientVersion,
|
||||
},
|
||||
{
|
||||
name: "server status getter error",
|
||||
clientOnly: false,
|
||||
serverStatusRequest: nil,
|
||||
getterError: errors.New("an error"),
|
||||
want: clientVersion + "<error getting server version: an error>\n",
|
||||
},
|
||||
{
|
||||
name: "server status getter returns normally",
|
||||
clientOnly: false,
|
||||
serverStatusRequest: serverstatusrequest.NewBuilder().ServerVersion("v1.0.1").Build(),
|
||||
getterError: nil,
|
||||
want: clientVersion + "Server:\n\tVersion: v1.0.1\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var (
|
||||
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.ArkV1()).Return(tc.serverStatusRequest, tc.getterError)
|
||||
}
|
||||
|
||||
printVersion(buf, tc.clientOnly, client.ArkV1(), serverStatusGetter)
|
||||
|
||||
assert.Equal(t, tc.want, buf.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// serverStatusGetter is an autogenerated mock type for the serverStatusGetter type
|
||||
type mockServerStatusGetter struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// getServerStatus provides a mock function with given fields: client
|
||||
func (_m *mockServerStatusGetter) getServerStatus(client v1.ServerStatusRequestsGetter) (*arkv1.ServerStatusRequest, error) {
|
||||
ret := _m.Called(client)
|
||||
|
||||
var r0 *arkv1.ServerStatusRequest
|
||||
if rf, ok := ret.Get(0).(func(v1.ServerStatusRequestsGetter) *arkv1.ServerStatusRequest); ok {
|
||||
r0 = rf(client)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*arkv1.ServerStatusRequest)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(v1.ServerStatusRequestsGetter) error); ok {
|
||||
r1 = rf(client)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
Reference in New Issue
Block a user