mirror of https://github.com/fluxcd/cli-utils.git
275 lines
6.4 KiB
Go
275 lines
6.4 KiB
Go
// Copyright 2020 The Kubernetes Authors.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package status
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/spf13/cobra"
|
|
"github.com/stretchr/testify/assert"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
|
|
cmdutil "k8s.io/kubectl/pkg/cmd/util"
|
|
"sigs.k8s.io/cli-utils/pkg/apply/poller"
|
|
"sigs.k8s.io/cli-utils/pkg/inventory"
|
|
"sigs.k8s.io/cli-utils/pkg/kstatus/polling"
|
|
pollevent "sigs.k8s.io/cli-utils/pkg/kstatus/polling/event"
|
|
"sigs.k8s.io/cli-utils/pkg/kstatus/status"
|
|
"sigs.k8s.io/cli-utils/pkg/manifestreader"
|
|
"sigs.k8s.io/cli-utils/pkg/object"
|
|
)
|
|
|
|
var (
|
|
inventoryTemplate = `
|
|
kind: ConfigMap
|
|
apiVersion: v1
|
|
metadata:
|
|
labels:
|
|
cli-utils.sigs.k8s.io/inventory-id: test
|
|
name: foo
|
|
namespace: default
|
|
`
|
|
depObject = object.ObjMetadata{
|
|
Name: "foo",
|
|
Namespace: "default",
|
|
GroupKind: schema.GroupKind{
|
|
Group: "apps",
|
|
Kind: "Deployment",
|
|
},
|
|
}
|
|
|
|
stsObject = object.ObjMetadata{
|
|
Name: "bar",
|
|
Namespace: "default",
|
|
GroupKind: schema.GroupKind{
|
|
Group: "apps",
|
|
Kind: "StatefulSet",
|
|
},
|
|
}
|
|
)
|
|
|
|
func TestStatusCommand(t *testing.T) {
|
|
testCases := map[string]struct {
|
|
pollUntil string
|
|
printer string
|
|
timeout time.Duration
|
|
input string
|
|
inventory object.ObjMetadataSet
|
|
events []pollevent.Event
|
|
expectedErrMsg string
|
|
expectedOutput string
|
|
}{
|
|
"no inventory in live state": {
|
|
input: inventoryTemplate,
|
|
expectedOutput: "no resources found in the inventory\n",
|
|
},
|
|
"wait for all known": {
|
|
pollUntil: "known",
|
|
printer: "events",
|
|
input: inventoryTemplate,
|
|
inventory: object.ObjMetadataSet{
|
|
depObject,
|
|
stsObject,
|
|
},
|
|
events: []pollevent.Event{
|
|
{
|
|
EventType: pollevent.ResourceUpdateEvent,
|
|
Resource: &pollevent.ResourceStatus{
|
|
Identifier: depObject,
|
|
Status: status.InProgressStatus,
|
|
Message: "inProgress",
|
|
},
|
|
},
|
|
{
|
|
EventType: pollevent.ResourceUpdateEvent,
|
|
Resource: &pollevent.ResourceStatus{
|
|
Identifier: stsObject,
|
|
Status: status.CurrentStatus,
|
|
Message: "current",
|
|
},
|
|
},
|
|
},
|
|
expectedOutput: `
|
|
deployment.apps/foo is InProgress: inProgress
|
|
statefulset.apps/bar is Current: current
|
|
`,
|
|
},
|
|
"wait for all current": {
|
|
pollUntil: "current",
|
|
printer: "events",
|
|
input: inventoryTemplate,
|
|
inventory: object.ObjMetadataSet{
|
|
depObject,
|
|
stsObject,
|
|
},
|
|
events: []pollevent.Event{
|
|
{
|
|
EventType: pollevent.ResourceUpdateEvent,
|
|
Resource: &pollevent.ResourceStatus{
|
|
Identifier: depObject,
|
|
Status: status.InProgressStatus,
|
|
Message: "inProgress",
|
|
},
|
|
},
|
|
{
|
|
EventType: pollevent.ResourceUpdateEvent,
|
|
Resource: &pollevent.ResourceStatus{
|
|
Identifier: stsObject,
|
|
Status: status.InProgressStatus,
|
|
Message: "inProgress",
|
|
},
|
|
},
|
|
{
|
|
EventType: pollevent.ResourceUpdateEvent,
|
|
Resource: &pollevent.ResourceStatus{
|
|
Identifier: stsObject,
|
|
Status: status.CurrentStatus,
|
|
Message: "current",
|
|
},
|
|
},
|
|
{
|
|
EventType: pollevent.ResourceUpdateEvent,
|
|
Resource: &pollevent.ResourceStatus{
|
|
Identifier: depObject,
|
|
Status: status.CurrentStatus,
|
|
Message: "current",
|
|
},
|
|
},
|
|
},
|
|
expectedOutput: `
|
|
deployment.apps/foo is InProgress: inProgress
|
|
statefulset.apps/bar is InProgress: inProgress
|
|
statefulset.apps/bar is Current: current
|
|
deployment.apps/foo is Current: current
|
|
`,
|
|
},
|
|
"wait for all deleted": {
|
|
pollUntil: "deleted",
|
|
printer: "events",
|
|
input: inventoryTemplate,
|
|
inventory: object.ObjMetadataSet{
|
|
depObject,
|
|
stsObject,
|
|
},
|
|
events: []pollevent.Event{
|
|
{
|
|
EventType: pollevent.ResourceUpdateEvent,
|
|
Resource: &pollevent.ResourceStatus{
|
|
Identifier: stsObject,
|
|
Status: status.NotFoundStatus,
|
|
Message: "notFound",
|
|
},
|
|
},
|
|
{
|
|
EventType: pollevent.ResourceUpdateEvent,
|
|
Resource: &pollevent.ResourceStatus{
|
|
Identifier: depObject,
|
|
Status: status.NotFoundStatus,
|
|
Message: "notFound",
|
|
},
|
|
},
|
|
},
|
|
expectedOutput: `
|
|
statefulset.apps/bar is NotFound: notFound
|
|
deployment.apps/foo is NotFound: notFound
|
|
`,
|
|
},
|
|
"forever with timeout": {
|
|
pollUntil: "forever",
|
|
printer: "events",
|
|
timeout: 2 * time.Second,
|
|
input: inventoryTemplate,
|
|
inventory: object.ObjMetadataSet{
|
|
depObject,
|
|
stsObject,
|
|
},
|
|
events: []pollevent.Event{
|
|
{
|
|
EventType: pollevent.ResourceUpdateEvent,
|
|
Resource: &pollevent.ResourceStatus{
|
|
Identifier: stsObject,
|
|
Status: status.InProgressStatus,
|
|
Message: "inProgress",
|
|
},
|
|
},
|
|
{
|
|
EventType: pollevent.ResourceUpdateEvent,
|
|
Resource: &pollevent.ResourceStatus{
|
|
Identifier: depObject,
|
|
Status: status.InProgressStatus,
|
|
Message: "inProgress",
|
|
},
|
|
},
|
|
},
|
|
expectedOutput: `
|
|
statefulset.apps/bar is InProgress: inProgress
|
|
deployment.apps/foo is InProgress: inProgress
|
|
`,
|
|
},
|
|
}
|
|
|
|
for tn, tc := range testCases {
|
|
t.Run(tn, func(t *testing.T) {
|
|
tf := cmdtesting.NewTestFactory().WithNamespace("namespace")
|
|
defer tf.Cleanup()
|
|
|
|
loader := manifestreader.NewFakeLoader(tf, tc.inventory)
|
|
runner := &StatusRunner{
|
|
factory: tf,
|
|
invFactory: inventory.FakeInventoryClientFactory(tc.inventory),
|
|
loader: loader,
|
|
pollerFactoryFunc: func(c cmdutil.Factory) (poller.Poller, error) {
|
|
return &fakePoller{tc.events}, nil
|
|
},
|
|
|
|
pollUntil: tc.pollUntil,
|
|
output: tc.printer,
|
|
timeout: tc.timeout,
|
|
}
|
|
|
|
cmd := &cobra.Command{
|
|
RunE: runner.runE,
|
|
}
|
|
cmd.SetIn(strings.NewReader(tc.input))
|
|
var buf bytes.Buffer
|
|
cmd.SetOut(&buf)
|
|
cmd.SetArgs([]string{})
|
|
|
|
err := cmd.Execute()
|
|
|
|
if tc.expectedErrMsg != "" {
|
|
if !assert.Error(t, err) {
|
|
t.FailNow()
|
|
}
|
|
assert.Contains(t, err.Error(), tc.expectedErrMsg)
|
|
return
|
|
}
|
|
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, strings.TrimSpace(buf.String()), strings.TrimSpace(tc.expectedOutput))
|
|
})
|
|
}
|
|
}
|
|
|
|
type fakePoller struct {
|
|
events []pollevent.Event
|
|
}
|
|
|
|
func (f *fakePoller) Poll(ctx context.Context, _ object.ObjMetadataSet,
|
|
_ polling.Options) <-chan pollevent.Event {
|
|
eventChannel := make(chan pollevent.Event)
|
|
go func() {
|
|
defer close(eventChannel)
|
|
for _, e := range f.events {
|
|
eventChannel <- e
|
|
}
|
|
<-ctx.Done()
|
|
}()
|
|
return eventChannel
|
|
}
|