diff --git a/go.mod b/go.mod index 2be5b7e9..0b91943a 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/devfile/library go 1.13 require ( - github.com/devfile/api/v2 v2.0.0-20210121164412-49ba915897f4 + github.com/devfile/api/v2 v2.0.0-20210202172954-6424f4139ac7 github.com/fatih/color v1.7.0 github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 github.com/gobwas/glob v0.2.3 diff --git a/go.sum b/go.sum index b6b09b9a..d8ff706f 100644 --- a/go.sum +++ b/go.sum @@ -44,8 +44,8 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/devfile/api/v2 v2.0.0-20210121164412-49ba915897f4 h1:jDzWFpF/BoyaemoPjAzw5SmlX172JsSsh+Frujm5Ww4= -github.com/devfile/api/v2 v2.0.0-20210121164412-49ba915897f4/go.mod h1:Cot4snybn3qhIh48oIFi9McocnIx7zY5fFbjfrIpPvg= +github.com/devfile/api/v2 v2.0.0-20210202172954-6424f4139ac7 h1:bQGUVLEGQtVkvS94K4gQbu57Rk/npcZQmgORmCWYNy8= +github.com/devfile/api/v2 v2.0.0-20210202172954-6424f4139ac7/go.mod h1:Cot4snybn3qhIh48oIFi9McocnIx7zY5fFbjfrIpPvg= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= diff --git a/pkg/devfile/parser/context/context.go b/pkg/devfile/parser/context/context.go index c00763c6..252a920c 100644 --- a/pkg/devfile/parser/context/context.go +++ b/pkg/devfile/parser/context/context.go @@ -1,6 +1,7 @@ package parser import ( + "fmt" "net/url" "github.com/devfile/library/pkg/testingutil/filesystem" @@ -8,6 +9,8 @@ import ( "k8s.io/klog" ) +var URIMap = make(map[string]bool) + // DevfileCtx stores context info regarding devfile type DevfileCtx struct { @@ -67,6 +70,10 @@ func (d *DevfileCtx) Populate() (err error) { return err } klog.V(4).Infof("absolute devfile path: '%s'", d.absPath) + if URIMap[d.absPath] { + return fmt.Errorf("URI %v is recursively referenced", d.absPath) + } + URIMap[d.absPath] = true // Read and save devfile content if err := d.SetDevfileContent(); err != nil { return err @@ -81,7 +88,10 @@ func (d *DevfileCtx) PopulateFromURL() (err error) { if err != nil { return err } - + if URIMap[d.url] { + return fmt.Errorf("URI %v is recursively referenced", d.url) + } + URIMap[d.url] = true // Read and save devfile content if err := d.SetDevfileContent(); err != nil { return err @@ -101,10 +111,16 @@ func (d *DevfileCtx) Validate() error { return d.ValidateDevfileSchema() } +// GetAbsPath func returns current devfile absolute path func (d *DevfileCtx) GetAbsPath() string { return d.absPath } +// GetURL func returns current devfile absolute URL address +func (d *DevfileCtx) GetURL() string { + return d.url +} + // SetAbsPath sets absolute file path for devfile func (d *DevfileCtx) SetAbsPath() (err error) { // Set devfile absolute path diff --git a/pkg/devfile/parser/parse.go b/pkg/devfile/parser/parse.go index 109e2e29..a86459b4 100644 --- a/pkg/devfile/parser/parse.go +++ b/pkg/devfile/parser/parse.go @@ -3,6 +3,9 @@ package parser import ( "encoding/json" "fmt" + "net/url" + "path" + "strings" devfileCtx "github.com/devfile/library/pkg/devfile/parser/context" "github.com/devfile/library/pkg/devfile/parser/data" @@ -13,11 +16,10 @@ import ( v1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" apiOverride "github.com/devfile/api/v2/pkg/utils/overriding" + "github.com/devfile/api/v2/pkg/validation" "github.com/pkg/errors" ) -var URLMap = make(map[string]bool) - // ParseDevfile func validates the devfile integrity. // Creates devfile context and runtime objects func parseDevfile(d DevfileObj, flattenedDevfile bool) (DevfileObj, error) { @@ -46,8 +48,8 @@ func parseDevfile(d DevfileObj, flattenedDevfile bool) (DevfileObj, error) { return DevfileObj{}, err } } - for url := range URLMap { - delete(URLMap, url) + for uri := range devfileCtx.URIMap { + delete(devfileCtx.URIMap, uri) } // Successful return d, nil @@ -84,11 +86,6 @@ func ParseRawDevfile(path string) (d DevfileObj, err error) { // ParseFromURL func parses and validates the devfile integrity. // Creates devfile context and runtime objects func ParseFromURL(url string) (d DevfileObj, err error) { - if _, exist := URLMap[url]; !exist { - URLMap[url] = true - } else { - return d, fmt.Errorf("URI %v is recursively referenced", url) - } d.Ctx = devfileCtx.NewURLDevfileCtx(url) // Fill the fields of DevfileCtx struct err = d.Ctx.PopulateFromURL() @@ -122,7 +119,7 @@ func parseParentAndPlugin(d DevfileObj) (err error) { parent := d.Data.GetParent() var parentDevfileObj DevfileObj if d.Data.GetParent().Uri != "" { - parentDevfileObj, err = ParseFromURL(parent.Uri) + parentDevfileObj, err = parseFromURI(parent.Uri, d.Ctx) if err != nil { return err } @@ -153,7 +150,7 @@ func parseParentAndPlugin(d DevfileObj) (err error) { plugin := component.Plugin var pluginDevfileObj DevfileObj if plugin.Uri != "" { - pluginDevfileObj, err = ParseFromURL(plugin.Uri) + pluginDevfileObj, err = parseFromURI(plugin.Uri, d.Ctx) if err != nil { return err } @@ -181,3 +178,34 @@ func parseParentAndPlugin(d DevfileObj) (err error) { return nil } + +func parseFromURI(uri string, curDevfileCtx devfileCtx.DevfileCtx) (DevfileObj, error) { + // validate URI + err := validation.ValidateURI(uri) + if err != nil { + return DevfileObj{}, err + } + + // absolute URL address + if strings.HasPrefix(uri, "http://") || strings.HasPrefix(uri, "https://") { + return ParseFromURL(uri) + } + + // relative path on disk + if curDevfileCtx.GetAbsPath() != "" { + return Parse(path.Join(path.Dir(curDevfileCtx.GetAbsPath()), uri)) + } + + if curDevfileCtx.GetURL() != "" { + u, err := url.Parse(curDevfileCtx.GetURL()) + if err != nil { + return DevfileObj{}, err + } + + u.Path = path.Join(path.Dir(u.Path), uri) + // u.String() is the joint absolute URL path + return ParseFromURL(u.String()) + } + + return DevfileObj{}, fmt.Errorf("fail to parse from uri: %s", uri) +} diff --git a/pkg/devfile/parser/parse_test.go b/pkg/devfile/parser/parse_test.go index 86e2b8e4..b1df4bd5 100644 --- a/pkg/devfile/parser/parse_test.go +++ b/pkg/devfile/parser/parse_test.go @@ -2,10 +2,14 @@ package parser import ( "fmt" + "io/ioutil" "net" "net/http" "net/http/httptest" + "os" + "path" "reflect" + "strings" "testing" v1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2" @@ -17,7 +21,6 @@ import ( ) const schemaV200 = "2.0.0" -const devfileTempPath = "devfile.yaml" func Test_parseParentAndPlugin(t *testing.T) { @@ -38,7 +41,7 @@ func Test_parseParentAndPlugin(t *testing.T) { name: "case 1: it should override the requested parent's data and add the local devfile's data", args: args{ devFileObj: DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ @@ -253,7 +256,7 @@ func Test_parseParentAndPlugin(t *testing.T) { name: "case 2: handle a parent'data without any local override and add the local devfile's data", args: args{ devFileObj: DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ @@ -436,7 +439,7 @@ func Test_parseParentAndPlugin(t *testing.T) { name: "case 3: it should error out when the override is invalid", args: args{ devFileObj: DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ @@ -502,7 +505,7 @@ func Test_parseParentAndPlugin(t *testing.T) { name: "case 4: error out if the same parent command is defined again in the local devfile", args: args{ devFileObj: DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ @@ -555,7 +558,7 @@ func Test_parseParentAndPlugin(t *testing.T) { name: "case 5: error out if the same parent component is defined again in the local devfile", args: args{ devFileObj: DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ @@ -612,7 +615,7 @@ func Test_parseParentAndPlugin(t *testing.T) { name: "case 6: should not have error if the same event is defined again in the local devfile", args: args{ devFileObj: DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ @@ -669,7 +672,7 @@ func Test_parseParentAndPlugin(t *testing.T) { name: "case 7: error out if the parent project is defined again in the local devfile", args: args{ devFileObj: DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ @@ -714,7 +717,7 @@ func Test_parseParentAndPlugin(t *testing.T) { name: "case 8: it should merge the plugin's uri data and add the local devfile's data", args: args{ devFileObj: DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ @@ -897,7 +900,7 @@ func Test_parseParentAndPlugin(t *testing.T) { name: "case 9: it should override the plugin's data with local overrides and add the local devfile's data", args: args{ devFileObj: DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ @@ -1107,7 +1110,7 @@ func Test_parseParentAndPlugin(t *testing.T) { name: "case 10: it should error out when the plugin devfile is invalid", args: args{ devFileObj: DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{}, @@ -1152,7 +1155,7 @@ func Test_parseParentAndPlugin(t *testing.T) { name: "case 11: error out if the same plugin command is defined again in the local devfile", args: args{ devFileObj: DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ @@ -1205,7 +1208,7 @@ func Test_parseParentAndPlugin(t *testing.T) { name: "case 12: error out if the same plugin component is defined again in the local devfile", args: args{ devFileObj: DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ @@ -1262,7 +1265,7 @@ func Test_parseParentAndPlugin(t *testing.T) { name: "case 13: error out if the plugin project is defined again in the local devfile", args: args{ devFileObj: DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ @@ -1307,7 +1310,7 @@ func Test_parseParentAndPlugin(t *testing.T) { name: "case 14: error out if the same project is defined in the both plugin devfile and parent", args: args{ devFileObj: DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ @@ -1371,7 +1374,7 @@ func Test_parseParentAndPlugin(t *testing.T) { name: "case 15: error out if the same command is defined in both plugin devfile and parent devfile", args: args{ devFileObj: DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ @@ -1447,7 +1450,7 @@ func Test_parseParentAndPlugin(t *testing.T) { name: "case 16: error out if the same component is defined in both plugin devfile and parent devfile", args: args{ devFileObj: DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ @@ -1529,7 +1532,7 @@ func Test_parseParentAndPlugin(t *testing.T) { name: "case 17: it should override the requested parent's data and plugin's data, and add the local devfile's data", args: args{ devFileObj: DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ @@ -1784,7 +1787,7 @@ func Test_parseParentAndPlugin(t *testing.T) { name: "case 18: error out if the plugin component is defined with a different component type in the local devfile", args: args{ devFileObj: DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ @@ -1841,7 +1844,7 @@ func Test_parseParentAndPlugin(t *testing.T) { name: "case 19: it should override with no errors if the plugin component is defined with a different component type in the plugin override", args: args{ devFileObj: DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{}, }, @@ -1914,7 +1917,7 @@ func Test_parseParentAndPlugin(t *testing.T) { name: "case 20: error out if the parent component is defined with a different component type in the local devfile", args: args{ devFileObj: DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ @@ -1971,7 +1974,7 @@ func Test_parseParentAndPlugin(t *testing.T) { name: "case 21: it should override with no errors if the parent component is defined with a different component type in the parent override", args: args{ devFileObj: DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ @@ -2049,7 +2052,7 @@ func Test_parseParentAndPlugin(t *testing.T) { name: "case 22: error out if the URI is recursively referenced", args: args{ devFileObj: DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{}, }, @@ -2189,7 +2192,7 @@ func Test_parseParentAndPlugin_RecursivelyReference_withMultipleURI(t *testing.T const httpPrefix = "http://" devFileObj := DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ @@ -2219,7 +2222,7 @@ func Test_parseParentAndPlugin_RecursivelyReference_withMultipleURI(t *testing.T }, } parentDevfile1 := DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevfileHeader: devfilepkg.DevfileHeader{ @@ -2252,7 +2255,7 @@ func Test_parseParentAndPlugin_RecursivelyReference_withMultipleURI(t *testing.T }, } parentDevfile2 := DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevfileHeader: devfilepkg.DevfileHeader{ @@ -2280,7 +2283,7 @@ func Test_parseParentAndPlugin_RecursivelyReference_withMultipleURI(t *testing.T }, } parentDevfile3 := DevfileObj{ - Ctx: devfileCtx.NewDevfileCtx(devfileTempPath), + Ctx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), Data: &v2.DevfileV2{ Devfile: v1.Devfile{ DevfileHeader: devfilepkg.DevfileHeader{ @@ -2395,3 +2398,218 @@ func Test_parseParentAndPlugin_RecursivelyReference_withMultipleURI(t *testing.T }) } + +func Test_parseFromURI(t *testing.T) { + const uri1 = "127.0.0.1:8080" + const httpPrefix = "http://" + const localRelativeURI = "testTmp/dir/devfile.yaml" + const notExistURI = "notexist/devfile.yaml" + const invalidURL = "http//invalid.com" + uri2 := path.Join(uri1, localRelativeURI) + + localDevfile := DevfileObj{ + Ctx: devfileCtx.NewDevfileCtx(localRelativeURI), + Data: &v2.DevfileV2{ + Devfile: v1.Devfile{ + DevfileHeader: devfilepkg.DevfileHeader{ + SchemaVersion: schemaV200, + }, + DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ + DevWorkspaceTemplateSpecContent: v1.DevWorkspaceTemplateSpecContent{ + Components: []v1.Component{ + { + Name: "runtime", + ComponentUnion: v1.ComponentUnion{ + Container: &v1.ContainerComponent{ + Container: v1.Container{ + Image: "nodejs", + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + // prepare for local file + err := os.MkdirAll(path.Dir(localRelativeURI), 0755) + if err != nil { + t.Errorf("failed to create folder: %v, error: %v", path.Dir(localRelativeURI), err) + return + } + yamlData, err := yaml.Marshal(localDevfile.Data) + if err != nil { + t.Errorf("failed to marshall devfile data: %v", err) + return + } + err = ioutil.WriteFile(localRelativeURI, yamlData, 0644) + if err != nil { + t.Errorf("fail to write to file: %v", err) + return + } + defer os.RemoveAll("testTmp/") + + parentDevfile := DevfileObj{ + Ctx: devfileCtx.NewURLDevfileCtx(httpPrefix + uri1), + Data: &v2.DevfileV2{ + Devfile: v1.Devfile{ + DevfileHeader: devfilepkg.DevfileHeader{ + SchemaVersion: schemaV200, + }, + DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ + Parent: &v1.Parent{ + ImportReference: v1.ImportReference{ + ImportReferenceUnion: v1.ImportReferenceUnion{ + Uri: localRelativeURI, + }, + }, + }, + DevWorkspaceTemplateSpecContent: v1.DevWorkspaceTemplateSpecContent{ + Components: []v1.Component{ + { + Name: "runtime2", + ComponentUnion: v1.ComponentUnion{ + Volume: &v1.VolumeComponent{ + Volume: v1.Volume{ + Size: "500Mi", + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + relativeParentDevfile := DevfileObj{ + Ctx: devfileCtx.NewURLDevfileCtx(httpPrefix + uri2), + Data: &v2.DevfileV2{ + Devfile: v1.Devfile{ + DevfileHeader: devfilepkg.DevfileHeader{ + SchemaVersion: schemaV200, + }, + DevWorkspaceTemplateSpec: v1.DevWorkspaceTemplateSpec{ + DevWorkspaceTemplateSpecContent: v1.DevWorkspaceTemplateSpecContent{ + Components: []v1.Component{ + { + Name: "runtime", + ComponentUnion: v1.ComponentUnion{ + Volume: &v1.VolumeComponent{ + Volume: v1.Volume{ + Size: "500Mi", + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + testServer := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if strings.Contains(r.URL.Path, "notexist") { + w.WriteHeader(http.StatusNotFound) + return + } + var data []byte + var err error + if strings.Contains(r.URL.Path, "devfile.yaml") { + data, err = yaml.Marshal(relativeParentDevfile.Data) + } else { + data, err = yaml.Marshal(parentDevfile.Data) + } + if err != nil { + t.Errorf("unexpected error: %v", err) + return + } + _, err = w.Write(data) + if err != nil { + t.Errorf("unexpected error: %v", err) + } + })) + // create a listener with the desired port. + l, err := net.Listen("tcp", uri1) + if err != nil { + t.Errorf("unexpected error: %v", err) + return + } + + // NewUnstartedServer creates a listener. Close that listener and replace + // with the one we created. + testServer.Listener.Close() + testServer.Listener = l + + testServer.Start() + defer testServer.Close() + + tests := []struct { + name string + curDevfileCtx devfileCtx.DevfileCtx + uri string + wantDevFile DevfileObj + wantErr bool + }{ + { + name: "case 1: should be able to parse from relative uri on local disk", + curDevfileCtx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), + wantDevFile: localDevfile, + uri: localRelativeURI, + }, + { + name: "case 2: should be able to parse relative uri from URL", + curDevfileCtx: parentDevfile.Ctx, + wantDevFile: relativeParentDevfile, + uri: localRelativeURI, + }, + { + name: "case 3: should fail if no path or url has been set for devfile ctx", + curDevfileCtx: devfileCtx.DevfileCtx{}, + wantErr: true, + }, + { + name: "case 4: should fail if file not exist", + curDevfileCtx: devfileCtx.NewDevfileCtx(OutputDevfileYamlPath), + uri: notExistURI, + wantErr: true, + }, + { + name: "case 5: should fail if url not exist", + curDevfileCtx: devfileCtx.NewURLDevfileCtx(httpPrefix + uri1), + uri: notExistURI, + wantErr: true, + }, + { + name: "case 6: should fail if with invalid URI format", + curDevfileCtx: devfileCtx.NewURLDevfileCtx(OutputDevfileYamlPath), + uri: invalidURL, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // if the main devfile is from local, need to set absolute path + if tt.curDevfileCtx.GetURL() == "" { + err := tt.curDevfileCtx.SetAbsPath() + if err != nil { + t.Errorf("Test_parseFromURI() unexpected error = %v", err) + return + } + } + got, err := parseFromURI(tt.uri, tt.curDevfileCtx) + if tt.wantErr == (err == nil) { + t.Errorf("Test_parseFromURI() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if !reflect.DeepEqual(got.Data, tt.wantDevFile.Data) { + t.Errorf("wanted: %v, got: %v, difference at %v", tt.wantDevFile, got, pretty.Compare(tt.wantDevFile, got)) + } + }) + } +}