Skip to content

Commit cce99a8

Browse files
authored
Automated cherry pick of kubernetes#130503: Unhandled panic crash on rollout_history printer.PrintObj (kubernetes#131496)
* Change: Handling nil runtime.Object Signed-off-by: Taha Farahani <[email protected]> * Change: Return only if there is error in rollout_history Signed-off-by: Taha Farahani <[email protected]> * Change: Return the unknown revision error directly in rollout_history.go Signed-off-by: Taha Farahani <[email protected]> * Change: Remove unintended newline Signed-off-by: Taha Farahani <[email protected]> * Change: Using go idiomatic way for checking if historyInfo[o.Revision] exists Signed-off-by: Taha Farahani <[email protected]> * Change: Remove 'error:' from returned error message in rollout_history.go Signed-off-by: Taha Farahani <[email protected]> * Change: Check for printer.PrintObj returned err Signed-off-by: Taha Farahani <[email protected]> * Change: Add TestRolloutHistoryErrors test Signed-off-by: Taha Farahani <[email protected]> * Change: Simple typo fix on Complete() function description Signed-off-by: Taha Farahani <[email protected]> * Change: Checking for error on o.Complete in TestRolloutHistoryErrors Signed-off-by: Taha Farahani <[email protected]> --------- Signed-off-by: Taha Farahani <[email protected]>
1 parent 00ebe85 commit cce99a8

File tree

2 files changed

+92
-2
lines changed

2 files changed

+92
-2
lines changed

staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_history.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func NewCmdRolloutHistory(f cmdutil.Factory, streams genericiooptions.IOStreams)
105105
return cmd
106106
}
107107

108-
// Complete completes al the required options
108+
// Complete completes all the required options
109109
func (o *RolloutHistoryOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
110110
o.Resources = args
111111

@@ -177,7 +177,15 @@ func (o *RolloutHistoryOptions) Run() error {
177177
}
178178

179179
if o.Revision > 0 {
180-
printer.PrintObj(historyInfo[o.Revision], o.Out)
180+
// Ensure the specified revision exists before printing
181+
revision, exists := historyInfo[o.Revision]
182+
if !exists {
183+
return fmt.Errorf("unable to find the specified revision")
184+
}
185+
186+
if err := printer.PrintObj(revision, o.Out); err != nil {
187+
return err
188+
}
181189
} else {
182190
sortedKeys := make([]int64, 0, len(historyInfo))
183191
for k := range historyInfo {

staging/src/k8s.io/kubectl/pkg/cmd/rollout/rollout_history_test.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,88 @@ replicaset.apps/rev2
401401
}
402402
}
403403

404+
func TestRolloutHistoryErrors(t *testing.T) {
405+
ns := scheme.Codecs.WithoutConversion()
406+
tf := cmdtesting.NewTestFactory().WithNamespace("test")
407+
defer tf.Cleanup()
408+
409+
info, _ := runtime.SerializerInfoForMediaType(ns.SupportedMediaTypes(), runtime.ContentTypeJSON)
410+
encoder := ns.EncoderForVersion(info.Serializer, rolloutPauseGroupVersionEncoder)
411+
412+
tf.Client = &RolloutPauseRESTClient{
413+
RESTClient: &fake.RESTClient{
414+
GroupVersion: rolloutPauseGroupVersionEncoder,
415+
NegotiatedSerializer: ns,
416+
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
417+
switch p, m := req.URL.Path, req.Method; {
418+
case p == "/namespaces/test/deployments/foo" && m == "GET":
419+
responseDeployment := &appsv1.Deployment{}
420+
responseDeployment.Name = "foo"
421+
body := io.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(encoder, responseDeployment))))
422+
return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: body}, nil
423+
default:
424+
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
425+
return nil, nil
426+
}
427+
}),
428+
},
429+
}
430+
431+
testCases := map[string]struct {
432+
revision int64
433+
outputFormat string
434+
expectedError string
435+
}{
436+
"get non-existing revision as yaml": {
437+
revision: 999,
438+
outputFormat: "yaml",
439+
expectedError: "unable to find the specified revision",
440+
},
441+
"get non-existing revision as json": {
442+
revision: 999,
443+
outputFormat: "json",
444+
expectedError: "unable to find the specified revision",
445+
},
446+
}
447+
448+
for name, tc := range testCases {
449+
t.Run(name, func(t *testing.T) {
450+
fhv := setupFakeHistoryViewer(t)
451+
fhv.getHistoryFn = func(namespace, name string) (map[int64]runtime.Object, error) {
452+
return map[int64]runtime.Object{
453+
1: &appsv1.ReplicaSet{ObjectMeta: v1.ObjectMeta{Name: "rev1"}},
454+
2: &appsv1.ReplicaSet{ObjectMeta: v1.ObjectMeta{Name: "rev2"}},
455+
}, nil
456+
}
457+
458+
streams := genericiooptions.NewTestIOStreamsDiscard()
459+
o := NewRolloutHistoryOptions(streams)
460+
461+
printFlags := &genericclioptions.PrintFlags{
462+
JSONYamlPrintFlags: &genericclioptions.JSONYamlPrintFlags{
463+
ShowManagedFields: true,
464+
},
465+
OutputFormat: &tc.outputFormat,
466+
OutputFlagSpecified: func() bool {
467+
return true
468+
},
469+
}
470+
471+
o.PrintFlags = printFlags
472+
o.Revision = tc.revision
473+
474+
if err := o.Complete(tf, nil, []string{"deployment/foo"}); err != nil {
475+
t.Fatalf("unexpected error: %v", err)
476+
}
477+
478+
err := o.Run()
479+
if err != nil && err.Error() != tc.expectedError {
480+
t.Fatalf("expected '%s' error, but got: %v", tc.expectedError, err)
481+
}
482+
})
483+
}
484+
}
485+
404486
func TestValidate(t *testing.T) {
405487
opts := RolloutHistoryOptions{
406488
Revision: 0,

0 commit comments

Comments
 (0)