Skip to content

Commit

Permalink
Initialize devfile on odo dev/deploy without devfile (#5531)
Browse files Browse the repository at this point in the history
* Personalize devfile on odo deploy without devfile

* Fix odo deploy on empty dir

* Interactive integration tests for odo deploy

* Get and personalize devfile on odo dev without devfile

* Test odo dev without devfile

* Review
  • Loading branch information
feloy authored Mar 9, 2022
1 parent c8e4ed3 commit 9e09d80
Show file tree
Hide file tree
Showing 12 changed files with 312 additions and 64 deletions.
25 changes: 25 additions & 0 deletions pkg/init/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import (
"strings"

"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
"github.com/devfile/library/pkg/devfile"
"github.com/devfile/library/pkg/devfile/parser"
dfutil "github.com/devfile/library/pkg/util"
"k8s.io/utils/pointer"

"github.com/redhat-developer/odo/pkg/catalog"
"github.com/redhat-developer/odo/pkg/devfile/location"
Expand Down Expand Up @@ -238,3 +240,26 @@ func (o InitClient) PersonalizeDevfileConfig(devfileobj parser.DevfileObj, flags
}
return backend.PersonalizeDevfileconfig(devfileobj)
}

func (o InitClient) SelectAndPersonalizeDevfile(flags map[string]string, contextDir string) (parser.DevfileObj, string, error) {
devfileLocation, err := o.SelectDevfile(flags, o.fsys, contextDir)
if err != nil {
return parser.DevfileObj{}, "", err
}

devfilePath, err := o.DownloadDevfile(devfileLocation, contextDir)
if err != nil {
return parser.DevfileObj{}, "", fmt.Errorf("unable to download devfile: %w", err)
}

devfileObj, _, err := devfile.ParseDevfileAndValidate(parser.ParserArgs{Path: devfilePath, FlattenedDevfile: pointer.BoolPtr(false)})
if err != nil {
return parser.DevfileObj{}, "", fmt.Errorf("unable to parse devfile: %w", err)
}

err = o.PersonalizeDevfileConfig(devfileObj, flags, o.fsys, contextDir)
if err != nil {
return parser.DevfileObj{}, "", fmt.Errorf("failed to configure devfile: %w", err)
}
return devfileObj, devfilePath, nil
}
4 changes: 4 additions & 0 deletions pkg/init/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,8 @@ type Client interface {

// PersonalizeDevfileConfig updates the env vars, and URL endpoints
PersonalizeDevfileConfig(devfileobj parser.DevfileObj, flags map[string]string, fs filesystem.Filesystem, dir string) error

// SelectAndPersonalizeDevfile selects a devfile, then downloads, parse and personalize it
// Returns the devfile object and its path
SelectAndPersonalizeDevfile(flags map[string]string, contextDir string) (parser.DevfileObj, string, error)
}
16 changes: 16 additions & 0 deletions pkg/init/mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

67 changes: 35 additions & 32 deletions pkg/odo/cli/deploy/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"

"github.com/devfile/library/pkg/devfile"
"github.com/devfile/library/pkg/devfile/parser"

"github.com/redhat-developer/odo/pkg/devfile/location"
"github.com/redhat-developer/odo/pkg/envinfo"
"github.com/redhat-developer/odo/pkg/odo/cli/component"
Expand All @@ -21,7 +18,6 @@ import (
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"

"k8s.io/kubectl/pkg/util/templates"
"k8s.io/utils/pointer"
)

// RecommendedCommandName is the recommended command name
Expand All @@ -35,8 +31,8 @@ type DeployOptions struct {
// Clients
clientset *clientset.Clientset

// Flags
contextFlag string
// working directory
contextDir string
}

var deployExample = templates.Examples(`
Expand All @@ -55,44 +51,30 @@ func (o *DeployOptions) SetClientset(clientset *clientset.Clientset) {

// Complete DeployOptions after they've been created
func (o *DeployOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {

cwd, err := os.Getwd()
o.contextDir, err = os.Getwd()
if err != nil {
return err
}
containsDevfile, err := location.DirectoryContainsDevfile(filesystem.DefaultFs{}, cwd)

isEmptyDir, err := location.DirIsEmpty(o.clientset.FS, o.contextDir)
if err != nil {
return err
}
if !containsDevfile {
devfileLocation, err2 := o.clientset.InitClient.SelectDevfile(map[string]string{}, o.clientset.FS, cwd)
if err2 != nil {
return err2
}

devfilePath, err2 := o.clientset.InitClient.DownloadDevfile(devfileLocation, cwd)
if err2 != nil {
return fmt.Errorf("unable to download devfile: %w", err2)
}

devfileObj, _, err2 := devfile.ParseDevfileAndValidate(parser.ParserArgs{Path: devfilePath, FlattenedDevfile: pointer.BoolPtr(false)})
if err2 != nil {
return fmt.Errorf("unable to download devfile: %w", err2)
}

// Set the name in the devfile and writes the devfile back to the disk
err = o.clientset.InitClient.PersonalizeName(devfileObj, map[string]string{})
if err != nil {
return fmt.Errorf("failed to update the devfile's name: %w", err)
}
if isEmptyDir {
return errors.New("this command cannot run in an empty directory, you need to run it in a directory containing source code")
}

err = o.initDevfile()
if err != nil {
return err
}
o.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline).NeedDevfile(o.contextFlag))

o.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline).NeedDevfile(o.contextDir))
if err != nil {
return err
}

envFileInfo, err := envinfo.NewEnvSpecificInfo(o.contextFlag)
envFileInfo, err := envinfo.NewEnvSpecificInfo(o.contextDir)
if err != nil {
return errors.Wrap(err, "unable to retrieve configuration information")
}
Expand Down Expand Up @@ -121,6 +103,27 @@ func (o *DeployOptions) Validate() error {
return nil
}

func (o *DeployOptions) initDevfile() error {
containsDevfile, err := location.DirectoryContainsDevfile(filesystem.DefaultFs{}, o.contextDir)
if err != nil {
return err
}
if containsDevfile {
return nil
}
devfileObj, _, err := o.clientset.InitClient.SelectAndPersonalizeDevfile(map[string]string{}, o.contextDir)
if err != nil {
return err
}

// Set the name in the devfile and writes the devfile back to the disk
err = o.clientset.InitClient.PersonalizeName(devfileObj, map[string]string{})
if err != nil {
return fmt.Errorf("failed to update the devfile's name: %w", err)
}
return nil
}

// Run contains the logic for the odo command
func (o *DeployOptions) Run() error {
devfileObj := o.EnvSpecificInfo.GetDevfileObj()
Expand Down
54 changes: 49 additions & 5 deletions pkg/odo/cli/dev/dev.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
package dev

import (
"errors"
"fmt"
"io"
"os"
"path/filepath"

"github.com/redhat-developer/odo/pkg/devfile/location"

"github.com/redhat-developer/odo/pkg/devfile"
"github.com/redhat-developer/odo/pkg/devfile/adapters"
"github.com/redhat-developer/odo/pkg/devfile/adapters/common"
"github.com/redhat-developer/odo/pkg/devfile/location"
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
"github.com/redhat-developer/odo/pkg/watch"

dfutil "github.com/devfile/library/pkg/util"
ododevfile "github.com/redhat-developer/odo/pkg/devfile"
"github.com/redhat-developer/odo/pkg/devfile/adapters/kubernetes"
"github.com/redhat-developer/odo/pkg/envinfo"
"github.com/redhat-developer/odo/pkg/odo/cli/component"
Expand All @@ -38,6 +39,9 @@ type DevOptions struct {
// Variables
ignorePaths []string
out io.Writer

// working directory
contextDir string
}

type DevHandler struct{}
Expand All @@ -64,6 +68,24 @@ func (o *DevOptions) SetClientset(clientset *clientset.Clientset) {
func (o *DevOptions) Complete(cmdline cmdline.Cmdline, args []string) error {
var err error

o.contextDir, err = os.Getwd()
if err != nil {
return err
}

isEmptyDir, err := location.DirIsEmpty(o.clientset.FS, o.contextDir)
if err != nil {
return err
}
if isEmptyDir {
return errors.New("this command cannot run in an empty directory, you need to run it in a directory containing source code")
}

err = o.initDevfile()
if err != nil {
return err
}

o.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline).NeedDevfile(""))
if err != nil {
return fmt.Errorf("unable to create context: %v", err)
Expand Down Expand Up @@ -119,6 +141,28 @@ func (o *DevOptions) Validate() error {
return err
}

func (o *DevOptions) initDevfile() error {
containsDevfile, err := location.DirectoryContainsDevfile(filesystem.DefaultFs{}, o.contextDir)
if err != nil {
return err
}
if containsDevfile {
return nil
}

devfileObj, _, err := o.clientset.InitClient.SelectAndPersonalizeDevfile(map[string]string{}, o.contextDir)
if err != nil {
return err
}

// Set the name in the devfile and writes the devfile back to the disk
err = o.clientset.InitClient.PersonalizeName(devfileObj, map[string]string{})
if err != nil {
return fmt.Errorf("failed to update the devfile's name: %w", err)
}
return nil
}

func (o *DevOptions) Run() error {
var err error
var platformContext = kubernetes.KubernetesContext{
Expand Down Expand Up @@ -152,7 +196,7 @@ func regenerateComponentAdapterFromWatchParams(parameters watch.WatchParameters)

// Parse devfile and validate. Path is hard coded because odo expects devfile.yaml to be present in the pwd/cwd.

devObj, err := devfile.ParseAndValidateFromFile(location.DevfileLocation(""))
devObj, err := ododevfile.ParseAndValidateFromFile(location.DevfileLocation(""))
if err != nil {
return nil, err
}
Expand All @@ -179,7 +223,7 @@ func NewCmdDev(name, fullName string) *cobra.Command {
},
}

clientset.Add(devCmd, clientset.DEV)
clientset.Add(devCmd, clientset.DEV, clientset.INIT)
// Add a defined annotation in order to appear in the help menu
devCmd.Annotations["command"] = "utility"
devCmd.SetUsageTemplate(odoutil.CmdUsageTemplate)
Expand Down
23 changes: 1 addition & 22 deletions pkg/odo/cli/init/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,6 @@ type InitOptions struct {
// Flags passed to the command
flags map[string]string

// devfileLocation is the information needed to pull a devfile
devfileLocation *backend.DevfileLocation

// Destination directory
contextDir string
}
Expand Down Expand Up @@ -136,28 +133,10 @@ func (o *InitOptions) Run() (err error) {
"odo will try to autodetect the language and project type in order to select the best suited Devfile for your project.")
}

o.devfileLocation, err = o.clientset.InitClient.SelectDevfile(o.flags, o.clientset.FS, o.contextDir)
if err != nil {
return err
}

devfilePath, err := o.clientset.InitClient.DownloadDevfile(o.devfileLocation, o.contextDir)
if err != nil {
return fmt.Errorf("unable to download devfile: %w", err)
}

devfileObj, _, err := devfile.ParseDevfileAndValidate(parser.ParserArgs{Path: devfilePath, FlattenedDevfile: pointer.BoolPtr(false)})
if err != nil {
return err
}
devfileObj, devfilePath, err := o.clientset.InitClient.SelectAndPersonalizeDevfile(o.flags, o.contextDir)

scontext.SetComponentType(o.ctx, component.GetComponentTypeFromDevfileMetadata(devfileObj.Data.GetMetadata()))

err = o.clientset.InitClient.PersonalizeDevfileConfig(devfileObj, o.flags, o.clientset.FS, o.contextDir)
if err != nil {
return fmt.Errorf("Failed to configure devfile: %w", err)
}

starterInfo, err := o.clientset.InitClient.SelectStarterProject(devfileObj, o.flags, o.clientset.FS, o.contextDir)
if err != nil {
return err
Expand Down
11 changes: 8 additions & 3 deletions tests/helper/helper_interactive.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ type InteractiveContext struct {
// Its content will get updated as long as there are interactions with the console, like sending lines or
// expecting lines.
buffer *bytes.Buffer

// A function yto call to stop the process
StopCommand func()
}

// Tester represents the function that contains all steps to test the given interactive command.
Expand Down Expand Up @@ -79,12 +82,14 @@ func RunInteractive(command []string, env []string, tester Tester) (string, erro
Command: command,
console: c,
buffer: buf,
StopCommand: func() {
_ = cmd.Process.Kill()
},
}
tester(ctx)

err = cmd.Wait()
if err != nil {
log.Fatal(err)
}

// Close the slave end of the pty, and read the remaining bytes from the master end.
c.Tty().Close()

Expand Down
13 changes: 13 additions & 0 deletions tests/integration/devfile/cmd_dev_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ var _ = Describe("odo dev command tests", func() {
helper.CommonAfterEach(commonVar)
})

When("directory is empty", func() {

BeforeEach(func() {
Expect(helper.ListFilesInDir(commonVar.Context)).To(HaveLen(0))
})

It("should error", func() {
output := helper.Cmd("odo", "dev").ShouldFail().Err()
Expect(output).To(ContainSubstring("this command cannot run in an empty directory"))

})
})

When("a component is bootstrapped and pushed", func() {
BeforeEach(func() {
helper.CopyExample(filepath.Join("source", "devfiles", "nodejs", "project"), commonVar.Context)
Expand Down
13 changes: 13 additions & 0 deletions tests/integration/devfile/cmd_devfile_deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ var _ = Describe("odo devfile deploy command tests", func() {
helper.CommonAfterEach(commonVar)
})

When("directory is empty", func() {

BeforeEach(func() {
Expect(helper.ListFilesInDir(commonVar.Context)).To(HaveLen(0))
})

It("should error", func() {
output := helper.Cmd("odo", "deploy").ShouldFail().Err()
Expect(output).To(ContainSubstring("this command cannot run in an empty directory"))

})
})

When("using a devfile.yaml containing a deploy command", func() {

BeforeEach(func() {
Expand Down
Loading

0 comments on commit 9e09d80

Please sign in to comment.