github.com/wmuizelaar/kpt@v0.0.0-20221018115725-bd564717b2ed/commands/live/status/cmdstatus_test.go (about)

     1  // Copyright 2022 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package status
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"path/filepath"
    21  	"strings"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/GoogleContainerTools/kpt/internal/printer/fake"
    26  	"github.com/GoogleContainerTools/kpt/internal/testutil"
    27  	kptfilev1 "github.com/GoogleContainerTools/kpt/pkg/api/kptfile/v1"
    28  	"github.com/GoogleContainerTools/kpt/pkg/kptfile/kptfileutil"
    29  	"github.com/stretchr/testify/assert"
    30  	"k8s.io/apimachinery/pkg/runtime/schema"
    31  	cmdtesting "k8s.io/kubectl/pkg/cmd/testing"
    32  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    33  	"sigs.k8s.io/cli-utils/pkg/apply/poller"
    34  	"sigs.k8s.io/cli-utils/pkg/inventory"
    35  	"sigs.k8s.io/cli-utils/pkg/kstatus/polling"
    36  	pollevent "sigs.k8s.io/cli-utils/pkg/kstatus/polling/event"
    37  	"sigs.k8s.io/cli-utils/pkg/kstatus/status"
    38  	"sigs.k8s.io/cli-utils/pkg/object"
    39  )
    40  
    41  var (
    42  	depObject = object.ObjMetadata{
    43  		Name:      "foo",
    44  		Namespace: "default",
    45  		GroupKind: schema.GroupKind{
    46  			Group: "apps",
    47  			Kind:  "Deployment",
    48  		},
    49  	}
    50  
    51  	stsObject = object.ObjMetadata{
    52  		Name:      "bar",
    53  		Namespace: "default",
    54  		GroupKind: schema.GroupKind{
    55  			Group: "apps",
    56  			Kind:  "StatefulSet",
    57  		},
    58  	}
    59  )
    60  
    61  func TestStatusCommand(t *testing.T) {
    62  	testCases := map[string]struct {
    63  		pollUntil      string
    64  		printer        string
    65  		timeout        time.Duration
    66  		kptfileInv     *kptfilev1.Inventory
    67  		inventory      []object.ObjMetadata
    68  		events         []pollevent.Event
    69  		expectedErrMsg string
    70  		expectedOutput string
    71  	}{
    72  		"no inventory template": {
    73  			kptfileInv:     nil,
    74  			expectedErrMsg: "no ResourceGroup object was provided within the stream or package",
    75  		},
    76  		"invalid value for pollUntil": {
    77  			pollUntil:      "doesNotExist",
    78  			expectedErrMsg: "pollUntil must be one of known,current,deleted,forever",
    79  		},
    80  		"no inventory in live state": {
    81  			kptfileInv: &kptfilev1.Inventory{
    82  				Name:        "foo",
    83  				Namespace:   "default",
    84  				InventoryID: "test",
    85  			},
    86  			expectedOutput: "no resources found in the inventory\n",
    87  		},
    88  		"wait for all known": {
    89  			pollUntil: "known",
    90  			printer:   "events",
    91  			kptfileInv: &kptfilev1.Inventory{
    92  				Name:        "foo",
    93  				Namespace:   "default",
    94  				InventoryID: "test",
    95  			},
    96  			inventory: []object.ObjMetadata{
    97  				depObject,
    98  				stsObject,
    99  			},
   100  			events: []pollevent.Event{
   101  				{
   102  					Type: pollevent.ResourceUpdateEvent,
   103  					Resource: &pollevent.ResourceStatus{
   104  						Identifier: depObject,
   105  						Status:     status.InProgressStatus,
   106  						Message:    "inProgress",
   107  					},
   108  				},
   109  				{
   110  					Type: pollevent.ResourceUpdateEvent,
   111  					Resource: &pollevent.ResourceStatus{
   112  						Identifier: stsObject,
   113  						Status:     status.CurrentStatus,
   114  						Message:    "current",
   115  					},
   116  				},
   117  			},
   118  			expectedOutput: `
   119  foo/deployment.apps/default/foo is InProgress: inProgress
   120  foo/statefulset.apps/default/bar is Current: current
   121  `,
   122  		},
   123  		"wait for all current": {
   124  			pollUntil: "current",
   125  			printer:   "events",
   126  			kptfileInv: &kptfilev1.Inventory{
   127  				Name:        "foo",
   128  				Namespace:   "default",
   129  				InventoryID: "test",
   130  			},
   131  			inventory: []object.ObjMetadata{
   132  				depObject,
   133  				stsObject,
   134  			},
   135  			events: []pollevent.Event{
   136  				{
   137  					Type: pollevent.ResourceUpdateEvent,
   138  					Resource: &pollevent.ResourceStatus{
   139  						Identifier: depObject,
   140  						Status:     status.InProgressStatus,
   141  						Message:    "inProgress",
   142  					},
   143  				},
   144  				{
   145  					Type: pollevent.ResourceUpdateEvent,
   146  					Resource: &pollevent.ResourceStatus{
   147  						Identifier: stsObject,
   148  						Status:     status.InProgressStatus,
   149  						Message:    "inProgress",
   150  					},
   151  				},
   152  				{
   153  					Type: pollevent.ResourceUpdateEvent,
   154  					Resource: &pollevent.ResourceStatus{
   155  						Identifier: stsObject,
   156  						Status:     status.CurrentStatus,
   157  						Message:    "current",
   158  					},
   159  				},
   160  				{
   161  					Type: pollevent.ResourceUpdateEvent,
   162  					Resource: &pollevent.ResourceStatus{
   163  						Identifier: depObject,
   164  						Status:     status.CurrentStatus,
   165  						Message:    "current",
   166  					},
   167  				},
   168  			},
   169  			expectedOutput: `
   170  foo/deployment.apps/default/foo is InProgress: inProgress
   171  foo/statefulset.apps/default/bar is InProgress: inProgress
   172  foo/statefulset.apps/default/bar is Current: current
   173  foo/deployment.apps/default/foo is Current: current
   174  `,
   175  		},
   176  		"wait for all deleted": {
   177  			pollUntil: "deleted",
   178  			printer:   "events",
   179  			kptfileInv: &kptfilev1.Inventory{
   180  				Name:        "foo",
   181  				Namespace:   "default",
   182  				InventoryID: "test",
   183  			},
   184  			inventory: []object.ObjMetadata{
   185  				depObject,
   186  				stsObject,
   187  			},
   188  			events: []pollevent.Event{
   189  				{
   190  					Type: pollevent.ResourceUpdateEvent,
   191  					Resource: &pollevent.ResourceStatus{
   192  						Identifier: stsObject,
   193  						Status:     status.NotFoundStatus,
   194  						Message:    "notFound",
   195  					},
   196  				},
   197  				{
   198  					Type: pollevent.ResourceUpdateEvent,
   199  					Resource: &pollevent.ResourceStatus{
   200  						Identifier: depObject,
   201  						Status:     status.NotFoundStatus,
   202  						Message:    "notFound",
   203  					},
   204  				},
   205  			},
   206  			expectedOutput: `
   207  foo/statefulset.apps/default/bar is NotFound: notFound
   208  foo/deployment.apps/default/foo is NotFound: notFound
   209  `,
   210  		},
   211  		"forever with timeout": {
   212  			pollUntil: "forever",
   213  			printer:   "events",
   214  			timeout:   2 * time.Second,
   215  			kptfileInv: &kptfilev1.Inventory{
   216  				Name:        "foo",
   217  				Namespace:   "default",
   218  				InventoryID: "test",
   219  			},
   220  			inventory: []object.ObjMetadata{
   221  				depObject,
   222  				stsObject,
   223  			},
   224  			events: []pollevent.Event{
   225  				{
   226  					Type: pollevent.ResourceUpdateEvent,
   227  					Resource: &pollevent.ResourceStatus{
   228  						Identifier: stsObject,
   229  						Status:     status.InProgressStatus,
   230  						Message:    "inProgress",
   231  					},
   232  				},
   233  				{
   234  					Type: pollevent.ResourceUpdateEvent,
   235  					Resource: &pollevent.ResourceStatus{
   236  						Identifier: depObject,
   237  						Status:     status.InProgressStatus,
   238  						Message:    "inProgress",
   239  					},
   240  				},
   241  			},
   242  			expectedOutput: `
   243  foo/statefulset.apps/default/bar is InProgress: inProgress
   244  foo/deployment.apps/default/foo is InProgress: inProgress
   245  `,
   246  		},
   247  	}
   248  
   249  	for tn, tc := range testCases {
   250  		t.Run(tn, func(t *testing.T) {
   251  			tf := cmdtesting.NewTestFactory().WithNamespace("namespace")
   252  			defer tf.Cleanup()
   253  
   254  			w, clean := testutil.SetupWorkspace(t)
   255  			defer clean()
   256  			kf := kptfileutil.DefaultKptfile(filepath.Base(w.WorkspaceDirectory))
   257  			kf.Inventory = tc.kptfileInv
   258  			testutil.AddKptfileToWorkspace(t, w, kf)
   259  
   260  			revert := testutil.Chdir(t, w.WorkspaceDirectory)
   261  			defer revert()
   262  
   263  			var outBuf bytes.Buffer
   264  			ctx := fake.CtxWithPrinter(&outBuf, &outBuf)
   265  			invFactory := inventory.FakeClientFactory(tc.inventory)
   266  			loader := NewFakeLoader(ctx, tf, tc.inventory)
   267  			runner := NewRunner(ctx, tf, invFactory, loader)
   268  			runner.PollerFactoryFunc = func(c cmdutil.Factory) (poller.Poller, error) {
   269  				return &fakePoller{tc.events}, nil
   270  			}
   271  
   272  			args := []string{}
   273  			if tc.pollUntil != "" {
   274  				args = append(args, []string{
   275  					"--poll-until", tc.pollUntil,
   276  				}...)
   277  			}
   278  			if tc.timeout != time.Duration(0) {
   279  				args = append(args, []string{
   280  					"--timeout", tc.timeout.String(),
   281  				}...)
   282  			}
   283  			runner.Command.SetArgs(args)
   284  			runner.Command.SetOut(&outBuf)
   285  			err := runner.Command.Execute()
   286  
   287  			if tc.expectedErrMsg != "" {
   288  				if !assert.Error(t, err) {
   289  					t.FailNow()
   290  				}
   291  				assert.Contains(t, err.Error(), tc.expectedErrMsg)
   292  				return
   293  			}
   294  
   295  			assert.NoError(t, err)
   296  			assert.Equal(t, strings.TrimSpace(tc.expectedOutput), strings.TrimSpace(outBuf.String()))
   297  		})
   298  	}
   299  }
   300  
   301  type fakePoller struct {
   302  	events []pollevent.Event
   303  }
   304  
   305  func (f *fakePoller) Poll(ctx context.Context, _ object.ObjMetadataSet,
   306  	_ polling.PollOptions) <-chan pollevent.Event {
   307  	eventChannel := make(chan pollevent.Event)
   308  	go func() {
   309  		defer close(eventChannel)
   310  		for _, e := range f.events {
   311  			eventChannel <- e
   312  		}
   313  		<-ctx.Done()
   314  	}()
   315  	return eventChannel
   316  }