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

Update Devfile Library Get Options #71

Merged
merged 5 commits into from
Mar 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
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
69 changes: 46 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Devfile Parser Library
# Devfile Library

## About

Expand All @@ -8,25 +8,43 @@ The Devfile Parser library is a Golang module that:
3. generates Kubernetes objects for the various devfile resources.
4. defines util functions for the devfile.

## Usage

The function documentation can be accessed via [pkg.go.dev](https://pkg.go.dev/github.com/devfile/library).
1. To parse a devfile, visit pkg/devfile/parse.go
```
// Parses the devfile and validates the devfile data
devfile, err := devfilePkg.ParseAndValidate(devfileLocation)

// To get all the components from the devfile
components, err := devfile.Data.GetComponents(DevfileOptions{})

// To get all the components from the devfile with attributes tagged - tool: console-import & import: {strategy: Dockerfile}
components, err := devfile.Data.GetComponents(DevfileOptions{
Filter: map[string]interface{}{
"tool": "console-import",
"import": map[string]interface{}{
"strategy": "Dockerfile",
},
```
// Parses the devfile and validates the devfile data
devfile, err := devfilePkg.ParseAndValidate(devfileLocation)

// To get all the components from the devfile
components, err := devfile.Data.GetComponents(DevfileOptions{})

// To get all the components from the devfile with attributes tagged - tool: console-import
// & import: {strategy: Dockerfile}
components, err := devfile.Data.GetComponents(DevfileOptions{
Filter: map[string]interface{}{
"tool": "console-import",
"import": map[string]interface{}{
"strategy": "Dockerfile",
},
})
```
},
})

// To get all the volume components
components, err := devfile.Data.GetComponents(DevfileOptions{
ComponentOptions: ComponentOptions{
ComponentType: v1.VolumeComponentType,
},
})

// To get all the exec commands that belong to the build group
commands, err := devfile.Data.GetCommands(DevfileOptions{
CommandOptions: CommandOptions{
CommandType: v1.ExecCommandType,
CommandGroupKind: v1.BuildCommandGroupKind,
},
})
```
2. To get the Kubernetes objects from the devfile, visit pkg/devfile/generator/generators.go
```
// To get a slice of Kubernetes containers of type corev1.Container from the devfile component containers
Expand All @@ -43,8 +61,9 @@ The function documentation can be accessed via [pkg.go.dev](https://pkg.go.dev/g
}
deployment := generator.GetDeployment(deployParams)
```

<br></br>

## Updating Library Schema

Run `updateApi.sh` can update to use latest `github.com/devfile/api` and update the schema saved under `pkg/devfile/parser/data`

The script also accepts version number as an argument to update devfile schema for a specific devfile version.
Expand All @@ -54,15 +73,19 @@ For example, run the following command will update devfile schema for 2.0.0
```
Running the script with no arguments will default to update the latest devfile version

## Projects using devfile/library

## Usage

In the future, the following projects will be consuming this library as a Golang dependency
The following projects are consuming this library as a Golang dependency

* [Workspace Operator](https://github.com/devfile/devworkspace-operator)
* [odo](https://github.com/openshift/odo)
* [OpenShift Console](https://github.com/openshift/console)

In the future, [Workspace Operator](https://github.com/devfile/devworkspace-operator) will be the next consumer of devfile/library.

## Issues

Issues are tracked in the [devfile/api](https://github.com/devfile/api) repo with the label [area/library](https://github.com/devfile/api/issues?q=is%3Aopen+is%3Aissue+label%3Aarea%2Flibrary)

## Releases

For devfile/library releases, please check the release [page](https://github.com/devfile/library/releases).
2 changes: 1 addition & 1 deletion pkg/devfile/parser/data/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ type DevfileData interface {
GetDevfileWorkspace() *v1.DevWorkspaceTemplateSpecContent
SetDevfileWorkspace(content v1.DevWorkspaceTemplateSpecContent)

//utils
// utils
GetDevfileContainerComponents(common.DevfileOptions) ([]v1.Component, error)
GetDevfileVolumeComponents(common.DevfileOptions) ([]v1.Component, error)
}
28 changes: 24 additions & 4 deletions pkg/devfile/parser/data/v2/commands.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package v2

import (
"reflect"
"strings"

v1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
Expand All @@ -9,21 +10,40 @@ import (

// GetCommands returns the slice of Command objects parsed from the Devfile
func (d *DevfileV2) GetCommands(options common.DevfileOptions) ([]v1.Command, error) {
if len(options.Filter) == 0 {

if reflect.DeepEqual(options, common.DevfileOptions{}) {
return d.Commands, nil
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

why remove this block?
can be reflect.DeepEqual(options, common.DevfileOptions{})
same for the other Get functions


var commands []v1.Command
for _, command := range d.Commands {
// Filter Command Attributes
filterIn, err := common.FilterDevfileObject(command.Attributes, options)
if err != nil {
return nil, err
} else if !filterIn {
continue
}

if filterIn {
command.Id = strings.ToLower(command.Id)
commands = append(commands, command)
// Filter Command Type - Exec, Composite, etc.
commandType, err := common.GetCommandType(command)
if err != nil {
return nil, err
}
if options.CommandOptions.CommandType != "" && commandType != options.CommandOptions.CommandType {
continue
}

// Filter Command Group Kind - Run, Build, etc.
commandGroup := common.GetGroup(command)
// exclude conditions:
// 1. options group is present and command group is present but does not match
// 2. options group is present and command group is not present
if options.CommandOptions.CommandGroupKind != "" && ((commandGroup != nil && options.CommandOptions.CommandGroupKind != commandGroup.Kind) || commandGroup == nil) {
continue
}

commands = append(commands, command)
}

return commands, nil
Expand Down
142 changes: 110 additions & 32 deletions pkg/devfile/parser/data/v2/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ import (

func TestDevfile200_GetCommands(t *testing.T) {

type args struct {
name string
}
tests := []struct {
name string
currentCommands []v1.Command
Expand All @@ -23,7 +20,7 @@ func TestDevfile200_GetCommands(t *testing.T) {
wantErr bool
}{
{
name: "case 1: get the necessary commands",
name: "Get all the commands",
currentCommands: []v1.Command{
{
Id: "command1",
Expand All @@ -38,12 +35,11 @@ func TestDevfile200_GetCommands(t *testing.T) {
},
},
},
filterOptions: common.DevfileOptions{},
wantCommands: []string{"command1", "command2"},
wantErr: false,
wantCommands: []string{"command1", "command2"},
wantErr: false,
},
{
name: "case 2: get the filtered commands",
name: "Get the filtered commands",
currentCommands: []v1.Command{
{
Id: "command1",
Expand All @@ -52,7 +48,15 @@ func TestDevfile200_GetCommands(t *testing.T) {
"secondString": "secondStringValue",
}),
CommandUnion: v1.CommandUnion{
Exec: &v1.ExecCommand{},
Exec: &v1.ExecCommand{
LabeledCommand: v1.LabeledCommand{
BaseCommand: v1.BaseCommand{
Group: &v1.CommandGroup{
Kind: v1.BuildCommandGroupKind,
},
},
},
},
},
},
{
Expand All @@ -65,18 +69,74 @@ func TestDevfile200_GetCommands(t *testing.T) {
Composite: &v1.CompositeCommand{},
},
},
{
Id: "command3",
Attributes: attributes.Attributes{}.FromStringMap(map[string]string{
"firstString": "firstStringValue",
"thirdString": "thirdStringValue",
}),
CommandUnion: v1.CommandUnion{
Composite: &v1.CompositeCommand{
LabeledCommand: v1.LabeledCommand{
BaseCommand: v1.BaseCommand{
Group: &v1.CommandGroup{
Kind: v1.BuildCommandGroupKind,
},
},
},
},
},
},
{
Id: "command4",
Attributes: attributes.Attributes{}.FromStringMap(map[string]string{
"thirdString": "thirdStringValue",
}),
CommandUnion: v1.CommandUnion{
Apply: &v1.ApplyCommand{
LabeledCommand: v1.LabeledCommand{
BaseCommand: v1.BaseCommand{
Group: &v1.CommandGroup{
Kind: v1.BuildCommandGroupKind,
},
},
},
},
},
},
{
Id: "command5",
Attributes: attributes.Attributes{}.FromStringMap(map[string]string{
"firstString": "firstStringValue",
"thirdString": "thirdStringValue",
}),
CommandUnion: v1.CommandUnion{
Composite: &v1.CompositeCommand{
LabeledCommand: v1.LabeledCommand{
BaseCommand: v1.BaseCommand{
Group: &v1.CommandGroup{
Kind: v1.RunCommandGroupKind,
},
},
},
},
},
},
},
filterOptions: common.DevfileOptions{
Filter: map[string]interface{}{
"firstString": "firstStringValue",
"secondString": "secondStringValue",
"firstString": "firstStringValue",
},
CommandOptions: common.CommandOptions{
CommandGroupKind: v1.BuildCommandGroupKind,
CommandType: v1.CompositeCommandType,
},
},
wantCommands: []string{"command1"},
wantCommands: []string{"command3"},
wantErr: false,
},
{
name: "case 3: get the wrong filtered commands",
name: "Wrong filter for commands",
currentCommands: []v1.Command{
{
Id: "command1",
Expand Down Expand Up @@ -104,8 +164,25 @@ func TestDevfile200_GetCommands(t *testing.T) {
"firstStringIsWrong": "firstStringValue",
},
},
wantCommands: []string{},
wantErr: false,
wantErr: false,
},
{
name: "Invalid command type",
currentCommands: []v1.Command{
{
Id: "command1",
Attributes: attributes.Attributes{}.FromStringMap(map[string]string{
"firstString": "firstStringValue",
}),
CommandUnion: v1.CommandUnion{},
},
},
filterOptions: common.DevfileOptions{
Filter: map[string]interface{}{
"firstString": "firstStringValue",
},
},
wantErr: true,
},
}
for _, tt := range tests {
Expand All @@ -121,26 +198,27 @@ func TestDevfile200_GetCommands(t *testing.T) {
}

commands, err := d.GetCommands(tt.filterOptions)
if !tt.wantErr && err != nil {
t.Errorf("TestDevfile200_GetCommands() unexpected error - %v", err)
return
} else if tt.wantErr && err == nil {
t.Errorf("TestDevfile200_GetCommands() expected an error but got nil %v", commands)
return
} else if tt.wantErr && err != nil {
return
}
if (err != nil) != tt.wantErr {
t.Errorf("TestDevfile200_GetCommands() error = %v, wantErr %v", err, tt.wantErr)
} else if err == nil {
// confirm the length of actual vs expected
if len(commands) != len(tt.wantCommands) {
t.Errorf("TestDevfile200_GetCommands() error - length of expected commands is not the same as the length of actual commands")
return
}

for _, wantCommand := range tt.wantCommands {
matched := false
for _, devfileCommand := range commands {
if wantCommand == devfileCommand.Id {
matched = true
// compare the command slices for content
for _, wantCommand := range tt.wantCommands {
matched := false
for _, command := range commands {
if wantCommand == command.Id {
matched = true
}
}
}

if !matched {
t.Errorf("TestDevfile200_GetCommands() error - command %s not found in the devfile", wantCommand)
if !matched {
t.Errorf("TestDevfile200_GetCommands() error - command %s not found in the devfile", wantCommand)
}
}
}
})
Expand Down
19 changes: 19 additions & 0 deletions pkg/devfile/parser/data/v2/common/command_helper.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package common

import (
"fmt"

v1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
)

Expand Down Expand Up @@ -47,3 +49,20 @@ func GetExecWorkingDir(dc v1.Command) string {

return ""
}

// GetCommandType returns the command type of a given command
func GetCommandType(command v1.Command) (v1.CommandType, error) {
switch {
case command.Apply != nil:
return v1.ApplyCommandType, nil
case command.Composite != nil:
return v1.CompositeCommandType, nil
case command.Exec != nil:
return v1.ExecCommandType, nil
case command.Custom != nil:
return v1.CustomCommandType, nil

default:
return "", fmt.Errorf("unknown command type")
}
}
Loading