Skip to content

Commit c8a6bbf

Browse files
authored
Merge pull request #82 from maysunfaisal/useGlobalVar-1
Consume top level variables & attributes
2 parents ec1e90d + 28cfac0 commit c8a6bbf

13 files changed

+426
-114
lines changed

README.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ The Devfile Parser library is a Golang module that:
1212

1313
The function documentation can be accessed via [pkg.go.dev](https://pkg.go.dev/github.com/devfile/library).
1414
1. To parse a devfile, visit pkg/devfile/parse.go
15-
```
15+
```go
1616
// Parses the devfile and validates the devfile data
17-
devfile, err := devfilePkg.ParseAndValidate(devfileLocation)
17+
// if top-level variables are not substituted successfully, the warnings can be logged by parsing variableWarning
18+
devfile, variableWarning, err := devfilePkg.ParseDevfileAndValidate(devfileLocation)
1819

1920
// To get all the components from the devfile
2021
components, err := devfile.Data.GetComponents(DevfileOptions{})
@@ -46,7 +47,7 @@ The function documentation can be accessed via [pkg.go.dev](https://pkg.go.dev/g
4647
})
4748
```
4849
2. To get the Kubernetes objects from the devfile, visit pkg/devfile/generator/generators.go
49-
```
50+
```go
5051
// To get a slice of Kubernetes containers of type corev1.Container from the devfile component containers
5152
containers, err := generator.GetContainers(devfile)
5253

devfile.yaml

+4-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ metadata:
44
version: 1.0.0
55
attributes:
66
alpha.build-dockerfile: /relative/path/to/Dockerfile
7+
variables:
8+
test: testValue
79
parent:
810
# uri: https://raw.githubusercontent.com/odo-devfiles/registry/master/devfiles/nodejs/devfile.yaml
911
id: nodejs
@@ -58,7 +60,7 @@ components:
5860
endpoints:
5961
- name: http-9090
6062
targetPort: 9090
61-
image: registry.access.redhat.com/ubi8/nodejs-12:1-45
63+
image: "{{invalid-var}}"
6264
memoryLimit: 1024Mi
6365
mountSources: true
6466
sourceMapping: /project
@@ -69,7 +71,7 @@ commands:
6971
group:
7072
isDefault: false
7173
kind: build
72-
workingDir: /project
74+
workingDir: "{{test}}"
7375
id: install2
7476
attributes:
7577
tool: odo

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/devfile/library
33
go 1.13
44

55
require (
6-
github.com/devfile/api/v2 v2.0.0-20210408144711-a313872749ed
6+
github.com/devfile/api/v2 v2.0.0-20210420202853-ff3c01bf8292
77
github.com/fatih/color v1.7.0
88
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32
99
github.com/gobwas/glob v0.2.3

go.sum

+2-49
Large diffs are not rendered by default.

main.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,13 @@ func parserTest() {
4040
}
4141
fmt.Println("parsing devfile from ./devfile.yaml")
4242
}
43-
devfile, err := devfilepkg.ParseDevfileAndValidate(args)
43+
devfile, warning, err := devfilepkg.ParseDevfileAndValidate(args)
4444
if err != nil {
4545
fmt.Println(err)
4646
} else {
47+
if len(warning.Commands) > 0 || len(warning.Components) > 0 || len(warning.Projects) > 0 || len(warning.StarterProjects) > 0 {
48+
fmt.Printf("top-level variables were not substituted successfully %+v\n", warning)
49+
}
4750
devdata := devfile.Data
4851
if (reflect.TypeOf(devdata) == reflect.TypeOf(&v2.DevfileV2{})) {
4952
d := devdata.(*v2.DevfileV2)
@@ -66,7 +69,7 @@ func parserTest() {
6669
}
6770
for _, command := range commands {
6871
if command.Exec != nil {
69-
fmt.Printf("command %s is with kind: %s", command.Id, command.Exec.Group.Kind)
72+
fmt.Printf("command %s is with kind: %s\n", command.Id, command.Exec.Group.Kind)
7073
fmt.Printf("workingDir is: %s\n", command.Exec.WorkingDir)
7174
}
7275
}

pkg/devfile/parse.go

+13-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package devfile
22

33
import (
4+
"github.com/devfile/api/v2/pkg/validation/variables"
45
"github.com/devfile/library/pkg/devfile/parser"
56
"github.com/devfile/library/pkg/devfile/validate"
67
)
@@ -69,21 +70,25 @@ func ParseAndValidate(path string) (d parser.DevfileObj, err error) {
6970
return d, err
7071
}
7172

72-
// ParseDevfileAndValidate func parses the devfile data
73-
// and validates the devfile integrity with the schema
74-
// and validates the devfile data.
75-
// Creates devfile context and runtime objects.
76-
func ParseDevfileAndValidate(args parser.ParserArgs) (d parser.DevfileObj, err error) {
73+
// ParseDevfileAndValidate func parses the devfile data, validates the devfile integrity with the schema
74+
// replaces the top-level variable keys if present and validates the devfile data.
75+
// It returns devfile context and runtime objects, variable substitution warning if any and an error.
76+
func ParseDevfileAndValidate(args parser.ParserArgs) (d parser.DevfileObj, varWarning variables.VariableWarning, err error) {
7777
d, err = parser.ParseDevfile(args)
7878
if err != nil {
79-
return d, err
79+
return d, varWarning, err
80+
}
81+
82+
if d.Data.GetSchemaVersion() != "2.0.0" {
83+
// replace the top level variable keys with their values in the devfile
84+
varWarning = variables.ValidateAndReplaceGlobalVariable(d.Data.GetDevfileWorkspaceSpec())
8085
}
8186

8287
// generic validation on devfile content
8388
err = validate.ValidateDevfileData(d.Data)
8489
if err != nil {
85-
return d, err
90+
return d, varWarning, err
8691
}
8792

88-
return d, err
93+
return d, varWarning, err
8994
}

pkg/devfile/parser/data/interface.go

+20
Original file line numberDiff line numberDiff line change
@@ -2,61 +2,81 @@ package data
22

33
import (
44
v1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
5+
"github.com/devfile/api/v2/pkg/attributes"
56
devfilepkg "github.com/devfile/api/v2/pkg/devfile"
67
"github.com/devfile/library/pkg/devfile/parser/data/v2/common"
78
)
89

910
// DevfileData is an interface that defines functions for Devfile data operations
1011
type DevfileData interface {
12+
13+
// header related methods
14+
1115
GetSchemaVersion() string
1216
SetSchemaVersion(version string)
1317
GetMetadata() devfilepkg.DevfileMetadata
1418
SetMetadata(metadata devfilepkg.DevfileMetadata)
1519

20+
// top-level attributes related method
21+
22+
GetAttributes() (attributes.Attributes, error)
23+
AddAttributes(key string, value interface{}) error
24+
UpdateAttributes(key string, value interface{}) error
25+
1626
// parent related methods
27+
1728
GetParent() *v1.Parent
1829
SetParent(parent *v1.Parent)
1930

2031
// event related methods
32+
2133
GetEvents() v1.Events
2234
AddEvents(events v1.Events) error
2335
UpdateEvents(postStart, postStop, preStart, preStop []string)
2436

2537
// component related methods
38+
2639
GetComponents(common.DevfileOptions) ([]v1.Component, error)
2740
AddComponents(components []v1.Component) error
2841
UpdateComponent(component v1.Component)
2942
DeleteComponent(name string) error
3043

3144
// project related methods
45+
3246
GetProjects(common.DevfileOptions) ([]v1.Project, error)
3347
AddProjects(projects []v1.Project) error
3448
UpdateProject(project v1.Project)
3549
DeleteProject(name string) error
3650

3751
// starter projects related commands
52+
3853
GetStarterProjects(common.DevfileOptions) ([]v1.StarterProject, error)
3954
AddStarterProjects(projects []v1.StarterProject) error
4055
UpdateStarterProject(project v1.StarterProject)
4156
DeleteStarterProject(name string) error
4257

4358
// command related methods
59+
4460
GetCommands(common.DevfileOptions) ([]v1.Command, error)
4561
AddCommands(commands []v1.Command) error
4662
UpdateCommand(command v1.Command)
4763
DeleteCommand(id string) error
4864

4965
// volume mount related methods
66+
5067
AddVolumeMounts(containerName string, volumeMounts []v1.VolumeMount) error
5168
DeleteVolumeMount(name string) error
5269
GetVolumeMountPaths(mountName, containerName string) ([]string, error)
5370

5471
// workspace related methods
72+
5573
GetDevfileWorkspaceSpecContent() *v1.DevWorkspaceTemplateSpecContent
5674
SetDevfileWorkspaceSpecContent(content v1.DevWorkspaceTemplateSpecContent)
75+
GetDevfileWorkspaceSpec() *v1.DevWorkspaceTemplateSpec
5776
SetDevfileWorkspaceSpec(spec v1.DevWorkspaceTemplateSpec)
5877

5978
// utils
79+
6080
GetDevfileContainerComponents(common.DevfileOptions) ([]v1.Component, error)
6181
GetDevfileVolumeComponents(common.DevfileOptions) ([]v1.Component, error)
6282
}

pkg/devfile/parser/data/v2/2.1.0/devfileJsonSchema210.go

+25-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ const JsonSchema210 = `{
99
"schemaVersion"
1010
],
1111
"properties": {
12+
"attributes": {
13+
"description": "Map of implementation-dependant free-form YAML attributes.",
14+
"type": "object",
15+
"additionalProperties": true
16+
},
1217
"commands": {
1318
"description": "Predefined, ready-to-use, devworkspace-related commands",
1419
"type": "array",
@@ -635,7 +640,7 @@ const JsonSchema210 = `{
635640
"type": "object",
636641
"properties": {
637642
"attributes": {
638-
"description": "Map of implementation-dependant free-form YAML attributes.",
643+
"description": "Map of implementation-dependant free-form YAML attributes. Deprecated, use the top-level attributes field instead.",
639644
"type": "object",
640645
"additionalProperties": true
641646
},
@@ -707,6 +712,11 @@ const JsonSchema210 = `{
707712
}
708713
],
709714
"properties": {
715+
"attributes": {
716+
"description": "Overrides of attributes encapsulated in a parent devfile. Overriding is done according to K8S strategic merge patch standard rules.",
717+
"type": "object",
718+
"additionalProperties": true
719+
},
710720
"commands": {
711721
"description": "Overrides of commands encapsulated in a parent devfile or a plugin. Overriding is done according to K8S strategic merge patch standard rules.",
712722
"type": "array",
@@ -1527,6 +1537,13 @@ const JsonSchema210 = `{
15271537
"uri": {
15281538
"description": "Uri of a Devfile yaml file",
15291539
"type": "string"
1540+
},
1541+
"variables": {
1542+
"description": "Overrides of variables encapsulated in a parent devfile. Overriding is done according to K8S strategic merge patch standard rules.",
1543+
"type": "object",
1544+
"additionalProperties": {
1545+
"type": "string"
1546+
}
15301547
}
15311548
},
15321549
"additionalProperties": false
@@ -1786,6 +1803,13 @@ const JsonSchema210 = `{
17861803
},
17871804
"additionalProperties": false
17881805
}
1806+
},
1807+
"variables": {
1808+
"description": "Map of key-value variables used for string replacement in the devfile. Values can can be referenced via {{variable-key}} to replace the corresponding value in string fields in the devfile. Replacement cannot be used for\n\n - schemaVersion, metadata, parent source - element identifiers, e.g. command id, component name, endpoint name, project name - references to identifiers, e.g. in events, a command's component, container's volume mount name - string enums, e.g. command group kind, endpoint exposure",
1809+
"type": "object",
1810+
"additionalProperties": {
1811+
"type": "string"
1812+
}
17891813
}
17901814
},
17911815
"additionalProperties": false
+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package v2
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/devfile/api/v2/pkg/attributes"
7+
)
8+
9+
// GetAttributes gets the devfile top level attributes
10+
func (d *DevfileV2) GetAttributes() (attributes.Attributes, error) {
11+
// This feature was introduced in 2.1.0; so any version 2.1.0 and up should use the 2.1.0 implementation
12+
switch d.SchemaVersion {
13+
case "2.0.0":
14+
return attributes.Attributes{}, fmt.Errorf("top-level attributes is not supported in devfile schema version 2.0.0")
15+
default:
16+
return d.Attributes, nil
17+
}
18+
}
19+
20+
// UpdateAttributes updates the devfile top level attribute for the specific key, err out if key is absent
21+
func (d *DevfileV2) UpdateAttributes(key string, value interface{}) error {
22+
var err error
23+
24+
// This feature was introduced in 2.1.0; so any version 2.1.0 and up should use the 2.1.0 implementation
25+
switch d.SchemaVersion {
26+
case "2.0.0":
27+
return fmt.Errorf("top-level attributes is not supported in devfile schema version 2.0.0")
28+
default:
29+
if d.Attributes.Exists(key) {
30+
d.Attributes.Put(key, value, &err)
31+
} else {
32+
return fmt.Errorf("cannot update top-level attribute, key %s is not present", key)
33+
}
34+
}
35+
36+
return err
37+
}
38+
39+
// AddAttributes adds to the devfile top level attributes, value will be overwritten if key is already present
40+
func (d *DevfileV2) AddAttributes(key string, value interface{}) error {
41+
var err error
42+
43+
// This feature was introduced in 2.1.0; so any version 2.1.0 and up should use the 2.1.0 implementation
44+
switch d.SchemaVersion {
45+
case "2.0.0":
46+
return fmt.Errorf("top-level attributes is not supported in devfile schema version 2.0.0")
47+
default:
48+
d.Attributes.Put(key, value, &err)
49+
}
50+
51+
return err
52+
}

0 commit comments

Comments
 (0)