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
 }
 

Reply via email to