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
|
set -e
|
||||||
make install
|
make install
|
||||||
make test-integration-devfile
|
make test-integration-devfile
|
||||||
make test-operator-hub
|
|
||||||
make test-e2e-devfile
|
make test-e2e-devfile
|
||||||
make test-cmd-project
|
make test-cmd-project
|
||||||
) |& tee "/tmp/${LOGFILE}"
|
) |& tee "/tmp/${LOGFILE}"
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ cleanup_namespaces
|
|||||||
make install
|
make install
|
||||||
make test-integration
|
make test-integration
|
||||||
make test-integration-devfile
|
make test-integration-devfile
|
||||||
make test-operator-hub
|
|
||||||
make test-cmd-login-logout
|
make test-cmd-login-logout
|
||||||
make test-cmd-project
|
make test-cmd-project
|
||||||
make test-e2e-devfile
|
make test-e2e-devfile
|
||||||
|
|||||||
@@ -66,8 +66,6 @@ function Run-Test {
|
|||||||
Check-ExitCode $LASTEXITCODE
|
Check-ExitCode $LASTEXITCODE
|
||||||
make test-integration | tee -a C:\Users\Administrator.ANSIBLE-TEST-VS\AppData\Local\Temp\$LOGFILE
|
make test-integration | tee -a C:\Users\Administrator.ANSIBLE-TEST-VS\AppData\Local\Temp\$LOGFILE
|
||||||
Check-ExitCode $LASTEXITCODE
|
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
|
make test-cmd-login-logout | tee -a C:\Users\Administrator.ANSIBLE-TEST-VS\AppData\Local\Temp\$LOGFILE
|
||||||
Check-ExitCode $LASTEXITCODE
|
Check-ExitCode $LASTEXITCODE
|
||||||
make test-cmd-project | tee -a C:\Users\Administrator.ANSIBLE-TEST-VS\AppData\Local\Temp\$LOGFILE
|
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:
|
openshiftci-presubmit-unittests:
|
||||||
./scripts/openshiftci-presubmit-unittests.sh
|
./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
|
.PHONY: test-cmd-devfile-describe
|
||||||
test-cmd-devfile-describe:
|
test-cmd-devfile-describe:
|
||||||
$(RUN_GINKGO) $(GINKGO_FLAGS) -focus="odo devfile describe command tests" tests/integration/devfile/
|
$(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
|
package envinfo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/devfile/library/pkg/devfile/generator"
|
"github.com/devfile/library/pkg/devfile/generator"
|
||||||
applabels "github.com/redhat-developer/odo/pkg/application/labels"
|
applabels "github.com/redhat-developer/odo/pkg/application/labels"
|
||||||
componentlabels "github.com/redhat-developer/odo/pkg/component/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/url/labels"
|
||||||
"github.com/redhat-developer/odo/pkg/util"
|
"github.com/redhat-developer/odo/pkg/util"
|
||||||
"github.com/redhat-developer/odo/pkg/version"
|
"github.com/redhat-developer/odo/pkg/version"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/redhat-developer/odo/pkg/unions"
|
"github.com/redhat-developer/odo/pkg/kclient/unions"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"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/devfile/library/pkg/devfile/generator"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import (
|
|||||||
projectv1 "github.com/openshift/api/project/v1"
|
projectv1 "github.com/openshift/api/project/v1"
|
||||||
routev1 "github.com/openshift/api/route/v1"
|
routev1 "github.com/openshift/api/route/v1"
|
||||||
olm "github.com/operator-framework/api/pkg/operators/v1alpha1"
|
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/log"
|
||||||
"github.com/redhat-developer/odo/pkg/unions"
|
|
||||||
appsv1 "k8s.io/api/apps/v1"
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ import (
|
|||||||
v1 "github.com/openshift/api/project/v1"
|
v1 "github.com/openshift/api/project/v1"
|
||||||
v10 "github.com/openshift/api/route/v1"
|
v10 "github.com/openshift/api/route/v1"
|
||||||
v1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
|
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"
|
log "github.com/redhat-developer/odo/pkg/log"
|
||||||
unions "github.com/redhat-developer/odo/pkg/unions"
|
|
||||||
v11 "k8s.io/api/apps/v1"
|
v11 "k8s.io/api/apps/v1"
|
||||||
v12 "k8s.io/api/core/v1"
|
v12 "k8s.io/api/core/v1"
|
||||||
meta "k8s.io/apimachinery/pkg/api/meta"
|
meta "k8s.io/apimachinery/pkg/api/meta"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package odogenerator
|
package unions
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/devfile/library/pkg/devfile/generator"
|
"github.com/devfile/library/pkg/devfile/generator"
|
||||||
@@ -54,7 +54,7 @@ func getNetworkingV1IngressSpec(ingressSpecParams generator.IngressSpecParams) *
|
|||||||
return ingressSpec
|
return ingressSpec
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetNetworkingV1Ingress(ingressParams generator.IngressParams) *v1.Ingress {
|
func getNetworkingV1Ingress(ingressParams generator.IngressParams) *v1.Ingress {
|
||||||
var ip *v1.Ingress
|
var ip *v1.Ingress
|
||||||
ingressSpec := getNetworkingV1IngressSpec(ingressParams.IngressSpecParams)
|
ingressSpec := getNetworkingV1IngressSpec(ingressParams.IngressSpecParams)
|
||||||
ip = &v1.Ingress{
|
ip = &v1.Ingress{
|
||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
|
|
||||||
v1alpha2 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
v1alpha2 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
||||||
"github.com/devfile/library/pkg/devfile/generator"
|
"github.com/devfile/library/pkg/devfile/generator"
|
||||||
"github.com/redhat-developer/odo/pkg/odogenerator"
|
|
||||||
"k8s.io/api/extensions/v1beta1"
|
"k8s.io/api/extensions/v1beta1"
|
||||||
v1 "k8s.io/api/networking/v1"
|
v1 "k8s.io/api/networking/v1"
|
||||||
)
|
)
|
||||||
@@ -37,7 +36,7 @@ func NewGeneratedKubernetesIngress() *KubernetesIngress {
|
|||||||
//NewKubernetesIngressFromParams generates a new KubernetesIngress from the ingress params
|
//NewKubernetesIngressFromParams generates a new KubernetesIngress from the ingress params
|
||||||
func NewKubernetesIngressFromParams(ingressParams generator.IngressParams) *KubernetesIngress {
|
func NewKubernetesIngressFromParams(ingressParams generator.IngressParams) *KubernetesIngress {
|
||||||
ki := NewGeneratedKubernetesIngress()
|
ki := NewGeneratedKubernetesIngress()
|
||||||
ki.NetworkingV1Ingress = odogenerator.GetNetworkingV1Ingress(ingressParams)
|
ki.NetworkingV1Ingress = getNetworkingV1Ingress(ingressParams)
|
||||||
ki.ExtensionV1Beta1Ingress = generator.GetIngress(v1alpha2.Endpoint{}, ingressParams)
|
ki.ExtensionV1Beta1Ingress = generator.GetIngress(v1alpha2.Endpoint{}, ingressParams)
|
||||||
return ki
|
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
|
// NewCmdCatalogDescribe implements the odo catalog describe command
|
||||||
func NewCmdCatalogDescribe(name, fullName string) *cobra.Command {
|
func NewCmdCatalogDescribe(name, fullName string) *cobra.Command {
|
||||||
component := NewCmdCatalogDescribeComponent(componentRecommendedCommandName, util.GetFullName(fullName, componentRecommendedCommandName))
|
component := NewCmdCatalogDescribeComponent(componentRecommendedCommandName, util.GetFullName(fullName, componentRecommendedCommandName))
|
||||||
service := NewCmdCatalogDescribeService(serviceRecommendedCommandName, util.GetFullName(fullName, serviceRecommendedCommandName))
|
|
||||||
catalogDescribeCmd := &cobra.Command{
|
catalogDescribeCmd := &cobra.Command{
|
||||||
Use: name,
|
Use: name,
|
||||||
Short: "Describe catalog item",
|
Short: "Describe catalog item",
|
||||||
Long: "Describe the given catalog item from OpenShift",
|
Long: "Describe the given catalog item from OpenShift",
|
||||||
Args: cobra.ExactArgs(1),
|
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(
|
catalogDescribeCmd.AddCommand(
|
||||||
component,
|
component,
|
||||||
service,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return catalogDescribeCmd
|
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
|
// NewCmdCatalogList implements the odo catalog list command
|
||||||
func NewCmdCatalogList(name, fullName string) *cobra.Command {
|
func NewCmdCatalogList(name, fullName string) *cobra.Command {
|
||||||
components := NewCmdCatalogListComponents(componentsRecommendedCommandName, util.GetFullName(fullName, componentsRecommendedCommandName))
|
components := NewCmdCatalogListComponents(componentsRecommendedCommandName, util.GetFullName(fullName, componentsRecommendedCommandName))
|
||||||
services := NewCmdCatalogListServices(servicesRecommendedCommandName, util.GetFullName(fullName, servicesRecommendedCommandName))
|
|
||||||
|
|
||||||
catalogListCmd := &cobra.Command{
|
catalogListCmd := &cobra.Command{
|
||||||
Use: name,
|
Use: name,
|
||||||
Short: "List all available component & service types.",
|
Short: "List all available component types.",
|
||||||
Long: "List all available component and service types from OpenShift",
|
Long: "List all available component types from OpenShift",
|
||||||
Example: fmt.Sprintf("%s\n\n%s\n", components.Example, services.Example),
|
Example: fmt.Sprintf("%s\n", components.Example),
|
||||||
}
|
}
|
||||||
|
|
||||||
catalogListCmd.AddCommand(
|
catalogListCmd.AddCommand(
|
||||||
components,
|
components,
|
||||||
services,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return catalogListCmd
|
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
|
// NewCmdCatalogSearch implements the odo catalog search command
|
||||||
func NewCmdCatalogSearch(name, fullName string) *cobra.Command {
|
func NewCmdCatalogSearch(name, fullName string) *cobra.Command {
|
||||||
component := NewCmdCatalogSearchComponent(componentRecommendedCommandName, util.GetFullName(fullName, componentRecommendedCommandName))
|
component := NewCmdCatalogSearchComponent(componentRecommendedCommandName, util.GetFullName(fullName, componentRecommendedCommandName))
|
||||||
service := NewCmdCatalogSearchService(serviceRecommendedCommandName, util.GetFullName(fullName, serviceRecommendedCommandName))
|
|
||||||
catalogSearchCmd := &cobra.Command{
|
catalogSearchCmd := &cobra.Command{
|
||||||
Use: name,
|
Use: name,
|
||||||
Short: "Search available component & service types.",
|
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
|
This searches for a partial match for the given search term in all the available
|
||||||
components & services.
|
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
|
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"
|
"os"
|
||||||
"strings"
|
"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/build_images"
|
||||||
"github.com/redhat-developer/odo/pkg/odo/cli/catalog"
|
"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/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/deploy"
|
||||||
"github.com/redhat-developer/odo/pkg/odo/cli/env"
|
|
||||||
_init "github.com/redhat-developer/odo/pkg/odo/cli/init"
|
_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/login"
|
||||||
"github.com/redhat-developer/odo/pkg/odo/cli/logout"
|
"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/preference"
|
||||||
"github.com/redhat-developer/odo/pkg/odo/cli/project"
|
"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/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/telemetry"
|
||||||
"github.com/redhat-developer/odo/pkg/odo/cli/url"
|
"github.com/redhat-developer/odo/pkg/odo/cli/url"
|
||||||
"github.com/redhat-developer/odo/pkg/odo/cli/utils"
|
"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("CapitalizeFlagDescriptions", util.CapitalizeFlagDescriptions)
|
||||||
cobra.AddTemplateFunc("ModifyAdditionalFlags", util.ModifyAdditionalFlags)
|
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)),
|
catalog.NewCmdCatalog(catalog.RecommendedCommandName, util.GetFullName(fullName, catalog.RecommendedCommandName)),
|
||||||
component.NewCmdComponent(component.RecommendedCommandName, util.GetFullName(fullName, component.RecommendedCommandName)),
|
component.NewCmdComponent(component.RecommendedCommandName, util.GetFullName(fullName, component.RecommendedCommandName)),
|
||||||
component.NewCmdCreate(component.CreateRecommendedCommandName, util.GetFullName(fullName, component.CreateRecommendedCommandName)),
|
component.NewCmdCreate(component.CreateRecommendedCommandName, util.GetFullName(fullName, component.CreateRecommendedCommandName)),
|
||||||
component.NewCmdDelete(component.DeleteRecommendedCommandName, util.GetFullName(fullName, component.DeleteRecommendedCommandName)),
|
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.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.NewCmdPush(component.PushRecommendedCommandName, util.GetFullName(fullName, component.PushRecommendedCommandName)),
|
||||||
component.NewCmdWatch(component.WatchRecommendedCommandName, util.GetFullName(fullName, component.WatchRecommendedCommandName)),
|
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)),
|
login.NewCmdLogin(login.RecommendedCommandName, util.GetFullName(fullName, login.RecommendedCommandName)),
|
||||||
logout.NewCmdLogout(logout.RecommendedCommandName, util.GetFullName(fullName, logout.RecommendedCommandName)),
|
logout.NewCmdLogout(logout.RecommendedCommandName, util.GetFullName(fullName, logout.RecommendedCommandName)),
|
||||||
project.NewCmdProject(project.RecommendedCommandName, util.GetFullName(fullName, project.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)),
|
url.NewCmdURL(url.RecommendedCommandName, util.GetFullName(fullName, url.RecommendedCommandName)),
|
||||||
utils.NewCmdUtils(utils.RecommendedCommandName, util.GetFullName(fullName, utils.RecommendedCommandName)),
|
utils.NewCmdUtils(utils.RecommendedCommandName, util.GetFullName(fullName, utils.RecommendedCommandName)),
|
||||||
version.NewCmdVersion(version.RecommendedCommandName, util.GetFullName(fullName, version.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)),
|
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)),
|
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),
|
telemetry.NewCmdTelemetry(telemetry.RecommendedCommandName),
|
||||||
build_images.NewCmdBuildImages(build_images.RecommendedCommandName, util.GetFullName(fullName, build_images.RecommendedCommandName)),
|
build_images.NewCmdBuildImages(build_images.RecommendedCommandName, util.GetFullName(fullName, build_images.RecommendedCommandName)),
|
||||||
deploy.NewCmdDeploy(deploy.RecommendedCommandName, util.GetFullName(fullName, deploy.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))
|
componentGetCmd := NewCmdGet(GetRecommendedCommandName, odoutil.GetFullName(fullName, GetRecommendedCommandName))
|
||||||
createCmd := NewCmdCreate(CreateRecommendedCommandName, odoutil.GetFullName(fullName, CreateRecommendedCommandName))
|
createCmd := NewCmdCreate(CreateRecommendedCommandName, odoutil.GetFullName(fullName, CreateRecommendedCommandName))
|
||||||
deleteCmd := NewCmdDelete(DeleteRecommendedCommandName, odoutil.GetFullName(fullName, DeleteRecommendedCommandName))
|
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))
|
listCmd := NewCmdList(ListRecommendedCommandName, odoutil.GetFullName(fullName, ListRecommendedCommandName))
|
||||||
logCmd := NewCmdLog(LogRecommendedCommandName, odoutil.GetFullName(fullName, LogRecommendedCommandName))
|
|
||||||
pushCmd := NewCmdPush(PushRecommendedCommandName, odoutil.GetFullName(fullName, PushRecommendedCommandName))
|
pushCmd := NewCmdPush(PushRecommendedCommandName, odoutil.GetFullName(fullName, PushRecommendedCommandName))
|
||||||
watchCmd := NewCmdWatch(WatchRecommendedCommandName, odoutil.GetFullName(fullName, WatchRecommendedCommandName))
|
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
|
// componentCmd represents the component command
|
||||||
var componentCmd = &cobra.Command{
|
var componentCmd = &cobra.Command{
|
||||||
@@ -68,8 +61,7 @@ func NewCmdComponent(name, fullName string) *cobra.Command {
|
|||||||
// add flags from 'get' to component command
|
// add flags from 'get' to component command
|
||||||
componentCmd.Flags().AddFlagSet(componentGetCmd.Flags())
|
componentCmd.Flags().AddFlagSet(componentGetCmd.Flags())
|
||||||
|
|
||||||
componentCmd.AddCommand(componentGetCmd, createCmd, deleteCmd, describeCmd, linkCmd, unlinkCmd, listCmd, logCmd, pushCmd, watchCmd, execCmd)
|
componentCmd.AddCommand(componentGetCmd, createCmd, deleteCmd, listCmd, pushCmd, watchCmd)
|
||||||
componentCmd.AddCommand(testCmd, statusCmd)
|
|
||||||
|
|
||||||
// Add a defined annotation in order to appear in the help menu
|
// Add a defined annotation in order to appear in the help menu
|
||||||
componentCmd.Annotations = map[string]string{"command": "main"}
|
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/devfile/location"
|
||||||
"github.com/redhat-developer/odo/pkg/envinfo"
|
"github.com/redhat-developer/odo/pkg/envinfo"
|
||||||
"github.com/redhat-developer/odo/pkg/log"
|
"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"
|
projectCmd "github.com/redhat-developer/odo/pkg/odo/cli/project"
|
||||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||||
odoutil "github.com/redhat-developer/odo/pkg/odo/util"
|
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)
|
odoutil.AddNowFlag(componentCreateCmd, &co.nowFlag)
|
||||||
//Adding `--project` flag
|
//Adding `--project` flag
|
||||||
projectCmd.AddProjectFlag(componentCreateCmd)
|
projectCmd.AddProjectFlag(componentCreateCmd)
|
||||||
//Adding `--application` flag
|
|
||||||
appCmd.AddApplicationFlag(componentCreateCmd)
|
|
||||||
|
|
||||||
completion.RegisterCommandHandler(componentCreateCmd, completion.CreateCompletionHandler)
|
completion.RegisterCommandHandler(componentCreateCmd, completion.CreateCompletionHandler)
|
||||||
completion.RegisterCommandFlagHandler(componentCreateCmd, "context", completion.FileCompletionHandler)
|
completion.RegisterCommandFlagHandler(componentCreateCmd, "context", completion.FileCompletionHandler)
|
||||||
|
|||||||
@@ -3,16 +3,16 @@ package component
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/redhat-developer/odo/pkg/devfile/adapters/kubernetes/component"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"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"
|
||||||
"github.com/redhat-developer/odo/pkg/devfile/adapters/common"
|
"github.com/redhat-developer/odo/pkg/devfile/adapters/common"
|
||||||
"github.com/redhat-developer/odo/pkg/devfile/consts"
|
"github.com/redhat-developer/odo/pkg/devfile/consts"
|
||||||
"github.com/redhat-developer/odo/pkg/log"
|
"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"
|
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/cli/ui"
|
||||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||||
@@ -267,8 +267,6 @@ func NewCmdDelete(name, fullName string) *cobra.Command {
|
|||||||
|
|
||||||
// Adding `--project` flag
|
// Adding `--project` flag
|
||||||
projectCmd.AddProjectFlag(componentDeleteCmd)
|
projectCmd.AddProjectFlag(componentDeleteCmd)
|
||||||
// Adding `--application` flag
|
|
||||||
appCmd.AddApplicationFlag(componentDeleteCmd)
|
|
||||||
|
|
||||||
return 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 (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/redhat-developer/odo/pkg/devfile"
|
"github.com/redhat-developer/odo/pkg/devfile"
|
||||||
|
|
||||||
devfilev1 "github.com/devfile/api/v2/pkg/apis/workspaces/v1alpha2"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/redhat-developer/odo/pkg/envinfo"
|
"github.com/redhat-developer/odo/pkg/envinfo"
|
||||||
"github.com/redhat-developer/odo/pkg/machineoutput"
|
"github.com/redhat-developer/odo/pkg/machineoutput"
|
||||||
@@ -114,58 +112,6 @@ func (po *PushOptions) devfilePushInner() (err error) {
|
|||||||
return
|
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
|
// DevfileUnDeploy undeploys the devfile kubernetes components
|
||||||
func (do *DeleteOptions) DevfileUnDeploy() error {
|
func (do *DeleteOptions) DevfileUnDeploy() error {
|
||||||
devObj, err := devfile.ParseAndValidateFromFile(do.GetDevfilePath())
|
devObj, err := devfile.ParseAndValidateFromFile(do.GetDevfilePath())
|
||||||
@@ -208,41 +154,3 @@ func (do *DeleteOptions) DevfileComponentDelete() error {
|
|||||||
|
|
||||||
return devfileHandler.Delete(labels, do.showLogFlag, do.waitFlag)
|
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"
|
"fmt"
|
||||||
|
|
||||||
"github.com/redhat-developer/odo/pkg/log"
|
"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/cli/project"
|
||||||
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
"github.com/redhat-developer/odo/pkg/odo/cmdline"
|
||||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||||
@@ -97,8 +96,6 @@ func NewCmdGet(name, fullName string) *cobra.Command {
|
|||||||
|
|
||||||
//Adding `--project` flag
|
//Adding `--project` flag
|
||||||
project.AddProjectFlag(componentGetCmd)
|
project.AddProjectFlag(componentGetCmd)
|
||||||
//Adding `--application` flag
|
|
||||||
appCmd.AddApplicationFlag(componentGetCmd)
|
|
||||||
|
|
||||||
return 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/component"
|
||||||
"github.com/redhat-developer/odo/pkg/log"
|
"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"
|
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/cmdline"
|
||||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||||
@@ -237,8 +236,6 @@ func NewCmdList(name, fullName string) *cobra.Command {
|
|||||||
|
|
||||||
//Adding `--project` flag
|
//Adding `--project` flag
|
||||||
projectCmd.AddProjectFlag(componentListCmd)
|
projectCmd.AddProjectFlag(componentListCmd)
|
||||||
//Adding `--application` flag
|
|
||||||
appCmd.AddApplicationFlag(componentListCmd)
|
|
||||||
|
|
||||||
completion.RegisterCommandFlagHandler(componentListCmd, "path", completion.FileCompletionHandler)
|
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/common"
|
||||||
"github.com/redhat-developer/odo/pkg/devfile/adapters/kubernetes"
|
"github.com/redhat-developer/odo/pkg/devfile/adapters/kubernetes"
|
||||||
"github.com/redhat-developer/odo/pkg/envinfo"
|
"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"
|
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/cmdline"
|
||||||
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
ktemplates "k8s.io/kubectl/pkg/util/templates"
|
||||||
@@ -177,9 +176,6 @@ func NewCmdWatch(name, fullName string) *cobra.Command {
|
|||||||
// Adding context flag
|
// Adding context flag
|
||||||
odoutil.AddContextFlag(watchCmd, &wo.contextFlag)
|
odoutil.AddContextFlag(watchCmd, &wo.contextFlag)
|
||||||
|
|
||||||
//Adding `--application` flag
|
|
||||||
appCmd.AddApplicationFlag(watchCmd)
|
|
||||||
|
|
||||||
//Adding `--project` flag
|
//Adding `--project` flag
|
||||||
projectCmd.AddProjectFlag(watchCmd)
|
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 (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
@@ -30,9 +30,9 @@ func getLatestReleaseTag() (string, error) {
|
|||||||
return strings.TrimSuffix(string(body), "\n"), nil
|
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
|
// 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"))
|
currentSemver, err := semver.Make(strings.TrimPrefix(currentVersion, "v"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Wrapf(err, "unable to make semver from the current version: %v", currentVersion)
|
return "", errors.Wrapf(err, "unable to make semver from the current version: %v", currentVersion)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package notify
|
package version
|
||||||
|
|
||||||
/*
|
/*
|
||||||
import (
|
import (
|
||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
"github.com/redhat-developer/odo/pkg/preference"
|
"github.com/redhat-developer/odo/pkg/preference"
|
||||||
odoversion "github.com/redhat-developer/odo/pkg/version"
|
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/redhat-developer/odo/pkg/odo/util"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"k8s.io/klog"
|
"k8s.io/klog"
|
||||||
@@ -136,7 +135,7 @@ func NewCmdVersion(name, fullName string) *cobra.Command {
|
|||||||
|
|
||||||
// GetLatestReleaseInfo Gets information about the latest release
|
// GetLatestReleaseInfo Gets information about the latest release
|
||||||
func GetLatestReleaseInfo(info chan<- string) {
|
func GetLatestReleaseInfo(info chan<- string) {
|
||||||
newTag, err := notify.CheckLatestReleaseTag(odoversion.VERSION)
|
newTag, err := checkLatestReleaseTag(odoversion.VERSION)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// The error is intentionally not being handled because we don't want
|
// The error is intentionally not being handled because we don't want
|
||||||
// to stop the execution of the program because of this failure
|
// 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/redhat-developer/odo/pkg/testingutil/filesystem"
|
||||||
|
|
||||||
"github.com/posener/complete"
|
"github.com/posener/complete"
|
||||||
"github.com/redhat-developer/odo/pkg/application"
|
|
||||||
"github.com/redhat-developer/odo/pkg/catalog"
|
"github.com/redhat-developer/odo/pkg/catalog"
|
||||||
"github.com/redhat-developer/odo/pkg/component"
|
"github.com/redhat-developer/odo/pkg/component"
|
||||||
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
"github.com/redhat-developer/odo/pkg/odo/genericclioptions"
|
||||||
@@ -16,25 +15,6 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"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
|
// FileCompletionHandler provides suggestions for files and directories
|
||||||
var FileCompletionHandler = func(cmd *cobra.Command, args parsedArgs, context *genericclioptions.Context) (completions []string) {
|
var FileCompletionHandler = func(cmd *cobra.Command, args parsedArgs, context *genericclioptions.Context) (completions []string) {
|
||||||
completions = append(completions, complete.PredictFiles("*").Predict(args.original)...)
|
completions = append(completions, complete.PredictFiles("*").Predict(args.original)...)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/redhat-developer/odo/pkg/unions"
|
"github.com/redhat-developer/odo/pkg/kclient/unions"
|
||||||
|
|
||||||
"github.com/devfile/library/pkg/devfile/generator"
|
"github.com/devfile/library/pkg/devfile/generator"
|
||||||
routev1 "github.com/openshift/api/route/v1"
|
routev1 "github.com/openshift/api/route/v1"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/redhat-developer/odo/pkg/unions"
|
"github.com/redhat-developer/odo/pkg/kclient/unions"
|
||||||
networkingv1 "k8s.io/api/networking/v1"
|
networkingv1 "k8s.io/api/networking/v1"
|
||||||
|
|
||||||
"github.com/devfile/library/pkg/devfile/generator"
|
"github.com/devfile/library/pkg/devfile/generator"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/redhat-developer/odo/pkg/unions"
|
"github.com/redhat-developer/odo/pkg/kclient/unions"
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
routev1 "github.com/openshift/api/route/v1"
|
routev1 "github.com/openshift/api/route/v1"
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
routev1 "github.com/openshift/api/route/v1"
|
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/localConfigProvider"
|
||||||
"github.com/redhat-developer/odo/pkg/machineoutput"
|
"github.com/redhat-developer/odo/pkg/machineoutput"
|
||||||
"github.com/redhat-developer/odo/pkg/unions"
|
|
||||||
urlLabels "github.com/redhat-developer/odo/pkg/url/labels"
|
urlLabels "github.com/redhat-developer/odo/pkg/url/labels"
|
||||||
iextensionsv1 "k8s.io/api/extensions/v1beta1"
|
iextensionsv1 "k8s.io/api/extensions/v1beta1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ SCRIPT_IDENTITY=${SCRIPT_IDENTITY:-"def-id"}
|
|||||||
|
|
||||||
# Integration tests
|
# Integration tests
|
||||||
shout "| Running integration Tests on MiniKube"
|
shout "| Running integration Tests on MiniKube"
|
||||||
make test-operator-hub
|
|
||||||
make test-cmd-project
|
make test-cmd-project
|
||||||
make test-integration-devfile
|
make test-integration-devfile
|
||||||
|
|
||||||
|
|||||||
@@ -27,10 +27,6 @@ mockgen -source=pkg/odo/cmdline/cmdline.go \
|
|||||||
-package cmdline \
|
-package cmdline \
|
||||||
-destination pkg/odo/cmdline/mock.go
|
-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 \
|
mockgen -source=pkg/project/project.go \
|
||||||
-package project \
|
-package project \
|
||||||
-destination pkg/project/mock.go
|
-destination pkg/project/mock.go
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ make test-integration || error=true
|
|||||||
make test-integration-devfile || error=true
|
make test-integration-devfile || error=true
|
||||||
make test-cmd-login-logout || error=true
|
make test-cmd-login-logout || error=true
|
||||||
make test-cmd-project || error=true
|
make test-cmd-project || error=true
|
||||||
make test-operator-hub || error=true
|
|
||||||
|
|
||||||
# E2e tests
|
# E2e tests
|
||||||
make test-e2e-all || error=true
|
make test-e2e-all || error=true
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ else
|
|||||||
make test-integration-devfile || error=true
|
make test-integration-devfile || error=true
|
||||||
make test-cmd-login-logout || error=true
|
make test-cmd-login-logout || error=true
|
||||||
make test-cmd-project || error=true
|
make test-cmd-project || error=true
|
||||||
make test-operator-hub || error=true
|
|
||||||
|
|
||||||
# E2e tests
|
# E2e tests
|
||||||
make test-e2e-all || error=true
|
make test-e2e-all || error=true
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ if [ "${ARCH}" == "s390x" ]; then
|
|||||||
make test-integration-devfile
|
make test-integration-devfile
|
||||||
make test-cmd-login-logout
|
make test-cmd-login-logout
|
||||||
make test-cmd-project
|
make test-cmd-project
|
||||||
make test-operator-hub
|
|
||||||
# E2e tests
|
# E2e tests
|
||||||
make test-e2e-all
|
make test-e2e-all
|
||||||
elif [ "${ARCH}" == "ppc64le" ]; then
|
elif [ "${ARCH}" == "ppc64le" ]; then
|
||||||
@@ -50,14 +49,12 @@ elif [ "${ARCH}" == "ppc64le" ]; then
|
|||||||
make test-cmd-project
|
make test-cmd-project
|
||||||
# E2e tests
|
# E2e tests
|
||||||
make test-e2e-all
|
make test-e2e-all
|
||||||
make test-operator-hub
|
|
||||||
else
|
else
|
||||||
# Integration tests
|
# Integration tests
|
||||||
make test-integration || error=true
|
make test-integration || error=true
|
||||||
make test-integration-devfile || error=true
|
make test-integration-devfile || error=true
|
||||||
make test-cmd-login-logout || error=true
|
make test-cmd-login-logout || error=true
|
||||||
make test-cmd-project || error=true
|
make test-cmd-project || error=true
|
||||||
make test-operator-hub || error=true
|
|
||||||
|
|
||||||
# E2e tests
|
# E2e tests
|
||||||
make test-e2e-all || error=true
|
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
|
#Run tests
|
||||||
make test-integration-devfile
|
make test-integration-devfile
|
||||||
make test-integration
|
make test-integration
|
||||||
make test-operator-hub
|
|
||||||
make test-e2e-all
|
make test-e2e-all
|
||||||
make test-cmd-project
|
make test-cmd-project
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,6 @@ set -x
|
|||||||
|
|
||||||
# # Integration tests
|
# # Integration tests
|
||||||
shout "Running integration Tests"
|
shout "Running integration Tests"
|
||||||
make test-operator-hub || error=true
|
|
||||||
make test-integration || error=true
|
make test-integration || error=true
|
||||||
make test-integration-devfile || error=true
|
make test-integration-devfile || error=true
|
||||||
make test-cmd-login-logout || 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