Add a Devfile dependency for commands (#7063)

* Define Devfile dependency for commands

* Remove MarkDevfileNotNeeded

* More commands not using Devfile

* Tests with invalid devfiles
This commit is contained in:
Philippe Martin
2023-09-04 18:12:34 +02:00
committed by GitHub
parent 03c109a6e2
commit a65812143c
22 changed files with 135 additions and 38 deletions

View File

@@ -35,6 +35,10 @@ func (o *AlizerOptions) SetClientset(clientset *clientset.Clientset) {
o.clientset = clientset
}
func (o *AlizerOptions) UseDevfile(ctx context.Context, cmdline cmdline.Cmdline, args []string) bool {
return false
}
func (o *AlizerOptions) Complete(ctx context.Context, cmdline cmdline.Cmdline, args []string) (err error) {
return nil
}
@@ -80,7 +84,6 @@ func NewCmdAlizer(name, fullName string, testClientset clientset.Clientset) *cob
}
clientset.Add(alizerCmd, clientset.ALIZER, clientset.FILESYSTEM)
util.SetCommandGroup(alizerCmd, util.UtilityGroup)
genericclioptions.MarkDevfileNotNeeded(alizerCmd)
commonflags.UseOutputFlag(alizerCmd)
alizerCmd.SetUsageTemplate(odoutil.CmdUsageTemplate)
return alizerCmd

View File

@@ -84,6 +84,10 @@ func (o *ComponentOptions) SetClientset(clientset *clientset.Clientset) {
o.clientset = clientset
}
func (o *ComponentOptions) UseDevfile(ctx context.Context, cmdline cmdline.Cmdline, args []string) bool {
return o.name == ""
}
func (o *ComponentOptions) Complete(ctx context.Context, cmdline cmdline.Cmdline, args []string) (err error) {
switch api.RunningMode(o.runningInFlag) {
case api.RunningModeDev:

View File

@@ -51,6 +51,10 @@ func (o *BindingOptions) SetClientset(clientset *clientset.Clientset) {
o.clientset = clientset
}
func (o *BindingOptions) UseDevfile(ctx context.Context, cmdline cmdline.Cmdline, args []string) bool {
return o.nameFlag == ""
}
func (o *BindingOptions) Complete(ctx context.Context, cmdline cmdline.Cmdline, args []string) (err error) {
if o.nameFlag == "" {
devfileObj := odocontext.GetEffectiveDevfileObj(ctx)

View File

@@ -61,6 +61,10 @@ func (o *ComponentOptions) SetClientset(clientset *clientset.Clientset) {
o.clientset = clientset
}
func (o *ComponentOptions) UseDevfile(ctx context.Context, cmdline cmdline.Cmdline, args []string) bool {
return o.nameFlag == ""
}
func (o *ComponentOptions) Complete(ctx context.Context, cmdline cmdline.Cmdline, args []string) (err error) {
platform := fcontext.GetPlatform(ctx, commonflags.PlatformCluster)
@@ -329,7 +333,7 @@ func NewCmdComponent(ctx context.Context, name, fullName string, testClientset c
}
componentCmd.Flags().StringVar(&o.nameFlag, "name", "", "Name of the component to describe, optional. By default, the component in the local devfile is described")
componentCmd.Flags().StringVar(&o.namespaceFlag, "namespace", "", "Namespace in which to find the component to describe, optional. By default, the current namespace defined in kubeconfig is used")
clientset.Add(componentCmd, clientset.KUBERNETES_NULLABLE, clientset.STATE)
clientset.Add(componentCmd, clientset.KUBERNETES_NULLABLE, clientset.STATE, clientset.FILESYSTEM)
if feature.IsEnabled(ctx, feature.GenericPlatformFlag) {
clientset.Add(componentCmd, clientset.PODMAN_NULLABLE)
}

View File

@@ -88,6 +88,10 @@ func (o *InitOptions) SetClientset(clientset *clientset.Clientset) {
o.clientset = clientset
}
func (o *InitOptions) UseDevfile(ctx context.Context, cmdline cmdline.Cmdline, args []string) bool {
return false
}
// Complete will build the parameters for init, using different backends based on the flags set,
// either by using flags or interactively if no flag is passed
// Complete will return an error immediately if the current working directory is not empty

View File

@@ -56,6 +56,10 @@ func (o *ServiceListOptions) SetClientset(clientset *clientset.Clientset) {
o.clientset = clientset
}
func (o *ServiceListOptions) UseDevfile(ctx context.Context, cmdline cmdline.Cmdline, args []string) bool {
return false
}
func (o *ServiceListOptions) Complete(ctx context.Context, cmdline cmdline.Cmdline, _ []string) error {
if o.namespaceFlag == "" && !o.allNamespacesFlag {
o.namespaceFlag = odocontext.GetNamespace(ctx)

View File

@@ -58,6 +58,10 @@ func (o *RegistryOptions) SetClientset(clientset *clientset.Clientset) {
o.clientset = clientset
}
func (o *RegistryOptions) UseDevfile(ctx context.Context, cmdline cmdline.Cmdline, args []string) bool {
return false
}
// Complete completes RegistryOptions after they've been created
func (o *RegistryOptions) Complete(ctx context.Context, cmdline cmdline.Cmdline, args []string) (err error) {
o.operation = "add"

View File

@@ -56,6 +56,10 @@ func (o *RegistryOptions) SetClientset(clientset *clientset.Clientset) {
o.clientset = clientset
}
func (o *RegistryOptions) UseDevfile(ctx context.Context, cmdline cmdline.Cmdline, args []string) bool {
return false
}
// Complete completes RegistryOptions after they've been created
func (o *RegistryOptions) Complete(ctx context.Context, cmdline cmdline.Cmdline, args []string) (err error) {
o.operation = "remove"

View File

@@ -53,6 +53,10 @@ func (o *SetOptions) SetClientset(clientset *clientset.Clientset) {
o.clientset = clientset
}
func (o *SetOptions) UseDevfile(ctx context.Context, cmdline cmdline.Cmdline, args []string) bool {
return false
}
// Complete completes SetOptions after they've been created
func (o *SetOptions) Complete(ctx context.Context, cmdline cmdline.Cmdline, args []string) (err error) {
o.paramName = strings.ToLower(args[0])

View File

@@ -52,6 +52,10 @@ func (o *UnsetOptions) SetClientset(clientset *clientset.Clientset) {
o.clientset = clientset
}
func (o *UnsetOptions) UseDevfile(ctx context.Context, cmdline cmdline.Cmdline, args []string) bool {
return false
}
// Complete completes UnsetOptions after they've been created
func (o *UnsetOptions) Complete(ctx context.Context, cmdline cmdline.Cmdline, args []string) (err error) {
o.paramName = strings.ToLower(args[0])

View File

@@ -41,6 +41,10 @@ func (o *ViewOptions) SetClientset(clientset *clientset.Clientset) {
o.clientset = clientset
}
func (o *ViewOptions) UseDevfile(ctx context.Context, cmdline cmdline.Cmdline, args []string) bool {
return false
}
// Complete completes ViewOptions after they've been created
func (o *ViewOptions) Complete(ctx context.Context, cmdline cmdline.Cmdline, args []string) (err error) {
return

View File

@@ -67,6 +67,10 @@ func (o *ListOptions) SetClientset(clientset *clientset.Clientset) {
o.clientset = clientset
}
func (o *ListOptions) UseDevfile(ctx context.Context, cmdline cmdline.Cmdline, args []string) bool {
return false
}
// Complete completes ListOptions after they've been created
func (o *ListOptions) Complete(ctx context.Context, cmdline cmdline.Cmdline, args []string) (err error) {

View File

@@ -56,6 +56,10 @@ func (so *SetOptions) SetClientset(clientset *clientset.Clientset) {
so.clientset = clientset
}
func (o *SetOptions) UseDevfile(ctx context.Context, cmdline cmdline.Cmdline, args []string) bool {
return false
}
// Complete completes SetOptions after they've been created
func (so *SetOptions) Complete(ctx context.Context, cmdline cmdline.Cmdline, args []string) (err error) {
so.namespaceName = args[0]

View File

@@ -15,17 +15,6 @@ import (
odoutil "github.com/redhat-developer/odo/pkg/util"
)
// MarkDevfileNotNeeded annotates the provided command such that it does not require a valid Devfile
// to be present in the current directory.
// A corollary to this is that commands annotated as such will not have any Devfile parsed in their root context,
// even if there is a local "devfile.yaml" in the current directory.
func MarkDevfileNotNeeded(cmd *cobra.Command) {
if cmd.Annotations == nil {
cmd.Annotations = map[string]string{}
}
cmd.Annotations["devfile-not-needed"] = "true"
}
func getDevfileInfo(cmd *cobra.Command, fsys filesystem.Filesystem, workingDir string, variables map[string]string, imageRegistry string) (
devfilePath string,
devfileObj *parser.DevfileObj,
@@ -34,8 +23,7 @@ func getDevfileInfo(cmd *cobra.Command, fsys filesystem.Filesystem, workingDir s
) {
devfilePath = location.DevfileLocation(fsys, workingDir)
isDevfile := odoutil.CheckPathExists(fsys, devfilePath)
requiresValidDevfile := cmd.Annotations["devfile-not-needed"] != "true"
if requiresValidDevfile && isDevfile {
if isDevfile {
devfilePath, err = dfutil.GetAbsPath(devfilePath)
if err != nil {
return "", nil, "", err

View File

@@ -71,6 +71,14 @@ type JsonOutputter interface {
RunForJsonOutput(ctx context.Context) (result interface{}, err error)
}
// DevfileUser must be implemented by commands that use Devfile depending on arguments, or
// commands that depend on FS but do not use Devfile.
// If the interface is not implemented and the command depends on FS, the command is expected to use Devfile
type DevfileUser interface {
// UseDevfile returns true if the command with the specified cmdline and args needs to have access to the Devfile
UseDevfile(ctx context.Context, cmdline cmdline.Cmdline, args []string) bool
}
const (
// defaultAppName is the default name of the application when an application name is not provided
defaultAppName = "app"
@@ -248,6 +256,12 @@ func GenericRun(o Runnable, testClientset clientset.Clientset, cmd *cobra.Comman
}
}
useDevfile := true
if devfileUser, ok := o.(DevfileUser); ok {
useDevfile = devfileUser.UseDevfile(ctx, cmdLineObj, args)
}
if useDevfile {
var devfilePath, componentName string
var devfileObj *parser.DevfileObj
devfilePath, devfileObj, componentName, err = getDevfileInfo(cmd, deps.FS, cwd, variables, userConfig.GetImageRegistry())
@@ -259,6 +273,7 @@ func GenericRun(o Runnable, testClientset clientset.Clientset, cmd *cobra.Comman
ctx = odocontext.WithEffectiveDevfileObj(ctx, devfileObj)
ctx = odocontext.WithComponentName(ctx, componentName)
}
}
// Run completion, validation and run.
// Only upload data to segment for completion and validation if a non-nil error is returned.

View File

@@ -319,3 +319,15 @@ func AppendToFile(filepath string, s string) error {
}
return nil
}
func CreateInvalidDevfile(dir string) {
devfilePath := filepath.Join(dir, "devfile.yaml")
err := CreateFileWithContent(devfilePath, "invalid")
Expect(err).ToNot(HaveOccurred())
}
func DeleteInvalidDevfile(dir string) {
devfilePath := filepath.Join(dir, "devfile.yaml")
err := os.Remove(devfilePath)
Expect(err).ToNot(HaveOccurred())
}

View File

@@ -149,6 +149,8 @@ var _ = Describe("odo delete command tests", func() {
Eventually(string(commonVar.CliRunner.Run(getSVCArgs...).Out.Contents()), 60, 3).ShouldNot(ContainSubstring(serviceName))
})
It("should output that there are no resources to be deleted", func() {
helper.CreateInvalidDevfile(commonVar.Context)
helper.Chdir(commonVar.Context)
args := []string{"delete", "component", "--name", cmpName, "--namespace", commonVar.Project}
if runningIn != "" {
args = append(args, "--running-in", runningIn)

View File

@@ -69,6 +69,7 @@ var _ = Describe("odo describe component command tests", func() {
It("should fail, with default cluster mode", func() {
By("running odo describe component -o json with an unknown name", func() {
helper.CreateInvalidDevfile(commonVar.Context)
res := helper.Cmd("odo", "describe", "component", "--name", "unknown-name", "-o", "json").ShouldFail()
stdout, stderr := res.Out(), res.Err()
Expect(helper.IsJSON(stderr)).To(BeTrue())
@@ -77,6 +78,7 @@ var _ = Describe("odo describe component command tests", func() {
})
By("running odo describe component with an unknown name", func() {
helper.CreateInvalidDevfile(commonVar.Context)
res := helper.Cmd("odo", "describe", "component", "--name", "unknown-name").ShouldFail()
stdout, stderr := res.Out(), res.Err()
Expect(stdout).To(BeEmpty())
@@ -86,6 +88,7 @@ var _ = Describe("odo describe component command tests", func() {
It("should fail, with cluster", func() {
By("running odo describe component -o json with an unknown name", func() {
helper.CreateInvalidDevfile(commonVar.Context)
res := helper.Cmd("odo", "describe", "component", "--name", "unknown-name", "--platform", "cluster", "-o", "json").ShouldFail()
stdout, stderr := res.Out(), res.Err()
Expect(helper.IsJSON(stderr)).To(BeTrue())
@@ -94,6 +97,7 @@ var _ = Describe("odo describe component command tests", func() {
})
By("running odo describe component with an unknown name", func() {
helper.CreateInvalidDevfile(commonVar.Context)
res := helper.Cmd("odo", "describe", "component", "--name", "unknown-name", "--platform", "cluster").ShouldFail()
stdout, stderr := res.Out(), res.Err()
Expect(stdout).To(BeEmpty())
@@ -103,6 +107,7 @@ var _ = Describe("odo describe component command tests", func() {
It("should fail, with podman", Label(helper.LabelPodman), func() {
By("running odo describe component -o json with an unknown name", func() {
helper.CreateInvalidDevfile(commonVar.Context)
res := helper.Cmd("odo", "describe", "component", "--name", "unknown-name", "--platform", "podman", "-o", "json").
ShouldFail()
stdout, stderr := res.Out(), res.Err()
@@ -112,6 +117,7 @@ var _ = Describe("odo describe component command tests", func() {
})
By("running odo describe component with an unknown name", func() {
helper.CreateInvalidDevfile(commonVar.Context)
stderr := helper.Cmd("odo", "describe", "component", "--name", "unknown-name", "--platform", "podman").
ShouldFail().Err()
Expect(stderr).To(ContainSubstring("no component found with name \"unknown-name\""))
@@ -257,8 +263,10 @@ var _ = Describe("odo describe component command tests", func() {
It("should not describe the component from another directory, with default cluster mode", func() {
By("running with json output", func() {
err := os.Chdir("/")
Expect(err).NotTo(HaveOccurred())
otherDir := filepath.Join(commonVar.Context, "tmp")
helper.MakeDir(otherDir)
helper.Chdir(otherDir)
helper.CreateInvalidDevfile(otherDir)
res := helper.Cmd("odo", "describe", "component", "--name", cmpName, "-o", "json").ShouldFail()
stdout, stderr := res.Out(), res.Err()
Expect(helper.IsJSON(stderr)).To(BeTrue())
@@ -267,8 +275,10 @@ var _ = Describe("odo describe component command tests", func() {
})
By("running with default output", func() {
err := os.Chdir("/")
Expect(err).NotTo(HaveOccurred())
otherDir := filepath.Join(commonVar.Context, "tmp")
helper.MakeDir(otherDir)
helper.Chdir(otherDir)
helper.CreateInvalidDevfile(otherDir)
res := helper.Cmd("odo", "describe", "component", "--name", cmpName).ShouldFail()
stdout, stderr := res.Out(), res.Err()
Expect(stdout).To(BeEmpty())
@@ -278,8 +288,10 @@ var _ = Describe("odo describe component command tests", func() {
It("should not describe the component from another directory, with cluster", func() {
By("running with json output", func() {
err := os.Chdir("/")
Expect(err).NotTo(HaveOccurred())
otherDir := filepath.Join(commonVar.Context, "tmp")
helper.MakeDir(otherDir)
helper.Chdir(otherDir)
helper.CreateInvalidDevfile(otherDir)
res := helper.Cmd("odo", "describe", "component", "--name", cmpName, "-o", "json", "--platform", "cluster").ShouldFail()
stdout, stderr := res.Out(), res.Err()
Expect(helper.IsJSON(stderr)).To(BeTrue())
@@ -288,8 +300,10 @@ var _ = Describe("odo describe component command tests", func() {
})
By("running with default output", func() {
err := os.Chdir("/")
Expect(err).NotTo(HaveOccurred())
otherDir := filepath.Join(commonVar.Context, "tmp")
helper.MakeDir(otherDir)
helper.Chdir(otherDir)
helper.CreateInvalidDevfile(otherDir)
res := helper.Cmd("odo", "describe", "component", "--name", cmpName, "--platform", "cluster").ShouldFail()
stdout, stderr := res.Out(), res.Err()
Expect(stdout).To(BeEmpty())
@@ -547,10 +561,12 @@ var _ = Describe("odo describe component command tests", func() {
}
})
By("checking the human readable output with component name", func() {
helper.CreateInvalidDevfile(commonVar.Context)
out := helper.Cmd("odo", "describe", "component", "--name", componentName).ShouldPass().Out()
helper.MatchAllInOutput(out, ctx.matchOutput)
})
By("checking the machine readable output with component name", func() {
helper.CreateInvalidDevfile(commonVar.Context)
out := helper.Cmd("odo", "describe", "component", "--name", componentName, "-o", "json").ShouldPass().Out()
for key, value := range ctx.matchJSONOutput {
helper.JsonPathContentContain(out, key, value)

View File

@@ -737,9 +737,11 @@ var _ = Describe("odo describe/list binding command tests", func() {
ctx.assertDescribeAllHumanReadableOutput(stdout, stderr)
})
By("JSON output from another directory with name flag", func() {
err := os.Chdir("/")
Expect(err).ToNot(HaveOccurred())
By("JSON output from another directory with name flag and invalid devfile", func() {
otherDir := filepath.Join(commonVar.Context, "tmp")
helper.MakeDir(otherDir)
helper.Chdir(otherDir)
helper.CreateInvalidDevfile(otherDir)
res := helper.Cmd("odo", "describe", "binding", "--name", "my-nodejs-app-cluster-sample-k8s", "-o", "json").ShouldPass()
stdout, stderr := res.Out(), res.Err()
ctx.assertDescribeNamedJsonOutput(stdout, stderr, "my-nodejs-app-cluster-sample-k8s")
@@ -748,9 +750,11 @@ var _ = Describe("odo describe/list binding command tests", func() {
stdout, stderr = res.Out(), res.Err()
ctx.assertDescribeNamedJsonOutput(stdout, stderr, "my-nodejs-app-cluster-sample-ocp")
})
By("human readable output from another directory with name flag", func() {
err := os.Chdir("/")
Expect(err).ToNot(HaveOccurred())
By("human readable output from another directory with name flag and invalid devfile", func() {
otherDir := filepath.Join(commonVar.Context, "tmp")
helper.MakeDir(otherDir)
helper.Chdir(otherDir)
helper.CreateInvalidDevfile(otherDir)
res := helper.Cmd("odo", "describe", "binding", "--name", "my-nodejs-app-cluster-sample-k8s").ShouldPass()
stdout, stderr := res.Out(), res.Err()
ctx.assertDescribeNamedHumanReadableOutput(stdout, stderr, "my-nodejs-app-cluster-sample-k8s")

View File

@@ -20,6 +20,7 @@ var _ = Describe("odo devfile registry command tests", func() {
var _ = BeforeEach(func() {
commonVar = helper.CommonBeforeEach()
helper.Chdir(commonVar.Context)
helper.CreateInvalidDevfile(commonVar.Context)
})
// This is run after every Spec (It)
@@ -182,6 +183,7 @@ var _ = Describe("odo devfile registry command tests", func() {
})
It("should pass, when doing odo init with --devfile-registry flag", func() {
helper.DeleteInvalidDevfile(commonVar.Context)
helper.Cmd("odo", "init", "--name", "aname", "--devfile", "nodejs", "--devfile-registry", registryName).ShouldPass()
})
@@ -194,6 +196,7 @@ var _ = Describe("odo devfile registry command tests", func() {
})
It("deleting registry and creating component with registry flag ", func() {
helper.DeleteInvalidDevfile(commonVar.Context)
helper.Cmd("odo", "preference", "remove", "registry", registryName, "-f").ShouldPass()
helper.Cmd("odo", "init", "--name", "aname", "--devfile", "java-maven", "--devfile-registry", registryName).ShouldFail()
})

View File

@@ -36,6 +36,9 @@ var _ = Describe("odo list services tests", func() {
commonVar.CliRunner.EnsurePodIsUp(randomProject, "cluster-sample-1")
Expect(addBindableKind.ExitCode()).To(BeEquivalentTo(0))
helper.Cmd("odo", "set", "project", commonVar.Project).ShouldPass()
helper.CreateInvalidDevfile(commonVar.Context)
helper.Chdir(commonVar.Context)
})
AfterEach(func() {

View File

@@ -26,6 +26,8 @@ var _ = Describe("odo preference and config command tests", func() {
// This is run before every Spec (It)
var _ = BeforeEach(func() {
commonVar = helper.CommonBeforeEach()
helper.CreateInvalidDevfile(commonVar.Context)
helper.Chdir(commonVar.Context)
})
// Clean up after the test
@@ -199,6 +201,7 @@ OdoSettings:
helper.Chdir(workingDir)
})
It("should not prompt the user", func() {
helper.DeleteInvalidDevfile(commonVar.Context)
helper.Cmd("odo", "preference", "set", "ConsentTelemetry", "false", "-f").ShouldPass()
output := helper.Cmd("odo", "init", "--name", "aname", "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile-registry.yaml")).ShouldPass().Out()
Expect(output).ToNot(ContainSubstring(promptMessageSubString))