From ccadb48ae3bef88c06351105f6b8f7657f681091 Mon Sep 17 00:00:00 2001 From: Sven Petersen <5971734+sven-petersen@users.noreply.github.com> Date: Fri, 20 Jan 2023 10:09:36 +0100 Subject: [PATCH] enh: target cmd - only register target flags with higher specificty (#206) * enh: target cmd - only register target flags with higher specificty * expose TargetFlags on factory * PR feedback --- docs/help/gardenctl_target_control-plane.md | 13 ++++---- docs/help/gardenctl_target_garden.md | 5 --- docs/help/gardenctl_target_project.md | 6 +--- docs/help/gardenctl_target_seed.md | 6 +--- docs/help/gardenctl_target_shoot.md | 12 +++----- docs/help/gardenctl_target_unset.md | 5 --- docs/help/gardenctl_target_view.md | 5 --- internal/fake/factory.go | 8 +++++ internal/util/factory.go | 19 ++++++++++-- internal/util/mocks/mock_factory.go | 17 +++++++++-- pkg/cmd/cmd.go | 7 ++--- pkg/cmd/cmd_test.go | 16 +++------- pkg/cmd/target/control-plane.go | 7 +++++ pkg/cmd/target/project.go | 4 +++ pkg/cmd/target/seed.go | 4 +++ pkg/cmd/target/shoot.go | 6 ++++ pkg/cmd/target/target.go | 7 ++--- pkg/flags/target.go | 22 ++++++++++--- pkg/flags/target_test.go | 12 ++------ pkg/target/target_flags.go | 34 ++++++++++++++++++++- 20 files changed, 136 insertions(+), 79 deletions(-) diff --git a/docs/help/gardenctl_target_control-plane.md b/docs/help/gardenctl_target_control-plane.md index 1f2bad5f..8bbe5554 100644 --- a/docs/help/gardenctl_target_control-plane.md +++ b/docs/help/gardenctl_target_control-plane.md @@ -23,8 +23,12 @@ gardenctl target control-plane --garden my-garden --project my-project --shoot m ### Options ``` - -h, --help help for control-plane - -o, --output string One of 'yaml' or 'json'. + --garden string target the given garden cluster + -h, --help help for control-plane + -o, --output string One of 'yaml' or 'json'. + --project string target the given project + --seed string target the given seed cluster + --shoot string target the given shoot cluster ``` ### Options inherited from parent commands @@ -33,17 +37,12 @@ gardenctl target control-plane --garden my-garden --project my-project --shoot m --add-dir-header If true, adds the file directory to the header of the log messages --alsologtostderr log to standard error as well as files (no effect when -logtostderr=true) --config string config file (default is ~/.garden/gardenctl-v2.yaml) - --control-plane target control plane of shoot, use together with shoot argument - --garden string target the given garden cluster --log-backtrace-at traceLocation when logging hits line file:N, emit a stack trace (default :0) --log-dir string If non-empty, write log files in this directory (no effect when -logtostderr=true) --log-file string If non-empty, use this log file (no effect when -logtostderr=true) --log-file-max-size uint Defines the maximum size a log file can grow to (no effect when -logtostderr=true). Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) --logtostderr log to standard error instead of files (default true) --one-output If true, only write logs to their native severity level (vs also writing to each lower severity level; no effect when -logtostderr=true) - --project string target the given project - --seed string target the given seed cluster - --shoot string target the given shoot cluster --skip-headers If true, avoid header prefixes in the log messages --skip-log-headers If true, avoid headers when opening log files (no effect when -logtostderr=true) --stderrthreshold severity logs at or above this threshold go to stderr when writing to files and stderr (no effect when -logtostderr=true or -alsologtostderr=false) (default 2) diff --git a/docs/help/gardenctl_target_garden.md b/docs/help/gardenctl_target_garden.md index 263b3547..1747a054 100644 --- a/docs/help/gardenctl_target_garden.md +++ b/docs/help/gardenctl_target_garden.md @@ -30,17 +30,12 @@ gardenctl target garden my-garden --add-dir-header If true, adds the file directory to the header of the log messages --alsologtostderr log to standard error as well as files (no effect when -logtostderr=true) --config string config file (default is ~/.garden/gardenctl-v2.yaml) - --control-plane target control plane of shoot, use together with shoot argument - --garden string target the given garden cluster --log-backtrace-at traceLocation when logging hits line file:N, emit a stack trace (default :0) --log-dir string If non-empty, write log files in this directory (no effect when -logtostderr=true) --log-file string If non-empty, use this log file (no effect when -logtostderr=true) --log-file-max-size uint Defines the maximum size a log file can grow to (no effect when -logtostderr=true). Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) --logtostderr log to standard error instead of files (default true) --one-output If true, only write logs to their native severity level (vs also writing to each lower severity level; no effect when -logtostderr=true) - --project string target the given project - --seed string target the given seed cluster - --shoot string target the given shoot cluster --skip-headers If true, avoid header prefixes in the log messages --skip-log-headers If true, avoid headers when opening log files (no effect when -logtostderr=true) --stderrthreshold severity logs at or above this threshold go to stderr when writing to files and stderr (no effect when -logtostderr=true or -alsologtostderr=false) (default 2) diff --git a/docs/help/gardenctl_target_project.md b/docs/help/gardenctl_target_project.md index a631102e..618fe140 100644 --- a/docs/help/gardenctl_target_project.md +++ b/docs/help/gardenctl_target_project.md @@ -23,6 +23,7 @@ gardenctl target project my-project --garden my-garden ### Options ``` + --garden string target the given garden cluster -h, --help help for project -o, --output string One of 'yaml' or 'json'. ``` @@ -33,17 +34,12 @@ gardenctl target project my-project --garden my-garden --add-dir-header If true, adds the file directory to the header of the log messages --alsologtostderr log to standard error as well as files (no effect when -logtostderr=true) --config string config file (default is ~/.garden/gardenctl-v2.yaml) - --control-plane target control plane of shoot, use together with shoot argument - --garden string target the given garden cluster --log-backtrace-at traceLocation when logging hits line file:N, emit a stack trace (default :0) --log-dir string If non-empty, write log files in this directory (no effect when -logtostderr=true) --log-file string If non-empty, use this log file (no effect when -logtostderr=true) --log-file-max-size uint Defines the maximum size a log file can grow to (no effect when -logtostderr=true). Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) --logtostderr log to standard error instead of files (default true) --one-output If true, only write logs to their native severity level (vs also writing to each lower severity level; no effect when -logtostderr=true) - --project string target the given project - --seed string target the given seed cluster - --shoot string target the given shoot cluster --skip-headers If true, avoid header prefixes in the log messages --skip-log-headers If true, avoid headers when opening log files (no effect when -logtostderr=true) --stderrthreshold severity logs at or above this threshold go to stderr when writing to files and stderr (no effect when -logtostderr=true or -alsologtostderr=false) (default 2) diff --git a/docs/help/gardenctl_target_seed.md b/docs/help/gardenctl_target_seed.md index c9c77b3b..414a46f2 100644 --- a/docs/help/gardenctl_target_seed.md +++ b/docs/help/gardenctl_target_seed.md @@ -23,6 +23,7 @@ gardenctl target seed my-seed --garden my-garden ### Options ``` + --garden string target the given garden cluster -h, --help help for seed -o, --output string One of 'yaml' or 'json'. ``` @@ -33,17 +34,12 @@ gardenctl target seed my-seed --garden my-garden --add-dir-header If true, adds the file directory to the header of the log messages --alsologtostderr log to standard error as well as files (no effect when -logtostderr=true) --config string config file (default is ~/.garden/gardenctl-v2.yaml) - --control-plane target control plane of shoot, use together with shoot argument - --garden string target the given garden cluster --log-backtrace-at traceLocation when logging hits line file:N, emit a stack trace (default :0) --log-dir string If non-empty, write log files in this directory (no effect when -logtostderr=true) --log-file string If non-empty, use this log file (no effect when -logtostderr=true) --log-file-max-size uint Defines the maximum size a log file can grow to (no effect when -logtostderr=true). Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) --logtostderr log to standard error instead of files (default true) --one-output If true, only write logs to their native severity level (vs also writing to each lower severity level; no effect when -logtostderr=true) - --project string target the given project - --seed string target the given seed cluster - --shoot string target the given shoot cluster --skip-headers If true, avoid header prefixes in the log messages --skip-log-headers If true, avoid headers when opening log files (no effect when -logtostderr=true) --stderrthreshold severity logs at or above this threshold go to stderr when writing to files and stderr (no effect when -logtostderr=true or -alsologtostderr=false) (default 2) diff --git a/docs/help/gardenctl_target_shoot.md b/docs/help/gardenctl_target_shoot.md index 12f8370b..f0052f61 100644 --- a/docs/help/gardenctl_target_shoot.md +++ b/docs/help/gardenctl_target_shoot.md @@ -23,8 +23,11 @@ gardenctl target shoot my-shoot --garden my-garden --project my-project ### Options ``` - -h, --help help for shoot - -o, --output string One of 'yaml' or 'json'. + --garden string target the given garden cluster + -h, --help help for shoot + -o, --output string One of 'yaml' or 'json'. + --project string target the given project + --seed string target the given seed cluster ``` ### Options inherited from parent commands @@ -33,17 +36,12 @@ gardenctl target shoot my-shoot --garden my-garden --project my-project --add-dir-header If true, adds the file directory to the header of the log messages --alsologtostderr log to standard error as well as files (no effect when -logtostderr=true) --config string config file (default is ~/.garden/gardenctl-v2.yaml) - --control-plane target control plane of shoot, use together with shoot argument - --garden string target the given garden cluster --log-backtrace-at traceLocation when logging hits line file:N, emit a stack trace (default :0) --log-dir string If non-empty, write log files in this directory (no effect when -logtostderr=true) --log-file string If non-empty, use this log file (no effect when -logtostderr=true) --log-file-max-size uint Defines the maximum size a log file can grow to (no effect when -logtostderr=true). Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) --logtostderr log to standard error instead of files (default true) --one-output If true, only write logs to their native severity level (vs also writing to each lower severity level; no effect when -logtostderr=true) - --project string target the given project - --seed string target the given seed cluster - --shoot string target the given shoot cluster --skip-headers If true, avoid header prefixes in the log messages --skip-log-headers If true, avoid headers when opening log files (no effect when -logtostderr=true) --stderrthreshold severity logs at or above this threshold go to stderr when writing to files and stderr (no effect when -logtostderr=true or -alsologtostderr=false) (default 2) diff --git a/docs/help/gardenctl_target_unset.md b/docs/help/gardenctl_target_unset.md index 7662b9a8..21d58a38 100644 --- a/docs/help/gardenctl_target_unset.md +++ b/docs/help/gardenctl_target_unset.md @@ -28,17 +28,12 @@ gardenctl target unset garden --add-dir-header If true, adds the file directory to the header of the log messages --alsologtostderr log to standard error as well as files (no effect when -logtostderr=true) --config string config file (default is ~/.garden/gardenctl-v2.yaml) - --control-plane target control plane of shoot, use together with shoot argument - --garden string target the given garden cluster --log-backtrace-at traceLocation when logging hits line file:N, emit a stack trace (default :0) --log-dir string If non-empty, write log files in this directory (no effect when -logtostderr=true) --log-file string If non-empty, use this log file (no effect when -logtostderr=true) --log-file-max-size uint Defines the maximum size a log file can grow to (no effect when -logtostderr=true). Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) --logtostderr log to standard error instead of files (default true) --one-output If true, only write logs to their native severity level (vs also writing to each lower severity level; no effect when -logtostderr=true) - --project string target the given project - --seed string target the given seed cluster - --shoot string target the given shoot cluster --skip-headers If true, avoid header prefixes in the log messages --skip-log-headers If true, avoid headers when opening log files (no effect when -logtostderr=true) --stderrthreshold severity logs at or above this threshold go to stderr when writing to files and stderr (no effect when -logtostderr=true or -alsologtostderr=false) (default 2) diff --git a/docs/help/gardenctl_target_view.md b/docs/help/gardenctl_target_view.md index 0812431a..a71ff642 100644 --- a/docs/help/gardenctl_target_view.md +++ b/docs/help/gardenctl_target_view.md @@ -19,17 +19,12 @@ gardenctl target view [flags] --add-dir-header If true, adds the file directory to the header of the log messages --alsologtostderr log to standard error as well as files (no effect when -logtostderr=true) --config string config file (default is ~/.garden/gardenctl-v2.yaml) - --control-plane target control plane of shoot, use together with shoot argument - --garden string target the given garden cluster --log-backtrace-at traceLocation when logging hits line file:N, emit a stack trace (default :0) --log-dir string If non-empty, write log files in this directory (no effect when -logtostderr=true) --log-file string If non-empty, use this log file (no effect when -logtostderr=true) --log-file-max-size uint Defines the maximum size a log file can grow to (no effect when -logtostderr=true). Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) --logtostderr log to standard error instead of files (default true) --one-output If true, only write logs to their native severity level (vs also writing to each lower severity level; no effect when -logtostderr=true) - --project string target the given project - --seed string target the given seed cluster - --shoot string target the given shoot cluster --skip-headers If true, avoid header prefixes in the log messages --skip-log-headers If true, avoid headers when opening log files (no effect when -logtostderr=true) --stderrthreshold severity logs at or above this threshold go to stderr when writing to files and stderr (no effect when -logtostderr=true or -alsologtostderr=false) (default 2) diff --git a/internal/fake/factory.go b/internal/fake/factory.go index 2469b883..2aee2dc4 100644 --- a/internal/fake/factory.go +++ b/internal/fake/factory.go @@ -30,6 +30,7 @@ type Factory struct { Config *config.Config ClientProviderImpl target.ClientProvider TargetProviderImpl target.TargetProvider + TargetFlagsImpl target.TargetFlags // Override the clock implementation. Will use a real clock if not set. ClockImpl util.Clock @@ -58,11 +59,14 @@ func NewFakeFactory(cfg *config.Config, clock util.Clock, clientProvider target. clock = &util.RealClock{} } + targetFlags := target.NewTargetFlags("", "", "", "", false) + return &Factory{ Config: cfg, ClockImpl: clock, ClientProviderImpl: clientProvider, TargetProviderImpl: targetProvider, + TargetFlagsImpl: targetFlags, } } @@ -95,3 +99,7 @@ func (f *Factory) Clock() util.Clock { func (f *Factory) PublicIPs(ctx context.Context) ([]string, error) { return []string{"192.0.2.42", "2001:db8::8a2e:370:7334"}, nil } + +func (f *Factory) TargetFlags() target.TargetFlags { + return f.TargetFlagsImpl +} diff --git a/internal/util/factory.go b/internal/util/factory.go index 1555db7e..fed852b8 100644 --- a/internal/util/factory.go +++ b/internal/util/factory.go @@ -48,6 +48,9 @@ type Factory interface { // returned slice can contain IPv6, IPv4 or both, in no particular // order. PublicIPs(context.Context) ([]string, error) + // TargetFlags returns the TargetFlags to which the cobra flags are bound allowing the user to + // override the target configuration stored on the filesystem. + TargetFlags() target.TargetFlags } // FactoryImpl implements util.Factory interface. @@ -63,13 +66,19 @@ type FactoryImpl struct { // if empty. ConfigFile string - // TargetFlags can be used to completely override the target configuration + // targetFlags can be used to completely override the target configuration // stored on the filesystem via a CLI flags. - TargetFlags target.TargetFlags + targetFlags target.TargetFlags } var _ Factory = &FactoryImpl{} +func NewFactoryImpl() *FactoryImpl { + return &FactoryImpl{ + targetFlags: target.NewTargetFlags("", "", "", "", false), + } +} + func (f *FactoryImpl) Context() context.Context { return context.Background() } @@ -92,7 +101,7 @@ func (f *FactoryImpl) Manager() (target.Manager, error) { return nil, fmt.Errorf("failed to create session directory: %w", err) } - targetProvider := target.NewTargetProvider(filepath.Join(sessionDirectory, "target.yaml"), f.TargetFlags) + targetProvider := target.NewTargetProvider(filepath.Join(sessionDirectory, "target.yaml"), f.targetFlags) clientProvider := target.NewClientProvider() return target.NewManager(cfg, targetProvider, clientProvider, sessionDirectory) @@ -125,6 +134,10 @@ func (f *FactoryImpl) PublicIPs(ctx context.Context) ([]string, error) { return addresses, nil } +func (f *FactoryImpl) TargetFlags() target.TargetFlags { + return f.targetFlags +} + func callIPify(ctx context.Context, domain string) (*net.IP, error) { req, err := http.NewRequest("GET", fmt.Sprintf("https://%s/", domain), nil) if err != nil { diff --git a/internal/util/mocks/mock_factory.go b/internal/util/mocks/mock_factory.go index 57dbab9a..a56e9a06 100644 --- a/internal/util/mocks/mock_factory.go +++ b/internal/util/mocks/mock_factory.go @@ -8,10 +8,9 @@ import ( context "context" reflect "reflect" - gomock "github.com/golang/mock/gomock" - util "github.com/gardener/gardenctl-v2/internal/util" target "github.com/gardener/gardenctl-v2/pkg/target" + gomock "github.com/golang/mock/gomock" ) // MockFactory is a mock of Factory interface. @@ -108,3 +107,17 @@ func (mr *MockFactoryMockRecorder) PublicIPs(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PublicIPs", reflect.TypeOf((*MockFactory)(nil).PublicIPs), arg0) } + +// TargetFlags mocks base method. +func (m *MockFactory) TargetFlags() target.TargetFlags { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TargetFlags") + ret0, _ := ret[0].(target.TargetFlags) + return ret0 +} + +// TargetFlags indicates an expected call of TargetFlags. +func (mr *MockFactoryMockRecorder) TargetFlags() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TargetFlags", reflect.TypeOf((*MockFactory)(nil).TargetFlags)) +} diff --git a/pkg/cmd/cmd.go b/pkg/cmd/cmd.go index 76b8112f..41136fda 100644 --- a/pkg/cmd/cmd.go +++ b/pkg/cmd/cmd.go @@ -27,7 +27,6 @@ import ( cmdsshpatch "github.com/gardener/gardenctl-v2/pkg/cmd/sshpatch" cmdtarget "github.com/gardener/gardenctl-v2/pkg/cmd/target" cmdversion "github.com/gardener/gardenctl-v2/pkg/cmd/version" - "github.com/gardener/gardenctl-v2/pkg/target" ) const ( @@ -52,12 +51,10 @@ func Execute() { // NewDefaultGardenctlCommand creates the `gardenctl` command with defaults. func NewDefaultGardenctlCommand() *cobra.Command { - factory := util.FactoryImpl{ - TargetFlags: target.NewTargetFlags("", "", "", "", false), - } + factory := util.NewFactoryImpl() ioStreams := util.NewIOStreams() - return NewGardenctlCommand(&factory, ioStreams) + return NewGardenctlCommand(factory, ioStreams) } // NewGardenctlCommand creates the `gardenctl` command. diff --git a/pkg/cmd/cmd_test.go b/pkg/cmd/cmd_test.go index d778a4e5..579c427f 100644 --- a/pkg/cmd/cmd_test.go +++ b/pkg/cmd/cmd_test.go @@ -26,10 +26,9 @@ var _ = Describe("Gardenctl command", func() { ) var ( - gardenName string - streams util.IOStreams - out *util.SafeBytesBuffer - targetFlags target.TargetFlags + gardenName string + streams util.IOStreams + out *util.SafeBytesBuffer ) BeforeEach(func() { @@ -37,8 +36,6 @@ var _ = Describe("Gardenctl command", func() { streams, _, out, _ = util.NewTestIOStreams() - targetFlags = target.NewTargetFlags("", "", "", "", false) - targetProvider := target.NewTargetProvider(filepath.Join(sessionDir, "target.yaml"), nil) Expect(targetProvider.Write(target.NewTarget(gardenName, projectName, "", shootName))).To(Succeed()) }) @@ -47,10 +44,7 @@ var _ = Describe("Gardenctl command", func() { var factory *util.FactoryImpl BeforeEach(func() { - factory = &util.FactoryImpl{ - TargetFlags: targetFlags, - ConfigFile: configFile, - } + factory = util.NewFactoryImpl() }) Context("when running the completion command", func() { @@ -74,7 +68,7 @@ var _ = Describe("Gardenctl command", func() { // check target flags tf := manager.TargetFlags() - Expect(tf).To(BeIdenticalTo(factory.TargetFlags)) + Expect(tf).To(BeIdenticalTo(factory.TargetFlags())) // check current target values current, err := manager.CurrentTarget() diff --git a/pkg/cmd/target/control-plane.go b/pkg/cmd/target/control-plane.go index 8f7c18bb..3edef7d4 100644 --- a/pkg/cmd/target/control-plane.go +++ b/pkg/cmd/target/control-plane.go @@ -11,6 +11,7 @@ import ( "github.com/gardener/gardenctl-v2/internal/util" "github.com/gardener/gardenctl-v2/pkg/cmd/base" + "github.com/gardener/gardenctl-v2/pkg/flags" ) // NewCmdTargetControlPlane returns a new target control plane command. @@ -36,5 +37,11 @@ gardenctl target control-plane --garden my-garden --project my-project --shoot m o.AddFlags(cmd.Flags()) + f.TargetFlags().AddGardenFlag(cmd.Flags()) + f.TargetFlags().AddProjectFlag(cmd.Flags()) + f.TargetFlags().AddShootFlag(cmd.Flags()) + f.TargetFlags().AddSeedFlag(cmd.Flags()) + flags.RegisterCompletionFuncsForTargetFlags(cmd, f, ioStreams, cmd.Flags()) + return cmd } diff --git a/pkg/cmd/target/project.go b/pkg/cmd/target/project.go index 1b04289b..1977db65 100644 --- a/pkg/cmd/target/project.go +++ b/pkg/cmd/target/project.go @@ -11,6 +11,7 @@ import ( "github.com/gardener/gardenctl-v2/internal/util" "github.com/gardener/gardenctl-v2/pkg/cmd/base" + "github.com/gardener/gardenctl-v2/pkg/flags" ) // NewCmdTargetProject returns a new target project command. @@ -36,5 +37,8 @@ gardenctl target project my-project --garden my-garden`, o.AddFlags(cmd.Flags()) + f.TargetFlags().AddGardenFlag(cmd.Flags()) + flags.RegisterCompletionFuncsForTargetFlags(cmd, f, ioStreams, cmd.Flags()) + return cmd } diff --git a/pkg/cmd/target/seed.go b/pkg/cmd/target/seed.go index 5ab2e1c1..7075733e 100644 --- a/pkg/cmd/target/seed.go +++ b/pkg/cmd/target/seed.go @@ -11,6 +11,7 @@ import ( "github.com/gardener/gardenctl-v2/internal/util" "github.com/gardener/gardenctl-v2/pkg/cmd/base" + "github.com/gardener/gardenctl-v2/pkg/flags" ) // NewCmdTargetSeed returns a new target seed command. @@ -36,5 +37,8 @@ gardenctl target seed my-seed --garden my-garden`, o.AddFlags(cmd.Flags()) + f.TargetFlags().AddGardenFlag(cmd.Flags()) + flags.RegisterCompletionFuncsForTargetFlags(cmd, f, ioStreams, cmd.Flags()) + return cmd } diff --git a/pkg/cmd/target/shoot.go b/pkg/cmd/target/shoot.go index 56b81f30..8e41c13a 100644 --- a/pkg/cmd/target/shoot.go +++ b/pkg/cmd/target/shoot.go @@ -11,6 +11,7 @@ import ( "github.com/gardener/gardenctl-v2/internal/util" "github.com/gardener/gardenctl-v2/pkg/cmd/base" + "github.com/gardener/gardenctl-v2/pkg/flags" ) // NewCmdTargetShoot returns a new target shoot command. @@ -36,5 +37,10 @@ gardenctl target shoot my-shoot --garden my-garden --project my-project`, o.AddFlags(cmd.Flags()) + f.TargetFlags().AddGardenFlag(cmd.Flags()) + f.TargetFlags().AddProjectFlag(cmd.Flags()) + f.TargetFlags().AddSeedFlag(cmd.Flags()) + flags.RegisterCompletionFuncsForTargetFlags(cmd, f, ioStreams, cmd.Flags()) + return cmd } diff --git a/pkg/cmd/target/target.go b/pkg/cmd/target/target.go index 79e25dea..fe32a5d3 100644 --- a/pkg/cmd/target/target.go +++ b/pkg/cmd/target/target.go @@ -15,7 +15,6 @@ import ( "github.com/fatih/color" "github.com/spf13/cobra" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" "github.com/gardener/gardenctl-v2/internal/util" "github.com/gardener/gardenctl-v2/pkg/ac" @@ -52,10 +51,8 @@ gardenctl target value/that/matches/pattern --control-plane`, o.AddFlags(cmd.Flags()) - manager, err := f.Manager() - utilruntime.Must(err) - manager.TargetFlags().AddFlags(cmd.PersistentFlags()) - flags.RegisterCompletionFuncsForTargetFlags(cmd, f, ioStreams, cmd.PersistentFlags()) + f.TargetFlags().AddFlags(cmd.Flags()) + flags.RegisterCompletionFuncsForTargetFlags(cmd, f, ioStreams, cmd.Flags()) return cmd } diff --git a/pkg/flags/target.go b/pkg/flags/target.go index 48ff4601..995a34d9 100644 --- a/pkg/flags/target.go +++ b/pkg/flags/target.go @@ -18,11 +18,25 @@ import ( "github.com/gardener/gardenctl-v2/pkg/target" ) +// RegisterCompletionFuncsForTargetFlags registers the completion functions to a given cobra command +// for the target flags (--garden, --project, --seed and --shoot). Each completion function is only +// registered if the flag has been previously added to the provided flag set. func RegisterCompletionFuncsForTargetFlags(cmd *cobra.Command, factory util.Factory, ioStreams util.IOStreams, flags *pflag.FlagSet) { - utilruntime.Must(cmd.RegisterFlagCompletionFunc("garden", completionWrapper(factory, ioStreams, gardenFlagCompletionFunc))) - utilruntime.Must(cmd.RegisterFlagCompletionFunc("project", completionWrapper(factory, ioStreams, projectFlagCompletionFunc))) - utilruntime.Must(cmd.RegisterFlagCompletionFunc("seed", completionWrapper(factory, ioStreams, seedFlagCompletionFunc))) - utilruntime.Must(cmd.RegisterFlagCompletionFunc("shoot", completionWrapper(factory, ioStreams, shootFlagCompletionFunc))) + if cmd.Flag("garden") != nil { + utilruntime.Must(cmd.RegisterFlagCompletionFunc("garden", completionWrapper(factory, ioStreams, gardenFlagCompletionFunc))) + } + + if cmd.Flag("project") != nil { + utilruntime.Must(cmd.RegisterFlagCompletionFunc("project", completionWrapper(factory, ioStreams, projectFlagCompletionFunc))) + } + + if cmd.Flag("seed") != nil { + utilruntime.Must(cmd.RegisterFlagCompletionFunc("seed", completionWrapper(factory, ioStreams, seedFlagCompletionFunc))) + } + + if cmd.Flag("shoot") != nil { + utilruntime.Must(cmd.RegisterFlagCompletionFunc("shoot", completionWrapper(factory, ioStreams, shootFlagCompletionFunc))) + } } type ( diff --git a/pkg/flags/target_test.go b/pkg/flags/target_test.go index 6aace75d..f1e3d321 100644 --- a/pkg/flags/target_test.go +++ b/pkg/flags/target_test.go @@ -43,7 +43,6 @@ var _ = Describe("Target flags", func() { gardenName2 string streams util.IOStreams errOut *util.SafeBytesBuffer - targetFlags target.TargetFlags ) BeforeEach(func() { @@ -52,8 +51,6 @@ var _ = Describe("Target flags", func() { streams, _, _, errOut = util.NewTestIOStreams() - targetFlags = target.NewTargetFlags("", "", "", "", false) - targetProvider := target.NewTargetProvider(filepath.Join(sessionDir, "target.yaml"), nil) Expect(targetProvider.Write(target.NewTarget(gardenName1, projectName, "", shootName))).To(Succeed()) }) @@ -263,10 +260,7 @@ var _ = Describe("Target flags", func() { var factory *util.FactoryImpl BeforeEach(func() { - factory = &util.FactoryImpl{ - TargetFlags: targetFlags, - ConfigFile: configFile, - } + factory = util.NewFactoryImpl() }) Context("when wrapping completion functions", func() { @@ -312,7 +306,7 @@ var _ = Describe("Target flags", func() { args := []string{ fmt.Sprintf("--shoot=%s", shootName), "target", - "view", + "control-plane", } cmd := cmd.NewGardenctlCommand(factory, streams) @@ -327,7 +321,7 @@ var _ = Describe("Target flags", func() { // check target flags values tf := manager.TargetFlags() - Expect(tf).To(BeIdenticalTo(factory.TargetFlags)) + Expect(tf).To(BeIdenticalTo(factory.TargetFlags())) Expect(tf.GardenName()).To(BeEmpty()) Expect(tf.ProjectName()).To(BeEmpty()) Expect(tf.SeedName()).To(BeEmpty()) diff --git a/pkg/target/target_flags.go b/pkg/target/target_flags.go index 89ea94ac..1a5ffe36 100644 --- a/pkg/target/target_flags.go +++ b/pkg/target/target_flags.go @@ -27,8 +27,20 @@ type TargetFlags interface { ShootName() string // ControlPlane returns the value that is tied to the corresponding cobra flag. ControlPlane() bool - // AddFlags binds target configuration flags to a given flagset + + // AddFlags binds all target configuration flags to a given flagset AddFlags(flags *pflag.FlagSet) + // AddGardenFlag adds the garden flag to the provided flag set + AddGardenFlag(flags *pflag.FlagSet) + // AddProjectFlag adds the project flag to the provided flag set + AddProjectFlag(flags *pflag.FlagSet) + // AddSeedFlag adds the seed flag to the provided flag set + AddSeedFlag(flags *pflag.FlagSet) + // AddShootFlag adds the shoot flag to the provided flag set + AddShootFlag(flags *pflag.FlagSet) + // AddControlPlaneFlag adds the control-plane flag to the provided flag set + AddControlPlaneFlag(flags *pflag.FlagSet) + // ToTarget converts the flags to a target ToTarget() Target // IsTargetValid returns true if the set of given CLI flags is enough @@ -80,10 +92,30 @@ func (tf *targetFlagsImpl) ControlPlane() bool { } func (tf *targetFlagsImpl) AddFlags(flags *pflag.FlagSet) { + tf.AddGardenFlag(flags) + tf.AddProjectFlag(flags) + tf.AddSeedFlag(flags) + tf.AddShootFlag(flags) + tf.AddControlPlaneFlag(flags) +} + +func (tf *targetFlagsImpl) AddGardenFlag(flags *pflag.FlagSet) { flags.StringVar(&tf.gardenName, "garden", "", "target the given garden cluster") +} + +func (tf *targetFlagsImpl) AddProjectFlag(flags *pflag.FlagSet) { flags.StringVar(&tf.projectName, "project", "", "target the given project") +} + +func (tf *targetFlagsImpl) AddSeedFlag(flags *pflag.FlagSet) { flags.StringVar(&tf.seedName, "seed", "", "target the given seed cluster") +} + +func (tf *targetFlagsImpl) AddShootFlag(flags *pflag.FlagSet) { flags.StringVar(&tf.shootName, "shoot", "", "target the given shoot cluster") +} + +func (tf *targetFlagsImpl) AddControlPlaneFlag(flags *pflag.FlagSet) { flags.BoolVar(&tf.controlPlane, "control-plane", tf.controlPlane, "target control plane of shoot, use together with shoot argument") }