Skip to content

Make the parser able to parse any file (including files with .yml extension) #186

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

Merged
merged 9 commits into from
Nov 2, 2023
Merged
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