Skip to content

Commit

Permalink
odo init filters devfile stacks by supported architectures (redhat-de…
Browse files Browse the repository at this point in the history
…veloper#7004)

* Add --architecture flag

* Ask architecture during interactive mode

* Display architectures of detected Devfile

* Fix integration tests

* Fix automated doc

* Fix e2e tests

* Ignore empty lines on doc automation tests

* Update pkg/odo/cli/registry/registry.go

Co-authored-by: Armel Soro <armel@rm3l.org>

* Fix Architectures field in API

* Change "select architectures" prompt

---------

Co-authored-by: Armel Soro <armel@rm3l.org>
  • Loading branch information
feloy and rm3l authored Aug 1, 2023
1 parent 3cb1f5c commit d41364e
Show file tree
Hide file tree
Showing 34 changed files with 391 additions and 110 deletions.
18 changes: 10 additions & 8 deletions cmd/odo/alizer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,15 @@ func TestOdoAlizer(t *testing.T) {
alizerClient := alizer.NewMockClient(ctrl)
path := "/"
alizerClient.EXPECT().DetectFramework(gomock.Any(), path).
Return(
model.DevFileType{
Return(alizer.DetectedFramework{
Type: model.DevFileType{
Name: "framework-name",
},
"1.1.1",
api.Registry{
DefaultVersion: "1.1.1",
Registry: api.Registry{
Name: "TheRegistryName",
},
},
nil,
)
alizerClient.EXPECT().DetectPorts(path).Return([]int{8080, 3000}, nil)
Expand Down Expand Up @@ -92,14 +93,15 @@ func TestOdoAlizer(t *testing.T) {
alizerClient := alizer.NewMockClient(ctrl)
path := "/"
alizerClient.EXPECT().DetectFramework(gomock.Any(), path).
Return(
model.DevFileType{
Return(alizer.DetectedFramework{
Type: model.DevFileType{
Name: "framework-name",
},
"1.1.1",
api.Registry{
DefaultVersion: "1.1.1",
Registry: api.Registry{
Name: "TheRegistryName",
},
},
nil,
)
alizerClient.EXPECT().DetectPorts(path).Return([]int{8080, 3000}, nil)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ $ odo init
Interactive mode enabled, please answer the following questions:
✓ Determining a Devfile for the current directory [1s]
Based on the files in the current directory odo detected
Supported architectures: all
Language: JavaScript
Project type: Node.js
Application ports: 8080
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ $ odo init
\__/

Interactive mode enabled, please answer the following questions:
? Select architectures to filter by: [Use arrows to move, space to select, <right> to all, <left> to none, type to filter]
> [x] amd64
[ ] arm64
[ ] ppc64le
[ ] s390x
? Select architectures to filter by: amd64
? Select language: Java
? Select project type: Maven Java
✓ Downloading devfile "java-maven" from registry "DefaultDevfileRegistry" [4s]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,17 @@ $ odo init
Interactive mode enabled, please answer the following questions:
✓ Determining a Devfile for the current directory [1s]
Based on the files in the current directory odo detected
Supported architectures: all
Language: .NET
Project type: dotnet
The devfile "dotnet50:1.0.3" from the registry "DefaultDevfileRegistry" will be downloaded.
? Is this correct? No
? Select architectures to filter by: [Use arrows to move, space to select, <right> to all, <left> to none, type to filter]
> [x] amd64
[ ] arm64
[ ] ppc64le
[ ] s390x
? Select architectures to filter by: amd64
? Select language: .NET
? Select project type: .NET 6.0
✓ Downloading devfile "dotnet60" from registry "DefaultDevfileRegistry" [3s]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ $ odo init
Interactive mode enabled, please answer the following questions:
✓ Determining a Devfile for the current directory [1s]
Based on the files in the current directory odo detected
Supported architectures: all
Language: Go
Project type: Go
Application ports: 8080
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ $ odo init
Interactive mode enabled, please answer the following questions:
✓ Determining a Devfile for the current directory [1s]
Based on the files in the current directory odo detected
Supported architectures: all
Language: Java
Project type: springboot
The devfile "java-springboot:1.2.0" from the registry "DefaultDevfileRegistry" will be downloaded.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ $ odo init
Interactive mode enabled, please answer the following questions:
✓ Determining a Devfile for the current directory [1s]
Based on the files in the current directory odo detected
Supported architectures: all
Language: JavaScript
Project type: Node.js
Application ports: 3000
Expand Down
14 changes: 10 additions & 4 deletions pkg/alizer/alizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ func NewAlizerClient(registryClient registry.Client) *Alizer {

// DetectFramework uses the alizer library in order to detect the devfile
// to use depending on the files in the path
func (o *Alizer) DetectFramework(ctx context.Context, path string) (_ model.DevFileType, defaultVersion string, _ api.Registry, _ error) {
func (o *Alizer) DetectFramework(ctx context.Context, path string) (DetectedFramework, error) {
types := []model.DevFileType{}
components, err := o.registryClient.ListDevfileStacks(ctx, "", "", "", false, false)
if err != nil {
return model.DevFileType{}, defaultVersion, api.Registry{}, err
return DetectedFramework{}, err
}
for _, component := range components.Items {
types = append(types, model.DevFileType{
Expand All @@ -45,15 +45,21 @@ func (o *Alizer) DetectFramework(ctx context.Context, path string) (_ model.DevF
}
typ, err := recognizer.SelectDevFileFromTypes(path, types)
if err != nil {
return model.DevFileType{}, defaultVersion, api.Registry{}, err
return DetectedFramework{}, err
}
// Get the default stack version that will be downloaded
var defaultVersion string
for _, version := range components.Items[typ].Versions {
if version.IsDefault {
defaultVersion = version.Version
}
}
return types[typ], defaultVersion, components.Items[typ].Registry, nil
return DetectedFramework{
Type: types[typ],
DefaultVersion: defaultVersion,
Registry: components.Items[typ].Registry,
Architectures: components.Items[typ].Architectures,
}, nil
}

// DetectName retrieves the name of the project (if available).
Expand Down
8 changes: 4 additions & 4 deletions pkg/alizer/alizer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,18 +119,18 @@ func TestDetectFramework(t *testing.T) {
registryClient.EXPECT().ListDevfileStacks(ctx, "", "", "", false, false).Return(list, nil)
alizerClient := NewAlizerClient(registryClient)
// Run function DetectFramework
detected, _, registry, err := alizerClient.DetectFramework(ctx, tt.args.path)

detected, err := alizerClient.DetectFramework(ctx, tt.args.path)
if !tt.wantErr == (err != nil) {
t.Errorf("unexpected error %v, wantErr %v", err, tt.wantErr)
return
}

if detected.Name != tt.wantedDevfile {
if detected.Type.Name != tt.wantedDevfile {
t.Errorf("unexpected devfile %v, wantedDevfile %v", detected, tt.wantedDevfile)
}
if registry.Name != tt.wantedRegistry {
t.Errorf("unexpected registry %v, wantedRegistry %v", registry, tt.wantedRegistry)
if detected.Registry.Name != tt.wantedRegistry {
t.Errorf("unexpected registry %v, wantedRegistry %v", detected.Registry, tt.wantedRegistry)
}
})
}
Expand Down
10 changes: 8 additions & 2 deletions pkg/alizer/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@ import (
"context"

"github.com/devfile/alizer/pkg/apis/model"

"github.com/redhat-developer/odo/pkg/api"
)

type DetectedFramework struct {
Type model.DevFileType
DefaultVersion string
Registry api.Registry
Architectures []string
}

type Client interface {
DetectFramework(ctx context.Context, path string) (_ model.DevFileType, defaultVersion string, _ api.Registry, _ error)
DetectFramework(ctx context.Context, path string) (DetectedFramework, error)
DetectName(path string) (string, error)
DetectPorts(path string) ([]int, error)
}
12 changes: 4 additions & 8 deletions pkg/alizer/mock.go

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

2 changes: 2 additions & 0 deletions pkg/api/analyze.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ type DetectionResult struct {
DevfileVersion string `json:"devfileVersion,omitempty"`
// Name represents the project/application name as detected by alizer
Name string `json:"name,omitempty"`
// Architectures represent the architectures with which the Devfile must be compatible with.
Architectures []string `json:"architectures,omitempty"`
}
28 changes: 23 additions & 5 deletions pkg/init/asker/asker.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,36 @@ func NewSurveyAsker() *Survey {
return &Survey{}
}

func (o *Survey) AskLanguage(langs []string) (string, error) {
func (o *Survey) AskArchitectures(archs []string, selectedDefault []string) ([]string, error) {
question := &survey.MultiSelect{
Message: "Select architectures to filter by:",
Options: archs,
Default: selectedDefault,
}
var answer []string
err := survey.AskOne(question, &answer)
if err != nil {
return nil, err
}
return answer, nil
}

func (o *Survey) AskLanguage(langs []string) (bool, string, error) {
sort.Strings(langs)
langs = append(langs, GOBACK)
question := &survey.Select{
Message: "Select language:",
Options: langs,
}
var answer string
err := survey.AskOne(question, &answer)
var answerPos int
err := survey.AskOne(question, &answerPos)
if err != nil {
return "", err
return false, "", err
}
return answer, nil
if answerPos == len(langs)-1 {
return true, "", nil
}
return false, langs[answerPos], nil
}

func (o *Survey) AskType(types registry.TypesWithDetails) (back bool, _ api.DevfileStack, _ error) {
Expand Down
8 changes: 6 additions & 2 deletions pkg/init/asker/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ import (

// Asker interactively asks for information to the user
type Asker interface {
// AskLanguage asks for a language, from a list of language names. The language name is returned
AskLanguage(langs []string) (string, error)
// AskArchitectures asks for a selection of architectures from a list of architecture names
AskArchitectures(archs []string, selectedDefault []string) ([]string, error)

// AskLanguage asks for a language, from a list of language names.
// back is returned as true if the user selected to go back, or the language name is returned
AskLanguage(langs []string) (back bool, result string, err error)

// AskType asks for a Devfile type, or to go back. back is returned as true if the user selected to go back,
// or the selected type is returned
Expand Down
24 changes: 20 additions & 4 deletions pkg/init/asker/mock.go

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

19 changes: 14 additions & 5 deletions pkg/init/backend/alizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ package backend
import (
"context"
"fmt"
"github.com/redhat-developer/odo/pkg/log"
"strconv"
"strings"

"github.com/redhat-developer/odo/pkg/log"

"github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
"github.com/devfile/library/v2/pkg/devfile/parser"

Expand Down Expand Up @@ -34,6 +35,13 @@ func (o *AlizerBackend) Validate(flags map[string]string, fs filesystem.Filesyst
return nil
}

func archList(archs []string) string {
if len(archs) == 0 {
return "all"
}
return strings.Join(archs, ", ")
}

// SelectDevfile calls the Alizer to detect the devfile and asks for confirmation to the user
func (o *AlizerBackend) SelectDevfile(ctx context.Context, flags map[string]string, fs filesystem.Filesystem, dir string) (*api.DetectionResult, error) {
type result struct {
Expand All @@ -47,12 +55,13 @@ func (o *AlizerBackend) SelectDevfile(ctx context.Context, flags map[string]stri
location, err := func() (location *api.DetectionResult, err error) {
spinner := log.Spinnerf("Determining a Devfile for the current directory")
defer spinner.End(err == nil)
selected, defaultVersion, registry, err := o.alizerClient.DetectFramework(ctx, dir)
detected, err := o.alizerClient.DetectFramework(ctx, dir)
if err != nil {
return nil, err
}

msg := fmt.Sprintf("Based on the files in the current directory odo detected\nLanguage: %s\nProject type: %s", selected.Language, selected.ProjectType)
msg := fmt.Sprintf("Based on the files in the current directory odo detected\nSupported architectures: %s\nLanguage: %s\nProject type: %s",
archList(detected.Architectures), detected.Type.Language, detected.Type.ProjectType)

appPorts, err := o.alizerClient.DetectPorts(dir)
if err != nil {
Expand All @@ -68,15 +77,15 @@ func (o *AlizerBackend) SelectDevfile(ctx context.Context, flags map[string]stri
}

fmt.Println(msg)
fmt.Printf("The devfile \"%s:%s\" from the registry %q will be downloaded.\n", selected.Name, defaultVersion, registry.Name)
fmt.Printf("The devfile \"%s:%s\" from the registry %q will be downloaded.\n", detected.Type.Name, detected.DefaultVersion, detected.Registry.Name)
confirm, err := o.askerClient.AskCorrect()
if err != nil {
return nil, err
}
if !confirm {
return nil, nil
}
return alizer.NewDetectionResult(selected, registry, appPorts, defaultVersion, ""), nil
return alizer.NewDetectionResult(detected.Type, detected.Registry, appPorts, detected.DefaultVersion, ""), nil
}()
resultChan <- result{
location: location,
Expand Down
Loading

0 comments on commit d41364e

Please sign in to comment.