Skip to content

Commit

Permalink
Add 'devfile.GetImageComponentsToPush' functions that returns the lis…
Browse files Browse the repository at this point in the history
…t of image components to auto-create

See devfile/api#852 (comment) for more context.
  • Loading branch information
rm3l committed Mar 21, 2023
1 parent c44d734 commit 9794fa8
Show file tree
Hide file tree
Showing 2 changed files with 233 additions and 5 deletions.
47 changes: 47 additions & 0 deletions pkg/devfile/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,53 @@ import (
parsercommon "github.com/devfile/library/v2/pkg/devfile/parser/data/v2/common"
)

// GetImageComponentsToPush returns the list of Image components that can be automatically created on startup.
// The list returned is governed by the AutoBuild field in each component.
// All components with AutoBuild set to true are included, along with those with no AutoBuild set and not-referenced.
func GetImageComponentsToPush(devfileObj parser.DevfileObj) ([]devfilev1.Component, error) {
imageComponents, err := devfileObj.Data.GetComponents(parsercommon.DevfileOptions{
ComponentOptions: parsercommon.ComponentOptions{ComponentType: devfilev1.ImageComponentType},
})
if err != nil {
return nil, err
}

allApplyCommands, err := devfileObj.Data.GetCommands(parsercommon.DevfileOptions{
CommandOptions: parsercommon.CommandOptions{CommandType: devfilev1.ApplyCommandType},
})
if err != nil {
return nil, err
}

m := make(map[string]devfilev1.Component)
for _, comp := range imageComponents {
if comp.Image == nil {
continue
}
var add bool
if comp.Image.AutoBuild == nil {
// auto-created only if not referenced by any apply command
if !isComponentReferenced(allApplyCommands, comp.Name) {
add = true
}
} else if *comp.Image.AutoBuild {
add = true
}
if !add {
continue
}
if _, present := m[comp.Name]; !present {
m[comp.Name] = comp
}
}

var result []devfilev1.Component
for _, comp := range m {
result = append(result, comp)
}
return result, nil
}

// GetK8sAndOcComponentsToPush returns the list of Kubernetes and OpenShift components to push,
// The list returned is governed by the DeployByDefault field in each component.
// All components with DeployByDefault set to true are included, along with those with no DeployByDefault set and not-referenced.
Expand Down
191 changes: 186 additions & 5 deletions pkg/devfile/components_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,6 @@ func TestGetK8sAndOcComponentsToPush(t *testing.T) {
},
}

lessFunc := func(x, y devfilev1.Component) bool {
return x.Name < y.Name
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
devfileObj, err := tt.args.devfileObj()
Expand All @@ -248,9 +244,194 @@ func TestGetK8sAndOcComponentsToPush(t *testing.T) {
t.Errorf("Got %d components, expected %d\n", len(got), len(tt.want))
}

if diff := cmp.Diff(tt.want, got, cmpopts.EquateEmpty(), cmpopts.SortSlices(lessFunc)); diff != "" {
if diff := cmp.Diff(tt.want, got, cmpopts.EquateEmpty(), cmpopts.SortSlices(less)); diff != "" {
t.Errorf("GetK8sAndOcComponentsToPush() mismatch (-want +got):\n%s", diff)
}
})
}
}

func TestGetImageComponentsToPush(t *testing.T) {
fs := devfileFileSystem.NewFakeFs()

buildImageComponent := func(name string, autoBuild *bool, referenced bool) (devfilev1.Component, devfilev1.Command) {
comp := devfilev1.Component{
Name: name,
ComponentUnion: devfilev1.ComponentUnion{
Image: &devfilev1.ImageComponent{
Image: devfilev1.Image{
ImageName: "my-image:" + name,
ImageUnion: devfilev1.ImageUnion{
AutoBuild: autoBuild,
},
},
},
},
}
if referenced {
cmd := devfilev1.Command{
Id: "apply-" + name,
CommandUnion: devfilev1.CommandUnion{
Apply: &devfilev1.ApplyCommand{
Component: name,
},
},
}
return comp, cmd
}
return comp, devfilev1.Command{}
}

var (
autoBuildTrueReferenced, applyAutoBuildTrueReferenced = buildImageComponent(
"autoBuildTrueReferenced", pointer.Bool(true), true)
autoBuildTrueNotReferenced, _ = buildImageComponent(
"autoBuildTrueNotReferenced", pointer.Bool(true), false)
autoBuildFalseReferenced, applyAutoBuildFalseReferenced = buildImageComponent(
"autoBuildFalseReferenced", pointer.Bool(false), true)
autoBuildFalseNotReferenced, _ = buildImageComponent(
"autoBuildFalseNotReferenced", pointer.Bool(false), false)
autoBuildNotSetReferenced, applyAutoBuildNotSetReferenced = buildImageComponent(
"autoBuildNotSetReferenced", nil, true)
autoBuildNotSetNotReferenced, _ = buildImageComponent(
"autoBuildNotSetNotReferenced", nil, false)
)

buildFullDevfile := func() (parser.DevfileObj, error) {
devfileData, err := data.NewDevfileData(string(data.APISchemaVersion220))
if err != nil {
return parser.DevfileObj{}, err
}
devfileData.SetMetadata(devfilepkg.DevfileMetadata{Name: "my-devfile"})
err = devfileData.AddComponents([]devfilev1.Component{
autoBuildTrueReferenced,
autoBuildTrueNotReferenced,
autoBuildFalseReferenced,
autoBuildFalseNotReferenced,
autoBuildNotSetReferenced,
autoBuildNotSetNotReferenced,

//Add other kinds of components
{
Name: "my-k8s-component",
ComponentUnion: devfilev1.ComponentUnion{
Kubernetes: &devfilev1.KubernetesComponent{
K8sLikeComponent: devfilev1.K8sLikeComponent{
K8sLikeComponentLocation: devfilev1.K8sLikeComponentLocation{
Inlined: "my-k8s-component-inlined",
},
},
},
},
},
{
Name: "container-component",
ComponentUnion: devfilev1.ComponentUnion{
Container: &devfilev1.ContainerComponent{
Container: devfilev1.Container{
DedicatedPod: pointer.Bool(true),
Image: "my-container-image",
},
},
},
},
})
if err != nil {
return parser.DevfileObj{}, err
}
err = devfileData.AddCommands([]devfilev1.Command{
applyAutoBuildTrueReferenced,
applyAutoBuildFalseReferenced,
applyAutoBuildNotSetReferenced,

//Add other kinds of components
{
Id: "apply-k8s-component",
CommandUnion: devfilev1.CommandUnion{
Apply: &devfilev1.ApplyCommand{
Component: "my-k8s-component",
},
},
},
{
Id: "exec-command",
CommandUnion: devfilev1.CommandUnion{
Apply: &devfilev1.ApplyCommand{
Component: "my-image-component",
},
Exec: &devfilev1.ExecCommand{
CommandLine: "/path/to/my/command -success",
Component: "container-component",
},
},
},
})
if err != nil {
return parser.DevfileObj{}, err
}
return parser.DevfileObj{
Ctx: devfileCtx.FakeContext(fs, parser.OutputDevfileYamlPath),
Data: devfileData,
}, nil
}

type args struct {
devfileObj func() (parser.DevfileObj, error)
}
tests := []struct {
name string
args args
want []devfilev1.Component
wantErr bool
}{
{
name: "empty devfile",
args: args{
devfileObj: func() (parser.DevfileObj, error) {
return parser.DevfileObj{
Data: devfiletesting.GetDevfileData(t, nil, nil),
Ctx: devfileCtx.FakeContext(fs, parser.OutputDevfileYamlPath),
}, nil
},
},
want: []devfilev1.Component{},
wantErr: false,
},
{
name: "return components that need to be created automatically on startup",
args: args{
devfileObj: buildFullDevfile,
},
want: []devfilev1.Component{
autoBuildTrueReferenced,
autoBuildTrueNotReferenced,
autoBuildNotSetNotReferenced,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
devfileObj, err := tt.args.devfileObj()
if err != nil {
t.Errorf("unable to create Devfile object: %v", err)
return
}

got, err := GetImageComponentsToPush(devfileObj)
if (err != nil) != tt.wantErr {
t.Errorf("GetImageComponentsToPush() error = %v, wantErr %v", err, tt.wantErr)
return
}
if len(got) != len(tt.want) {
t.Errorf("Got %d components, expected %d\n", len(got), len(tt.want))
}
if diff := cmp.Diff(tt.want, got, cmpopts.EquateEmpty(), cmpopts.SortSlices(less)); diff != "" {
t.Errorf("GetImageComponentsToPush() mismatch (-want +got):\n%s", diff)
}
})
}
}

func less(x, y devfilev1.Component) bool {
return x.Name < y.Name
}

0 comments on commit 9794fa8

Please sign in to comment.