From 6fe4f059b12e6eb163ff83d11c844e76eeaece66 Mon Sep 17 00:00:00 2001 From: Tom Wieczorek Date: Fri, 13 Dec 2024 09:25:48 +0100 Subject: [PATCH] Reject unknown sub-commands for k0s airgap Cobra has the property of not reporting any unrecognized sub-commands and simply displaying the help page for any command that doesn't have a run function set. Overwrite this for k0s's airgap sub-command. Signed-off-by: Tom Wieczorek --- cmd/airgap/airgap.go | 2 ++ cmd/airgap/airgap_test.go | 41 +++++++++++++++++++++++++++++++++++ cmd/airgap/listimages.go | 1 + cmd/airgap/listimages_test.go | 21 ++++++++++++------ 4 files changed, 58 insertions(+), 7 deletions(-) create mode 100644 cmd/airgap/airgap_test.go diff --git a/cmd/airgap/airgap.go b/cmd/airgap/airgap.go index 4c07343e9e7f..9d15ee6df5d0 100644 --- a/cmd/airgap/airgap.go +++ b/cmd/airgap/airgap.go @@ -26,6 +26,8 @@ func NewAirgapCmd() *cobra.Command { cmd := &cobra.Command{ Use: "airgap", Short: "Manage airgap setup", + Args: cobra.NoArgs, + Run: func(*cobra.Command, []string) { /* Enforce arg validation. */ }, } cmd.AddCommand(NewAirgapListImagesCmd()) diff --git a/cmd/airgap/airgap_test.go b/cmd/airgap/airgap_test.go new file mode 100644 index 000000000000..24a8223f39b7 --- /dev/null +++ b/cmd/airgap/airgap_test.go @@ -0,0 +1,41 @@ +/* +Copyright 2024 k0s authors + +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 airgap_test + +import ( + "errors" + "strings" + "testing" + "testing/iotest" + + "github.com/k0sproject/k0s/cmd" + "github.com/stretchr/testify/assert" +) + +func TestAirgapCmd_RejectsUnknownCommands(t *testing.T) { + var stdout, stderr strings.Builder + underTest := cmd.NewRootCmd() + underTest.SetArgs([]string{"airgap", "bogus"}) + underTest.SetIn(iotest.ErrReader(errors.New("unexpected read from standard input"))) + underTest.SetOut(&stdout) + underTest.SetErr(&stderr) + + msg := `unknown command "bogus" for "k0s airgap"` + assert.ErrorContains(t, underTest.Execute(), msg) + assert.Equal(t, "Error: "+msg+"\n", stderr.String()) + assert.Empty(t, stdout.String()) +} diff --git a/cmd/airgap/listimages.go b/cmd/airgap/listimages.go index 842ad5ce1705..b35bd88c8c63 100644 --- a/cmd/airgap/listimages.go +++ b/cmd/airgap/listimages.go @@ -32,6 +32,7 @@ func NewAirgapListImagesCmd() *cobra.Command { Use: "list-images", Short: "List image names and version needed for air-gap install", Example: `k0s airgap list-images`, + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { opts, err := config.GetCmdOpts(cmd) if err != nil { diff --git a/cmd/airgap/listimages_test.go b/cmd/airgap/listimages_test.go index be20ffc85a87..57cdd182d82c 100644 --- a/cmd/airgap/listimages_test.go +++ b/cmd/airgap/listimages_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package airgap +package airgap_test import ( "errors" @@ -26,6 +26,7 @@ import ( "testing" "testing/iotest" + "github.com/k0sproject/k0s/cmd" internalio "github.com/k0sproject/k0s/internal/io" "github.com/k0sproject/k0s/pkg/apis/k0s/v1beta1" @@ -44,17 +45,23 @@ func TestAirgapListImages(t *testing.T) { defaultImage := v1beta1.DefaultEnvoyProxyImage().URI() + t.Run("RejectsUnknownCommands", func(t *testing.T) { + underTest, stdout, stderr := newAirgapListImagesCmdWithConfig(t, "", "bogus") + + msg := `unknown command "bogus" for "k0s airgap list-images"` + assert.ErrorContains(t, underTest.Execute(), msg) + assert.Equal(t, "Error: "+msg+"\n", stderr.String()) + assert.Empty(t, stdout.String()) + }) + t.Run("HonorsIOErrors", func(t *testing.T) { var writes uint - underTest := NewAirgapListImagesCmd() - underTest.SetIn(iotest.ErrReader(errors.New("unexpected read from standard input"))) + underTest, _, stderr := newAirgapListImagesCmdWithConfig(t, "") underTest.SilenceUsage = true // Cobra writes usage to stdout on errors 🤔 underTest.SetOut(internalio.WriterFunc(func(p []byte) (int, error) { writes++ return 0, assert.AnError })) - var stderr strings.Builder - underTest.SetErr(&stderr) assert.Same(t, assert.AnError, underTest.Execute()) assert.Equal(t, uint(1), writes, "Expected a single write to stdout") @@ -127,8 +134,8 @@ func newAirgapListImagesCmdWithConfig(t *testing.T, config string, args ...strin require.NoError(t, os.WriteFile(configFile, []byte(config), 0644)) out, err = new(strings.Builder), new(strings.Builder) - cmd := NewAirgapListImagesCmd() - cmd.SetArgs(append([]string{"--config=" + configFile}, args...)) + cmd := cmd.NewRootCmd() + cmd.SetArgs(append([]string{"airgap", "--config=" + configFile, "list-images"}, args...)) cmd.SetIn(iotest.ErrReader(errors.New("unexpected read from standard input"))) cmd.SetOut(out) cmd.SetErr(err)