mirror of
https://github.com/redhat-developer/odo.git
synced 2025-10-19 03:06:19 +03:00
Remove commands that wont be implemented for v3-alpha1 (#5433)
* Remove odo staorage commands * Remove "odo service" + "odo catalog * service" commands * Remove odo link/unlink commands * Remove related integration tests * Remove application concept * fix rebase * fix test * Remove config command * Remove env command * Remove application package * Remove config package * Move odogenerator and unions packages into kclient * Move notify package to cli/version * Fix script mockgen * Remove odo debug command oand debug package * Remove odo component describe/exec/log/status/test * Remove operator-hub tests from IBM tests * Remove operator hub tests from CI * Fix e2e tests
This commit is contained in:
@@ -13,7 +13,6 @@ cleanup_namespaces
|
||||
set -e
|
||||
make install
|
||||
make test-integration-devfile
|
||||
make test-operator-hub
|
||||
make test-e2e-devfile
|
||||
make test-cmd-project
|
||||
) |& tee "/tmp/${LOGFILE}"
|
||||
|
||||
@@ -13,7 +13,6 @@ cleanup_namespaces
|
||||
make install
|
||||
make test-integration
|
||||
make test-integration-devfile
|
||||
make test-operator-hub
|
||||
make test-cmd-login-logout
|
||||
make test-cmd-project
|
||||
make test-e2e-devfile
|
||||
|
||||
@@ -66,8 +66,6 @@ function Run-Test {
|
||||
Check-ExitCode $LASTEXITCODE
|
||||
make test-integration | tee -a C:\Users\Administrator.ANSIBLE-TEST-VS\AppData\Local\Temp\$LOGFILE
|
||||
Check-ExitCode $LASTEXITCODE
|
||||
make test-operator-hub | tee -a C:\Users\Administrator.ANSIBLE-TEST-VS\AppData\Local\Temp\$LOGFILE
|
||||
Check-ExitCode $LASTEXITCODE
|
||||
make test-cmd-login-logout | tee -a C:\Users\Administrator.ANSIBLE-TEST-VS\AppData\Local\Temp\$LOGFILE
|
||||
Check-ExitCode $LASTEXITCODE
|
||||
make test-cmd-project | tee -a C:\Users\Administrator.ANSIBLE-TEST-VS\AppData\Local\Temp\$LOGFILE
|
||||
|
||||
4
Makefile
4
Makefile
@@ -297,10 +297,6 @@ vendor-update: ## Update vendoring
|
||||
openshiftci-presubmit-unittests:
|
||||
./scripts/openshiftci-presubmit-unittests.sh
|
||||
|
||||
.PHONY: test-operator-hub
|
||||
test-operator-hub: ## Run OperatorHub tests
|
||||
$(RUN_GINKGO) $(GINKGO_FLAGS) tests/integration/operatorhub/
|
||||
|
||||
.PHONY: test-cmd-devfile-describe
|
||||
test-cmd-devfile-describe:
|
||||
$(RUN_GINKGO) $(GINKGO_FLAGS) -focus="odo devfile describe command tests" tests/integration/devfile/
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package application
|
||||
|
||||
import "github.com/redhat-developer/odo/pkg/component"
|
||||
|
||||
type Client interface {
|
||||
List() ([]string, error)
|
||||
Exists(app string) (bool, error)
|
||||
Delete(name string) error
|
||||
ComponentList(name string) ([]component.Component, error)
|
||||
GetMachineReadableFormat(appName string, projectName string) App
|
||||
GetMachineReadableFormatForList(apps []App) AppList
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
// Package application provides functions to list, check existence of, delete and get machine readable description of applications.
|
||||
// An application is a set of components and services.
|
||||
// An application is materialized by the `app:` label in `deployments`, `deploymentconfigs`,
|
||||
// or service instances (service instances from Operator Backed Services).
|
||||
package application
|
||||
@@ -1,112 +0,0 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
applabels "github.com/redhat-developer/odo/pkg/application/labels"
|
||||
"github.com/redhat-developer/odo/pkg/component"
|
||||
"github.com/redhat-developer/odo/pkg/kclient"
|
||||
"github.com/redhat-developer/odo/pkg/machineoutput"
|
||||
"github.com/redhat-developer/odo/pkg/util"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
type kubernetesClient struct {
|
||||
client kclient.ClientInterface
|
||||
}
|
||||
|
||||
func NewClient(client kclient.ClientInterface) Client {
|
||||
return kubernetesClient{
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
// List all applications names in current project by looking at `app` labels in deployments
|
||||
func (o kubernetesClient) List() ([]string, error) {
|
||||
if o.client == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Get all Deployments with the "app" label
|
||||
deploymentAppNames, err := o.client.GetDeploymentLabelValues(applabels.ApplicationLabel, applabels.ApplicationLabel)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to list applications from deployments")
|
||||
}
|
||||
|
||||
// Filter out any names, as there could be multiple components but within the same application
|
||||
return util.RemoveDuplicates(deploymentAppNames), nil
|
||||
}
|
||||
|
||||
// Exists checks whether the given app exist or not in the list of applications
|
||||
func (o kubernetesClient) Exists(app string) (bool, error) {
|
||||
|
||||
appList, err := o.List()
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, appName := range appList {
|
||||
if appName == app {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Delete the given application by deleting deployments and services instances belonging to this application
|
||||
func (o kubernetesClient) Delete(name string) error {
|
||||
klog.V(4).Infof("Deleting application %q", name)
|
||||
|
||||
labels := applabels.GetLabels(name, false)
|
||||
|
||||
// delete application from cluster
|
||||
err := o.client.Delete(labels, false)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to delete application %s", name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ComponentList returns the list of components for an application
|
||||
func (o kubernetesClient) ComponentList(name string) ([]component.Component, error) {
|
||||
selector := applabels.GetSelector(name)
|
||||
componentList, err := component.List(o.client, selector)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get Component list")
|
||||
}
|
||||
return componentList.Items, nil
|
||||
}
|
||||
|
||||
// GetMachineReadableFormat returns resource information in machine readable format
|
||||
func (o kubernetesClient) GetMachineReadableFormat(appName string, projectName string) App {
|
||||
componentList, _ := component.GetComponentNames(o.client, appName)
|
||||
appDef := App{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: appKind,
|
||||
APIVersion: machineoutput.APIVersion,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: appName,
|
||||
Namespace: projectName,
|
||||
},
|
||||
Spec: AppSpec{
|
||||
Components: componentList,
|
||||
},
|
||||
}
|
||||
return appDef
|
||||
}
|
||||
|
||||
// GetMachineReadableFormatForList returns application list in machine readable format
|
||||
func (o kubernetesClient) GetMachineReadableFormatForList(apps []App) AppList {
|
||||
return AppList{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: appList,
|
||||
APIVersion: machineoutput.APIVersion,
|
||||
},
|
||||
ListMeta: metav1.ListMeta{},
|
||||
Items: apps,
|
||||
}
|
||||
}
|
||||
@@ -1,348 +0,0 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
|
||||
applabels "github.com/redhat-developer/odo/pkg/application/labels"
|
||||
componentlabels "github.com/redhat-developer/odo/pkg/component/labels"
|
||||
"github.com/redhat-developer/odo/pkg/kclient"
|
||||
"github.com/redhat-developer/odo/pkg/machineoutput"
|
||||
"github.com/redhat-developer/odo/pkg/testingutil"
|
||||
"github.com/redhat-developer/odo/pkg/unions"
|
||||
"github.com/redhat-developer/odo/pkg/version"
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
ktesting "k8s.io/client-go/testing"
|
||||
)
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
kubclient := kclient.NewMockClientInterface(ctrl)
|
||||
kubclient.EXPECT().GetDeploymentLabelValues("app.kubernetes.io/part-of", "app.kubernetes.io/part-of").Return([]string{"app1", "app3", "app1", "app2"}, nil).AnyTimes()
|
||||
appClient := NewClient(kubclient)
|
||||
result, err := appClient.List()
|
||||
expected := []string{"app1", "app2", "app3"}
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil error, got %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(result, expected) {
|
||||
t.Errorf("Got %v, expected %v", result, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExists(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
search string
|
||||
result bool
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
name: "not exists",
|
||||
search: "an-app",
|
||||
result: false,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
name: "exists",
|
||||
search: "app1",
|
||||
result: true,
|
||||
err: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
kubclient := kclient.NewMockClientInterface(ctrl)
|
||||
kubclient.EXPECT().GetDeploymentLabelValues("app.kubernetes.io/part-of", "app.kubernetes.io/part-of").Return([]string{"app1", "app3", "app1", "app2"}, nil).AnyTimes()
|
||||
appClient := NewClient(kubclient)
|
||||
result, err := appClient.Exists(tt.search)
|
||||
if err != nil != tt.err {
|
||||
t.Errorf("Expected %v error, got %v", tt.err, err)
|
||||
}
|
||||
if result != tt.result {
|
||||
t.Errorf("Expected %v, got %v", tt.result, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
deleteReturn error
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
name: "kubernetes delete works",
|
||||
deleteReturn: nil,
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
name: "kubernetes delete fails",
|
||||
deleteReturn: errors.New("an error"),
|
||||
expectedErr: "unable to delete application",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
kubclient := kclient.NewMockClientInterface(ctrl)
|
||||
appClient := NewClient(kubclient)
|
||||
labels := map[string]string{
|
||||
"app.kubernetes.io/part-of": "an-app",
|
||||
}
|
||||
kubclient.EXPECT().Delete(labels, false).Return(tt.deleteReturn).Times(1)
|
||||
|
||||
// kube Delete works
|
||||
err := appClient.Delete("an-app")
|
||||
|
||||
if err == nil && tt.expectedErr != "" {
|
||||
t.Errorf("Expected %v, got no error", tt.expectedErr)
|
||||
return
|
||||
}
|
||||
if err != nil && tt.expectedErr == "" {
|
||||
t.Errorf("Expected no error, got %v", err.Error())
|
||||
return
|
||||
}
|
||||
if err != nil && tt.expectedErr != "" && !strings.Contains(err.Error(), tt.expectedErr) {
|
||||
t.Errorf("Expected error %v, got %v", tt.expectedErr, err.Error())
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestComponentList(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
kubclient := kclient.NewMockClientInterface(ctrl)
|
||||
depList := []appsv1.Deployment{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/instance": "a-component",
|
||||
"app.kubernetes.io/part-of": "an-app-name",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"odo.dev/project-type": "nodejs",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
kubclient.EXPECT().GetDeploymentFromSelector("app=an-app-name,app.kubernetes.io/managed-by=odo,app.kubernetes.io/part-of=an-app-name").Return(depList, nil).AnyTimes()
|
||||
kubclient.EXPECT().GetCurrentNamespace().Return("my-namespace").AnyTimes()
|
||||
kubclient.EXPECT().GetOneDeployment("a-component", "an-app-name").Return(&depList[0], nil).AnyTimes()
|
||||
ingresses := &unions.KubernetesIngressList{
|
||||
Items: nil,
|
||||
}
|
||||
kubclient.EXPECT().ListIngresses("app.kubernetes.io/instance=a-component,app.kubernetes.io/part-of=an-app-name").Return(ingresses, nil).AnyTimes()
|
||||
kubclient.EXPECT().IsServiceBindingSupported().Return(false, nil).AnyTimes()
|
||||
kubclient.EXPECT().ListSecrets("app.kubernetes.io/instance=a-component,app.kubernetes.io/part-of=an-app-name").Return(nil, nil).AnyTimes()
|
||||
kubclient.EXPECT().ListServices("").Return(nil, nil).AnyTimes()
|
||||
appClient := NewClient(kubclient)
|
||||
|
||||
result, err := appClient.ComponentList("an-app-name")
|
||||
if len(result) != 1 {
|
||||
t.Errorf("expected 1 component in list, got %d", len(result))
|
||||
}
|
||||
component := result[0]
|
||||
if component.Name != "a-component" {
|
||||
t.Errorf("Expected component name %q, got %q", "a-component", component.Name)
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error, got %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMachineReadableFormat(t *testing.T) {
|
||||
type args struct {
|
||||
appName string
|
||||
projectName string
|
||||
active bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want App
|
||||
}{
|
||||
{
|
||||
|
||||
name: "Test Case: machine readable output for application",
|
||||
args: args{
|
||||
appName: "myapp",
|
||||
projectName: "myproject",
|
||||
active: true,
|
||||
},
|
||||
want: App{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: appKind,
|
||||
APIVersion: machineoutput.APIVersion,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "myapp",
|
||||
Namespace: "myproject",
|
||||
},
|
||||
Spec: AppSpec{
|
||||
Components: []string{"frontend"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
deploymentList := appsv1.DeploymentList{
|
||||
Items: []appsv1.Deployment{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "frontend-myapp",
|
||||
Namespace: "myproject",
|
||||
Labels: map[string]string{
|
||||
applabels.ApplicationLabel: "myapp",
|
||||
componentlabels.ComponentLabel: "frontend",
|
||||
componentlabels.ComponentTypeLabel: "nodejs",
|
||||
applabels.ManagedBy: "odo",
|
||||
applabels.ManagerVersion: version.VERSION,
|
||||
},
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "dummyContainer",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "backend-app",
|
||||
Namespace: "myproject",
|
||||
Labels: map[string]string{
|
||||
applabels.ApplicationLabel: "app",
|
||||
componentlabels.ComponentLabel: "backend",
|
||||
componentlabels.ComponentTypeLabel: "java",
|
||||
applabels.ManagedBy: "odo",
|
||||
applabels.ManagerVersion: version.VERSION,
|
||||
},
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "dummyContainer",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Fake the client with the appropriate arguments
|
||||
client, fakeClientSet := kclient.FakeNew()
|
||||
|
||||
// fake the project
|
||||
fakeClientSet.Kubernetes.PrependReactor("get", "projects", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||
return true, &testingutil.FakeOnlyOneExistingProjects().Items[0], nil
|
||||
})
|
||||
|
||||
//fake the deployments
|
||||
fakeClientSet.Kubernetes.PrependReactor("list", "deployments", func(action ktesting.Action) (bool, runtime.Object, error) {
|
||||
return true, &deploymentList, nil
|
||||
})
|
||||
|
||||
for i := range deploymentList.Items {
|
||||
fakeClientSet.Kubernetes.PrependReactor("get", "deployments", func(action ktesting.Action) (bool, runtime.Object, error) {
|
||||
return true, &deploymentList.Items[i], nil
|
||||
})
|
||||
}
|
||||
kclient := NewClient(client)
|
||||
if got := kclient.GetMachineReadableFormat(tt.args.appName, tt.args.projectName); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("GetMachineReadableFormat() = %v,\n want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMachineReadableFormatForList(t *testing.T) {
|
||||
type args struct {
|
||||
apps []App
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want AppList
|
||||
}{
|
||||
{
|
||||
name: "Test Case: Machine Readable for Application List",
|
||||
args: args{
|
||||
apps: []App{
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: appKind,
|
||||
APIVersion: machineoutput.APIVersion,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "myapp",
|
||||
},
|
||||
Spec: AppSpec{
|
||||
Components: []string{"frontend"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: AppList{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: appList,
|
||||
APIVersion: machineoutput.APIVersion,
|
||||
},
|
||||
ListMeta: metav1.ListMeta{},
|
||||
Items: []App{
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: appKind,
|
||||
APIVersion: machineoutput.APIVersion,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "myapp",
|
||||
},
|
||||
Spec: AppSpec{
|
||||
Components: []string{"frontend"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
client, _ := kclient.FakeNew()
|
||||
kclient := NewClient(client)
|
||||
if got := kclient.GetMachineReadableFormatForList(tt.args.apps); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("GetMachineReadableFormatForList() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: pkg/application/application.go
|
||||
|
||||
// Package application is a generated GoMock package.
|
||||
package application
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
component "github.com/redhat-developer/odo/pkg/component"
|
||||
)
|
||||
|
||||
// MockClient is a mock of Client interface.
|
||||
type MockClient struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockClientMockRecorder
|
||||
}
|
||||
|
||||
// MockClientMockRecorder is the mock recorder for MockClient.
|
||||
type MockClientMockRecorder struct {
|
||||
mock *MockClient
|
||||
}
|
||||
|
||||
// NewMockClient creates a new mock instance.
|
||||
func NewMockClient(ctrl *gomock.Controller) *MockClient {
|
||||
mock := &MockClient{ctrl: ctrl}
|
||||
mock.recorder = &MockClientMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockClient) EXPECT() *MockClientMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// ComponentList mocks base method.
|
||||
func (m *MockClient) ComponentList(name string) ([]component.Component, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ComponentList", name)
|
||||
ret0, _ := ret[0].([]component.Component)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ComponentList indicates an expected call of ComponentList.
|
||||
func (mr *MockClientMockRecorder) ComponentList(name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ComponentList", reflect.TypeOf((*MockClient)(nil).ComponentList), name)
|
||||
}
|
||||
|
||||
// Delete mocks base method.
|
||||
func (m *MockClient) Delete(name string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Delete", name)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Delete indicates an expected call of Delete.
|
||||
func (mr *MockClientMockRecorder) Delete(name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockClient)(nil).Delete), name)
|
||||
}
|
||||
|
||||
// Exists mocks base method.
|
||||
func (m *MockClient) Exists(app string) (bool, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Exists", app)
|
||||
ret0, _ := ret[0].(bool)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Exists indicates an expected call of Exists.
|
||||
func (mr *MockClientMockRecorder) Exists(app interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exists", reflect.TypeOf((*MockClient)(nil).Exists), app)
|
||||
}
|
||||
|
||||
// GetMachineReadableFormat mocks base method.
|
||||
func (m *MockClient) GetMachineReadableFormat(appName, projectName string) App {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetMachineReadableFormat", appName, projectName)
|
||||
ret0, _ := ret[0].(App)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetMachineReadableFormat indicates an expected call of GetMachineReadableFormat.
|
||||
func (mr *MockClientMockRecorder) GetMachineReadableFormat(appName, projectName interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMachineReadableFormat", reflect.TypeOf((*MockClient)(nil).GetMachineReadableFormat), appName, projectName)
|
||||
}
|
||||
|
||||
// GetMachineReadableFormatForList mocks base method.
|
||||
func (m *MockClient) GetMachineReadableFormatForList(apps []App) AppList {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetMachineReadableFormatForList", apps)
|
||||
ret0, _ := ret[0].(AppList)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// GetMachineReadableFormatForList indicates an expected call of GetMachineReadableFormatForList.
|
||||
func (mr *MockClientMockRecorder) GetMachineReadableFormatForList(apps interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMachineReadableFormatForList", reflect.TypeOf((*MockClient)(nil).GetMachineReadableFormatForList), apps)
|
||||
}
|
||||
|
||||
// List mocks base method.
|
||||
func (m *MockClient) List() ([]string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "List")
|
||||
ret0, _ := ret[0].([]string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// List indicates an expected call of List.
|
||||
func (mr *MockClientMockRecorder) List() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockClient)(nil).List))
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
appKind = "Application"
|
||||
appList = "List"
|
||||
)
|
||||
|
||||
// Application
|
||||
type App struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
Spec AppSpec `json:"spec,omitempty"`
|
||||
}
|
||||
|
||||
// AppSpec is list of components present in given application
|
||||
type AppSpec struct {
|
||||
Components []string `json:"components,omitempty"`
|
||||
}
|
||||
|
||||
// AppList is a list of applications
|
||||
type AppList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []App `json:"items"`
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/devfile/library/pkg/devfile/parser"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/redhat-developer/odo/pkg/util"
|
||||
)
|
||||
|
||||
const (
|
||||
// Name is the name of the setting controlling the component name
|
||||
Name = "Name"
|
||||
// NameDescription is human-readable description of the Name setting
|
||||
NameDescription = "The name of the component"
|
||||
// Memory is the name of the setting controlling the memory a component consumes
|
||||
Memory = "Memory"
|
||||
// MemoryDescription is the description of the setting controlling the min and max memory to same value
|
||||
MemoryDescription = "The minimum and maximum memory a component can consume"
|
||||
// Ports is the space separated list of user specified ports to be opened in the component
|
||||
Ports = "Ports"
|
||||
// PortsDescription is the description of the ports component setting
|
||||
PortsDescription = "Ports to be opened in the component"
|
||||
)
|
||||
|
||||
var (
|
||||
supportedDevfileParameterDescriptions = map[string]string{
|
||||
Name: NameDescription,
|
||||
Ports: PortsDescription,
|
||||
Memory: MemoryDescription,
|
||||
}
|
||||
lowerCaseDevfileParameters = util.GetLowerCaseParameters(GetDevfileSupportedParameters())
|
||||
)
|
||||
|
||||
// FormatDevfileSupportedParameters outputs supported parameters and their description
|
||||
func FormatDevfileSupportedParameters() (result string) {
|
||||
for _, v := range GetDevfileSupportedParameters() {
|
||||
result = result + " " + v + " - " + supportedDevfileParameterDescriptions[v] + "\n"
|
||||
}
|
||||
return "\nAvailable Parameters for Devfile Components:\n" + result
|
||||
}
|
||||
|
||||
func GetDevfileSupportedParameters() []string {
|
||||
return util.GetSortedKeys(supportedDevfileParameterDescriptions)
|
||||
}
|
||||
|
||||
// AsDevfileSupportedParameter returns the parameter in lower case and a boolean indicating if it is a supported parameter
|
||||
func AsDevfileSupportedParameter(param string) (string, bool) {
|
||||
lower := strings.ToLower(param)
|
||||
return lower, lowerCaseDevfileParameters[lower]
|
||||
}
|
||||
|
||||
// SetDevfileConfiguration allows setting all the parameters that are configurable in a devfile
|
||||
func SetDevfileConfiguration(d parser.DevfileObj, parameter string, value interface{}) error {
|
||||
|
||||
// we are ignoring this error becase a developer is usually aware of the type of value that is
|
||||
// being passed. So consider this a shortcut, if you know its a string value use this strValue
|
||||
// else parse it inside the switch case.
|
||||
strValue, _ := value.(string)
|
||||
if param, ok := AsDevfileSupportedParameter(parameter); ok {
|
||||
switch param {
|
||||
case "name":
|
||||
return d.SetMetadataName(strValue)
|
||||
case "ports":
|
||||
arrValue := strings.Split(strValue, ",")
|
||||
return d.SetPorts(arrValue...)
|
||||
case "memory":
|
||||
return d.SetMemory(strValue)
|
||||
}
|
||||
|
||||
}
|
||||
return errors.Errorf("unknown parameter :'%s' is not a configurable parameter in the devfile", parameter)
|
||||
|
||||
}
|
||||
|
||||
// DeleteConfiguration allows deleting the parameters that are configurable in a devfile
|
||||
func DeleteDevfileConfiguration(d parser.DevfileObj, parameter string) error {
|
||||
if param, ok := AsDevfileSupportedParameter(parameter); ok {
|
||||
switch param {
|
||||
case "name":
|
||||
return d.SetMetadataName("")
|
||||
case "ports":
|
||||
return d.RemovePorts()
|
||||
case "memory":
|
||||
return d.SetMemory("")
|
||||
}
|
||||
}
|
||||
return errors.Errorf("unknown parameter :'%s' is not a configurable parameter in the devfile", parameter)
|
||||
}
|
||||
|
||||
// IsSet checks if a parameter is set in the devfile
|
||||
func IsSetInDevfile(d parser.DevfileObj, parameter string) bool {
|
||||
if p, ok := AsDevfileSupportedParameter(parameter); ok {
|
||||
switch p {
|
||||
case "name":
|
||||
return d.GetMetadataName() != ""
|
||||
case "ports":
|
||||
return d.HasPorts()
|
||||
case "memory":
|
||||
return d.GetMemory() != ""
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
}
|
||||
@@ -1,209 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/devfile/library/pkg/devfile/parser/data"
|
||||
|
||||
devfilev1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
||||
"github.com/devfile/library/pkg/devfile/parser"
|
||||
devfileCtx "github.com/devfile/library/pkg/devfile/parser/context"
|
||||
devfilefs "github.com/devfile/library/pkg/testingutil/filesystem"
|
||||
"github.com/kylelemons/godebug/pretty"
|
||||
odoTestingUtil "github.com/redhat-developer/odo/pkg/testingutil"
|
||||
)
|
||||
|
||||
func TestSetDevfileConfiguration(t *testing.T) {
|
||||
|
||||
// Use fakeFs
|
||||
fs := devfilefs.NewFakeFs()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args map[string]string
|
||||
currentDevfile parser.DevfileObj
|
||||
wantDevFile parser.DevfileObj
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "case 1: set memory to 500Mi",
|
||||
args: map[string]string{
|
||||
"memory": "500Mi",
|
||||
},
|
||||
currentDevfile: odoTestingUtil.GetTestDevfileObj(fs),
|
||||
wantDevFile: parser.DevfileObj{
|
||||
Ctx: devfileCtx.FakeContext(fs, parser.OutputDevfileYamlPath),
|
||||
Data: func() data.DevfileData {
|
||||
devfileData, err := data.NewDevfileData(string(data.APISchemaVersion200))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
err = devfileData.AddComponents([]devfilev1.Component{
|
||||
{
|
||||
Name: "runtime",
|
||||
ComponentUnion: devfilev1.ComponentUnion{
|
||||
Container: &devfilev1.ContainerComponent{
|
||||
Container: devfilev1.Container{
|
||||
Image: "quay.io/nodejs-12",
|
||||
MemoryLimit: "500Mi",
|
||||
},
|
||||
Endpoints: []devfilev1.Endpoint{
|
||||
{
|
||||
Name: "port-3030",
|
||||
TargetPort: 3000,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "loadbalancer",
|
||||
ComponentUnion: devfilev1.ComponentUnion{
|
||||
Container: &devfilev1.ContainerComponent{
|
||||
Container: devfilev1.Container{
|
||||
Image: "quay.io/nginx",
|
||||
MemoryLimit: "500Mi",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
err = devfileData.AddCommands([]devfilev1.Command{
|
||||
{
|
||||
Id: "devbuild",
|
||||
CommandUnion: devfilev1.CommandUnion{
|
||||
Exec: &devfilev1.ExecCommand{
|
||||
WorkingDir: "/projects/nodejs-starter",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
return devfileData
|
||||
}(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "case 2: set ports array",
|
||||
args: map[string]string{
|
||||
"ports": "8080,8081/UDP,8080/TCP",
|
||||
},
|
||||
currentDevfile: odoTestingUtil.GetTestDevfileObj(fs),
|
||||
wantDevFile: parser.DevfileObj{
|
||||
Ctx: devfileCtx.FakeContext(fs, parser.OutputDevfileYamlPath),
|
||||
Data: func() data.DevfileData {
|
||||
devfileData, err := data.NewDevfileData(string(data.APISchemaVersion200))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
err = devfileData.AddCommands([]devfilev1.Command{
|
||||
{
|
||||
Id: "devbuild",
|
||||
CommandUnion: devfilev1.CommandUnion{
|
||||
Exec: &devfilev1.ExecCommand{
|
||||
WorkingDir: "/projects/nodejs-starter",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
err = devfileData.AddComponents([]devfilev1.Component{
|
||||
{
|
||||
Name: "runtime",
|
||||
ComponentUnion: devfilev1.ComponentUnion{
|
||||
Container: &devfilev1.ContainerComponent{
|
||||
Container: devfilev1.Container{
|
||||
Image: "quay.io/nodejs-12",
|
||||
},
|
||||
Endpoints: []devfilev1.Endpoint{
|
||||
{
|
||||
Name: "port-3030",
|
||||
TargetPort: 3000,
|
||||
},
|
||||
{
|
||||
Name: "port-8080-tcp",
|
||||
TargetPort: 8080,
|
||||
Protocol: "tcp",
|
||||
}, {
|
||||
Name: "port-8081-udp",
|
||||
TargetPort: 8081,
|
||||
Protocol: "udp",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "loadbalancer",
|
||||
ComponentUnion: devfilev1.ComponentUnion{
|
||||
Container: &devfilev1.ContainerComponent{
|
||||
Container: devfilev1.Container{
|
||||
Image: "quay.io/nginx",
|
||||
},
|
||||
Endpoints: []devfilev1.Endpoint{
|
||||
{
|
||||
Name: "port-8080-tcp",
|
||||
TargetPort: 8080,
|
||||
Protocol: "tcp",
|
||||
}, {
|
||||
Name: "port-8081-udp",
|
||||
TargetPort: 8081,
|
||||
Protocol: "udp",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
return devfileData
|
||||
}(),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "case 3: set ports array fails due to validation",
|
||||
args: map[string]string{
|
||||
"ports": "8080,8081/UDP,8083/",
|
||||
},
|
||||
currentDevfile: odoTestingUtil.GetTestDevfileObj(fs),
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
for key, value := range tt.args {
|
||||
err := SetDevfileConfiguration(tt.currentDevfile, key, value)
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Errorf("expected error but got nil")
|
||||
}
|
||||
// we dont expect an error here
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("error while setting configuration %+v", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !tt.wantErr {
|
||||
if !reflect.DeepEqual(tt.currentDevfile.Data, tt.wantDevFile.Data) {
|
||||
t.Errorf("wanted: %v, got: %v, difference at %v", tt.wantDevFile, tt.currentDevfile, pretty.Compare(tt.currentDevfile.Data, tt.wantDevFile.Data))
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
# See the OWNERS docs: https://git.k8s.io/community/contributors/guide/owners.md
|
||||
|
||||
approvers:
|
||||
- mik-dass
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
package debug
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
// Info contains the information about the current Debug session
|
||||
type Info struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
Spec InfoSpec `json:"spec"`
|
||||
}
|
||||
|
||||
type InfoSpec struct {
|
||||
App string `json:"app,omitempty"`
|
||||
DebugProcessID int `json:"debugProcessID"`
|
||||
RemotePort int `json:"remotePort"`
|
||||
LocalPort int `json:"localPort"`
|
||||
}
|
||||
|
||||
// GetDebugInfoFilePath gets the file path of the debug info file
|
||||
func GetDebugInfoFilePath(componentName, appName string, projectName string) string {
|
||||
tempDir := os.TempDir()
|
||||
debugFileSuffix := "odo-debug.json"
|
||||
var arr []string
|
||||
if appName == "" {
|
||||
arr = []string{projectName, componentName, debugFileSuffix}
|
||||
} else {
|
||||
arr = []string{projectName, appName, componentName, debugFileSuffix}
|
||||
}
|
||||
debugFileName := strings.Join(arr, "-")
|
||||
return filepath.Join(tempDir, debugFileName)
|
||||
}
|
||||
|
||||
func CreateDebugInfoFile(f *DefaultPortForwarder, portPair string) error {
|
||||
return createDebugInfoFile(f, portPair, filesystem.DefaultFs{})
|
||||
}
|
||||
|
||||
// createDebugInfoFile creates a file in the temp directory with information regarding the debugging session of a component
|
||||
func createDebugInfoFile(f *DefaultPortForwarder, portPair string, fs filesystem.Filesystem) error {
|
||||
portPairs := strings.Split(portPair, ":")
|
||||
if len(portPairs) != 2 {
|
||||
return errors.New("port pair should be of the format localPort:RemotePort")
|
||||
}
|
||||
|
||||
localPort, err := strconv.Atoi(portPairs[0])
|
||||
if err != nil {
|
||||
return errors.New("local port should be a int")
|
||||
}
|
||||
remotePort, err := strconv.Atoi(portPairs[1])
|
||||
if err != nil {
|
||||
return errors.New("remote port should be a int")
|
||||
}
|
||||
|
||||
debugFile := Info{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "OdoDebugInfo",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: f.componentName,
|
||||
Namespace: f.projectName,
|
||||
},
|
||||
Spec: InfoSpec{
|
||||
App: f.appName,
|
||||
DebugProcessID: os.Getpid(),
|
||||
RemotePort: remotePort,
|
||||
LocalPort: localPort,
|
||||
},
|
||||
}
|
||||
odoDebugPathData, err := json.Marshal(debugFile)
|
||||
if err != nil {
|
||||
return errors.New("error marshalling json data")
|
||||
}
|
||||
|
||||
// writes the data to the debug info file
|
||||
file, err := fs.OpenFile(GetDebugInfoFilePath(f.componentName, f.appName, f.projectName), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close() // #nosec G307
|
||||
|
||||
_, err = file.Write(odoDebugPathData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetInfo gathers the information with regards to debugging information
|
||||
func GetInfo(f *DefaultPortForwarder) (Info, bool) {
|
||||
return getInfo(f, filesystem.DefaultFs{})
|
||||
}
|
||||
|
||||
// getInfo gets information regarding the debugging session of the component
|
||||
// returns the OdoDebugFile from the debug info file
|
||||
// returns true if debugging is running else false
|
||||
func getInfo(f *DefaultPortForwarder, fs filesystem.Filesystem) (Info, bool) {
|
||||
// gets the debug info file path and reads/unmarshalls it
|
||||
debugInfoFilePath := GetDebugInfoFilePath(f.componentName, f.appName, f.projectName)
|
||||
readFile, err := fs.ReadFile(debugInfoFilePath)
|
||||
if err != nil {
|
||||
klog.V(4).Infof("the debug %v is not present", debugInfoFilePath)
|
||||
return Info{}, false
|
||||
}
|
||||
|
||||
var info Info
|
||||
err = json.Unmarshal(readFile, &info)
|
||||
if err != nil {
|
||||
klog.V(4).Infof("couldn't unmarshal the debug file %v", debugInfoFilePath)
|
||||
return Info{}, false
|
||||
}
|
||||
|
||||
// get the debug process id and send a signal 0 to check if it's alive or not
|
||||
// according to https://golang.org/pkg/os/#FindProcess
|
||||
// On Unix systems, FindProcess always succeeds and returns a Process for the given pid, regardless of whether the process exists.
|
||||
// thus this step will pass on Unix systems and so for those systems and some others supporting signals
|
||||
// we check if the process is alive or not by sending a signal 0 to the process
|
||||
processInfo, err := os.FindProcess(info.Spec.DebugProcessID)
|
||||
if err != nil || processInfo == nil {
|
||||
klog.V(4).Infof("error getting the process info for pid %v", info.Spec.DebugProcessID)
|
||||
return Info{}, false
|
||||
}
|
||||
|
||||
// signal is not available on windows so we skip this step for windows
|
||||
if runtime.GOOS != "windows" {
|
||||
err = processInfo.Signal(syscall.Signal(0))
|
||||
if err != nil {
|
||||
klog.V(4).Infof("error sending signal 0 to pid %v, cause: %v", info.Spec.DebugProcessID, err)
|
||||
return Info{}, false
|
||||
}
|
||||
}
|
||||
|
||||
// gets the debug local port and tries to listen on it
|
||||
// if error doesn't occur the debug port was free and thus no debug process was using the port
|
||||
addressLook := "localhost:" + strconv.Itoa(info.Spec.LocalPort)
|
||||
listener, err := net.Listen("tcp", addressLook)
|
||||
if err == nil {
|
||||
klog.V(4).Infof("the debug port %v is free, thus debug is not running", info.Spec.LocalPort)
|
||||
err = listener.Close()
|
||||
if err != nil {
|
||||
klog.V(4).Infof("error occurred while closing the listener, cause :%v", err)
|
||||
}
|
||||
return Info{}, false
|
||||
}
|
||||
|
||||
return info, true
|
||||
}
|
||||
@@ -1,369 +0,0 @@
|
||||
package debug
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/util"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/testingutil"
|
||||
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// fakeOdoDebugFileString creates a json string of a fake OdoDebugFile
|
||||
func fakeOdoDebugFileString(typeMeta metav1.TypeMeta, processID int, projectName, appName, componentName string, remotePort, localPort int) (string, error) {
|
||||
file := Info{
|
||||
TypeMeta: typeMeta,
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: projectName,
|
||||
Name: componentName,
|
||||
},
|
||||
Spec: InfoSpec{
|
||||
App: appName,
|
||||
DebugProcessID: processID,
|
||||
RemotePort: remotePort,
|
||||
LocalPort: localPort,
|
||||
},
|
||||
}
|
||||
|
||||
data, err := json.Marshal(file)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
|
||||
func Test_createDebugInfoFile(t *testing.T) {
|
||||
|
||||
// create a fake fs in memory
|
||||
fs := filesystem.NewFakeFs()
|
||||
|
||||
type args struct {
|
||||
defaultPortForwarder *DefaultPortForwarder
|
||||
portPair string
|
||||
fs filesystem.Filesystem
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
alreadyExistFile bool
|
||||
wantDebugInfo Info
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "case 1: normal json write to the debug file",
|
||||
args: args{
|
||||
defaultPortForwarder: &DefaultPortForwarder{
|
||||
componentName: "nodejs-ex",
|
||||
appName: "app",
|
||||
projectName: "testing-1",
|
||||
},
|
||||
portPair: "5858:9001",
|
||||
fs: fs,
|
||||
},
|
||||
wantDebugInfo: Info{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "OdoDebugInfo",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "nodejs-ex",
|
||||
Namespace: "testing-1",
|
||||
},
|
||||
Spec: InfoSpec{
|
||||
DebugProcessID: os.Getpid(),
|
||||
App: "app",
|
||||
RemotePort: 9001,
|
||||
LocalPort: 5858,
|
||||
},
|
||||
},
|
||||
alreadyExistFile: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "case 2: overwrite the debug file",
|
||||
args: args{
|
||||
defaultPortForwarder: &DefaultPortForwarder{
|
||||
componentName: "nodejs-ex",
|
||||
appName: "app",
|
||||
projectName: "testing-1",
|
||||
},
|
||||
portPair: "5758:9004",
|
||||
fs: fs,
|
||||
},
|
||||
wantDebugInfo: Info{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "OdoDebugInfo",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "nodejs-ex",
|
||||
Namespace: "testing-1",
|
||||
},
|
||||
Spec: InfoSpec{
|
||||
DebugProcessID: os.Getpid(),
|
||||
App: "app",
|
||||
RemotePort: 9004,
|
||||
LocalPort: 5758,
|
||||
},
|
||||
},
|
||||
alreadyExistFile: true,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
debugFilePath := GetDebugInfoFilePath(tt.args.defaultPortForwarder.componentName, tt.args.defaultPortForwarder.appName, tt.args.defaultPortForwarder.projectName)
|
||||
// create a already existing file
|
||||
if tt.alreadyExistFile {
|
||||
_, err := testingutil.MkFileWithContent(debugFilePath, "blah", fs)
|
||||
if err != nil {
|
||||
t.Errorf("error happened while writing, cause: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := createDebugInfoFile(tt.args.defaultPortForwarder, tt.args.portPair, tt.args.fs); (err != nil) != tt.wantErr {
|
||||
t.Errorf("createDebugInfoFile() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
|
||||
readBytes, err := fs.ReadFile(debugFilePath)
|
||||
if err != nil {
|
||||
t.Errorf("error while reading file, cause: %v", err)
|
||||
}
|
||||
var odoDebugFileData Info
|
||||
err = json.Unmarshal(readBytes, &odoDebugFileData)
|
||||
if err != nil {
|
||||
t.Errorf("error occured while unmarshalling json, cause: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(tt.wantDebugInfo, odoDebugFileData) {
|
||||
t.Errorf("odo debug info on file doesn't match, got: %v, want: %v", odoDebugFileData, tt.wantDebugInfo)
|
||||
}
|
||||
|
||||
// clear the odo debug info file
|
||||
_ = fs.RemoveAll(debugFilePath)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getDebugInfo(t *testing.T) {
|
||||
|
||||
// create a fake fs in memory
|
||||
fs := filesystem.NewFakeFs()
|
||||
|
||||
type args struct {
|
||||
defaultPortForwarder *DefaultPortForwarder
|
||||
fs filesystem.Filesystem
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
fileExists bool
|
||||
debugPortListening bool
|
||||
readDebugFile Info
|
||||
wantDebugFile Info
|
||||
debugRunning bool
|
||||
}{
|
||||
{
|
||||
name: "case 1: the debug file exists",
|
||||
args: args{
|
||||
defaultPortForwarder: &DefaultPortForwarder{
|
||||
appName: "app",
|
||||
componentName: "nodejs-ex",
|
||||
projectName: "testing-1",
|
||||
},
|
||||
fs: fs,
|
||||
},
|
||||
wantDebugFile: Info{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "OdoDebugInfo",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "nodejs-ex",
|
||||
Namespace: "testing-1",
|
||||
},
|
||||
Spec: InfoSpec{
|
||||
DebugProcessID: os.Getpid(),
|
||||
App: "app",
|
||||
RemotePort: 5858,
|
||||
LocalPort: 9001,
|
||||
},
|
||||
},
|
||||
readDebugFile: Info{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "OdoDebugInfo",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "nodejs-ex",
|
||||
Namespace: "testing-1",
|
||||
},
|
||||
Spec: InfoSpec{
|
||||
DebugProcessID: os.Getpid(),
|
||||
App: "app",
|
||||
RemotePort: 5858,
|
||||
LocalPort: 9001,
|
||||
},
|
||||
},
|
||||
debugPortListening: true,
|
||||
fileExists: true,
|
||||
debugRunning: true,
|
||||
},
|
||||
{
|
||||
name: "case 2: the debug file doesn't exists",
|
||||
args: args{
|
||||
defaultPortForwarder: &DefaultPortForwarder{
|
||||
appName: "app",
|
||||
componentName: "nodejs-ex",
|
||||
projectName: "testing-1",
|
||||
},
|
||||
fs: fs,
|
||||
},
|
||||
debugPortListening: true,
|
||||
wantDebugFile: Info{},
|
||||
readDebugFile: Info{},
|
||||
fileExists: false,
|
||||
debugRunning: false,
|
||||
},
|
||||
{
|
||||
name: "case 3: debug port not listening",
|
||||
args: args{
|
||||
defaultPortForwarder: &DefaultPortForwarder{
|
||||
appName: "app",
|
||||
componentName: "nodejs-ex",
|
||||
projectName: "testing-1",
|
||||
},
|
||||
fs: fs,
|
||||
},
|
||||
debugPortListening: false,
|
||||
wantDebugFile: Info{},
|
||||
readDebugFile: Info{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "OdoDebugInfo",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "nodejs-ex",
|
||||
Namespace: "testing-1",
|
||||
},
|
||||
Spec: InfoSpec{
|
||||
DebugProcessID: os.Getpid(),
|
||||
App: "app",
|
||||
RemotePort: 5858,
|
||||
LocalPort: 9001,
|
||||
},
|
||||
},
|
||||
fileExists: true,
|
||||
debugRunning: false,
|
||||
},
|
||||
{
|
||||
name: "case 4: the process is not running",
|
||||
args: args{
|
||||
defaultPortForwarder: &DefaultPortForwarder{
|
||||
appName: "app",
|
||||
componentName: "nodejs-ex",
|
||||
projectName: "testing-1",
|
||||
},
|
||||
fs: fs,
|
||||
},
|
||||
debugPortListening: true,
|
||||
wantDebugFile: Info{},
|
||||
readDebugFile: Info{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "OdoDebugInfo",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "nodejs-ex",
|
||||
Namespace: "testing-1",
|
||||
},
|
||||
Spec: InfoSpec{
|
||||
DebugProcessID: os.Getpid() + 818177979,
|
||||
App: "app",
|
||||
RemotePort: 5858,
|
||||
LocalPort: 9001,
|
||||
},
|
||||
},
|
||||
fileExists: true,
|
||||
debugRunning: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
freePort, err := util.HTTPGetFreePort()
|
||||
if err != nil {
|
||||
t.Errorf("error occured while getting a free port, cause: %v", err)
|
||||
}
|
||||
|
||||
if tt.readDebugFile.Spec.LocalPort != 0 {
|
||||
tt.readDebugFile.Spec.LocalPort = freePort
|
||||
}
|
||||
|
||||
if tt.wantDebugFile.Spec.LocalPort != 0 {
|
||||
tt.wantDebugFile.Spec.LocalPort = freePort
|
||||
}
|
||||
|
||||
odoDebugFilePath := GetDebugInfoFilePath(tt.args.defaultPortForwarder.componentName, tt.args.defaultPortForwarder.appName, tt.args.defaultPortForwarder.projectName)
|
||||
if tt.fileExists {
|
||||
fakeString, err := fakeOdoDebugFileString(tt.readDebugFile.TypeMeta,
|
||||
tt.readDebugFile.Spec.DebugProcessID,
|
||||
tt.readDebugFile.ObjectMeta.Namespace,
|
||||
tt.readDebugFile.Spec.App,
|
||||
tt.readDebugFile.ObjectMeta.Name,
|
||||
tt.readDebugFile.Spec.RemotePort,
|
||||
tt.readDebugFile.Spec.LocalPort)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("error occured while getting odo debug file string, cause: %v", err)
|
||||
}
|
||||
|
||||
_, err = testingutil.MkFileWithContent(odoDebugFilePath, fakeString, fs)
|
||||
if err != nil {
|
||||
t.Errorf("error occured while writing to file, cause: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
stopListenerChan := make(chan bool)
|
||||
listenerStarted := false
|
||||
if tt.debugPortListening {
|
||||
startListenerChan := make(chan bool)
|
||||
go func() {
|
||||
err := testingutil.FakePortListener(startListenerChan, stopListenerChan, tt.readDebugFile.Spec.LocalPort)
|
||||
if err != nil {
|
||||
// the fake listener failed, show error and close the channel
|
||||
t.Errorf("error while starting fake port listerner, cause: %v", err)
|
||||
close(startListenerChan)
|
||||
}
|
||||
}()
|
||||
// wait for the test server to start listening
|
||||
if <-startListenerChan {
|
||||
listenerStarted = true
|
||||
}
|
||||
}
|
||||
|
||||
got, resultRunning := getInfo(tt.args.defaultPortForwarder, tt.args.fs)
|
||||
|
||||
if !reflect.DeepEqual(got, tt.wantDebugFile) {
|
||||
t.Errorf("getDebugInfo() got = %v, want %v", got, tt.wantDebugFile)
|
||||
}
|
||||
if resultRunning != tt.debugRunning {
|
||||
t.Errorf("getDebugInfo() got1 = %v, want %v", resultRunning, tt.debugRunning)
|
||||
}
|
||||
|
||||
// clear the odo debug info file
|
||||
_ = fs.RemoveAll(odoDebugFilePath)
|
||||
|
||||
// close the listener
|
||||
if listenerStarted == true {
|
||||
stopListenerChan <- true
|
||||
}
|
||||
close(stopListenerChan)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
package debug
|
||||
|
||||
import (
|
||||
"github.com/redhat-developer/odo/pkg/kclient"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
k8sgenclioptions "k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/client-go/tools/portforward"
|
||||
"k8s.io/client-go/transport/spdy"
|
||||
)
|
||||
|
||||
// DefaultPortForwarder implements the SPDY based port forwarder
|
||||
type DefaultPortForwarder struct {
|
||||
kClient kclient.ClientInterface
|
||||
k8sgenclioptions.IOStreams
|
||||
componentName string
|
||||
appName string
|
||||
projectName string
|
||||
}
|
||||
|
||||
func NewDefaultPortForwarder(componentName, appName string, projectName string, kClient kclient.ClientInterface, streams k8sgenclioptions.IOStreams) *DefaultPortForwarder {
|
||||
return &DefaultPortForwarder{
|
||||
kClient: kClient,
|
||||
IOStreams: streams,
|
||||
componentName: componentName,
|
||||
appName: appName,
|
||||
projectName: projectName,
|
||||
}
|
||||
}
|
||||
|
||||
// ForwardPorts forwards the port using the url for the remote pod.
|
||||
// portPair is a pair of port in format "localPort:RemotePort" that is to be forwarded
|
||||
// stop Chan is used to stop port forwarding
|
||||
// ready Chan is used to signal failure to the channel receiver
|
||||
func (f *DefaultPortForwarder) ForwardPorts(portPair string, stopChan, readyChan chan struct{}, isDevfile bool) error {
|
||||
var pod *corev1.Pod
|
||||
var conf *rest.Config
|
||||
var err error
|
||||
|
||||
if f.kClient != nil && isDevfile {
|
||||
conf, err = f.kClient.GetConfig().ClientConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pod, err = f.kClient.GetOnePod(f.componentName, f.appName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
conf = f.kClient.GetClientConfig()
|
||||
}
|
||||
|
||||
if pod.Status.Phase != corev1.PodRunning {
|
||||
return fmt.Errorf("unable to forward port because pod is not running. Current status=%v", pod.Status.Phase)
|
||||
}
|
||||
|
||||
transport, upgrader, err := spdy.RoundTripperFor(conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := f.kClient.GeneratePortForwardReq(pod.Name)
|
||||
|
||||
dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, "POST", req.URL())
|
||||
fw, err := portforward.New(dialer, []string{portPair}, stopChan, readyChan, f.Out, f.ErrOut)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("Started port forwarding at ports -", portPair)
|
||||
return fw.ForwardPorts()
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// envinfo package is DEPRECATED and will be removed during v3 implementation
|
||||
package envinfo
|
||||
|
||||
import (
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/devfile/library/pkg/devfile/generator"
|
||||
applabels "github.com/redhat-developer/odo/pkg/application/labels"
|
||||
componentlabels "github.com/redhat-developer/odo/pkg/component/labels"
|
||||
"github.com/redhat-developer/odo/pkg/unions"
|
||||
"github.com/redhat-developer/odo/pkg/kclient/unions"
|
||||
"github.com/redhat-developer/odo/pkg/url/labels"
|
||||
"github.com/redhat-developer/odo/pkg/util"
|
||||
"github.com/redhat-developer/odo/pkg/version"
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/unions"
|
||||
"github.com/redhat-developer/odo/pkg/kclient/unions"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/unions"
|
||||
"github.com/redhat-developer/odo/pkg/kclient/unions"
|
||||
|
||||
"github.com/devfile/library/pkg/devfile/generator"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
projectv1 "github.com/openshift/api/project/v1"
|
||||
routev1 "github.com/openshift/api/route/v1"
|
||||
olm "github.com/operator-framework/api/pkg/operators/v1alpha1"
|
||||
"github.com/redhat-developer/odo/pkg/kclient/unions"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
"github.com/redhat-developer/odo/pkg/unions"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
@@ -14,8 +14,8 @@ import (
|
||||
v1 "github.com/openshift/api/project/v1"
|
||||
v10 "github.com/openshift/api/route/v1"
|
||||
v1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
|
||||
unions "github.com/redhat-developer/odo/pkg/kclient/unions"
|
||||
log "github.com/redhat-developer/odo/pkg/log"
|
||||
unions "github.com/redhat-developer/odo/pkg/unions"
|
||||
v11 "k8s.io/api/apps/v1"
|
||||
v12 "k8s.io/api/core/v1"
|
||||
meta "k8s.io/apimachinery/pkg/api/meta"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package odogenerator
|
||||
package unions
|
||||
|
||||
import (
|
||||
"github.com/devfile/library/pkg/devfile/generator"
|
||||
@@ -54,7 +54,7 @@ func getNetworkingV1IngressSpec(ingressSpecParams generator.IngressSpecParams) *
|
||||
return ingressSpec
|
||||
}
|
||||
|
||||
func GetNetworkingV1Ingress(ingressParams generator.IngressParams) *v1.Ingress {
|
||||
func getNetworkingV1Ingress(ingressParams generator.IngressParams) *v1.Ingress {
|
||||
var ip *v1.Ingress
|
||||
ingressSpec := getNetworkingV1IngressSpec(ingressParams.IngressSpecParams)
|
||||
ip = &v1.Ingress{
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
|
||||
v1alpha2 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
||||
"github.com/devfile/library/pkg/devfile/generator"
|
||||
"github.com/redhat-developer/odo/pkg/odogenerator"
|
||||
"k8s.io/api/extensions/v1beta1"
|
||||
v1 "k8s.io/api/networking/v1"
|
||||
)
|
||||
@@ -37,7 +36,7 @@ func NewGeneratedKubernetesIngress() *KubernetesIngress {
|
||||
//NewKubernetesIngressFromParams generates a new KubernetesIngress from the ingress params
|
||||
func NewKubernetesIngressFromParams(ingressParams generator.IngressParams) *KubernetesIngress {
|
||||
ki := NewGeneratedKubernetesIngress()
|
||||
ki.NetworkingV1Ingress = odogenerator.GetNetworkingV1Ingress(ingressParams)
|
||||
ki.NetworkingV1Ingress = getNetworkingV1Ingress(ingressParams)
|
||||
ki.ExtensionV1Beta1Ingress = generator.GetIngress(v1alpha2.Endpoint{}, ingressParams)
|
||||
return ki
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/odo/util"
|
||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||
"github.com/redhat-developer/odo/pkg/odo/util/completion"
|
||||
)
|
||||
|
||||
// RecommendedCommandName is the recommended app command name
|
||||
const RecommendedCommandName = "app"
|
||||
|
||||
// NewCmdApplication implements the odo application command
|
||||
func NewCmdApplication(name, fullName string) *cobra.Command {
|
||||
delete := NewCmdDelete(deleteRecommendedCommandName, odoutil.GetFullName(fullName, deleteRecommendedCommandName))
|
||||
describe := NewCmdDescribe(describeRecommendedCommandName, odoutil.GetFullName(fullName, describeRecommendedCommandName))
|
||||
list := NewCmdList(listRecommendedCommandName, odoutil.GetFullName(fullName, listRecommendedCommandName))
|
||||
applicationCmd := &cobra.Command{
|
||||
Use: name,
|
||||
Short: "Perform application operations",
|
||||
Long: `Performs application operations related to your project.`,
|
||||
Example: fmt.Sprintf("%s\n\n%s\n\n%s",
|
||||
delete.Example,
|
||||
describe.Example,
|
||||
list.Example),
|
||||
Aliases: []string{"application"},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
},
|
||||
}
|
||||
|
||||
applicationCmd.AddCommand(delete, describe, list)
|
||||
|
||||
// Add a defined annotation in order to appear in the help menu
|
||||
applicationCmd.Annotations = map[string]string{"command": "main"}
|
||||
applicationCmd.SetUsageTemplate(odoutil.CmdUsageTemplate)
|
||||
|
||||
return applicationCmd
|
||||
}
|
||||
|
||||
// AddApplicationFlag adds a `app` flag to the given cobra command
|
||||
// Also adds a completion handler to the flag
|
||||
func AddApplicationFlag(cmd *cobra.Command) {
|
||||
cmd.Flags().String(util.ApplicationFlagName, "", "Application, defaults to active application")
|
||||
completion.RegisterCommandFlagHandler(cmd, "app", completion.AppCompletionHandler)
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/application"
|
||||
"github.com/redhat-developer/odo/pkg/kclient"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/project"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/ui"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
odoUtil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||
"github.com/redhat-developer/odo/pkg/odo/util/completion"
|
||||
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
const deleteRecommendedCommandName = "delete"
|
||||
|
||||
var (
|
||||
deleteExample = ktemplates.Examples(` # Delete the application
|
||||
%[1]s myapp`)
|
||||
)
|
||||
|
||||
// DeleteOptions encapsulates the options for the odo command
|
||||
type DeleteOptions struct {
|
||||
// Context
|
||||
*genericclioptions.Context
|
||||
|
||||
// Clients
|
||||
appClient application.Client
|
||||
|
||||
// Parameters
|
||||
appName string
|
||||
|
||||
// Flags
|
||||
forceFlag bool
|
||||
}
|
||||
|
||||
// NewDeleteOptions creates a new DeleteOptions instance
|
||||
func NewDeleteOptions(appClient application.Client) *DeleteOptions {
|
||||
return &DeleteOptions{
|
||||
appClient: appClient,
|
||||
}
|
||||
}
|
||||
|
||||
// Complete completes DeleteOptions after they've been created
|
||||
func (o *DeleteOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
o.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.appName = o.GetApplication()
|
||||
if len(args) == 1 {
|
||||
// If app name passed, consider it for deletion
|
||||
o.appName = args[0]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Validate validates the DeleteOptions based on completed values
|
||||
func (o *DeleteOptions) Validate() (err error) {
|
||||
if o.Context.GetProject() == "" || o.appName == "" {
|
||||
return odoUtil.ThrowContextError()
|
||||
}
|
||||
|
||||
exist, err := o.appClient.Exists(o.appName)
|
||||
if !exist {
|
||||
return fmt.Errorf("%s app does not exists", o.appName)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Run contains the logic for the odo command
|
||||
func (o *DeleteOptions) Run() (err error) {
|
||||
if o.IsJSON() {
|
||||
return o.appClient.Delete(o.appName)
|
||||
}
|
||||
|
||||
// Print App Information which will be deleted
|
||||
err = printAppInfo(o.appClient, o.appName, o.GetProject())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if o.forceFlag || ui.Proceed(fmt.Sprintf("Are you sure you want to delete the application: %v from project: %v", o.appName, o.GetProject())) {
|
||||
err = o.appClient.Delete(o.appName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Infof("Deleted application: %s from project: %v", o.appName, o.GetProject())
|
||||
} else {
|
||||
log.Infof("Aborting deletion of application: %v", o.appName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// printAppInfo will print information about the app requested for deletion
|
||||
func printAppInfo(appClient application.Client, appName string, projectName string) error {
|
||||
components, err := appClient.ComponentList(appName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(components) != 0 {
|
||||
log.Info("This application has following components that will be deleted")
|
||||
for _, currentComponent := range components {
|
||||
log.Info("component named", currentComponent.Name)
|
||||
|
||||
if len(currentComponent.Spec.URL) != 0 {
|
||||
log.Info("This component has following urls that will be deleted with component")
|
||||
for _, u := range currentComponent.Spec.URLSpec {
|
||||
log.Info("URL named", u.GetName(), "with host", u.Spec.Host, "having protocol", u.Spec.Protocol, "at port", u.Spec.Port)
|
||||
}
|
||||
}
|
||||
|
||||
if len(currentComponent.Spec.Storage) != 0 {
|
||||
log.Info("The component has following storages which will be deleted with the component")
|
||||
for _, storage := range currentComponent.Spec.StorageSpec {
|
||||
store := storage
|
||||
log.Info("Storage named", store.GetName(), "of size", store.Spec.Size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewCmdDelete implements the odo command.
|
||||
func NewCmdDelete(name, fullName string) *cobra.Command {
|
||||
// The error is not handled at this point, it will be handled during Context creation
|
||||
kubclient, _ := kclient.New()
|
||||
o := NewDeleteOptions(application.NewClient(kubclient))
|
||||
command := &cobra.Command{
|
||||
Use: name,
|
||||
Short: "Delete the given application",
|
||||
Long: "Delete the given application",
|
||||
Example: fmt.Sprintf(deleteExample, fullName),
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(o, cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
command.Flags().BoolVarP(&o.forceFlag, "force", "f", false, "Delete application without prompting")
|
||||
|
||||
project.AddProjectFlag(command)
|
||||
completion.RegisterCommandHandler(command, completion.AppCompletionHandler)
|
||||
return command
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/application"
|
||||
"github.com/redhat-developer/odo/pkg/envinfo"
|
||||
"github.com/redhat-developer/odo/pkg/kclient"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
|
||||
prefixDir, err := os.MkdirTemp(os.TempDir(), "unittests-")
|
||||
if err != nil {
|
||||
t.Errorf("Error creating temp directory for tests")
|
||||
return
|
||||
}
|
||||
workingDir := filepath.Join(prefixDir, "myapp")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
populateWorkingDir func(fs filesystem.Filesystem)
|
||||
args []string
|
||||
existingApps []string
|
||||
wantAppName string
|
||||
wantErrValidate string
|
||||
}{
|
||||
{
|
||||
name: "default app",
|
||||
populateWorkingDir: func(fs filesystem.Filesystem) {
|
||||
_ = fs.MkdirAll(filepath.Join(prefixDir, "myapp", ".odo", "env"), 0755)
|
||||
env, err := envinfo.NewEnvSpecificInfo(filepath.Join(prefixDir, "myapp"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = env.SetComponentSettings(envinfo.ComponentSettings{
|
||||
Name: "a-name",
|
||||
Project: "a-project",
|
||||
AppName: "an-app-name",
|
||||
})
|
||||
},
|
||||
existingApps: []string{"an-app-name", "another-app-name"},
|
||||
wantAppName: "an-app-name",
|
||||
},
|
||||
{
|
||||
name: "app from args",
|
||||
populateWorkingDir: func(fs filesystem.Filesystem) {
|
||||
_ = fs.MkdirAll(filepath.Join(prefixDir, "myapp", ".odo", "env"), 0755)
|
||||
env, err := envinfo.NewEnvSpecificInfo(filepath.Join(prefixDir, "myapp"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = env.SetComponentSettings(envinfo.ComponentSettings{
|
||||
Name: "a-name",
|
||||
Project: "a-project",
|
||||
AppName: "an-app-name",
|
||||
})
|
||||
},
|
||||
args: []string{"another-app-name"},
|
||||
existingApps: []string{"an-app-name", "another-app-name"},
|
||||
wantAppName: "another-app-name",
|
||||
},
|
||||
{
|
||||
name: "empty app name",
|
||||
populateWorkingDir: func(fs filesystem.Filesystem) {
|
||||
_ = fs.MkdirAll(filepath.Join(prefixDir, "myapp", ".odo", "env"), 0755)
|
||||
env, err := envinfo.NewEnvSpecificInfo(filepath.Join(prefixDir, "myapp"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = env.SetComponentSettings(envinfo.ComponentSettings{
|
||||
Name: "a-name",
|
||||
Project: "a-project",
|
||||
AppName: "",
|
||||
})
|
||||
},
|
||||
existingApps: []string{"an-app-name", "another-app-name"},
|
||||
wantAppName: "",
|
||||
wantErrValidate: "Please specify the application name and project name",
|
||||
},
|
||||
{
|
||||
name: "non existing app name",
|
||||
populateWorkingDir: func(fs filesystem.Filesystem) {
|
||||
_ = fs.MkdirAll(filepath.Join(prefixDir, "myapp", ".odo", "env"), 0755)
|
||||
env, err := envinfo.NewEnvSpecificInfo(filepath.Join(prefixDir, "myapp"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = env.SetComponentSettings(envinfo.ComponentSettings{
|
||||
Name: "a-name",
|
||||
Project: "a-project",
|
||||
AppName: "an-app-name",
|
||||
})
|
||||
},
|
||||
args: []string{"an-unknown-app-name"},
|
||||
existingApps: []string{"an-app-name", "another-app-name"},
|
||||
wantAppName: "an-unknown-app-name",
|
||||
wantErrValidate: " app does not exists",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// the first one is to cleanup the directory before execution (in case there are remaining files from a previous execution)
|
||||
os.RemoveAll(prefixDir)
|
||||
// the second one to cleanup after execution
|
||||
defer os.RemoveAll(prefixDir)
|
||||
|
||||
// Fake Cobra
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
cmdline := cmdline.NewMockCmdline(ctrl)
|
||||
|
||||
// Fake odo Kube client
|
||||
kclient := kclient.NewMockClientInterface(ctrl)
|
||||
|
||||
/* Mocks for Complete */
|
||||
cmdline.EXPECT().GetWorkingDirectory().Return(workingDir, nil).AnyTimes()
|
||||
cmdline.EXPECT().CheckIfConfigurationNeeded().Return(false, nil).AnyTimes()
|
||||
cmdline.EXPECT().FlagValueIfSet("project").Return("").AnyTimes()
|
||||
cmdline.EXPECT().FlagValueIfSet("app").Return("").AnyTimes()
|
||||
cmdline.EXPECT().FlagValueIfSet("component").Return("").AnyTimes()
|
||||
cmdline.EXPECT().FlagValueIfSet("o").Return("").AnyTimes()
|
||||
cmdline.EXPECT().GetKubeClient().Return(kclient, nil).AnyTimes()
|
||||
|
||||
ns := &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "a-project",
|
||||
},
|
||||
}
|
||||
kclient.EXPECT().GetNamespaceNormal("a-project").Return(ns, nil).AnyTimes()
|
||||
kclient.EXPECT().SetNamespace("a-project").AnyTimes()
|
||||
|
||||
tt.populateWorkingDir(filesystem.DefaultFs{})
|
||||
|
||||
/* Mocks for Complete */
|
||||
appClient := application.NewMockClient(ctrl)
|
||||
appClient.EXPECT().Exists(tt.wantAppName).Return(func() bool {
|
||||
for _, app := range tt.existingApps {
|
||||
if tt.wantAppName == app {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}(), nil).AnyTimes()
|
||||
appClient.EXPECT().ComponentList(tt.wantAppName).AnyTimes()
|
||||
opts := NewDeleteOptions(appClient)
|
||||
// Force to disable interactive confirmation
|
||||
opts.forceFlag = true
|
||||
|
||||
/* COMPLETE */
|
||||
err := opts.Complete(cmdline, tt.args)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil error, got %s", err)
|
||||
return
|
||||
}
|
||||
if opts.appName != tt.wantAppName {
|
||||
t.Errorf("Got appName %q, expected %q", opts.appName, tt.wantAppName)
|
||||
}
|
||||
|
||||
/* VALIDATE */
|
||||
err = opts.Validate()
|
||||
|
||||
if err == nil && tt.wantErrValidate != "" {
|
||||
t.Errorf("Expected %v, got no error", tt.wantErrValidate)
|
||||
return
|
||||
}
|
||||
if err != nil && tt.wantErrValidate == "" {
|
||||
t.Errorf("Expected no error, got %v", err.Error())
|
||||
return
|
||||
}
|
||||
if err != nil && tt.wantErrValidate != "" && !strings.Contains(err.Error(), tt.wantErrValidate) {
|
||||
t.Errorf("Expected error %v, got %v", tt.wantErrValidate, err.Error())
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
/* Mocks for Run */
|
||||
kclient.EXPECT().GetDeploymentFromSelector(fmt.Sprintf("app=%s,app.kubernetes.io/managed-by=odo,app.kubernetes.io/part-of=%s", tt.wantAppName, tt.wantAppName)).AnyTimes()
|
||||
appClient.EXPECT().Delete(tt.wantAppName).Times(1)
|
||||
|
||||
/* RUN */
|
||||
err = opts.Run()
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil err, got %s", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/application"
|
||||
"github.com/redhat-developer/odo/pkg/kclient"
|
||||
"github.com/redhat-developer/odo/pkg/machineoutput"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/project"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
"github.com/redhat-developer/odo/pkg/odo/util"
|
||||
"github.com/redhat-developer/odo/pkg/odo/util/completion"
|
||||
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
const describeRecommendedCommandName = "describe"
|
||||
|
||||
var (
|
||||
describeExample = ktemplates.Examples(` # Describe 'webapp' application
|
||||
%[1]s webapp`)
|
||||
)
|
||||
|
||||
// DescribeOptions encapsulates the options for the odo command
|
||||
type DescribeOptions struct {
|
||||
// Context
|
||||
*genericclioptions.Context
|
||||
|
||||
// Clients
|
||||
appClient application.Client
|
||||
|
||||
// Parameters
|
||||
appName string
|
||||
}
|
||||
|
||||
// NewDescribeOptions creates a new DescribeOptions instance
|
||||
func NewDescribeOptions(appClient application.Client) *DescribeOptions {
|
||||
return &DescribeOptions{
|
||||
appClient: appClient,
|
||||
}
|
||||
}
|
||||
|
||||
// Complete completes DescribeOptions after they've been created
|
||||
func (o *DescribeOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
o.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.appName = o.GetApplication()
|
||||
if len(args) == 1 {
|
||||
o.appName = args[0]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Validate validates the DescribeOptions based on completed values
|
||||
func (o *DescribeOptions) Validate() (err error) {
|
||||
if o.Context.GetProject() == "" || o.appName == "" {
|
||||
return util.ThrowContextError()
|
||||
}
|
||||
|
||||
exist, err := o.appClient.Exists(o.appName)
|
||||
if !exist {
|
||||
return fmt.Errorf("%s app does not exists", o.appName)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Run contains the logic for the odo command
|
||||
func (o *DescribeOptions) Run() (err error) {
|
||||
if o.IsJSON() {
|
||||
appDef := o.appClient.GetMachineReadableFormat(o.appName, o.GetProject())
|
||||
machineoutput.OutputSuccess(appDef)
|
||||
return nil
|
||||
}
|
||||
|
||||
componentList, err := o.appClient.ComponentList(o.appName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(componentList) == 0 {
|
||||
fmt.Printf("Application %s has no components or services deployed.", o.appName)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Application Name: %s has %v component(s):\n--------------------------------------\n",
|
||||
o.appName, len(componentList))
|
||||
for _, currentComponent := range componentList {
|
||||
err := util.PrintComponentInfo(o.KClient, currentComponent.Name, currentComponent, o.appName, o.GetProject())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("--------------------------------------")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewCmdDescribe implements the odo command.
|
||||
func NewCmdDescribe(name, fullName string) *cobra.Command {
|
||||
// The error is not handled at this point, it will be handled during Context creation
|
||||
kubclient, _ := kclient.New()
|
||||
o := NewDescribeOptions(application.NewClient(kubclient))
|
||||
command := &cobra.Command{
|
||||
Use: fmt.Sprintf("%s [application_name]", name),
|
||||
Short: "Describe the given application",
|
||||
Long: "Describe the given application",
|
||||
Example: fmt.Sprintf(describeExample, fullName),
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
Annotations: map[string]string{"machineoutput": "json"},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(o, cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
completion.RegisterCommandHandler(command, completion.AppCompletionHandler)
|
||||
|
||||
project.AddProjectFlag(command)
|
||||
return command
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/application"
|
||||
"github.com/redhat-developer/odo/pkg/envinfo"
|
||||
"github.com/redhat-developer/odo/pkg/kclient"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestDescribe(t *testing.T) {
|
||||
|
||||
prefixDir, err := os.MkdirTemp(os.TempDir(), "unittests-")
|
||||
if err != nil {
|
||||
t.Errorf("Error creating temp directory for tests")
|
||||
return
|
||||
}
|
||||
workingDir := filepath.Join(prefixDir, "myapp")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
populateWorkingDir func(fs filesystem.Filesystem)
|
||||
args []string
|
||||
existingApps []string
|
||||
wantAppName string
|
||||
wantErrValidate string
|
||||
}{
|
||||
{
|
||||
name: "default app",
|
||||
populateWorkingDir: func(fs filesystem.Filesystem) {
|
||||
_ = fs.MkdirAll(filepath.Join(prefixDir, "myapp", ".odo", "env"), 0755)
|
||||
env, err := envinfo.NewEnvSpecificInfo(filepath.Join(prefixDir, "myapp"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = env.SetComponentSettings(envinfo.ComponentSettings{
|
||||
Name: "a-name",
|
||||
Project: "a-project",
|
||||
AppName: "an-app-name",
|
||||
})
|
||||
},
|
||||
existingApps: []string{"an-app-name", "another-app-name"},
|
||||
wantAppName: "an-app-name",
|
||||
},
|
||||
{
|
||||
name: "app from args",
|
||||
populateWorkingDir: func(fs filesystem.Filesystem) {
|
||||
_ = fs.MkdirAll(filepath.Join(prefixDir, "myapp", ".odo", "env"), 0755)
|
||||
env, err := envinfo.NewEnvSpecificInfo(filepath.Join(prefixDir, "myapp"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = env.SetComponentSettings(envinfo.ComponentSettings{
|
||||
Name: "a-name",
|
||||
Project: "a-project",
|
||||
AppName: "an-app-name",
|
||||
})
|
||||
},
|
||||
args: []string{"another-app-name"},
|
||||
existingApps: []string{"an-app-name", "another-app-name"},
|
||||
wantAppName: "another-app-name",
|
||||
},
|
||||
{
|
||||
name: "empty app name",
|
||||
populateWorkingDir: func(fs filesystem.Filesystem) {
|
||||
_ = fs.MkdirAll(filepath.Join(prefixDir, "myapp", ".odo", "env"), 0755)
|
||||
env, err := envinfo.NewEnvSpecificInfo(filepath.Join(prefixDir, "myapp"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = env.SetComponentSettings(envinfo.ComponentSettings{
|
||||
Name: "a-name",
|
||||
Project: "a-project",
|
||||
AppName: "",
|
||||
})
|
||||
},
|
||||
existingApps: []string{"an-app-name", "another-app-name"},
|
||||
wantAppName: "",
|
||||
wantErrValidate: "Please specify the application name and project name",
|
||||
},
|
||||
{
|
||||
name: "non existing app name",
|
||||
populateWorkingDir: func(fs filesystem.Filesystem) {
|
||||
_ = fs.MkdirAll(filepath.Join(prefixDir, "myapp", ".odo", "env"), 0755)
|
||||
env, err := envinfo.NewEnvSpecificInfo(filepath.Join(prefixDir, "myapp"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = env.SetComponentSettings(envinfo.ComponentSettings{
|
||||
Name: "a-name",
|
||||
Project: "a-project",
|
||||
AppName: "an-app-name",
|
||||
})
|
||||
},
|
||||
args: []string{"an-unknown-app-name"},
|
||||
existingApps: []string{"an-app-name", "another-app-name"},
|
||||
wantAppName: "an-unknown-app-name",
|
||||
wantErrValidate: " app does not exists",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// the first one is to cleanup the directory before execution (in case there are remaining files from a previous execution)
|
||||
os.RemoveAll(prefixDir)
|
||||
// the second one to cleanup after execution
|
||||
defer os.RemoveAll(prefixDir)
|
||||
|
||||
// Fake Cobra
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
cmdline := cmdline.NewMockCmdline(ctrl)
|
||||
|
||||
// Fake odo Kube client
|
||||
kclient := kclient.NewMockClientInterface(ctrl)
|
||||
|
||||
/* Mocks for Complete */
|
||||
cmdline.EXPECT().GetWorkingDirectory().Return(workingDir, nil).AnyTimes()
|
||||
cmdline.EXPECT().CheckIfConfigurationNeeded().Return(false, nil).AnyTimes()
|
||||
cmdline.EXPECT().FlagValueIfSet("project").Return("").AnyTimes()
|
||||
cmdline.EXPECT().FlagValueIfSet("app").Return("").AnyTimes()
|
||||
cmdline.EXPECT().FlagValueIfSet("component").Return("").AnyTimes()
|
||||
cmdline.EXPECT().FlagValueIfSet("o").Return("").AnyTimes()
|
||||
cmdline.EXPECT().GetKubeClient().Return(kclient, nil).AnyTimes()
|
||||
|
||||
ns := &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "a-project",
|
||||
},
|
||||
}
|
||||
kclient.EXPECT().GetNamespaceNormal("a-project").Return(ns, nil).AnyTimes()
|
||||
kclient.EXPECT().SetNamespace("a-project").AnyTimes()
|
||||
|
||||
tt.populateWorkingDir(filesystem.DefaultFs{})
|
||||
|
||||
/* Mocks for Complete */
|
||||
appClient := application.NewMockClient(ctrl)
|
||||
appClient.EXPECT().Exists(tt.wantAppName).Return(func() bool {
|
||||
for _, app := range tt.existingApps {
|
||||
if tt.wantAppName == app {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}(), nil).AnyTimes()
|
||||
opts := NewDescribeOptions(appClient)
|
||||
|
||||
/* COMPLETE */
|
||||
err := opts.Complete(cmdline, tt.args)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if opts.appName != tt.wantAppName {
|
||||
t.Errorf("Got appName %q, expected %q", opts.appName, tt.wantAppName)
|
||||
}
|
||||
|
||||
/* VALIDATE */
|
||||
err = opts.Validate()
|
||||
|
||||
if err == nil && tt.wantErrValidate != "" {
|
||||
t.Errorf("Expected %v, got no error", tt.wantErrValidate)
|
||||
return
|
||||
}
|
||||
if err != nil && tt.wantErrValidate == "" {
|
||||
t.Errorf("Expected no error, got %v", err.Error())
|
||||
return
|
||||
}
|
||||
if err != nil && tt.wantErrValidate != "" && !strings.Contains(err.Error(), tt.wantErrValidate) {
|
||||
t.Errorf("Expected error %v, got %v", tt.wantErrValidate, err.Error())
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
/* Mocks for Run */
|
||||
appClient.EXPECT().ComponentList(tt.wantAppName)
|
||||
|
||||
/* RUN */
|
||||
err = opts.Run()
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil err, got %s", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/application"
|
||||
"github.com/redhat-developer/odo/pkg/kclient"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
"github.com/redhat-developer/odo/pkg/machineoutput"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/project"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
"github.com/redhat-developer/odo/pkg/odo/util"
|
||||
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
const listRecommendedCommandName = "list"
|
||||
|
||||
var (
|
||||
listExample = ktemplates.Examples(` # List all applications in the current project
|
||||
%[1]s
|
||||
|
||||
# List all applications in the specified project
|
||||
%[1]s --project myproject`)
|
||||
)
|
||||
|
||||
// ListOptions encapsulates the options for the odo command
|
||||
type ListOptions struct {
|
||||
// Context
|
||||
*genericclioptions.Context
|
||||
|
||||
// Clients
|
||||
appClient application.Client
|
||||
}
|
||||
|
||||
// NewListOptions creates a new ListOptions instance
|
||||
func NewListOptions(appClient application.Client) *ListOptions {
|
||||
return &ListOptions{
|
||||
appClient: appClient,
|
||||
}
|
||||
}
|
||||
|
||||
// Complete completes ListOptions after they've been created
|
||||
func (o *ListOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
o.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline))
|
||||
return err
|
||||
}
|
||||
|
||||
// Validate validates the ListOptions based on completed values
|
||||
func (o *ListOptions) Validate() (err error) {
|
||||
// list doesn't need the app name
|
||||
if o.Context.GetProject() == "" {
|
||||
return util.ThrowContextError()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run contains the logic for the odo command
|
||||
func (o *ListOptions) Run() (err error) {
|
||||
apps, err := o.appClient.List()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get list of applications: %v", err)
|
||||
}
|
||||
|
||||
if len(apps) == 0 {
|
||||
if o.IsJSON() {
|
||||
apps := o.appClient.GetMachineReadableFormatForList([]application.App{})
|
||||
machineoutput.OutputSuccess(apps)
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Infof("There are no applications deployed in the project '%v'", o.GetProject())
|
||||
return nil
|
||||
}
|
||||
|
||||
if o.IsJSON() {
|
||||
var appList []application.App
|
||||
for _, app := range apps {
|
||||
appDef := o.appClient.GetMachineReadableFormat(app, o.GetProject())
|
||||
appList = append(appList, appDef)
|
||||
}
|
||||
|
||||
appListDef := o.appClient.GetMachineReadableFormatForList(appList)
|
||||
machineoutput.OutputSuccess(appListDef)
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Infof("The project '%v' has the following applications:", o.GetProject())
|
||||
tabWriter := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent)
|
||||
_, err = fmt.Fprintln(tabWriter, "NAME")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, app := range apps {
|
||||
_, err := fmt.Fprintln(tabWriter, app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return tabWriter.Flush()
|
||||
|
||||
}
|
||||
|
||||
// NewCmdList implements the odo command.
|
||||
func NewCmdList(name, fullName string) *cobra.Command {
|
||||
// The error is not handled at this point, it will be handled during Context creation
|
||||
kubclient, _ := kclient.New()
|
||||
o := NewListOptions(application.NewClient(kubclient))
|
||||
command := &cobra.Command{
|
||||
Use: name,
|
||||
Short: "List all applications in the current project",
|
||||
Long: "List all applications in the current project",
|
||||
Example: fmt.Sprintf(listExample, fullName),
|
||||
Args: cobra.NoArgs,
|
||||
Annotations: map[string]string{"machineoutput": "json"},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(o, cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
project.AddProjectFlag(command)
|
||||
return command
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/application"
|
||||
"github.com/redhat-developer/odo/pkg/envinfo"
|
||||
"github.com/redhat-developer/odo/pkg/kclient"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
|
||||
prefixDir, err := os.MkdirTemp(os.TempDir(), "unittests-")
|
||||
if err != nil {
|
||||
t.Errorf("Error creating temp directory for tests")
|
||||
return
|
||||
}
|
||||
workingDir := filepath.Join(prefixDir, "myapp")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
populateWorkingDir func(fs filesystem.Filesystem)
|
||||
currentNamespace string
|
||||
wantErrValidate string
|
||||
}{
|
||||
{
|
||||
name: "default app",
|
||||
populateWorkingDir: func(fs filesystem.Filesystem) {
|
||||
_ = fs.MkdirAll(filepath.Join(prefixDir, "myapp", ".odo", "env"), 0755)
|
||||
env, err := envinfo.NewEnvSpecificInfo(filepath.Join(prefixDir, "myapp"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = env.SetComponentSettings(envinfo.ComponentSettings{
|
||||
Name: "a-name",
|
||||
Project: "a-project",
|
||||
AppName: "an-app-name",
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty project name",
|
||||
populateWorkingDir: func(fs filesystem.Filesystem) {
|
||||
_ = fs.MkdirAll(filepath.Join(prefixDir, "myapp", ".odo", "env"), 0755)
|
||||
env, err := envinfo.NewEnvSpecificInfo(filepath.Join(prefixDir, "myapp"))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = env.SetComponentSettings(envinfo.ComponentSettings{
|
||||
Name: "a-name",
|
||||
Project: "",
|
||||
AppName: "an-app-name",
|
||||
})
|
||||
},
|
||||
currentNamespace: "",
|
||||
wantErrValidate: "Please specify the application name and project name",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// the first one is to cleanup the directory before execution (in case there are remaining files from a previous execution)
|
||||
os.RemoveAll(prefixDir)
|
||||
// the second one to cleanup after execution
|
||||
defer os.RemoveAll(prefixDir)
|
||||
|
||||
// Fake Cobra
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
cmdline := cmdline.NewMockCmdline(ctrl)
|
||||
|
||||
// Fake odo Kube client
|
||||
kclient := kclient.NewMockClientInterface(ctrl)
|
||||
|
||||
/* Mocks for Complete */
|
||||
cmdline.EXPECT().GetWorkingDirectory().Return(workingDir, nil).AnyTimes()
|
||||
cmdline.EXPECT().CheckIfConfigurationNeeded().Return(false, nil).AnyTimes()
|
||||
cmdline.EXPECT().FlagValueIfSet("project").Return("").AnyTimes()
|
||||
cmdline.EXPECT().FlagValueIfSet("app").Return("").AnyTimes()
|
||||
cmdline.EXPECT().FlagValueIfSet("component").Return("").AnyTimes()
|
||||
cmdline.EXPECT().FlagValueIfSet("o").Return("").AnyTimes()
|
||||
cmdline.EXPECT().GetName().Return("list").AnyTimes()
|
||||
cmdline.EXPECT().GetParentName().Return("application").AnyTimes()
|
||||
cmdline.EXPECT().GetKubeClient().Return(kclient, nil).AnyTimes()
|
||||
|
||||
ns := &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "a-project",
|
||||
},
|
||||
}
|
||||
kclient.EXPECT().GetNamespaceNormal("a-project").Return(ns, nil).AnyTimes()
|
||||
kclient.EXPECT().GetNamespaceNormal("").Return(nil, nil).AnyTimes()
|
||||
kclient.EXPECT().SetNamespace("a-project").AnyTimes()
|
||||
kclient.EXPECT().SetNamespace("").AnyTimes()
|
||||
kclient.EXPECT().GetCurrentNamespace().Return(tt.currentNamespace).AnyTimes()
|
||||
|
||||
tt.populateWorkingDir(filesystem.DefaultFs{})
|
||||
|
||||
/* Mocks for Complete */
|
||||
appClient := application.NewMockClient(ctrl)
|
||||
opts := NewListOptions(appClient)
|
||||
|
||||
/* COMPLETE */
|
||||
err := opts.Complete(cmdline, []string{})
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
/* VALIDATE */
|
||||
err = opts.Validate()
|
||||
|
||||
if err == nil && tt.wantErrValidate != "" {
|
||||
t.Errorf("Expected %v, got no error", tt.wantErrValidate)
|
||||
return
|
||||
}
|
||||
if err != nil && tt.wantErrValidate == "" {
|
||||
t.Errorf("Expected no error, got %v", err.Error())
|
||||
return
|
||||
}
|
||||
if err != nil && tt.wantErrValidate != "" && !strings.Contains(err.Error(), tt.wantErrValidate) {
|
||||
t.Errorf("Expected error %v, got %v", tt.wantErrValidate, err.Error())
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
/* Mocks for Run */
|
||||
appClient.EXPECT().List().Times(1)
|
||||
|
||||
/* RUN */
|
||||
err = opts.Run()
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil err, got %s", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -13,17 +13,15 @@ const RecommendedCommandName = "describe"
|
||||
// NewCmdCatalogDescribe implements the odo catalog describe command
|
||||
func NewCmdCatalogDescribe(name, fullName string) *cobra.Command {
|
||||
component := NewCmdCatalogDescribeComponent(componentRecommendedCommandName, util.GetFullName(fullName, componentRecommendedCommandName))
|
||||
service := NewCmdCatalogDescribeService(serviceRecommendedCommandName, util.GetFullName(fullName, serviceRecommendedCommandName))
|
||||
catalogDescribeCmd := &cobra.Command{
|
||||
Use: name,
|
||||
Short: "Describe catalog item",
|
||||
Long: "Describe the given catalog item from OpenShift",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Example: fmt.Sprintf("%s\n\n%s\n", component.Example, service.Example),
|
||||
Example: fmt.Sprintf("%s\n", component.Example),
|
||||
}
|
||||
catalogDescribeCmd.AddCommand(
|
||||
component,
|
||||
service,
|
||||
)
|
||||
|
||||
return catalogDescribeCmd
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package describe
|
||||
|
||||
// CatalogProviderBackend is implemented by the catalog backends supported by odo
|
||||
// It is used in "odo catalog describe service".
|
||||
type CatalogProviderBackend interface {
|
||||
// the second argument can be a list of anything that needs to be sent to populate internal
|
||||
// structs
|
||||
CompleteDescribeService(*DescribeServiceOptions, []string) error
|
||||
ValidateDescribeService(*DescribeServiceOptions) error
|
||||
RunDescribeService(*DescribeServiceOptions) error
|
||||
}
|
||||
@@ -1,345 +0,0 @@
|
||||
package describe
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
olm "github.com/operator-framework/api/pkg/operators/v1alpha1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
"github.com/redhat-developer/odo/pkg/machineoutput"
|
||||
"github.com/redhat-developer/odo/pkg/service"
|
||||
"gopkg.in/yaml.v2"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
type operatorBackend struct {
|
||||
Name string
|
||||
OperatorType string
|
||||
CustomResource string
|
||||
CSV olm.ClusterServiceVersion
|
||||
CR *olm.CRDDescription
|
||||
CRDSpec *spec.Schema
|
||||
CRDList *service.OperatorBackedServiceCRList
|
||||
}
|
||||
|
||||
func NewOperatorBackend() *operatorBackend {
|
||||
return &operatorBackend{}
|
||||
}
|
||||
|
||||
func (ohb *operatorBackend) CompleteDescribeService(dso *DescribeServiceOptions, args []string) error {
|
||||
ohb.Name = args[0]
|
||||
oprType, CR, err := service.SplitServiceKindName(ohb.Name)
|
||||
if err != nil {
|
||||
klog.V(2).Infof("could not determine csv, falling back to describing all of them")
|
||||
oprType = args[0]
|
||||
CR = ""
|
||||
}
|
||||
// we check if the cluster supports ClusterServiceVersion or not.
|
||||
isCSVSupported, err := dso.KClient.IsCSVSupported()
|
||||
if err != nil {
|
||||
// if there is an error checking it, we return the error.
|
||||
return err
|
||||
}
|
||||
// if its not supported then we return an error
|
||||
if !isCSVSupported {
|
||||
return errors.New("it seems the cluster doesn't support Operators. Please install OLM and try again")
|
||||
}
|
||||
ohb.OperatorType = oprType
|
||||
ohb.CustomResource = CR
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ohb *operatorBackend) ValidateDescribeService(dso *DescribeServiceOptions) error {
|
||||
var err error
|
||||
if ohb.OperatorType == "" {
|
||||
return errors.New("invalid service name provided. should either be <operator-type> or <operator-type>/<crd-name>")
|
||||
}
|
||||
// make sure that CSV of the specified OperatorType exists
|
||||
ohb.CSV, err = dso.KClient.GetClusterServiceVersion(ohb.OperatorType)
|
||||
if err != nil {
|
||||
// error only occurs when OperatorHub is not installed.
|
||||
// k8s does't have it installed by default but OCP does
|
||||
return err
|
||||
}
|
||||
|
||||
//if both operator type and cr are known, validate that it exists
|
||||
if ohb.OperatorType != "" && ohb.CustomResource != "" {
|
||||
var hasCR bool
|
||||
hasCR, ohb.CR = dso.KClient.CheckCustomResourceInCSV(ohb.CustomResource, &ohb.CSV)
|
||||
if !hasCR {
|
||||
return fmt.Errorf("the %q resource doesn't exist in specified %q operator", ohb.CustomResource, ohb.OperatorType)
|
||||
}
|
||||
|
||||
ohb.CRDSpec, err = dso.KClient.GetCRDSpec(ohb.CR, ohb.OperatorType, ohb.CustomResource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (ohb *operatorBackend) RunDescribeService(dso *DescribeServiceOptions) error {
|
||||
if ohb.OperatorType != "" && ohb.CustomResource == "" {
|
||||
//we don't have cr so list all possible crds
|
||||
ohb.CRDList = service.NewOperatorBackedCRList(ohb.OperatorType, ohb.CSV.Spec.DisplayName, ohb.CSV.Spec.Description)
|
||||
crds := *dso.KClient.GetCustomResourcesFromCSV(&ohb.CSV)
|
||||
for _, custRes := range crds {
|
||||
ohb.CRDList.Spec.CRDS = append(ohb.CRDList.Spec.CRDS, service.OperatorServiceCRItem{
|
||||
Kind: custRes.Kind,
|
||||
Description: custRes.Description,
|
||||
})
|
||||
}
|
||||
if log.IsJSON() {
|
||||
machineoutput.OutputSuccess(ohb.CRDList)
|
||||
} else {
|
||||
HumanReadableCRListOutput(os.Stdout, ohb.CRDList)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if dso.exampleFlag {
|
||||
almExample, err := service.GetAlmExample(ohb.CSV, ohb.CustomResource, ohb.OperatorType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if log.IsJSON() {
|
||||
jsonExample := service.NewOperatorExample(almExample)
|
||||
jsonCR, err := json.MarshalIndent(jsonExample, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(string(jsonCR))
|
||||
|
||||
} else {
|
||||
yamlCR, err := yaml.Marshal(almExample)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info(string(yamlCR))
|
||||
}
|
||||
}
|
||||
svc := service.NewOperatorBackedService(ohb.Name, ohb.CR.Kind, ohb.CR.Version, ohb.CR.Description, ohb.CR.DisplayName, ohb.CRDSpec)
|
||||
if log.IsJSON() {
|
||||
machineoutput.OutputSuccess(svc)
|
||||
} else {
|
||||
|
||||
HumanReadableOutput(os.Stdout, svc)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func HumanReadableOutput(w io.Writer, service service.OperatorBackedService) {
|
||||
fmt.Fprintf(w, "KIND: %s\n", service.Spec.Kind)
|
||||
fmt.Fprintf(w, "VERSION: %s\n", service.Spec.Version)
|
||||
fmt.Fprintf(w, "\nDESCRIPTION:\n%s", indentText(service.Spec.Description, 5))
|
||||
|
||||
if service.Spec.Schema == nil {
|
||||
log.Warningf("Unable to get parameters from CRD or CSV; Operator %q doesn't have the required information", service.Name)
|
||||
return
|
||||
}
|
||||
fmt.Fprintln(w, "\nFIELDS:")
|
||||
displayProperties(w, service.Spec.Schema, "")
|
||||
}
|
||||
|
||||
func HumanReadableCRListOutput(w io.Writer, crsList *service.OperatorBackedServiceCRList) {
|
||||
fmt.Fprintf(w, "NAME:\t%s\n", crsList.Name)
|
||||
descriptionLines := strings.ReplaceAll(crsList.Spec.Description, "\n", "\n\t")
|
||||
fmt.Fprintf(w, "DESCRIPTION:\n\n\t%s\n\n", descriptionLines)
|
||||
fmt.Fprintf(w, "CRDs:\n")
|
||||
tw := tabwriter.NewWriter(w, 4, 4, 3, ' ', tabwriter.TabIndent)
|
||||
defer tw.Flush()
|
||||
fmt.Fprintf(tw, "\tNAME\tDESCRIPTION\n")
|
||||
for _, it := range crsList.Spec.CRDS {
|
||||
fmt.Fprintf(tw, "\t%s\t%s\n", it.Kind, it.Description)
|
||||
}
|
||||
}
|
||||
|
||||
// displayProperties displays the properties of an OpenAPI schema in a human readable form
|
||||
// required fields are displayed first
|
||||
func displayProperties(w io.Writer, schema *spec.Schema, prefix string) {
|
||||
required := schema.Required
|
||||
requiredMap := map[string]bool{}
|
||||
for _, req := range required {
|
||||
requiredMap[req] = true
|
||||
}
|
||||
|
||||
reqKeys := []string{}
|
||||
for key := range schema.Properties {
|
||||
if requiredMap[key] {
|
||||
reqKeys = append(reqKeys, key)
|
||||
}
|
||||
}
|
||||
sort.Strings(reqKeys)
|
||||
|
||||
nonReqKeys := []string{}
|
||||
for key := range schema.Properties {
|
||||
if !requiredMap[key] {
|
||||
nonReqKeys = append(nonReqKeys, key)
|
||||
}
|
||||
}
|
||||
sort.Strings(nonReqKeys)
|
||||
keys := append(reqKeys, nonReqKeys...)
|
||||
|
||||
for _, key := range keys {
|
||||
property := schema.Properties[key]
|
||||
requiredInfo := ""
|
||||
if requiredMap[key] {
|
||||
requiredInfo = "-required-"
|
||||
}
|
||||
fmt.Fprintf(w, "%s%s (%s) %s\n", strings.Repeat(" ", 3+2*strings.Count(prefix, ".")), prefix+key, getTypeString(property), requiredInfo)
|
||||
nl := false
|
||||
if len(property.Title) > 0 {
|
||||
fmt.Fprintf(w, "%s\n", indentText(property.Title, 5+2*strings.Count(prefix, ".")))
|
||||
nl = true
|
||||
}
|
||||
if len(property.Description) > 0 {
|
||||
fmt.Fprintf(w, "%s\n", indentText(property.Description, 5+2*strings.Count(prefix, ".")))
|
||||
nl = true
|
||||
}
|
||||
if !nl {
|
||||
fmt.Fprintln(w)
|
||||
}
|
||||
if property.Type.Contains("object") {
|
||||
displayProperties(w, &property, prefix+key+".")
|
||||
} else if property.Type.Contains("array") && property.Items.Schema.Type.Contains("object") {
|
||||
displayProperties(w, property.Items.Schema, prefix+key+".*.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getTypeString(property spec.Schema) string {
|
||||
if len(property.Type) != 1 {
|
||||
// should not happen
|
||||
return strings.Join(property.Type, ", ")
|
||||
}
|
||||
tpe := property.Type[0]
|
||||
if tpe == "array" {
|
||||
tpe = "[]" + getTypeString(*property.Items.Schema)
|
||||
}
|
||||
return tpe
|
||||
}
|
||||
|
||||
func indentText(t string, indent int) string {
|
||||
lines := wrapString(t, 80-indent)
|
||||
res := ""
|
||||
for _, line := range lines {
|
||||
res += strings.Repeat(" ", indent) + line + "\n"
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Following code from https://github.com/kubernetes/kubectl/blob/159a770147fb28337c6807abb1b2b9db843d0aff/pkg/explain/formatter.go
|
||||
|
||||
type line struct {
|
||||
wrap int
|
||||
words []string
|
||||
}
|
||||
|
||||
func (l *line) String() string {
|
||||
return strings.Join(l.words, " ")
|
||||
}
|
||||
|
||||
func (l *line) Empty() bool {
|
||||
return len(l.words) == 0
|
||||
}
|
||||
|
||||
func (l *line) Len() int {
|
||||
return len(l.String())
|
||||
}
|
||||
|
||||
// Add adds the word to the line, returns true if we could, false if we
|
||||
// didn't have enough room. It's always possible to add to an empty line.
|
||||
func (l *line) Add(word string) bool {
|
||||
newLine := line{
|
||||
wrap: l.wrap,
|
||||
words: append(l.words, word),
|
||||
}
|
||||
if newLine.Len() <= l.wrap || len(l.words) == 0 {
|
||||
l.words = newLine.words
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func wrapString(str string, wrap int) []string {
|
||||
wrapped := []string{}
|
||||
l := line{wrap: wrap}
|
||||
// track the last word added to the current line
|
||||
lastWord := ""
|
||||
flush := func() {
|
||||
if !l.Empty() {
|
||||
lastWord = ""
|
||||
wrapped = append(wrapped, l.String())
|
||||
l = line{wrap: wrap}
|
||||
}
|
||||
}
|
||||
|
||||
// iterate over the lines in the original description
|
||||
for _, str := range strings.Split(str, "\n") {
|
||||
// preserve code blocks and blockquotes as-is
|
||||
if strings.HasPrefix(str, " ") {
|
||||
flush()
|
||||
wrapped = append(wrapped, str)
|
||||
continue
|
||||
}
|
||||
|
||||
// preserve empty lines after the first line, since they can separate logical sections
|
||||
if len(wrapped) > 0 && len(strings.TrimSpace(str)) == 0 {
|
||||
flush()
|
||||
wrapped = append(wrapped, "")
|
||||
continue
|
||||
}
|
||||
|
||||
// flush if we should start a new line
|
||||
if shouldStartNewLine(lastWord, str) {
|
||||
flush()
|
||||
}
|
||||
words := strings.Fields(str)
|
||||
for _, word := range words {
|
||||
lastWord = word
|
||||
if !l.Add(word) {
|
||||
flush()
|
||||
if !l.Add(word) {
|
||||
panic("Couldn't add to empty line.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
flush()
|
||||
return wrapped
|
||||
}
|
||||
|
||||
var bullet = regexp.MustCompile(`^(\d+\.?|-|\*)\s`)
|
||||
|
||||
func shouldStartNewLine(lastWord, str string) bool {
|
||||
// preserve line breaks ending in :
|
||||
if strings.HasSuffix(lastWord, ":") {
|
||||
return true
|
||||
}
|
||||
|
||||
// preserve code blocks
|
||||
if strings.HasPrefix(str, " ") {
|
||||
return true
|
||||
}
|
||||
str = strings.TrimSpace(str)
|
||||
// preserve empty lines
|
||||
if len(str) == 0 {
|
||||
return true
|
||||
}
|
||||
// preserve lines that look like they're starting lists
|
||||
if bullet.MatchString(str) {
|
||||
return true
|
||||
}
|
||||
// otherwise combine
|
||||
return false
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package describe
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
)
|
||||
|
||||
func TestGetTypeString(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
property spec.Schema
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "string type",
|
||||
property: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
},
|
||||
},
|
||||
want: "string",
|
||||
},
|
||||
{
|
||||
name: "array of strings type",
|
||||
property: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: "[]string",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := getTypeString(tt.property)
|
||||
if result != tt.want {
|
||||
t.Errorf("Failed %s: got: %q, want: %q", t.Name(), result, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package describe
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
"github.com/spf13/cobra"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
const serviceRecommendedCommandName = "service"
|
||||
|
||||
var (
|
||||
serviceExample = ktemplates.Examples(`# Describe a Operator backed service
|
||||
%[1]s
|
||||
`)
|
||||
|
||||
serviceLongDesc = ktemplates.LongDesc(`Describes a service type.
|
||||
This command supports Operator backed services.
|
||||
A user can describe an Operator backed service by providing the full identifier for an Operand i.e. <operator_type>/<cr_name> which they can find by running "odo catalog list services".
|
||||
`)
|
||||
)
|
||||
|
||||
// DescribeServiceOptions encapsulates the options for the odo catalog describe service command
|
||||
type DescribeServiceOptions struct {
|
||||
// Context
|
||||
*genericclioptions.Context
|
||||
|
||||
// Flags
|
||||
exampleFlag bool
|
||||
|
||||
// Service backend
|
||||
backend CatalogProviderBackend
|
||||
}
|
||||
|
||||
// NewDescribeServiceOptions creates a new DescribeServiceOptions instance
|
||||
func NewDescribeServiceOptions() *DescribeServiceOptions {
|
||||
return &DescribeServiceOptions{}
|
||||
}
|
||||
|
||||
// Complete completes DescribeServiceOptions after they've been created
|
||||
func (o *DescribeServiceOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
o.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//we initialize operator backend regardless of if we can split name or not. Decision
|
||||
//to describe crs or not will be taken later
|
||||
o.backend = NewOperatorBackend()
|
||||
return o.backend.CompleteDescribeService(o, args)
|
||||
}
|
||||
|
||||
// Validate validates the DescribeServiceOptions based on completed values
|
||||
func (o *DescribeServiceOptions) Validate() (err error) {
|
||||
return o.backend.ValidateDescribeService(o)
|
||||
|
||||
}
|
||||
|
||||
// Run contains the logic for the command associated with DescribeServiceOptions
|
||||
func (o *DescribeServiceOptions) Run() (err error) {
|
||||
return o.backend.RunDescribeService(o)
|
||||
}
|
||||
|
||||
// NewCmdCatalogDescribeService implements the odo catalog describe service command
|
||||
func NewCmdCatalogDescribeService(name, fullName string) *cobra.Command {
|
||||
o := NewDescribeServiceOptions()
|
||||
command := &cobra.Command{
|
||||
Use: name,
|
||||
Short: "Describe a service",
|
||||
Long: serviceLongDesc,
|
||||
Example: fmt.Sprintf(serviceExample, fullName),
|
||||
Annotations: map[string]string{"machineoutput": "json"},
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(o, cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
command.Flags().BoolVarP(&o.exampleFlag, "example", "e", false, "Show an example of the service")
|
||||
|
||||
return command
|
||||
}
|
||||
@@ -13,18 +13,16 @@ const RecommendedCommandName = "list"
|
||||
// NewCmdCatalogList implements the odo catalog list command
|
||||
func NewCmdCatalogList(name, fullName string) *cobra.Command {
|
||||
components := NewCmdCatalogListComponents(componentsRecommendedCommandName, util.GetFullName(fullName, componentsRecommendedCommandName))
|
||||
services := NewCmdCatalogListServices(servicesRecommendedCommandName, util.GetFullName(fullName, servicesRecommendedCommandName))
|
||||
|
||||
catalogListCmd := &cobra.Command{
|
||||
Use: name,
|
||||
Short: "List all available component & service types.",
|
||||
Long: "List all available component and service types from OpenShift",
|
||||
Example: fmt.Sprintf("%s\n\n%s\n", components.Example, services.Example),
|
||||
Short: "List all available component types.",
|
||||
Long: "List all available component types from OpenShift",
|
||||
Example: fmt.Sprintf("%s\n", components.Example),
|
||||
}
|
||||
|
||||
catalogListCmd.AddCommand(
|
||||
components,
|
||||
services,
|
||||
)
|
||||
|
||||
return catalogListCmd
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
package list
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
olm "github.com/operator-framework/api/pkg/operators/v1alpha1"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
"github.com/redhat-developer/odo/pkg/machineoutput"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/catalog/util"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
"github.com/redhat-developer/odo/pkg/service"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const servicesRecommendedCommandName = "services"
|
||||
|
||||
var servicesExample = ` # Get the supported services
|
||||
%[1]s`
|
||||
|
||||
// ServiceOptions encapsulates the options for the odo catalog list services command
|
||||
type ServiceOptions struct {
|
||||
// Context
|
||||
*genericclioptions.Context
|
||||
|
||||
// list of clusterserviceversions (installed by Operators)
|
||||
csvs *olm.ClusterServiceVersionList
|
||||
}
|
||||
|
||||
// NewServiceOptions creates a new ListServicesOptions instance
|
||||
func NewServiceOptions() *ServiceOptions {
|
||||
return &ServiceOptions{}
|
||||
}
|
||||
|
||||
// Complete completes ListServicesOptions after they've been created
|
||||
func (o *ServiceOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
o.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.csvs, err = service.ListSucceededClusterServiceVersions(o.KClient)
|
||||
if err != nil && !strings.Contains(err.Error(), "could not find specified operator") {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates the ListServicesOptions based on completed values
|
||||
func (o *ServiceOptions) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run contains the logic for the command associated with ListServicesOptions
|
||||
func (o *ServiceOptions) Run() error {
|
||||
if log.IsJSON() {
|
||||
machineoutput.OutputSuccess(newCatalogListOutput(o.csvs))
|
||||
} else {
|
||||
if len(o.csvs.Items) == 0 {
|
||||
log.Info("no deployable operators found")
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(o.csvs.Items) > 0 {
|
||||
util.DisplayClusterServiceVersions(o.csvs)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewCmdCatalogListServices implements the odo catalog list services command
|
||||
func NewCmdCatalogListServices(name, fullName string) *cobra.Command {
|
||||
o := NewServiceOptions()
|
||||
return &cobra.Command{
|
||||
Use: name,
|
||||
Short: "Lists all available services",
|
||||
Long: "Lists all available services",
|
||||
Example: fmt.Sprintf(servicesExample, fullName),
|
||||
Args: cobra.ExactArgs(0),
|
||||
Annotations: map[string]string{"machineoutput": "json"},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(o, cmd, args)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type catalogListOutput struct {
|
||||
v1.TypeMeta `json:",inline"`
|
||||
v1.ObjectMeta `json:"metadata,omitempty"`
|
||||
// list of clusterserviceversions (installed by Operators)
|
||||
Operators *olm.ClusterServiceVersionList `json:"operators,omitempty"`
|
||||
}
|
||||
|
||||
func newCatalogListOutput(operators *olm.ClusterServiceVersionList) catalogListOutput {
|
||||
return catalogListOutput{
|
||||
TypeMeta: v1.TypeMeta{
|
||||
Kind: "List",
|
||||
APIVersion: machineoutput.APIVersion,
|
||||
},
|
||||
Operators: operators,
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@ const RecommendedCommandName = "search"
|
||||
// NewCmdCatalogSearch implements the odo catalog search command
|
||||
func NewCmdCatalogSearch(name, fullName string) *cobra.Command {
|
||||
component := NewCmdCatalogSearchComponent(componentRecommendedCommandName, util.GetFullName(fullName, componentRecommendedCommandName))
|
||||
service := NewCmdCatalogSearchService(serviceRecommendedCommandName, util.GetFullName(fullName, serviceRecommendedCommandName))
|
||||
catalogSearchCmd := &cobra.Command{
|
||||
Use: name,
|
||||
Short: "Search available component & service types.",
|
||||
@@ -22,9 +21,9 @@ func NewCmdCatalogSearch(name, fullName string) *cobra.Command {
|
||||
This searches for a partial match for the given search term in all the available
|
||||
components & services.
|
||||
`,
|
||||
Example: fmt.Sprintf("%s\n\n%s\n", component.Example, service.Example),
|
||||
Example: fmt.Sprintf("%s\n", component.Example),
|
||||
}
|
||||
catalogSearchCmd.AddCommand(component, service)
|
||||
catalogSearchCmd.AddCommand(component)
|
||||
|
||||
return catalogSearchCmd
|
||||
}
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
olm "github.com/operator-framework/api/pkg/operators/v1alpha1"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/catalog/util"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const serviceRecommendedCommandName = "service"
|
||||
|
||||
var serviceExample = ` # Search for a service
|
||||
%[1]s mysql`
|
||||
|
||||
// SearchServiceOptions encapsulates the options for the odo catalog describe service command
|
||||
type SearchServiceOptions struct {
|
||||
// Context
|
||||
*genericclioptions.Context
|
||||
|
||||
// Parameters
|
||||
searchTerm string
|
||||
|
||||
// list of clusterserviceversions (installed by Operators)
|
||||
csvs *olm.ClusterServiceVersionList
|
||||
}
|
||||
|
||||
// NewSearchServiceOptions creates a new SearchServiceOptions instance
|
||||
func NewSearchServiceOptions() *SearchServiceOptions {
|
||||
return &SearchServiceOptions{}
|
||||
}
|
||||
|
||||
// Complete completes SearchServiceOptions after they've been created
|
||||
func (o *SearchServiceOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
o.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.searchTerm = args[0]
|
||||
|
||||
o.csvs, err = o.KClient.SearchClusterServiceVersionList(o.searchTerm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to list services because Operator Hub is not enabled in your cluster: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates the SearchServiceOptions based on completed values
|
||||
func (o *SearchServiceOptions) Validate() error {
|
||||
if len(o.csvs.Items) == 0 {
|
||||
return fmt.Errorf("no service matched the query: %s", o.searchTerm)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run contains the logic for the command associated with SearchServiceOptions
|
||||
func (o *SearchServiceOptions) Run() error {
|
||||
if len(o.csvs.Items) > 0 {
|
||||
util.DisplayClusterServiceVersions(o.csvs)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewCmdCatalogSearchService implements the odo catalog search service command
|
||||
func NewCmdCatalogSearchService(name, fullName string) *cobra.Command {
|
||||
o := NewSearchServiceOptions()
|
||||
return &cobra.Command{
|
||||
Use: name,
|
||||
Short: "Search service type in catalog",
|
||||
Long: `Search service type in catalog.
|
||||
|
||||
This searches for a partial match for the given search term in all the available
|
||||
services from operator hub services.
|
||||
`,
|
||||
Example: fmt.Sprintf(serviceExample, fullName),
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(o, cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,14 +6,10 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/application"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/build_images"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/catalog"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/component"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/config"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/debug"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/deploy"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/env"
|
||||
_init "github.com/redhat-developer/odo/pkg/odo/cli/init"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/login"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/logout"
|
||||
@@ -21,8 +17,6 @@ import (
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/preference"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/project"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/registry"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/service"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/storage"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/telemetry"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/url"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/utils"
|
||||
@@ -181,34 +175,22 @@ func odoRootCmd(name, fullName string) *cobra.Command {
|
||||
cobra.AddTemplateFunc("CapitalizeFlagDescriptions", util.CapitalizeFlagDescriptions)
|
||||
cobra.AddTemplateFunc("ModifyAdditionalFlags", util.ModifyAdditionalFlags)
|
||||
|
||||
rootCmdList := append([]*cobra.Command{}, application.NewCmdApplication(application.RecommendedCommandName, util.GetFullName(fullName, application.RecommendedCommandName)),
|
||||
rootCmdList := append([]*cobra.Command{},
|
||||
catalog.NewCmdCatalog(catalog.RecommendedCommandName, util.GetFullName(fullName, catalog.RecommendedCommandName)),
|
||||
component.NewCmdComponent(component.RecommendedCommandName, util.GetFullName(fullName, component.RecommendedCommandName)),
|
||||
component.NewCmdCreate(component.CreateRecommendedCommandName, util.GetFullName(fullName, component.CreateRecommendedCommandName)),
|
||||
component.NewCmdDelete(component.DeleteRecommendedCommandName, util.GetFullName(fullName, component.DeleteRecommendedCommandName)),
|
||||
component.NewCmdDescribe(component.DescribeRecommendedCommandName, util.GetFullName(fullName, component.DescribeRecommendedCommandName)),
|
||||
component.NewCmdLink(component.LinkRecommendedCommandName, util.GetFullName(fullName, component.LinkRecommendedCommandName)),
|
||||
component.NewCmdUnlink(component.UnlinkRecommendedCommandName, util.GetFullName(fullName, component.UnlinkRecommendedCommandName)),
|
||||
component.NewCmdList(component.ListRecommendedCommandName, util.GetFullName(fullName, component.ListRecommendedCommandName)),
|
||||
component.NewCmdLog(component.LogRecommendedCommandName, util.GetFullName(fullName, component.LogRecommendedCommandName)),
|
||||
component.NewCmdPush(component.PushRecommendedCommandName, util.GetFullName(fullName, component.PushRecommendedCommandName)),
|
||||
component.NewCmdWatch(component.WatchRecommendedCommandName, util.GetFullName(fullName, component.WatchRecommendedCommandName)),
|
||||
component.NewCmdStatus(component.StatusRecommendedCommandName, util.GetFullName(fullName, component.StatusRecommendedCommandName)),
|
||||
component.NewCmdExec(component.ExecRecommendedCommandName, util.GetFullName(fullName, component.ExecRecommendedCommandName)),
|
||||
login.NewCmdLogin(login.RecommendedCommandName, util.GetFullName(fullName, login.RecommendedCommandName)),
|
||||
logout.NewCmdLogout(logout.RecommendedCommandName, util.GetFullName(fullName, logout.RecommendedCommandName)),
|
||||
project.NewCmdProject(project.RecommendedCommandName, util.GetFullName(fullName, project.RecommendedCommandName)),
|
||||
service.NewCmdService(service.RecommendedCommandName, util.GetFullName(fullName, service.RecommendedCommandName)),
|
||||
storage.NewCmdStorage(storage.RecommendedCommandName, util.GetFullName(fullName, storage.RecommendedCommandName)),
|
||||
url.NewCmdURL(url.RecommendedCommandName, util.GetFullName(fullName, url.RecommendedCommandName)),
|
||||
utils.NewCmdUtils(utils.RecommendedCommandName, util.GetFullName(fullName, utils.RecommendedCommandName)),
|
||||
version.NewCmdVersion(version.RecommendedCommandName, util.GetFullName(fullName, version.RecommendedCommandName)),
|
||||
config.NewCmdConfiguration(config.RecommendedCommandName, util.GetFullName(fullName, config.RecommendedCommandName)),
|
||||
preference.NewCmdPreference(preference.RecommendedCommandName, util.GetFullName(fullName, preference.RecommendedCommandName)),
|
||||
debug.NewCmdDebug(debug.RecommendedCommandName, util.GetFullName(fullName, debug.RecommendedCommandName)),
|
||||
registry.NewCmdRegistry(registry.RecommendedCommandName, util.GetFullName(fullName, registry.RecommendedCommandName)),
|
||||
component.NewCmdTest(component.TestRecommendedCommandName, util.GetFullName(fullName, component.TestRecommendedCommandName)),
|
||||
env.NewCmdEnv(env.RecommendedCommandName, util.GetFullName(fullName, env.RecommendedCommandName)),
|
||||
telemetry.NewCmdTelemetry(telemetry.RecommendedCommandName),
|
||||
build_images.NewCmdBuildImages(build_images.RecommendedCommandName, util.GetFullName(fullName, build_images.RecommendedCommandName)),
|
||||
deploy.NewCmdDeploy(deploy.RecommendedCommandName, util.GetFullName(fullName, deploy.RecommendedCommandName)),
|
||||
|
||||
@@ -1,390 +0,0 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/devfile"
|
||||
"github.com/redhat-developer/odo/pkg/kclient"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
"github.com/redhat-developer/odo/pkg/odo/util"
|
||||
svc "github.com/redhat-developer/odo/pkg/service"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
servicebinding "github.com/redhat-developer/service-binding-operator/apis/binding/v1alpha1"
|
||||
"gopkg.in/yaml.v2"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
const unlink = "unlink"
|
||||
|
||||
type commonLinkOptions struct {
|
||||
secretName string
|
||||
isTargetAService bool
|
||||
name string
|
||||
bindAsFiles bool
|
||||
|
||||
devfilePath string
|
||||
|
||||
suppliedName string
|
||||
operation func(secretName, componentName, applicationName string) error
|
||||
operationName string
|
||||
|
||||
// Service Binding Operator options
|
||||
serviceBinding *servicebinding.ServiceBinding
|
||||
serviceType string
|
||||
serviceName string
|
||||
*genericclioptions.Context
|
||||
// choose between Operator Hub and Service Catalog. If true, Operator Hub
|
||||
csvSupport bool
|
||||
|
||||
inlined bool
|
||||
// mappings is an array of strings representing the custom binding data that user wants to inject into the component
|
||||
mappings []string
|
||||
}
|
||||
|
||||
func newCommonLinkOptions() *commonLinkOptions {
|
||||
return &commonLinkOptions{}
|
||||
}
|
||||
|
||||
func (o *commonLinkOptions) getLinkType() string {
|
||||
linkType := "component"
|
||||
if o.isTargetAService {
|
||||
linkType = "service"
|
||||
}
|
||||
return linkType
|
||||
}
|
||||
|
||||
// Complete completes LinkOptions after they've been created
|
||||
func (o *commonLinkOptions) complete(cmdline cmdline.Cmdline, args []string, context string) (err error) {
|
||||
o.operationName = cmdline.GetName()
|
||||
o.suppliedName = args[0]
|
||||
|
||||
o.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline).NeedDevfile(context))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.csvSupport, _ = o.KClient.IsCSVSupported()
|
||||
|
||||
o.serviceType, o.serviceName, err = svc.IsOperatorServiceNameValid(o.suppliedName)
|
||||
if err != nil {
|
||||
// error indicates the service name provided by user doesn't adhere to <crd-name>/<instance-name>
|
||||
// so it's another odo component that they want to link/unlink to/from
|
||||
o.serviceName = o.suppliedName
|
||||
o.isTargetAService = false
|
||||
o.serviceType = "Service" // Kubernetes Service
|
||||
|
||||
// TODO find the service using an app name to link components in other apps
|
||||
// requires modification of the app flag or finding some other way
|
||||
var s *v1.Service
|
||||
s, err = o.Context.KClient.GetOneService(o.suppliedName, o.EnvSpecificInfo.GetApplication())
|
||||
if kerrors.IsNotFound(err) {
|
||||
return fmt.Errorf("couldn't find component named %q. Refer %q to see list of running components", o.suppliedName, "odo list")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.serviceName = s.Name
|
||||
} else {
|
||||
o.isTargetAService = true
|
||||
}
|
||||
|
||||
if o.operationName == unlink {
|
||||
// rest of the code is specific to link operation
|
||||
return nil
|
||||
}
|
||||
|
||||
componentName := o.EnvSpecificInfo.GetName()
|
||||
|
||||
deployment, err := o.KClient.GetOneDeployment(componentName, o.EnvSpecificInfo.GetApplication())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deploymentGVR, err := o.KClient.GetDeploymentAPIVersion()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
paramsMap, err := util.MapFromParameters(o.mappings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// MappingsMap is a map of mappings to be used in the ServiceBinding we create for an "odo link"
|
||||
var mappingsMap []servicebinding.Mapping
|
||||
for kv := range paramsMap {
|
||||
mapping := servicebinding.Mapping{
|
||||
Name: kv,
|
||||
Value: paramsMap[kv],
|
||||
}
|
||||
mappingsMap = append(mappingsMap, mapping)
|
||||
}
|
||||
|
||||
var service servicebinding.Service
|
||||
if o.isTargetAService {
|
||||
// since the service exists, let's get more info to populate service binding request
|
||||
// first get the CR itself
|
||||
cr, err := o.KClient.GetCustomResource(o.serviceType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// now get the group, version, kind information from CR
|
||||
group, version, kind, err := svc.GetGVKFromCR(cr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
service = servicebinding.Service{
|
||||
Id: &o.serviceName, // Id field is helpful if user wants to inject mappings (custom binding data)
|
||||
NamespacedRef: servicebinding.NamespacedRef{
|
||||
Ref: servicebinding.Ref{
|
||||
Group: group,
|
||||
Version: version,
|
||||
Kind: kind,
|
||||
Name: o.serviceName,
|
||||
},
|
||||
},
|
||||
}
|
||||
} else {
|
||||
service = servicebinding.Service{
|
||||
Id: &o.serviceName, // Id field is helpful if user wants to inject mappings (custom binding data)
|
||||
NamespacedRef: servicebinding.NamespacedRef{
|
||||
Ref: servicebinding.Ref{
|
||||
Version: "v1",
|
||||
Kind: "Service",
|
||||
Name: o.serviceName,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
o.serviceBinding = &servicebinding.ServiceBinding{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: strings.Join([]string{kclient.ServiceBindingGroup, kclient.ServiceBindingVersion}, "/"),
|
||||
Kind: kclient.ServiceBindingKind,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: o.getServiceBindingName(componentName),
|
||||
},
|
||||
Spec: servicebinding.ServiceBindingSpec{
|
||||
DetectBindingResources: true,
|
||||
BindAsFiles: o.bindAsFiles,
|
||||
Application: servicebinding.Application{
|
||||
Ref: servicebinding.Ref{
|
||||
Name: deployment.Name,
|
||||
Group: deploymentGVR.Group,
|
||||
Version: deploymentGVR.Version,
|
||||
Resource: deploymentGVR.Resource,
|
||||
},
|
||||
},
|
||||
Mappings: mappingsMap,
|
||||
Services: []servicebinding.Service{service},
|
||||
},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *commonLinkOptions) validate() (err error) {
|
||||
if o.EnvSpecificInfo == nil {
|
||||
return fmt.Errorf("failed to find environment info to validate")
|
||||
}
|
||||
|
||||
var svcFullName string
|
||||
|
||||
if o.isTargetAService {
|
||||
// let's validate if the service exists
|
||||
svcFullName = strings.Join([]string{o.serviceType, o.serviceName}, "/")
|
||||
svcExists, err := svc.OperatorSvcExists(o.KClient, svcFullName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !svcExists {
|
||||
return fmt.Errorf("couldn't find service named %q. Refer %q to see list of running services", svcFullName, "odo service list")
|
||||
}
|
||||
} else {
|
||||
svcFullName = o.serviceName
|
||||
if o.suppliedName == o.EnvSpecificInfo.GetName() {
|
||||
if o.operationName == unlink {
|
||||
return fmt.Errorf("the component %q cannot be unlinked from itself", o.suppliedName)
|
||||
} else {
|
||||
return fmt.Errorf("the component %q cannot be linked with itself", o.suppliedName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if o.operationName == unlink {
|
||||
_, found, err := svc.FindDevfileServiceBinding(o.EnvSpecificInfo.GetDevfileObj(), o.serviceType, o.serviceName, o.GetComponentContext())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
if o.getLinkType() == "service" {
|
||||
return fmt.Errorf("failed to unlink the %s %q since no link was found in the configuration referring this %s", o.getLinkType(), svcFullName, o.getLinkType())
|
||||
}
|
||||
return fmt.Errorf("failed to unlink the %s %q since no link was found in the configuration referring this %s", o.getLinkType(), o.suppliedName, o.getLinkType())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *commonLinkOptions) run() (err error) {
|
||||
if o.Context.EnvSpecificInfo != nil {
|
||||
if o.operationName == unlink {
|
||||
return o.unlinkOperator()
|
||||
}
|
||||
return o.linkOperator()
|
||||
}
|
||||
|
||||
var component string
|
||||
if o.Context.EnvSpecificInfo != nil {
|
||||
component = o.EnvSpecificInfo.GetName()
|
||||
err = o.operation(o.secretName, component, o.GetApplication())
|
||||
} else {
|
||||
component, err = o.Component()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = o.operation(o.secretName, component, o.GetApplication())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch o.operationName {
|
||||
case "link":
|
||||
log.Successf("The %s %s has been successfully linked to the component %s\n", o.getLinkType(), o.suppliedName, component)
|
||||
case "unlink":
|
||||
log.Successf("The %s %s has been successfully unlinked from the component %s\n", o.getLinkType(), o.suppliedName, component)
|
||||
default:
|
||||
return fmt.Errorf("unknown operation %s", o.operationName)
|
||||
}
|
||||
|
||||
secret, err := o.KClient.GetSecret(o.secretName, o.GetProject())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(secret.Data) == 0 {
|
||||
log.Infof("There are no secret environment variables to expose within the %s service", o.suppliedName)
|
||||
} else {
|
||||
if o.operationName == "link" {
|
||||
log.Infof("The below secret environment variables were added to the '%s' component:\n", component)
|
||||
} else {
|
||||
log.Infof("The below secret environment variables were removed from the '%s' component:\n", component)
|
||||
}
|
||||
|
||||
// Output the environment variables
|
||||
for i := range secret.Data {
|
||||
fmt.Printf("· %v\n", i)
|
||||
}
|
||||
|
||||
// Retrieve the first variable to use as an example.
|
||||
// Have to use a range to access the map
|
||||
var exampleEnv string
|
||||
for i := range secret.Data {
|
||||
exampleEnv = i
|
||||
break
|
||||
}
|
||||
|
||||
// Output what to do next if first linking...
|
||||
if o.operationName == "link" {
|
||||
log.Italicf(`
|
||||
You can now access the environment variables from within the component pod, for example:
|
||||
$%s is now available as a variable within component %s`, exampleEnv, component)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// getServiceBindingName creates a name to be used for creation/deletion of SBR during link/unlink operations
|
||||
func (o *commonLinkOptions) getServiceBindingName(componentName string) string {
|
||||
if len(o.name) > 0 {
|
||||
return o.name
|
||||
}
|
||||
if !o.isTargetAService {
|
||||
return strings.Join([]string{componentName, o.serviceName}, "-")
|
||||
}
|
||||
return strings.Join([]string{componentName, strings.ToLower(o.serviceType), o.serviceName}, "-")
|
||||
}
|
||||
|
||||
// linkOperator creates a service binding resource and links
|
||||
// the current component with the given odo service or
|
||||
// the current component with the given component's service
|
||||
// and stores the link info in the env
|
||||
func (o *commonLinkOptions) linkOperator() (err error) {
|
||||
// Convert ServiceBinding -> JSON -> Map -> YAML
|
||||
// JSON conversion step is necessary to inline TypeMeta
|
||||
|
||||
intermediate, err := json.Marshal(o.serviceBinding)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serviceBindingMap := make(map[string]interface{})
|
||||
err = json.Unmarshal(intermediate, &serviceBindingMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
yamlDesc, err := yaml.Marshal(serviceBindingMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check if the component is already linked to the requested component/service
|
||||
_, found, err := svc.FindDevfileServiceBinding(o.EnvSpecificInfo.GetDevfileObj(), o.serviceType, o.serviceName, o.GetComponentContext())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found {
|
||||
return fmt.Errorf("component %q is already linked with the %s %q", o.Context.EnvSpecificInfo.GetName(), o.getLinkType(), o.suppliedName)
|
||||
}
|
||||
|
||||
if o.inlined {
|
||||
err = devfile.AddKubernetesComponentToDevfile(string(yamlDesc), o.serviceBinding.Name, o.EnvSpecificInfo.GetDevfileObj())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = devfile.AddKubernetesComponent(string(yamlDesc), o.serviceBinding.Name, o.GetComponentContext(), o.EnvSpecificInfo.GetDevfileObj())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Successf("Successfully created link between component %q and %s %q\n", o.Context.EnvSpecificInfo.GetName(), o.getLinkType(), o.suppliedName)
|
||||
log.Italic("To apply the link, please use `odo push`")
|
||||
return err
|
||||
}
|
||||
|
||||
// unlinkOperator deletes the service binding resource from the devfile
|
||||
func (o *commonLinkOptions) unlinkOperator() (err error) {
|
||||
|
||||
// We already tested `found` in `validateForOperator`
|
||||
name, _, err := svc.FindDevfileServiceBinding(o.EnvSpecificInfo.GetDevfileObj(), o.serviceType, o.serviceName, o.GetComponentContext())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = devfile.DeleteKubernetesComponentFromDevfile(name, o.EnvSpecificInfo.GetDevfileObj(), o.GetComponentContext())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Successf("Successfully unlinked component %q from %s %q\n", o.Context.EnvSpecificInfo.GetName(), o.getLinkType(), o.suppliedName)
|
||||
log.Italic("To apply the changes, please use `odo push`")
|
||||
return nil
|
||||
}
|
||||
@@ -43,16 +43,9 @@ func NewCmdComponent(name, fullName string) *cobra.Command {
|
||||
componentGetCmd := NewCmdGet(GetRecommendedCommandName, odoutil.GetFullName(fullName, GetRecommendedCommandName))
|
||||
createCmd := NewCmdCreate(CreateRecommendedCommandName, odoutil.GetFullName(fullName, CreateRecommendedCommandName))
|
||||
deleteCmd := NewCmdDelete(DeleteRecommendedCommandName, odoutil.GetFullName(fullName, DeleteRecommendedCommandName))
|
||||
describeCmd := NewCmdDescribe(DescribeRecommendedCommandName, odoutil.GetFullName(fullName, DescribeRecommendedCommandName))
|
||||
linkCmd := NewCmdLink(LinkRecommendedCommandName, odoutil.GetFullName(fullName, LinkRecommendedCommandName))
|
||||
unlinkCmd := NewCmdUnlink(UnlinkRecommendedCommandName, odoutil.GetFullName(fullName, UnlinkRecommendedCommandName))
|
||||
listCmd := NewCmdList(ListRecommendedCommandName, odoutil.GetFullName(fullName, ListRecommendedCommandName))
|
||||
logCmd := NewCmdLog(LogRecommendedCommandName, odoutil.GetFullName(fullName, LogRecommendedCommandName))
|
||||
pushCmd := NewCmdPush(PushRecommendedCommandName, odoutil.GetFullName(fullName, PushRecommendedCommandName))
|
||||
watchCmd := NewCmdWatch(WatchRecommendedCommandName, odoutil.GetFullName(fullName, WatchRecommendedCommandName))
|
||||
testCmd := NewCmdTest(TestRecommendedCommandName, odoutil.GetFullName(fullName, TestRecommendedCommandName))
|
||||
execCmd := NewCmdExec(ExecRecommendedCommandName, odoutil.GetFullName(fullName, ExecRecommendedCommandName))
|
||||
statusCmd := NewCmdStatus(StatusRecommendedCommandName, odoutil.GetFullName(fullName, StatusRecommendedCommandName))
|
||||
|
||||
// componentCmd represents the component command
|
||||
var componentCmd = &cobra.Command{
|
||||
@@ -68,8 +61,7 @@ func NewCmdComponent(name, fullName string) *cobra.Command {
|
||||
// add flags from 'get' to component command
|
||||
componentCmd.Flags().AddFlagSet(componentGetCmd.Flags())
|
||||
|
||||
componentCmd.AddCommand(componentGetCmd, createCmd, deleteCmd, describeCmd, linkCmd, unlinkCmd, listCmd, logCmd, pushCmd, watchCmd, execCmd)
|
||||
componentCmd.AddCommand(testCmd, statusCmd)
|
||||
componentCmd.AddCommand(componentGetCmd, createCmd, deleteCmd, listCmd, pushCmd, watchCmd)
|
||||
|
||||
// Add a defined annotation in order to appear in the help menu
|
||||
componentCmd.Annotations = map[string]string{"command": "main"}
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"github.com/redhat-developer/odo/pkg/devfile/location"
|
||||
"github.com/redhat-developer/odo/pkg/envinfo"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
appCmd "github.com/redhat-developer/odo/pkg/odo/cli/application"
|
||||
projectCmd "github.com/redhat-developer/odo/pkg/odo/cli/project"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||
@@ -394,8 +393,6 @@ func NewCmdCreate(name, fullName string) *cobra.Command {
|
||||
odoutil.AddNowFlag(componentCreateCmd, &co.nowFlag)
|
||||
//Adding `--project` flag
|
||||
projectCmd.AddProjectFlag(componentCreateCmd)
|
||||
//Adding `--application` flag
|
||||
appCmd.AddApplicationFlag(componentCreateCmd)
|
||||
|
||||
completion.RegisterCommandHandler(componentCreateCmd, completion.CreateCompletionHandler)
|
||||
completion.RegisterCommandFlagHandler(componentCreateCmd, "context", completion.FileCompletionHandler)
|
||||
|
||||
@@ -3,16 +3,16 @@ package component
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/redhat-developer/odo/pkg/devfile/adapters/kubernetes/component"
|
||||
"github.com/spf13/cobra"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/devfile/adapters/kubernetes/component"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/devfile"
|
||||
"github.com/redhat-developer/odo/pkg/devfile/adapters/common"
|
||||
"github.com/redhat-developer/odo/pkg/devfile/consts"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
appCmd "github.com/redhat-developer/odo/pkg/odo/cli/application"
|
||||
projectCmd "github.com/redhat-developer/odo/pkg/odo/cli/project"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/ui"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
@@ -267,8 +267,6 @@ func NewCmdDelete(name, fullName string) *cobra.Command {
|
||||
|
||||
// Adding `--project` flag
|
||||
projectCmd.AddProjectFlag(componentDeleteCmd)
|
||||
// Adding `--application` flag
|
||||
appCmd.AddApplicationFlag(componentDeleteCmd)
|
||||
|
||||
return componentDeleteCmd
|
||||
}
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
"github.com/redhat-developer/odo/pkg/machineoutput"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/component"
|
||||
appCmd "github.com/redhat-developer/odo/pkg/odo/cli/application"
|
||||
projectCmd "github.com/redhat-developer/odo/pkg/odo/cli/project"
|
||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||
"github.com/redhat-developer/odo/pkg/odo/util/completion"
|
||||
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// DescribeRecommendedCommandName is the recommended describe command name
|
||||
const DescribeRecommendedCommandName = "describe"
|
||||
|
||||
var describeExample = ktemplates.Examples(` # Describe nodejs component
|
||||
%[1]s nodejs
|
||||
`)
|
||||
|
||||
// DescribeOptions is a dummy container to attach complete, validate and run pattern
|
||||
type DescribeOptions struct {
|
||||
// Component context
|
||||
*ComponentOptions
|
||||
|
||||
// Flags
|
||||
contextFlag string
|
||||
}
|
||||
|
||||
// NewDescribeOptions returns new instance of ListOptions
|
||||
func NewDescribeOptions() *DescribeOptions {
|
||||
return &DescribeOptions{
|
||||
ComponentOptions: &ComponentOptions{},
|
||||
}
|
||||
}
|
||||
|
||||
// Complete completes describe args
|
||||
func (do *DescribeOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
if do.contextFlag == "" {
|
||||
do.contextFlag, err = os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = do.ComponentOptions.Complete(cmdline, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates the describe parameters
|
||||
func (do *DescribeOptions) Validate() (err error) {
|
||||
|
||||
if !((do.GetApplication() != "" && do.GetProject() != "") || do.EnvSpecificInfo.Exists()) {
|
||||
return fmt.Errorf("component %v does not exist", do.componentName)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run has the logic to perform the required actions as part of command
|
||||
func (do *DescribeOptions) Run() (err error) {
|
||||
|
||||
cfd, err := component.NewComponentFullDescriptionFromClientAndLocalConfigProvider(do.Context.KClient, do.EnvSpecificInfo, do.componentName, do.Context.GetApplication(), do.Context.GetProject(), do.contextFlag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if log.IsJSON() {
|
||||
machineoutput.OutputSuccess(cfd)
|
||||
} else {
|
||||
err = cfd.Print(do.Context.KClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewCmdDescribe implements the describe odo command
|
||||
func NewCmdDescribe(name, fullName string) *cobra.Command {
|
||||
do := NewDescribeOptions()
|
||||
|
||||
var describeCmd = &cobra.Command{
|
||||
Use: fmt.Sprintf("%s [component_name]", name),
|
||||
Short: "Describe component",
|
||||
Long: `Describe component.`,
|
||||
Example: fmt.Sprintf(describeExample, fullName),
|
||||
Args: cobra.RangeArgs(0, 1),
|
||||
Annotations: map[string]string{"machineoutput": "json", "command": "component"},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(do, cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
describeCmd.SetUsageTemplate(odoutil.CmdUsageTemplate)
|
||||
completion.RegisterCommandHandler(describeCmd, completion.ComponentNameCompletionHandler)
|
||||
// Adding --context flag
|
||||
odoutil.AddContextFlag(describeCmd, &do.contextFlag)
|
||||
|
||||
//Adding `--project` flag
|
||||
projectCmd.AddProjectFlag(describeCmd)
|
||||
//Adding `--application` flag
|
||||
appCmd.AddApplicationFlag(describeCmd)
|
||||
|
||||
return describeCmd
|
||||
}
|
||||
@@ -2,12 +2,10 @@ package component
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/devfile"
|
||||
|
||||
devfilev1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/redhat-developer/odo/pkg/envinfo"
|
||||
"github.com/redhat-developer/odo/pkg/machineoutput"
|
||||
@@ -114,58 +112,6 @@ func (po *PushOptions) devfilePushInner() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// DevfileComponentLog fetch and display log from devfile components
|
||||
func (lo LogOptions) DevfileComponentLog() error {
|
||||
devObj, err := devfile.ParseAndValidateFromFile(lo.GetDevfilePath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
componentName := lo.Context.EnvSpecificInfo.GetName()
|
||||
|
||||
var platformContext interface{}
|
||||
kc := kubernetes.KubernetesContext{
|
||||
Namespace: lo.KClient.GetCurrentNamespace(),
|
||||
}
|
||||
platformContext = kc
|
||||
|
||||
devfileHandler, err := adapters.NewComponentAdapter(componentName, lo.contextFlag, lo.GetApplication(), devObj, platformContext)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var command devfilev1.Command
|
||||
if lo.debugFlag {
|
||||
command, err = common.GetDebugCommand(devObj.Data, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if reflect.DeepEqual(devfilev1.Command{}, command) {
|
||||
return errors.Errorf("no debug command found in devfile, please run \"odo log\" for run command logs")
|
||||
}
|
||||
|
||||
} else {
|
||||
command, err = common.GetRunCommand(devObj.Data, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Start or update the component
|
||||
rd, err := devfileHandler.Log(lo.followFlag, command)
|
||||
if err != nil {
|
||||
log.Errorf(
|
||||
"Failed to log component with name %s.\nError: %v",
|
||||
componentName,
|
||||
err,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
return util.DisplayLog(lo.followFlag, rd, os.Stdout, componentName, -1)
|
||||
}
|
||||
|
||||
// DevfileUnDeploy undeploys the devfile kubernetes components
|
||||
func (do *DeleteOptions) DevfileUnDeploy() error {
|
||||
devObj, err := devfile.ParseAndValidateFromFile(do.GetDevfilePath())
|
||||
@@ -208,41 +154,3 @@ func (do *DeleteOptions) DevfileComponentDelete() error {
|
||||
|
||||
return devfileHandler.Delete(labels, do.showLogFlag, do.waitFlag)
|
||||
}
|
||||
|
||||
// RunTestCommand runs the specific test command in devfile
|
||||
func (to *TestOptions) RunTestCommand() error {
|
||||
componentName := to.Context.EnvSpecificInfo.GetName()
|
||||
|
||||
var platformContext interface{}
|
||||
kc := kubernetes.KubernetesContext{
|
||||
Namespace: to.KClient.GetCurrentNamespace(),
|
||||
}
|
||||
platformContext = kc
|
||||
|
||||
devfileHandler, err := adapters.NewComponentAdapter(componentName, to.contextFlag, to.GetApplication(), to.devObj, platformContext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return devfileHandler.Test(to.testCommandFlag, to.showLogFlag)
|
||||
}
|
||||
|
||||
// DevfileComponentExec executes the given user command inside the component
|
||||
func (eo *ExecOptions) DevfileComponentExec(command []string) error {
|
||||
devObj, err := devfile.ParseAndValidateFromFile(eo.componentOptions.GetDevfilePath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
componentName := eo.componentOptions.EnvSpecificInfo.GetName()
|
||||
|
||||
kc := kubernetes.KubernetesContext{
|
||||
Namespace: eo.componentOptions.KClient.GetCurrentNamespace(),
|
||||
}
|
||||
|
||||
devfileHandler, err := adapters.NewComponentAdapter(componentName, eo.contextFlag, eo.componentOptions.GetApplication(), devObj, kc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return devfileHandler.Exec(command)
|
||||
}
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
appCmd "github.com/redhat-developer/odo/pkg/odo/cli/application"
|
||||
projectCmd "github.com/redhat-developer/odo/pkg/odo/cli/project"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||
"github.com/redhat-developer/odo/pkg/odo/util/completion"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
// ExecRecommendedCommandName is the recommended exec command name
|
||||
const ExecRecommendedCommandName = "exec"
|
||||
|
||||
var execExample = ktemplates.Examples(` # Executes a command inside the component
|
||||
%[1]s -- ls -a
|
||||
`)
|
||||
|
||||
// ExecOptions contains exec options
|
||||
type ExecOptions struct {
|
||||
// Component context
|
||||
componentOptions *ComponentOptions
|
||||
|
||||
// Parameters
|
||||
command []string
|
||||
|
||||
// Flags
|
||||
contextFlag string
|
||||
}
|
||||
|
||||
// NewExecOptions returns new instance of ExecOptions
|
||||
func NewExecOptions() *ExecOptions {
|
||||
return &ExecOptions{
|
||||
componentOptions: &ComponentOptions{},
|
||||
}
|
||||
}
|
||||
|
||||
// Complete completes exec args
|
||||
func (eo *ExecOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
// gets the command args passed after the dash i.e `--`
|
||||
eo.command, err = cmdline.GetArgsAfterDashes(args)
|
||||
if err != nil || len(eo.command) <= 0 {
|
||||
return fmt.Errorf(`no command was given for the exec command
|
||||
Please provide a command to execute, odo exec -- <command to be execute>`)
|
||||
}
|
||||
|
||||
// checks if something is passed between `odo exec` and the dash `--`
|
||||
if len(eo.command) != len(args) {
|
||||
return fmt.Errorf("no parameter is expected for the command")
|
||||
}
|
||||
|
||||
eo.componentOptions.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline).NeedDevfile(eo.contextFlag))
|
||||
return err
|
||||
}
|
||||
|
||||
// Validate validates the exec parameters
|
||||
func (eo *ExecOptions) Validate() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Run has the logic to perform the required actions as part of command
|
||||
func (eo *ExecOptions) Run() (err error) {
|
||||
return eo.DevfileComponentExec(eo.command)
|
||||
}
|
||||
|
||||
// NewCmdExec implements the exec odo command
|
||||
func NewCmdExec(name, fullName string) *cobra.Command {
|
||||
o := NewExecOptions()
|
||||
|
||||
var execCmd = &cobra.Command{
|
||||
Use: name,
|
||||
Short: "Executes a command inside the component",
|
||||
Long: `Executes a command inside the component`,
|
||||
Example: fmt.Sprintf(execExample, fullName),
|
||||
Annotations: map[string]string{"command": "component"},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(o, cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
execCmd.SetUsageTemplate(odoutil.CmdUsageTemplate)
|
||||
completion.RegisterCommandHandler(execCmd, completion.ComponentNameCompletionHandler)
|
||||
odoutil.AddContextFlag(execCmd, &o.contextFlag)
|
||||
|
||||
//Adding `--project` flag
|
||||
projectCmd.AddProjectFlag(execCmd)
|
||||
|
||||
// Adding `--app` flag
|
||||
appCmd.AddApplicationFlag(execCmd)
|
||||
|
||||
return execCmd
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
appCmd "github.com/redhat-developer/odo/pkg/odo/cli/application"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/project"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
@@ -97,8 +96,6 @@ func NewCmdGet(name, fullName string) *cobra.Command {
|
||||
|
||||
//Adding `--project` flag
|
||||
project.AddProjectFlag(componentGetCmd)
|
||||
//Adding `--application` flag
|
||||
appCmd.AddApplicationFlag(componentGetCmd)
|
||||
|
||||
return componentGetCmd
|
||||
}
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
servicebinding "github.com/redhat-developer/service-binding-operator/apis/binding/v1alpha1"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/devfile/location"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
// LinkRecommendedCommandName is the recommended link command name
|
||||
const LinkRecommendedCommandName = "link"
|
||||
|
||||
var (
|
||||
linkExample = ktemplates.Examples(`# Link the current component to the 'EtcdCluster' named 'myetcd'
|
||||
%[1]s EtcdCluster/myetcd
|
||||
|
||||
# Link current component to the 'backend' component (backend must have a single exposed port)
|
||||
%[1]s backend
|
||||
|
||||
# Link current component to the 'backend' component and puts the link definition in the devfile instead of a separate file
|
||||
%[1]s backend --inlined
|
||||
|
||||
# Link component 'nodejs' to the 'backend' component
|
||||
%[1]s backend --component nodejs
|
||||
|
||||
# Link current component to port 8080 of the 'backend' component (backend must have port 8080 exposed)
|
||||
%[1]s backend --port 8080
|
||||
|
||||
# Link the current component to the 'EtcdCluster' named 'myetcd'
|
||||
# and make the secrets accessible as files in the '/bindings/etcd/' directory
|
||||
%[1]s EtcdCluster/myetcd --bind-as-files --name etcd`)
|
||||
|
||||
linkLongDesc = `Link current or provided component to a service (backed by an Operator) or another component
|
||||
|
||||
The appropriate secret will be added to the environment of the source component as environment variables by
|
||||
default.
|
||||
|
||||
For example:
|
||||
|
||||
Let us say we have created a nodejs application called 'frontend' which we link to an another component called
|
||||
'backend' which exposes port 8080, then linking the 2 using:
|
||||
odo link backend --component frontend
|
||||
|
||||
The frontend has 2 ENV variables it can use:
|
||||
SERVICE_BACKEND_IP=10.217.4.194
|
||||
SERVICE_BACKEND_PORT_PORT-8080=8080
|
||||
|
||||
Using the '--bind-as-files' flag, secrets will be accessible as files instead of environment variables.
|
||||
The value of the '--name' flag indicates the name of the directory under '/bindings/' containing the secrets files.
|
||||
`
|
||||
)
|
||||
|
||||
// LinkOptions encapsulates the options for the odo link command
|
||||
type LinkOptions struct {
|
||||
// Common link/unlink context
|
||||
*commonLinkOptions
|
||||
|
||||
// Flags
|
||||
contextFlag string
|
||||
}
|
||||
|
||||
// NewLinkOptions creates a new LinkOptions instance
|
||||
func NewLinkOptions() *LinkOptions {
|
||||
options := LinkOptions{}
|
||||
options.commonLinkOptions = newCommonLinkOptions()
|
||||
options.commonLinkOptions.serviceBinding = &servicebinding.ServiceBinding{}
|
||||
return &options
|
||||
}
|
||||
|
||||
// Complete completes LinkOptions after they've been created
|
||||
func (o *LinkOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
o.commonLinkOptions.devfilePath = location.DevfileLocation(o.contextFlag)
|
||||
|
||||
err = o.complete(cmdline, args, o.contextFlag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if o.csvSupport {
|
||||
o.operation = o.KClient.LinkSecret
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Validate validates the LinkOptions based on completed values
|
||||
func (o *LinkOptions) Validate() (err error) {
|
||||
return o.validate()
|
||||
}
|
||||
|
||||
// Run contains the logic for the odo link command
|
||||
func (o *LinkOptions) Run() (err error) {
|
||||
return o.run()
|
||||
}
|
||||
|
||||
// NewCmdLink implements the link odo command
|
||||
func NewCmdLink(name, fullName string) *cobra.Command {
|
||||
o := NewLinkOptions()
|
||||
|
||||
linkCmd := &cobra.Command{
|
||||
Use: fmt.Sprintf("%s <operator-service-type>/<service-name> OR %s <operator-service-type>/<service-name> --component [component] OR %s <component> --component [component]", name, name, name),
|
||||
Short: "Link component to a service or component",
|
||||
Long: linkLongDesc,
|
||||
Example: fmt.Sprintf(linkExample, fullName),
|
||||
Args: cobra.ExactArgs(1),
|
||||
Annotations: map[string]string{"command": "component"},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(o, cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
linkCmd.PersistentFlags().BoolVarP(&o.inlined, "inlined", "", false, "Puts the link definition in the devfile instead of a separate file")
|
||||
linkCmd.PersistentFlags().StringVar(&o.name, "name", "", "Name of the created ServiceBinding resource")
|
||||
linkCmd.PersistentFlags().BoolVar(&o.bindAsFiles, "bind-as-files", false, "If enabled, configuration values will be mounted as files, instead of declared as environment variables")
|
||||
linkCmd.PersistentFlags().StringArrayVarP(&o.mappings, "map", "", []string{}, "Mappings (custom binding data) to be added to the component; each map should be specified as <key>=<value>")
|
||||
linkCmd.SetUsageTemplate(odoutil.CmdUsageTemplate)
|
||||
|
||||
//Adding `--component` flag
|
||||
AddComponentFlag(linkCmd)
|
||||
|
||||
//Adding context flag
|
||||
odoutil.AddContextFlag(linkCmd, &o.contextFlag)
|
||||
|
||||
return linkCmd
|
||||
}
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/component"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
appCmd "github.com/redhat-developer/odo/pkg/odo/cli/application"
|
||||
projectCmd "github.com/redhat-developer/odo/pkg/odo/cli/project"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
@@ -237,8 +236,6 @@ func NewCmdList(name, fullName string) *cobra.Command {
|
||||
|
||||
//Adding `--project` flag
|
||||
projectCmd.AddProjectFlag(componentListCmd)
|
||||
//Adding `--application` flag
|
||||
appCmd.AddApplicationFlag(componentListCmd)
|
||||
|
||||
completion.RegisterCommandFlagHandler(componentListCmd, "path", completion.FileCompletionHandler)
|
||||
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
appCmd "github.com/redhat-developer/odo/pkg/odo/cli/application"
|
||||
projectCmd "github.com/redhat-developer/odo/pkg/odo/cli/project"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
"github.com/redhat-developer/odo/pkg/odo/util/completion"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
|
||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// LogRecommendedCommandName is the recommended watch command name
|
||||
const LogRecommendedCommandName = "log"
|
||||
|
||||
var logExample = ktemplates.Examples(` # Get the logs for the nodejs component
|
||||
%[1]s nodejs
|
||||
`)
|
||||
|
||||
// LogOptions contains log options
|
||||
type LogOptions struct {
|
||||
// Component context
|
||||
*ComponentOptions
|
||||
|
||||
// Flags
|
||||
followFlag bool
|
||||
debugFlag bool
|
||||
contextFlag string
|
||||
}
|
||||
|
||||
// NewLogOptions returns new instance of LogOptions
|
||||
func NewLogOptions() *LogOptions {
|
||||
return &LogOptions{
|
||||
ComponentOptions: &ComponentOptions{},
|
||||
}
|
||||
}
|
||||
|
||||
// Complete completes log args
|
||||
func (lo *LogOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
lo.ComponentOptions.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline).NeedDevfile(lo.contextFlag))
|
||||
return err
|
||||
}
|
||||
|
||||
// Validate validates the log parameters
|
||||
func (lo *LogOptions) Validate() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Run has the logic to perform the required actions as part of command
|
||||
func (lo *LogOptions) Run() (err error) {
|
||||
err = lo.DevfileComponentLog()
|
||||
return
|
||||
}
|
||||
|
||||
// NewCmdLog implements the log odo command
|
||||
func NewCmdLog(name, fullName string) *cobra.Command {
|
||||
o := NewLogOptions()
|
||||
|
||||
var logCmd = &cobra.Command{
|
||||
Use: fmt.Sprintf("%s [component_name]", name),
|
||||
Short: "Retrieve the log for the given component",
|
||||
Long: `Retrieve the log for the given component`,
|
||||
Example: fmt.Sprintf(logExample, fullName),
|
||||
Args: cobra.RangeArgs(0, 1),
|
||||
Annotations: map[string]string{"command": "component"},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(o, cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
logCmd.Flags().BoolVarP(&o.followFlag, "follow", "f", false, "Follow logs")
|
||||
logCmd.Flags().BoolVar(&o.debugFlag, "debug", false, "Show logs for debug command")
|
||||
|
||||
logCmd.SetUsageTemplate(odoutil.CmdUsageTemplate)
|
||||
completion.RegisterCommandHandler(logCmd, completion.ComponentNameCompletionHandler)
|
||||
// Adding `--context` flag
|
||||
odoutil.AddContextFlag(logCmd, &o.contextFlag)
|
||||
|
||||
//Adding `--project` flag
|
||||
projectCmd.AddProjectFlag(logCmd)
|
||||
//Adding `--application` flag
|
||||
appCmd.AddApplicationFlag(logCmd)
|
||||
|
||||
return logCmd
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/redhat-developer/odo/pkg/devfile/adapters"
|
||||
"github.com/redhat-developer/odo/pkg/devfile/adapters/common"
|
||||
"github.com/redhat-developer/odo/pkg/devfile/adapters/kubernetes"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
"github.com/redhat-developer/odo/pkg/machineoutput"
|
||||
appCmd "github.com/redhat-developer/odo/pkg/odo/cli/application"
|
||||
projectCmd "github.com/redhat-developer/odo/pkg/odo/cli/project"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
"github.com/redhat-developer/odo/pkg/url"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/odo/util/completion"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
|
||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// StatusRecommendedCommandName is the recommended watch command name
|
||||
const StatusRecommendedCommandName = "status"
|
||||
|
||||
var statusExample = ktemplates.Examples(` # Get the status for the nodejs component
|
||||
%[1]s nodejs -o json --follow
|
||||
`)
|
||||
|
||||
// StatusOptions contains status options
|
||||
type StatusOptions struct {
|
||||
// Context
|
||||
*genericclioptions.Context
|
||||
|
||||
// Flags
|
||||
contextFlag string
|
||||
followFlag bool
|
||||
|
||||
componentName string
|
||||
|
||||
devfileHandler common.ComponentAdapter
|
||||
}
|
||||
|
||||
// NewStatusOptions returns new instance of StatusOptions
|
||||
func NewStatusOptions() *StatusOptions {
|
||||
return &StatusOptions{}
|
||||
}
|
||||
|
||||
// Complete completes status args
|
||||
func (so *StatusOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
so.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline).NeedDevfile(so.contextFlag))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the component name
|
||||
so.componentName = so.EnvSpecificInfo.GetName()
|
||||
|
||||
platformContext := kubernetes.KubernetesContext{
|
||||
Namespace: so.KClient.GetCurrentNamespace(),
|
||||
}
|
||||
|
||||
so.devfileHandler, err = adapters.NewComponentAdapter(so.componentName, so.contextFlag, so.GetApplication(), so.EnvSpecificInfo.GetDevfileObj(), platformContext)
|
||||
return err
|
||||
}
|
||||
|
||||
// Validate validates the status parameters
|
||||
func (so *StatusOptions) Validate() (err error) {
|
||||
|
||||
if !so.followFlag {
|
||||
return fmt.Errorf("this command must be called with --follow")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Run has the logic to perform the required actions as part of command
|
||||
func (so *StatusOptions) Run() (err error) {
|
||||
if !log.IsJSON() {
|
||||
return errors.New("this command only supports the '-o json' output format")
|
||||
}
|
||||
so.devfileHandler.StartSupervisordCtlStatusWatch()
|
||||
so.devfileHandler.StartContainerStatusWatch()
|
||||
|
||||
loggingClient := machineoutput.NewConsoleMachineEventLoggingClient()
|
||||
|
||||
url.StartURLHttpRequestStatusWatchForK8S(so.KClient, &so.LocalConfigProvider, loggingClient)
|
||||
|
||||
// You can call Run() any time you like, but you can never leave.
|
||||
for {
|
||||
time.Sleep(60 * time.Second)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// NewCmdStatus implements the status odo command
|
||||
func NewCmdStatus(name, fullName string) *cobra.Command {
|
||||
o := NewStatusOptions()
|
||||
|
||||
annotations := map[string]string{"command": "component", "machineoutput": "json"}
|
||||
|
||||
var statusCmd = &cobra.Command{
|
||||
Use: fmt.Sprintf("%s [component_name]", name),
|
||||
Short: "Watches the given component and outputs machine-readable JSON events representing component status changes",
|
||||
Long: `Watches the given component and outputs machine-readable JSON events representing component status changes`,
|
||||
Example: fmt.Sprintf(statusExample, fullName),
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
Annotations: annotations,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(o, cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
statusCmd.SetUsageTemplate(odoutil.CmdUsageTemplate)
|
||||
|
||||
// Adding context flag
|
||||
odoutil.AddContextFlag(statusCmd, &o.contextFlag)
|
||||
|
||||
statusCmd.Flags().BoolVarP(&o.followFlag, "follow", "f", false, "Follow the component and report all changes")
|
||||
|
||||
//Adding `--application` flag
|
||||
appCmd.AddApplicationFlag(statusCmd)
|
||||
|
||||
//Adding `--project` flag
|
||||
projectCmd.AddProjectFlag(statusCmd)
|
||||
|
||||
completion.RegisterCommandHandler(statusCmd, completion.ComponentNameCompletionHandler)
|
||||
|
||||
return statusCmd
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/devfile"
|
||||
appCmd "github.com/redhat-developer/odo/pkg/odo/cli/application"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/util"
|
||||
|
||||
devfileParser "github.com/devfile/library/pkg/devfile/parser"
|
||||
projectCmd "github.com/redhat-developer/odo/pkg/odo/cli/project"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||
"github.com/redhat-developer/odo/pkg/odo/util/completion"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
// TestRecommendedCommandName is the recommended test command name
|
||||
const TestRecommendedCommandName = "test"
|
||||
|
||||
// TestOptions encapsulates the options for the odo command
|
||||
type TestOptions struct {
|
||||
// Context
|
||||
*genericclioptions.Context
|
||||
|
||||
// Flags
|
||||
testCommandFlag string
|
||||
contextFlag string
|
||||
showLogFlag bool
|
||||
|
||||
// devfile content
|
||||
devObj devfileParser.DevfileObj
|
||||
}
|
||||
|
||||
var testExample = templates.Examples(`
|
||||
# Run default test command
|
||||
%[1]s
|
||||
|
||||
# Run a specific test command
|
||||
%[1]s --test-command <command name>
|
||||
|
||||
`)
|
||||
|
||||
// NewTestOptions creates a new TestOptions instance
|
||||
func NewTestOptions() *TestOptions {
|
||||
return &TestOptions{}
|
||||
}
|
||||
|
||||
// Complete completes TestOptions after they've been created
|
||||
func (to *TestOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
to.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline).NeedDevfile(to.contextFlag))
|
||||
return
|
||||
}
|
||||
|
||||
// Validate validates the TestOptions based on completed values
|
||||
func (to *TestOptions) Validate() (err error) {
|
||||
|
||||
if !util.CheckPathExists(to.Context.GetDevfilePath()) {
|
||||
return fmt.Errorf("unable to find devfile, odo test command is only supported by devfile components")
|
||||
}
|
||||
|
||||
devObj, err := devfile.ParseAndValidateFromFile(to.Context.GetDevfilePath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
to.devObj = devObj
|
||||
return
|
||||
}
|
||||
|
||||
// Run contains the logic for the odo command
|
||||
func (to *TestOptions) Run() (err error) {
|
||||
return to.RunTestCommand()
|
||||
}
|
||||
|
||||
// NewCmdTest implements the odo test command
|
||||
func NewCmdTest(name, fullName string) *cobra.Command {
|
||||
to := NewTestOptions()
|
||||
testCmd := &cobra.Command{
|
||||
Use: name,
|
||||
Short: "Run the test command defined in the devfile",
|
||||
Long: "Run the test command defined in the devfile",
|
||||
Example: fmt.Sprintf(testExample, fullName),
|
||||
Args: cobra.MaximumNArgs(0),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(to, cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
// Add a defined annotation in order to appear in the help menu
|
||||
testCmd.Annotations = map[string]string{"command": "main"}
|
||||
testCmd.SetUsageTemplate(odoutil.CmdUsageTemplate)
|
||||
testCmd.Flags().StringVar(&to.testCommandFlag, "test-command", "", "Devfile Test Command to execute")
|
||||
testCmd.Flags().BoolVar(&to.showLogFlag, "show-log", false, "If enabled, logs will be shown when running the test command")
|
||||
//Adding `--context` flag
|
||||
odoutil.AddContextFlag(testCmd, &to.contextFlag)
|
||||
//Adding `--project` flag
|
||||
projectCmd.AddProjectFlag(testCmd)
|
||||
// Adding `--app` flag
|
||||
appCmd.AddApplicationFlag(testCmd)
|
||||
completion.RegisterCommandHandler(testCmd, completion.ComponentNameCompletionHandler)
|
||||
return testCmd
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/odo/util"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// UnlinkRecommendedCommandName is the recommended unlink command name
|
||||
const UnlinkRecommendedCommandName = "unlink"
|
||||
|
||||
var (
|
||||
unlinkExample = ktemplates.Examples(`# Unlink the 'my-postgresql' service from the current component
|
||||
%[1]s my-postgresql
|
||||
|
||||
# Unlink the 'my-postgresql' service from the 'nodejs' component
|
||||
%[1]s my-postgresql --component nodejs
|
||||
|
||||
# Unlink the 'backend' component from the current component (backend must have a single exposed port)
|
||||
%[1]s backend
|
||||
|
||||
# Unlink the 'backend' service from the 'nodejs' component
|
||||
%[1]s backend --component nodejs
|
||||
|
||||
# Unlink the backend's 8080 port from the current component
|
||||
%[1]s backend --port 8080`)
|
||||
|
||||
unlinkLongDesc = `Unlink component or service from a component.
|
||||
For this command to be successful, the service or component needs to have been linked prior to the invocation using 'odo link'`
|
||||
)
|
||||
|
||||
// UnlinkOptions encapsulates the options for the odo link command
|
||||
type UnlinkOptions struct {
|
||||
// Common link/unlink context
|
||||
*commonLinkOptions
|
||||
|
||||
// Flags
|
||||
contextFlag string
|
||||
}
|
||||
|
||||
// NewUnlinkOptions creates a new UnlinkOptions instance
|
||||
func NewUnlinkOptions() *UnlinkOptions {
|
||||
options := UnlinkOptions{}
|
||||
options.commonLinkOptions = newCommonLinkOptions()
|
||||
return &options
|
||||
}
|
||||
|
||||
// Complete completes UnlinkOptions after they've been created
|
||||
func (o *UnlinkOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
err = o.complete(cmdline, args, o.contextFlag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if o.csvSupport {
|
||||
o.operation = o.KClient.UnlinkSecret
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Validate validates the UnlinkOptions based on completed values
|
||||
func (o *UnlinkOptions) Validate() (err error) {
|
||||
return o.validate()
|
||||
}
|
||||
|
||||
// Run contains the logic for the odo link command
|
||||
func (o *UnlinkOptions) Run() (err error) {
|
||||
return o.run()
|
||||
}
|
||||
|
||||
// NewCmdUnlink implements the link odo command
|
||||
func NewCmdUnlink(name, fullName string) *cobra.Command {
|
||||
o := NewUnlinkOptions()
|
||||
|
||||
unlinkCmd := &cobra.Command{
|
||||
Use: fmt.Sprintf("%s <service> --component [component] OR %s <component> --component [component]", name, name),
|
||||
Short: "Unlink component to a service or component",
|
||||
Long: unlinkLongDesc,
|
||||
Example: fmt.Sprintf(unlinkExample, fullName),
|
||||
Args: cobra.ExactArgs(1),
|
||||
Annotations: map[string]string{"command": "component"},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(o, cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
unlinkCmd.SetUsageTemplate(util.CmdUsageTemplate)
|
||||
//Adding `--component` flag
|
||||
AddComponentFlag(unlinkCmd)
|
||||
// Adding context flag
|
||||
odoutil.AddContextFlag(unlinkCmd, &o.contextFlag)
|
||||
|
||||
return unlinkCmd
|
||||
}
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/redhat-developer/odo/pkg/devfile/adapters/common"
|
||||
"github.com/redhat-developer/odo/pkg/devfile/adapters/kubernetes"
|
||||
"github.com/redhat-developer/odo/pkg/envinfo"
|
||||
appCmd "github.com/redhat-developer/odo/pkg/odo/cli/application"
|
||||
projectCmd "github.com/redhat-developer/odo/pkg/odo/cli/project"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
@@ -177,9 +176,6 @@ func NewCmdWatch(name, fullName string) *cobra.Command {
|
||||
// Adding context flag
|
||||
odoutil.AddContextFlag(watchCmd, &wo.contextFlag)
|
||||
|
||||
//Adding `--application` flag
|
||||
appCmd.AddApplicationFlag(watchCmd)
|
||||
|
||||
//Adding `--project` flag
|
||||
projectCmd.AddProjectFlag(watchCmd)
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/config"
|
||||
"github.com/redhat-developer/odo/pkg/odo/util"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
// RecommendedCommandName is the recommended config command name
|
||||
const RecommendedCommandName = "config"
|
||||
|
||||
var configLongDesc = ktemplates.LongDesc(`Modifies odo specific configuration settings within the devfile or config file.
|
||||
|
||||
%[1]s
|
||||
`)
|
||||
|
||||
// NewCmdConfiguration implements the utils config odo command
|
||||
func NewCmdConfiguration(name, fullName string) *cobra.Command {
|
||||
configurationViewCmd := NewCmdView(viewCommandName, util.GetFullName(fullName, viewCommandName))
|
||||
configurationSetCmd := NewCmdSet(setCommandName, util.GetFullName(fullName, setCommandName))
|
||||
configurationUnsetCmd := NewCmdUnset(unsetCommandName, util.GetFullName(fullName, unsetCommandName))
|
||||
configurationCmd := &cobra.Command{
|
||||
Use: name,
|
||||
Short: "Change or view configuration",
|
||||
Long: fmt.Sprintf(configLongDesc, config.FormatDevfileSupportedParameters()),
|
||||
Example: fmt.Sprintf("%s\n%s\n%s",
|
||||
configurationViewCmd.Example,
|
||||
configurationSetCmd.Example,
|
||||
configurationUnsetCmd.Example,
|
||||
),
|
||||
Aliases: []string{"configuration"},
|
||||
}
|
||||
|
||||
configurationCmd.AddCommand(configurationViewCmd, configurationSetCmd)
|
||||
configurationCmd.AddCommand(configurationUnsetCmd)
|
||||
configurationCmd.SetUsageTemplate(util.CmdUsageTemplate)
|
||||
configurationCmd.Annotations = map[string]string{"command": "main"}
|
||||
|
||||
return configurationCmd
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/envvar"
|
||||
"github.com/redhat-developer/odo/pkg/kclient"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/preference"
|
||||
"github.com/redhat-developer/odo/pkg/project"
|
||||
"github.com/redhat-developer/odo/pkg/util"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/redhat-developer/odo/pkg/config"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
clicomponent "github.com/redhat-developer/odo/pkg/odo/cli/component"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/ui"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||
"github.com/redhat-developer/odo/pkg/odo/util/validation"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
const setCommandName = "set"
|
||||
|
||||
var (
|
||||
setLongDesc = ktemplates.LongDesc(`Set an individual value in the devfile or odo configuration file.
|
||||
%[1]s
|
||||
`)
|
||||
|
||||
devfileSetExample = ktemplates.Examples(`
|
||||
# Set a configuration value in the devfile
|
||||
%[1]s %[2]s testapp
|
||||
%[1]s %[3]s 8080/TCP,8443/TCP
|
||||
%[1]s %[4]s 500M
|
||||
|
||||
# Set a env variable in the devfiles
|
||||
%[1]s --env KAFKA_HOST=kafka --env KAFKA_PORT=6639
|
||||
`)
|
||||
)
|
||||
|
||||
// SetOptions encapsulates the options for the command
|
||||
type SetOptions struct {
|
||||
// Push context
|
||||
*clicomponent.PushOptions
|
||||
|
||||
// Parameters
|
||||
paramName string
|
||||
paramValue string
|
||||
|
||||
// Flags
|
||||
forceFlag bool
|
||||
envArrayFlag []string
|
||||
nowFlag bool
|
||||
}
|
||||
|
||||
// NewSetOptions creates a new SetOptions instance
|
||||
func NewSetOptions(prjClient project.Client, prefClient preference.Client) *SetOptions {
|
||||
return &SetOptions{
|
||||
PushOptions: clicomponent.NewPushOptions(prjClient, prefClient),
|
||||
}
|
||||
}
|
||||
|
||||
// Complete completes SetOptions after they've been created
|
||||
func (o *SetOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
params := genericclioptions.NewCreateParameters(cmdline).NeedDevfile(o.GetComponentContext())
|
||||
if o.nowFlag {
|
||||
params.CreateAppIfNeeded().RequireRouteAvailability()
|
||||
}
|
||||
o.Context, err = genericclioptions.New(params)
|
||||
if err != nil {
|
||||
if err1 := util.IsInvalidKubeConfigError(err); err1 != nil {
|
||||
return err1
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
o.DevfilePath = o.Context.EnvSpecificInfo.GetDevfilePath()
|
||||
o.EnvSpecificInfo = o.Context.EnvSpecificInfo
|
||||
|
||||
if o.envArrayFlag == nil {
|
||||
o.paramName = args[0]
|
||||
o.paramValue = args[1]
|
||||
}
|
||||
|
||||
if o.nowFlag {
|
||||
prjName := o.Context.LocalConfigProvider.GetNamespace()
|
||||
o.ResolveSrcAndConfigFlags()
|
||||
err = o.ResolveProject(prjName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates the SetOptions based on completed values
|
||||
func (o *SetOptions) Validate() error {
|
||||
if !o.Context.LocalConfigProvider.Exists() {
|
||||
return fmt.Errorf("the directory doesn't contain a component. Use 'odo create' to create a component")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run contains the logic for the command
|
||||
func (o *SetOptions) Run() error {
|
||||
if o.envArrayFlag != nil {
|
||||
newEnvVarList, err := envvar.NewListFromSlice(o.envArrayFlag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = o.EnvSpecificInfo.GetDevfileObj().AddEnvVars(newEnvVarList.ToDevfileEnvVar())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Success("Environment variables were successfully updated")
|
||||
if o.nowFlag {
|
||||
return o.DevfilePush()
|
||||
}
|
||||
log.Italic("\nRun `odo push` command to apply changes to the cluster")
|
||||
return err
|
||||
}
|
||||
if !o.forceFlag {
|
||||
if config.IsSetInDevfile(o.EnvSpecificInfo.GetDevfileObj(), o.paramName) {
|
||||
if !ui.Proceed(fmt.Sprintf("%v is already set. Do you want to override it in the devfile", o.paramName)) {
|
||||
fmt.Println("Aborted by the user.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err := config.SetDevfileConfiguration(o.EnvSpecificInfo.GetDevfileObj(), strings.ToLower(o.paramName), o.paramValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Success("Devfile successfully updated")
|
||||
if o.nowFlag {
|
||||
return o.DevfilePush()
|
||||
}
|
||||
log.Italic("\nRun `odo push` command to apply changes to the cluster")
|
||||
return err
|
||||
}
|
||||
|
||||
func isValidArgumentList(args []string) error {
|
||||
|
||||
if len(args) < 2 {
|
||||
return fmt.Errorf("please provide a parameter name and value")
|
||||
} else if len(args) > 2 {
|
||||
return fmt.Errorf("only one value per parameter is allowed")
|
||||
}
|
||||
|
||||
var err error
|
||||
param, ok := config.AsDevfileSupportedParameter(args[0])
|
||||
|
||||
if !ok {
|
||||
err = errors.Errorf("the provided parameter is not supported, %v", args[0])
|
||||
}
|
||||
|
||||
switch param {
|
||||
case "memory", "minmemory", "maxmemory", "cpu", "mincpu", "maxcpu":
|
||||
err = validation.NonNegativeValidator(args[1])
|
||||
if err != nil {
|
||||
err = errors.Errorf("%s is invalid %v", param, err)
|
||||
}
|
||||
case "ports", "debugport":
|
||||
err = validation.PortsValidator(args[1])
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err = errors.Errorf("validation failed for the provided arguments, %v", err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// NewCmdSet implements the config set odo command
|
||||
func NewCmdSet(name, fullName string) *cobra.Command {
|
||||
// The error is not handled at this point, it will be handled during Context creation
|
||||
kubclient, _ := kclient.New()
|
||||
prefClient, err := preference.NewClient()
|
||||
if err != nil {
|
||||
odoutil.LogErrorAndExit(err, "unable to set preference, something is wrong with odo, kindly raise an issue at https://github.com/redhat-developer/odo/issues/new?template=Bug.md")
|
||||
}
|
||||
o := NewSetOptions(project.NewClient(kubclient), prefClient)
|
||||
configurationSetCmd := &cobra.Command{
|
||||
Use: name,
|
||||
Short: "Set a value in odo config file",
|
||||
Long: fmt.Sprintf(setLongDesc, config.FormatDevfileSupportedParameters()),
|
||||
Example: fmt.Sprintf("\n"+devfileSetExample, fullName, config.Name, config.Ports, config.Memory),
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if o.envArrayFlag != nil {
|
||||
// no args are needed
|
||||
if len(args) > 0 {
|
||||
return fmt.Errorf("expected 0 args")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return isValidArgumentList(args)
|
||||
}, Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(o, cmd, args)
|
||||
},
|
||||
}
|
||||
configurationSetCmd.Flags().BoolVarP(&o.forceFlag, "force", "f", false, "Don't ask for confirmation, set the config directly")
|
||||
configurationSetCmd.Flags().StringArrayVarP(&o.envArrayFlag, "env", "e", nil, "Set the environment variables in config")
|
||||
o.AddContextFlag(configurationSetCmd)
|
||||
odoutil.AddNowFlag(configurationSetCmd, &o.nowFlag)
|
||||
|
||||
return configurationSetCmd
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/kclient"
|
||||
"github.com/redhat-developer/odo/pkg/preference"
|
||||
"github.com/redhat-developer/odo/pkg/project"
|
||||
"github.com/redhat-developer/odo/pkg/util"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/config"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
clicomponent "github.com/redhat-developer/odo/pkg/odo/cli/component"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/ui"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||
"github.com/spf13/cobra"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
const unsetCommandName = "unset"
|
||||
|
||||
var (
|
||||
unsetLongDesc = ktemplates.LongDesc(`Unset an individual value in the devfile or odo configuration file.
|
||||
%[1]s
|
||||
`)
|
||||
devfileUnsetExample = ktemplates.Examples(`
|
||||
# Unset a configuration value in the devfile
|
||||
%[1]s %[2]s
|
||||
%[1]s %[3]s
|
||||
%[1]s %[4]s
|
||||
|
||||
# Unset a env variable in the devfiles
|
||||
%[1]s --env KAFKA_HOST --env KAFKA_PORT
|
||||
`)
|
||||
)
|
||||
|
||||
// UnsetOptions encapsulates the options for the command
|
||||
type UnsetOptions struct {
|
||||
// Push context
|
||||
*clicomponent.PushOptions
|
||||
|
||||
// Parameters
|
||||
paramName string
|
||||
|
||||
// Flags
|
||||
forceFlag bool
|
||||
envArrayFlag []string
|
||||
nowFlag bool
|
||||
}
|
||||
|
||||
// NewUnsetOptions creates a new UnsetOptions instance
|
||||
func NewUnsetOptions(prjClient project.Client, prefClient preference.Client) *UnsetOptions {
|
||||
return &UnsetOptions{
|
||||
PushOptions: clicomponent.NewPushOptions(prjClient, prefClient),
|
||||
}
|
||||
}
|
||||
|
||||
// Complete completes UnsetOptions after they've been created
|
||||
func (o *UnsetOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
params := genericclioptions.NewCreateParameters(cmdline).NeedDevfile(o.GetComponentContext())
|
||||
if o.nowFlag {
|
||||
params.CreateAppIfNeeded().RequireRouteAvailability()
|
||||
}
|
||||
o.Context, err = genericclioptions.New(params)
|
||||
if err != nil {
|
||||
if err1 := util.IsInvalidKubeConfigError(err); err1 != nil {
|
||||
return err1
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
o.DevfilePath = o.Context.EnvSpecificInfo.GetDevfilePath()
|
||||
o.EnvSpecificInfo = o.Context.EnvSpecificInfo
|
||||
|
||||
if o.envArrayFlag == nil {
|
||||
o.paramName = args[0]
|
||||
}
|
||||
|
||||
if o.nowFlag {
|
||||
prjName := o.Context.LocalConfigProvider.GetNamespace()
|
||||
o.ResolveSrcAndConfigFlags()
|
||||
err = o.ResolveProject(prjName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates the UnsetOptions based on completed values
|
||||
func (o *UnsetOptions) Validate() error {
|
||||
if !o.Context.LocalConfigProvider.Exists() {
|
||||
return fmt.Errorf("the directory doesn't contain a component. Use 'odo create' to create a component")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run contains the logic for the command
|
||||
func (o *UnsetOptions) Run() error {
|
||||
if o.envArrayFlag != nil {
|
||||
|
||||
if err := o.EnvSpecificInfo.GetDevfileObj().RemoveEnvVars(o.envArrayFlag); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Success("Environment variables were successfully updated")
|
||||
if o.nowFlag {
|
||||
return o.DevfilePush()
|
||||
}
|
||||
log.Italic("\nRun `odo push` command to apply changes to the cluster")
|
||||
return nil
|
||||
}
|
||||
if isSet := config.IsSetInDevfile(o.EnvSpecificInfo.GetDevfileObj(), o.paramName); isSet {
|
||||
if !o.forceFlag && !ui.Proceed(fmt.Sprintf("Do you want to unset %s in the devfile", o.paramName)) {
|
||||
fmt.Println("Aborted by the user.")
|
||||
return nil
|
||||
}
|
||||
err := config.DeleteDevfileConfiguration(o.EnvSpecificInfo.GetDevfileObj(), strings.ToLower(o.paramName))
|
||||
log.Success("Devfile was successfully updated.")
|
||||
if o.nowFlag {
|
||||
return o.DevfilePush()
|
||||
}
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("config already unset, cannot unset a configuration which is not set")
|
||||
}
|
||||
|
||||
// NewCmdUnset implements the config unset odo command
|
||||
func NewCmdUnset(name, fullName string) *cobra.Command {
|
||||
// The error is not handled at this point, it will be handled during Context creation
|
||||
kubclient, _ := kclient.New()
|
||||
prefClient, err := preference.NewClient()
|
||||
if err != nil {
|
||||
odoutil.LogErrorAndExit(err, "unable to set preference, something is wrong with odo, kindly raise an issue at https://github.com/redhat-developer/odo/issues/new?template=Bug.md")
|
||||
}
|
||||
o := NewUnsetOptions(project.NewClient(kubclient), prefClient)
|
||||
configurationUnsetCmd := &cobra.Command{
|
||||
Use: name,
|
||||
Short: "Unset a value in odo config file",
|
||||
Long: fmt.Sprintf(unsetLongDesc, config.FormatDevfileSupportedParameters()),
|
||||
Example: fmt.Sprintf("\n"+devfileUnsetExample, fullName, config.Name, config.Ports, config.Memory),
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if o.envArrayFlag != nil {
|
||||
// no args are needed
|
||||
if len(args) > 0 {
|
||||
return fmt.Errorf("expected 0 args")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("please provide a parameter name")
|
||||
} else if len(args) > 1 {
|
||||
return fmt.Errorf("only one parameter is allowed")
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
}, Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(o, cmd, args)
|
||||
},
|
||||
}
|
||||
configurationUnsetCmd.Flags().BoolVarP(&o.forceFlag, "force", "f", false, "Don't ask for confirmation, unsetting the config directly")
|
||||
configurationUnsetCmd.Flags().StringSliceVarP(&o.envArrayFlag, "env", "e", nil, "Unset the environment variables in config")
|
||||
o.AddContextFlag(configurationUnsetCmd)
|
||||
odoutil.AddNowFlag(configurationUnsetCmd, &o.nowFlag)
|
||||
return configurationUnsetCmd
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/component"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
"github.com/redhat-developer/odo/pkg/machineoutput"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||
"github.com/spf13/cobra"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
const viewCommandName = "view"
|
||||
|
||||
var viewExample = ktemplates.Examples(`# For viewing the current configuration from devfile or local config file
|
||||
%[1]s
|
||||
|
||||
`)
|
||||
|
||||
// ViewOptions encapsulates the options for the command
|
||||
type ViewOptions struct {
|
||||
// Context
|
||||
*genericclioptions.Context
|
||||
|
||||
// Flags
|
||||
contextFlag string
|
||||
}
|
||||
|
||||
// NewViewOptions creates a new ViewOptions instance
|
||||
func NewViewOptions() *ViewOptions {
|
||||
return &ViewOptions{}
|
||||
}
|
||||
|
||||
// Complete completes ViewOptions after they've been created
|
||||
func (o *ViewOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
params := genericclioptions.NewCreateParameters(cmdline).NeedDevfile(o.contextFlag)
|
||||
o.Context, err = genericclioptions.New(params)
|
||||
return err
|
||||
}
|
||||
|
||||
// Validate validates the ViewOptions based on completed values
|
||||
func (o *ViewOptions) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run contains the logic for the command
|
||||
func (o *ViewOptions) Run() (err error) {
|
||||
w := tabwriter.NewWriter(os.Stdout, 5, 2, 2, ' ', tabwriter.TabIndent)
|
||||
repr, err := component.ToDevfileRepresentation(o.Context.EnvSpecificInfo.GetDevfileObj())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if log.IsJSON() {
|
||||
machineoutput.OutputSuccess(component.WrapFromJSONOutput(repr))
|
||||
return nil
|
||||
}
|
||||
representation, err := yaml.Marshal(repr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintln(w, string(representation))
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewCmdView implements the config view odo command
|
||||
func NewCmdView(name, fullName string) *cobra.Command {
|
||||
o := NewViewOptions()
|
||||
configurationViewCmd := &cobra.Command{
|
||||
Use: name,
|
||||
Short: "View current configuration values",
|
||||
Long: "View current configuration values",
|
||||
Annotations: map[string]string{"machineoutput": "json"},
|
||||
Example: fmt.Sprintf(fmt.Sprint("\n", viewExample), fullName),
|
||||
Args: cobra.ExactArgs(0),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(o, cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
odoutil.AddContextFlag(configurationViewCmd, &o.contextFlag)
|
||||
|
||||
return configurationViewCmd
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package debug
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/odo/util"
|
||||
"github.com/spf13/cobra"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
const (
|
||||
// RecommendedCommandName is the recommended debug command name
|
||||
RecommendedCommandName = "debug"
|
||||
)
|
||||
|
||||
var debugLongDesc = ktemplates.LongDesc(`Debug allows you to remotely debug your application.`)
|
||||
|
||||
func NewCmdDebug(name, fullName string) *cobra.Command {
|
||||
|
||||
portforwardCmd := NewCmdPortForward(portforwardCommandName, util.GetFullName(fullName, portforwardCommandName))
|
||||
infoCmd := NewCmdInfo(infoCommandName, util.GetFullName(fullName, infoCommandName))
|
||||
|
||||
debugCmd := &cobra.Command{
|
||||
Use: name,
|
||||
Short: "Debug commands",
|
||||
Example: fmt.Sprintf("%s\n\n%s",
|
||||
portforwardCmd.Example,
|
||||
infoCmd.Example),
|
||||
Long: debugLongDesc,
|
||||
Aliases: []string{"d"},
|
||||
}
|
||||
|
||||
debugCmd.SetUsageTemplate(util.CmdUsageTemplate)
|
||||
debugCmd.AddCommand(portforwardCmd)
|
||||
debugCmd.AddCommand(infoCmd)
|
||||
debugCmd.Annotations = map[string]string{"command": "main"}
|
||||
|
||||
return debugCmd
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
package debug
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/debug"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
"github.com/redhat-developer/odo/pkg/machineoutput"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||
"github.com/spf13/cobra"
|
||||
k8sgenclioptions "k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
// InfoOptions contains all the options for running the info cli command.
|
||||
type InfoOptions struct {
|
||||
// Context
|
||||
*genericclioptions.Context
|
||||
|
||||
// Flags
|
||||
contextFlag string
|
||||
|
||||
// Port forwarder backend
|
||||
PortForwarder *debug.DefaultPortForwarder
|
||||
}
|
||||
|
||||
var (
|
||||
infoLong = templates.LongDesc(`
|
||||
Gets information regarding any debug session of the component.
|
||||
`)
|
||||
|
||||
infoExample = templates.Examples(`
|
||||
# Get information regarding any debug session of the component
|
||||
odo debug info
|
||||
|
||||
`)
|
||||
)
|
||||
|
||||
const (
|
||||
infoCommandName = "info"
|
||||
)
|
||||
|
||||
func NewInfoOptions() *InfoOptions {
|
||||
return &InfoOptions{}
|
||||
}
|
||||
|
||||
// Complete completes all the required options for port-forward cmd.
|
||||
func (o *InfoOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
o.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Using Discard streams because nothing important is logged
|
||||
o.PortForwarder = debug.NewDefaultPortForwarder(o.Context.EnvSpecificInfo.GetName(), o.Context.GetApplication(), o.Context.EnvSpecificInfo.GetNamespace(), o.KClient, k8sgenclioptions.NewTestIOStreamsDiscard())
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Validate validates all the required options for port-forward cmd.
|
||||
func (o InfoOptions) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run implements all the necessary functionality for port-forward cmd.
|
||||
func (o InfoOptions) Run() error {
|
||||
if debugInfo, debugging := debug.GetInfo(o.PortForwarder); debugging {
|
||||
if log.IsJSON() {
|
||||
machineoutput.OutputSuccess(debugInfo)
|
||||
} else {
|
||||
log.Infof("Debug is running for the component on the local port : %v", debugInfo.Spec.LocalPort)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("debug is not running for the component %v", o.Context.EnvSpecificInfo.GetName())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewCmdInfo implements the debug info odo command
|
||||
func NewCmdInfo(name, fullName string) *cobra.Command {
|
||||
|
||||
opts := NewInfoOptions()
|
||||
cmd := &cobra.Command{
|
||||
Use: name,
|
||||
Short: "Displays debug info of a component",
|
||||
Long: infoLong,
|
||||
Example: infoExample,
|
||||
Annotations: map[string]string{"machineoutput": "json"},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(opts, cmd, args)
|
||||
},
|
||||
}
|
||||
odoutil.AddContextFlag(cmd, &opts.contextFlag)
|
||||
|
||||
return cmd
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
package debug
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/debug"
|
||||
"github.com/redhat-developer/odo/pkg/devfile/location"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||
"github.com/redhat-developer/odo/pkg/util"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
k8sgenclioptions "k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultDebugPort is the default port used for debugging on remote pod
|
||||
DefaultDebugPort = 5858
|
||||
)
|
||||
|
||||
// PortForwardOptions contains all the options for running the port-forward cli command.
|
||||
type PortForwardOptions struct {
|
||||
// Context
|
||||
*genericclioptions.Context
|
||||
|
||||
// Flags
|
||||
contextFlag string
|
||||
localPortFlag int
|
||||
|
||||
// PortPair is the combination of local and remote port in the format "local:remote"
|
||||
PortPair string
|
||||
|
||||
// Port forwarder backend
|
||||
PortForwarder *debug.DefaultPortForwarder
|
||||
|
||||
// StopChannel is used to stop port forwarding
|
||||
StopChannel chan struct{}
|
||||
|
||||
// ReadChannel is used to receive status of port forwarding ( ready or not ready )
|
||||
ReadyChannel chan struct{}
|
||||
}
|
||||
|
||||
var (
|
||||
portforwardLong = templates.LongDesc(`Forward a local port to a remote port on the pod where the application is listening for a debugger. By default the local port and the remote port will be same. To change the local port you can use --local-port argument and to change the remote port use "odo env set DebugPort <port>"
|
||||
`)
|
||||
|
||||
portforwardExample = templates.Examples(`
|
||||
# Listen on default port and forwarding to the default port in the pod
|
||||
odo debug port-forward
|
||||
|
||||
# Listen on the 5000 port locally, forwarding to default port in the pod
|
||||
odo debug port-forward --local-port 5000
|
||||
|
||||
`)
|
||||
)
|
||||
|
||||
const (
|
||||
portforwardCommandName = "port-forward"
|
||||
)
|
||||
|
||||
// NewPortForwardOptions returns the PortForwardOptions struct
|
||||
func NewPortForwardOptions() *PortForwardOptions {
|
||||
return &PortForwardOptions{}
|
||||
}
|
||||
|
||||
// Complete completes all the required options for port-forward cmd.
|
||||
func (o *PortForwardOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
o.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
remotePort := o.Context.EnvSpecificInfo.GetDebugPort()
|
||||
|
||||
// try to listen on the given local port and check if the port is free or not
|
||||
addressLook := "localhost:" + strconv.Itoa(o.localPortFlag)
|
||||
listener, err := net.Listen("tcp", addressLook)
|
||||
if err != nil {
|
||||
// if the local-port flag is set by the user, return the error and stop execution
|
||||
if cmdline.IsFlagSet("local-port") {
|
||||
return err
|
||||
}
|
||||
// else display a error message and auto select a new free port
|
||||
log.Errorf("the local debug port %v is not free, cause: %v", o.localPortFlag, err)
|
||||
o.localPortFlag, err = util.HTTPGetFreePort()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Infof("The local port %v is auto selected", o.localPortFlag)
|
||||
} else {
|
||||
err = listener.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
o.PortPair = fmt.Sprintf("%d:%d", o.localPortFlag, remotePort)
|
||||
|
||||
// Using Discard streams because nothing important is logged
|
||||
o.PortForwarder = debug.NewDefaultPortForwarder(o.Context.EnvSpecificInfo.GetName(), o.Context.GetApplication(), o.Context.EnvSpecificInfo.GetNamespace(), o.KClient, k8sgenclioptions.NewTestIOStreamsDiscard())
|
||||
|
||||
o.StopChannel = make(chan struct{}, 1)
|
||||
o.ReadyChannel = make(chan struct{})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates all the required options for port-forward cmd.
|
||||
func (o PortForwardOptions) Validate() error {
|
||||
if len(o.PortPair) < 1 {
|
||||
return fmt.Errorf("ports cannot be empty")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run implements all the necessary functionality for port-forward cmd.
|
||||
func (o PortForwardOptions) Run() error {
|
||||
|
||||
signals := make(chan os.Signal, 1)
|
||||
signal.Notify(signals, os.Interrupt,
|
||||
syscall.SIGHUP,
|
||||
syscall.SIGINT,
|
||||
syscall.SIGTERM,
|
||||
syscall.SIGQUIT)
|
||||
defer signal.Stop(signals)
|
||||
defer os.RemoveAll(debug.GetDebugInfoFilePath(o.Context.EnvSpecificInfo.GetName(), o.Context.GetApplication(), o.Context.EnvSpecificInfo.GetNamespace()))
|
||||
|
||||
go func() {
|
||||
<-signals
|
||||
if o.StopChannel != nil {
|
||||
close(o.StopChannel)
|
||||
}
|
||||
}()
|
||||
|
||||
err := debug.CreateDebugInfoFile(o.PortForwarder, o.PortPair)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
devfilePath := location.DevfileLocation(o.contextFlag)
|
||||
return o.PortForwarder.ForwardPorts(o.PortPair, o.StopChannel, o.ReadyChannel, util.CheckPathExists(devfilePath))
|
||||
}
|
||||
|
||||
// NewCmdPortForward implements the port-forward odo command
|
||||
func NewCmdPortForward(name, fullName string) *cobra.Command {
|
||||
|
||||
opts := NewPortForwardOptions()
|
||||
cmd := &cobra.Command{
|
||||
Use: name,
|
||||
Short: "Forward one or more local ports to a pod",
|
||||
Long: portforwardLong,
|
||||
Example: portforwardExample,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(opts, cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
odoutil.AddContextFlag(cmd, &opts.contextFlag)
|
||||
cmd.Flags().IntVarP(&opts.localPortFlag, "local-port", "l", DefaultDebugPort, "Set the local port")
|
||||
|
||||
return cmd
|
||||
}
|
||||
68
pkg/odo/cli/env/env.go
vendored
68
pkg/odo/cli/env/env.go
vendored
@@ -1,68 +0,0 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/odo/util"
|
||||
genericUtil "github.com/redhat-developer/odo/pkg/util"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
// RecommendedCommandName is the recommended env command name
|
||||
const RecommendedCommandName = "env"
|
||||
|
||||
const (
|
||||
nameParameter = "Name"
|
||||
nameParameterDescription = "Use this value to set component name"
|
||||
projectParameter = "Project"
|
||||
projectParameterDescription = "Use this value to set component project"
|
||||
debugportParameter = "DebugPort"
|
||||
debugportParameterDescription = "Use this value to set component debug port"
|
||||
)
|
||||
|
||||
var envLongDesc = ktemplates.LongDesc(`Modifies odo specific configuration settings within environment file`)
|
||||
|
||||
// NewCmdEnv implements the environment configuration command
|
||||
func NewCmdEnv(name, fullName string) *cobra.Command {
|
||||
envViewCmd := NewCmdView(viewCommandName, util.GetFullName(fullName, viewCommandName))
|
||||
envSetCmd := NewCmdSet(setCommandName, util.GetFullName(fullName, setCommandName))
|
||||
envUnsetCmd := NewCmdUnset(unsetCommandName, util.GetFullName(fullName, unsetCommandName))
|
||||
envCmd := &cobra.Command{
|
||||
Use: name,
|
||||
Short: "Change or view environment configuration",
|
||||
Long: envLongDesc,
|
||||
Example: fmt.Sprintf("%s\n\n%s\n\n%s",
|
||||
envViewCmd.Example,
|
||||
envSetCmd.Example,
|
||||
envUnsetCmd.Example,
|
||||
),
|
||||
}
|
||||
|
||||
envCmd.AddCommand(envViewCmd, envSetCmd, envUnsetCmd)
|
||||
envCmd.SetUsageTemplate(util.CmdUsageTemplate)
|
||||
envCmd.Annotations = map[string]string{"command": "main"}
|
||||
|
||||
return envCmd
|
||||
}
|
||||
|
||||
func printSupportedParameters(supportedParameters map[string]string) string {
|
||||
output := "\n\nAvailable parameters:\n"
|
||||
for _, parameter := range genericUtil.GetSortedKeys(supportedParameters) {
|
||||
output = fmt.Sprintf("%s %s: %s\n", output, parameter, supportedParameters[parameter])
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
func isSupportedParameter(parameter string, supportedParameters map[string]string) bool {
|
||||
for supportedParameter := range supportedParameters {
|
||||
if strings.EqualFold(supportedParameter, parameter) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
61
pkg/odo/cli/env/env_test.go
vendored
61
pkg/odo/cli/env/env_test.go
vendored
@@ -1,61 +0,0 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPrintSupportedParameters(t *testing.T) {
|
||||
supportedSetParameters := map[string]string{
|
||||
nameParameter: nameParameterDescription,
|
||||
projectParameter: projectParameterDescription,
|
||||
debugportParameter: debugportParameterDescription,
|
||||
}
|
||||
|
||||
wantSetParameters := `Available parameters:
|
||||
DebugPort: Use this value to set component debug port
|
||||
Name: Use this value to set component name
|
||||
Project: Use this value to set component project`
|
||||
|
||||
supportedUnsetParameters := map[string]string{
|
||||
debugportParameter: debugportParameterDescription,
|
||||
}
|
||||
|
||||
wantUnsetParameters := `Available parameters:
|
||||
DebugPort: Use this value to set component debug port`
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
supportedParameters map[string]string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "Case 1: Test print supported set parameters",
|
||||
supportedParameters: supportedSetParameters,
|
||||
want: wantSetParameters,
|
||||
},
|
||||
{
|
||||
name: "Case 2: Test print supported unset parameters",
|
||||
supportedParameters: supportedUnsetParameters,
|
||||
want: wantUnsetParameters,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := strings.TrimSpace(printSupportedParameters(tt.supportedParameters))
|
||||
|
||||
gotStrings := strings.Split(got, "\n")
|
||||
wantStrings := strings.Split(tt.want, "\n")
|
||||
|
||||
sort.Strings(gotStrings)
|
||||
sort.Strings(wantStrings)
|
||||
|
||||
if !reflect.DeepEqual(wantStrings, gotStrings) {
|
||||
t.Errorf("\nGot: %s\nWant: %s", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
138
pkg/odo/cli/env/set.go
vendored
138
pkg/odo/cli/env/set.go
vendored
@@ -1,138 +0,0 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/envinfo"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/ui"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
"github.com/spf13/cobra"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
const setCommandName = "set"
|
||||
|
||||
var (
|
||||
setLongDesc = ktemplates.LongDesc(`
|
||||
Set an individual value in the odo environment file
|
||||
`)
|
||||
|
||||
setExample = ktemplates.Examples(`
|
||||
# Set an individual value in the odo environment file
|
||||
%[1]s %[2]s myNodejs
|
||||
%[1]s %[3]s myProject
|
||||
%[1]s %[4]s 8888
|
||||
`)
|
||||
)
|
||||
|
||||
var (
|
||||
supportedSetParameters = map[string]string{
|
||||
nameParameter: nameParameterDescription,
|
||||
projectParameter: projectParameterDescription,
|
||||
debugportParameter: debugportParameterDescription,
|
||||
}
|
||||
)
|
||||
|
||||
// SetOptions encapsulates the options for the command
|
||||
type SetOptions struct {
|
||||
// Env context
|
||||
cfg *envinfo.EnvSpecificInfo
|
||||
|
||||
// Parameters
|
||||
paramName string
|
||||
paramValue string
|
||||
|
||||
// Flags
|
||||
contextFlag string
|
||||
forceFlag bool
|
||||
}
|
||||
|
||||
// NewSetOptions creates a new SetOptions instance
|
||||
func NewSetOptions() *SetOptions {
|
||||
return &SetOptions{}
|
||||
}
|
||||
|
||||
// Complete completes SetOptions after they've been created
|
||||
func (o *SetOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
o.cfg, err = envinfo.NewEnvSpecificInfo(o.contextFlag)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to load environment file")
|
||||
}
|
||||
|
||||
o.paramName = args[0]
|
||||
o.paramValue = args[1]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates the SetOptions based on completed values
|
||||
func (o *SetOptions) Validate() (err error) {
|
||||
if !o.cfg.Exists() {
|
||||
return errors.Errorf("the context directory doesn't contain a component, please refer `odo create --help` to create a component")
|
||||
}
|
||||
|
||||
if !isSupportedParameter(o.paramName, supportedSetParameters) {
|
||||
return errors.Errorf("%q is not a valid parameter to set, please refer `odo env set --help` to set a valid parameter", o.paramName)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run contains the logic for the command
|
||||
func (o *SetOptions) Run() (err error) {
|
||||
if !o.forceFlag {
|
||||
if isSet := o.cfg.IsSet(o.paramName); isSet {
|
||||
if !ui.Proceed(fmt.Sprintf("%v is already set. Do you want to override it in the environment", o.paramName)) {
|
||||
log.Info("Aborted by the user")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = o.cfg.SetConfiguration(strings.ToLower(o.paramName), o.paramValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("Environment was successfully updated")
|
||||
if strings.ToLower(o.paramName) == "name" || strings.ToLower(o.paramName) == "project" {
|
||||
log.Warningf("Updated %q would create a new component", o.paramName)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewCmdSet implements the env set odo command
|
||||
func NewCmdSet(name, fullName string) *cobra.Command {
|
||||
o := NewSetOptions()
|
||||
envSetCmd := &cobra.Command{
|
||||
Use: name,
|
||||
Short: "Set a value in odo environment file",
|
||||
Long: setLongDesc + printSupportedParameters(supportedSetParameters),
|
||||
Example: fmt.Sprintf(fmt.Sprint(setExample), fullName,
|
||||
envinfo.Name, envinfo.Project, envinfo.DebugPort),
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 2 {
|
||||
return fmt.Errorf("please provide a parameter name and value")
|
||||
} else if len(args) > 2 {
|
||||
return fmt.Errorf("only one value per parameter is allowed")
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
}, Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(o, cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
envSetCmd.Flags().BoolVarP(&o.forceFlag, "force", "f", false, "Don't ask for confirmation, set the environment directly")
|
||||
envSetCmd.Flags().StringVar(&o.contextFlag, "context", "", "Use given context directory as a source for component settings")
|
||||
|
||||
return envSetCmd
|
||||
}
|
||||
129
pkg/odo/cli/env/unset.go
vendored
129
pkg/odo/cli/env/unset.go
vendored
@@ -1,129 +0,0 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/envinfo"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/ui"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
const unsetCommandName = "unset"
|
||||
|
||||
var (
|
||||
unsetLongDesc = ktemplates.LongDesc(`
|
||||
Unset an individual value in the odo environment file
|
||||
`)
|
||||
|
||||
unsetExample = ktemplates.Examples(`
|
||||
# Unset an individual value in the environment file
|
||||
%[1]s %[2]s
|
||||
`)
|
||||
)
|
||||
|
||||
var (
|
||||
supportedUnsetParameters = map[string]string{
|
||||
debugportParameter: debugportParameterDescription,
|
||||
}
|
||||
)
|
||||
|
||||
// UnsetOptions encapsulates the options for the command
|
||||
type UnsetOptions struct {
|
||||
// Env context
|
||||
cfg *envinfo.EnvSpecificInfo
|
||||
|
||||
// Parameters
|
||||
paramName string
|
||||
|
||||
// Flags
|
||||
contextFlag string
|
||||
forceFlag bool
|
||||
}
|
||||
|
||||
// NewUnsetOptions creates a new UnsetOptions instance
|
||||
func NewUnsetOptions() *UnsetOptions {
|
||||
return &UnsetOptions{}
|
||||
}
|
||||
|
||||
// Complete completes UnsetOptions after they've been created
|
||||
func (o *UnsetOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
o.cfg, err = envinfo.NewEnvSpecificInfo(o.contextFlag)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to load environment file")
|
||||
}
|
||||
|
||||
o.paramName = args[0]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates the UnsetOptions based on completed values
|
||||
func (o *UnsetOptions) Validate() (err error) {
|
||||
if !o.cfg.Exists() {
|
||||
return errors.Errorf("the context directory doesn't contain a component, please refer `odo create --help` to create a component")
|
||||
}
|
||||
|
||||
if !isSupportedParameter(o.paramName, supportedUnsetParameters) {
|
||||
return errors.Errorf("%q is not a valid parameter to unset, please refer `odo env unset --help` to unset a valid parameter", o.paramName)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run contains the logic for the command
|
||||
func (o *UnsetOptions) Run() (err error) {
|
||||
if !o.forceFlag {
|
||||
if isSet := o.cfg.IsSet(o.paramName); isSet {
|
||||
if !ui.Proceed(fmt.Sprintf("Do you want to unset %s in the environment", o.paramName)) {
|
||||
log.Infof("Aborted by the user")
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
return errors.New("environment already unset, cannot unset a environment which is not set")
|
||||
}
|
||||
}
|
||||
|
||||
err = o.cfg.DeleteConfiguration(strings.ToLower(o.paramName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("Environment was successfully updated")
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// NewCmdUnset implements the environment unset odo command
|
||||
func NewCmdUnset(name, fullName string) *cobra.Command {
|
||||
o := NewUnsetOptions()
|
||||
envUnsetCmd := &cobra.Command{
|
||||
Use: name,
|
||||
Short: "Unset a value in odo environment file",
|
||||
Long: unsetLongDesc + printSupportedParameters(supportedUnsetParameters),
|
||||
Example: fmt.Sprintf(fmt.Sprint(unsetExample), fullName, envinfo.DebugPort),
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("please provide a parameter name")
|
||||
} else if len(args) > 1 {
|
||||
return fmt.Errorf("only one parameter is allowed")
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
}, Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(o, cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
envUnsetCmd.Flags().BoolVarP(&o.forceFlag, "force", "f", false, "Don't ask for confirmation, unsetting the environment directly")
|
||||
envUnsetCmd.Flags().StringVar(&o.contextFlag, "context", "", "Use given context directory as a source for component settings")
|
||||
|
||||
return envUnsetCmd
|
||||
}
|
||||
93
pkg/odo/cli/env/view.go
vendored
93
pkg/odo/cli/env/view.go
vendored
@@ -1,93 +0,0 @@
|
||||
package env
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/redhat-developer/odo/pkg/envinfo"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
"github.com/redhat-developer/odo/pkg/machineoutput"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
"github.com/spf13/cobra"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
const viewCommandName = "view"
|
||||
|
||||
var (
|
||||
viewLongDesc = ktemplates.LongDesc(`
|
||||
View current values in odo environment file
|
||||
`)
|
||||
|
||||
viewExample = ktemplates.Examples(`
|
||||
# For viewing the current environment configuration settings
|
||||
%[1]s
|
||||
`)
|
||||
)
|
||||
|
||||
// ViewOptions encapsulates the options for the command
|
||||
type ViewOptions struct {
|
||||
// Env context
|
||||
cfg *envinfo.EnvSpecificInfo
|
||||
|
||||
// Flags
|
||||
contextFlag string
|
||||
}
|
||||
|
||||
// NewViewOptions creates a new ViewOptions instance
|
||||
func NewViewOptions() *ViewOptions {
|
||||
return &ViewOptions{}
|
||||
}
|
||||
|
||||
// Complete completes ViewOptions after they've been created
|
||||
func (o *ViewOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
o.cfg, err = envinfo.NewEnvSpecificInfo(o.contextFlag)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to load environment file")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates the ViewOptions based on completed values
|
||||
func (o *ViewOptions) Validate() (err error) {
|
||||
if !o.cfg.Exists() {
|
||||
return errors.Errorf("the context directory doesn't contain a component, please refer `odo create --help` on how to create a component")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run contains the logic for the command
|
||||
func (o *ViewOptions) Run() (err error) {
|
||||
info := envinfo.NewInfo(o.cfg.GetComponentSettings())
|
||||
if log.IsJSON() {
|
||||
machineoutput.OutputSuccess(info)
|
||||
return
|
||||
}
|
||||
info.Output(os.Stdout)
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewCmdView implements the env view odo command
|
||||
func NewCmdView(name, fullName string) *cobra.Command {
|
||||
o := NewViewOptions()
|
||||
envViewCmd := &cobra.Command{
|
||||
Use: name,
|
||||
Short: "View current values in odo environment file",
|
||||
Long: viewLongDesc,
|
||||
Example: fmt.Sprintf(fmt.Sprint(viewExample), fullName),
|
||||
Annotations: map[string]string{"machineoutput": "json"},
|
||||
|
||||
Args: cobra.ExactArgs(0),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(o, cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
envViewCmd.Flags().StringVar(&o.contextFlag, "context", "", "Use given context directory as a source for component settings")
|
||||
|
||||
return envViewCmd
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
# See the OWNERS docs: https://git.k8s.io/community/contributors/guide/owners.md
|
||||
|
||||
approvers:
|
||||
- dharmit
|
||||
- mik-dass
|
||||
|
||||
reviewers:
|
||||
- dharmit
|
||||
- mik-dass
|
||||
@@ -1,144 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
"github.com/redhat-developer/odo/pkg/odo/util"
|
||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||
"github.com/spf13/cobra"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
const (
|
||||
createRecommendedCommandName = "create"
|
||||
)
|
||||
|
||||
var (
|
||||
createOperatorExample = ktemplates.Examples(`
|
||||
# Create new EtcdCluster service from etcdoperator.v0.9.4 operator.
|
||||
%[1]s etcdoperator.v0.9.4/EtcdCluster
|
||||
|
||||
# Create new EtcdCluster service from etcdoperator.v0.9.4 operator and puts the service definition in the devfile instead of a separate file.
|
||||
%[1]s etcdoperator.v0.9.4/EtcdCluster --inlined`)
|
||||
|
||||
createShortDesc = `Create a new service from Operator Hub and deploy it on Kubernetes or OpenShift.`
|
||||
|
||||
createLongDesc = ktemplates.LongDesc(`
|
||||
Create a new service from Operator Hub and deploy it on Kubernetes or OpenShift.
|
||||
|
||||
Service creation can be performed from a valid component directory (one containing a devfile.yaml) only.
|
||||
|
||||
To create the service from outside a component directory, specify path to a valid component directory using "--context" flag.
|
||||
|
||||
When creating a service using Operator Hub, provide a service name along with Operator name.
|
||||
|
||||
For a full list of service types, use: 'odo catalog list services'`)
|
||||
)
|
||||
|
||||
// CreateOptions encapsulates the options for the odo service create command
|
||||
type CreateOptions struct {
|
||||
// Context
|
||||
*genericclioptions.Context
|
||||
|
||||
// Flags
|
||||
parametersFlag []string
|
||||
waitFlag bool
|
||||
contextFlag string
|
||||
DryRunFlag bool
|
||||
fromFileFlag string
|
||||
inlinedFlag bool
|
||||
|
||||
// ServiceType corresponds to the service class name
|
||||
ServiceType string
|
||||
// ServiceName is how the service will be named and known by odo
|
||||
ServiceName string
|
||||
// ParametersMap is populated from the flag-provided values (parameters) and/or the interactive mode and is the expected format by the business logic
|
||||
ParametersMap map[string]string
|
||||
// interactive specifies whether the command operates in interactive mode or not
|
||||
interactive bool
|
||||
// CmdFullName records the command's full name
|
||||
CmdFullName string
|
||||
// Backend is the service provider backend providing the service requested by the user
|
||||
Backend ServiceProviderBackend
|
||||
}
|
||||
|
||||
// NewCreateOptions creates a new CreateOptions instance
|
||||
func NewCreateOptions() *CreateOptions {
|
||||
return &CreateOptions{}
|
||||
}
|
||||
|
||||
// Complete completes CreateOptions after they've been created
|
||||
func (o *CreateOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
o.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline).NeedDevfile(o.contextFlag))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// we convert the param list provided in the format of key=value list
|
||||
// to a map
|
||||
o.ParametersMap, err = util.MapFromParameters(o.parametersFlag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = validDevfileDirectory(o.contextFlag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// if no args are provided and if request is not from file, user wants interactive mode
|
||||
if o.fromFileFlag == "" && len(args) == 0 {
|
||||
return fmt.Errorf("odo doesn't support interactive mode for creating Operator backed service")
|
||||
}
|
||||
o.Backend = NewOperatorBackend()
|
||||
o.interactive = false
|
||||
return o.Backend.CompleteServiceCreate(o, args)
|
||||
}
|
||||
|
||||
// Validate validates the CreateOptions based on completed values
|
||||
func (o *CreateOptions) Validate() (err error) {
|
||||
return o.Backend.ValidateServiceCreate(o)
|
||||
}
|
||||
|
||||
// Run contains the logic for the odo service create command
|
||||
func (o *CreateOptions) Run() (err error) {
|
||||
err = o.Backend.RunServiceCreate(o)
|
||||
if err != nil {
|
||||
return fmt.Errorf("service %q already exists in configuration", o.ServiceName)
|
||||
}
|
||||
|
||||
// Information on what to do next; don't do this if "--dry-run" was requested as it gets appended to the file
|
||||
if !o.DryRunFlag {
|
||||
log.Info("Successfully added service to the configuration; do 'odo push' to create service on the cluster")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewCmdServiceCreate implements the odo service create command.
|
||||
func NewCmdServiceCreate(name, fullName string) *cobra.Command {
|
||||
o := NewCreateOptions()
|
||||
o.CmdFullName = fullName
|
||||
serviceCreateCmd := &cobra.Command{
|
||||
Use: name + " <operator_type>/<crd_name> [service_name] [flags]",
|
||||
Short: createShortDesc,
|
||||
Long: createLongDesc,
|
||||
Example: fmt.Sprintf(createOperatorExample, fullName),
|
||||
Args: cobra.RangeArgs(0, 2),
|
||||
Annotations: map[string]string{"machineoutput": "json"},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(o, cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
serviceCreateCmd.Flags().BoolVar(&o.inlinedFlag, "inlined", false, "Puts the service definition in the devfile instead of a separate file")
|
||||
serviceCreateCmd.Flags().BoolVar(&o.DryRunFlag, "dry-run", false, "Print the yaml specificiation that will be used to create the operator backed service")
|
||||
// remove this feature after enabling service create interactive mode for operator backed services
|
||||
serviceCreateCmd.Flags().StringVar(&o.fromFileFlag, "from-file", "", "Path to the file containing yaml specification to use to start operator backed service")
|
||||
|
||||
serviceCreateCmd.Flags().StringArrayVarP(&o.parametersFlag, "parameters", "p", []string{}, "Parameters to be used to create Operator backed service where a parameter is expressed as <key>=<value")
|
||||
serviceCreateCmd.Flags().BoolVarP(&o.waitFlag, "wait", "w", false, "Wait until the service is ready")
|
||||
odoutil.AddContextFlag(serviceCreateCmd, &o.contextFlag)
|
||||
return serviceCreateCmd
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/service"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/ui"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/klog"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
const deleteRecommendedCommandName = "delete"
|
||||
|
||||
var (
|
||||
deleteExample = ktemplates.Examples(`
|
||||
# Delete the service named 'mysql-persistent'
|
||||
%[1]s mysql-persistent`)
|
||||
|
||||
deleteLongDesc = ktemplates.LongDesc(`
|
||||
Delete an existing service`)
|
||||
)
|
||||
|
||||
// DeleteOptions encapsulates the options for the odo service delete command
|
||||
type DeleteOptions struct {
|
||||
// Context
|
||||
*genericclioptions.Context
|
||||
|
||||
// Parameters
|
||||
serviceName string
|
||||
|
||||
// Flags
|
||||
forceFlag bool
|
||||
contextFlag string
|
||||
|
||||
// Backend is the service provider backend that was used to create the service
|
||||
Backend ServiceProviderBackend
|
||||
}
|
||||
|
||||
// NewDeleteOptions creates a new DeleteOptions instance
|
||||
func NewDeleteOptions() *DeleteOptions {
|
||||
return &DeleteOptions{}
|
||||
}
|
||||
|
||||
// Complete completes DeleteOptions after they've been created
|
||||
func (o *DeleteOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
o.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline).NeedDevfile(o.contextFlag))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = validDevfileDirectory(o.contextFlag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.serviceName = args[0]
|
||||
_, _, err = service.SplitServiceKindName(o.serviceName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid service name")
|
||||
}
|
||||
o.Backend = NewOperatorBackend()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates the DeleteOptions based on completed values
|
||||
func (o *DeleteOptions) Validate() (err error) {
|
||||
svcDefined, err := o.Backend.ServiceDefined(o.Context, o.serviceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !svcDefined {
|
||||
return fmt.Errorf("couldn't find service named %q. Refer %q to see list of defined services", o.serviceName, "odo service list")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run contains the logic for the odo service delete command
|
||||
func (o *DeleteOptions) Run() (err error) {
|
||||
if o.forceFlag || ui.Proceed(fmt.Sprintf("Are you sure you want to delete %v", o.serviceName)) {
|
||||
err = o.Backend.DeleteService(o, o.serviceName, o.GetApplication())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Infof("Service %q has been successfully deleted; do 'odo push' to delete service from the cluster", o.serviceName)
|
||||
} else {
|
||||
log.Errorf("Aborting deletion of service: %v", o.serviceName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewCmdServiceDelete implements the odo service delete command.
|
||||
func NewCmdServiceDelete(name, fullName string) *cobra.Command {
|
||||
o := NewDeleteOptions()
|
||||
serviceDeleteCmd := &cobra.Command{
|
||||
Use: name + " <service_name>",
|
||||
Short: "Delete an existing service",
|
||||
Long: deleteLongDesc,
|
||||
Example: fmt.Sprintf(deleteExample, fullName),
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
klog.V(4).Infof("service delete called\n args: %#v", strings.Join(args, " "))
|
||||
genericclioptions.GenericRun(o, cmd, args)
|
||||
},
|
||||
}
|
||||
serviceDeleteCmd.Flags().BoolVarP(&o.forceFlag, "force", "f", false, "Delete service without prompting")
|
||||
odoutil.AddContextFlag(serviceDeleteCmd, &o.contextFlag)
|
||||
return serviceDeleteCmd
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||
"github.com/redhat-developer/odo/pkg/service"
|
||||
svc "github.com/redhat-developer/odo/pkg/service"
|
||||
"github.com/spf13/cobra"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
const describeRecommendedCommandName = "describe"
|
||||
|
||||
var (
|
||||
describeExample = ktemplates.Examples(`
|
||||
# Describe the service named 'mysql-persistent'
|
||||
%[1]s mysql-persistent`)
|
||||
|
||||
describeLongDesc = ktemplates.LongDesc(`
|
||||
Describe an existing service, either defined locally or deployed to the cluster`)
|
||||
)
|
||||
|
||||
// DescribeOptions encapsulates the options for the odo service describe command
|
||||
type DescribeOptions struct {
|
||||
// Context
|
||||
*genericclioptions.Context
|
||||
|
||||
// Parameters
|
||||
serviceName string
|
||||
|
||||
// Flags
|
||||
contextFlag string
|
||||
|
||||
// Backend is the service provider backend that was used to create the service
|
||||
Backend ServiceProviderBackend
|
||||
}
|
||||
|
||||
// NewDescribeOptions creates a new DescribeOptions instance
|
||||
func NewDescribeOptions() *DescribeOptions {
|
||||
return &DescribeOptions{}
|
||||
}
|
||||
|
||||
func (o *DescribeOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
o.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline).NeedDevfile(o.contextFlag))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = validDevfileDirectory(o.contextFlag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.serviceName = args[0]
|
||||
_, _, err = service.SplitServiceKindName(o.serviceName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid service name")
|
||||
}
|
||||
o.Backend = NewOperatorBackend()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates the DescribeOptions based on completed values
|
||||
func (o *DescribeOptions) Validate() error {
|
||||
svcDefined, err := o.Backend.ServiceDefined(o.Context, o.serviceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
svcDeployed, err := svc.OperatorSvcExists(o.KClient, o.serviceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !svcDefined && !svcDeployed {
|
||||
return fmt.Errorf("couldn't find service named %q. Refer %q to see list of defined services", o.serviceName, "odo service list")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run contains the logic for the odo service describe command
|
||||
func (o *DescribeOptions) Run() error {
|
||||
return o.Backend.DescribeService(o, o.serviceName, o.GetApplication())
|
||||
}
|
||||
|
||||
// NewCmdDescribe implements the describe odo command
|
||||
func NewCmdServiceDescribe(name, fullName string) *cobra.Command {
|
||||
do := NewDescribeOptions()
|
||||
|
||||
var describeCmd = &cobra.Command{
|
||||
Use: fmt.Sprintf("%s [service_name]", name),
|
||||
Short: "Describe an existing service",
|
||||
Long: describeLongDesc,
|
||||
Example: fmt.Sprintf(describeExample, fullName),
|
||||
Args: cobra.ExactArgs(1),
|
||||
Annotations: map[string]string{"machineoutput": "json"},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(do, cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
odoutil.AddContextFlag(describeCmd, &do.contextFlag)
|
||||
return describeCmd
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
)
|
||||
|
||||
// ServiceProviderBackend is implemented by the backends supported by odo
|
||||
// It is used in "odo service create" and "odo service delete"
|
||||
type ServiceProviderBackend interface {
|
||||
CompleteServiceCreate(options *CreateOptions, args []string) error
|
||||
ValidateServiceCreate(options *CreateOptions) error
|
||||
RunServiceCreate(options *CreateOptions) error
|
||||
|
||||
ServiceDefined(context *genericclioptions.Context, name string) (bool, error)
|
||||
DeleteService(options *DeleteOptions, serviceName, app string) error
|
||||
DescribeService(options *DescribeOptions, serviceName, app string) error
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||
"github.com/spf13/cobra"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
const listRecommendedCommandName = "list"
|
||||
|
||||
var (
|
||||
listExample = ktemplates.Examples(`
|
||||
# List all services in the application
|
||||
%[1]s`)
|
||||
listLongDesc = ktemplates.LongDesc(`
|
||||
List all services in the current application
|
||||
`)
|
||||
)
|
||||
|
||||
// ServiceListOptions encapsulates the options for the odo service list command
|
||||
type ServiceListOptions struct {
|
||||
// Context
|
||||
*genericclioptions.Context
|
||||
|
||||
// Flags
|
||||
contextFlag string
|
||||
|
||||
// If true, Operator Hub is installed on the cluster
|
||||
csvSupport bool
|
||||
}
|
||||
|
||||
// NewServiceListOptions creates a new ServiceListOptions instance
|
||||
func NewServiceListOptions() *ServiceListOptions {
|
||||
return &ServiceListOptions{}
|
||||
}
|
||||
|
||||
// Complete completes ServiceListOptions after they've been created
|
||||
func (o *ServiceListOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
o.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline).NeedDevfile(o.contextFlag))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.csvSupport, err = o.KClient.IsCSVSupported()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !o.csvSupport {
|
||||
return fmt.Errorf("failed to list Operator backed services, make sure you have installed the Operators on the cluster")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates the ServiceListOptions based on completed values
|
||||
func (o *ServiceListOptions) Validate() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run contains the logic for the odo service list command
|
||||
func (o *ServiceListOptions) Run() (err error) {
|
||||
return o.listOperatorServices()
|
||||
}
|
||||
|
||||
// NewCmdServiceList implements the odo service list command.
|
||||
func NewCmdServiceList(name, fullName string) *cobra.Command {
|
||||
o := NewServiceListOptions()
|
||||
serviceListCmd := &cobra.Command{
|
||||
Use: name,
|
||||
Short: "List all services in the current application",
|
||||
Long: listLongDesc,
|
||||
Example: fmt.Sprintf(listExample, fullName),
|
||||
Args: cobra.NoArgs,
|
||||
Annotations: map[string]string{"machineoutput": "json"},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(o, cmd, args)
|
||||
},
|
||||
}
|
||||
odoutil.AddContextFlag(serviceListCmd, &o.contextFlag)
|
||||
return serviceListCmd
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
applabels "github.com/redhat-developer/odo/pkg/application/labels"
|
||||
cmplabels "github.com/redhat-developer/odo/pkg/component/labels"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
"github.com/redhat-developer/odo/pkg/machineoutput"
|
||||
svc "github.com/redhat-developer/odo/pkg/service"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
const ServiceItemKind = "Service"
|
||||
|
||||
type clusterInfo struct {
|
||||
Labels map[string]string `json:"labels"`
|
||||
CreationTimestamp time.Time `json:"creationTimestamp"`
|
||||
}
|
||||
|
||||
type serviceItem struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
ClusterInfo *clusterInfo `json:"clusterInfo,omitempty"`
|
||||
InDevfile bool `json:"inDevfile"`
|
||||
Deployed bool `json:"deployed"`
|
||||
Manifest map[string]interface{} `json:"manifest"`
|
||||
}
|
||||
|
||||
type serviceItemList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []serviceItem `json:"items"`
|
||||
}
|
||||
|
||||
func NewServiceItem(name string) *serviceItem {
|
||||
return &serviceItem{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: ServiceItemKind,
|
||||
APIVersion: machineoutput.APIVersion,
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// listOperatorServices lists the operator backed services
|
||||
// - deployed in the cluster
|
||||
// - defined in the current devfile
|
||||
func (o *ServiceListOptions) listOperatorServices() (err error) {
|
||||
|
||||
// get the services deployed
|
||||
var clusterList []unstructured.Unstructured
|
||||
clusterList, failedListingCR, err := svc.ListOperatorServices(o.KClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// get the services defined in the devfile
|
||||
// and the name of the component of the devfile
|
||||
var devfileList map[string]unstructured.Unstructured
|
||||
var devfileComponent string
|
||||
if o.EnvSpecificInfo != nil {
|
||||
devfileList, err = svc.ListDevfileServices(o.KClient, o.EnvSpecificInfo.GetDevfileObj(), o.contextFlag)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading devfile")
|
||||
}
|
||||
devfileComponent = o.EnvSpecificInfo.GetComponentSettings().Name
|
||||
}
|
||||
|
||||
servicesItems := mixServices(clusterList, devfileList)
|
||||
|
||||
if len(servicesItems.Items) == 0 {
|
||||
if len(failedListingCR) > 0 {
|
||||
fmt.Printf("Failed to fetch services for operator(s): %q\n\n", strings.Join(failedListingCR, ", "))
|
||||
}
|
||||
return fmt.Errorf("no operator backed services found in namespace: %s", o.KClient.GetCurrentNamespace())
|
||||
}
|
||||
|
||||
if log.IsJSON() {
|
||||
machineoutput.OutputSuccess(servicesItems)
|
||||
return nil
|
||||
}
|
||||
|
||||
// output result
|
||||
w := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent)
|
||||
fmt.Fprintln(w, "NAME", "\t", "MANAGED BY ODO", "\t", "STATE", "\t", "AGE")
|
||||
for i := range servicesItems.Items {
|
||||
item := servicesItems.Items[i]
|
||||
managedByOdo, state, duration := getTabularInfo(&item, devfileComponent)
|
||||
fmt.Fprintln(w, item.Name, "\t", managedByOdo, "\t", state, "\t", duration)
|
||||
}
|
||||
w.Flush()
|
||||
|
||||
if len(failedListingCR) > 0 {
|
||||
fmt.Printf("\nFailed to fetch services for operator(s): %q\n", strings.Join(failedListingCR, ", "))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// mixServices returns a structure containing both the services in cluster and defined in devfile
|
||||
func mixServices(clusterList []unstructured.Unstructured, devfileList map[string]unstructured.Unstructured) serviceItemList {
|
||||
servicesItems := map[string]*serviceItem{}
|
||||
for _, item := range clusterList {
|
||||
if item.GetKind() == "ServiceBinding" {
|
||||
continue
|
||||
}
|
||||
name := strings.Join([]string{item.GetKind(), item.GetName()}, "/")
|
||||
if _, ok := servicesItems[name]; !ok {
|
||||
servicesItems[name] = NewServiceItem(name)
|
||||
}
|
||||
servicesItems[name].Manifest = item.Object
|
||||
servicesItems[name].Deployed = true
|
||||
servicesItems[name].ClusterInfo = &clusterInfo{
|
||||
Labels: item.GetLabels(),
|
||||
CreationTimestamp: item.GetCreationTimestamp().Time,
|
||||
}
|
||||
}
|
||||
|
||||
for name, manifest := range devfileList {
|
||||
if manifest.GetKind() == "ServiceBinding" {
|
||||
continue
|
||||
}
|
||||
if _, ok := servicesItems[name]; !ok {
|
||||
servicesItems[name] = NewServiceItem(name)
|
||||
}
|
||||
servicesItems[name].InDevfile = true
|
||||
if !servicesItems[name].Deployed {
|
||||
servicesItems[name].Manifest = manifest.Object
|
||||
}
|
||||
}
|
||||
|
||||
return serviceItemList{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "List",
|
||||
APIVersion: machineoutput.APIVersion,
|
||||
},
|
||||
Items: getOrderedServices(servicesItems),
|
||||
}
|
||||
}
|
||||
|
||||
// getOrderedServices returns the services as a slice, ordered by name
|
||||
func getOrderedServices(items map[string]*serviceItem) []serviceItem {
|
||||
orderedNames := getOrderedServicesNames(items)
|
||||
result := make([]serviceItem, len(items))
|
||||
i := 0
|
||||
for _, name := range orderedNames {
|
||||
result[i] = *items[name]
|
||||
i++
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// getOrderedServicesNames returns the names of the services ordered in alphabetic order
|
||||
func getOrderedServicesNames(items map[string]*serviceItem) []string {
|
||||
orderedNames := make([]string, len(items))
|
||||
i := 0
|
||||
for name := range items {
|
||||
orderedNames[i] = name
|
||||
i++
|
||||
}
|
||||
sort.Strings(orderedNames)
|
||||
return orderedNames
|
||||
}
|
||||
|
||||
// getTabularInfo returns information to be displayed in the output for a specific service and a specific current devfile component
|
||||
func getTabularInfo(serviceItem *serviceItem, devfileComponent string) (managedByOdo, state, duration string) {
|
||||
clusterItem := serviceItem.ClusterInfo
|
||||
inDevfile := serviceItem.InDevfile
|
||||
if clusterItem != nil {
|
||||
// service deployed into cluster
|
||||
var component string
|
||||
labels := clusterItem.Labels
|
||||
isManagedByOdo := labels[applabels.ManagedBy] == "odo"
|
||||
if isManagedByOdo {
|
||||
component = labels[cmplabels.ComponentLabel]
|
||||
managedByOdo = fmt.Sprintf("Yes (%s)", component)
|
||||
} else {
|
||||
managedByOdo = "No"
|
||||
}
|
||||
duration = time.Since(clusterItem.CreationTimestamp).Truncate(time.Second).String()
|
||||
if inDevfile {
|
||||
// service deployed into cluster and defined in devfile
|
||||
state = "Pushed"
|
||||
} else {
|
||||
// service deployed into cluster and not defined in devfile
|
||||
if isManagedByOdo {
|
||||
if devfileComponent == component {
|
||||
state = "Deleted locally"
|
||||
} else {
|
||||
state = ""
|
||||
}
|
||||
} else {
|
||||
state = ""
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if inDevfile {
|
||||
// service not deployed into cluster and defined in devfile
|
||||
state = "Not pushed"
|
||||
managedByOdo = fmt.Sprintf("Yes (%s)", devfileComponent)
|
||||
} else {
|
||||
// service not deployed into cluster and not defined in devfile
|
||||
// should not happen
|
||||
state = "Err!"
|
||||
managedByOdo = "Err!"
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,277 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
func TestGetOrderedServicesNames(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
services map[string]*serviceItem
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "Unordered names",
|
||||
services: map[string]*serviceItem{
|
||||
"name3": nil,
|
||||
"name1": nil,
|
||||
"name2": nil,
|
||||
},
|
||||
want: []string{"name1", "name2", "name3"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := getOrderedServicesNames(tt.services)
|
||||
if !reflect.DeepEqual(result, tt.want) {
|
||||
t.Errorf("Failed %s: got: %q, want: %q", t.Name(), result, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type getTabularInfoResult struct {
|
||||
managedByOdo string
|
||||
state string
|
||||
durationContent bool
|
||||
}
|
||||
|
||||
func TestGetTabularInfo(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
service *serviceItem
|
||||
devfileComponent string
|
||||
want getTabularInfoResult
|
||||
}{
|
||||
{
|
||||
name: "case 1: service in cluster managed by current devfile",
|
||||
service: &serviceItem{
|
||||
ClusterInfo: &clusterInfo{
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/managed-by": "odo",
|
||||
"app.kubernetes.io/instance": "component1",
|
||||
},
|
||||
},
|
||||
InDevfile: true,
|
||||
},
|
||||
devfileComponent: "component1",
|
||||
want: getTabularInfoResult{
|
||||
managedByOdo: "Yes (component1)",
|
||||
state: "Pushed",
|
||||
durationContent: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "case 2: service in cluster not managed by Odo",
|
||||
service: &serviceItem{
|
||||
ClusterInfo: &clusterInfo{
|
||||
Labels: map[string]string{},
|
||||
},
|
||||
InDevfile: false,
|
||||
},
|
||||
devfileComponent: "component1",
|
||||
want: getTabularInfoResult{
|
||||
managedByOdo: "No",
|
||||
state: "",
|
||||
durationContent: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "case 3: service in cluster absent from current devfile",
|
||||
service: &serviceItem{
|
||||
ClusterInfo: &clusterInfo{
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/managed-by": "odo",
|
||||
"app.kubernetes.io/instance": "component1",
|
||||
},
|
||||
},
|
||||
InDevfile: false,
|
||||
},
|
||||
devfileComponent: "component1",
|
||||
want: getTabularInfoResult{
|
||||
managedByOdo: "Yes (component1)",
|
||||
state: "Deleted locally",
|
||||
durationContent: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "case 4: service in cluster maaged by another devfile",
|
||||
service: &serviceItem{
|
||||
ClusterInfo: &clusterInfo{
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/managed-by": "odo",
|
||||
"app.kubernetes.io/instance": "component2",
|
||||
},
|
||||
},
|
||||
InDevfile: false,
|
||||
},
|
||||
devfileComponent: "component1",
|
||||
want: getTabularInfoResult{
|
||||
managedByOdo: "Yes (component2)",
|
||||
state: "",
|
||||
durationContent: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "case 5: service defined in devfile, not in cluster",
|
||||
service: &serviceItem{
|
||||
ClusterInfo: nil,
|
||||
InDevfile: true,
|
||||
},
|
||||
devfileComponent: "component1",
|
||||
want: getTabularInfoResult{
|
||||
managedByOdo: "Yes (component1)",
|
||||
state: "Not pushed",
|
||||
durationContent: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
managedByOdo, state, duration := getTabularInfo(tt.service, tt.devfileComponent)
|
||||
if managedByOdo != tt.want.managedByOdo {
|
||||
t.Errorf("Failed %s: managedByOdo got: %q, want: %q", t.Name(), managedByOdo, tt.want.managedByOdo)
|
||||
}
|
||||
if state != tt.want.state {
|
||||
t.Errorf("Failed %s: state got: %q, want: %q", t.Name(), state, tt.want.state)
|
||||
}
|
||||
if len(duration) > 0 != tt.want.durationContent {
|
||||
t.Errorf("Failed %s: duration content got: %v, want: %v", t.Name(), len(duration) > 0, tt.want.durationContent)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMixServices(t *testing.T) {
|
||||
atime, _ := time.Parse(time.RFC3339, "2021-06-02T08:39:20Z00:00")
|
||||
tests := []struct {
|
||||
name string
|
||||
clusterListInlined []string
|
||||
devfileList []string
|
||||
want []serviceItem
|
||||
}{
|
||||
{
|
||||
name: "two in cluster and two in devfile, including one in common",
|
||||
clusterListInlined: []string{`
|
||||
kind: kind1
|
||||
metadata:
|
||||
name: name1
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: odo
|
||||
app.kubernetes.io/instance: component1
|
||||
creationTimestamp: 2021-06-02T08:39:20Z00:00
|
||||
spec:
|
||||
field1: value1`,
|
||||
`
|
||||
kind: kind2
|
||||
metadata:
|
||||
name: name2
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: odo
|
||||
app.kubernetes.io/instance: component2
|
||||
creationTimestamp: 2021-06-02T08:39:20Z00:00
|
||||
spec:
|
||||
field2: value2`},
|
||||
devfileList: []string{`
|
||||
kind: kind1
|
||||
metadata:
|
||||
name: name1
|
||||
spec:
|
||||
field1: value1`, `
|
||||
kind: kind3
|
||||
metadata:
|
||||
name: name3
|
||||
spec:
|
||||
field3: value3`},
|
||||
want: []serviceItem{
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Service",
|
||||
APIVersion: "odo.dev/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "kind1/name1",
|
||||
},
|
||||
ClusterInfo: &clusterInfo{
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/managed-by": "odo",
|
||||
"app.kubernetes.io/instance": "component1",
|
||||
},
|
||||
CreationTimestamp: atime,
|
||||
},
|
||||
InDevfile: true,
|
||||
Deployed: true,
|
||||
},
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Service",
|
||||
APIVersion: "odo.dev/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "kind2/name2",
|
||||
},
|
||||
ClusterInfo: &clusterInfo{
|
||||
Labels: map[string]string{
|
||||
"app.kubernetes.io/managed-by": "odo",
|
||||
"app.kubernetes.io/instance": "component2",
|
||||
},
|
||||
CreationTimestamp: atime,
|
||||
},
|
||||
InDevfile: false,
|
||||
Deployed: true,
|
||||
},
|
||||
{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Service",
|
||||
APIVersion: "odo.dev/v1alpha1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "kind3/name3",
|
||||
},
|
||||
ClusterInfo: nil,
|
||||
InDevfile: true,
|
||||
Deployed: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
usCluster := make([]unstructured.Unstructured, len(tt.clusterListInlined))
|
||||
for i, clusterInlined := range tt.clusterListInlined {
|
||||
err := yaml.Unmarshal([]byte(clusterInlined), &usCluster[i])
|
||||
if err != nil {
|
||||
t.Errorf("Failed to unmarshal spec manifest %q: %u", clusterInlined, err)
|
||||
}
|
||||
}
|
||||
usDevfiles := make(map[string]unstructured.Unstructured)
|
||||
for _, devfile := range tt.devfileList {
|
||||
usDevfile := unstructured.Unstructured{}
|
||||
err := yaml.Unmarshal([]byte(devfile), &usDevfile)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to unmarshal spec manifest %q, %u", devfile, err)
|
||||
}
|
||||
usDevfiles[usDevfile.GetKind()+"/"+usDevfile.GetName()] = usDevfile
|
||||
}
|
||||
result := mixServices(usCluster, usDevfiles)
|
||||
for i := range result.Items {
|
||||
if reflect.DeepEqual(result.Items[i].Manifest, unstructured.Unstructured{}) {
|
||||
t.Errorf("Manifest is empty")
|
||||
}
|
||||
// do not check manifest content
|
||||
result.Items[i].Manifest = unstructured.Unstructured{}.Object
|
||||
}
|
||||
if !reflect.DeepEqual(result.Items, tt.want) {
|
||||
t.Errorf("Failed %s\n\ngot: %+v\n\nwant: %+v\n", t.Name(), result.Items, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,385 +0,0 @@
|
||||
/*
|
||||
This file contains code for various service backends supported by odo. Different backends have different logics for
|
||||
Complete, Validate and Run functions. These are covered in this file.
|
||||
*/
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/validate"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/redhat-developer/odo/pkg/devfile"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
"github.com/redhat-developer/odo/pkg/machineoutput"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
svc "github.com/redhat-developer/odo/pkg/service"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoMetadataName = errors.New("invalid \"metadata\" in the yaml; provide a valid \"metadata.name\"")
|
||||
)
|
||||
|
||||
// CompleteServiceCreate contains logic to complete the "odo service create" call for the case of Operator backend
|
||||
func (b *OperatorBackend) CompleteServiceCreate(o *CreateOptions, args []string) (err error) {
|
||||
// since interactive mode is not supported for Operators yet, set it to false
|
||||
o.interactive = false
|
||||
|
||||
// if user has just used "odo service create", simply return
|
||||
if o.fromFileFlag == "" && len(args) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// if user wants to create service from file and use a name given on CLI
|
||||
if o.fromFileFlag != "" {
|
||||
if len(args) == 1 {
|
||||
o.ServiceName = args[0]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// split the name provided on CLI and populate servicetype & customresource
|
||||
o.ServiceType, b.CustomResource, err = svc.SplitServiceKindName(args[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid service name, use the format <operator-type>/<crd-name>")
|
||||
}
|
||||
|
||||
// if two args are given, first is service type and second one is service name
|
||||
if len(args) == 2 {
|
||||
o.ServiceName = args[1]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *OperatorBackend) ValidateServiceCreate(o *CreateOptions) error {
|
||||
u := unstructured.Unstructured{}
|
||||
// if the user wants to create service from a file, we check for
|
||||
// existence of file and validate if the requested operator and CR
|
||||
// exist on the cluster
|
||||
if o.fromFileFlag != "" {
|
||||
if _, err := os.Stat(o.fromFileFlag); err != nil {
|
||||
return errors.Wrap(err, "unable to find specified file")
|
||||
}
|
||||
|
||||
// Parse the file to find Operator and CR info
|
||||
fileContents, err := ioutil.ReadFile(o.fromFileFlag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal(fileContents, &u.Object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gvk := u.GroupVersionKind()
|
||||
b.group, b.version, b.kind = gvk.Group, gvk.Version, gvk.Kind
|
||||
|
||||
if u.GetName() == "" {
|
||||
return ErrNoMetadataName
|
||||
}
|
||||
|
||||
if o.ServiceName != "" && !o.DryRunFlag {
|
||||
// First check if service with provided name already exists
|
||||
svcFullName := strings.Join([]string{b.kind, o.ServiceName}, "/")
|
||||
exists, e := svc.OperatorSvcExists(o.KClient, svcFullName)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
if exists {
|
||||
return fmt.Errorf("service %q already exists; please provide a different name or delete the existing service first", svcFullName)
|
||||
}
|
||||
u.SetName(o.ServiceName)
|
||||
} else {
|
||||
o.ServiceName = u.GetName()
|
||||
}
|
||||
|
||||
csvPtr, err := o.KClient.GetCSVWithCR(u.GetKind())
|
||||
if err != nil {
|
||||
// error only occurs when OperatorHub is not installed.
|
||||
// k8s doesn't have it installed by default but OCP does
|
||||
return err
|
||||
}
|
||||
csv := *csvPtr
|
||||
|
||||
// CRD is valid. We can use it further to create a service from it.
|
||||
b.CustomResourceDefinition = u.Object
|
||||
|
||||
// Validate spec
|
||||
hasCR, cr := o.KClient.CheckCustomResourceInCSV(b.kind, &csv)
|
||||
if !hasCR {
|
||||
return fmt.Errorf("the %q resource doesn't exist in specified %q operator", b.CustomResource, b.group)
|
||||
}
|
||||
|
||||
crd, err := o.KClient.GetCRDSpec(cr, b.group, b.kind)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = validate.AgainstSchema(crd, u.Object["spec"], strfmt.Default)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
} else if b.CustomResource != "" {
|
||||
// make sure that CSV of the specified ServiceType exists
|
||||
csv, err := o.KClient.GetClusterServiceVersion(o.ServiceType)
|
||||
if err != nil {
|
||||
// error only occurs when OperatorHub is not installed.
|
||||
// k8s doesn't have it installed by default but OCP does
|
||||
return err
|
||||
}
|
||||
b.group, b.version, b.resource, err = svc.GetGVRFromOperator(csv, b.CustomResource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// if the service name is blank then we set it to custom resource name
|
||||
if o.ServiceName == "" {
|
||||
o.ServiceName = strings.ToLower(b.CustomResource)
|
||||
}
|
||||
|
||||
hasCR, cr := o.KClient.CheckCustomResourceInCSV(b.CustomResource, &csv)
|
||||
if !hasCR {
|
||||
return fmt.Errorf("the %q resource doesn't exist in specified %q operator", b.CustomResource, b.group)
|
||||
}
|
||||
|
||||
crd, err := o.KClient.GetCRDSpec(cr, b.group, b.CustomResource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(o.parametersFlag) != 0 {
|
||||
builtCRD, e := svc.BuildCRDFromParams(o.ParametersMap, crd, b.group, b.version, b.CustomResource)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
u.Object = builtCRD
|
||||
} else {
|
||||
almExample, e := svc.GetAlmExample(csv, b.CustomResource, o.ServiceType)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
u.Object = almExample
|
||||
}
|
||||
|
||||
if o.ServiceName != "" && !o.DryRunFlag {
|
||||
// First check if service with provided name already exists
|
||||
svcFullName := strings.Join([]string{b.CustomResource, o.ServiceName}, "/")
|
||||
exists, e := svc.OperatorSvcExists(o.KClient, svcFullName)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
if exists {
|
||||
return fmt.Errorf("service %q already exists; please provide a different name or delete the existing service first", svcFullName)
|
||||
}
|
||||
}
|
||||
|
||||
u.SetName(o.ServiceName)
|
||||
if u.GetName() == "" {
|
||||
return ErrNoMetadataName
|
||||
}
|
||||
|
||||
// CRD is valid. We can use it further to create a service from it.
|
||||
b.CustomResourceDefinition = u.Object
|
||||
|
||||
if o.ServiceName == "" {
|
||||
o.ServiceName = u.GetName()
|
||||
}
|
||||
|
||||
// Validate spec
|
||||
err = validate.AgainstSchema(crd, u.Object["spec"], strfmt.Default)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
} else {
|
||||
// This block is executed only when user has neither provided a
|
||||
// file nor a valid `odo service create <operator-name>` to start
|
||||
// the service from an Operator. So we raise an error because the
|
||||
// correct way is to execute:
|
||||
// `odo service create <operator-name>/<crd-name>`
|
||||
|
||||
return fmt.Errorf("please use a valid command to start an Operator backed service; desired format: %q", "odo service create <operator-name>/<crd-name>")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *OperatorBackend) RunServiceCreate(o *CreateOptions) (err error) {
|
||||
s := &log.Status{}
|
||||
|
||||
// if cluster has resources of type CSV and o.CustomResource is not
|
||||
// empty, we're expected to create an Operator backed service
|
||||
if o.DryRunFlag {
|
||||
// if it's dry run, only print the alm-example (o.CustomResourceDefinition) and exit
|
||||
jsonCR, err := json.MarshalIndent(b.CustomResourceDefinition, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// convert json to yaml
|
||||
yamlCR, err := yaml.JSONToYAML(jsonCR)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info(string(yamlCR))
|
||||
|
||||
return nil
|
||||
} else {
|
||||
|
||||
if o.inlinedFlag {
|
||||
crdYaml, err := yaml.Marshal(b.CustomResourceDefinition)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = devfile.AddKubernetesComponentToDevfile(string(crdYaml), o.ServiceName, o.EnvSpecificInfo.GetDevfileObj())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
} else {
|
||||
crdYaml, err := yaml.Marshal(b.CustomResourceDefinition)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = devfile.AddKubernetesComponent(string(crdYaml), o.ServiceName, o.contextFlag, o.EnvSpecificInfo.GetDevfileObj())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if log.IsJSON() {
|
||||
svcFullName := strings.Join([]string{b.CustomResource, o.ServiceName}, "/")
|
||||
svc := NewServiceItem(svcFullName)
|
||||
svc.Manifest = b.CustomResourceDefinition
|
||||
svc.InDevfile = true
|
||||
machineoutput.OutputSuccess(svc)
|
||||
}
|
||||
}
|
||||
s.End(true)
|
||||
return
|
||||
}
|
||||
|
||||
// ServiceDefined returns true if the service is defined in the devfile
|
||||
func (b *OperatorBackend) ServiceDefined(ctx *genericclioptions.Context, name string) (bool, error) {
|
||||
_, instanceName, err := svc.SplitServiceKindName(name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return devfile.IsComponentDefined(instanceName, ctx.EnvSpecificInfo.GetDevfileObj())
|
||||
}
|
||||
|
||||
func (b *OperatorBackend) DeleteService(o *DeleteOptions, name string, application string) error {
|
||||
// "name" is of the form CR-Name/Instance-Name so we split it
|
||||
_, instanceName, err := svc.SplitServiceKindName(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = devfile.DeleteKubernetesComponentFromDevfile(instanceName, o.EnvSpecificInfo.GetDevfileObj(), o.contextFlag)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to delete service from the devfile")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *OperatorBackend) DescribeService(o *DescribeOptions, serviceName, app string) error {
|
||||
|
||||
clusterList, _, err := svc.ListOperatorServices(o.KClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var clusterFound *unstructured.Unstructured
|
||||
for i, clusterInstance := range clusterList {
|
||||
fullName := strings.Join([]string{clusterInstance.GetKind(), clusterInstance.GetName()}, "/")
|
||||
if fullName == serviceName {
|
||||
clusterFound = &clusterList[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
devfileList, err := svc.ListDevfileServices(o.KClient, o.EnvSpecificInfo.GetDevfileObj(), o.contextFlag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
devfileService, inDevfile := devfileList[serviceName]
|
||||
|
||||
item := NewServiceItem(serviceName)
|
||||
item.InDevfile = inDevfile
|
||||
item.Deployed = clusterFound != nil
|
||||
if item.Deployed {
|
||||
item.Manifest = clusterFound.Object
|
||||
} else if item.InDevfile {
|
||||
item.Manifest = devfileService.Object
|
||||
}
|
||||
|
||||
if log.IsJSON() {
|
||||
machineoutput.OutputSuccess(item)
|
||||
return nil
|
||||
}
|
||||
|
||||
return PrintHumanReadableOutput(item)
|
||||
}
|
||||
|
||||
// PrintHumanReadableOutput outputs the description of a service in a human readable format
|
||||
func PrintHumanReadableOutput(item *serviceItem) error {
|
||||
log.Describef("Version: ", "%s", item.Manifest["apiVersion"])
|
||||
log.Describef("Kind: ", "%s", item.Manifest["kind"])
|
||||
metadata, ok := item.Manifest["metadata"].(map[string]interface{})
|
||||
if !ok {
|
||||
return errors.New("unable to get name from manifest")
|
||||
}
|
||||
log.Describef("Name: ", "%s", metadata["name"])
|
||||
spec, ok := item.Manifest["spec"].(map[string]interface{})
|
||||
if !ok {
|
||||
return errors.New("unable to get specifications from manifest")
|
||||
}
|
||||
|
||||
var tab bytes.Buffer
|
||||
|
||||
wr := tabwriter.NewWriter(&tab, 5, 2, 3, ' ', tabwriter.TabIndent)
|
||||
fmt.Fprint(wr, "NAME", "\t", "VALUE", "\n")
|
||||
displayParameters(wr, spec, "")
|
||||
wr.Flush()
|
||||
|
||||
log.Describef("Parameters:\n", tab.String())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// displayParameters adds lines describing fields of a given map
|
||||
func displayParameters(wr *tabwriter.Writer, spec map[string]interface{}, prefix string) {
|
||||
keys := make([]string, len(spec))
|
||||
i := 0
|
||||
for key := range spec {
|
||||
keys[i] = key
|
||||
i++
|
||||
}
|
||||
|
||||
for _, k := range keys {
|
||||
v := spec[k]
|
||||
switch val := v.(type) {
|
||||
case map[string]interface{}:
|
||||
displayParameters(wr, val, prefix+k+".")
|
||||
default:
|
||||
fmt.Fprintf(wr, "%s%s\t%v\n", prefix, k, val)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
appCmd "github.com/redhat-developer/odo/pkg/odo/cli/application"
|
||||
projectCmd "github.com/redhat-developer/odo/pkg/odo/cli/project"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/odo/util"
|
||||
"github.com/spf13/cobra"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
// RecommendedCommandName is the recommended service command name
|
||||
const RecommendedCommandName = "service"
|
||||
|
||||
var serviceLongDesc = ktemplates.LongDesc(`Perform service related operations`)
|
||||
|
||||
// NewCmdService implements the odo service command
|
||||
func NewCmdService(name, fullName string) *cobra.Command {
|
||||
serviceCreateCmd := NewCmdServiceCreate(createRecommendedCommandName, util.GetFullName(fullName, createRecommendedCommandName))
|
||||
serviceListCmd := NewCmdServiceList(listRecommendedCommandName, util.GetFullName(fullName, listRecommendedCommandName))
|
||||
serviceDeleteCmd := NewCmdServiceDelete(deleteRecommendedCommandName, util.GetFullName(fullName, deleteRecommendedCommandName))
|
||||
serviceDescribeCmd := NewCmdServiceDescribe(describeRecommendedCommandName, util.GetFullName(fullName, describeRecommendedCommandName))
|
||||
serviceCmd := &cobra.Command{
|
||||
Use: name,
|
||||
Short: "Perform service related operations",
|
||||
Long: serviceLongDesc,
|
||||
Example: fmt.Sprintf("%s\n\n%s\n\n%s\n\n%s",
|
||||
serviceCreateCmd.Example,
|
||||
serviceDeleteCmd.Example,
|
||||
serviceDescribeCmd.Example,
|
||||
serviceListCmd.Example),
|
||||
Args: cobra.RangeArgs(1, 3),
|
||||
}
|
||||
// Add a defined annotation in order to appear in the help menu
|
||||
serviceCmd.Annotations = map[string]string{"command": "main"}
|
||||
serviceCmd.SetUsageTemplate(util.CmdUsageTemplate)
|
||||
serviceCmd.AddCommand(serviceCreateCmd, serviceDeleteCmd, serviceDescribeCmd, serviceListCmd)
|
||||
|
||||
//Adding `--project` flag
|
||||
projectCmd.AddProjectFlag(serviceCreateCmd)
|
||||
projectCmd.AddProjectFlag(serviceDeleteCmd)
|
||||
projectCmd.AddProjectFlag(serviceDescribeCmd)
|
||||
projectCmd.AddProjectFlag(serviceListCmd)
|
||||
|
||||
//Adding `--application` flag
|
||||
appCmd.AddApplicationFlag(serviceCreateCmd)
|
||||
appCmd.AddApplicationFlag(serviceDeleteCmd)
|
||||
appCmd.AddApplicationFlag(serviceDescribeCmd)
|
||||
appCmd.AddApplicationFlag(serviceListCmd)
|
||||
|
||||
return serviceCmd
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package service
|
||||
|
||||
//OperatorBackend implements the interface ServiceProviderBackend and contains methods that help create a service from Operators
|
||||
type OperatorBackend struct {
|
||||
// Custom Resrouce to create service from
|
||||
CustomResource string
|
||||
// Custom Resrouce's Definition fetched from alm-examples
|
||||
CustomResourceDefinition map[string]interface{}
|
||||
// Group of the GVR
|
||||
group string
|
||||
// Version of the GVR
|
||||
version string
|
||||
// Resource of the GVR
|
||||
resource string
|
||||
// Kind of GVK
|
||||
kind string
|
||||
}
|
||||
|
||||
func NewOperatorBackend() *OperatorBackend {
|
||||
return &OperatorBackend{}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/devfile/location"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/component"
|
||||
"github.com/redhat-developer/odo/pkg/util"
|
||||
)
|
||||
|
||||
// validDevfileDirectory returns an error if the "odo service" command is executed from a directory not containing devfile.yaml
|
||||
func validDevfileDirectory(componentContext string) error {
|
||||
if componentContext == "" {
|
||||
componentContext = component.LocalDirectoryDefaultLocation
|
||||
}
|
||||
devfilePath := location.DevfileLocation(componentContext)
|
||||
if !util.CheckPathExists(devfilePath) {
|
||||
return fmt.Errorf("service can be created/deleted from a valid component directory only\n"+
|
||||
"refer %q for more information", "odo service create -h")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/localConfigProvider"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
"github.com/redhat-developer/odo/pkg/machineoutput"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||
"github.com/redhat-developer/odo/pkg/odo/util/completion"
|
||||
"github.com/redhat-developer/odo/pkg/storage"
|
||||
"github.com/redhat-developer/odo/pkg/util"
|
||||
"github.com/spf13/cobra"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
const createRecommendedCommandName = "create"
|
||||
|
||||
var (
|
||||
storageCreateShortDesc = `Create storage and mount to a component`
|
||||
storageCreateLongDesc = ktemplates.LongDesc(`Create storage and mount to a component`)
|
||||
storageCreateExample = ktemplates.Examples(`
|
||||
# Create storage of size 1Gb to a component
|
||||
%[1]s mystorage --path=/opt/app-root/src/storage/ --size=1Gi
|
||||
|
||||
# Create storage with ephemeral volume of size 2Gi to a component
|
||||
%[1]s mystorage --path=/opt/app-root/src/storage/ --size=2Gi --ephemeral
|
||||
`)
|
||||
)
|
||||
|
||||
type CreateOptions struct {
|
||||
// Context
|
||||
*genericclioptions.Context
|
||||
|
||||
// Parameters
|
||||
storageName string
|
||||
|
||||
// Flags
|
||||
sizeFlag string
|
||||
pathFlag string
|
||||
contextFlag string
|
||||
containerFlag string
|
||||
ephemeralFlag bool
|
||||
|
||||
storage localConfigProvider.LocalStorage
|
||||
}
|
||||
|
||||
// NewStorageCreateOptions creates a new CreateOptions instance
|
||||
func NewStorageCreateOptions() *CreateOptions {
|
||||
return &CreateOptions{}
|
||||
}
|
||||
|
||||
// Complete completes CreateOptions after they've been created
|
||||
func (o *CreateOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
o.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline).NeedDevfile(o.contextFlag))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(args) != 0 {
|
||||
o.storageName = args[0]
|
||||
} else {
|
||||
o.storageName = fmt.Sprintf("%s-%s", o.Context.LocalConfigProvider.GetName(), util.GenerateRandomString(4))
|
||||
}
|
||||
|
||||
var eph *bool
|
||||
if o.ephemeralFlag {
|
||||
eph = &o.ephemeralFlag
|
||||
}
|
||||
o.storage = localConfigProvider.LocalStorage{
|
||||
Name: o.storageName,
|
||||
Size: o.sizeFlag,
|
||||
Ephemeral: eph,
|
||||
Path: o.pathFlag,
|
||||
Container: o.containerFlag,
|
||||
}
|
||||
|
||||
o.Context.LocalConfigProvider.CompleteStorage(&o.storage)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates the CreateOptions based on completed values
|
||||
func (o *CreateOptions) Validate() (err error) {
|
||||
// validate the storage
|
||||
return o.LocalConfigProvider.ValidateStorage(o.storage)
|
||||
}
|
||||
|
||||
// Run contains the logic for the odo storage create command
|
||||
func (o *CreateOptions) Run() (err error) {
|
||||
err = o.Context.LocalConfigProvider.CreateStorage(o.storage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if log.IsJSON() {
|
||||
storageResultMachineReadable := storage.NewStorage(o.storage.Name, o.storage.Size, o.storage.Path, nil)
|
||||
machineoutput.OutputSuccess(storageResultMachineReadable)
|
||||
} else {
|
||||
log.Successf("Added storage %v to %v", o.storageName, o.Context.LocalConfigProvider.GetName())
|
||||
|
||||
log.Italic("\nPlease use `odo push` command to make the storage accessible to the component")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewCmdStorageCreate implements the odo storage create command.
|
||||
func NewCmdStorageCreate(name, fullName string) *cobra.Command {
|
||||
o := NewStorageCreateOptions()
|
||||
storageCreateCmd := &cobra.Command{
|
||||
Use: name,
|
||||
Short: storageCreateShortDesc,
|
||||
Long: storageCreateLongDesc,
|
||||
Example: fmt.Sprintf(storageCreateExample, fullName),
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
Annotations: map[string]string{"machineoutput": "json"},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(o, cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
storageCreateCmd.Flags().StringVar(&o.sizeFlag, "size", "", "Size of storage to add")
|
||||
storageCreateCmd.Flags().StringVar(&o.pathFlag, "path", "", "Path to mount the storage on")
|
||||
storageCreateCmd.Flags().StringVar(&o.containerFlag, "container", "", "Name of container to attach the storage to in devfile")
|
||||
storageCreateCmd.Flags().BoolVar(&o.ephemeralFlag, "ephemeral", false, "Set volume as ephemeral")
|
||||
|
||||
odoutil.AddContextFlag(storageCreateCmd, &o.contextFlag)
|
||||
completion.RegisterCommandFlagHandler(storageCreateCmd, "context", completion.FileCompletionHandler)
|
||||
|
||||
return storageCreateCmd
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
"github.com/redhat-developer/odo/pkg/machineoutput"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cli/ui"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||
"github.com/redhat-developer/odo/pkg/odo/util/completion"
|
||||
"github.com/redhat-developer/odo/pkg/storage"
|
||||
"github.com/spf13/cobra"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
const deleteRecommendedCommandName = "delete"
|
||||
|
||||
var (
|
||||
storageDeleteShortDesc = `Delete storage from component`
|
||||
storageDeleteLongDesc = ktemplates.LongDesc(`Delete storage from component`)
|
||||
storageDeleteExample = ktemplates.Examples(`
|
||||
# Delete storage mystorage from the currently active component
|
||||
%[1]s mystorage
|
||||
`)
|
||||
)
|
||||
|
||||
type DeleteOptions struct {
|
||||
// Context
|
||||
*genericclioptions.Context
|
||||
|
||||
// Parameters
|
||||
storageName string
|
||||
|
||||
// Flags
|
||||
forceFlag bool
|
||||
contextFlag string
|
||||
}
|
||||
|
||||
// NewStorageDeleteOptions creates a new DeleteOptions instance
|
||||
func NewStorageDeleteOptions() *DeleteOptions {
|
||||
return &DeleteOptions{}
|
||||
}
|
||||
|
||||
// Complete completes DeleteOptions after they've been created
|
||||
func (o *DeleteOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
o.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline).NeedDevfile(o.contextFlag))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.storageName = args[0]
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates the DeleteOptions based on completed values
|
||||
func (o *DeleteOptions) Validate() (err error) {
|
||||
gotStorage, err := o.LocalConfigProvider.GetStorage(o.storageName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if gotStorage == nil {
|
||||
return fmt.Errorf("the storage %v does not exists in the application %v, cause %v", o.storageName, o.GetApplication(), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run contains the logic for the odo storage delete command
|
||||
func (o *DeleteOptions) Run() (err error) {
|
||||
mPath, err := o.Context.LocalConfigProvider.GetStorageMountPath(o.storageName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
deleteMsg := fmt.Sprintf("Are you sure you want to delete the storage %v mounted to %v in %v component", o.storageName, mPath, o.Context.LocalConfigProvider.GetName())
|
||||
|
||||
if log.IsJSON() || o.forceFlag || ui.Proceed(deleteMsg) {
|
||||
err := o.Context.LocalConfigProvider.DeleteStorage(o.storageName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete storage, cause %v", err)
|
||||
}
|
||||
|
||||
successMessage := fmt.Sprintf("Deleted storage %v from %v", o.storageName, o.Context.LocalConfigProvider.GetName())
|
||||
|
||||
log.Infof(successMessage)
|
||||
log.Italic("\nPlease use `odo push` command to delete the storage from the cluster")
|
||||
|
||||
if log.IsJSON() {
|
||||
machineoutput.SuccessStatus(storage.StorageKind, o.storageName, successMessage)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("aborting deletion of storage: %v", o.storageName)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewCmdStorageDelete implements the odo storage delete command.
|
||||
func NewCmdStorageDelete(name, fullName string) *cobra.Command {
|
||||
o := NewStorageDeleteOptions()
|
||||
storageDeleteCmd := &cobra.Command{
|
||||
Use: name,
|
||||
Short: storageDeleteShortDesc,
|
||||
Long: storageDeleteLongDesc,
|
||||
Example: fmt.Sprintf(storageDeleteExample, fullName),
|
||||
Args: cobra.ExactArgs(1),
|
||||
Annotations: map[string]string{"machineoutput": "json"},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(o, cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
storageDeleteCmd.Flags().BoolVarP(&o.forceFlag, "force", "f", false, "Delete storage without prompting")
|
||||
completion.RegisterCommandHandler(storageDeleteCmd, completion.StorageDeleteCompletionHandler)
|
||||
|
||||
odoutil.AddContextFlag(storageDeleteCmd, &o.contextFlag)
|
||||
completion.RegisterCommandFlagHandler(storageDeleteCmd, "context", completion.FileCompletionHandler)
|
||||
|
||||
return storageDeleteCmd
|
||||
}
|
||||
@@ -1,216 +0,0 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/localConfigProvider"
|
||||
"github.com/redhat-developer/odo/pkg/log"
|
||||
"github.com/redhat-developer/odo/pkg/machineoutput"
|
||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||
"github.com/redhat-developer/odo/pkg/odo/util/completion"
|
||||
"github.com/redhat-developer/odo/pkg/storage"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const listRecommendedCommandName = "list"
|
||||
|
||||
var (
|
||||
storageListShortDesc = `List storage attached to a component`
|
||||
storageListLongDesc = ktemplates.LongDesc(`List storage attached to a component`)
|
||||
storageListExample = ktemplates.Examples(`
|
||||
# List all storage attached to the current component
|
||||
%[1]s
|
||||
`)
|
||||
)
|
||||
|
||||
type ListOptions struct {
|
||||
// Context
|
||||
*genericclioptions.Context
|
||||
|
||||
// Flags
|
||||
contextFlag string
|
||||
|
||||
// Backend
|
||||
client storage.Client
|
||||
}
|
||||
|
||||
// NewStorageListOptions creates a new ListOptions instance
|
||||
func NewStorageListOptions() *ListOptions {
|
||||
return &ListOptions{}
|
||||
}
|
||||
|
||||
// Complete completes ListOptions after they've been created
|
||||
func (o *ListOptions) Complete(cmdline cmdline.Cmdline, args []string) (err error) {
|
||||
o.Context, err = genericclioptions.New(genericclioptions.NewCreateParameters(cmdline).NeedDevfile(o.contextFlag))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
o.client = storage.NewClient(storage.ClientOptions{
|
||||
LocalConfigProvider: o.Context.LocalConfigProvider,
|
||||
Client: o.Context.KClient,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates the ListOptions based on completed values
|
||||
func (o *ListOptions) Validate() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *ListOptions) Run() (err error) {
|
||||
storageList, err := o.client.List()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if log.IsJSON() {
|
||||
machineoutput.OutputSuccess(storageList)
|
||||
} else {
|
||||
localContainers, err := o.Context.LocalConfigProvider.GetContainers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if isContainerDisplay(storageList, localContainers) {
|
||||
printStorageWithContainer(storageList, o.Context.LocalConfigProvider.GetName())
|
||||
} else {
|
||||
printStorage(storageList, o.Context.LocalConfigProvider.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// printStorage prints the given storageList
|
||||
func printStorage(storageList storage.StorageList, compName string) {
|
||||
|
||||
if len(storageList.Items) > 0 {
|
||||
|
||||
tabWriterMounted := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent)
|
||||
|
||||
storageMap := make(map[string]bool)
|
||||
|
||||
// create headers of mounted storage table
|
||||
fmt.Fprintln(tabWriterMounted, "NAME", "\t", "SIZE", "\t", "PATH", "\t", "STATE")
|
||||
// iterating over all mounted storage and put in the mount storage table
|
||||
for _, mStorage := range storageList.Items {
|
||||
_, ok := storageMap[mStorage.Name]
|
||||
if !ok {
|
||||
storageMap[mStorage.Name] = true
|
||||
fmt.Fprintln(tabWriterMounted, mStorage.Name, "\t", mStorage.Spec.Size, "\t", mStorage.Spec.Path, "\t", mStorage.Status)
|
||||
}
|
||||
}
|
||||
|
||||
// print all mounted storage of the given component
|
||||
log.Infof("The component '%v' has the following storage attached:", compName)
|
||||
tabWriterMounted.Flush()
|
||||
} else {
|
||||
log.Infof("The component '%v' has no storage attached", compName)
|
||||
}
|
||||
|
||||
fmt.Println("")
|
||||
}
|
||||
|
||||
// printStorageWithContainer prints the given storageList with the corresponding container name
|
||||
func printStorageWithContainer(storageList storage.StorageList, compName string) {
|
||||
|
||||
if len(storageList.Items) > 0 {
|
||||
|
||||
tabWriterMounted := tabwriter.NewWriter(os.Stdout, 5, 2, 3, ' ', tabwriter.TabIndent)
|
||||
|
||||
// create headers of mounted storage table
|
||||
fmt.Fprintln(tabWriterMounted, "NAME", "\t", "SIZE", "\t", "PATH", "\t", "CONTAINER", "\t", "STATE")
|
||||
// iterating over all mounted storage and put in the mount storage table
|
||||
for _, mStorage := range storageList.Items {
|
||||
fmt.Fprintln(tabWriterMounted, mStorage.Name, "\t", mStorage.Spec.Size, "\t", mStorage.Spec.Path, "\t", mStorage.Spec.ContainerName, "\t", mStorage.Status)
|
||||
}
|
||||
|
||||
// print all mounted storage of the given component
|
||||
log.Infof("The component '%v' has the following storage attached:", compName)
|
||||
tabWriterMounted.Flush()
|
||||
} else {
|
||||
log.Infof("The component '%v' has no storage attached", compName)
|
||||
}
|
||||
|
||||
fmt.Println("")
|
||||
}
|
||||
|
||||
// isContainerDisplay checks whether the container name should be included in the output
|
||||
func isContainerDisplay(storageList storage.StorageList, components []localConfigProvider.LocalContainer) bool {
|
||||
|
||||
// get all the container names
|
||||
componentsMap := make(map[string]bool)
|
||||
for _, comp := range components {
|
||||
componentsMap[comp.Name] = true
|
||||
}
|
||||
|
||||
storageCompMap := make(map[string][]string)
|
||||
pathMap := make(map[string]string)
|
||||
storageMap := make(map[string]storage.StorageStatus)
|
||||
|
||||
for _, storageItem := range storageList.Items {
|
||||
if pathMap[storageItem.Name] == "" {
|
||||
pathMap[storageItem.Name] = storageItem.Spec.Path
|
||||
}
|
||||
if storageMap[storageItem.Name] == "" {
|
||||
storageMap[storageItem.Name] = storageItem.Status
|
||||
}
|
||||
|
||||
// check if the storage is mounted on the same path in all the containers
|
||||
if pathMap[storageItem.Name] != storageItem.Spec.Path {
|
||||
return true
|
||||
}
|
||||
|
||||
// check if the storage is in the same state for all the containers
|
||||
if storageMap[storageItem.Name] != storageItem.Status {
|
||||
return true
|
||||
}
|
||||
|
||||
// check if the storage is mounted on a valid devfile container
|
||||
// this situation can arrive when a container is removed from the devfile
|
||||
// but the state is not pushed thus it exists on the cluster
|
||||
_, ok := componentsMap[storageItem.Spec.ContainerName]
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
storageCompMap[storageItem.Name] = append(storageCompMap[storageItem.Name], storageItem.Spec.ContainerName)
|
||||
}
|
||||
|
||||
for _, containerNames := range storageCompMap {
|
||||
// check if the storage is mounted on all the devfile containers
|
||||
if len(containerNames) != len(componentsMap) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// NewCmdStorageList implements the odo storage list command.
|
||||
func NewCmdStorageList(name, fullName string) *cobra.Command {
|
||||
o := NewStorageListOptions()
|
||||
storageListCmd := &cobra.Command{
|
||||
Use: name,
|
||||
Short: storageListShortDesc,
|
||||
Long: storageListLongDesc,
|
||||
Example: fmt.Sprintf(storageListExample, fullName),
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
Annotations: map[string]string{"machineoutput": "json"},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
genericclioptions.GenericRun(o, cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
odoutil.AddContextFlag(storageListCmd, &o.contextFlag)
|
||||
completion.RegisterCommandFlagHandler(storageListCmd, "context", completion.FileCompletionHandler)
|
||||
|
||||
return storageListCmd
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/localConfigProvider"
|
||||
"github.com/redhat-developer/odo/pkg/storage"
|
||||
)
|
||||
|
||||
func Test_isContainerDisplay(t *testing.T) {
|
||||
generateStorage := func(storage storage.Storage, status storage.StorageStatus, containerName string) storage.Storage {
|
||||
storage.Status = status
|
||||
storage.Spec.ContainerName = containerName
|
||||
return storage
|
||||
}
|
||||
|
||||
type args struct {
|
||||
storageList storage.StorageList
|
||||
obj []localConfigProvider.LocalContainer
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "case 1: storage is mounted on all the containers on the same path",
|
||||
args: args{
|
||||
storageList: storage.StorageList{
|
||||
Items: []storage.Storage{
|
||||
generateStorage(storage.NewStorage("pvc-1", "1Gi", "/data", nil), storage.StateTypePushed, "container-0"),
|
||||
generateStorage(storage.NewStorage("pvc-1", "1Gi", "/data", nil), storage.StateTypePushed, "container-1"),
|
||||
},
|
||||
},
|
||||
obj: []localConfigProvider.LocalContainer{
|
||||
{
|
||||
Name: "container-0",
|
||||
},
|
||||
{
|
||||
Name: "container-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "case 2: storage is mounted on different paths",
|
||||
args: args{
|
||||
storageList: storage.StorageList{
|
||||
Items: []storage.Storage{
|
||||
generateStorage(storage.NewStorage("pvc-1", "1Gi", "/data", nil), storage.StateTypePushed, "container-0"),
|
||||
generateStorage(storage.NewStorage("pvc-1", "1Gi", "/path", nil), storage.StateTypePushed, "container-1"),
|
||||
},
|
||||
},
|
||||
obj: []localConfigProvider.LocalContainer{
|
||||
{
|
||||
Name: "container-0",
|
||||
},
|
||||
{
|
||||
Name: "container-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "case 3: storage is mounted to the same path on all the containers but states are different",
|
||||
args: args{
|
||||
storageList: storage.StorageList{
|
||||
Items: []storage.Storage{
|
||||
generateStorage(storage.NewStorage("pvc-1", "1Gi", "/data", nil), storage.StateTypePushed, "container-0"),
|
||||
generateStorage(storage.NewStorage("pvc-1", "1Gi", "/data", nil), storage.StateTypeNotPushed, "container-1"),
|
||||
},
|
||||
},
|
||||
obj: []localConfigProvider.LocalContainer{
|
||||
{
|
||||
Name: "container-0",
|
||||
},
|
||||
{
|
||||
Name: "container-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "case 4: storage is not mounted on all the containers",
|
||||
args: args{
|
||||
storageList: storage.StorageList{
|
||||
Items: []storage.Storage{
|
||||
generateStorage(storage.NewStorage("pvc-1", "1Gi", "/data", nil), storage.StateTypePushed, "container-0"),
|
||||
},
|
||||
},
|
||||
obj: []localConfigProvider.LocalContainer{
|
||||
{
|
||||
Name: "container-0",
|
||||
},
|
||||
{
|
||||
Name: "container-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "case 5: storage is mounted on a container deleted locally from the devfile",
|
||||
args: args{
|
||||
storageList: storage.StorageList{
|
||||
Items: []storage.Storage{
|
||||
generateStorage(storage.NewStorage("pvc-1", "1Gi", "/data", nil), storage.StateTypePushed, "container-0"),
|
||||
generateStorage(storage.NewStorage("pvc-1", "1Gi", "/data", nil), storage.StateTypePushed, "container-1"),
|
||||
},
|
||||
},
|
||||
obj: []localConfigProvider.LocalContainer{
|
||||
{
|
||||
Name: "container-0",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := isContainerDisplay(tt.args.storageList, tt.args.obj); got != tt.want {
|
||||
t.Errorf("isContainerDisplay() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
||||
"github.com/spf13/cobra"
|
||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||
)
|
||||
|
||||
// RecommendedCommandName is the recommended command name
|
||||
const RecommendedCommandName = "storage"
|
||||
|
||||
var (
|
||||
storageShortDesc = `Perform storage operations`
|
||||
storageLongDesc = ktemplates.LongDesc(`Perform storage operations`)
|
||||
)
|
||||
|
||||
// NewCmdStorage implements the odo storage command
|
||||
func NewCmdStorage(name, fullName string) *cobra.Command {
|
||||
storageCreateCmd := NewCmdStorageCreate(createRecommendedCommandName, odoutil.GetFullName(fullName, createRecommendedCommandName))
|
||||
storageDeleteCmd := NewCmdStorageDelete(deleteRecommendedCommandName, odoutil.GetFullName(fullName, deleteRecommendedCommandName))
|
||||
storageListCmd := NewCmdStorageList(listRecommendedCommandName, odoutil.GetFullName(fullName, listRecommendedCommandName))
|
||||
|
||||
var storageCmd = &cobra.Command{
|
||||
Use: name,
|
||||
Short: storageShortDesc,
|
||||
Long: storageLongDesc,
|
||||
Example: fmt.Sprintf("%s\n\n%s\n\n%s",
|
||||
storageCreateCmd.Example,
|
||||
storageDeleteCmd.Example,
|
||||
storageListCmd.Example),
|
||||
}
|
||||
|
||||
storageCmd.AddCommand(storageCreateCmd)
|
||||
storageCmd.AddCommand(storageDeleteCmd)
|
||||
storageCmd.AddCommand(storageListCmd)
|
||||
|
||||
// Add a defined annotation in order to appear in the help menu
|
||||
storageCmd.Annotations = map[string]string{"command": "main"}
|
||||
storageCmd.SetUsageTemplate(odoutil.CmdUsageTemplate)
|
||||
|
||||
return storageCmd
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package notify
|
||||
package version
|
||||
|
||||
import (
|
||||
"strings"
|
||||
@@ -30,9 +30,9 @@ func getLatestReleaseTag() (string, error) {
|
||||
return strings.TrimSuffix(string(body), "\n"), nil
|
||||
}
|
||||
|
||||
// CheckLatestReleaseTag returns the latest release tag if a newer latest
|
||||
// checkLatestReleaseTag returns the latest release tag if a newer latest
|
||||
// release is available, else returns an empty string
|
||||
func CheckLatestReleaseTag(currentVersion string) (string, error) {
|
||||
func checkLatestReleaseTag(currentVersion string) (string, error) {
|
||||
currentSemver, err := semver.Make(strings.TrimPrefix(currentVersion, "v"))
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "unable to make semver from the current version: %v", currentVersion)
|
||||
@@ -1,4 +1,4 @@
|
||||
package notify
|
||||
package version
|
||||
|
||||
/*
|
||||
import (
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/redhat-developer/odo/pkg/preference"
|
||||
odoversion "github.com/redhat-developer/odo/pkg/version"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/notify"
|
||||
"github.com/redhat-developer/odo/pkg/odo/util"
|
||||
"github.com/spf13/cobra"
|
||||
"k8s.io/klog"
|
||||
@@ -136,7 +135,7 @@ func NewCmdVersion(name, fullName string) *cobra.Command {
|
||||
|
||||
// GetLatestReleaseInfo Gets information about the latest release
|
||||
func GetLatestReleaseInfo(info chan<- string) {
|
||||
newTag, err := notify.CheckLatestReleaseTag(odoversion.VERSION)
|
||||
newTag, err := checkLatestReleaseTag(odoversion.VERSION)
|
||||
if err != nil {
|
||||
// The error is intentionally not being handled because we don't want
|
||||
// to stop the execution of the program because of this failure
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||
|
||||
"github.com/posener/complete"
|
||||
"github.com/redhat-developer/odo/pkg/application"
|
||||
"github.com/redhat-developer/odo/pkg/catalog"
|
||||
"github.com/redhat-developer/odo/pkg/component"
|
||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||
@@ -16,25 +15,6 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// AppCompletionHandler provides completion for the app commands
|
||||
var AppCompletionHandler = func(cmd *cobra.Command, args parsedArgs, context *genericclioptions.Context) (completions []string) {
|
||||
completions = make([]string, 0)
|
||||
|
||||
appClient := application.NewClient(context.KClient)
|
||||
applications, err := appClient.List()
|
||||
if err != nil {
|
||||
return completions
|
||||
}
|
||||
|
||||
for _, app := range applications {
|
||||
if args.commands[app] {
|
||||
return nil
|
||||
}
|
||||
completions = append(completions, app)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FileCompletionHandler provides suggestions for files and directories
|
||||
var FileCompletionHandler = func(cmd *cobra.Command, args parsedArgs, context *genericclioptions.Context) (completions []string) {
|
||||
completions = append(completions, complete.PredictFiles("*").Predict(args.original)...)
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/unions"
|
||||
"github.com/redhat-developer/odo/pkg/kclient/unions"
|
||||
|
||||
"github.com/devfile/library/pkg/devfile/generator"
|
||||
routev1 "github.com/openshift/api/route/v1"
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/unions"
|
||||
"github.com/redhat-developer/odo/pkg/kclient/unions"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
|
||||
"github.com/devfile/library/pkg/devfile/generator"
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/redhat-developer/odo/pkg/unions"
|
||||
"github.com/redhat-developer/odo/pkg/kclient/unions"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
routev1 "github.com/openshift/api/route/v1"
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"reflect"
|
||||
|
||||
routev1 "github.com/openshift/api/route/v1"
|
||||
"github.com/redhat-developer/odo/pkg/kclient/unions"
|
||||
"github.com/redhat-developer/odo/pkg/localConfigProvider"
|
||||
"github.com/redhat-developer/odo/pkg/machineoutput"
|
||||
"github.com/redhat-developer/odo/pkg/unions"
|
||||
urlLabels "github.com/redhat-developer/odo/pkg/url/labels"
|
||||
iextensionsv1 "k8s.io/api/extensions/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
@@ -13,7 +13,6 @@ SCRIPT_IDENTITY=${SCRIPT_IDENTITY:-"def-id"}
|
||||
|
||||
# Integration tests
|
||||
shout "| Running integration Tests on MiniKube"
|
||||
make test-operator-hub
|
||||
make test-cmd-project
|
||||
make test-integration-devfile
|
||||
|
||||
|
||||
@@ -27,10 +27,6 @@ mockgen -source=pkg/odo/cmdline/cmdline.go \
|
||||
-package cmdline \
|
||||
-destination pkg/odo/cmdline/mock.go
|
||||
|
||||
mockgen -source=pkg/application/application.go \
|
||||
-package application \
|
||||
-destination pkg/application/mock.go
|
||||
|
||||
mockgen -source=pkg/project/project.go \
|
||||
-package project \
|
||||
-destination pkg/project/mock.go
|
||||
|
||||
@@ -31,7 +31,6 @@ make test-integration || error=true
|
||||
make test-integration-devfile || error=true
|
||||
make test-cmd-login-logout || error=true
|
||||
make test-cmd-project || error=true
|
||||
make test-operator-hub || error=true
|
||||
|
||||
# E2e tests
|
||||
make test-e2e-all || error=true
|
||||
|
||||
@@ -53,7 +53,6 @@ else
|
||||
make test-integration-devfile || error=true
|
||||
make test-cmd-login-logout || error=true
|
||||
make test-cmd-project || error=true
|
||||
make test-operator-hub || error=true
|
||||
|
||||
# E2e tests
|
||||
make test-e2e-all || error=true
|
||||
|
||||
@@ -39,7 +39,6 @@ if [ "${ARCH}" == "s390x" ]; then
|
||||
make test-integration-devfile
|
||||
make test-cmd-login-logout
|
||||
make test-cmd-project
|
||||
make test-operator-hub
|
||||
# E2e tests
|
||||
make test-e2e-all
|
||||
elif [ "${ARCH}" == "ppc64le" ]; then
|
||||
@@ -50,14 +49,12 @@ elif [ "${ARCH}" == "ppc64le" ]; then
|
||||
make test-cmd-project
|
||||
# E2e tests
|
||||
make test-e2e-all
|
||||
make test-operator-hub
|
||||
else
|
||||
# Integration tests
|
||||
make test-integration || error=true
|
||||
make test-integration-devfile || error=true
|
||||
make test-cmd-login-logout || error=true
|
||||
make test-cmd-project || error=true
|
||||
make test-operator-hub || error=true
|
||||
|
||||
# E2e tests
|
||||
make test-e2e-all || error=true
|
||||
|
||||
@@ -69,7 +69,6 @@ git clone $REPO_URL odo && cd $WORKING_DIR/odo && git checkout "v$VERSION"
|
||||
#Run tests
|
||||
make test-integration-devfile
|
||||
make test-integration
|
||||
make test-operator-hub
|
||||
make test-e2e-all
|
||||
make test-cmd-project
|
||||
|
||||
|
||||
@@ -73,7 +73,6 @@ set -x
|
||||
|
||||
# # Integration tests
|
||||
shout "Running integration Tests"
|
||||
make test-operator-hub || error=true
|
||||
make test-integration || error=true
|
||||
make test-integration-devfile || error=true
|
||||
make test-cmd-login-logout || error=true
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user