diff --git a/README.md b/README.md index 902d4352..6ab17c23 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/pkg/devfile/parse_test.go b/pkg/devfile/parse_test.go index eaafe75b..3213518a 100644 --- a/pkg/devfile/parse_test.go +++ b/pkg/devfile/parse_test.go @@ -17,6 +17,7 @@ package devfile import ( "context" + "fmt" "net" "net/http" "net/http/httptest" @@ -273,6 +274,7 @@ spec: wantKubernetesInline string wantOpenshiftInline string wantVariables map[string]string + additionalChecks func(parser.DevfileObj) error }{ { name: "with external overriding variables", @@ -415,7 +417,7 @@ spec: ExternalVariables: map[string]string{ "PARAMS": "baz", }, - Path: "./testdata/devfile1.yaml", + Path: "./testdata/devfile.yaml", }, }, wantCommandLine: "./main baz", @@ -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) { @@ -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) + } + } }) } } diff --git a/pkg/devfile/parser/context/context.go b/pkg/devfile/parser/context/context.go index 97ab49d3..0a283121 100644 --- a/pkg/devfile/parser/context/context.go +++ b/pkg/devfile/parser/context/context.go @@ -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" @@ -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 @@ -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() diff --git a/pkg/devfile/parser/context/location.go b/pkg/devfile/parser/context/location.go new file mode 100644 index 00000000..559bb817 --- /dev/null +++ b/pkg/devfile/parser/context/location.go @@ -0,0 +1,64 @@ +// +// Copyright 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 parser + +import ( + "errors" + "fmt" + "io/fs" + "path/filepath" + "strings" + + "github.com/devfile/library/v2/pkg/testingutil/filesystem" +) + +// possibleDevfileNames contains possible filenames for a devfile. +// Those are checked in this priority order from a given context dir. +var possibleDevfileNames = []string{ + "devfile.yaml", + ".devfile.yaml", + "devfile.yml", + ".devfile.yml", +} + +// lookupDevfileFromPath returns the file path to use as devfile filename, by looking at the relative path specified in relPath. +// If relPath is not a directory, it is returned as is. +// For backward compatibility, if relPath is a directory, it will try to detect the first existing devfile filename under relPath, +// based on the list of possible devfile filenames defined in the sorted possibleDevfileNames. +// It returns any error found while interacting with the filesystem, or if no file was found from the list of possible devfile names. +func lookupDevfileFromPath(fsys filesystem.Filesystem, relPath string) (string, error) { + stat, err := fsys.Stat(relPath) + if err != nil { + return "", err + } + + if !stat.IsDir() { + return relPath, nil + } + + for _, possibleDevfileName := range possibleDevfileNames { + p := filepath.Join(relPath, possibleDevfileName) + if _, err = fsys.Stat(p); errors.Is(err, fs.ErrNotExist) { + continue + } + return p, nil + } + + return "", fmt.Errorf( + "the provided path is not a valid yaml filepath, and no possible devfile could be found in the provided path : %s. Possible filenames for a devfile: %s", + relPath, + strings.Join(possibleDevfileNames, ", ")) +} diff --git a/pkg/devfile/parser/context/location_test.go b/pkg/devfile/parser/context/location_test.go new file mode 100644 index 00000000..8358a7f9 --- /dev/null +++ b/pkg/devfile/parser/context/location_test.go @@ -0,0 +1,218 @@ +// +// Copyright 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 parser + +import ( + "path/filepath" + "testing" + + "github.com/devfile/library/v2/pkg/testingutil/filesystem" + "github.com/google/go-cmp/cmp" +) + +func Test_lookupDevfileFromPath(t *testing.T) { + type fields struct { + relPath string + fsCustomizer func(filesystem.Filesystem) error + } + tests := []struct { + name string + fields fields + want string + wantErr bool + }{ + { + name: "invalid relative path", + fields: fields{ + relPath: "non/existent/relative/path", + }, + wantErr: true, + }, + { + name: "invalid absolute path", + fields: fields{ + relPath: "/non/existent/absolute/path", + }, + wantErr: true, + }, + { + name: "relative path to file", + fields: fields{ + relPath: "my-devfile.yaml", + fsCustomizer: func(fs filesystem.Filesystem) error { + _, err := fs.Create("my-devfile.yaml") + return err + }, + }, + wantErr: false, + want: "my-devfile.yaml", + }, + { + name: "absolute path to file", + fields: fields{ + relPath: "/my-absolute-devfile.yaml", + fsCustomizer: func(fs filesystem.Filesystem) error { + _, err := fs.Create("/my-absolute-devfile.yaml") + return err + }, + }, + wantErr: false, + want: "/my-absolute-devfile.yaml", + }, + { + name: "empty directory", + fields: fields{ + relPath: "my-files", + fsCustomizer: func(fs filesystem.Filesystem) error { + return fs.MkdirAll("my-files", 0755) + }, + }, + wantErr: true, + }, + { + name: "directory with no devfile filename detected", + fields: fields{ + relPath: "my-files", + fsCustomizer: func(fs filesystem.Filesystem) error { + dir := "my-files" + err := fs.MkdirAll("my-files", 0755) + if err != nil { + return err + } + for _, f := range possibleDevfileNames { + if _, err = fs.Create(filepath.Join(dir, f+".bak")); err != nil { + return err + } + } + return err + }, + }, + wantErr: true, + }, + { + name: "directory with all possible devfile filenames => priority to devfile.yaml", + fields: fields{ + relPath: "my-devfiles", + fsCustomizer: func(fs filesystem.Filesystem) error { + dir := "my-devfiles" + err := fs.MkdirAll("my-devfiles", 0755) + if err != nil { + return err + } + for _, f := range possibleDevfileNames { + if _, err = fs.Create(filepath.Join(dir, f)); err != nil { + return err + } + } + return err + }, + }, + wantErr: false, + want: "my-devfiles/devfile.yaml", + }, + { + name: "directory with missing devfile.yaml => priority to .devfile.yaml", + fields: fields{ + relPath: "my-devfiles", + fsCustomizer: func(fs filesystem.Filesystem) error { + dir := "my-devfiles" + err := fs.MkdirAll("my-devfiles", 0755) + if err != nil { + return err + } + for _, f := range possibleDevfileNames { + if f == "devfile.yaml" { + continue + } + if _, err = fs.Create(filepath.Join(dir, f)); err != nil { + return err + } + } + return err + }, + }, + wantErr: false, + want: "my-devfiles/.devfile.yaml", + }, + { + name: "directory with missing devfile.yaml and .devfile.yaml => priority to devfile.yml", + fields: fields{ + relPath: "my-devfiles", + fsCustomizer: func(fs filesystem.Filesystem) error { + dir := "my-devfiles" + err := fs.MkdirAll("my-devfiles", 0755) + if err != nil { + return err + } + for _, f := range possibleDevfileNames { + if f == "devfile.yaml" || f == ".devfile.yaml" { + continue + } + if _, err = fs.Create(filepath.Join(dir, f)); err != nil { + return err + } + } + return err + }, + }, + wantErr: false, + want: "my-devfiles/devfile.yml", + }, + { + name: "directory with missing devfile.yaml and .devfile.yaml and devfile.yml => priority to .devfile.yml", + fields: fields{ + relPath: "my-devfiles", + fsCustomizer: func(fs filesystem.Filesystem) error { + dir := "my-devfiles" + err := fs.MkdirAll("my-devfiles", 0755) + if err != nil { + return err + } + for _, f := range possibleDevfileNames { + if f == "devfile.yaml" || f == ".devfile.yaml" || f == "devfile.yml" { + continue + } + if _, err = fs.Create(filepath.Join(dir, f)); err != nil { + return err + } + } + return err + }, + }, + wantErr: false, + want: "my-devfiles/.devfile.yml", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fs := filesystem.NewFakeFs() + var err error + if tt.fields.fsCustomizer != nil { + err = tt.fields.fsCustomizer(fs) + } + if err != nil { + t.Fatalf("unexpected error while setting up filesystem: %v", err) + } + got, err := lookupDevfileFromPath(fs, tt.fields.relPath) + if (err != nil) != tt.wantErr { + t.Errorf("lookupDevfileFromPath(): unexpected error: %v. wantErr=%v", err, tt.wantErr) + } + if diff := cmp.Diff(tt.want, got); diff != "" { + t.Errorf("lookupDevfileFromPath(): mismatch (-want +got): %s\n", diff) + } + }) + } +} diff --git a/pkg/devfile/parser/parse.go b/pkg/devfile/parser/parse.go index f6b681f9..29bf13d9 100644 --- a/pkg/devfile/parser/parse.go +++ b/pkg/devfile/parser/parse.go @@ -80,7 +80,10 @@ func parseDevfile(d DevfileObj, resolveCtx *resolutionContextTree, tool resolver // ParserArgs is the struct to pass into parser functions which contains required info for parsing devfile. // It accepts devfile path, devfile URL or devfile content in []byte format. type ParserArgs struct { - // Path is a relative or absolute devfile path on disk + // Path is a relative or absolute devfile path on disk. + // 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 Path string // URL is the URL address of the specific devfile. URL string diff --git a/pkg/devfile/parser/writer.go b/pkg/devfile/parser/writer.go index ed3f2870..a22c218e 100644 --- a/pkg/devfile/parser/writer.go +++ b/pkg/devfile/parser/writer.go @@ -26,7 +26,7 @@ import ( "k8s.io/klog" ) -// WriteYamlDevfile creates a devfile.yaml file +// WriteYamlDevfile writes the content of the Devfile data to its absolute path on the filesystem. func (d *DevfileObj) WriteYamlDevfile() error { // Check kubernetes components, and restore original uri content @@ -41,7 +41,7 @@ func (d *DevfileObj) WriteYamlDevfile() error { if err != nil { return errors.Wrapf(err, "failed to marshal devfile object into yaml") } - // Write to devfile.yaml + // Write to the absolute path fs := d.Ctx.GetFs() if fs == nil { fs = filesystem.DefaultFs{} @@ -52,7 +52,7 @@ func (d *DevfileObj) WriteYamlDevfile() error { } // Successful - klog.V(2).Infof("devfile yaml created at: '%s'", OutputDevfileYamlPath) + klog.V(2).Infof("devfile written to: '%s'", d.Ctx.GetAbsPath()) return nil } diff --git a/pkg/devfile/parser/writer_test.go b/pkg/devfile/parser/writer_test.go index db50c8ec..e31d49ed 100644 --- a/pkg/devfile/parser/writer_test.go +++ b/pkg/devfile/parser/writer_test.go @@ -17,67 +17,96 @@ package parser import ( "fmt" + "strings" + "testing" + v1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" apiAttributes "github.com/devfile/api/v2/pkg/attributes" devfilepkg "github.com/devfile/api/v2/pkg/devfile" devfileCtx "github.com/devfile/library/v2/pkg/devfile/parser/context" v2 "github.com/devfile/library/v2/pkg/devfile/parser/data/v2" "github.com/devfile/library/v2/pkg/testingutil/filesystem" - "strings" - "testing" ) -func TestWriteYamlDevfile(t *testing.T) { +func TestDevfileObj_WriteYamlDevfile(t *testing.T) { var ( schemaVersion = "2.2.0" - testName = "TestName" uri = "./relative/path/deploy.yaml" uri2 = "./relative/path/deploy2.yaml" - attributes = apiAttributes.Attributes{}.PutString(K8sLikeComponentOriginalURIKey, uri) - attributes2 = apiAttributes.Attributes{}.PutString(K8sLikeComponentOriginalURIKey, uri2) ) - t.Run("write yaml devfile", func(t *testing.T) { - - // Use fakeFs - fs := filesystem.NewFakeFs() + tests := []struct { + name string + fileName string + wantErr bool + }{ + { + name: "write devfile with .yaml extension", + fileName: OutputDevfileYamlPath, + }, + { + name: "write .devfile with .yaml extension", + fileName: ".devfile.yaml", + }, + { + name: "write devfile with .yml extension", + fileName: "devfile.yml", + }, + { + name: "write .devfile with .yml extension", + fileName: ".devfile.yml", + }, + { + name: "write any file, regardless of name and extension", + fileName: "some-random-file", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var ( + // Use fakeFs + fs = filesystem.NewFakeFs() + attributes = apiAttributes.Attributes{}.PutString(K8sLikeComponentOriginalURIKey, uri) + attributes2 = apiAttributes.Attributes{}.PutString(K8sLikeComponentOriginalURIKey, uri2) + ) - // DevfileObj - devfileObj := DevfileObj{ - Ctx: devfileCtx.FakeContext(fs, OutputDevfileYamlPath), - Data: &v2.DevfileV2{ - Devfile: v1.Devfile{ - DevfileHeader: devfilepkg.DevfileHeader{ - SchemaVersion: schemaVersion, - Metadata: devfilepkg.DevfileMetadata{ - Name: testName, + // DevfileObj + devfileObj := DevfileObj{ + Ctx: devfileCtx.FakeContext(fs, tt.fileName), + Data: &v2.DevfileV2{ + Devfile: v1.Devfile{ + DevfileHeader: devfilepkg.DevfileHeader{ + SchemaVersion: schemaVersion, + Metadata: devfilepkg.DevfileMetadata{ + Name: tt.name, + }, }, - }, - DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ - DevWorkspaceTemplateSpecContent: v1.DevWorkspaceTemplateSpecContent{ - Components: []v1.Component{ - { - Name: "kubeComp", - Attributes: attributes, - ComponentUnion: v1.ComponentUnion{ - Kubernetes: &v1.KubernetesComponent{ - K8sLikeComponent: v1.K8sLikeComponent{ - K8sLikeComponentLocation: v1.K8sLikeComponentLocation{ - Inlined: "placeholder", + DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ + DevWorkspaceTemplateSpecContent: v1.DevWorkspaceTemplateSpecContent{ + Components: []v1.Component{ + { + Name: "kubeComp", + Attributes: attributes, + ComponentUnion: v1.ComponentUnion{ + Kubernetes: &v1.KubernetesComponent{ + K8sLikeComponent: v1.K8sLikeComponent{ + K8sLikeComponentLocation: v1.K8sLikeComponentLocation{ + Inlined: "placeholder", + }, }, }, }, }, - }, - { - Name: "openshiftComp", - Attributes: attributes2, - ComponentUnion: v1.ComponentUnion{ - Openshift: &v1.OpenshiftComponent{ - K8sLikeComponent: v1.K8sLikeComponent{ - K8sLikeComponentLocation: v1.K8sLikeComponentLocation{ - Inlined: "placeholder", + { + Name: "openshiftComp", + Attributes: attributes2, + ComponentUnion: v1.ComponentUnion{ + Openshift: &v1.OpenshiftComponent{ + K8sLikeComponent: v1.K8sLikeComponent{ + K8sLikeComponentLocation: v1.K8sLikeComponentLocation{ + Inlined: "placeholder", + }, }, }, }, @@ -87,24 +116,26 @@ func TestWriteYamlDevfile(t *testing.T) { }, }, }, - }, - } - devfileObj.Ctx.SetConvertUriToInlined(true) + } + devfileObj.Ctx.SetConvertUriToInlined(true) - // test func() - err := devfileObj.WriteYamlDevfile() - if err != nil { - t.Errorf("TestWriteYamlDevfile() unexpected error: '%v'", err) - } + // test func() + err := devfileObj.WriteYamlDevfile() + if (err != nil) != tt.wantErr { + t.Errorf("TestWriteYamlDevfile() unexpected error: '%v', wantErr=%v", err, tt.wantErr) + return + } - if _, err := fs.Stat(OutputDevfileYamlPath); err != nil { - t.Errorf("TestWriteYamlDevfile() unexpected error: '%v'", err) - } + if _, err := fs.Stat(tt.fileName); err != nil { + t.Errorf("TestWriteYamlDevfile() unexpected error: '%v'", err) + } + + data, err := fs.ReadFile(tt.fileName) + if err != nil { + t.Errorf("TestWriteYamlDevfile() unexpected error: '%v'", err) + return + } - data, err := fs.ReadFile(OutputDevfileYamlPath) - if err != nil { - t.Errorf("TestWriteYamlDevfile() unexpected error: '%v'", err) - } else { content := string(data) if strings.Contains(content, "inlined") || strings.Contains(content, K8sLikeComponentOriginalURIKey) { t.Errorf("TestWriteYamlDevfile() failed: kubernetes component should not contain inlined or %s", K8sLikeComponentOriginalURIKey) @@ -115,6 +146,6 @@ func TestWriteYamlDevfile(t *testing.T) { if !strings.Contains(content, fmt.Sprintf("uri: %s", uri2)) { t.Errorf("TestWriteYamlDevfile() failed: openshift component does not contain uri") } - } - }) + }) + } } diff --git a/pkg/devfile/testdata/.devfile.yaml b/pkg/devfile/testdata/.devfile.yaml new file mode 100644 index 00000000..927dff87 --- /dev/null +++ b/pkg/devfile/testdata/.devfile.yaml @@ -0,0 +1,35 @@ +commands: +- exec: + commandLine: ./main {{ PARAMS }} + component: runtime + group: + isDefault: true + kind: run + workingDir: ${PROJECT_SOURCE} + id: run +components: +- container: + endpoints: + - name: http + targetPort: 8080 + image: golang:latest + memoryLimit: 1024Mi + mountSources: true + name: runtime +- kubernetes: + uri: http://127.0.0.1:8080/outerloop-deploy.yaml + name: outerloop-deploy +- openshift: + uri: http://127.0.0.1:8080/outerloop-service.yaml + name: outerloop-deploy2 +metadata: + description: Stack with the latest Go version + displayName: Go Runtime (.devfile.yaml) + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/golang.svg + language: go + name: my-go-app + projectType: go + tags: + - Go + version: 1.0.0 +schemaVersion: 2.2.0 \ No newline at end of file diff --git a/pkg/devfile/testdata/.devfile.yml b/pkg/devfile/testdata/.devfile.yml new file mode 100644 index 00000000..3625c0b8 --- /dev/null +++ b/pkg/devfile/testdata/.devfile.yml @@ -0,0 +1,28 @@ +commands: +- exec: + commandLine: ./main {{ PARAMS }} + component: runtime + group: + isDefault: true + kind: run + workingDir: ${PROJECT_SOURCE} + id: run +components: +- container: + image: busybox:latest + command: [tail] + args: [ -f, /dev/null ] + mountSources: true + name: runtime +- kubernetes: + uri: http://127.0.0.1:8080/outerloop-deploy.yaml + name: outerloop-deploy +- openshift: + uri: http://127.0.0.1:8080/outerloop-service.yaml + name: outerloop-deploy2 +metadata: + description: Test stack (Busybox) + displayName: Test stack (.devfile.yml) + name: my-test-app + version: 0.1.0 +schemaVersion: 2.2.0 \ No newline at end of file diff --git a/pkg/devfile/testdata/devfile1.yaml b/pkg/devfile/testdata/devfile.yaml similarity index 95% rename from pkg/devfile/testdata/devfile1.yaml rename to pkg/devfile/testdata/devfile.yaml index 9286575b..63abe9ce 100644 --- a/pkg/devfile/testdata/devfile1.yaml +++ b/pkg/devfile/testdata/devfile.yaml @@ -24,7 +24,7 @@ components: name: outerloop-deploy2 metadata: description: Stack with the latest Go version - displayName: Go Runtime + displayName: Go Runtime (devfile.yaml) icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/golang.svg language: go name: my-go-app diff --git a/pkg/devfile/testdata/devfile.yml b/pkg/devfile/testdata/devfile.yml new file mode 100644 index 00000000..23bd7e0b --- /dev/null +++ b/pkg/devfile/testdata/devfile.yml @@ -0,0 +1,28 @@ +commands: +- exec: + commandLine: ./main {{ PARAMS }} + component: runtime + group: + isDefault: true + kind: run + workingDir: ${PROJECT_SOURCE} + id: run +components: +- container: + image: busybox:latest + command: [tail] + args: [ -f, /dev/null ] + mountSources: true + name: runtime +- kubernetes: + uri: http://127.0.0.1:8080/outerloop-deploy.yaml + name: outerloop-deploy +- openshift: + uri: http://127.0.0.1:8080/outerloop-service.yaml + name: outerloop-deploy2 +metadata: + description: Test stack (Busybox) + displayName: Test stack (devfile.yml) + name: my-test-app + version: 0.1.0 +schemaVersion: 2.2.0 \ No newline at end of file diff --git a/pkg/devfile/testdata/priority-for-devfile_yml/.devfile.yml b/pkg/devfile/testdata/priority-for-devfile_yml/.devfile.yml new file mode 100644 index 00000000..3625c0b8 --- /dev/null +++ b/pkg/devfile/testdata/priority-for-devfile_yml/.devfile.yml @@ -0,0 +1,28 @@ +commands: +- exec: + commandLine: ./main {{ PARAMS }} + component: runtime + group: + isDefault: true + kind: run + workingDir: ${PROJECT_SOURCE} + id: run +components: +- container: + image: busybox:latest + command: [tail] + args: [ -f, /dev/null ] + mountSources: true + name: runtime +- kubernetes: + uri: http://127.0.0.1:8080/outerloop-deploy.yaml + name: outerloop-deploy +- openshift: + uri: http://127.0.0.1:8080/outerloop-service.yaml + name: outerloop-deploy2 +metadata: + description: Test stack (Busybox) + displayName: Test stack (.devfile.yml) + name: my-test-app + version: 0.1.0 +schemaVersion: 2.2.0 \ No newline at end of file diff --git a/pkg/devfile/testdata/priority-for-devfile_yml/devfile.yml b/pkg/devfile/testdata/priority-for-devfile_yml/devfile.yml new file mode 100644 index 00000000..23bd7e0b --- /dev/null +++ b/pkg/devfile/testdata/priority-for-devfile_yml/devfile.yml @@ -0,0 +1,28 @@ +commands: +- exec: + commandLine: ./main {{ PARAMS }} + component: runtime + group: + isDefault: true + kind: run + workingDir: ${PROJECT_SOURCE} + id: run +components: +- container: + image: busybox:latest + command: [tail] + args: [ -f, /dev/null ] + mountSources: true + name: runtime +- kubernetes: + uri: http://127.0.0.1:8080/outerloop-deploy.yaml + name: outerloop-deploy +- openshift: + uri: http://127.0.0.1:8080/outerloop-service.yaml + name: outerloop-deploy2 +metadata: + description: Test stack (Busybox) + displayName: Test stack (devfile.yml) + name: my-test-app + version: 0.1.0 +schemaVersion: 2.2.0 \ No newline at end of file diff --git a/pkg/devfile/testdata/priority-for-dot_devfile_yaml/.devfile.yaml b/pkg/devfile/testdata/priority-for-dot_devfile_yaml/.devfile.yaml new file mode 100644 index 00000000..927dff87 --- /dev/null +++ b/pkg/devfile/testdata/priority-for-dot_devfile_yaml/.devfile.yaml @@ -0,0 +1,35 @@ +commands: +- exec: + commandLine: ./main {{ PARAMS }} + component: runtime + group: + isDefault: true + kind: run + workingDir: ${PROJECT_SOURCE} + id: run +components: +- container: + endpoints: + - name: http + targetPort: 8080 + image: golang:latest + memoryLimit: 1024Mi + mountSources: true + name: runtime +- kubernetes: + uri: http://127.0.0.1:8080/outerloop-deploy.yaml + name: outerloop-deploy +- openshift: + uri: http://127.0.0.1:8080/outerloop-service.yaml + name: outerloop-deploy2 +metadata: + description: Stack with the latest Go version + displayName: Go Runtime (.devfile.yaml) + icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/golang.svg + language: go + name: my-go-app + projectType: go + tags: + - Go + version: 1.0.0 +schemaVersion: 2.2.0 \ No newline at end of file diff --git a/pkg/devfile/testdata/priority-for-dot_devfile_yaml/.devfile.yml b/pkg/devfile/testdata/priority-for-dot_devfile_yaml/.devfile.yml new file mode 100644 index 00000000..3625c0b8 --- /dev/null +++ b/pkg/devfile/testdata/priority-for-dot_devfile_yaml/.devfile.yml @@ -0,0 +1,28 @@ +commands: +- exec: + commandLine: ./main {{ PARAMS }} + component: runtime + group: + isDefault: true + kind: run + workingDir: ${PROJECT_SOURCE} + id: run +components: +- container: + image: busybox:latest + command: [tail] + args: [ -f, /dev/null ] + mountSources: true + name: runtime +- kubernetes: + uri: http://127.0.0.1:8080/outerloop-deploy.yaml + name: outerloop-deploy +- openshift: + uri: http://127.0.0.1:8080/outerloop-service.yaml + name: outerloop-deploy2 +metadata: + description: Test stack (Busybox) + displayName: Test stack (.devfile.yml) + name: my-test-app + version: 0.1.0 +schemaVersion: 2.2.0 \ No newline at end of file diff --git a/pkg/devfile/testdata/priority-for-dot_devfile_yaml/devfile.yml b/pkg/devfile/testdata/priority-for-dot_devfile_yaml/devfile.yml new file mode 100644 index 00000000..23bd7e0b --- /dev/null +++ b/pkg/devfile/testdata/priority-for-dot_devfile_yaml/devfile.yml @@ -0,0 +1,28 @@ +commands: +- exec: + commandLine: ./main {{ PARAMS }} + component: runtime + group: + isDefault: true + kind: run + workingDir: ${PROJECT_SOURCE} + id: run +components: +- container: + image: busybox:latest + command: [tail] + args: [ -f, /dev/null ] + mountSources: true + name: runtime +- kubernetes: + uri: http://127.0.0.1:8080/outerloop-deploy.yaml + name: outerloop-deploy +- openshift: + uri: http://127.0.0.1:8080/outerloop-service.yaml + name: outerloop-deploy2 +metadata: + description: Test stack (Busybox) + displayName: Test stack (devfile.yml) + name: my-test-app + version: 0.1.0 +schemaVersion: 2.2.0 \ No newline at end of file diff --git a/pkg/devfile/testdata/priority-for-dot_devfile_yml/.devfile.yml b/pkg/devfile/testdata/priority-for-dot_devfile_yml/.devfile.yml new file mode 100644 index 00000000..3625c0b8 --- /dev/null +++ b/pkg/devfile/testdata/priority-for-dot_devfile_yml/.devfile.yml @@ -0,0 +1,28 @@ +commands: +- exec: + commandLine: ./main {{ PARAMS }} + component: runtime + group: + isDefault: true + kind: run + workingDir: ${PROJECT_SOURCE} + id: run +components: +- container: + image: busybox:latest + command: [tail] + args: [ -f, /dev/null ] + mountSources: true + name: runtime +- kubernetes: + uri: http://127.0.0.1:8080/outerloop-deploy.yaml + name: outerloop-deploy +- openshift: + uri: http://127.0.0.1:8080/outerloop-service.yaml + name: outerloop-deploy2 +metadata: + description: Test stack (Busybox) + displayName: Test stack (.devfile.yml) + name: my-test-app + version: 0.1.0 +schemaVersion: 2.2.0 \ No newline at end of file diff --git a/pkg/devfile/testdata/valid-devfile.yaml.txt b/pkg/devfile/testdata/valid-devfile.yaml.txt new file mode 100644 index 00000000..8cb9d9e9 --- /dev/null +++ b/pkg/devfile/testdata/valid-devfile.yaml.txt @@ -0,0 +1,28 @@ +commands: +- exec: + commandLine: ./main {{ PARAMS }} + component: runtime + group: + isDefault: true + kind: run + workingDir: ${PROJECT_SOURCE} + id: run +components: +- container: + image: busybox:latest + command: [tail] + args: [ -f, /dev/null ] + mountSources: true + name: runtime +- kubernetes: + uri: http://127.0.0.1:8080/outerloop-deploy.yaml + name: outerloop-deploy +- openshift: + uri: http://127.0.0.1:8080/outerloop-service.yaml + name: outerloop-deploy2 +metadata: + description: Test stack (Busybox) + displayName: Test stack (valid-devfile.yaml.txt) + name: my-test-app + version: 0.1.0 +schemaVersion: 2.2.0 \ No newline at end of file