Skip to content
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

Add Support for Devfile V2 #3216

Merged
merged 21 commits into from
Jun 2, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 24 additions & 9 deletions pkg/devfile/parser/context/apiVersion.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,34 @@ func (d *DevfileCtx) SetDevfileAPIVersion() error {
return errors.Wrapf(err, "failed to decode devfile json")
}

// Get "apiVersion" value from the map
apiVersion, ok := r["apiVersion"]
if !ok {
return fmt.Errorf("apiVersion not present in devfile")
}
var apiVer string

// Get "apiVersion" value from map for devfile V1
apiVersion, okApi := r["apiVersion"]

// Get "schemaVersion" value from map for devfile V2
schemaVersion, okSchema := r["schemaVersion"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens if both are provided, apiVersion and schemaVersion?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no field apiVersion for V2 and no field schemaVersion for V1, so they cannot be provided together, json validation would fail for such case.


if okApi {
apiVer = apiVersion.(string)
// apiVersion cannot be empty
if apiVer == "" {
return fmt.Errorf("apiVersion in devfile cannot be empty")
}

} else if okSchema {
apiVer = schemaVersion.(string)
// SchemaVersion cannot be empty
if schemaVersion.(string) == "" {
return fmt.Errorf("schemaVersion in devfile cannot be empty")
}
} else {
return fmt.Errorf("apiVersion or schemaVersion not present in devfile")

// apiVersion cannot be empty
if apiVersion.(string) == "" {
return fmt.Errorf("apiVersion in devfile cannot be empty")
}

// Successful
d.apiVersion = apiVersion.(string)
d.apiVersion = apiVer
klog.V(4).Infof("devfile apiVersion: '%s'", d.apiVersion)
return nil
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/devfile/parser/context/apiVersion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func TestSetDevfileAPIVersion(t *testing.T) {
name: "apiVersion not present",
rawJson: []byte(emptyJson),
want: "",
wantErr: fmt.Errorf("apiVersion not present in devfile"),
wantErr: fmt.Errorf("apiVersion or schemaVersion not present in devfile"),
},
{
name: "apiVersion empty",
Expand Down
176 changes: 166 additions & 10 deletions pkg/devfile/parser/data/1.0.0/components.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,37 @@
package version100

import (
"strings"

"github.com/openshift/odo/pkg/devfile/parser/data/common"
)

// GetComponents returns the slice of DevfileComponent objects parsed from the Devfile
func (d *Devfile100) GetMetadata() common.DevfileMetadata {
// No GenerateName field in V2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part is confusing. There is no GenerateName in V2. But there is in V1. So we would need to still return GenerateName here as this is the 1.0.0 parser (hence the 1.0.0 dir). So we'd most likely need to include it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To add generateName field here, i need to add it common types. currently we have updated our code not to use any v1 specific fields, generateName is currently used by odo catalog list components that has a parser of its own, so adding it here would of no use.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the odo catalog list components uses generateName to figure out the component type i.e. nodejs. how would that change for v2 then?

Copy link
Contributor Author

@adisky adisky Jun 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thats what i said, odo catalog list components has parser of its own, so no effect to add it here.
implementing odo catalog list components for v2 is a separate issue. #3249

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly I still think we should add GenerateName here... This is fairly confusing as this is the v1.0.0 parser folder but we are implementing v2 features.

return common.DevfileMetadata{
Name: *d.Metadata.Name,
//Version: No field in V1
}
}

/// GetComponents returns the slice of DevfileComponent objects parsed from the Devfile
func (d *Devfile100) GetComponents() []common.DevfileComponent {
return d.Components
var comps []common.DevfileComponent
for _, v := range d.Components {
comps = append(comps, convertV1ComponentToCommon(v))
}
return comps
}

// GetAliasedComponents returns the slice of DevfileComponent objects that each have an alias
func (d *Devfile100) GetAliasedComponents() []common.DevfileComponent {
// TODO(adi): we might not need this for V2 as name is a required field now.
var comps []common.DevfileComponent
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, see other comment. These sections are all about Version 1. So why are we making changes / breaking backwards compatibility here?

Copy link
Contributor

@maysunfaisal maysunfaisal Jun 1, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cdrage Aditi can explain in more detail, but I think she had to make changes here to use the new common structs for both v1 and v2; so that v1 doesn't break

Like calling func convertV1ComponentToCommon()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cdrage all v1 integration tests are passing, so it is the first sign we are not breaking anything.
secondly it is just a refactoring comment to remove this method from interface when we remove V1, as all components are aliased(they have name) for v2. agreed though comment is not clear.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then we should rename the folder / all mentions of v1.0.0 to something else in this folder as to be honest, we are implementing v2 features where v1 is..

for _, v := range d.Components {
comps = append(comps, convertV1ComponentToCommon(v))
}

var aliasedComponents = []common.DevfileComponent{}
for _, comp := range d.Components {
if comp.Alias != nil {
for _, comp := range comps {
if comp.Container.Name != "" {
aliasedComponents = append(aliasedComponents, comp)
}
}
Expand All @@ -24,17 +40,157 @@ func (d *Devfile100) GetAliasedComponents() []common.DevfileComponent {

// GetProjects returns the slice of DevfileProject objects parsed from the Devfile
func (d *Devfile100) GetProjects() []common.DevfileProject {
return d.Projects

var projects []common.DevfileProject
for _, v := range d.Projects {
// We are only supporting ProjectType git in V1
if v.Source.Type == ProjectTypeGit {
projects = append(projects, convertV1ProjectToCommon(v))
}
}

return projects
}

// GetCommands returns the slice of DevfileCommand objects parsed from the Devfile
func (d *Devfile100) GetCommands() []common.DevfileCommand {

var commands []common.DevfileCommand
for _, v := range d.Commands {
cmd := convertV1CommandToCommon(v)

for _, command := range d.Commands {
command.Name = strings.ToLower(command.Name)
commands = append(commands, command)
commands = append(commands, cmd)
}

return commands
}

func (d *Devfile100) GetParent() common.DevfileParent {
return common.DevfileParent{}

}

func (d *Devfile100) GetEvents() common.DevfileEvents {
return common.DevfileEvents{}

}

func convertV1CommandToCommon(c Command) (d common.DevfileCommand) {
var exec common.Exec

for _, action := range c.Actions {

if *action.Type == DevfileCommandTypeExec {
exec = common.Exec{
Attributes: c.Attributes,
CommandLine: *action.Command,
Component: *action.Component,
Group: getGroup(c.Name),
Id: c.Name,
WorkingDir: action.Workdir,
// Env:
// Label:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't we remove these?

}
}

}

// TODO: Previewurl
return common.DevfileCommand{
//TODO(adi): Type
Exec: &exec,
}
}

func convertV1ComponentToCommon(c Component) (d common.DevfileComponent) {
girishramnani marked this conversation as resolved.
Show resolved Hide resolved

var endpoints []*common.Endpoint
for _, v := range c.ComponentDockerimage.Endpoints {
endpoints = append(endpoints, convertV1EndpointsToCommon(v))
}

var envs []*common.Env
for _, v := range c.ComponentDockerimage.Env {
envs = append(envs, convertV1EnvToCommon(v))
}

var volumes []*common.VolumeMount
for _, v := range c.ComponentDockerimage.Volumes {
volumes = append(volumes, convertV1VolumeToCommon(v))
}

container := common.Container{
Name: *c.Alias,
Endpoints: endpoints,
Env: envs,
Image: *c.ComponentDockerimage.Image,
girishramnani marked this conversation as resolved.
Show resolved Hide resolved
MemoryLimit: *c.ComponentDockerimage.MemoryLimit,
MountSources: c.MountSources,
VolumeMounts: volumes,
// SourceMapping: Not present in V1
}

d = common.DevfileComponent{Container: &container}

return d
}

func convertV1EndpointsToCommon(e DockerimageEndpoint) *common.Endpoint {
return &common.Endpoint{
// Attributes:
// Configuration:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we remove these commented attributes?

Name: *e.Name,
TargetPort: *e.Port,
}
}

func convertV1EnvToCommon(e DockerimageEnv) *common.Env {
return &common.Env{
Name: *e.Name,
Value: *e.Value,
}
}

func convertV1VolumeToCommon(v DockerimageVolume) *common.VolumeMount {
return &common.VolumeMount{
Name: *v.Name,
Path: *v.ContainerPath,
}
}

func convertV1ProjectToCommon(p Project) common.DevfileProject {

git := common.Git{
Branch: *p.Source.Branch,
Location: p.Source.Location,
SparseCheckoutDir: *p.Source.SparseCheckoutDir,
StartPoint: *p.Source.StartPoint,
}

return common.DevfileProject{
ClonePath: p.ClonePath,
Name: p.Name,
Git: &git,
}

}

func getGroup(name string) *common.Group {
var kind common.DevfileCommandGroupType

switch name {
case "devRun":
kind = common.RunCommandGroupType
case "devBuild":
kind = common.BuildCommandGroupType
case "devInit":
kind = common.InitCommandGroupType
default:
kind = ""
}

return &common.Group{
// TODO(adi): IsDefault:
Kind: kind,
}
}
Loading