mirror of
https://github.com/redhat-developer/odo.git
synced 2025-10-19 03:06:19 +03:00
* Add alizer library and test functionality <!-- Thank you for opening a PR! Here are some things you need to know before submitting: 1. Please read our developer guideline: https://github.com/redhat-developer/odo/wiki/Developer-Guidelines 2. Label this PR accordingly with the '/kind' line 3. Ensure you have written and ran the appropriate tests: https://github.com/redhat-developer/odo/wiki/Writing-and-running-tests 4. Read how we approve and LGTM each PR: https://github.com/redhat-developer/odo/wiki/PR-Review Documentation: If you are pushing a change to documentation, please read: https://github.com/redhat-developer/odo/wiki/Contributing-to-Docs --> **What type of PR is this:** <!-- Add one of the following kinds: /kind bug /kind cleanup /kind tests /kind documentation Feel free to use other [labels](https://github.com/redhat-developer/odo/labels) as needed. However one of the above labels must be present or the PR will not be reviewed. This instruction is for reviewers as well. --> /kind feature **What does this PR do / why we need it:** Adds the alizer library from https://github.com/redhat-developer/alizer/tree/main/go as part of our implementaion of `odo dev` and `odo init`. This builds upon @feloy 's PR located here: https://github.com/redhat-developer/odo/pull/5434 **Which issue(s) this PR fixes:** <!-- Specifying the issue will automatically close it when this PR is merged --> Fixes # **PR acceptance criteria:** - [X] Unit test - [X] Integration test - [X] Documentation **How to test changes / Special notes to the reviewer:** N/A. Only function implementation * New alizer version * Use alizer for odo init * Add integration tests * Add Alizer to odo deploy * review * Ask component name for odo deploy * Fix unit test Co-authored-by: Charlie Drage <charlie@charliedrage.com>
208 lines
6.9 KiB
Go
208 lines
6.9 KiB
Go
package init
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/devfile/library/pkg/devfile"
|
|
"github.com/devfile/library/pkg/devfile/parser"
|
|
|
|
"github.com/redhat-developer/odo/pkg/component"
|
|
"github.com/redhat-developer/odo/pkg/devfile/location"
|
|
"github.com/redhat-developer/odo/pkg/init/backend"
|
|
"github.com/redhat-developer/odo/pkg/log"
|
|
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
|
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
|
"github.com/redhat-developer/odo/pkg/odo/genericclioptions/clientset"
|
|
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
|
scontext "github.com/redhat-developer/odo/pkg/segment/context"
|
|
|
|
"k8s.io/kubectl/pkg/util/templates"
|
|
"k8s.io/utils/pointer"
|
|
)
|
|
|
|
// RecommendedCommandName is the recommended command name
|
|
const RecommendedCommandName = "init"
|
|
|
|
var initExample = templates.Examples(`
|
|
# Boostrap a new component in interactive mode
|
|
%[1]s
|
|
|
|
# Bootstrap a new component with a specific devfile from registry
|
|
%[1]s --name my-app --devfile nodejs
|
|
|
|
# Bootstrap a new component with a specific devfile from a specific registry
|
|
%[1]s --name my-app --devfile nodejs --devfile-registry MyRegistry
|
|
|
|
# Bootstrap a new component with a specific devfile from the local filesystem
|
|
%[1]s --name my-app --devfile-path $HOME/devfiles/nodejs/devfile.yaml
|
|
|
|
# Bootstrap a new component with a specific devfile from the web
|
|
%[1]s --name my-app --devfile-path https://devfiles.example.com/nodejs/devfile.yaml
|
|
|
|
# Bootstrap a new component and download a starter project
|
|
%[1]s --name my-app --devfile nodejs --starter nodejs-starter
|
|
`)
|
|
|
|
type InitOptions struct {
|
|
// CMD context
|
|
ctx context.Context
|
|
|
|
// Clients
|
|
clientset *clientset.Clientset
|
|
|
|
// 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
|
|
}
|
|
|
|
// NewInitOptions creates a new InitOptions instance
|
|
func NewInitOptions() *InitOptions {
|
|
return &InitOptions{}
|
|
}
|
|
|
|
func (o *InitOptions) SetClientset(clientset *clientset.Clientset) {
|
|
o.clientset = clientset
|
|
}
|
|
|
|
// 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
|
|
func (o *InitOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
|
|
|
o.ctx = cmdline.Context()
|
|
|
|
o.contextDir, err = o.clientset.FS.Getwd()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
o.flags = cmdline.GetFlags()
|
|
|
|
return nil
|
|
}
|
|
|
|
// Validate validates the InitOptions based on completed values
|
|
func (o *InitOptions) Validate() error {
|
|
|
|
devfilePresent, err := location.DirectoryContainsDevfile(o.clientset.FS, o.contextDir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if devfilePresent {
|
|
return errors.New("a devfile already exists in the current directory")
|
|
}
|
|
|
|
err = o.clientset.InitClient.Validate(o.flags, o.clientset.FS, o.contextDir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Run contains the logic for the odo command
|
|
func (o *InitOptions) Run() (err error) {
|
|
|
|
var starterDownloaded bool
|
|
|
|
defer func() {
|
|
if err == nil {
|
|
return
|
|
}
|
|
if starterDownloaded {
|
|
err = fmt.Errorf("%w\nthe command failed after downloading the starter project. By security, the directory is not cleaned up", err)
|
|
} else {
|
|
_ = o.clientset.FS.Remove("devfile.yaml")
|
|
err = fmt.Errorf("%w\nthe command failed, the devfile has been removed from current directory", err)
|
|
}
|
|
}()
|
|
|
|
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
|
|
}
|
|
|
|
scontext.SetComponentType(o.ctx, component.GetComponentTypeFromDevfileMetadata(devfileObj.Data.GetMetadata()))
|
|
|
|
starterInfo, err := o.clientset.InitClient.SelectStarterProject(devfileObj, o.flags, o.clientset.FS, o.contextDir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if starterInfo != nil {
|
|
// WARNING: this will remove all the content of the destination directory, ie the devfile.yaml file
|
|
err = o.clientset.InitClient.DownloadStarterProject(starterInfo, o.contextDir)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to download starter project %q: %w", starterInfo.Name, err)
|
|
}
|
|
starterDownloaded = true
|
|
|
|
// in case the starter project contains a devfile, read it again
|
|
if _, err = o.clientset.FS.Stat(devfilePath); err == nil {
|
|
devfileObj, _, err = devfile.ParseDevfileAndValidate(parser.ParserArgs{Path: devfilePath, FlattenedDevfile: pointer.BoolPtr(false)})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set the name in the devfile *AND* writes the devfile back to the disk in case
|
|
// it has been removed and not replaced by the starter project
|
|
err = o.clientset.InitClient.PersonalizeName(devfileObj, o.flags)
|
|
if err != nil {
|
|
return fmt.Errorf("Failed to update the devfile's name: %w", err)
|
|
}
|
|
|
|
log.Italicf(`
|
|
Your new component %q is ready in the current directory.
|
|
To start editing your component, use "odo dev" and open this folder in your favorite IDE.
|
|
Changes will be directly reflected on the cluster.
|
|
To deploy your component to a cluster use "odo deploy".`, devfileObj.Data.GetMetadata().Name)
|
|
|
|
return nil
|
|
}
|
|
|
|
// NewCmdInit implements the odo command
|
|
func NewCmdInit(name, fullName string) *cobra.Command {
|
|
|
|
o := NewInitOptions()
|
|
initCmd := &cobra.Command{
|
|
Use: name,
|
|
Short: "Init bootstraps a new project",
|
|
Long: "Bootstraps a new project",
|
|
Example: fmt.Sprintf(initExample, fullName),
|
|
Args: cobra.MaximumNArgs(0),
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
genericclioptions.GenericRun(o, cmd, args)
|
|
},
|
|
}
|
|
clientset.Add(initCmd, clientset.PREFERENCE, clientset.FILESYSTEM, clientset.REGISTRY, clientset.INIT)
|
|
|
|
initCmd.Flags().String(backend.FLAG_NAME, "", "name of the component to create")
|
|
initCmd.Flags().String(backend.FLAG_DEVFILE, "", "name of the devfile in devfile registry")
|
|
initCmd.Flags().String(backend.FLAG_DEVFILE_REGISTRY, "", "name of the devfile registry (as configured in \"odo registry list\"). It can be used in combination with --devfile, but not with --devfile-path")
|
|
initCmd.Flags().String(backend.FLAG_STARTER, "", "name of the starter project. Available starter projects can be found with \"odo catalog describe component <devfile>\"")
|
|
initCmd.Flags().String(backend.FLAG_DEVFILE_PATH, "", "path to a devfile. This is an alternative to using devfile from Devfile registry. It can be local filesystem path or http(s) URL")
|
|
|
|
// Add a defined annotation in order to appear in the help menu
|
|
initCmd.SetUsageTemplate(odoutil.CmdUsageTemplate)
|
|
return initCmd
|
|
}
|