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

Implement a basic, first pass odo push with Docker support #2806

Merged
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 9 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -137,5 +137,13 @@ jobs:
- travis_wait make test-e2e-source
- travis_wait make test-e2e-images
- odo logout


- <<: *base-test
stage: test
name: "docker devfile push command integration tests"
script:
- ./scripts/oc-cluster.sh
- make bin
- sudo cp odo /usr/bin
- travis_wait make test-cmd-docker-devfile-push

5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,11 @@ test-cmd-url:
test-cmd-devfile-url:
ginkgo $(GINKGO_FLAGS) -focus="odo devfile url command tests" tests/integration/devfile/

# Run odo push docker devfile command tests
.PHONY: test-cmd-docker-devfile-push
test-cmd-docker-devfile-push:
ginkgo $(GINKGO_FLAGS) -focus="odo docker devfile push command tests" tests/integration/docker/

# Run odo watch command tests
.PHONY: test-cmd-watch
test-cmd-watch:
Expand Down
44 changes: 44 additions & 0 deletions pkg/devfile/adapters/docker/adapter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package docker

import (
"github.com/openshift/odo/pkg/devfile/adapters/common"
"github.com/openshift/odo/pkg/devfile/adapters/docker/component"
"github.com/openshift/odo/pkg/lclient"
"github.com/pkg/errors"
)

// Adapter maps Devfiles to Docker resources and actions
type Adapter struct {
componentAdapter common.ComponentAdapter
}

// New instantiates a Docker adapter
func New(adapterContext common.AdapterContext, client lclient.Client) Adapter {

compAdapter := component.New(adapterContext, client)

return Adapter{
componentAdapter: compAdapter,
}
}

// Push creates Docker resources that correspond to the devfile if they don't already exist
func (d Adapter) Push(parameters common.PushParameters) error {

err := d.componentAdapter.Push(parameters)
if err != nil {
return errors.Wrap(err, "Failed to create the component")
}

return nil
}

// DoesComponentExist returns true if a component with the specified name exists
func (d Adapter) DoesComponentExist(cmpName string) bool {
return d.componentAdapter.DoesComponentExist(cmpName)
}

// Delete attempts to delete the component with the specified labels, returning an error if it fails
func (d Adapter) Delete(labels map[string]string) error {
return d.componentAdapter.Delete(labels)
}
51 changes: 51 additions & 0 deletions pkg/devfile/adapters/docker/component/adapter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package component

import (
"github.com/pkg/errors"

"github.com/openshift/odo/pkg/devfile/adapters/common"
"github.com/openshift/odo/pkg/devfile/adapters/docker/utils"
"github.com/openshift/odo/pkg/lclient"
)

// New instantiantes a component adapter
func New(adapterContext common.AdapterContext, client lclient.Client) Adapter {
return Adapter{
Client: client,
AdapterContext: adapterContext,
}
}

// Adapter is a component adapter implementation for Kubernetes
type Adapter struct {
Client lclient.Client
common.AdapterContext
}

// Push updates the component if a matching component exists or creates one if it doesn't exist
func (a Adapter) Push(parameters common.PushParameters) (err error) {
componentExists := utils.ComponentExists(a.Client, a.ComponentName)

if componentExists {
err = a.updateComponent()
} else {
err = a.createComponent()
}

if err != nil {
return errors.Wrap(err, "unable to create or update component")
}

return nil
}

// DoesComponentExist returns true if a component with the specified name exists, false otherwise
func (a Adapter) DoesComponentExist(cmpName string) bool {
return utils.ComponentExists(a.Client, cmpName)
}

// Delete attempts to delete the component with the specified labels, returning an error if it fails
// Stub function until the functionality is added as part of https://github.com/openshift/odo/issues/2581
func (d Adapter) Delete(labels map[string]string) error {
return nil
}
131 changes: 131 additions & 0 deletions pkg/devfile/adapters/docker/component/adapter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package component

import (
"testing"

adaptersCommon "github.com/openshift/odo/pkg/devfile/adapters/common"
devfileParser "github.com/openshift/odo/pkg/devfile/parser"
versionsCommon "github.com/openshift/odo/pkg/devfile/parser/data/common"
"github.com/openshift/odo/pkg/lclient"
"github.com/openshift/odo/pkg/testingutil"
)

func TestPush(t *testing.T) {

testComponentName := "test"
fakeClient := lclient.FakeNew()
fakeErrorClient := lclient.FakeErrorNew()

tests := []struct {
name string
componentType versionsCommon.DevfileComponentType
client *lclient.Client
wantErr bool
}{
{
name: "Case 1: Invalid devfile",
componentType: "",
client: fakeClient,
wantErr: true,
},
{
name: "Case 2: Valid devfile",
componentType: versionsCommon.DevfileComponentTypeDockerimage,
client: fakeClient,
wantErr: false,
},
{
name: "Case 3: Valid devfile, docker client error",
componentType: versionsCommon.DevfileComponentTypeDockerimage,
client: fakeErrorClient,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
devObj := devfileParser.DevfileObj{
Data: testingutil.TestDevfileData{
ComponentType: tt.componentType,
},
}

adapterCtx := adaptersCommon.AdapterContext{
ComponentName: testComponentName,
Devfile: devObj,
}

componentAdapter := New(adapterCtx, *tt.client)
// ToDo: Add more meaningful unit tests once Push actually does something with its parameters
err := componentAdapter.Push(adaptersCommon.PushParameters{})

// Checks for unexpected error cases
if !tt.wantErr == (err != nil) {
t.Errorf("component adapter create unexpected error %v, wantErr %v", err, tt.wantErr)
}
})
}

}

func TestDoesComponentExist(t *testing.T) {
fakeClient := lclient.FakeNew()
fakeErrorClient := lclient.FakeErrorNew()

tests := []struct {
name string
client *lclient.Client
componentType versionsCommon.DevfileComponentType
componentName string
getComponentName string
want bool
}{
{
name: "Case 1: Valid component name",
client: fakeClient,
componentType: versionsCommon.DevfileComponentTypeDockerimage,
componentName: "golang",
getComponentName: "golang",
want: true,
},
{
name: "Case 2: Non-existent component name",
componentType: versionsCommon.DevfileComponentTypeDockerimage,
client: fakeClient,
componentName: "test-name",
getComponentName: "fake-component",
want: false,
},
{
name: "Case 3: Docker client error",
componentType: versionsCommon.DevfileComponentTypeDockerimage,
client: fakeErrorClient,
componentName: "test-name",
getComponentName: "fake-component",
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
devObj := devfileParser.DevfileObj{
Data: testingutil.TestDevfileData{
ComponentType: tt.componentType,
},
}

adapterCtx := adaptersCommon.AdapterContext{
ComponentName: tt.componentName,
Devfile: devObj,
}

componentAdapter := New(adapterCtx, *tt.client)

// Verify that a comopnent with the specified name exists
componentExists := componentAdapter.DoesComponentExist(tt.getComponentName)
if componentExists != tt.want {
t.Errorf("expected %v, actual %v", tt.want, componentExists)
}

})
}

}