Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add min max cli args #5

Merged
merged 12 commits into from
Jul 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates

version: 2
updates:
- package-ecosystem: "gomod" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"
2 changes: 1 addition & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,6 @@ jobs:
uses: softprops/action-gh-release@v1
with:
draft: false
files: go/${{env.BINARY_NAME}}
files: ${{env.BINARY_NAME}}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ $ go build alizer.go
```

```sh
--log {debug|info|warning} sets the logging level of the CLI. The arg accepts only 3 values [`debug`, `info`, `warning`]. The default value is `warning` and the logging level is `ErrorLevel`.
--port-detection {docker|compose|source} port detection strategy to use when detecting a port. Currently supported strategies are 'docker', 'compose' and 'source'. You can pass more strategies at the same time. They will be executed in order. By default Alizer will execute docker, compose and source.
```

Expand All @@ -53,7 +54,10 @@ $ go build alizer.go
```

```sh
--log {debug|info|warning} sets the logging level of the CLI. The arg accepts only 3 values [`debug`, `info`, `warning`]. The default value is `warning` and the logging level is `ErrorLevel`.
--registry strings registry where to download the devfiles. Default value: https://registry.devfile.io
--min-version strings the minimum SchemaVersion of the matched devfile(s). The minimum accepted value is `2.0.0`, otherwise an error is returned.
--max-version strings the maximum SchemaVersion of the matched devfile(s). The minimum accepted value is `2.0.0`, otherwise an error is returned.
```

### Library Package
Expand Down
111 changes: 111 additions & 0 deletions docs/proposals/dockerfile_component_detection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Enhance port detection for dockerfiles that are not in a component.

## Related GitHub Issues

- https://github.com/redhat-developer/alizer/issues/235

## Background

What is a component?

Components are defined in the `languages-customization.yml` as a language that has specific `configuration_files`.

Example of a Go component:

```yaml
Go:
exclude_folders:
- "vendor"
configuration_files:
- "go.mod"
component: true
```

What happens when the repo being scanned has component(s) found but also contains a dockerfile one level outside the repo?

```shell
.repo-being-detected-by-alizer
├── go-component/
│ ├── go.mod
│ └── dockerfile
└── dockerfile
```

The first component detected would be `go-component` because it contains a `go.mod` file.

```shell
└── go-component/
├── go.mod
└── dockerfile
```

The dockerfile in the root level would not be used during analysis because it does not have a component definition in the `languages-customization.yml`.

## Potential Solution

Introduce a new component field called `container_component` and add it to the `Dockerfile` language:

```yaml
Dockerfile:
configuration_files:
- "[Dd]ockerfile"
- "[Cc]ontainerfile"
container_component: true
```

Introduce a new component enricher for the `Dockerfile` language that reuses the existing strategy for finding ports in a Dockerfile. There are currently three strategies for port detection, but only the DockerFile strategy is required. [1]

```go
func (d DockerEnricher) DoEnrichComponent(component *model.Component, settings model.DetectionSettings, ctx *context.Context) {
// get ports from the dockerfile using our existing dockerfile strategy
for _, algorithm := range settings.PortDetectionStrategy {
switch algorithm {
case model.DockerFile:
{
ports = GetPortsFromDockerFile(component.Path)
break
}
}
}
```

Using the `container_component` field, we can separate out a dockerfile as a component from what we currently define as a component. This can also be used for any future components that are similar to a dockerfile.

As a result, the example from above will return two components:

- A Go component:
```shell
└── go-component/ # detected as a Go component
├── go.mod
└── dockerfile
```
- And the newly defined Dockerfile component:
```shell
.repo-being-detected-by-alizer
└── dockerfile # will be a component with path `repo-being-detected-by-alizer`
```

This can also be applied to common locations for Dockerfiles where it is inside a directory:

```shell
.repo-being-detected-by-alizer
├── docker/
│ └── dockerfile # will be a component with path `docker/dockerfile`
├── .docker/
│ └── Containerfile # will be a component with path `.docker/Containerfile`
└── build/
└── containerfile # will be a component with path `build/containerfile`
```

## Potential Problems

- Weak documentation as to what the fields in the `language-customization.yml` currently do.
- Action: Add descriptions of the fields similar to `languages.yml`
- Could have potential naming collision with field names if the `language.yml` file is updated.
- Action: Could add a workflow test that checks the fields between `language-customization.yml` and `languages.yml`
- Action: Add as a PR review check item
- Action: Add a comment about potential naming collisions in the `language-customization.yml` file

## References

[1] More information about port detection [here](../public/port_detection.md)
2 changes: 1 addition & 1 deletion docs/proposals/support_22x_devfiles.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ filter := map[string]interface{} {
"max-version": "2.1.0",
"min-version": "2.0.0",
}
devfiles, err := recognizer.SelectDevFilesFromTypesWithArgs("myproject", devfiles, filter)
devfiles, err := recognizer.MatchDevfiles("myproject", devfiles, filter)
```

### model.DevfileType
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.19
require (
github.com/go-git/go-git/v5 v5.7.0
github.com/go-logr/logr v1.2.4
github.com/hashicorp/go-version v1.6.0
github.com/moby/buildkit v0.11.6
github.com/pkg/errors v0.9.1
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
Expand Down
7 changes: 7 additions & 0 deletions pkg/apis/enricher/framework/go/go_detector.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ func GetPortWithMatchIndexesGo(content string, matchIndexes []int, toBeReplaced
portPlaceholder := content[matchIndexes[0]:matchIndexes[1]]
// we should end up with something like ".ListenAndServe(PORT"
portPlaceholder = strings.Replace(portPlaceholder, toBeReplaced, "", -1)
// try to replace any string quotes
portPlaceholder = strings.Replace(portPlaceholder, "\"", "", -1)
// check if the placeholder is an IP:PORT
splitedPlaceholder := strings.Split(portPlaceholder, ":")
if len(splitedPlaceholder) > 1 {
portPlaceholder = splitedPlaceholder[len(splitedPlaceholder)-1]
}
// if we are lucky enough portPlaceholder contains a real HOST:PORT otherwise it is a variable/expression
re, err := regexp.Compile(`:*(\d+)`)
if err != nil {
Expand Down
11 changes: 11 additions & 0 deletions pkg/apis/model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,24 @@ type Component struct {
Ports []int
}

type Version struct {
SchemaVersion string
Default bool
Version string
}

type DevFileType struct {
Name string
Language string
ProjectType string
Tags []string
}

type DevfileFilter struct {
MinVersion string
MaxVersion string
}

type ApplicationFileInfo struct {
Dir string
File string
Expand Down
92 changes: 79 additions & 13 deletions pkg/apis/recognizer/devfile_recognizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,11 @@ import (

"github.com/devfile/alizer/pkg/apis/model"
"github.com/devfile/alizer/pkg/utils"
"github.com/hashicorp/go-version"
)

const MinimumAllowedVersion = "2.0.0"

func SelectDevFilesFromTypes(path string, devFileTypes []model.DevFileType) ([]int, error) {
alizerLogger := utils.GetOrCreateLogger()
ctx := context.Background()
Expand Down Expand Up @@ -102,15 +105,31 @@ func SelectDevFileUsingLanguagesFromTypes(languages []model.Language, devFileTyp
return devFilesIndexes[0], nil
}

func MatchDevfiles(path string, url string, filter model.DevfileFilter) ([]model.DevFileType, error) {
alizerLogger := utils.GetOrCreateLogger()
alizerLogger.V(0).Info("Starting devfile matching")
alizerLogger.V(1).Info(fmt.Sprintf("Downloading devfiles from registry %s", url))
devFileTypesFromRegistry, err := downloadDevFileTypesFromRegistry(url, filter)
if err != nil {
return []model.DevFileType{}, err
}

return selectDevfiles(path, devFileTypesFromRegistry)
}

func SelectDevFilesFromRegistry(path string, url string) ([]model.DevFileType, error) {
alizerLogger := utils.GetOrCreateLogger()
alizerLogger.V(0).Info("Starting devfile matching")
alizerLogger.V(1).Info(fmt.Sprintf("Downloading devfiles from registry %s", url))
devFileTypesFromRegistry, err := downloadDevFileTypesFromRegistry(url)
devFileTypesFromRegistry, err := downloadDevFileTypesFromRegistry(url, model.DevfileFilter{MinVersion: "", MaxVersion: ""})
if err != nil {
return []model.DevFileType{}, err
}
alizerLogger.V(1).Info(fmt.Sprintf("Fetched %d devfiles", len(devFileTypesFromRegistry)))

return selectDevfiles(path, devFileTypesFromRegistry)
}

func selectDevfiles(path string, devFileTypesFromRegistry []model.DevFileType) ([]model.DevFileType, error) {
indexes, err := SelectDevFilesFromTypes(path, devFileTypesFromRegistry)
if err != nil {
return []model.DevFileType{}, err
Expand All @@ -122,10 +141,11 @@ func SelectDevFilesFromRegistry(path string, url string) ([]model.DevFileType, e
}

return devFileTypes, nil

}

func SelectDevFileFromRegistry(path string, url string) (model.DevFileType, error) {
devFileTypes, err := downloadDevFileTypesFromRegistry(url)
devFileTypes, err := downloadDevFileTypesFromRegistry(url, model.DevfileFilter{MinVersion: "", MaxVersion: ""})
if err != nil {
return model.DevFileType{}, err
}
Expand All @@ -137,17 +157,63 @@ func SelectDevFileFromRegistry(path string, url string) (model.DevFileType, erro
return devFileTypes[index], nil
}

func downloadDevFileTypesFromRegistry(url string) ([]model.DevFileType, error) {
url = adaptUrl(url)
// Get the data
resp, err := http.Get(url)
func GetUrlWithVersions(url, minVersion, maxVersion string) (string, error) {
minAllowedVersion, err := version.NewVersion(MinimumAllowedVersion)
if err != nil {
// retry by appending index to url
url = appendIndexPath(url)
resp, err = http.Get(url)
return "", nil
}

if minVersion != "" && maxVersion != "" {
minV, err := version.NewVersion(minVersion)
if err != nil {
return url, nil
}
maxV, err := version.NewVersion(maxVersion)
if err != nil {
return url, nil
}
if maxV.LessThan(minV) {
return "", fmt.Errorf("max-version cannot be lower than min-version")
}
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.Sprintf("%s?minSchemaVersion=%s&maxSchemaVersion=%s", url, minVersion, maxVersion), nil
} else if minVersion != "" {
minV, err := version.NewVersion(minVersion)
if err != nil {
return "", nil
}
if minV.LessThan(minAllowedVersion) {
return "", fmt.Errorf("min version is lower than the minimum allowed version (2.0.0)")
}
return fmt.Sprintf("%s?minSchemaVersion=%s", url, minVersion), nil
} else if maxVersion != "" {
maxV, err := version.NewVersion(maxVersion)
if err != nil {
return []model.DevFileType{}, err
return "", nil
}
if maxV.LessThan(minAllowedVersion) {
return "", fmt.Errorf("max version is lower than the minimum allowed version (2.0.0)")
}
return fmt.Sprintf("%s?maxSchemaVersion=%s", url, maxVersion), nil
} else {
return url, nil
}
}

func downloadDevFileTypesFromRegistry(url string, filter model.DevfileFilter) ([]model.DevFileType, error) {
url = adaptUrl(url)
tmpUrl := appendIndexPath(url)
url, err := GetUrlWithVersions(tmpUrl, filter.MinVersion, filter.MaxVersion)
if err != nil {
return nil, err
}
// This value is set by the user in order to configure the registry
resp, err := http.Get(url) // #nosec G107
if err != nil {
return []model.DevFileType{}, err
}
defer func() error {
if err := resp.Body.Close(); err != nil {
Expand Down Expand Up @@ -177,9 +243,9 @@ func downloadDevFileTypesFromRegistry(url string) ([]model.DevFileType, error) {

func appendIndexPath(url string) string {
if strings.HasSuffix(url, "/") {
return url + "index"
return url + "v2index"
}
return url + "/index"
return url + "/v2index"
}

func adaptUrl(url string) string {
Expand Down
Loading