Skip to content

Commit

Permalink
Make the parser able to parse any file (including files with .yml ext…
Browse files Browse the repository at this point in the history
…ension)

This merges pull request #186 from rm3l/1265-support-.yml-extension

Signed-off-by: Armel Soro <asoro@redhat.com>
  • Loading branch information
rm3l committed Nov 2, 2023
2 parents 36607c5 + 728befe commit 57a7da8
Show file tree
Hide file tree
Showing 19 changed files with 918 additions and 83 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
## About

The Devfile Parser library is a Golang module that:
1. parses the devfile.yaml as specified by the [api](https://devfile.github.io/devfile/api-reference.html) & [schema](https://github.com/devfile/api/tree/main/schemas/latest).
2. writes to the devfile.yaml with the updated data.
1. parses a devfile as specified by the [api](https://devfile.io/docs/2.2.1/devfile-schema) & [schema](https://github.com/devfile/api/tree/main/schemas/latest).
2. writes to the specified devfile with the updated data.
3. generates Kubernetes objects for the various devfile resources.
4. defines util functions for the devfile.
5. downloads resources from a parent devfile if specified in the devfile.yaml
5. downloads resources from a parent devfile if specified in the devfile.

## Private repository support

Expand Down
235 changes: 234 additions & 1 deletion pkg/devfile/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package devfile

import (
"context"
"fmt"
"net"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -273,6 +274,7 @@ spec:
wantKubernetesInline string
wantOpenshiftInline string
wantVariables map[string]string
additionalChecks func(parser.DevfileObj) error
}{
{
name: "with external overriding variables",
Expand Down Expand Up @@ -415,7 +417,7 @@ spec:
ExternalVariables: map[string]string{
"PARAMS": "baz",
},
Path: "./testdata/devfile1.yaml",
Path: "./testdata/devfile.yaml",
},
},
wantCommandLine: "./main baz",
Expand Down Expand Up @@ -527,6 +529,230 @@ spec:
StarterProjects: map[string][]string{},
},
},
{
name: "parsing devfile with context path containing multiple devfiles => priority to devfile.yaml",
args: args{
args: parser.ParserArgs{
ExternalVariables: map[string]string{
"PARAMS": "from devfile.yaml based on priority",
},
Path: "./testdata",
},
},
wantCommandLine: "./main from devfile.yaml based on priority",
wantVariables: map[string]string{
"PARAMS": "from devfile.yaml based on priority",
},
wantVarWarning: variables.VariableWarning{
Commands: map[string][]string{},
Components: map[string][]string{},
Projects: map[string][]string{},
StarterProjects: map[string][]string{},
},
additionalChecks: func(devfileObj parser.DevfileObj) error {
if devfileObj.Data.GetMetadata().DisplayName != "Go Runtime (devfile.yaml)" {
return fmt.Errorf("expected 'Go Runtime (devfile.yaml)' as metadata.displayName in devfile, but got %q",
devfileObj.Data.GetMetadata().DisplayName)
}
return nil
},
},
{
name: "parsing devfile with context path containing multiple devfiles => priority to .devfile.yaml",
args: args{
args: parser.ParserArgs{
ExternalVariables: map[string]string{
"PARAMS": "from .devfile.yaml based on priority",
},
Path: "./testdata/priority-for-dot_devfile_yaml",
},
},
wantCommandLine: "./main from .devfile.yaml based on priority",
wantVariables: map[string]string{
"PARAMS": "from .devfile.yaml based on priority",
},
wantVarWarning: variables.VariableWarning{
Commands: map[string][]string{},
Components: map[string][]string{},
Projects: map[string][]string{},
StarterProjects: map[string][]string{},
},
additionalChecks: func(devfileObj parser.DevfileObj) error {
if devfileObj.Data.GetMetadata().DisplayName != "Go Runtime (.devfile.yaml)" {
return fmt.Errorf("expected 'Go Runtime (.devfile.yaml)' as metadata.displayName in devfile, but got %q",
devfileObj.Data.GetMetadata().DisplayName)
}
return nil
},
},
{
name: "parsing devfile with context path containing multiple devfiles => priority to devfile.yml",
args: args{
args: parser.ParserArgs{
ExternalVariables: map[string]string{
"PARAMS": "from devfile.yml based on priority",
},
Path: "./testdata/priority-for-devfile_yml",
},
},
wantCommandLine: "./main from devfile.yml based on priority",
wantVariables: map[string]string{
"PARAMS": "from devfile.yml based on priority",
},
wantVarWarning: variables.VariableWarning{
Commands: map[string][]string{},
Components: map[string][]string{},
Projects: map[string][]string{},
StarterProjects: map[string][]string{},
},
additionalChecks: func(devfileObj parser.DevfileObj) error {
if devfileObj.Data.GetMetadata().DisplayName != "Test stack (devfile.yml)" {
return fmt.Errorf("expected 'Test stack (devfile.yml)' as metadata.displayName in devfile, but got %q",
devfileObj.Data.GetMetadata().DisplayName)
}
return nil
},
},
{
name: "parsing devfile with context path containing multiple devfiles => priority to .devfile.yml",
args: args{
args: parser.ParserArgs{
ExternalVariables: map[string]string{
"PARAMS": "from .devfile.yml based on priority",
},
Path: "./testdata/priority-for-dot_devfile_yml",
},
},
wantCommandLine: "./main from .devfile.yml based on priority",
wantVariables: map[string]string{
"PARAMS": "from .devfile.yml based on priority",
},
wantVarWarning: variables.VariableWarning{
Commands: map[string][]string{},
Components: map[string][]string{},
Projects: map[string][]string{},
StarterProjects: map[string][]string{},
},
additionalChecks: func(devfileObj parser.DevfileObj) error {
if devfileObj.Data.GetMetadata().DisplayName != "Test stack (.devfile.yml)" {
return fmt.Errorf("expected 'Test stack (.devfile.yml)' as metadata.displayName in devfile, but got %q",
devfileObj.Data.GetMetadata().DisplayName)
}
return nil
},
},
{
name: "parsing devfile with .yml extension",
args: args{
args: parser.ParserArgs{
ExternalVariables: map[string]string{
"PARAMS": "from devfile.yml",
},
Path: "./testdata/devfile.yml",
},
},
wantCommandLine: "./main from devfile.yml",
wantVariables: map[string]string{
"PARAMS": "from devfile.yml",
},
wantVarWarning: variables.VariableWarning{
Commands: map[string][]string{},
Components: map[string][]string{},
Projects: map[string][]string{},
StarterProjects: map[string][]string{},
},
additionalChecks: func(devfileObj parser.DevfileObj) error {
if devfileObj.Data.GetMetadata().DisplayName != "Test stack (devfile.yml)" {
return fmt.Errorf("expected 'Test stack (devfile.yml)' as metadata.displayName in devfile, but got %q",
devfileObj.Data.GetMetadata().DisplayName)
}
return nil
},
},
{
name: "parsing .devfile with .yml extension",
args: args{
args: parser.ParserArgs{
ExternalVariables: map[string]string{
"PARAMS": "from .devfile.yml",
},
Path: "./testdata/.devfile.yml",
},
},
wantCommandLine: "./main from .devfile.yml",
wantVariables: map[string]string{
"PARAMS": "from .devfile.yml",
},
wantVarWarning: variables.VariableWarning{
Commands: map[string][]string{},
Components: map[string][]string{},
Projects: map[string][]string{},
StarterProjects: map[string][]string{},
},
additionalChecks: func(devfileObj parser.DevfileObj) error {
if devfileObj.Data.GetMetadata().DisplayName != "Test stack (.devfile.yml)" {
return fmt.Errorf("expected 'Test stack (.devfile.yml)' as metadata.displayName in devfile, but got %q",
devfileObj.Data.GetMetadata().DisplayName)
}
return nil
},
},
{
name: "parsing .devfile with .yaml extension",
args: args{
args: parser.ParserArgs{
ExternalVariables: map[string]string{
"PARAMS": "from .devfile.yaml",
},
Path: "./testdata/.devfile.yaml",
},
},
wantCommandLine: "./main from .devfile.yaml",
wantVariables: map[string]string{
"PARAMS": "from .devfile.yaml",
},
wantVarWarning: variables.VariableWarning{
Commands: map[string][]string{},
Components: map[string][]string{},
Projects: map[string][]string{},
StarterProjects: map[string][]string{},
},
additionalChecks: func(devfileObj parser.DevfileObj) error {
if devfileObj.Data.GetMetadata().DisplayName != "Go Runtime (.devfile.yaml)" {
return fmt.Errorf("expected 'Go Runtime (.devfile.yaml)' as metadata.displayName in devfile, but got %q",
devfileObj.Data.GetMetadata().DisplayName)
}
return nil
},
},
{
name: "parsing any valid devfile regardless of extension",
args: args{
args: parser.ParserArgs{
ExternalVariables: map[string]string{
"PARAMS": "from any valid devfile file",
},
Path: "./testdata/valid-devfile.yaml.txt",
},
},
wantCommandLine: "./main from any valid devfile file",
wantVariables: map[string]string{
"PARAMS": "from any valid devfile file",
},
wantVarWarning: variables.VariableWarning{
Commands: map[string][]string{},
Components: map[string][]string{},
Projects: map[string][]string{},
StarterProjects: map[string][]string{},
},
additionalChecks: func(devfileObj parser.DevfileObj) error {
if devfileObj.Data.GetMetadata().DisplayName != "Test stack (valid-devfile.yaml.txt)" {
return fmt.Errorf("expected 'Test stack (valid-devfile.yaml.txt)' as metadata.displayName in devfile, but got %q",
devfileObj.Data.GetMetadata().DisplayName)
}
return nil
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down Expand Up @@ -647,6 +873,13 @@ spec:
if !reflect.DeepEqual(variables, tt.wantVariables) {
t.Errorf("variables are %+v, expected %+v", variables, tt.wantVariables)
}

if tt.additionalChecks != nil {
err = tt.additionalChecks(gotD)
if err != nil {
t.Errorf("unexpected error while performing specific checks: %v", err)
}
}
})
}
}
26 changes: 9 additions & 17 deletions pkg/devfile/parser/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,7 @@
package parser

import (
"fmt"
"net/url"
"os"
"path/filepath"
"strings"

"github.com/devfile/library/v2/pkg/testingutil/filesystem"
"github.com/devfile/library/v2/pkg/util"
Expand All @@ -36,7 +32,10 @@ type DevfileCtx struct {
// absolute path of devfile
absPath string

// relative path of devfile
// relative path of devfile.
// It can also be a relative or absolute path to a folder containing one or more devfiles,
// in which case the library will try to pick an existing one, based on the following priority order:
// devfile.yaml > .devfile.yaml > devfile.yml > .devfile.yml
relPath string

// raw content of the devfile
Expand Down Expand Up @@ -96,23 +95,16 @@ func (d *DevfileCtx) populateDevfile() (err error) {

// Populate fills the DevfileCtx struct with relevant context info
func (d *DevfileCtx) Populate() (err error) {
if !strings.HasSuffix(d.relPath, ".yaml") {
if _, err := os.Stat(filepath.Join(d.relPath, "devfile.yaml")); os.IsNotExist(err) {
if _, err := os.Stat(filepath.Join(d.relPath, ".devfile.yaml")); os.IsNotExist(err) {
return fmt.Errorf("the provided path is not a valid yaml filepath, and devfile.yaml or .devfile.yaml not found in the provided path : %s", d.relPath)
} else {
d.relPath = filepath.Join(d.relPath, ".devfile.yaml")
}
} else {
d.relPath = filepath.Join(d.relPath, "devfile.yaml")
}
d.relPath, err = lookupDevfileFromPath(d.fs, d.relPath)
if err != nil {
return err
}
if err := d.SetAbsPath(); err != nil {
if err = d.SetAbsPath(); err != nil {
return err
}
klog.V(4).Infof("absolute devfile path: '%s'", d.absPath)
// Read and save devfile content
if err := d.SetDevfileContent(); err != nil {
if err = d.SetDevfileContent(); err != nil {
return err
}
return d.populateDevfile()
Expand Down
Loading

0 comments on commit 57a7da8

Please sign in to comment.