This is an automated email from the ASF dual-hosted git repository. tsato pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-k.git
commit 57b4c519555669cb0328eb88614d62c5c8bde4e1 Author: Tadayoshi Sato <[email protected]> AuthorDate: Fri Jul 29 15:16:06 2022 +0900 fix(cli): more user-friendly error messages for kamel local subcommands Fix #3028 --- pkg/cmd/local.go | 15 +++++++- pkg/cmd/local_build.go | 97 +++++++++++++++++++++++++++++++----------------- pkg/cmd/local_inspect.go | 6 +-- pkg/cmd/local_run.go | 48 ++++++++++++------------ pkg/cmd/local_util.go | 7 ++-- 5 files changed, 105 insertions(+), 68 deletions(-) diff --git a/pkg/cmd/local.go b/pkg/cmd/local.go index 65163bfa5..c5616d5ed 100644 --- a/pkg/cmd/local.go +++ b/pkg/cmd/local.go @@ -24,6 +24,14 @@ import ( "github.com/spf13/cobra" ) +// Usage descritions of common flags that are shared across some of the subcommands. +const ( + usageImage = `Full path to integration image including registry, e.g. "docker.io/user/app"` + usageIntegrationDirectory = "Directory to hold local integration files" + usagePropertyFile = "Add a property file to the integration" + usageProperty = "Add a Camel property to the integration" +) + // newCmdLocal -- Add local kamel subcommand with several other subcommands of its own. func newCmdLocal(rootCmdOptions *RootCmdOptions) (*cobra.Command, *LocalCmdOptions) { options := LocalCmdOptions{ @@ -41,6 +49,8 @@ func newCmdLocal(rootCmdOptions *RootCmdOptions) (*cobra.Command, *LocalCmdOptio } cmd.PersistentFlags().StringArrayVarP(&options.Dependencies, "dependency", "d", nil, usageDependency) + cmd.PersistentFlags().StringArrayVar(&options.MavenRepositories, "maven-repository", nil, + "Use a maven repository") // hidden flags for compatibility with kamel run cmd.PersistentFlags().StringArrayVarP(&options.Traits, "trait", "t", nil, "") @@ -57,8 +67,9 @@ func newCmdLocal(rootCmdOptions *RootCmdOptions) (*cobra.Command, *LocalCmdOptio type LocalCmdOptions struct { *RootCmdOptions - Dependencies []string `mapstructure:"dependencies"` - Traits []string `mapstructure:"traits"` + Dependencies []string `mapstructure:"dependencies"` + MavenRepositories []string `mapstructure:"maven-repositories"` + Traits []string `mapstructure:"traits"` } func (o *LocalCmdOptions) persistentPreRun(cmd *cobra.Command, args []string) error { diff --git a/pkg/cmd/local_build.go b/pkg/cmd/local_build.go index fba6d4d82..cf36c9d85 100644 --- a/pkg/cmd/local_build.go +++ b/pkg/cmd/local_build.go @@ -37,7 +37,7 @@ func newCmdLocalBuild(localCmdOptions *LocalCmdOptions) (*cobra.Command, *localB Long: `Build integration images locally for containerized integrations.`, PreRunE: decode(&options), RunE: func(cmd *cobra.Command, args []string) error { - if err := options.validate(args); err != nil { + if err := options.validate(cmd, args); err != nil { return err } if err := options.init(args); err != nil { @@ -57,16 +57,15 @@ func newCmdLocalBuild(localCmdOptions *LocalCmdOptions) (*cobra.Command, *localB }, } - cmd.Flags().Bool("base-image", false, "Build base image used as a starting point for any integration.") + cmd.Flags().Bool("base-image", false, "Build base image used as a starting point for any integration") cmd.Flags().Bool("dependencies-only", false, "Only output the integration dependencies. The integration-directory flag must be set.") cmd.Flags().String("container-registry", "", "Registry that holds intermediate images. This flag should only be used in conjunction with the base-image flag.") - cmd.Flags().String("image", "", "Full path to integration image including registry.") - cmd.Flags().String("integration-directory", "", "Directory to hold local integration files.") - cmd.Flags().StringArray("property-file", nil, "Add a property file to the integration.") - cmd.Flags().StringArrayP("property", "p", nil, "Add a Camel property to the integration.") - cmd.Flags().StringArray("maven-repository", nil, "Use a maven repository") + cmd.Flags().String("image", "", usageImage) + cmd.Flags().String("integration-directory", "", usageIntegrationDirectory) + cmd.Flags().StringArray("property-file", nil, usagePropertyFile) + cmd.Flags().StringArrayP("property", "p", nil, usageProperty) return &cmd, &options } @@ -80,50 +79,78 @@ type localBuildCmdOptions struct { IntegrationDirectory string `mapstructure:"integration-directory"` Properties []string `mapstructure:"properties"` PropertyFiles []string `mapstructure:"property-files"` - MavenRepositories []string `mapstructure:"maven-repositories"` } -func (o *localBuildCmdOptions) validate(args []string) error { - // Validate integration files. +func (o *localBuildCmdOptions) validate(cmd *cobra.Command, args []string) error { + if o.BaseImage { + return o.validateBaseImageMode(cmd, args) + } + + return o.validateIntegrationMode(args) +} + +func (o *localBuildCmdOptions) validateBaseImageMode(cmd *cobra.Command, args []string) error { + // Cannot have both integration files and the base image construction enabled. if len(args) > 0 { - if err := validateFiles(args); err != nil { - return err - } + return errors.New("cannot use --base-image with integration files") } - // Validate additional dependencies specified by the user. - if err := validateDependencies(o.Dependencies); err != nil { - return err + // Docker registry must be set. + if o.ContainerRegistry == "" { + return errors.New("--base-image requires --container-registry") } - // Validate properties file. - if err := validateFiles(o.PropertyFiles); err != nil { - return err + // If an integration directory is provided then no base image containerization can be enabled. + if o.IntegrationDirectory != "" { + return errors.New("cannot use --integration-directory with --base-image") } - if o.BaseImage { - // Cannot have both integration files and the base image construction enabled. - if len(args) > 0 { - return errors.New("integration files have been provided and the base image construction is enabled") - } + if o.DependenciesOnly { + return errors.New("cannot use --dependencies-only with --base-image") + } - // Docker registry must be set. - if o.ContainerRegistry == "" { - return errors.New("base image cannot be built because container registry has not been provided") - } + if len(o.Dependencies) > 0 || len(o.PropertyFiles) > 0 || len(o.Properties) > 0 { + fmt.Fprintln(cmd.OutOrStdout(), + "Warning: --dependency, --property, and --property-file are ignored in --base-image mode") + } - // If an integration directory is provided then no base image containerization can be enabled. - if o.IntegrationDirectory != "" { - return errors.New("base image construction does not use integration files") + return nil +} + +func (o *localBuildCmdOptions) validateIntegrationMode(args []string) error { + if len(args) == 0 { + if o.IntegrationDirectory == "" { + return errors.New("either integration files, --integration-directory, or --base-image must be provided") } - } else if o.ContainerRegistry != "" { + } else { + if o.IntegrationDirectory == "" && o.Image == "" { + return errors.New("either --integration-directory or --image must be provided with integration files") + } + } + + if o.ContainerRegistry != "" { // ContainerRegistry should only be specified when building the base image. - return errors.New("cannot specify container registry unless a base integration image is being built") + return errors.New("--container-registry must be used with --base-image") } // The integration directory must be set when only outputting dependencies. if o.DependenciesOnly && o.IntegrationDirectory == "" { - return errors.New("to output dependencies the integration directory flag must be set") + return errors.New("--dependencies-only requires --integration-directory") + } + + // Validate integration files. + if err := validateFiles(args); err != nil { + return err + } + + // Validate additional dependencies specified by the user. + if err := validateDependencies(o.Dependencies); err != nil { + return err + } + + // Validate properties file. + if err := validateFiles(o.PropertyFiles); err != nil { + return err } return nil @@ -206,6 +233,8 @@ func (o *localBuildCmdOptions) run(cmd *cobra.Command, args []string) error { // The only case in which we should not execute the integration image creation is when we want to // just output the files that comprise the integration locally. if o.Image == "" { + fmt.Fprintf(cmd.OutOrStdout(), + "Integration directory generated: %s\n", o.IntegrationDirectory) return nil } } diff --git a/pkg/cmd/local_inspect.go b/pkg/cmd/local_inspect.go index e5df5bd7b..22c6aefb9 100644 --- a/pkg/cmd/local_inspect.go +++ b/pkg/cmd/local_inspect.go @@ -59,16 +59,14 @@ will be generated by calling Maven and then printed in the selected output forma cmd.Flags().Bool("all-dependencies", false, "Enable computation of transitive dependencies.") cmd.Flags().StringP("output", "o", "", "Output format. One of: json|yaml") - cmd.Flags().StringArray("maven-repository", nil, "Use a maven repository") return &cmd, &options } type localInspectCmdOptions struct { *LocalCmdOptions - AllDependencies bool `mapstructure:"all-dependencies"` - OutputFormat string `mapstructure:"output"` - MavenRepositories []string `mapstructure:"maven-repositories"` + AllDependencies bool `mapstructure:"all-dependencies"` + OutputFormat string `mapstructure:"output"` } func (o *localInspectCmdOptions) validate(args []string) error { diff --git a/pkg/cmd/local_run.go b/pkg/cmd/local_run.go index 74027a11c..467c35076 100644 --- a/pkg/cmd/local_run.go +++ b/pkg/cmd/local_run.go @@ -23,6 +23,7 @@ import ( "os/signal" "syscall" + "github.com/apache/camel-k/pkg/util" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -72,14 +73,12 @@ func newCmdLocalRun(localCmdOptions *LocalCmdOptions) (*cobra.Command, *localRun } cmd.Flags().Bool("containerize", false, "Run integration in a local container.") - cmd.Flags().String("image", "", "Full path to integration image including registry.") + cmd.Flags().String("image", "", usageImage) cmd.Flags().String("network", "", "Custom network name to be used by the underlying Docker command.") - cmd.Flags().String("integration-directory", "", - "Directory which holds the locally built integration and is the result of a local build action.") + cmd.Flags().String("integration-directory", "", usageIntegrationDirectory) cmd.Flags().StringArrayP("env", "e", nil, "Flag to specify an environment variable [--env VARIABLE=value].") - cmd.Flags().StringArray("property-file", nil, "Add a property file to the integration.") - cmd.Flags().StringArrayP("property", "p", nil, "Add a Camel property to the integration.") - cmd.Flags().StringArray("maven-repository", nil, "Use a maven repository") + cmd.Flags().StringArray("property-file", nil, usagePropertyFile) + cmd.Flags().StringArrayP("property", "p", nil, usageProperty) return &cmd, &options } @@ -93,21 +92,21 @@ type localRunCmdOptions struct { EnvironmentVariables []string `mapstructure:"envs"` PropertyFiles []string `mapstructure:"property-files"` Properties []string `mapstructure:"properties"` - MavenRepositories []string `mapstructure:"maven-repositories"` } func (o *localRunCmdOptions) validate(args []string) error { - // Validate integration files when no image is provided and we are - // not running an already locally-built integration. - if o.Image == "" && o.IntegrationDirectory == "" { - if len(args) == 0 { - return errors.New("no integration files have been provided") - } + if len(args) == 0 && o.IntegrationDirectory == "" && o.Image == "" { + return errors.New("either integration files, --image, or --integration-directory must be provided") + } - // Validate integration files. - if err := validateFiles(args); err != nil { - return err - } + // If containerize is set then docker image name must be set. + if o.Containerize && o.Image == "" { + return errors.New("--containerize requires --image") + } + + // Validate integration files. + if err := validateFiles(args); err != nil { + return err } // Validate additional dependencies specified by the user. @@ -120,9 +119,12 @@ func (o *localRunCmdOptions) validate(args []string) error { return err } - // If containerize is set then docker image name must be set. - if o.Containerize && o.Image == "" { - return errors.New("containerization is active but no image name has been provided") + if o.IntegrationDirectory != "" { + if ok, err := util.DirectoryExists(o.IntegrationDirectory); err != nil { + return err + } else if !ok { + return errors.Errorf("integration directory %q does not exist", o.IntegrationDirectory) + } } return nil @@ -133,14 +135,11 @@ func (o *localRunCmdOptions) init() error { if err := createDockerBaseWorkingDirectory(); err != nil { return err } - if err := createDockerWorkingDirectory(); err != nil { return err } } - setDockerNetworkName(o.Network) - setDockerEnvVars(o.EnvironmentVariables) return createMavenWorkingDirectory() @@ -167,14 +166,13 @@ func (o *localRunCmdOptions) run(cmd *cobra.Command, args []string) error { } if o.Containerize { - // If this is a containerized local run, create, build and run the container image. + // Create, build, and run the container image. if err := createAndBuildIntegrationImage(o.Context, "", false, o.Image, propertyFiles, dependencies, routes, o.IntegrationDirectory != "", cmd.OutOrStdout(), cmd.ErrOrStderr()); err != nil { return err } - // Run integration image. return runIntegrationImage(o.Context, o.Image, cmd.OutOrStdout(), cmd.ErrOrStderr()) } diff --git a/pkg/cmd/local_util.go b/pkg/cmd/local_util.go index 166489a7a..bc51bd674 100644 --- a/pkg/cmd/local_util.go +++ b/pkg/cmd/local_util.go @@ -50,13 +50,13 @@ func getDependencies(ctx context.Context, args []string, additionalDependencies // Fetch existing catalog or create new one if one does not already exist catalog, err := createCamelCatalog(ctx) if err != nil { - return nil, err + return nil, errors.Wrap(err, "failed to create Camel catalog") } // Get top-level dependencies dependencies, err := getTopLevelDependencies(ctx, catalog, args) if err != nil { - return nil, err + return nil, errors.Wrap(err, "failed to get top-level dependencies") } // Add additional user-provided dependencies @@ -72,9 +72,10 @@ func getDependencies(ctx context.Context, args []string, additionalDependencies dependencies, err = getTransitiveDependencies(ctx, catalog, dependencies, repositories) if err != nil { - return nil, err + return nil, errors.Wrap(err, "failed to compute transitive dependencies") } } + return dependencies, nil }
