Go: Bump github.com/devfile/alizer from 1.0.1 to 1.2.1 (#7126)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Armel Soro <asoro@redhat.com>
This commit is contained in:
dependabot[bot]
2023-10-23 18:09:52 +02:00
committed by GitHub
parent 4d6a785cff
commit d77ca3e95a
47 changed files with 1513 additions and 619 deletions

View File

@@ -53,7 +53,7 @@ func TestOdoAlizer(t *testing.T) {
path := "/" path := "/"
alizerClient.EXPECT().DetectFramework(gomock.Any(), path). alizerClient.EXPECT().DetectFramework(gomock.Any(), path).
Return(alizer.DetectedFramework{ Return(alizer.DetectedFramework{
Type: model.DevFileType{ Type: model.DevfileType{
Name: "framework-name", Name: "framework-name",
}, },
DefaultVersion: "1.1.1", DefaultVersion: "1.1.1",
@@ -94,7 +94,7 @@ func TestOdoAlizer(t *testing.T) {
path := "/" path := "/"
alizerClient.EXPECT().DetectFramework(gomock.Any(), path). alizerClient.EXPECT().DetectFramework(gomock.Any(), path).
Return(alizer.DetectedFramework{ Return(alizer.DetectedFramework{
Type: model.DevFileType{ Type: model.DevfileType{
Name: "framework-name", Name: "framework-name",
}, },
DefaultVersion: "1.1.1", DefaultVersion: "1.1.1",

2
go.mod
View File

@@ -8,7 +8,7 @@ require (
github.com/AlecAivazis/survey/v2 v2.3.5 github.com/AlecAivazis/survey/v2 v2.3.5
github.com/Xuanwo/go-locale v1.1.0 github.com/Xuanwo/go-locale v1.1.0
github.com/blang/semver v3.5.1+incompatible github.com/blang/semver v3.5.1+incompatible
github.com/devfile/alizer v1.0.1 github.com/devfile/alizer v1.2.1
github.com/devfile/api/v2 v2.2.1-alpha.0.20230413012049-a6c32fca0dbd github.com/devfile/api/v2 v2.2.1-alpha.0.20230413012049-a6c32fca0dbd
github.com/devfile/library/v2 v2.2.1-0.20230524160049-04a8b3fc66c0 github.com/devfile/library/v2 v2.2.1-0.20230524160049-04a8b3fc66c0
github.com/devfile/registry-support/index/generator v0.0.0-20230322155332-33914affc83b github.com/devfile/registry-support/index/generator v0.0.0-20230322155332-33914affc83b

4
go.sum generated
View File

@@ -362,8 +362,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
github.com/devfile/alizer v1.0.1 h1:VcqX8kVGvCbRTzt66S2F132q83e9vbkV2U6ShmUl3Ew= github.com/devfile/alizer v1.2.1 h1:0CrHYGjT0xTw5CYoK/U1pYGmcpKxw9YUUVaqJWF3pZM=
github.com/devfile/alizer v1.0.1/go.mod h1:s08rBJWzwGFAAhdrJP0LduJDfSESfaVn/3dkUlK7Kmc= github.com/devfile/alizer v1.2.1/go.mod h1:kRCsbXuzCwXIbnSR4xpVZDr8Pl8j01rl+gPfkukbf/c=
github.com/devfile/api/v2 v2.0.0-20211021164004-dabee4e633ed/go.mod h1:d99eTN6QxgzihOOFyOZA+VpUyD4Q1pYRYHZ/ci9J96Q= github.com/devfile/api/v2 v2.0.0-20211021164004-dabee4e633ed/go.mod h1:d99eTN6QxgzihOOFyOZA+VpUyD4Q1pYRYHZ/ci9J96Q=
github.com/devfile/api/v2 v2.0.0-20220117162434-6e6e6a8bc14c/go.mod h1:d99eTN6QxgzihOOFyOZA+VpUyD4Q1pYRYHZ/ci9J96Q= github.com/devfile/api/v2 v2.0.0-20220117162434-6e6e6a8bc14c/go.mod h1:d99eTN6QxgzihOOFyOZA+VpUyD4Q1pYRYHZ/ci9J96Q=
github.com/devfile/api/v2 v2.2.0/go.mod h1:dN7xFrOVG+iPqn4UKGibXLd5oVsdE8XyK9OEb5JL3aI= github.com/devfile/api/v2 v2.2.0/go.mod h1:dN7xFrOVG+iPqn4UKGibXLd5oVsdE8XyK9OEb5JL3aI=

View File

@@ -30,13 +30,13 @@ func NewAlizerClient(registryClient registry.Client) *Alizer {
// DetectFramework uses the alizer library in order to detect the devfile // DetectFramework uses the alizer library in order to detect the devfile
// to use depending on the files in the path // to use depending on the files in the path
func (o *Alizer) DetectFramework(ctx context.Context, path string) (DetectedFramework, error) { func (o *Alizer) DetectFramework(ctx context.Context, path string) (DetectedFramework, error) {
types := []model.DevFileType{} types := []model.DevfileType{}
components, err := o.registryClient.ListDevfileStacks(ctx, "", "", "", false, false) components, err := o.registryClient.ListDevfileStacks(ctx, "", "", "", false, false)
if err != nil { if err != nil {
return DetectedFramework{}, err return DetectedFramework{}, err
} }
for _, component := range components.Items { for _, component := range components.Items {
types = append(types, model.DevFileType{ types = append(types, model.DevfileType{
Name: component.Name, Name: component.Name,
Language: component.Language, Language: component.Language,
ProjectType: component.ProjectType, ProjectType: component.ProjectType,
@@ -144,7 +144,7 @@ func (o *Alizer) DetectPorts(path string) ([]int, error) {
return components[0].Ports, nil return components[0].Ports, nil
} }
func NewDetectionResult(typ model.DevFileType, registry api.Registry, appPorts []int, devfileVersion, name string) *api.DetectionResult { func NewDetectionResult(typ model.DevfileType, registry api.Registry, appPorts []int, devfileVersion, name string) *api.DetectionResult {
return &api.DetectionResult{ return &api.DetectionResult{
Devfile: typ.Name, Devfile: typ.Name,
DevfileRegistry: registry.Name, DevfileRegistry: registry.Name,

View File

@@ -4,11 +4,12 @@ import (
"context" "context"
"github.com/devfile/alizer/pkg/apis/model" "github.com/devfile/alizer/pkg/apis/model"
"github.com/redhat-developer/odo/pkg/api" "github.com/redhat-developer/odo/pkg/api"
) )
type DetectedFramework struct { type DetectedFramework struct {
Type model.DevFileType Type model.DevfileType
DefaultVersion string DefaultVersion string
Registry api.Registry Registry api.Registry
Architectures []string Architectures []string

View File

@@ -95,7 +95,7 @@ func TestAlizerBackend_SelectDevfile(t *testing.T) {
alizerClient: func(ctrl *gomock.Controller) alizer.Client { alizerClient: func(ctrl *gomock.Controller) alizer.Client {
alizerClient := alizer.NewMockClient(ctrl) alizerClient := alizer.NewMockClient(ctrl)
alizerClient.EXPECT().DetectFramework(gomock.Any(), gomock.Any()).Return(alizer.DetectedFramework{ alizerClient.EXPECT().DetectFramework(gomock.Any(), gomock.Any()).Return(alizer.DetectedFramework{
Type: model.DevFileType{ Type: model.DevfileType{
Name: "a-devfile-name", Name: "a-devfile-name",
}, },
DefaultVersion: "1.0.0", DefaultVersion: "1.0.0",
@@ -124,7 +124,7 @@ func TestAlizerBackend_SelectDevfile(t *testing.T) {
alizerClient: func(ctrl *gomock.Controller) alizer.Client { alizerClient: func(ctrl *gomock.Controller) alizer.Client {
alizerClient := alizer.NewMockClient(ctrl) alizerClient := alizer.NewMockClient(ctrl)
alizerClient.EXPECT().DetectFramework(gomock.Any(), gomock.Any()).Return(alizer.DetectedFramework{ alizerClient.EXPECT().DetectFramework(gomock.Any(), gomock.Any()).Return(alizer.DetectedFramework{
Type: model.DevFileType{ Type: model.DevfileType{
Name: "a-devfile-name", Name: "a-devfile-name",
}, },
DefaultVersion: "1.0.0", DefaultVersion: "1.0.0",
@@ -178,7 +178,7 @@ func TestAlizerBackend_SelectDevfile(t *testing.T) {
alizerClient: func(ctrl *gomock.Controller) alizer.Client { alizerClient: func(ctrl *gomock.Controller) alizer.Client {
alizerClient := alizer.NewMockClient(ctrl) alizerClient := alizer.NewMockClient(ctrl)
alizerClient.EXPECT().DetectFramework(gomock.Any(), gomock.Any()).Return(alizer.DetectedFramework{ alizerClient.EXPECT().DetectFramework(gomock.Any(), gomock.Any()).Return(alizer.DetectedFramework{
Type: model.DevFileType{ Type: model.DevfileType{
Name: "a-devfile-name", Name: "a-devfile-name",
}, },
DefaultVersion: "1.0.0", DefaultVersion: "1.0.0",

View File

@@ -3,6 +3,7 @@ package alizer
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"github.com/redhat-developer/odo/pkg/alizer" "github.com/redhat-developer/odo/pkg/alizer"
"github.com/redhat-developer/odo/pkg/api" "github.com/redhat-developer/odo/pkg/api"
@@ -56,7 +57,8 @@ func (o *AlizerOptions) RunForJsonOutput(ctx context.Context) (out interface{},
workingDir := odocontext.GetWorkingDirectory(ctx) workingDir := odocontext.GetWorkingDirectory(ctx)
detected, err := o.clientset.AlizerClient.DetectFramework(ctx, workingDir) detected, err := o.clientset.AlizerClient.DetectFramework(ctx, workingDir)
if err != nil { if err != nil {
return nil, err //revive:disable:error-strings This is a top-level error message displayed as is to the end user
return nil, fmt.Errorf("No valid devfile found for project in %s: %w", workingDir, err)
} }
appPorts, err := o.clientset.AlizerClient.DetectPorts(workingDir) appPorts, err := o.clientset.AlizerClient.DetectPorts(workingDir)
if err != nil { if err != nil {

View File

@@ -604,19 +604,23 @@ var _ = Describe("odo init interactive command tests", func() {
lines, err := helper.ExtractLines(output) lines, err := helper.ExtractLines(output)
Expect(err).To(BeNil()) Expect(err).To(BeNil())
Expect(len(lines)).To(BeNumerically(">", 2)) Expect(len(lines)).To(BeNumerically(">", 2), output)
Expect(lines[len(lines)-1]).To(Equal("Your new component 'my-dotnet-app' is ready in the current directory")) Expect(lines[len(lines)-1]).To(Equal("Your new component 'my-dotnet-app' is ready in the current directory"), output)
componentNameQuestionIdx, ok := helper.FindFirstElementIndexMatchingRegExp(lines, ".*Enter component name:.*") componentNameQuestionIdx, ok := helper.FindFirstElementIndexMatchingRegExp(lines, ".*Enter component name:.*")
Expect(ok).To(BeTrue()) Expect(ok).To(BeTrue(),
fmt.Sprintf("'Enter component name:' not found in output below:\n===OUTPUT===\n%s============\n", output))
starterProjectDownloadActionIdx, found := helper.FindFirstElementIndexMatchingRegExp(lines, starterProjectDownloadActionIdx, found := helper.FindFirstElementIndexMatchingRegExp(lines,
".*Downloading starter project \"([^\\s]+)\" \\[.*") ".*Downloading starter project \"([^\\s]+)\" \\[.*")
Expect(found).To(BeTrue()) Expect(found).To(BeTrue(),
Expect(starterProjectDownloadActionIdx).To(SatisfyAll( fmt.Sprintf("'Downloading starter project \"([^\\s]+)\"' not found in output below:\n===OUTPUT===\n%s============\n", output))
Not(BeZero()), Expect(starterProjectDownloadActionIdx).To(
// #5495: component name question should be displayed before starter project is actually downloaded SatisfyAll(
BeNumerically(">", componentNameQuestionIdx), Not(BeZero()),
), "Action 'Downloading starter project' should have been displayed after the last interactive question ('Enter component name')") // #5495: component name question should be displayed before starter project is actually downloaded
BeNumerically(">", componentNameQuestionIdx)),
fmt.Sprintf("Action 'Downloading starter project' should have been displayed after the last interactive question ('Enter component name').\n===OUTPUT===\n%s============\n",
output))
Expect(helper.ListFilesInDir(commonVar.Context)).To(ContainElements("devfile.yaml")) Expect(helper.ListFilesInDir(commonVar.Context)).To(ContainElements("devfile.yaml"))
}) })

View File

@@ -0,0 +1,52 @@
//
// Copyright 2023 Red Hat, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package enricher
import (
"context"
"github.com/devfile/alizer/pkg/apis/model"
)
type DockerEnricher struct{}
type DockerFrameworkDetector interface {
DoPortsDetection(component *model.Component, ctx *context.Context)
}
func (d DockerEnricher) GetSupportedLanguages() []string {
return []string{"dockerfile"}
}
func (d DockerEnricher) DoEnrichLanguage(language *model.Language, _ *[]string) {
// The Dockerfile language does not contain frameworks
return
}
func (d DockerEnricher) DoEnrichComponent(component *model.Component, _ model.DetectionSettings, _ *context.Context) {
projectName := GetDefaultProjectName(component.Path)
component.Name = projectName
var ports []int
ports = GetPortsFromDockerFile(component.Path)
if len(ports) > 0 {
component.Ports = ports
}
return
}
func (d DockerEnricher) IsConfigValidForComponentDetection(language string, config string) bool {
return IsConfigurationValidForLanguage(language, config)
}

View File

@@ -19,18 +19,14 @@ package enricher
import ( import (
"context" "context"
"fmt" "fmt"
"io"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strconv"
"strings" "strings"
"github.com/devfile/alizer/pkg/apis/model" "github.com/devfile/alizer/pkg/apis/model"
"github.com/devfile/alizer/pkg/utils" "github.com/devfile/alizer/pkg/utils"
"github.com/devfile/alizer/pkg/utils/langfiles" "github.com/devfile/alizer/pkg/utils/langfiles"
"github.com/moby/buildkit/frontend/dockerfile/parser"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -93,6 +89,7 @@ func getEnrichers() []Enricher {
&DotNetEnricher{}, &DotNetEnricher{},
&GoEnricher{}, &GoEnricher{},
&PHPEnricher{}, &PHPEnricher{},
&DockerEnricher{},
} }
} }
@@ -123,7 +120,7 @@ func GetDefaultProjectName(path string) string {
// GetPortsFromDockerFile returns a slice of port numbers from Dockerfiles in the given directory. // GetPortsFromDockerFile returns a slice of port numbers from Dockerfiles in the given directory.
func GetPortsFromDockerFile(root string) []int { func GetPortsFromDockerFile(root string) []int {
locations := getLocations(root) locations := utils.GetLocations(root)
for _, location := range locations { for _, location := range locations {
filePath := filepath.Join(root, location) filePath := filepath.Join(root, location)
cleanFilePath := filepath.Clean(filePath) cleanFilePath := filepath.Clean(filePath)
@@ -135,57 +132,12 @@ func GetPortsFromDockerFile(root string) []int {
} }
return nil return nil
}() }()
return getPortsFromReader(file) return utils.ReadPortsFromDockerfile(file)
} }
} }
return []int{} return []int{}
} }
func getLocations(root string) []string {
locations := []string{"Dockerfile", "Containerfile"}
dirItems, err := ioutil.ReadDir(root)
if err != nil {
return locations
}
for _, item := range dirItems {
if strings.HasPrefix(item.Name(), ".") {
continue
}
tmpPath := fmt.Sprintf("%s%s", root, item.Name())
fileInfo, err := os.Stat(tmpPath)
if err != nil {
continue
}
if fileInfo.IsDir() {
locations = append(locations, fmt.Sprintf("%s/%s", item.Name(), "Dockerfile"))
locations = append(locations, fmt.Sprintf("%s/%s", item.Name(), "Containerfile"))
}
}
return locations
}
// getPortsFromReader returns a slice of port numbers.
func getPortsFromReader(file io.Reader) []int {
var ports []int
res, err := parser.Parse(file)
if err != nil {
return ports
}
for _, child := range res.AST.Children {
// check for the potential port number in a Dockerfile/Containerfile
if strings.ToLower(child.Value) == "expose" {
for n := child.Next; n != nil; n = n.Next {
if port, err := strconv.Atoi(n.Value); err == nil {
ports = append(ports, port)
}
}
}
}
return ports
}
// GetPortsFromDockerComposeFile returns a slice of port numbers from a compose file. // GetPortsFromDockerComposeFile returns a slice of port numbers from a compose file.
func GetPortsFromDockerComposeFile(componentPath string, settings model.DetectionSettings) []int { func GetPortsFromDockerComposeFile(componentPath string, settings model.DetectionSettings) []int {
var ports []int var ports []int

View File

@@ -15,7 +15,7 @@ import (
"context" "context"
"encoding/xml" "encoding/xml"
"fmt" "fmt"
"io/ioutil" "io"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@@ -31,6 +31,11 @@ func (d DotNetDetector) GetSupportedFrameworks() []string {
return []string{""} return []string{""}
} }
func (d DotNetDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
// not implemented yet
return []model.ApplicationFileInfo{}
}
// DoFrameworkDetection uses configFilePath to check for the name of the framework // DoFrameworkDetection uses configFilePath to check for the name of the framework
func (d DotNetDetector) DoFrameworkDetection(language *model.Language, configFilePath string) { func (d DotNetDetector) DoFrameworkDetection(language *model.Language, configFilePath string) {
framework := getFrameworks(configFilePath) framework := getFrameworks(configFilePath)
@@ -52,6 +57,7 @@ func (d DotNetDetector) DoFrameworkDetection(language *model.Language, configFil
} }
func (d DotNetDetector) DoPortsDetection(component *model.Component, ctx *context.Context) { func (d DotNetDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
// not implemented yet
} }
func getFrameworks(configFilePath string) string { func getFrameworks(configFilePath string) string {
@@ -60,7 +66,7 @@ func getFrameworks(configFilePath string) string {
if err != nil { if err != nil {
return "" return ""
} }
byteValue, _ := ioutil.ReadAll(xmlFile) byteValue, _ := io.ReadAll(xmlFile)
var proj schema.DotNetProject var proj schema.DotNetProject
err = xml.Unmarshal(byteValue, &proj) err = xml.Unmarshal(byteValue, &proj)

View File

@@ -26,6 +26,17 @@ func (b BeegoDetector) GetSupportedFrameworks() []string {
return []string{"Beego"} return []string{"Beego"}
} }
func (b BeegoDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "conf",
File: "app.conf",
},
}
}
// DoFrameworkDetection uses a tag to check for the framework name // DoFrameworkDetection uses a tag to check for the framework name
func (b BeegoDetector) DoFrameworkDetection(language *model.Language, goMod *modfile.File) { func (b BeegoDetector) DoFrameworkDetection(language *model.Language, goMod *modfile.File) {
if hasFramework(goMod.Require, "github.com/beego/beego") { if hasFramework(goMod.Require, "github.com/beego/beego") {
@@ -40,15 +51,16 @@ type ApplicationPropertiesFile struct {
// DoPortsDetection searches for the port in conf/app.conf // DoPortsDetection searches for the port in conf/app.conf
func (b BeegoDetector) DoPortsDetection(component *model.Component, ctx *context.Context) { func (b BeegoDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
bytes, err := utils.ReadAnyApplicationFile(component.Path, []model.ApplicationFileInfo{ fileContents, err := utils.GetApplicationFileContents(b.GetApplicationFileInfos(component.Path, ctx))
{
Dir: "conf",
File: "app.conf",
},
}, ctx)
if err != nil { if err != nil {
return return
} }
re := regexp.MustCompile(`httpport\s*=\s*(\d+)`) for _, fileContent := range fileContents {
component.Ports = utils.FindAllPortsSubmatch(re, string(bytes), 1) re := regexp.MustCompile(`httpport\s*=\s*(\d+)`)
ports := utils.FindAllPortsSubmatch(re, fileContent, 1)
if len(ports) > 0 {
component.Ports = ports
return
}
}
} }

View File

@@ -26,6 +26,14 @@ func (e EchoDetector) GetSupportedFrameworks() []string {
return []string{"Echo"} return []string{"Echo"}
} }
func (e EchoDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
files, err := utils.GetCachedFilePathsFromRoot(componentPath, ctx)
if err != nil {
return []model.ApplicationFileInfo{}
}
return utils.GenerateApplicationFileFromFilters(files, componentPath, ".go", ctx)
}
// DoFrameworkDetection uses a tag to check for the framework name // DoFrameworkDetection uses a tag to check for the framework name
func (e EchoDetector) DoFrameworkDetection(language *model.Language, goMod *modfile.File) { func (e EchoDetector) DoFrameworkDetection(language *model.Language, goMod *modfile.File) {
if hasFramework(goMod.Require, "github.com/labstack/echo") { if hasFramework(goMod.Require, "github.com/labstack/echo") {
@@ -34,11 +42,10 @@ func (e EchoDetector) DoFrameworkDetection(language *model.Language, goMod *modf
} }
func (e EchoDetector) DoPortsDetection(component *model.Component, ctx *context.Context) { func (e EchoDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
files, err := utils.GetCachedFilePathsFromRoot(component.Path, ctx) fileContents, err := utils.GetApplicationFileContents(e.GetApplicationFileInfos(component.Path, ctx))
if err != nil { if err != nil {
return return
} }
matchRegexRules := model.PortMatchRules{ matchRegexRules := model.PortMatchRules{
MatchIndexRegexes: []model.PortMatchRule{ MatchIndexRegexes: []model.PortMatchRule{
{ {
@@ -58,8 +65,11 @@ func (e EchoDetector) DoPortsDetection(component *model.Component, ctx *context.
}, },
} }
ports := GetPortFromFilesGo(matchRegexRules, files) for _, fileContent := range fileContents {
if len(ports) > 0 { ports := GetPortFromFileGo(matchRegexRules, fileContent)
component.Ports = ports if len(ports) > 0 {
component.Ports = ports
return
}
} }
} }

View File

@@ -26,6 +26,14 @@ func (f FastHttpDetector) GetSupportedFrameworks() []string {
return []string{"FastHttp"} return []string{"FastHttp"}
} }
func (f FastHttpDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
files, err := utils.GetCachedFilePathsFromRoot(componentPath, ctx)
if err != nil {
return []model.ApplicationFileInfo{}
}
return utils.GenerateApplicationFileFromFilters(files, componentPath, ".go", ctx)
}
// DoFrameworkDetection uses a tag to check for the framework name // DoFrameworkDetection uses a tag to check for the framework name
func (f FastHttpDetector) DoFrameworkDetection(language *model.Language, goMod *modfile.File) { func (f FastHttpDetector) DoFrameworkDetection(language *model.Language, goMod *modfile.File) {
if hasFramework(goMod.Require, "github.com/valyala/fasthttp") { if hasFramework(goMod.Require, "github.com/valyala/fasthttp") {
@@ -34,7 +42,7 @@ func (f FastHttpDetector) DoFrameworkDetection(language *model.Language, goMod *
} }
func (f FastHttpDetector) DoPortsDetection(component *model.Component, ctx *context.Context) { func (f FastHttpDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
files, err := utils.GetCachedFilePathsFromRoot(component.Path, ctx) fileContents, err := utils.GetApplicationFileContents(f.GetApplicationFileInfos(component.Path, ctx))
if err != nil { if err != nil {
return return
} }
@@ -47,8 +55,11 @@ func (f FastHttpDetector) DoPortsDetection(component *model.Component, ctx *cont
}, },
}, },
} }
ports := GetPortFromFilesGo(matchRegexRules, files) for _, fileContent := range fileContents {
if len(ports) > 0 { ports := GetPortFromFileGo(matchRegexRules, fileContent)
component.Ports = ports if len(ports) > 0 {
component.Ports = ports
return
}
} }
} }

View File

@@ -26,6 +26,14 @@ func (g GinDetector) GetSupportedFrameworks() []string {
return []string{"Gin"} return []string{"Gin"}
} }
func (g GinDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
files, err := utils.GetCachedFilePathsFromRoot(componentPath, ctx)
if err != nil {
return []model.ApplicationFileInfo{}
}
return utils.GenerateApplicationFileFromFilters(files, componentPath, ".go", ctx)
}
// DoFrameworkDetection uses a tag to check for the framework name // DoFrameworkDetection uses a tag to check for the framework name
func (g GinDetector) DoFrameworkDetection(language *model.Language, goMod *modfile.File) { func (g GinDetector) DoFrameworkDetection(language *model.Language, goMod *modfile.File) {
if hasFramework(goMod.Require, "github.com/gin-gonic/gin") { if hasFramework(goMod.Require, "github.com/gin-gonic/gin") {
@@ -34,7 +42,7 @@ func (g GinDetector) DoFrameworkDetection(language *model.Language, goMod *modfi
} }
func (g GinDetector) DoPortsDetection(component *model.Component, ctx *context.Context) { func (g GinDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
files, err := utils.GetCachedFilePathsFromRoot(component.Path, ctx) fileContents, err := utils.GetApplicationFileContents(g.GetApplicationFileInfos(component.Path, ctx))
if err != nil { if err != nil {
return return
} }
@@ -48,8 +56,11 @@ func (g GinDetector) DoPortsDetection(component *model.Component, ctx *context.C
}, },
} }
ports := GetPortFromFilesGo(matchRegexRules, files) for _, fileContent := range fileContents {
if len(ports) > 0 { ports := GetPortFromFileGo(matchRegexRules, fileContent)
component.Ports = ports if len(ports) > 0 {
component.Ports = ports
return
}
} }
} }

View File

@@ -13,8 +13,6 @@ package enricher
import ( import (
"context" "context"
"os"
"path/filepath"
"regexp" "regexp"
"strings" "strings"
@@ -38,7 +36,11 @@ func DoGoPortsDetection(component *model.Component, ctx *context.Context) {
if err != nil { if err != nil {
return return
} }
appFileInfos := utils.GenerateApplicationFileFromFilters(files, component.Path, ".go", ctx)
fileContents, err := utils.GetApplicationFileContents(appFileInfos)
if err != nil {
return
}
matchRegexRules := model.PortMatchRules{ matchRegexRules := model.PortMatchRules{
MatchIndexRegexes: []model.PortMatchRule{ MatchIndexRegexes: []model.PortMatchRule{
{ {
@@ -58,9 +60,12 @@ func DoGoPortsDetection(component *model.Component, ctx *context.Context) {
}, },
} }
ports := GetPortFromFilesGo(matchRegexRules, files) for _, fileContent := range fileContents {
if len(ports) > 0 { ports := GetPortFromFileGo(matchRegexRules, fileContent)
component.Ports = ports if len(ports) > 0 {
component.Ports = ports
return
}
} }
} }
@@ -135,20 +140,3 @@ func GetPortWithMatchIndexesGo(content string, matchIndexes []int, toBeReplaced
return -1 return -1
} }
// GetPortFromFilesGo loops through a list of paths and tries to find a port matching the
// given set PortMatchRules
func GetPortFromFilesGo(matchRegexRules model.PortMatchRules, files []string) []int {
for _, file := range files {
cleanFile := filepath.Clean(file)
bytes, err := os.ReadFile(cleanFile)
if err != nil {
continue
}
ports := GetPortFromFileGo(matchRegexRules, string(bytes))
if len(ports) > 0 {
return ports
}
}
return []int{}
}

View File

@@ -26,6 +26,14 @@ func (g GoFiberDetector) GetSupportedFrameworks() []string {
return []string{"GoFiber"} return []string{"GoFiber"}
} }
func (g GoFiberDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
files, err := utils.GetCachedFilePathsFromRoot(componentPath, ctx)
if err != nil {
return []model.ApplicationFileInfo{}
}
return utils.GenerateApplicationFileFromFilters(files, componentPath, ".go", ctx)
}
// DoFrameworkDetection uses a tag to check for the framework name // DoFrameworkDetection uses a tag to check for the framework name
func (g GoFiberDetector) DoFrameworkDetection(language *model.Language, goMod *modfile.File) { func (g GoFiberDetector) DoFrameworkDetection(language *model.Language, goMod *modfile.File) {
if hasFramework(goMod.Require, "github.com/gofiber/fiber") { if hasFramework(goMod.Require, "github.com/gofiber/fiber") {
@@ -34,7 +42,7 @@ func (g GoFiberDetector) DoFrameworkDetection(language *model.Language, goMod *m
} }
func (g GoFiberDetector) DoPortsDetection(component *model.Component, ctx *context.Context) { func (g GoFiberDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
files, err := utils.GetCachedFilePathsFromRoot(component.Path, ctx) fileContents, err := utils.GetApplicationFileContents(g.GetApplicationFileInfos(component.Path, ctx))
if err != nil { if err != nil {
return return
} }
@@ -47,8 +55,12 @@ func (g GoFiberDetector) DoPortsDetection(component *model.Component, ctx *conte
}, },
}, },
} }
ports := GetPortFromFilesGo(matchRegexRules, files)
if len(ports) > 0 { for _, fileContent := range fileContents {
component.Ports = ports ports := GetPortFromFileGo(matchRegexRules, fileContent)
if len(ports) > 0 {
component.Ports = ports
return
}
} }
} }

View File

@@ -26,6 +26,14 @@ func (m MuxDetector) GetSupportedFrameworks() []string {
return []string{"Mux"} return []string{"Mux"}
} }
func (m MuxDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
files, err := utils.GetCachedFilePathsFromRoot(componentPath, ctx)
if err != nil {
return []model.ApplicationFileInfo{}
}
return utils.GenerateApplicationFileFromFilters(files, componentPath, ".go", ctx)
}
// DoFrameworkDetection uses a tag to check for the framework name // DoFrameworkDetection uses a tag to check for the framework name
func (m MuxDetector) DoFrameworkDetection(language *model.Language, goMod *modfile.File) { func (m MuxDetector) DoFrameworkDetection(language *model.Language, goMod *modfile.File) {
if hasFramework(goMod.Require, "github.com/gorilla/mux") { if hasFramework(goMod.Require, "github.com/gorilla/mux") {
@@ -34,7 +42,7 @@ func (m MuxDetector) DoFrameworkDetection(language *model.Language, goMod *modfi
} }
func (m MuxDetector) DoPortsDetection(component *model.Component, ctx *context.Context) { func (m MuxDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
files, err := utils.GetCachedFilePathsFromRoot(component.Path, ctx) fileContents, err := utils.GetApplicationFileContents(m.GetApplicationFileInfos(component.Path, ctx))
if err != nil { if err != nil {
return return
} }
@@ -54,8 +62,11 @@ func (m MuxDetector) DoPortsDetection(component *model.Component, ctx *context.C
}, },
} }
ports := GetPortFromFilesGo(matchRegexRules, files) for _, fileContent := range fileContents {
if len(ports) > 0 { ports := GetPortFromFileGo(matchRegexRules, fileContent)
component.Ports = ports if len(ports) > 0 {
component.Ports = ports
return
}
} }
} }

View File

@@ -15,6 +15,7 @@ import (
"regexp" "regexp"
"strings" "strings"
"github.com/devfile/alizer/pkg/schema"
"github.com/devfile/alizer/pkg/utils" "github.com/devfile/alizer/pkg/utils"
) )
@@ -36,13 +37,8 @@ func hasFramework(configFile, groupId, artifactId string) (bool, error) {
// GetPortsForJBossFrameworks tries to detect any port information inside javaOpts of configuration // GetPortsForJBossFrameworks tries to detect any port information inside javaOpts of configuration
// of a given profiles plugin // of a given profiles plugin
func GetPortsForJBossFrameworks(pomFilePath, pluginArtifactId, pluginGroupId string) string { func GetPortsForJBossFrameworks(pom schema.Pom, pluginArtifactId string, pluginGroupId string) string {
portPlaceholder := "" portPlaceholder := ""
pom, err := utils.GetPomFileContent(pomFilePath)
if err != nil {
return portPlaceholder
}
re := regexp.MustCompile(`jboss.https?.port=\d*`) re := regexp.MustCompile(`jboss.https?.port=\d*`)
// Check for port configuration inside profiles // Check for port configuration inside profiles
for _, profile := range pom.Profiles.Profile { for _, profile := range pom.Profiles.Profile {

View File

@@ -13,8 +13,10 @@ package enricher
import ( import (
"context" "context"
"encoding/xml"
"github.com/devfile/alizer/pkg/apis/model" "github.com/devfile/alizer/pkg/apis/model"
"github.com/devfile/alizer/pkg/schema"
"github.com/devfile/alizer/pkg/utils" "github.com/devfile/alizer/pkg/utils"
) )
@@ -24,6 +26,17 @@ func (o JBossEAPDetector) GetSupportedFrameworks() []string {
return []string{"JBoss EAP"} return []string{"JBoss EAP"}
} }
func (o JBossEAPDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "",
File: "pom.xml",
},
}
}
// DoFrameworkDetection uses the groupId and artifactId to check for the framework name // DoFrameworkDetection uses the groupId and artifactId to check for the framework name
func (o JBossEAPDetector) DoFrameworkDetection(language *model.Language, config string) { func (o JBossEAPDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFwk, _ := hasFramework(config, "org.jboss.eap.plugins", "eap-maven-plugin"); hasFwk { if hasFwk, _ := hasFramework(config, "org.jboss.eap.plugins", "eap-maven-plugin"); hasFwk {
@@ -34,22 +47,35 @@ func (o JBossEAPDetector) DoFrameworkDetection(language *model.Language, config
func (o JBossEAPDetector) DoPortsDetection(component *model.Component, ctx *context.Context) { func (o JBossEAPDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
ports := []int{} ports := []int{}
// Fetch the content of xml for this component // Fetch the content of xml for this component
paths, err := utils.GetCachedFilePathsFromRoot(component.Path, ctx) appFileInfos := o.GetApplicationFileInfos(component.Path, ctx)
if err != nil { if len(appFileInfos) == 0 {
return
}
pomXML := utils.GetFile(&paths, "pom.xml")
portPlaceholder := GetPortsForJBossFrameworks(pomXML, "eap-maven-plugin", "org.jboss.eap.plugins")
if portPlaceholder == "" {
return return
} }
if port, err := utils.GetValidPort(portPlaceholder); err == nil { for _, appFileInfo := range appFileInfos {
ports = append(ports, port) fileBytes, err := utils.GetApplicationFileBytes(appFileInfo)
} if err != nil {
continue
}
if len(ports) > 0 { var pom schema.Pom
component.Ports = ports err = xml.Unmarshal(fileBytes, &pom)
return if err != nil {
continue
}
portPlaceholder := GetPortsForJBossFrameworks(pom, "eap-maven-plugin", "org.jboss.eap.plugins")
if portPlaceholder == "" {
continue
}
if port, err := utils.GetValidPort(portPlaceholder); err == nil {
ports = append(ports, port)
}
if len(ports) > 0 {
component.Ports = ports
return
}
} }
} }

View File

@@ -22,22 +22,27 @@ import (
type MicronautDetector struct{} type MicronautDetector struct{}
type MicronautApplicationProps struct {
Micronaut struct {
Server struct {
Port int `yaml:"port,omitempty"`
SSL struct {
Enabled bool `yaml:"enabled,omitempty"`
Port int `yaml:"port,omitempty"`
} `yaml:"ssl,omitempty"`
} `yaml:"server,omitempty"`
} `yaml:"micronaut,omitempty"`
}
func (m MicronautDetector) GetSupportedFrameworks() []string { func (m MicronautDetector) GetSupportedFrameworks() []string {
return []string{"Micronaut"} return []string{"Micronaut"}
} }
func (m MicronautDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "src/main/resources",
File: "application.yml",
},
{
Context: ctx,
Root: componentPath,
Dir: "src/main/resources",
File: "application.yaml",
},
}
}
// DoFrameworkDetection uses the groupId to check for the framework name // DoFrameworkDetection uses the groupId to check for the framework name
func (m MicronautDetector) DoFrameworkDetection(language *model.Language, config string) { func (m MicronautDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFwk, _ := hasFramework(config, "io.micronaut", ""); hasFwk { if hasFwk, _ := hasFramework(config, "io.micronaut", ""); hasFwk {
@@ -54,28 +59,36 @@ func (m MicronautDetector) DoPortsDetection(component *model.Component, ctx *con
return return
} }
bytes, err := utils.ReadAnyApplicationFile(component.Path, []model.ApplicationFileInfo{ // check if port is set on dockerfile as env var
{ ports = getMicronautPortsFromEnvDockerfile(component.Path)
Dir: "src/main/resources",
File: "application.yml",
},
{
Dir: "src/main/resources",
File: "application.yaml",
},
}, ctx)
if err != nil {
return
}
ports = getMicronautPortsFromBytes(bytes)
if len(ports) > 0 { if len(ports) > 0 {
component.Ports = ports component.Ports = ports
return
}
// check source code
appFileInfos := m.GetApplicationFileInfos(component.Path, ctx)
if len(appFileInfos) == 0 {
return
}
for _, appFileInfo := range appFileInfos {
fileBytes, err := utils.GetApplicationFileBytes(appFileInfo)
if err != nil {
continue
}
ports = getMicronautPortsFromBytes(fileBytes)
if len(ports) > 0 {
component.Ports = ports
return
}
} }
} }
func getMicronautPortsFromBytes(bytes []byte) []int { func getMicronautPortsFromBytes(bytes []byte) []int {
var ports []int var ports []int
var data MicronautApplicationProps var data model.MicronautApplicationProps
err := yaml.Unmarshal(bytes, &data) err := yaml.Unmarshal(bytes, &data)
if err != nil { if err != nil {
return []int{} return []int{}
@@ -97,3 +110,24 @@ func getMicronautPortsFromEnvs() []int {
} }
return utils.GetValidPortsFromEnvs(envs) return utils.GetValidPortsFromEnvs(envs)
} }
func getMicronautPortsFromEnvDockerfile(path string) []int {
envVars, err := utils.GetEnvVarsFromDockerFile(path)
if err != nil {
return nil
}
sslEnabled := ""
envs := []string{"MICRONAUT_SERVER_PORT"}
for _, envVar := range envVars {
if envVar.Name == "MICRONAUT_SERVER_SSL_ENABLED" {
sslEnabled = envVar.Value
break
}
}
if sslEnabled == "true" {
envs = append(envs, "MICRONAUT_SERVER_SSL_PORT")
}
return utils.GetValidPortsFromEnvDockerfile(envs, envVars)
}

View File

@@ -21,17 +21,27 @@ import (
type OpenLibertyDetector struct{} type OpenLibertyDetector struct{}
type ServerXml struct {
HttpEndpoint struct {
HttpPort string `xml:"httpPort,attr"`
HttpsPort string `xml:"httpsPort,attr"`
} `xml:"httpEndpoint"`
}
func (o OpenLibertyDetector) GetSupportedFrameworks() []string { func (o OpenLibertyDetector) GetSupportedFrameworks() []string {
return []string{"OpenLiberty"} return []string{"OpenLiberty"}
} }
func (o OpenLibertyDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "",
File: "server.xml",
},
{
Context: ctx,
Root: componentPath,
Dir: "src/main/liberty/config",
File: "server.xml",
},
}
}
// DoFrameworkDetection uses the groupId to check for the framework name // DoFrameworkDetection uses the groupId to check for the framework name
func (o OpenLibertyDetector) DoFrameworkDetection(language *model.Language, config string) { func (o OpenLibertyDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFwk, _ := hasFramework(config, "io.openliberty", ""); hasFwk { if hasFwk, _ := hasFramework(config, "io.openliberty", ""); hasFwk {
@@ -41,26 +51,26 @@ func (o OpenLibertyDetector) DoFrameworkDetection(language *model.Language, conf
// DoPortsDetection searches for the port in src/main/liberty/config/server.xml and /server.xml // DoPortsDetection searches for the port in src/main/liberty/config/server.xml and /server.xml
func (o OpenLibertyDetector) DoPortsDetection(component *model.Component, ctx *context.Context) { func (o OpenLibertyDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
bytes, err := utils.ReadAnyApplicationFile(component.Path, []model.ApplicationFileInfo{ appFileInfos := o.GetApplicationFileInfos(component.Path, ctx)
{ if len(appFileInfos) == 0 {
Dir: "",
File: "server.xml",
},
{
Dir: "src/main/liberty/config",
File: "server.xml",
},
}, ctx)
if err != nil {
return return
} }
var data ServerXml
err = xml.Unmarshal(bytes, &data) for _, appFileInfo := range appFileInfos {
if err != nil { fileBytes, err := utils.GetApplicationFileBytes(appFileInfo)
return if err != nil {
} continue
ports := utils.GetValidPorts([]string{data.HttpEndpoint.HttpPort, data.HttpEndpoint.HttpsPort}) }
if len(ports) > 0 {
component.Ports = ports var data model.OpenLibertyServerXml
err = xml.Unmarshal(fileBytes, &data)
if err != nil {
continue
}
ports := utils.GetValidPorts([]string{data.HttpEndpoint.HttpPort, data.HttpEndpoint.HttpsPort})
if len(ports) > 0 {
component.Ports = ports
return
}
} }
} }

View File

@@ -14,7 +14,6 @@ package enricher
import ( import (
"context" "context"
"errors" "errors"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
@@ -25,24 +24,33 @@ import (
type QuarkusDetector struct{} type QuarkusDetector struct{}
type QuarkusApplicationYaml struct {
Quarkus QuarkusHttp `yaml:"quarkus,omitempty"`
}
type QuarkusHttp struct {
Http QuarkusHttpPort `yaml:"http,omitempty"`
}
type QuarkusHttpPort struct {
Port int `yaml:"port,omitempty"`
InsecureRequests string `yaml:"insecure-requests,omitempty"`
SSLPort int `yaml:"ssl-port,omitempty"`
}
func (q QuarkusDetector) GetSupportedFrameworks() []string { func (q QuarkusDetector) GetSupportedFrameworks() []string {
return []string{"Quarkus"} return []string{"Quarkus"}
} }
func (q QuarkusDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "src/main/resources",
File: "application.properties",
},
{
Context: ctx,
Root: componentPath,
Dir: "src/main/resources",
File: "application.yml",
},
{
Context: ctx,
Root: componentPath,
Dir: "src/main/resources",
File: "application.yaml",
},
}
}
// DoFrameworkDetection uses the groupId to check for the framework name // DoFrameworkDetection uses the groupId to check for the framework name
func (q QuarkusDetector) DoFrameworkDetection(language *model.Language, config string) { func (q QuarkusDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFwk, _ := hasFramework(config, "io.quarkus", ""); hasFwk { if hasFwk, _ := hasFramework(config, "io.quarkus", ""); hasFwk {
@@ -59,6 +67,14 @@ func (q QuarkusDetector) DoPortsDetection(component *model.Component, ctx *conte
component.Ports = ports component.Ports = ports
return return
} }
// check if port is set on env var of a dockerfile
ports = getQuarkusPortsFromEnvDockerfile(component.Path)
if len(ports) > 0 {
component.Ports = ports
return
}
// check if port is set on .env file // check if port is set on .env file
insecureRequestEnabled := utils.GetStringValueFromEnvFile(component.Path, `QUARKUS_HTTP_INSECURE_REQUESTS=(\w*)`) insecureRequestEnabled := utils.GetStringValueFromEnvFile(component.Path, `QUARKUS_HTTP_INSECURE_REQUESTS=(\w*)`)
regexes := []string{`QUARKUS_HTTP_SSL_PORT=(\d*)`} regexes := []string{`QUARKUS_HTTP_SSL_PORT=(\d*)`}
@@ -71,20 +87,13 @@ func (q QuarkusDetector) DoPortsDetection(component *model.Component, ctx *conte
return return
} }
applicationFile := utils.GetAnyApplicationFilePath(component.Path, []model.ApplicationFileInfo{ appFileInfos := q.GetApplicationFileInfos(component.Path, ctx)
{ if len(appFileInfos) == 0 {
Dir: "src/main/resources", return
File: "application.properties", }
},
{ // case: no port found as env var. Look into source code.
Dir: "src/main/resources", applicationFile := utils.GetAnyApplicationFilePath(component.Path, appFileInfos, ctx)
File: "application.yml",
},
{
Dir: "src/main/resources",
File: "application.yaml",
},
}, ctx)
if applicationFile == "" { if applicationFile == "" {
return return
} }
@@ -110,6 +119,27 @@ func getQuarkusPortsFromEnvs() []int {
return utils.GetValidPortsFromEnvs(envs) return utils.GetValidPortsFromEnvs(envs)
} }
func getQuarkusPortsFromEnvDockerfile(path string) []int {
envVars, err := utils.GetEnvVarsFromDockerFile(path)
if err != nil {
return nil
}
insecureRequestEnabled := ""
envs := []string{"QUARKUS_HTTP_SSL_PORT"}
for _, envVar := range envVars {
if envVar.Name == "QUARKUS_HTTP_INSECURE_REQUESTS" {
insecureRequestEnabled = envVar.Value
break
}
}
if insecureRequestEnabled == "true" {
envs = append(envs, "QUARKUS_HTTP_PORT")
}
return utils.GetValidPortsFromEnvDockerfile(envs, envVars)
}
func getServerPortsFromQuarkusPropertiesFile(file string) ([]int, error) { func getServerPortsFromQuarkusPropertiesFile(file string) ([]int, error) {
var ports []int var ports []int
props, err := utils.ConvertPropertiesFileAsPathToMap(file) props, err := utils.ConvertPropertiesFileAsPathToMap(file)
@@ -135,11 +165,11 @@ func getServerPortsFromQuarkusPropertiesFile(file string) ([]int, error) {
} }
func getServerPortsFromQuarkusApplicationYamlFile(file string) ([]int, error) { func getServerPortsFromQuarkusApplicationYamlFile(file string) ([]int, error) {
yamlFile, err := ioutil.ReadFile(file) yamlFile, err := os.ReadFile(filepath.Clean(file))
if err != nil { if err != nil {
return []int{}, err return []int{}, err
} }
var data QuarkusApplicationYaml var data model.QuarkusApplicationYaml
err = yaml.Unmarshal(yamlFile, &data) err = yaml.Unmarshal(yamlFile, &data)
if err != nil { if err != nil {
return []int{}, err return []int{}, err

View File

@@ -14,7 +14,7 @@ package enricher
import ( import (
"context" "context"
"errors" "errors"
"io/ioutil" "os"
"path/filepath" "path/filepath"
"github.com/devfile/alizer/pkg/apis/model" "github.com/devfile/alizer/pkg/apis/model"
@@ -24,19 +24,33 @@ import (
type SpringDetector struct{} type SpringDetector struct{}
type ApplicationProsServer struct {
Server struct {
Port int `yaml:"port,omitempty"`
Http struct {
Port int `yaml:"port,omitempty"`
} `yaml:"http,omitempty"`
} `yaml:"server,omitempty"`
}
func (s SpringDetector) GetSupportedFrameworks() []string { func (s SpringDetector) GetSupportedFrameworks() []string {
return []string{"Spring", "Spring Boot"} return []string{"Spring", "Spring Boot"}
} }
func (s SpringDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "src/main/resources",
File: "application.properties",
},
{
Context: ctx,
Root: componentPath,
Dir: "src/main/resources",
File: "application.yml",
},
{
Context: ctx,
Root: componentPath,
Dir: "src/main/resources",
File: "application.yaml",
},
}
}
// DoFrameworkDetection uses the groupId to check for the framework name // DoFrameworkDetection uses the groupId to check for the framework name
func (s SpringDetector) DoFrameworkDetection(language *model.Language, config string) { func (s SpringDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFwk, _ := hasFramework(config, "org.springframework", ""); hasFwk { if hasFwk, _ := hasFramework(config, "org.springframework", ""); hasFwk {
@@ -47,30 +61,31 @@ func (s SpringDetector) DoFrameworkDetection(language *model.Language, config st
// DoPortsDetection searches for ports in the env var and // DoPortsDetection searches for ports in the env var and
// src/main/resources/application.properties, or src/main/resources/application.yaml // src/main/resources/application.properties, or src/main/resources/application.yaml
func (s SpringDetector) DoPortsDetection(component *model.Component, ctx *context.Context) { func (s SpringDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
// check if port is set on env var // case: port is set on env var
ports := getSpringPortsFromEnvs() ports := getSpringPortsFromEnvs()
if len(ports) > 0 { if len(ports) > 0 {
component.Ports = ports component.Ports = ports
return return
} }
applicationFile := utils.GetAnyApplicationFilePath(component.Path, []model.ApplicationFileInfo{ // check if port is set on env var of dockerfile
{ ports = getSpringPortsFromEnvDockerfile(component.Path)
Dir: "src/main/resources", if len(ports) > 0 {
File: "application.properties", component.Ports = ports
}, return
{ }
Dir: "src/main/resources",
File: "application.yml", // check if port is set inside application file
}, appFileInfos := s.GetApplicationFileInfos(component.Path, ctx)
{ if len(appFileInfos) == 0 {
Dir: "src/main/resources", return
File: "application.yaml", }
},
}, ctx) applicationFile := utils.GetAnyApplicationFilePath(component.Path, appFileInfos, ctx)
if applicationFile == "" { if applicationFile == "" {
return return
} }
var err error var err error
if filepath.Ext(applicationFile) == ".yml" || filepath.Ext(applicationFile) == ".yaml" { if filepath.Ext(applicationFile) == ".yml" || filepath.Ext(applicationFile) == ".yaml" {
ports, err = getServerPortsFromYamlFile(applicationFile) ports, err = getServerPortsFromYamlFile(applicationFile)
@@ -88,6 +103,15 @@ func getSpringPortsFromEnvs() []int {
return utils.GetValidPortsFromEnvs([]string{"SERVER_PORT", "SERVER_HTTP_PORT"}) return utils.GetValidPortsFromEnvs([]string{"SERVER_PORT", "SERVER_HTTP_PORT"})
} }
func getSpringPortsFromEnvDockerfile(path string) []int {
envVars, err := utils.GetEnvVarsFromDockerFile(path)
if err != nil {
return nil
}
envs := []string{"SERVER_PORT", "SERVER_HTTP_PORT"}
return utils.GetValidPortsFromEnvDockerfile(envs, envVars)
}
func getServerPortsFromPropertiesFile(file string) ([]int, error) { func getServerPortsFromPropertiesFile(file string) ([]int, error) {
props, err := utils.ConvertPropertiesFileAsPathToMap(file) props, err := utils.ConvertPropertiesFileAsPathToMap(file)
if err != nil { if err != nil {
@@ -122,11 +146,11 @@ func getPortFromMap(props map[string]string, key string) int {
} }
func getServerPortsFromYamlFile(file string) ([]int, error) { func getServerPortsFromYamlFile(file string) ([]int, error) {
yamlFile, err := ioutil.ReadFile(file) yamlFile, err := os.ReadFile(filepath.Clean(file))
if err != nil { if err != nil {
return []int{}, err return []int{}, err
} }
var data ApplicationProsServer var data model.SpringApplicationProsServer
err = yaml.Unmarshal(yamlFile, &data) err = yaml.Unmarshal(yamlFile, &data)
if err != nil { if err != nil {
return []int{}, err return []int{}, err

View File

@@ -21,18 +21,21 @@ import (
type VertxDetector struct{} type VertxDetector struct{}
type VertxConf struct {
Port int `json:"http.port,omitempty"`
ServerConfig ServerConfig `json:"http.server,omitempty"`
}
type ServerConfig struct {
Port int `json:"http.server.port,omitempty"`
}
func (v VertxDetector) GetSupportedFrameworks() []string { func (v VertxDetector) GetSupportedFrameworks() []string {
return []string{"Vertx"} return []string{"Vertx"}
} }
func (v VertxDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "src/main/conf",
File: ".*.json",
},
}
}
// DoFrameworkDetection uses the groupId to check for the framework name // DoFrameworkDetection uses the groupId to check for the framework name
func (v VertxDetector) DoFrameworkDetection(language *model.Language, config string) { func (v VertxDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFwk, _ := hasFramework(config, "io.vertx", ""); hasFwk { if hasFwk, _ := hasFramework(config, "io.vertx", ""); hasFwk {
@@ -42,24 +45,31 @@ func (v VertxDetector) DoFrameworkDetection(language *model.Language, config str
// DoPortsDetection searches for the port in json files under src/main/conf/ // DoPortsDetection searches for the port in json files under src/main/conf/
func (v VertxDetector) DoPortsDetection(component *model.Component, ctx *context.Context) { func (v VertxDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
bytes, err := utils.ReadAnyApplicationFile(component.Path, []model.ApplicationFileInfo{ appFileInfos := v.GetApplicationFileInfos(component.Path, ctx)
{ if len(appFileInfos) == 0 {
Dir: "src/main/conf",
File: ".*.json",
},
}, ctx)
if err != nil {
return return
} }
var data VertxConf
err = json.Unmarshal(bytes, &data)
if err != nil {
return
}
if utils.IsValidPort(data.Port) {
component.Ports = []int{data.Port}
} else if utils.IsValidPort(data.ServerConfig.Port) {
component.Ports = []int{data.ServerConfig.Port}
}
for _, appFileInfo := range appFileInfos {
fileBytes, err := utils.GetApplicationFileBytes(appFileInfo)
if err != nil {
continue
}
var data model.VertxConf
err = json.Unmarshal(fileBytes, &data)
if err != nil {
continue
}
if utils.IsValidPort(data.Port) {
component.Ports = []int{data.Port}
return
}
if utils.IsValidPort(data.ServerConfig.Port) {
component.Ports = []int{data.ServerConfig.Port}
return
}
}
} }

View File

@@ -13,19 +13,30 @@ package enricher
import ( import (
"context" "context"
"encoding/xml"
"github.com/devfile/alizer/pkg/apis/model" "github.com/devfile/alizer/pkg/apis/model"
"github.com/devfile/alizer/pkg/schema"
"github.com/devfile/alizer/pkg/utils" "github.com/devfile/alizer/pkg/utils"
) )
type WildFlyDetector struct{} type WildFlyDetector struct{}
func (o WildFlyDetector) GetSupportedFrameworks() []string { func (w WildFlyDetector) GetSupportedFrameworks() []string {
return []string{"WildFly"} return []string{"WildFly"}
} }
func (w WildFlyDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
files, err := utils.GetCachedFilePathsFromRoot(componentPath, ctx)
if err != nil {
return []model.ApplicationFileInfo{}
}
pomXML := utils.GetFile(&files, "pom.xml")
return utils.GenerateApplicationFileFromFilters([]string{pomXML}, componentPath, "", ctx)
}
// DoFrameworkDetection uses the groupId and artifactId to check for the framework name // DoFrameworkDetection uses the groupId and artifactId to check for the framework name
func (o WildFlyDetector) DoFrameworkDetection(language *model.Language, config string) { func (w WildFlyDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFwk, _ := hasFramework(config, "org.wildfly.plugins", "wildfly-maven-plugin"); hasFwk { if hasFwk, _ := hasFramework(config, "org.wildfly.plugins", "wildfly-maven-plugin"); hasFwk {
language.Frameworks = append(language.Frameworks, "WildFly") language.Frameworks = append(language.Frameworks, "WildFly")
} }
@@ -33,25 +44,38 @@ func (o WildFlyDetector) DoFrameworkDetection(language *model.Language, config s
// DoPortsDetection for wildfly fetches the pom.xml and tries to find any javaOpts under // DoPortsDetection for wildfly fetches the pom.xml and tries to find any javaOpts under
// the wildfly-maven-plugin profiles. If there is one it looks if jboss.http.port is defined. // the wildfly-maven-plugin profiles. If there is one it looks if jboss.http.port is defined.
func (o WildFlyDetector) DoPortsDetection(component *model.Component, ctx *context.Context) { func (w WildFlyDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
ports := []int{} ports := []int{}
// Fetch the content of xml for this component // Fetch the content of xml for this component
paths, err := utils.GetCachedFilePathsFromRoot(component.Path, ctx) appFileInfos := w.GetApplicationFileInfos(component.Path, ctx)
if err != nil { if len(appFileInfos) == 0 {
return
}
pomXML := utils.GetFile(&paths, "pom.xml")
portPlaceholder := GetPortsForJBossFrameworks(pomXML, "wildfly-maven-plugin", "org.wildfly.plugins")
if portPlaceholder == "" {
return return
} }
if port, err := utils.GetValidPort(portPlaceholder); err == nil { for _, appFileInfo := range appFileInfos {
ports = append(ports, port) fileBytes, err := utils.GetApplicationFileBytes(appFileInfo)
} if err != nil {
continue
}
if len(ports) > 0 { var pom schema.Pom
component.Ports = ports err = xml.Unmarshal(fileBytes, &pom)
return if err != nil {
continue
}
portPlaceholder := GetPortsForJBossFrameworks(pom, "wildfly-maven-plugin", "org.wildfly.plugins")
if portPlaceholder == "" {
continue
}
if port, err := utils.GetValidPort(portPlaceholder); err == nil {
ports = append(ports, port)
}
if len(ports) > 0 {
component.Ports = ports
return
}
} }
} }

View File

@@ -19,35 +19,29 @@ import (
"github.com/devfile/alizer/pkg/utils" "github.com/devfile/alizer/pkg/utils"
) )
type AngularCliJson struct {
Defaults struct {
Serve HostPort `json:"serve"`
} `json:"defaults"`
}
type AngularJson struct {
Projects map[string]ProjectBody `json:"projects"`
}
type ProjectBody struct {
Architect struct {
Serve struct {
Options HostPort `json:"options"`
} `json:"serve"`
} `json:"architect"`
}
type HostPort struct {
Host string `json:"host"`
Port int `json:"port"`
}
type AngularDetector struct{} type AngularDetector struct{}
func (a AngularDetector) GetSupportedFrameworks() []string { func (a AngularDetector) GetSupportedFrameworks() []string {
return []string{"Angular"} return []string{"Angular"}
} }
func (a AngularDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "",
File: "angular.json",
},
{
Context: ctx,
Root: componentPath,
Dir: "",
File: "angular-cli.json",
},
}
}
// DoFrameworkDetection uses a tag to check for the framework name // DoFrameworkDetection uses a tag to check for the framework name
func (a AngularDetector) DoFrameworkDetection(language *model.Language, config string) { func (a AngularDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFramework(config, "angular") { if hasFramework(config, "angular") {
@@ -58,17 +52,26 @@ func (a AngularDetector) DoFrameworkDetection(language *model.Language, config s
// DoPortsDetection searches for the port in angular.json, package.json, and angular-cli.json // DoPortsDetection searches for the port in angular.json, package.json, and angular-cli.json
func (a AngularDetector) DoPortsDetection(component *model.Component, ctx *context.Context) { func (a AngularDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
// check if port is set on angular.json file // check if port is set on angular.json file
bytes, err := utils.ReadAnyApplicationFile(component.Path, []model.ApplicationFileInfo{ appFileInfos := a.GetApplicationFileInfos(component.Path, ctx)
{ if len(appFileInfos) == 0 {
Dir: "", return
File: "angular.json", }
},
}, ctx) appFileInfo, err := utils.GetApplicationFileInfo(appFileInfos, "angular.json")
if err != nil { if err != nil {
return return
} }
var data AngularJson
err = json.Unmarshal(bytes, &data) fileBytes, err := utils.GetApplicationFileBytes(appFileInfo)
if err != nil {
return
}
if err != nil {
return
}
var data model.AngularJson
err = json.Unmarshal(fileBytes, &data)
if err != nil { if err != nil {
return return
} }
@@ -89,17 +92,18 @@ func (a AngularDetector) DoPortsDetection(component *model.Component, ctx *conte
} }
// check if port is set on angular-cli.json file // check if port is set on angular-cli.json file
bytes, err = utils.ReadAnyApplicationFile(component.Path, []model.ApplicationFileInfo{ appFileInfoCli, err := utils.GetApplicationFileInfo(appFileInfos, "angular-cli.json")
{
Dir: "",
File: "angular-cli.json",
},
}, ctx)
if err != nil { if err != nil {
return return
} }
var dataCli AngularCliJson
err = json.Unmarshal(bytes, &dataCli) fileBytesCli, err := utils.GetApplicationFileBytes(appFileInfoCli)
if err != nil {
return
}
var dataCli model.AngularCliJson
err = json.Unmarshal(fileBytesCli, &dataCli)
if err != nil { if err != nil {
return return
} }

View File

@@ -14,7 +14,6 @@ package enricher
import ( import (
"context" "context"
"os" "os"
"path/filepath"
"regexp" "regexp"
"strings" "strings"
@@ -28,6 +27,14 @@ func (e ExpressDetector) GetSupportedFrameworks() []string {
return []string{"Express"} return []string{"Express"}
} }
func (e ExpressDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
files, err := utils.GetCachedFilePathsFromRoot(componentPath, ctx)
if err != nil {
return []model.ApplicationFileInfo{}
}
return utils.GenerateApplicationFileFromFilters(files, componentPath, ".js", ctx)
}
// DoFrameworkDetection uses a tag to check for the framework name // DoFrameworkDetection uses a tag to check for the framework name
func (e ExpressDetector) DoFrameworkDetection(language *model.Language, config string) { func (e ExpressDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFramework(config, "express") { if hasFramework(config, "express") {
@@ -36,25 +43,19 @@ func (e ExpressDetector) DoFrameworkDetection(language *model.Language, config s
} }
func (e ExpressDetector) DoPortsDetection(component *model.Component, ctx *context.Context) { func (e ExpressDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
files, err := utils.GetCachedFilePathsFromRoot(component.Path, ctx) fileContents, err := utils.GetApplicationFileContents(e.GetApplicationFileInfos(component.Path, ctx))
if err != nil { if err != nil {
return return
} }
re := regexp.MustCompile(`\.listen\([^,)]*`) re := regexp.MustCompile(`\.listen\([^,)]*`)
var ports []int var ports []int
for _, file := range files { for _, content := range fileContents {
cleanFile := filepath.Clean(file)
bytes, err := os.ReadFile(cleanFile)
if err != nil {
continue
}
content := string(bytes)
matchesIndexes := re.FindAllStringSubmatchIndex(content, -1) matchesIndexes := re.FindAllStringSubmatchIndex(content, -1)
for _, matchIndexes := range matchesIndexes { for _, matchIndexes := range matchesIndexes {
port := getPort(content, matchIndexes) portList := getPorts(content, matchIndexes, component.Path)
if port != -1 { if len(portList) != 0 {
ports = append(ports, port) ports = append(ports, portList...)
} }
} }
if len(ports) > 0 { if len(ports) > 0 {
@@ -82,14 +83,39 @@ func GetEnvPort(envPlaceholder string) int {
return -1 return -1
} }
func getPort(content string, matchIndexes []int) int { // GetEnvPortFromDockerfile returns a port value from the Dockerfile (locations provided by the 'utils.GetLocations' function)
// matching the specified 'envPlaceHolder'.
// It first extracts the environment variable name to lookup from 'envPlaceholder' by removing any 'process.env.' prefix.
// It then searches through all environment variables detected from the Dockerfile (as determined by 'utils.GetEnvVarsFromDockerFile').
// And if the environment variable specified via 'envPlaceholder' is found in the Dockerfile environment variables
// and its corresponding value is a valid port (as determined by 'utils.GetValidPort'), it returns this valid port value.
//
// If there is an error reading the Dockerfile or if the environment variable specified via 'envPlaceholder' is not found among
// the Dockerfile environment variables, the function returns -1.
func GetEnvPortFromDockerfile(envPlaceholder string, path string) int {
envPlaceholder = strings.Replace(envPlaceholder, "process.env.", "", -1)
envVars, err := utils.GetEnvVarsFromDockerFile(path)
if err != nil {
return -1
}
for _, envVar := range envVars {
if envVar.Name == envPlaceholder {
if port, err := utils.GetValidPort(envVar.Value); err == nil {
return port
}
}
}
return -1
}
func getPorts(content string, matchIndexes []int, path string) []int {
// Express configures its port with app.listen() // Express configures its port with app.listen()
portPlaceholder := content[matchIndexes[0]:matchIndexes[1]] portPlaceholder := content[matchIndexes[0]:matchIndexes[1]]
portPlaceholder = strings.Replace(portPlaceholder, ".listen(", "", -1) portPlaceholder = strings.Replace(portPlaceholder, ".listen(", "", -1)
// Case: Raw port value -> return it directly // Case: Raw port value -> return it directly
if port, err := utils.GetValidPort(portPlaceholder); err == nil { if port, err := utils.GetValidPort(portPlaceholder); err == nil {
return port return []int{port}
} }
// Case: Env var given as value in app.listen -> Get env value // Case: Env var given as value in app.listen -> Get env value
@@ -115,13 +141,20 @@ func getPort(content string, matchIndexes []int) int {
} }
} }
} }
var result []int
// After double-checking for env vars try to get the value of this port // After double-checking for env vars try to get the value of this port
if len(envMatchIndexes) > 1 { if len(envMatchIndexes) > 1 {
envPlaceholder := envPortValue[envMatchIndexes[0]:envMatchIndexes[1]] envPlaceholder := envPortValue[envMatchIndexes[0]:envMatchIndexes[1]]
port := GetEnvPort(envPlaceholder) port := GetEnvPort(envPlaceholder)
// The port will be return only if a value was found for the given env var // The port will be return only if a value was found for the given env var
if port > 0 { if port > 0 {
return port result = append(result, port)
} else {
// If no env var was found on system try to find one in a root dockerfile
port = GetEnvPortFromDockerfile(envPlaceholder, path)
if port > 0 {
result = append(result, port)
}
} }
} }
// Case: No env var or raw value found -> check for raw value into a var // Case: No env var or raw value found -> check for raw value into a var
@@ -131,9 +164,10 @@ func getPort(content string, matchIndexes []int) int {
portValues := strings.Split(potentialPortGroup, " || ") portValues := strings.Split(potentialPortGroup, " || ")
for _, portValue := range portValues { for _, portValue := range portValues {
if port, err := utils.GetValidPort(portValue); err == nil { if port, err := utils.GetValidPort(portValue); err == nil {
return port result = append(result, port)
break
} }
} }
} }
return -1 return result
} }

View File

@@ -24,6 +24,12 @@ func (n NextDetector) GetSupportedFrameworks() []string {
return []string{"Next"} return []string{"Next"}
} }
func (a NextDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
// Next.js enricher does not apply source code detection.
// It only detects ports from start/dev script
return []model.ApplicationFileInfo{}
}
// DoFrameworkDetection uses a tag to check for the framework name // DoFrameworkDetection uses a tag to check for the framework name
func (n NextDetector) DoFrameworkDetection(language *model.Language, config string) { func (n NextDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFramework(config, "next") { if hasFramework(config, "next") {

View File

@@ -25,6 +25,17 @@ func (n NuxtDetector) GetSupportedFrameworks() []string {
return []string{"Nuxt"} return []string{"Nuxt"}
} }
func (n NuxtDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "",
File: "nuxt.config.js",
},
}
}
// DoFrameworkDetection uses a tag to check for the framework name // DoFrameworkDetection uses a tag to check for the framework name
func (n NuxtDetector) DoFrameworkDetection(language *model.Language, config string) { func (n NuxtDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFramework(config, "nuxt") { if hasFramework(config, "nuxt") {
@@ -34,6 +45,7 @@ func (n NuxtDetector) DoFrameworkDetection(language *model.Language, config stri
// DoPortsDetection searches for the port in package.json, and nuxt.config.js // DoPortsDetection searches for the port in package.json, and nuxt.config.js
func (n NuxtDetector) DoPortsDetection(component *model.Component, ctx *context.Context) { func (n NuxtDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
ports := []int{}
regexes := []string{`--port=(\d*)`} regexes := []string{`--port=(\d*)`}
// check if port is set in start script in package.json // check if port is set in start script in package.json
port := getPortFromStartScript(component.Path, regexes) port := getPortFromStartScript(component.Path, regexes)
@@ -50,15 +62,22 @@ func (n NuxtDetector) DoPortsDetection(component *model.Component, ctx *context.
} }
//check inside the nuxt.config.js file //check inside the nuxt.config.js file
bytes, err := utils.ReadAnyApplicationFile(component.Path, []model.ApplicationFileInfo{ appFileInfos := n.GetApplicationFileInfos(component.Path, ctx)
{ if len(appFileInfos) == 0 {
Dir: "",
File: "nuxt.config.js",
},
}, ctx)
if err != nil {
return return
} }
re := regexp.MustCompile(`port:\s*(\d+)*`)
component.Ports = utils.FindAllPortsSubmatch(re, string(bytes), 1) for _, appFileInfo := range appFileInfos {
fileBytes, err := utils.GetApplicationFileBytes(appFileInfo)
if err != nil {
continue
}
re := regexp.MustCompile(`port:\s*(\d+)*`)
ports = utils.FindAllPortsSubmatch(re, string(fileBytes), 1)
if len(ports) > 0 {
component.Ports = ports
return
}
}
} }

View File

@@ -25,6 +25,12 @@ func (r ReactJsDetector) GetSupportedFrameworks() []string {
return []string{"React"} return []string{"React"}
} }
func (r ReactJsDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
// React.js enricher does not apply source code detection.
// It only detects ports from start script or env vars
return nil
}
// DoFrameworkDetection uses a tag to check for the framework name // DoFrameworkDetection uses a tag to check for the framework name
func (r ReactJsDetector) DoFrameworkDetection(language *model.Language, config string) { func (r ReactJsDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFramework(config, "react") { if hasFramework(config, "react") {
@@ -46,6 +52,14 @@ func (r ReactJsDetector) DoPortsDetection(component *model.Component, ctx *conte
component.Ports = []int{port} component.Ports = []int{port}
return return
} }
// check if port is set on as env var inside a dockerfile
ports, err := utils.GetEnvVarPortValueFromDockerfile(component.Path, []string{"PORT"})
if err == nil {
component.Ports = ports
return
}
// check if port is set in start script in package.json // check if port is set in start script in package.json
port = getPortFromStartScript(component.Path, []string{`PORT=(\d*)`}) port = getPortFromStartScript(component.Path, []string{`PORT=(\d*)`})
if utils.IsValidPort(port) { if utils.IsValidPort(port) {

View File

@@ -24,6 +24,12 @@ func (s SvelteDetector) GetSupportedFrameworks() []string {
return []string{"Svelte"} return []string{"Svelte"}
} }
func (s SvelteDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
// Svelte.js enricher does not apply source code detection.
// It only detects ports from dev script
return nil
}
// DoFrameworkDetection uses a tag to check for the framework name // DoFrameworkDetection uses a tag to check for the framework name
func (s SvelteDetector) DoFrameworkDetection(language *model.Language, config string) { func (s SvelteDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFramework(config, "svelte") { if hasFramework(config, "svelte") {

View File

@@ -25,6 +25,17 @@ func (v VueDetector) GetSupportedFrameworks() []string {
return []string{"Vue"} return []string{"Vue"}
} }
func (v VueDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "",
File: "vue.config.js",
},
}
}
// DoFrameworkDetection uses a tag to check for the framework name // DoFrameworkDetection uses a tag to check for the framework name
func (v VueDetector) DoFrameworkDetection(language *model.Language, config string) { func (v VueDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFramework(config, "vue") { if hasFramework(config, "vue") {
@@ -35,6 +46,7 @@ func (v VueDetector) DoFrameworkDetection(language *model.Language, config strin
// DoPortsDetection searches for the port in package.json, .env file, and vue.config.js // DoPortsDetection searches for the port in package.json, .env file, and vue.config.js
func (v VueDetector) DoPortsDetection(component *model.Component, ctx *context.Context) { func (v VueDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
regexes := []string{`--port (\d*)`, `PORT=(\d*)`} regexes := []string{`--port (\d*)`, `PORT=(\d*)`}
ports := []int{}
// check if --port or PORT is set in start script in package.json // check if --port or PORT is set in start script in package.json
port := getPortFromStartScript(component.Path, regexes) port := getPortFromStartScript(component.Path, regexes)
if utils.IsValidPort(port) { if utils.IsValidPort(port) {
@@ -54,16 +66,30 @@ func (v VueDetector) DoPortsDetection(component *model.Component, ctx *context.C
return return
} }
//check inside the vue.config.js file // check if port is set on as env var inside a dockerfile
bytes, err := utils.ReadAnyApplicationFile(component.Path, []model.ApplicationFileInfo{ ports, err := utils.GetEnvVarPortValueFromDockerfile(component.Path, []string{"PORT"})
{ if err == nil {
Dir: "", component.Ports = ports
File: "vue.config.js",
},
}, ctx)
if err != nil {
return return
} }
re := regexp.MustCompile(`port:\s*(\d+)*`)
component.Ports = utils.FindAllPortsSubmatch(re, string(bytes), 1) //check inside the vue.config.js file
appFileInfos := v.GetApplicationFileInfos(component.Path, ctx)
if len(appFileInfos) == 0 {
return
}
for _, appFileInfo := range appFileInfos {
fileBytes, err := utils.GetApplicationFileBytes(appFileInfo)
if err != nil {
continue
}
re := regexp.MustCompile(`port:\s*(\d+)*`)
ports = utils.FindAllPortsSubmatch(re, string(fileBytes), 1)
if len(ports) > 0 {
component.Ports = ports
return
}
}
} }

View File

@@ -13,6 +13,7 @@ package enricher
import ( import (
"context" "context"
"github.com/devfile/alizer/pkg/apis/model" "github.com/devfile/alizer/pkg/apis/model"
"github.com/devfile/alizer/pkg/utils" "github.com/devfile/alizer/pkg/utils"
) )
@@ -23,6 +24,12 @@ func (d LaravelDetector) GetSupportedFrameworks() []string {
return []string{"Laravel"} return []string{"Laravel"}
} }
func (d LaravelDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
// laravel enricher does not apply source code detection.
// It only detects ports declared as env vars
return nil
}
// DoFrameworkDetection uses a tag to check for the framework name // DoFrameworkDetection uses a tag to check for the framework name
func (d LaravelDetector) DoFrameworkDetection(language *model.Language, config string) { func (d LaravelDetector) DoFrameworkDetection(language *model.Language, config string) {
if hasFramework(config, "laravel") { if hasFramework(config, "laravel") {
@@ -34,8 +41,17 @@ func (d LaravelDetector) DoFrameworkDetection(language *model.Language, config s
// configuring the APP_PORT variable which is dedicated to port configuration. // configuring the APP_PORT variable which is dedicated to port configuration.
func (d LaravelDetector) DoPortsDetection(component *model.Component, ctx *context.Context) { func (d LaravelDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
regexes := []string{`APP_PORT=(\d*)`} regexes := []string{`APP_PORT=(\d*)`}
// Case ENV file
ports := utils.GetPortValuesFromEnvFile(component.Path, regexes) ports := utils.GetPortValuesFromEnvFile(component.Path, regexes)
if len(ports) > 0 { if len(ports) > 0 {
component.Ports = ports component.Ports = ports
return
} }
// Case env var defined inside dockerfile
ports, err := utils.GetEnvVarPortValueFromDockerfile(component.Path, []string{"APP_PORT"})
if len(ports) > 0 && err != nil {
component.Ports = ports
return
}
} }

View File

@@ -25,46 +25,66 @@ func (d DjangoDetector) GetSupportedFrameworks() []string {
return []string{"Django"} return []string{"Django"}
} }
func (d DjangoDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "",
File: "manage.py",
},
}
}
func (d DjangoDetector) GetDjangoFilenames() []string {
return []string{"manage.py", "urls.py", "wsgi.py", "asgi.py"}
}
func (d DjangoDetector) GetConfigDjangoFilenames() []string {
return []string{"requirements.txt", "pyproject.toml"}
}
// DoFrameworkDetection uses a tag to check for the framework name // DoFrameworkDetection uses a tag to check for the framework name
// with django files and django config files // with django files and django config files
func (d DjangoDetector) DoFrameworkDetection(language *model.Language, files *[]string) { func (d DjangoDetector) DoFrameworkDetection(language *model.Language, files *[]string) {
managePy := utils.GetFile(files, "manage.py")
urlsPy := utils.GetFile(files, "urls.py")
wsgiPy := utils.GetFile(files, "wsgi.py")
asgiPy := utils.GetFile(files, "asgi.py")
requirementsTxt := utils.GetFile(files, "requirements.txt")
projectToml := utils.GetFile(files, "pyproject.toml")
var djangoFiles []string var djangoFiles []string
var configDjangoFiles []string var configDjangoFiles []string
utils.AddToArrayIfValueExist(&djangoFiles, managePy)
utils.AddToArrayIfValueExist(&djangoFiles, urlsPy) for _, filename := range d.GetDjangoFilenames() {
utils.AddToArrayIfValueExist(&djangoFiles, wsgiPy) filePy := utils.GetFile(files, filename)
utils.AddToArrayIfValueExist(&djangoFiles, asgiPy) utils.AddToArrayIfValueExist(&djangoFiles, filePy)
utils.AddToArrayIfValueExist(&configDjangoFiles, requirementsTxt) }
utils.AddToArrayIfValueExist(&configDjangoFiles, projectToml)
for _, filename := range d.GetConfigDjangoFilenames() {
configFile := utils.GetFile(files, filename)
utils.AddToArrayIfValueExist(&configDjangoFiles, configFile)
}
if hasFramework(&djangoFiles, "from django.") || hasFramework(&configDjangoFiles, "django") || hasFramework(&configDjangoFiles, "Django") { if hasFramework(&djangoFiles, "from django.") || hasFramework(&configDjangoFiles, "django") || hasFramework(&configDjangoFiles, "Django") {
language.Frameworks = append(language.Frameworks, "Django") language.Frameworks = append(language.Frameworks, "Django")
} }
} }
type ApplicationPropertiesFile struct {
Dir string
File string
}
// DoPortsDetection searches for the port in /manage.py // DoPortsDetection searches for the port in /manage.py
func (d DjangoDetector) DoPortsDetection(component *model.Component, ctx *context.Context) { func (d DjangoDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
bytes, err := utils.ReadAnyApplicationFile(component.Path, []model.ApplicationFileInfo{ ports := []int{}
{ appFileInfos := d.GetApplicationFileInfos(component.Path, ctx)
Dir: "", if len(appFileInfos) == 0 {
File: "manage.py",
},
}, ctx)
if err != nil {
return return
} }
re := regexp.MustCompile(`.default_port\s*=\s*"([^"]*)`)
component.Ports = utils.FindAllPortsSubmatch(re, string(bytes), 1) for _, appFileInfo := range appFileInfos {
fileBytes, err := utils.GetApplicationFileBytes(appFileInfo)
if err != nil {
continue
}
re := regexp.MustCompile(`.default_port\s*=\s*"([^"]*)`)
component.Ports = utils.FindAllPortsSubmatch(re, string(fileBytes), 1)
if len(ports) > 0 {
component.Ports = ports
return
}
}
} }

View File

@@ -22,30 +22,92 @@ import (
type FlaskDetector struct{} type FlaskDetector struct{}
func (d FlaskDetector) GetSupportedFrameworks() []string { func (f FlaskDetector) GetSupportedFrameworks() []string {
return []string{"Flask"} return []string{"Flask"}
} }
func (f FlaskDetector) GetApplicationFileInfos(componentPath string, ctx *context.Context) []model.ApplicationFileInfo {
return []model.ApplicationFileInfo{
{
Context: ctx,
Root: componentPath,
Dir: "",
File: "app.py",
},
{
Context: ctx,
Root: componentPath,
Dir: "",
File: "wsgi.py",
},
{
Context: ctx,
Root: componentPath,
Dir: "app",
File: "__init__.py",
},
}
}
func (f FlaskDetector) GetFlaskFilenames() []string {
return []string{"app.py", "wsgi.py"}
}
func (f FlaskDetector) GetConfigFlaskFilenames() []string {
return []string{"requirements.txt", "pyproject.toml"}
}
// DoFrameworkDetection uses a tag to check for the framework name // DoFrameworkDetection uses a tag to check for the framework name
// with flask files and flask config files // with flask files and flask config files
func (d FlaskDetector) DoFrameworkDetection(language *model.Language, files *[]string) { func (f FlaskDetector) DoFrameworkDetection(language *model.Language, files *[]string) {
appPy := utils.GetFile(files, "app.py") var flaskFiles []string
wsgiPy := utils.GetFile(files, "wsgi.py") var configFlaskFiles []string
requirementsTxt := utils.GetFile(files, "requirements.txt")
projectToml := utils.GetFile(files, "pyproject.toml")
flaskFiles := []string{} for _, filename := range f.GetFlaskFilenames() {
configFlaskFiles := []string{} filePy := utils.GetFile(files, filename)
utils.AddToArrayIfValueExist(&flaskFiles, appPy) utils.AddToArrayIfValueExist(&flaskFiles, filePy)
utils.AddToArrayIfValueExist(&flaskFiles, wsgiPy) }
utils.AddToArrayIfValueExist(&configFlaskFiles, requirementsTxt)
utils.AddToArrayIfValueExist(&configFlaskFiles, projectToml) for _, filename := range f.GetConfigFlaskFilenames() {
configFile := utils.GetFile(files, filename)
utils.AddToArrayIfValueExist(&configFlaskFiles, configFile)
}
if hasFramework(&flaskFiles, "from flask ") || hasFramework(&configFlaskFiles, "Flask") || hasFramework(&configFlaskFiles, "flask") { if hasFramework(&flaskFiles, "from flask ") || hasFramework(&configFlaskFiles, "Flask") || hasFramework(&configFlaskFiles, "flask") {
language.Frameworks = append(language.Frameworks, "Flask") language.Frameworks = append(language.Frameworks, "Flask")
} }
} }
// DoPortsDetection searches for the port in app/__init__.py, app.py or /wsgi.py
func (f FlaskDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
appFileInfos := f.GetApplicationFileInfos(component.Path, ctx)
if len(appFileInfos) == 0 {
return
}
for _, appFileInfo := range appFileInfos {
fileBytes, err := utils.GetApplicationFileBytes(appFileInfo)
if err != nil {
continue
}
matchIndexRegexes := []model.PortMatchRule{
{
Regex: regexp.MustCompile(`.run\([^)]*`),
ToReplace: ".run(",
},
}
if err != nil {
continue
}
ports := getPortFromFileFlask(matchIndexRegexes, string(fileBytes))
if len(ports) > 0 {
component.Ports = ports
return
}
}
}
// getPortFromFileFlask tries to find a port configuration inside a given file content // getPortFromFileFlask tries to find a port configuration inside a given file content
func getPortFromFileFlask(matchIndexRegexes []model.PortMatchRule, text string) []int { func getPortFromFileFlask(matchIndexRegexes []model.PortMatchRule, text string) []int {
var ports []int var ports []int
@@ -99,36 +161,3 @@ func getPortWithMatchIndexesFlask(content string, matchIndexes []int, toBeReplac
return -1 return -1
} }
// DoPortsDetection searches for the port in app/__init__.py, app.py or /wsgi.py
func (d FlaskDetector) DoPortsDetection(component *model.Component, ctx *context.Context) {
bytes, err := utils.ReadAnyApplicationFile(component.Path, []model.ApplicationFileInfo{
{
Dir: "",
File: "app.py",
},
{
Dir: "",
File: "wsgi.py",
},
{
Dir: "app",
File: "__init__.py",
},
}, ctx)
matchIndexRegexes := []model.PortMatchRule{
{
Regex: regexp.MustCompile(`.run\([^)]*`),
ToReplace: ".run(",
},
}
if err != nil {
return
}
ports := getPortFromFileFlask(matchIndexRegexes, string(bytes))
if len(ports) > 0 {
component.Ports = ports
return
}
}

View File

@@ -14,12 +14,12 @@ package enricher
import ( import (
"context" "context"
"errors" "errors"
"io/ioutil"
framework "github.com/devfile/alizer/pkg/apis/enricher/framework/go" framework "github.com/devfile/alizer/pkg/apis/enricher/framework/go"
"github.com/devfile/alizer/pkg/apis/model" "github.com/devfile/alizer/pkg/apis/model"
"github.com/devfile/alizer/pkg/utils" "github.com/devfile/alizer/pkg/utils"
"golang.org/x/mod/modfile" "golang.org/x/mod/modfile"
"os"
"path/filepath"
) )
type GoEnricher struct{} type GoEnricher struct{}
@@ -108,7 +108,7 @@ func (g GoEnricher) IsConfigValidForComponentDetection(language string, config s
} }
func getGoModFile(filePath string) (*modfile.File, error) { func getGoModFile(filePath string) (*modfile.File, error) {
b, err := ioutil.ReadFile(filePath) b, err := os.ReadFile(filepath.Clean(filePath))
if err != nil { if err != nil {
return nil, errors.New("unable to read go.mod file") return nil, errors.New("unable to read go.mod file")
} }

View File

@@ -11,9 +11,10 @@
package model package model
import "regexp" import (
"context"
type PortDetectionAlgorithm int "regexp"
)
const ( const (
DockerFile PortDetectionAlgorithm = 0 DockerFile PortDetectionAlgorithm = 0
@@ -21,66 +22,242 @@ const (
Source PortDetectionAlgorithm = 2 Source PortDetectionAlgorithm = 2
) )
type DetectionSettings struct { // All models inside model.go are sorted by name A-Z
BasePath string
PortDetectionStrategy []PortDetectionAlgorithm // AngularCliJson represents the angular-cli.json file
type AngularCliJson struct {
Defaults struct {
Serve AngularHostPort `json:"serve"`
} `json:"defaults"`
} }
type Language struct { // AngularHostPort represents the value of AngularCliJson.Defaults.Serve
Name string type AngularHostPort struct {
Aliases []string Host string `json:"host"`
Weight float64 Port int `json:"port"`
Frameworks []string
Tools []string
CanBeComponent bool
} }
type Component struct { // AngularJson represents angular.json
Name string type AngularJson struct {
Path string Projects map[string]AngularProjectBody `json:"projects"`
Languages []Language
Ports []int
} }
type Version struct { // AngularProjectBody represents the value of each key of the map for AngularJson.Projects
SchemaVersion string type AngularProjectBody struct {
Default bool Architect struct {
Version string Serve struct {
} Options AngularHostPort `json:"options"`
} `json:"serve"`
type DevFileType struct { } `json:"architect"`
Name string
Language string
ProjectType string
Tags []string
}
type DevfileFilter struct {
MinVersion string
MaxVersion string
} }
// ApplicationFileInfo is the main struct used to select potential application files
// for detectors
type ApplicationFileInfo struct { type ApplicationFileInfo struct {
Dir string // Context is the given context
Context *context.Context
// Root is the root path of the component
Root string
// Dir is the directory of the application file
Dir string
// File is the filename of the application file
File string File string
} }
type PortMatchRules struct { // Component represents every component detected from analysis process
MatchIndexRegexes []PortMatchRule type Component struct {
MatchRegexes []PortMatchSubRule // Name is the name of the component
Name string
// Path is the root path of the component
Path string
// Languages is the slice of languages detected inside the component
Languages []Language
// Ports is the slice of integers (port values) detected
Ports []int
} }
// DetectionSettings represents the required settings for component detection
type DetectionSettings struct {
// BasePath is the root path we need to apply detection process
BasePath string
// PortDetectionStrategy is the list of areas that we will apply port detection
// Accepted values can be found at PortDetectionAlgorithm
PortDetectionStrategy []PortDetectionAlgorithm
}
// DevfileFilter represents all filters passed to registry api upon requests
type DevfileFilter struct {
// MinSchemaVersion is the minimum schemaVersion of the fetched devfiles
MinSchemaVersion string
// MaxSchemaVersion is the maximum schemaVersion of the fetched devfiles
MaxSchemaVersion string
}
// DevfileScore represents the score that each devfile gets upon devfile matching process
type DevfileScore struct {
// DevfileIndex is the index of the fetched registry stacks slice
DevfileIndex int
// Score is the score that a devfile has. The biggest score gets matched with a given source code
Score int
}
// DevfileType represents a devfile.y(a)ml file
type DevfileType struct {
// Name is the name of a devfile
Name string
// Language is the language of a devfile
Language string
// ProjectType is the projectType of a devfile
ProjectType string
// Tags is a slice of tags of a devfile
Tags []string
// Versions is a slice of versions of a devfile
Versions []Version
}
// EnvVar represents an environment variable with a name and a corresponding value.
type EnvVar struct {
// Name is the name of the environment variable.
Name string
// Value is the value associated with the environment variable.
Value string
}
// Language represents every language detected from language analysis process
type Language struct {
// Name is the name of the language
Name string
// Aliases is the slice of aliases for this language
Aliases []string
// Weight is the float value which shows the importance of this language inside a given source code
Weight float64
// Frameworks is the slice of frameworks detected for this language
Frameworks []string
// Tools is the slice of tools detected for this language
Tools []string
// CanBeComponent is the bool value shows if this language can be detected as component
CanBeComponent bool
// CanBeContainerComponent is the bool value shows if this language can be detected as container component
CanBeContainerComponent bool
}
// MicronautApplicationProps represents the application.properties file of micronaut applications
type MicronautApplicationProps struct {
Micronaut struct {
Server struct {
Port int `yaml:"port,omitempty"`
SSL struct {
Enabled bool `yaml:"enabled,omitempty"`
Port int `yaml:"port,omitempty"`
} `yaml:"ssl,omitempty"`
} `yaml:"server,omitempty"`
} `yaml:"micronaut,omitempty"`
}
// OpenLibertyServerXml represents the server.xml file inside an open liberty application
type OpenLibertyServerXml struct {
HttpEndpoint struct {
HttpPort string `xml:"httpPort,attr"`
HttpsPort string `xml:"httpsPort,attr"`
} `xml:"httpEndpoint"`
}
// PortDetectionAlgorithm represents one of port detection algorithm values
type PortDetectionAlgorithm int
// PortMatchRule represents a rule for port matching with a given regex and a string to replace
type PortMatchRule struct { type PortMatchRule struct {
Regex *regexp.Regexp // Regex is the regexp.Regexp value which will be used to match ports
Regex *regexp.Regexp
// ToReplace is the string value which will be replaced once the Regex is matched
ToReplace string ToReplace string
} }
// PortMatchRules represents a struct of rules and subrules for port matching
type PortMatchRules struct {
// MatchIndexRegexes is a slice of PortMatchRule
MatchIndexRegexes []PortMatchRule
// MatchRegexes is a slice of PortMatchSubRule
MatchRegexes []PortMatchSubRule
}
// PortMatchSubRule represents a sub rule for port matching
type PortMatchSubRule struct { type PortMatchSubRule struct {
Regex *regexp.Regexp // Regex is the primary regexp.Regexp value for the sub rule
Regex *regexp.Regexp
// Regex is the secondary regexp.Regexp value for the sub rule
SubRegex *regexp.Regexp SubRegex *regexp.Regexp
} }
type DevFileScore struct { // QuarkusApplicationYaml represents the application.yaml used for quarkus applications
DevFileIndex int type QuarkusApplicationYaml struct {
Score int Quarkus QuarkusHttp `yaml:"quarkus,omitempty"`
}
// QuarkusHttp represents the port field from application.yaml of quarkus applications
type QuarkusHttp struct {
Http QuarkusHttpPort `yaml:"http,omitempty"`
}
// QuarkusHttpPort represents the port value from application.yaml of quarkus applications
type QuarkusHttpPort struct {
Port int `yaml:"port,omitempty"`
InsecureRequests string `yaml:"insecure-requests,omitempty"`
SSLPort int `yaml:"ssl-port,omitempty"`
}
// SpringApplicationProsServer represents the application.properties file used for spring applications
type SpringApplicationProsServer struct {
Server struct {
Port int `yaml:"port,omitempty"`
Http struct {
Port int `yaml:"port,omitempty"`
} `yaml:"http,omitempty"`
} `yaml:"server,omitempty"`
}
// Version represents a version of a devfile
type Version struct {
// SchemaVersion is the schemaVersion value of a devfile version
SchemaVersion string
// Default is the default value of a devfile version
Default bool
// Version is the version tag of a devfile version
Version string
}
// VertxConf represents the config file for vertx applications
type VertxConf struct {
Port int `json:"http.port,omitempty"`
ServerConfig VertexServerConfig `json:"http.server,omitempty"`
}
// VertexServerConfig represents the server config file for vertx applications
type VertexServerConfig struct {
Port int `json:"http.server.port,omitempty"`
} }

View File

@@ -38,6 +38,11 @@ func DetectComponents(path string) ([]model.Component, error) {
return detectComponentsWithPathAndPortStartegy(path, []model.PortDetectionAlgorithm{model.DockerFile, model.Compose, model.Source}, &ctx) return detectComponentsWithPathAndPortStartegy(path, []model.PortDetectionAlgorithm{model.DockerFile, model.Compose, model.Source}, &ctx)
} }
func DetectComponentsWithoutPortDetection(path string) ([]model.Component, error) {
ctx := context.Background()
return detectComponentsWithPathAndPortStartegy(path, []model.PortDetectionAlgorithm{}, &ctx)
}
func DetectComponentsInRootWithPathAndPortStartegy(path string, portDetectionStrategy []model.PortDetectionAlgorithm) ([]model.Component, error) { func DetectComponentsInRootWithPathAndPortStartegy(path string, portDetectionStrategy []model.PortDetectionAlgorithm) ([]model.Component, error) {
ctx := context.Background() ctx := context.Background()
return detectComponentsInRootWithPathAndPortStartegy(path, portDetectionStrategy, &ctx) return detectComponentsInRootWithPathAndPortStartegy(path, portDetectionStrategy, &ctx)
@@ -182,6 +187,17 @@ func isAnyComponentInPath(path string, components []model.Component) bool {
return false return false
} }
// isAnyComponentInDirectPath checks if a component is present in the exact path.
// Search starts from path and will return true if a component is found.
func isAnyComponentInDirectPath(path string, components []model.Component) bool {
for _, component := range components {
if strings.Contains(path, component.Path) {
return true
}
}
return false
}
// isFirstPathParentOfSecond check if first path is parent (direct or not) of second path. // isFirstPathParentOfSecond check if first path is parent (direct or not) of second path.
func isFirstPathParentOfSecond(firstPath string, secondPath string) bool { func isFirstPathParentOfSecond(firstPath string, secondPath string) bool {
return strings.Contains(secondPath, firstPath) return strings.Contains(secondPath, firstPath)
@@ -194,6 +210,7 @@ func DetectComponentsFromFilesList(files []string, settings model.DetectionSetti
alizerLogger.V(0).Info(fmt.Sprintf("Detecting components for %d fetched file paths", len(files))) alizerLogger.V(0).Info(fmt.Sprintf("Detecting components for %d fetched file paths", len(files)))
configurationPerLanguage := langfiles.Get().GetConfigurationPerLanguageMapping() configurationPerLanguage := langfiles.Get().GetConfigurationPerLanguageMapping()
var components []model.Component var components []model.Component
var containerComponents []model.Component
for _, file := range files { for _, file := range files {
alizerLogger.V(1).Info(fmt.Sprintf("Accessing %s", file)) alizerLogger.V(1).Info(fmt.Sprintf("Accessing %s", file))
languages, err := getLanguagesByConfigurationFile(configurationPerLanguage, file) languages, err := getLanguagesByConfigurationFile(configurationPerLanguage, file)
@@ -210,8 +227,20 @@ func DetectComponentsFromFilesList(files []string, settings model.DetectionSetti
alizerLogger.V(1).Info(err.Error()) alizerLogger.V(1).Info(err.Error())
continue continue
} }
alizerLogger.V(0).Info(fmt.Sprintf("Component %s found", component.Name)) if component.Languages[0].CanBeComponent {
components = appendIfMissing(components, component) alizerLogger.V(0).Info(fmt.Sprintf("Component %s found", component.Name))
components = appendIfMissing(components, component)
}
if component.Languages[0].CanBeContainerComponent {
alizerLogger.V(0).Info(fmt.Sprintf("Container component %s found", component.Name))
containerComponents = appendIfMissing(containerComponents, component)
}
}
for _, component := range containerComponents {
if !isAnyComponentInDirectPath(component.Path, components) {
components = appendIfMissing(components, component)
}
} }
return components return components
} }
@@ -226,8 +255,9 @@ func appendIfMissing(components []model.Component, component model.Component) []
} }
func getLanguagesByConfigurationFile(configurationPerLanguage map[string][]string, file string) ([]string, error) { func getLanguagesByConfigurationFile(configurationPerLanguage map[string][]string, file string) ([]string, error) {
filename := filepath.Base(file)
for regex, languages := range configurationPerLanguage { for regex, languages := range configurationPerLanguage {
if match, _ := regexp.MatchString(regex, file); match { if match, _ := regexp.MatchString(regex, filename); match {
return languages, nil return languages, nil
} }
} }

View File

@@ -16,7 +16,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"regexp" "regexp"
"strings" "strings"
@@ -28,192 +28,219 @@ import (
const MinimumAllowedVersion = "2.0.0" const MinimumAllowedVersion = "2.0.0"
func SelectDevFilesFromTypes(path string, devFileTypes []model.DevFileType) ([]int, error) { // DEPRECATION WARNING: This function is deprecated, please use devfile_recognizer.MatchDevfiles
// instead.
// func SelectDevFilesFromTypes: Returns a list of devfiles matched for the given application
func SelectDevFilesFromTypes(path string, devfileTypes []model.DevfileType) ([]int, error) {
alizerLogger := utils.GetOrCreateLogger() alizerLogger := utils.GetOrCreateLogger()
ctx := context.Background() ctx := context.Background()
alizerLogger.V(0).Info("Applying component detection to match a devfile") alizerLogger.V(0).Info("Applying component detection to match a devfile")
devFilesIndexes := selectDevFilesFromComponentsDetectedInPath(path, devFileTypes) devfilesIndexes := selectDevfilesFromComponentsDetectedInPath(path, devfileTypes)
if len(devFilesIndexes) > 0 { if len(devfilesIndexes) > 0 {
alizerLogger.V(0).Info(fmt.Sprintf("Found %d potential matches", len(devFilesIndexes))) alizerLogger.V(0).Info(fmt.Sprintf("Found %d potential matches", len(devfilesIndexes)))
return devFilesIndexes, nil return devfilesIndexes, nil
} }
alizerLogger.V(0).Info("No components found, applying language analysis for devfile matching") alizerLogger.V(0).Info("No components found, applying language analysis for devfile matching")
languages, err := analyze(path, &ctx) languages, err := analyze(path, &ctx)
if err != nil { if err != nil {
return []int{}, err return []int{}, err
} }
devfile, err := SelectDevFileUsingLanguagesFromTypes(languages, devFileTypes) mainLanguage, err := getMainLanguage(languages)
if err != nil {
return []int{}, err
}
devfiles, err := selectDevfilesByLanguage(mainLanguage, devfileTypes)
if err != nil { if err != nil {
return []int{}, errors.New("No valid devfile found for project in " + path) return []int{}, errors.New("No valid devfile found for project in " + path)
} }
return []int{devfile}, nil return devfiles, nil
} }
func selectDevFilesFromComponentsDetectedInPath(path string, devFileTypes []model.DevFileType) []int { func getMainLanguage(languages []model.Language) (model.Language, error) {
if len(languages) == 0 {
return model.Language{}, fmt.Errorf("cannot detect main language due to empty languages list")
}
mainLanguage := languages[0]
for _, language := range languages {
if language.Weight > mainLanguage.Weight {
mainLanguage = language
}
}
return mainLanguage, nil
}
func selectDevfilesFromComponentsDetectedInPath(path string, devfileTypes []model.DevfileType) []int {
components, _ := DetectComponentsInRoot(path) components, _ := DetectComponentsInRoot(path)
devFilesIndexes := selectDevFilesFromComponents(components, devFileTypes) devfilesIndexes := selectDevfilesFromComponents(components, devfileTypes)
if len(devFilesIndexes) > 0 { if len(devfilesIndexes) > 0 {
return devFilesIndexes return devfilesIndexes
} }
components, _ = DetectComponents(path) components, _ = DetectComponents(path)
return selectDevFilesFromComponents(components, devFileTypes) return selectDevfilesFromComponents(components, devfileTypes)
} }
func selectDevFilesFromComponents(components []model.Component, devFileTypes []model.DevFileType) []int { func selectDevfilesFromComponents(components []model.Component, devfileTypes []model.DevfileType) []int {
var devFilesIndexes []int var devfilesIndexes []int
for _, component := range components { for _, component := range components {
devFiles, err := selectDevFilesByLanguage(component.Languages[0], devFileTypes) devfiles, err := selectDevfilesByLanguage(component.Languages[0], devfileTypes)
if err == nil { if err == nil {
devFilesIndexes = append(devFilesIndexes, devFiles...) devfilesIndexes = append(devfilesIndexes, devfiles...)
} }
} }
return devFilesIndexes return devfilesIndexes
} }
func SelectDevFileFromTypes(path string, devFileTypes []model.DevFileType) (int, error) { // DEPRECATION WARNING: This function is deprecated, please use devfile_recognizer.MatchDevfiles
devfiles, err := SelectDevFilesFromTypes(path, devFileTypes) // instead.
// func SelectDevFileFromTypes: Returns the first devfile from the list of devfiles returned
// from SelectDevFilesFromTypes func. It also returns an error if exists.
func SelectDevFileFromTypes(path string, devfileTypes []model.DevfileType) (int, error) {
devfiles, err := SelectDevFilesFromTypes(path, devfileTypes)
if err != nil { if err != nil {
return -1, err return -1, err
} }
return devfiles[0], nil return devfiles[0], nil
} }
func SelectDevFilesUsingLanguagesFromTypes(languages []model.Language, devFileTypes []model.DevFileType) ([]int, error) { func SelectDevfilesUsingLanguagesFromTypes(languages []model.Language, devfileTypes []model.DevfileType) ([]int, error) {
var devFilesIndexes []int var devfilesIndexes []int
alizerLogger := utils.GetOrCreateLogger() alizerLogger := utils.GetOrCreateLogger()
alizerLogger.V(1).Info("Searching potential matches from detected languages") alizerLogger.V(1).Info("Searching potential matches from detected languages")
for _, language := range languages { for _, language := range languages {
alizerLogger.V(1).Info(fmt.Sprintf("Accessing %s language", language.Name)) alizerLogger.V(1).Info(fmt.Sprintf("Accessing %s language", language.Name))
devFiles, err := selectDevFilesByLanguage(language, devFileTypes) devfiles, err := selectDevfilesByLanguage(language, devfileTypes)
if err == nil { if err == nil {
alizerLogger.V(1).Info(fmt.Sprintf("Found %d potential matches for language %s", len(devFiles), language.Name)) alizerLogger.V(1).Info(fmt.Sprintf("Found %d potential matches for language %s", len(devfiles), language.Name))
devFilesIndexes = append(devFilesIndexes, devFiles...) devfilesIndexes = append(devfilesIndexes, devfiles...)
} }
} }
if len(devFilesIndexes) > 0 { if len(devfilesIndexes) > 0 {
return devFilesIndexes, nil return devfilesIndexes, nil
} }
return []int{}, errors.New("no valid devfile found by using those languages") return []int{}, errors.New("no valid devfile found by using those languages")
} }
func SelectDevFileUsingLanguagesFromTypes(languages []model.Language, devFileTypes []model.DevFileType) (int, error) { func SelectDevfileUsingLanguagesFromTypes(languages []model.Language, devfileTypes []model.DevfileType) (int, error) {
devFilesIndexes, err := SelectDevFilesUsingLanguagesFromTypes(languages, devFileTypes) devfilesIndexes, err := SelectDevfilesUsingLanguagesFromTypes(languages, devfileTypes)
if err != nil { if err != nil {
return -1, err return -1, err
} }
return devFilesIndexes[0], nil return devfilesIndexes[0], nil
} }
func MatchDevfiles(path string, url string, filter model.DevfileFilter) ([]model.DevFileType, error) { func MatchDevfiles(path string, url string, filter model.DevfileFilter) ([]model.DevfileType, error) {
alizerLogger := utils.GetOrCreateLogger() alizerLogger := utils.GetOrCreateLogger()
alizerLogger.V(0).Info("Starting devfile matching") alizerLogger.V(0).Info("Starting devfile matching")
alizerLogger.V(1).Info(fmt.Sprintf("Downloading devfiles from registry %s", url)) alizerLogger.V(1).Info(fmt.Sprintf("Downloading devfiles from registry %s", url))
devFileTypesFromRegistry, err := downloadDevFileTypesFromRegistry(url, filter) devfileTypesFromRegistry, err := DownloadDevfileTypesFromRegistry(url, filter)
if err != nil { if err != nil {
return []model.DevFileType{}, err return []model.DevfileType{}, err
} }
return selectDevfiles(path, devFileTypesFromRegistry) return selectDevfiles(path, devfileTypesFromRegistry)
} }
func SelectDevFilesFromRegistry(path string, url string) ([]model.DevFileType, error) { func SelectDevfilesFromRegistry(path string, url string) ([]model.DevfileType, error) {
alizerLogger := utils.GetOrCreateLogger() alizerLogger := utils.GetOrCreateLogger()
alizerLogger.V(0).Info("Starting devfile matching") alizerLogger.V(0).Info("Starting devfile matching")
alizerLogger.V(1).Info(fmt.Sprintf("Downloading devfiles from registry %s", url)) alizerLogger.V(1).Info(fmt.Sprintf("Downloading devfiles from registry %s", url))
devFileTypesFromRegistry, err := downloadDevFileTypesFromRegistry(url, model.DevfileFilter{MinVersion: "", MaxVersion: ""}) devfileTypesFromRegistry, err := DownloadDevfileTypesFromRegistry(url, model.DevfileFilter{MinSchemaVersion: "", MaxSchemaVersion: ""})
if err != nil { if err != nil {
return []model.DevFileType{}, err return []model.DevfileType{}, err
} }
return selectDevfiles(path, devFileTypesFromRegistry) return selectDevfiles(path, devfileTypesFromRegistry)
} }
func selectDevfiles(path string, devFileTypesFromRegistry []model.DevFileType) ([]model.DevFileType, error) { // selectDevfiles is exposed as global var in the purpose of mocking tests
indexes, err := SelectDevFilesFromTypes(path, devFileTypesFromRegistry) var selectDevfiles = func(path string, devfileTypesFromRegistry []model.DevfileType) ([]model.DevfileType, error) {
indexes, err := SelectDevFilesFromTypes(path, devfileTypesFromRegistry)
if err != nil { if err != nil {
return []model.DevFileType{}, err return []model.DevfileType{}, err
} }
var devFileTypes []model.DevFileType var devfileTypes []model.DevfileType
for _, index := range indexes { for _, index := range indexes {
devFileTypes = append(devFileTypes, devFileTypesFromRegistry[index]) devfileTypes = append(devfileTypes, devfileTypesFromRegistry[index])
} }
return devFileTypes, nil return devfileTypes, nil
} }
func SelectDevFileFromRegistry(path string, url string) (model.DevFileType, error) { func SelectDevfileFromRegistry(path string, url string) (model.DevfileType, error) {
devFileTypes, err := downloadDevFileTypesFromRegistry(url, model.DevfileFilter{MinVersion: "", MaxVersion: ""}) devfileTypes, err := DownloadDevfileTypesFromRegistry(url, model.DevfileFilter{MinSchemaVersion: "", MaxSchemaVersion: ""})
if err != nil { if err != nil {
return model.DevFileType{}, err return model.DevfileType{}, err
} }
index, err := SelectDevFileFromTypes(path, devFileTypes) index, err := SelectDevFileFromTypes(path, devfileTypes)
if err != nil { if err != nil {
return model.DevFileType{}, err return model.DevfileType{}, err
} }
return devFileTypes[index], nil return devfileTypes[index], nil
} }
func GetUrlWithVersions(url, minVersion, maxVersion string) (string, error) { func GetUrlWithVersions(url, minSchemaVersion, maxSchemaVersion string) (string, error) {
minAllowedVersion, err := version.NewVersion(MinimumAllowedVersion) minAllowedVersion, err := version.NewVersion(MinimumAllowedVersion)
if err != nil { if err != nil {
return "", nil return "", nil
} }
if minVersion != "" && maxVersion != "" { if minSchemaVersion != "" && maxSchemaVersion != "" {
minV, err := version.NewVersion(minVersion) minV, err := version.NewVersion(minSchemaVersion)
if err != nil { if err != nil {
return url, nil return url, nil
} }
maxV, err := version.NewVersion(maxVersion) maxV, err := version.NewVersion(maxSchemaVersion)
if err != nil { if err != nil {
return url, nil return url, nil
} }
if maxV.LessThan(minV) { if maxV.LessThan(minV) {
return "", fmt.Errorf("max-version cannot be lower than min-version") return "", fmt.Errorf("max-schema-version cannot be lower than min-schema-version")
} }
if maxV.LessThan(minAllowedVersion) || minV.LessThan(minAllowedVersion) { if maxV.LessThan(minAllowedVersion) || minV.LessThan(minAllowedVersion) {
return "", fmt.Errorf("min and/or max version are lower than the minimum allowed version (2.0.0)") return "", fmt.Errorf("min and/or max version are lower than the minimum allowed version (2.0.0)")
} }
return fmt.Sprintf("%s?minSchemaVersion=%s&maxSchemaVersion=%s", url, minVersion, maxVersion), nil return fmt.Sprintf("%s?minSchemaVersion=%s&maxSchemaVersion=%s", url, minSchemaVersion, maxSchemaVersion), nil
} else if minVersion != "" { } else if minSchemaVersion != "" {
minV, err := version.NewVersion(minVersion) minV, err := version.NewVersion(minSchemaVersion)
if err != nil { if err != nil {
return "", nil return "", nil
} }
if minV.LessThan(minAllowedVersion) { if minV.LessThan(minAllowedVersion) {
return "", fmt.Errorf("min version is lower than the minimum allowed version (2.0.0)") return "", fmt.Errorf("min version is lower than the minimum allowed version (2.0.0)")
} }
return fmt.Sprintf("%s?minSchemaVersion=%s", url, minVersion), nil return fmt.Sprintf("%s?minSchemaVersion=%s", url, minSchemaVersion), nil
} else if maxVersion != "" { } else if maxSchemaVersion != "" {
maxV, err := version.NewVersion(maxVersion) maxV, err := version.NewVersion(maxSchemaVersion)
if err != nil { if err != nil {
return "", nil return "", nil
} }
if maxV.LessThan(minAllowedVersion) { if maxV.LessThan(minAllowedVersion) {
return "", fmt.Errorf("max version is lower than the minimum allowed version (2.0.0)") return "", fmt.Errorf("max version is lower than the minimum allowed version (2.0.0)")
} }
return fmt.Sprintf("%s?maxSchemaVersion=%s", url, maxVersion), nil return fmt.Sprintf("%s?maxSchemaVersion=%s", url, maxSchemaVersion), nil
} else { } else {
return url, nil return url, nil
} }
} }
func downloadDevFileTypesFromRegistry(url string, filter model.DevfileFilter) ([]model.DevFileType, error) { // DownloadDevfileTypesFromRegistry is exposed as a global variable for the purpose of running mock tests
var DownloadDevfileTypesFromRegistry = func(url string, filter model.DevfileFilter) ([]model.DevfileType, error) {
url = adaptUrl(url) url = adaptUrl(url)
tmpUrl := appendIndexPath(url) tmpUrl := appendIndexPath(url)
url, err := GetUrlWithVersions(tmpUrl, filter.MinVersion, filter.MaxVersion) url, err := GetUrlWithVersions(tmpUrl, filter.MinSchemaVersion, filter.MaxSchemaVersion)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// This value is set by the user in order to configure the registry // This value is set by the user in order to configure the registry
resp, err := http.Get(url) // #nosec G107 resp, err := http.Get(url) // #nosec G107
if err != nil { if err != nil {
return []model.DevFileType{}, err return []model.DevfileType{}, err
} }
defer func() error { defer func() error {
if err := resp.Body.Close(); err != nil { if err := resp.Body.Close(); err != nil {
@@ -224,21 +251,21 @@ func downloadDevFileTypesFromRegistry(url string, filter model.DevfileFilter) ([
// Check server response // Check server response
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return []model.DevFileType{}, errors.New("unable to fetch devfiles from the registry") return []model.DevfileType{}, errors.New("unable to fetch devfiles from the registry")
} }
body, err2 := ioutil.ReadAll(resp.Body) body, err2 := io.ReadAll(resp.Body)
if err2 != nil { if err2 != nil {
return []model.DevFileType{}, errors.New("unable to fetch devfiles from the registry") return []model.DevfileType{}, errors.New("unable to fetch devfiles from the registry")
} }
var devFileTypes []model.DevFileType var devfileTypes []model.DevfileType
err = json.Unmarshal(body, &devFileTypes) err = json.Unmarshal(body, &devfileTypes)
if err != nil { if err != nil {
return []model.DevFileType{}, errors.New("unable to fetch devfiles from the registry") return []model.DevfileType{}, errors.New("unable to fetch devfiles from the registry")
} }
return devFileTypes, nil return devfileTypes, nil
} }
func appendIndexPath(url string) string { func appendIndexPath(url string) string {
@@ -261,7 +288,7 @@ func adaptUrl(url string) string {
return url return url
} }
// selectDevFilesByLanguage detects devfiles that fit best with a project. // selectDevfilesByLanguage detects devfiles that fit best with a project.
// //
// Performs a search in two steps looping through all devfiles available. // Performs a search in two steps looping through all devfiles available.
// When a framework is detected, this is stored in a map but still not saved. A check is made eventually as there could be that a future or // When a framework is detected, this is stored in a map but still not saved. A check is made eventually as there could be that a future or
@@ -270,21 +297,21 @@ func adaptUrl(url string) string {
// //
// At the end, if some framework is supported by some devfile, they are returned. Otherwise, Alizer was not able to find any // At the end, if some framework is supported by some devfile, they are returned. Otherwise, Alizer was not able to find any
// specific devfile for the frameworks detected and returned the devfiles which got the largest score. // specific devfile for the frameworks detected and returned the devfiles which got the largest score.
func selectDevFilesByLanguage(language model.Language, devFileTypes []model.DevFileType) ([]int, error) { func selectDevfilesByLanguage(language model.Language, devfileTypes []model.DevfileType) ([]int, error) {
var devFileIndexes []int var devfileIndexes []int
frameworkPerDevFile := make(map[string]model.DevFileScore) frameworkPerDevfile := make(map[string]model.DevfileScore)
scoreTarget := 0 scoreTarget := 0
for index, devFile := range devFileTypes { for index, devfile := range devfileTypes {
score := 0 score := 0
frameworkPerDevfileTmp := make(map[string]interface{}) frameworkPerDevfileTmp := make(map[string]interface{})
if strings.EqualFold(devFile.Language, language.Name) || matches(language.Aliases, devFile.Language) != "" { if strings.EqualFold(devfile.Language, language.Name) || matches(language.Aliases, devfile.Language) != "" {
score++ score++
if frw := matchesFormatted(language.Frameworks, devFile.ProjectType); frw != "" { if frw := matchesFormatted(language.Frameworks, devfile.ProjectType); frw != "" {
frameworkPerDevfileTmp[frw] = nil frameworkPerDevfileTmp[frw] = nil
score += utils.FRAMEWORK_WEIGHT score += utils.FRAMEWORK_WEIGHT
} }
for _, tag := range devFile.Tags { for _, tag := range devfile.Tags {
if frw := matchesFormatted(language.Frameworks, tag); frw != "" { if frw := matchesFormatted(language.Frameworks, tag); frw != "" {
frameworkPerDevfileTmp[frw] = nil frameworkPerDevfileTmp[frw] = nil
score += utils.FRAMEWORK_WEIGHT score += utils.FRAMEWORK_WEIGHT
@@ -295,37 +322,37 @@ func selectDevFilesByLanguage(language model.Language, devFileTypes []model.DevF
} }
for framework := range frameworkPerDevfileTmp { for framework := range frameworkPerDevfileTmp {
devFileObj := frameworkPerDevFile[framework] devfileObj := frameworkPerDevfile[framework]
if score > devFileObj.Score { if score > devfileObj.Score {
frameworkPerDevFile[framework] = model.DevFileScore{ frameworkPerDevfile[framework] = model.DevfileScore{
DevFileIndex: index, DevfileIndex: index,
Score: score, Score: score,
} }
} }
} }
if len(frameworkPerDevFile) == 0 { if len(frameworkPerDevfile) == 0 {
if score == scoreTarget { if score == scoreTarget {
devFileIndexes = append(devFileIndexes, index) devfileIndexes = append(devfileIndexes, index)
} else if score > scoreTarget { } else if score > scoreTarget {
scoreTarget = score scoreTarget = score
devFileIndexes = []int{index} devfileIndexes = []int{index}
} }
} }
} }
} }
if len(frameworkPerDevFile) > 0 { if len(frameworkPerDevfile) > 0 {
devFileIndexes = []int{} devfileIndexes = []int{}
for _, val := range frameworkPerDevFile { for _, val := range frameworkPerDevfile {
devFileIndexes = append(devFileIndexes, val.DevFileIndex) devfileIndexes = append(devfileIndexes, val.DevfileIndex)
} }
} }
if len(devFileIndexes) == 0 { if len(devfileIndexes) == 0 {
return devFileIndexes, errors.New("No valid devfile found for current language " + language.Name) return devfileIndexes, errors.New("No valid devfile found for current language " + language.Name)
} }
return devFileIndexes, nil return devfileIndexes, nil
} }
func matchesFormatted(values []string, valueToFind string) string { func matchesFormatted(values []string, valueToFind string) string {

View File

@@ -127,12 +127,14 @@ func AnalyzeFile(configFile string, targetLanguage string) (model.Language, erro
return model.Language{}, err return model.Language{}, err
} }
tmpLanguage := model.Language{ tmpLanguage := model.Language{
Name: lang.Name, Name: lang.Name,
Aliases: lang.Aliases, Aliases: lang.Aliases,
Frameworks: []string{}, Frameworks: []string{},
Tools: []string{}, Tools: []string{},
Weight: 100, Weight: 100,
CanBeComponent: true} CanBeComponent: lang.Component,
CanBeContainerComponent: lang.ContainerComponent,
}
langEnricher := enricher.GetEnricherByLanguage(targetLanguage) langEnricher := enricher.GetEnricherByLanguage(targetLanguage)
if langEnricher != nil { if langEnricher != nil {
langEnricher.DoEnrichLanguage(&tmpLanguage, &[]string{configFile}) langEnricher.DoEnrichLanguage(&tmpLanguage, &[]string{configFile})

View File

@@ -0,0 +1,27 @@
/*******************************************************************************
* Copyright (c) 2023 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Red Hat, Inc.
******************************************************************************/
package schema
type DevfileYaml struct {
StarterProjects []struct {
Git struct {
CheckoutFrom struct {
Remote string `yaml:"remote"`
Revision string `yaml:"revision"`
} `yaml:"checkoutFrom"`
Remotes struct {
Origin string `yaml:"origin"`
} `yaml:"remotes"`
} `yaml:"git"`
SubDir string `yaml:"subDir"`
} `yaml:"starterProjects"`
}

View File

@@ -29,6 +29,7 @@ type LanguagesProperties map[string]LanguageProperties
type LanguageCustomization struct { type LanguageCustomization struct {
ConfigurationFiles []string `yaml:"configuration_files"` ConfigurationFiles []string `yaml:"configuration_files"`
Component bool `yaml:"component"` Component bool `yaml:"component"`
ContainerComponent bool `yaml:"container_component"`
ExcludeFolders []string `yaml:"exclude_folders,omitempty"` ExcludeFolders []string `yaml:"exclude_folders,omitempty"`
Aliases []string `yaml:"aliases"` Aliases []string `yaml:"aliases"`
Disabled bool `default:"false" yaml:"disable_detection"` Disabled bool `default:"false" yaml:"disable_detection"`

View File

@@ -19,17 +19,18 @@ import (
"encoding/xml" "encoding/xml"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io"
"io/fs"
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"github.com/devfile/alizer/pkg/utils/langfiles"
"github.com/devfile/alizer/pkg/apis/model" "github.com/devfile/alizer/pkg/apis/model"
"github.com/devfile/alizer/pkg/schema" "github.com/devfile/alizer/pkg/schema"
"github.com/devfile/alizer/pkg/utils/langfiles"
"github.com/moby/buildkit/frontend/dockerfile/parser"
ignore "github.com/sabhiram/go-gitignore" ignore "github.com/sabhiram/go-gitignore"
) )
@@ -65,16 +66,6 @@ func GetFile(filePaths *[]string, wantedFile string) string {
return "" return ""
} }
// HasFile checks if the file is in a filePaths path.
func HasFile(files *[]string, wantedFile string) bool {
for _, path := range *files {
if IsPathOfWantedFile(path, wantedFile) {
return true
}
}
return false
}
// IsPathOfWantedFile checks if the file is in the path. // IsPathOfWantedFile checks if the file is in the path.
func IsPathOfWantedFile(path string, wantedFile string) bool { func IsPathOfWantedFile(path string, wantedFile string) bool {
_, file := filepath.Split(path) _, file := filepath.Split(path)
@@ -83,7 +74,7 @@ func IsPathOfWantedFile(path string, wantedFile string) bool {
// IsTagInFile checks if the file contains the tag. // IsTagInFile checks if the file contains the tag.
func IsTagInFile(file string, tag string) (bool, error) { func IsTagInFile(file string, tag string) (bool, error) {
contentInByte, err := ioutil.ReadFile(file) contentInByte, err := os.ReadFile(filepath.Clean(file))
if err != nil { if err != nil {
return false, err return false, err
} }
@@ -143,7 +134,7 @@ func GetPomFileContent(pomFilePath string) (schema.Pom, error) {
if err != nil { if err != nil {
return schema.Pom{}, err return schema.Pom{}, err
} }
byteValue, _ := ioutil.ReadAll(xmlFile) byteValue, _ := io.ReadAll(xmlFile)
var pom schema.Pom var pom schema.Pom
err = xml.Unmarshal(byteValue, &pom) err = xml.Unmarshal(byteValue, &pom)
@@ -298,7 +289,7 @@ func isFileInRoot(root string, file string) bool {
// GetFilePathsInRoot returns a slice of all files in the root. // GetFilePathsInRoot returns a slice of all files in the root.
func GetFilePathsInRoot(root string) ([]string, error) { func GetFilePathsInRoot(root string) ([]string, error) {
fileInfos, err := ioutil.ReadDir(root) fileInfos, err := os.ReadDir(root)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -309,14 +300,16 @@ func GetFilePathsInRoot(root string) ([]string, error) {
return files, nil return files, nil
} }
// ConvertPropertiesFileAsPathToMap fetches a file from a given path and transforms it into a map
func ConvertPropertiesFileAsPathToMap(path string) (map[string]string, error) { func ConvertPropertiesFileAsPathToMap(path string) (map[string]string, error) {
bytes, err := ioutil.ReadFile(path) bytes, err := os.ReadFile(filepath.Clean(path))
if err != nil { if err != nil {
return nil, err return nil, err
} }
return ConvertPropertiesFileToMap(bytes) return ConvertPropertiesFileToMap(bytes)
} }
// ConvertPropertiesFileAsPathToMap transforms a slice of bytes it into a map
func ConvertPropertiesFileToMap(fileInBytes []byte) (map[string]string, error) { func ConvertPropertiesFileToMap(fileInBytes []byte) (map[string]string, error) {
config := map[string]string{} config := map[string]string{}
scanner := bufio.NewScanner(bytes.NewReader(fileInBytes)) scanner := bufio.NewScanner(bytes.NewReader(fileInBytes))
@@ -352,6 +345,173 @@ func GetValidPortsFromEnvs(envs []string) []int {
return validPorts return validPorts
} }
// GetEnvVarsFromDockerFile returns a slice of env vars from Dockerfiles in the given directory.
func GetEnvVarsFromDockerFile(root string) ([]model.EnvVar, error) {
locations := GetLocations(root)
for _, location := range locations {
filePath := filepath.Join(root, location)
cleanFilePath := filepath.Clean(filePath)
file, err := os.Open(cleanFilePath)
if err == nil {
defer func() error {
if err := file.Close(); err != nil {
return fmt.Errorf("error closing file: %s", err)
}
return nil
}()
return readEnvVarsFromDockerfile(file)
}
}
return nil, fmt.Errorf("no dockefile found inside dir: %s", root)
}
// GetValidPortsFromEnvs returns a slice of valid ports from a dockerfile.
func GetValidPortsFromEnvDockerfile(envs []string, envVars []model.EnvVar) []int {
var validPorts []int
for _, env := range envs {
for _, envVar := range envVars {
if envVar.Name == env {
if port, err := GetValidPort(envVar.Value); err == nil {
validPorts = append(validPorts, port)
}
break
}
}
}
return validPorts
}
// GetLocations returns a list of file paths representing common locations where Dockerfiles might be found
// within the specified 'root' directory and one level down.
//
// It starts with a predefined list of common file names for a Dockerfile
// ('Dockerfile', 'Containerfile', 'dockerfile', 'containerfile'), and appends such file names to the 'root' subdirectories.
//
// Note that hidden files and directories (starting with a dot, e.g., '.git') are ignored while traversing the 'root' directory.
func GetLocations(root string) []string {
filenames := []string{"Dockerfile", "Containerfile", "dockerfile", "containerfile"}
locations := make([]string, len(filenames))
copy(locations, filenames)
entries, err := os.ReadDir(root)
if err != nil {
return locations
}
dirItems := make([]fs.FileInfo, 0, len(entries))
for _, entry := range entries {
info, err := entry.Info()
if err != nil {
return locations
}
dirItems = append(dirItems, info)
}
for _, item := range dirItems {
if strings.HasPrefix(item.Name(), ".") {
continue
}
tmpPath := filepath.Join(root, item.Name())
fileInfo, err := os.Stat(tmpPath)
if err != nil {
continue
}
if fileInfo.IsDir() {
for _, filename := range filenames {
locations = append(locations, filepath.Join(item.Name(), filename))
}
}
}
return locations
}
// ReadPortsFromDockerfile returns a slice of port numbers.
func ReadPortsFromDockerfile(file io.Reader) []int {
var ports []int
res, err := parser.Parse(file)
if err != nil {
return ports
}
for _, child := range res.AST.Children {
// check for the potential port number in a Dockerfile/Containerfile
if strings.ToLower(child.Value) == "expose" {
for n := child.Next; n != nil; n = n.Next {
if port, err := strconv.Atoi(n.Value); err == nil {
ports = append(ports, port)
}
}
}
}
return ports
}
func upsertEnvVar(envVars []model.EnvVar, envVar model.EnvVar) []model.EnvVar {
isPresent := false
for i := range envVars {
if envVars[i].Name == envVar.Name {
isPresent = true
envVars[i].Value = envVar.Value
}
}
if !isPresent {
envVars = append(envVars, envVar)
}
return envVars
}
// readEnvVarsFromDockerfile returns a slice of envVars.
func readEnvVarsFromDockerfile(file io.Reader) ([]model.EnvVar, error) {
var envVars []model.EnvVar
res, err := parser.Parse(file)
if err != nil {
return envVars, err
}
for _, child := range res.AST.Children {
// check for the potential env var in a Dockerfile/Containerfile
if strings.ToLower(child.Value) != "env" {
continue
}
firstNode := child.Next
var secondNode *parser.Node
if firstNode == nil {
continue
}
secondNode = firstNode.Next
if secondNode == nil {
continue
}
envVar := model.EnvVar{
Name: firstNode.Value,
Value: secondNode.Value,
}
envVars = upsertEnvVar(envVars, envVar)
}
return envVars, nil
}
// GetEnvVarPortValueFromDockerfile gets port value defined as env vars.
func GetEnvVarPortValueFromDockerfile(path string, portPlaceholders []string) ([]int, error) {
envVars, err := GetEnvVarsFromDockerFile(path)
ports := []int{}
if err != nil {
return ports, err
}
for _, envVar := range envVars {
for _, portPlaceholder := range portPlaceholders {
if envVar.Name != portPlaceholder {
continue
}
if port, err := GetValidPort(envVar.Value); err == nil {
ports = append(ports, port)
}
}
}
return ports, nil
}
// GetValidPorts returns a slice of valid ports. // GetValidPorts returns a slice of valid ports.
func GetValidPorts(ports []string) []int { func GetValidPorts(ports []string) []int {
var validPorts []int var validPorts []int
@@ -407,9 +567,62 @@ func GetAnyApplicationFilePathExactMatch(root string, propsFiles []model.Applica
return "" return ""
} }
// ReadAnyApplicationFile returns a byte slice of a file if it exists in the directory and the given file name is a substring. // GenerateApplicationFileFromFilters generates a slice of model.ApplicationFileInfo
func ReadAnyApplicationFile(root string, propsFiles []model.ApplicationFileInfo, ctx *context.Context) ([]byte, error) { // from a given list of files and the root path of a component. If suffix exists
return readAnyApplicationFile(root, propsFiles, false, ctx) // it generates items only for files ending with this suffix.
func GenerateApplicationFileFromFilters(files []string, path string, suffix string, ctx *context.Context) []model.ApplicationFileInfo {
applicationFileInfos := []model.ApplicationFileInfo{}
for _, file := range files {
if strings.HasSuffix(file, suffix) {
cleanPath := filepath.Clean(file)
filename := filepath.Base(cleanPath)
tmpDir := strings.ReplaceAll(file, path, "")
dir := strings.ReplaceAll(tmpDir, filename, "")
appFileInfo := model.ApplicationFileInfo{
Context: ctx,
Root: path,
Dir: dir,
File: filename,
}
applicationFileInfos = append(applicationFileInfos, appFileInfo)
}
}
return applicationFileInfos
}
// GetApplicationFileContents returns a slice of strings for all file contents found for a given
// slice of ApplicationFileInfo.
func GetApplicationFileContents(appFileInfos []model.ApplicationFileInfo) ([]string, error) {
fileContents := []string{}
for _, appFileInfo := range appFileInfos {
fileContent, err := GetApplicationFileBytes(appFileInfo)
if err == nil {
fileContents = append(fileContents, string(fileContent))
}
}
if len(fileContents) == 0 {
return fileContents, fmt.Errorf("error: no application file found matching given criteria")
}
return fileContents, nil
}
// GetApplicationFileBytes returns a slice of bytes of a file if it exists in the directory and the given file name is a substring.
func GetApplicationFileBytes(propsFile model.ApplicationFileInfo) ([]byte, error) {
bytes, err := readAnyApplicationFile(propsFile.Root, []model.ApplicationFileInfo{propsFile}, false, propsFile.Context)
if err != nil {
return bytes, fmt.Errorf("error: %s", err)
}
return bytes, nil
}
// GetApplicationFileInfo returns an item from a slice of applicationFileInfos if it matches the given filename
func GetApplicationFileInfo(propsFiles []model.ApplicationFileInfo, filename string) (model.ApplicationFileInfo, error) {
for _, propsFile := range propsFiles {
if propsFile.File == filename {
return propsFile, nil
}
}
return model.ApplicationFileInfo{}, fmt.Errorf("no ApplicationFileInfo found")
} }
// ReadAnyApplicationFileExactMatch returns a byte slice if the exact given file exists in the directory. // ReadAnyApplicationFileExactMatch returns a byte slice if the exact given file exists in the directory.
@@ -426,11 +639,12 @@ func readAnyApplicationFile(root string, propsFiles []model.ApplicationFileInfo,
path = GetAnyApplicationFilePath(root, propsFiles, ctx) path = GetAnyApplicationFilePath(root, propsFiles, ctx)
} }
if path != "" { if path != "" {
return ioutil.ReadFile(path) return os.ReadFile(filepath.Clean(path))
} }
return nil, errors.New("no file found") return nil, errors.New("no file found")
} }
// FindPortSubMatch returns a port number in case it finds one for a given regex group
func FindPortSubmatch(re *regexp.Regexp, text string, group int) int { func FindPortSubmatch(re *regexp.Regexp, text string, group int) int {
potentialPortGroup := FindPotentialPortGroup(re, text, group) potentialPortGroup := FindPotentialPortGroup(re, text, group)
if potentialPortGroup != "" { if potentialPortGroup != "" {
@@ -441,6 +655,7 @@ func FindPortSubmatch(re *regexp.Regexp, text string, group int) int {
return -1 return -1
} }
// FindPotentialPortGroup returns a placeholder for port if is found
func FindPotentialPortGroup(re *regexp.Regexp, text string, group int) string { func FindPotentialPortGroup(re *regexp.Regexp, text string, group int) string {
if text != "" { if text != "" {
matches := re.FindStringSubmatch(text) matches := re.FindStringSubmatch(text)
@@ -451,6 +666,7 @@ func FindPotentialPortGroup(re *regexp.Regexp, text string, group int) string {
return "" return ""
} }
// FindAllPortsSubmatch returns a slice of port int values, matching a regex inside a given text
func FindAllPortsSubmatch(re *regexp.Regexp, text string, group int) []int { func FindAllPortsSubmatch(re *regexp.Regexp, text string, group int) []int {
var ports []int var ports []int
if text != "" { if text != "" {
@@ -467,6 +683,8 @@ func FindAllPortsSubmatch(re *regexp.Regexp, text string, group int) []int {
return ports return ports
} }
// GetPortValueFromEnvFile returns the first port value of a slice of port values
// declared from env var files.
func GetPortValueFromEnvFile(root string, regex string) int { func GetPortValueFromEnvFile(root string, regex string) int {
ports := GetPortValuesFromEnvFile(root, []string{regex}) ports := GetPortValuesFromEnvFile(root, []string{regex})
if len(ports) > 0 { if len(ports) > 0 {
@@ -475,6 +693,7 @@ func GetPortValueFromEnvFile(root string, regex string) int {
return -1 return -1
} }
// GetPortValuesFromEnvFile returns all port values found inside an env var file
func GetPortValuesFromEnvFile(root string, regexes []string) []int { func GetPortValuesFromEnvFile(root string, regexes []string) []int {
var ports []int var ports []int
text, err := getEnvFileContent(root) text, err := getEnvFileContent(root)
@@ -494,6 +713,7 @@ func GetPortValuesFromEnvFile(root string, regexes []string) []int {
return ports return ports
} }
// GetStringValueFromEnvFile returns port values as string from env file
func GetStringValueFromEnvFile(root string, regex string) string { func GetStringValueFromEnvFile(root string, regex string) string {
text, err := getEnvFileContent(root) text, err := getEnvFileContent(root)
if err != nil { if err != nil {
@@ -523,6 +743,7 @@ var getEnvFileContent = func(root string) (string, error) {
return string(bytes), nil return string(bytes), nil
} }
// NormalizeSplit splits a filepath into dir and filename
func NormalizeSplit(file string) (string, string) { func NormalizeSplit(file string) (string, string) {
dir, fileName := filepath.Split(file) dir, fileName := filepath.Split(file)
if dir == "" { if dir == "" {

View File

@@ -28,6 +28,7 @@ type LanguageItem struct {
ConfigurationFiles []string ConfigurationFiles []string
ExcludeFolders []string ExcludeFolders []string
Component bool Component bool
ContainerComponent bool
disabled bool disabled bool
} }
@@ -87,6 +88,7 @@ func customizeLanguage(languageItem *LanguageItem) {
(*languageItem).ConfigurationFiles = customization.ConfigurationFiles (*languageItem).ConfigurationFiles = customization.ConfigurationFiles
(*languageItem).ExcludeFolders = customization.ExcludeFolders (*languageItem).ExcludeFolders = customization.ExcludeFolders
(*languageItem).Component = customization.Component (*languageItem).Component = customization.Component
(*languageItem).ContainerComponent = customization.ContainerComponent
(*languageItem).Aliases = appendSlice((*languageItem).Aliases, customization.Aliases) (*languageItem).Aliases = appendSlice((*languageItem).Aliases, customization.Aliases)
(*languageItem).disabled = customization.Disabled (*languageItem).disabled = customization.Disabled
} }

View File

@@ -6,6 +6,13 @@ C#:
- ".*\\.\\w+proj" - ".*\\.\\w+proj"
- "appsettings.json" - "appsettings.json"
component: true component: true
Dockerfile:
aliases:
- "Containerfile"
configuration_files:
- "[Dd]ockerfile(\\.\\w+)?$"
- "[Cc]ontainerfile(\\.\\w+)?$"
container_component: true
F#: F#:
aliases: aliases:
- "dotnet" - "dotnet"
@@ -33,12 +40,12 @@ JavaScript:
exclude_folders: exclude_folders:
- "node_modules" - "node_modules"
configuration_files: configuration_files:
- "[^-]package.json" - "package.json"
component: true component: true
PHP: PHP:
configuration_files: configuration_files:
- "composer.json" - "composer.json"
- "[^-]package.json" - "package.json"
component: true component: true
Python: Python:
configuration_files: configuration_files:
@@ -55,7 +62,7 @@ TypeScript:
exclude_folders: exclude_folders:
- "node_modules" - "node_modules"
configuration_files: configuration_files:
- "[^-]package.json" - "package.json"
component: true component: true
Visual Basic .NET: Visual Basic .NET:
aliases: aliases:

2
vendor/modules.txt generated vendored
View File

@@ -150,7 +150,7 @@ github.com/danieljoos/wincred
# github.com/davecgh/go-spew v1.1.1 # github.com/davecgh/go-spew v1.1.1
## explicit ## explicit
github.com/davecgh/go-spew/spew github.com/davecgh/go-spew/spew
# github.com/devfile/alizer v1.0.1 # github.com/devfile/alizer v1.2.1
## explicit; go 1.19 ## explicit; go 1.19
github.com/devfile/alizer/pkg/apis/enricher github.com/devfile/alizer/pkg/apis/enricher
github.com/devfile/alizer/pkg/apis/enricher/framework/dotnet github.com/devfile/alizer/pkg/apis/enricher/framework/dotnet