github.com/oam-dev/kubevela@v1.9.11/pkg/cmd/builder.go (about)

     1  /*
     2  Copyright 2022 The KubeVela Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package cmd
    18  
    19  import (
    20  	"github.com/spf13/cobra"
    21  	"k8s.io/kubectl/pkg/util/term"
    22  
    23  	"github.com/oam-dev/kubevela/apis/types"
    24  	cmdutil "github.com/oam-dev/kubevela/pkg/cmd/util"
    25  	"github.com/oam-dev/kubevela/pkg/utils/util"
    26  )
    27  
    28  // Builder build command with factory
    29  type Builder struct {
    30  	cmd *cobra.Command
    31  	f   Factory
    32  }
    33  
    34  // NamespaceFlagConfig config for namespace flag in cmd
    35  type NamespaceFlagConfig struct {
    36  	completion bool
    37  	usage      string
    38  	loadEnv    bool
    39  }
    40  
    41  // NamespaceFlagOption the option for configuring namespace flag in cmd
    42  type NamespaceFlagOption interface {
    43  	ApplyToNamespaceFlagOptions(*NamespaceFlagConfig)
    44  }
    45  
    46  func newNamespaceFlagOptions(options ...NamespaceFlagOption) NamespaceFlagConfig {
    47  	cfg := NamespaceFlagConfig{
    48  		completion: true,
    49  		usage:      usageNamespace,
    50  		loadEnv:    true,
    51  	}
    52  	for _, option := range options {
    53  		option.ApplyToNamespaceFlagOptions(&cfg)
    54  	}
    55  	return cfg
    56  }
    57  
    58  // ClusterFlagConfig config for cluster flag in cmd
    59  type ClusterFlagConfig struct {
    60  	completion        bool
    61  	usage             string
    62  	disableSliceInput bool
    63  }
    64  
    65  // ClusterFlagOption the option for configuring cluster flag
    66  type ClusterFlagOption interface {
    67  	ApplyToClusterFlagOptions(*ClusterFlagConfig)
    68  }
    69  
    70  func newClusterFlagOptions(options ...ClusterFlagOption) ClusterFlagConfig {
    71  	cfg := ClusterFlagConfig{
    72  		completion:        true,
    73  		usage:             usageCluster,
    74  		disableSliceInput: false,
    75  	}
    76  	for _, option := range options {
    77  		option.ApplyToClusterFlagOptions(&cfg)
    78  	}
    79  	return cfg
    80  }
    81  
    82  // FlagNoCompletionOption disable auto-completion for flag
    83  type FlagNoCompletionOption struct{}
    84  
    85  // ApplyToNamespaceFlagOptions .
    86  func (option FlagNoCompletionOption) ApplyToNamespaceFlagOptions(cfg *NamespaceFlagConfig) {
    87  	cfg.completion = false
    88  }
    89  
    90  // ApplyToClusterFlagOptions .
    91  func (option FlagNoCompletionOption) ApplyToClusterFlagOptions(cfg *ClusterFlagConfig) {
    92  	cfg.completion = false
    93  }
    94  
    95  // UsageOption the usage description for flag
    96  type UsageOption string
    97  
    98  // ApplyToNamespaceFlagOptions .
    99  func (option UsageOption) ApplyToNamespaceFlagOptions(cfg *NamespaceFlagConfig) {
   100  	cfg.usage = string(option)
   101  }
   102  
   103  // ApplyToClusterFlagOptions .
   104  func (option UsageOption) ApplyToClusterFlagOptions(cfg *ClusterFlagConfig) {
   105  	cfg.usage = string(option)
   106  }
   107  
   108  // NamespaceFlagDisableEnvOption disable loading namespace from env
   109  type NamespaceFlagDisableEnvOption struct{}
   110  
   111  // ApplyToNamespaceFlagOptions .
   112  func (option NamespaceFlagDisableEnvOption) ApplyToNamespaceFlagOptions(cfg *NamespaceFlagConfig) {
   113  	cfg.loadEnv = false
   114  }
   115  
   116  // WithNamespaceFlag add namespace flag to the command, by default, it will also add env flag to the command
   117  func (builder *Builder) WithNamespaceFlag(options ...NamespaceFlagOption) *Builder {
   118  	cfg := newNamespaceFlagOptions(options...)
   119  	builder.cmd.Flags().StringP(flagNamespace, "n", "", cfg.usage)
   120  	if cfg.completion {
   121  		cmdutil.CheckErr(builder.cmd.RegisterFlagCompletionFunc(
   122  			flagNamespace,
   123  			func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
   124  				return GetNamespacesForCompletion(cmd.Context(), builder.f, toComplete)
   125  			}))
   126  	}
   127  	if cfg.loadEnv {
   128  		return builder.WithEnvFlag()
   129  	}
   130  	return builder
   131  }
   132  
   133  // WithEnvFlag add env flag to the command
   134  func (builder *Builder) WithEnvFlag() *Builder {
   135  	builder.cmd.PersistentFlags().StringP(flagEnv, "e", "", usageEnv)
   136  	return builder
   137  }
   138  
   139  // ClusterFlagDisableSliceInputOption set the cluster flag to allow multiple input
   140  type ClusterFlagDisableSliceInputOption struct{}
   141  
   142  // ApplyToClusterFlagOptions .
   143  func (option ClusterFlagDisableSliceInputOption) ApplyToClusterFlagOptions(cfg *ClusterFlagConfig) {
   144  	cfg.disableSliceInput = true
   145  }
   146  
   147  // WithClusterFlag add cluster flag to the command
   148  func (builder *Builder) WithClusterFlag(options ...ClusterFlagOption) *Builder {
   149  	cfg := newClusterFlagOptions(options...)
   150  	if cfg.disableSliceInput {
   151  		builder.cmd.Flags().StringP(flagCluster, "c", types.ClusterLocalName, cfg.usage)
   152  	} else {
   153  		builder.cmd.Flags().StringSliceP(flagCluster, "c", []string{types.ClusterLocalName}, cfg.usage)
   154  	}
   155  	if cfg.completion {
   156  		cmdutil.CheckErr(builder.cmd.RegisterFlagCompletionFunc(
   157  			flagCluster,
   158  			func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
   159  				return GetClustersForCompletion(cmd.Context(), builder.f, toComplete)
   160  			}))
   161  	}
   162  	return builder
   163  }
   164  
   165  // WithStreams set the in/out/err streams for the command
   166  func (builder *Builder) WithStreams(streams util.IOStreams) *Builder {
   167  	builder.cmd.SetIn(streams.In)
   168  	builder.cmd.SetOut(streams.Out)
   169  	builder.cmd.SetErr(streams.ErrOut)
   170  	return builder
   171  }
   172  
   173  // WithResponsiveWriter format the command outputs
   174  func (builder *Builder) WithResponsiveWriter() *Builder {
   175  	builder.cmd.SetOut(term.NewResponsiveWriter(builder.cmd.OutOrStdout()))
   176  	return builder
   177  }
   178  
   179  // Build construct the command
   180  func (builder *Builder) Build() *cobra.Command {
   181  	return builder.cmd
   182  }
   183  
   184  // NewCommandBuilder builder for command
   185  func NewCommandBuilder(f Factory, cmd *cobra.Command) *Builder {
   186  	return &Builder{cmd: cmd, f: f}
   187  }