Make odo work if optional metadata.name field is missing in Devfile (#6015)

* Move 'starter_project.go' from 'pkg/component' to 'pkg/registry'

Functions in this file are only called from the 'registry' package,
so it makes sense for this file to belong to this package.

Furthermore, this paves the way to calling 'alizer.DetectName' from 'component.go',
thus avoiding a cyclic dependency between packages.

* Introduce central logic for determining component names in 'pkg/component/component.go'

This rewrites the 'component#GatherName' function that was already there,
but not used, to meet the expectations, i.e.:

- use 'metadata.name' field (after sanitizing it) if it is defined in the Devfile
- otherwise, use Alizer to detect the name. Under the hood, this leverages the 'alizer#DetectName' introduced in 83ad3ee, which means that:
-- use Alizer to detect the name automatically
-- otherwise, use the name of the Devfile base directory after sanitizing it

* Compute and store the component name in the CLI context, and pass it as needed

As commented out in [1], the context should ideally be built
and passed down to the business clients structs.

[1] https://github.com/redhat-developer/odo/pull/6015#discussion_r957005479

* Enrich relevant integration test cases

For the sake of both performance and readability,
only the tests that break in the absence of a 'metadata.name'
field in their Devfiles have been updated (to test this specific case).

* Add test case for 'odo dev' when a project with no source code is used with no 'metadata.name' in the Devfile

The rationale behind this is to purposely make
the Alizer library unable to detect the project.
Per the requirements, this would force us to use the project
directory name as component name.

This highlights an interesting behavior if the project
directory name is all-numeric (as is the case in our tests);
our sanitization logic automatically prepends an "x" prefix
to the directory name, so it can be used as a valid name
for the component.
This commit is contained in:
Armel Soro
2022-08-30 08:54:31 +02:00
committed by GitHub
parent 64ee3db5b3
commit 843717cab3
39 changed files with 1860 additions and 1226 deletions

View File

@@ -14,7 +14,6 @@ import (
"github.com/devfile/registry-support/registry-library/library"
"github.com/redhat-developer/odo/pkg/api"
"github.com/redhat-developer/odo/pkg/component"
"github.com/redhat-developer/odo/pkg/devfile"
"github.com/redhat-developer/odo/pkg/devfile/location"
"github.com/redhat-developer/odo/pkg/log"
@@ -51,7 +50,7 @@ func (o RegistryClient) DownloadFileInMemory(params dfutil.HTTPRequestParams) ([
// DownloadStarterProject downloads a starter project referenced in devfile
// This will first remove the content of the contextDir
func (o RegistryClient) DownloadStarterProject(starterProject *devfilev1.StarterProject, decryptedToken string, contextDir string, verbose bool) error {
return component.DownloadStarterProject(starterProject, decryptedToken, contextDir, verbose)
return DownloadStarterProject(starterProject, decryptedToken, contextDir, verbose)
}
// GetDevfileRegistries gets devfile registries from preference file,

View File

@@ -0,0 +1,188 @@
package registry
import (
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
devfilev1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
parsercommon "github.com/devfile/library/pkg/devfile/parser/data/v2/common"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/redhat-developer/odo/pkg/devfile/location"
"github.com/redhat-developer/odo/pkg/log"
"github.com/redhat-developer/odo/pkg/util"
)
const (
RegistryUser = "default"
)
func checkoutProject(subDir, zipURL, path, starterToken string) error {
if subDir == "" {
subDir = "/"
}
err := util.GetAndExtractZip(zipURL, path, subDir, starterToken)
if err != nil {
return fmt.Errorf("failed to download and extract project zip folder: %w", err)
}
return nil
}
// DownloadStarterProject downloads a starter project referenced in devfile
// This will first remove the content of the contextDir
func DownloadStarterProject(starterProject *devfilev1.StarterProject, decryptedToken string, contextDir string, verbose bool) error {
var path string
var err error
// Retrieve the working directory in order to clone correctly
if contextDir == "" {
path, err = os.Getwd()
if err != nil {
return fmt.Errorf("could not get the current working directory: %w", err)
}
} else {
path = contextDir
}
// We will check to see if the project has a valid directory
err = util.IsValidProjectDir(path, location.DevfileLocation(""))
if err != nil {
return err
}
if verbose {
log.Info("\nStarter Project")
}
if starterProject.Git != nil {
err := downloadGitProject(starterProject, decryptedToken, path, verbose)
if err != nil {
return err
}
} else if starterProject.Zip != nil {
url := starterProject.Zip.Location
sparseDir := starterProject.SubDir
var downloadSpinner *log.Status
if verbose {
downloadSpinner = log.Spinnerf("Downloading starter project %s from %s", starterProject.Name, url)
}
err := checkoutProject(sparseDir, url, path, decryptedToken)
if err != nil {
if verbose {
downloadSpinner.End(false)
}
return err
}
if verbose {
downloadSpinner.End(true)
}
} else {
return errors.New("project type not supported")
}
return nil
}
// downloadGitProject downloads the git starter projects from devfile.yaml
func downloadGitProject(starterProject *devfilev1.StarterProject, starterToken, path string, verbose bool) error {
remoteName, remoteUrl, revision, err := parsercommon.GetDefaultSource(starterProject.Git.GitLikeProjectSource)
if err != nil {
return fmt.Errorf("unable to get default project source for starter project %s: %w", starterProject.Name, err)
}
// convert revision to referenceName type, ref name could be a branch or tag
// if revision is not specified it would be the default branch of the project
refName := plumbing.ReferenceName(revision)
if plumbing.IsHash(revision) {
// Specifying commit in the reference name is not supported by the go-git library
// while doing git.PlainClone()
log.Warning("Specifying commit in 'revision' is not yet supported in odo.")
// overriding revision to empty as we do not support this
revision = ""
}
if revision != "" {
// lets consider revision to be a branch name first
refName = plumbing.NewBranchReferenceName(revision)
}
var downloadSpinner *log.Status
if verbose {
downloadSpinner = log.Spinnerf("Downloading starter project %s from %s", starterProject.Name, remoteUrl)
defer downloadSpinner.End(false)
}
cloneOptions := &git.CloneOptions{
URL: remoteUrl,
RemoteName: remoteName,
ReferenceName: refName,
SingleBranch: true,
// we don't need history for starter projects
Depth: 1,
}
if starterToken != "" {
cloneOptions.Auth = &http.BasicAuth{
Username: RegistryUser,
Password: starterToken,
}
}
originalPath := ""
if starterProject.SubDir != "" {
originalPath = path
path, err = ioutil.TempDir("", "")
if err != nil {
return err
}
}
_, err = git.PlainClone(path, false, cloneOptions)
if err != nil {
// it returns the following error if no matching ref found
// if we get this error, we are trying again considering revision as tag, only if revision is specified.
if _, ok := err.(git.NoMatchingRefSpecError); !ok || revision == "" {
return err
}
// try again to consider revision as tag name
cloneOptions.ReferenceName = plumbing.NewTagReferenceName(revision)
// remove if any .git folder downloaded in above try
_ = os.RemoveAll(filepath.Join(path, ".git"))
_, err = git.PlainClone(path, false, cloneOptions)
if err != nil {
return err
}
}
// we don't want to download project be a git repo
err = os.RemoveAll(filepath.Join(path, ".git"))
if err != nil {
// we don't need to return (fail) if this happens
log.Warning("Unable to delete .git from cloned starter project")
}
if starterProject.SubDir != "" {
err = util.GitSubDir(path, originalPath,
starterProject.SubDir)
if err != nil {
return err
}
}
if verbose {
downloadSpinner.End(true)
}
return nil
}