Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

odo add binding - Bind as files UI update #5817

Merged
merged 11 commits into from
Jun 14, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,50 @@
title: odo add binding
---

## Description
The `odo add binding` command can add a link between an operator-backed service and a component. odo uses the Service Binding Operator to create this link. Running this command will make the necessary changes to the Devfile, and once pushed to the cluster, it creates an instance of the `ServiceBinding` resource.
valaparthvi marked this conversation as resolved.
Show resolved Hide resolved

Currently, it only allows connecting to the operator-backed services which support binding via the Service Binding Operator.
valaparthvi marked this conversation as resolved.
Show resolved Hide resolved
To know about the operators supported by the Service Binding Operator, read their [README](https://github.com/redhat-developer/service-binding-operator#known-bindable-operators).
To know about the Operators supported by the Service Binding Operator, read its [README](https://github.com/redhat-developer/service-binding-operator#known-bindable-operators).

## Pre-requisites
## Running the Command

### Pre-requisites
* A directory containing a Devfile; if you don't have one, see [odo init](init.md) on obtaining a devfile.
* A cluster with the Service Binding operator installed, along with the operator whose service you need to bind to
valaparthvi marked this conversation as resolved.
Show resolved Hide resolved

## Interactive Mode
### Interactive Mode
In the interactive mode, you will be guided to choose:
* a service from the list of bindable service instances as supported by the Service Binding Operator,
* option to bind the service as a file,
* option to bind the service as a file (see [Understanding Bind as Files](#understanding-bind-as-files) for more information on this),
* a name for the binding.

```shell
# Add binding between service named 'myservice',
# and the component present in the working directory in the interactive mode
# Add binding between a service, and the component present in the working directory in the interactive mode
odo add binding
```

## Non-interactive mode
### Non-interactive mode
In the non-interactive mode, you will have to specify the following required information through the command-line:
* `--service` flag to specify the service you want to bind to,
* `--name` flag to specify a name for the binding,
* `--name` flag to specify a name for the binding; see [Understanding Bind as Files](#understanding-bind-as-files) for more information on this.
valaparthvi marked this conversation as resolved.
Show resolved Hide resolved
* `--bind-as-files` flag to specify if the service should be bound as a file; this flag is set to true by default.


```shell
# Add binding between a service named 'myservice',
# Add binding between a service named 'cluster-sample',
# and the component present in the working directory in the non-interactive mode
odo add binding --name mybinding --service myRedisService.Redis
```

### Formats supported by the `--service` flag
#### Understanding Bind as Files
To connect your component with a service, you need to store some data(e.g. username, password, host address) on your component's container.
If the service is bound as files, this data will be written to a file and stored on the container, else it will be exported to Environment Variables inside the container.

Note that every piece of data is stored in its own individual file or environment variable.
For example, if your data includes a username, and password, then 2 separate files, or 2 environment variables will be created to store them both.
valaparthvi marked this conversation as resolved.
Show resolved Hide resolved

#### Formats supported by the `--service` flag
The `--service` flag supports the following formats to specify the service name:
* `<name>`
* `<name>.<kind>`
Expand All @@ -46,19 +55,18 @@ The `--service` flag supports the following formats to specify the service name:

The above formats are helpful when multiple services with the same name exist on the cluster.

#### Examples -
### Using different formats
```shell
# Add binding between a service named 'myservice',
# and the component present in the working directory
odo add binding --service myservice --name myRedisService
# Add binding between a service named 'cluster-sample', and the component present in the working directory
odo add binding --service cluster-sample --name restapi-cluster-sample

# Add binding between service named 'myservice' of kind 'Redis', and APIGroup 'redis.redis.opstreelab.in',
# Add binding between service named 'cluster-sample' of kind 'Cluster', and APIGroup 'postgresql.k8s.enterprisedb.io',
# and the component present in the working directory
odo add binding --service myservice/Redis.redis.redis.opstreelab.in --name myRedisService
odo add binding --service myservice.Redis.redis.redis.opstreelab.in --name myRedisService
odo add binding --service cluster-sample/Cluster.postgresql.k8s.enterprisedb.io --name restapi-cluster-sample
odo add binding --service cluster-sample.Cluster.postgresql.k8s.enterprisedb.io --name restapi-cluster-sample

# Add binding between service named 'myservice' of kind 'Redis',
# Add binding between service named 'cluster-sample' of kind 'Cluster',
# and the component present in the working directory
odo add binding --service myservice/Redis --name myRedisService
odo add binding --service myservice.Redis --name myRedisService
```
odo add binding --service cluster-sample/Cluster --name restapi-cluster-sample
odo add binding --service cluster-sample.Cluster --name restapi-cluster-sample
```
22 changes: 16 additions & 6 deletions pkg/binding/asker/survey_asker.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import (
"github.com/AlecAivazis/survey/v2"
)

const (
BindAsFiles = "Bind As Files"
BindAsEnvVar = "Bind As Environment Variables"
)
dharmit marked this conversation as resolved.
Show resolved Hide resolved

type Survey struct{}

func NewSurveyAsker() *Survey {
Expand Down Expand Up @@ -40,14 +45,19 @@ func (s *Survey) AskServiceBindingName(defaultName string) (string, error) {
}

func (o *Survey) AskBindAsFiles() (bool, error) {
question := &survey.Confirm{
Message: "Bind as files?",
Default: true,
question := &survey.Select{
Message: "How do you want to bind the service?",
Options: []string{BindAsFiles, BindAsEnvVar},
}
var answer bool
var answer string
err := survey.AskOne(question, &answer)
if err != nil {
return false, err
return true, err
}
return answer, nil

var bindAsFiles bool
if answer == BindAsFiles {
bindAsFiles = true
}
return bindAsFiles, nil
}
4 changes: 2 additions & 2 deletions pkg/odo/cli/add/binding/binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
const BindingRecommendedCommandName = "binding"

var addBindingExample = ktemplates.Examples(`
# Add binding between service named 'myservice' and the component present in the working directory in the interactive mode
# Add binding between a service, and the component present in the working directory in the interactive mode
valaparthvi marked this conversation as resolved.
Show resolved Hide resolved
%[1]s

# Add binding between service named 'myservice' and the component present in the working directory
Expand Down Expand Up @@ -117,7 +117,7 @@ func (o *AddBindingOptions) Run(_ context.Context) error {
kindGroup := strings.ReplaceAll(strings.ReplaceAll(splitService[1], "(", ""), ")", "")
exitMessage += fmt.Sprintf("\nYou can automate this command by executing:\n odo add binding --service %s.%s --name %s", serviceName, kindGroup, bindingName)
if !bindAsFiles {
exitMessage += " --bind-as-files false"
exitMessage += " --bind-as-files=false"
}
}
log.Infof(exitMessage)
Expand Down
1 change: 1 addition & 0 deletions tests/integration/cmd_namespace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ var _ = Describe("odo create/delete/list/set namespace/project tests", func() {
Describe("create "+commandName, func() {
namespace := fmt.Sprintf("%s-%s", helper.RandString(4), commandName)

// TODO: Remove for loop; find a way to use DescribeTable
It(fmt.Sprintf("should successfully create the %s", commandName), func() {
helper.Cmd("odo", "create", commandName, namespace, "--wait").ShouldPass()
defer func(ns string) {
Expand Down
104 changes: 104 additions & 0 deletions tests/interactive/cmd_add_binding_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
//go:build linux || darwin || dragonfly || solaris || openbsd || netbsd || freebsd
// +build linux darwin dragonfly solaris openbsd netbsd freebsd

package interactive

import (
"fmt"
"os"
"path/filepath"

"github.com/redhat-developer/odo/tests/helper"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("odo init interactive command tests", func() {

var commonVar helper.CommonVar
var serviceName string

// This is run before every Spec (It)
var _ = BeforeEach(func() {
if helper.IsKubernetesCluster() {
Skip("Operators have not been setup on Kubernetes cluster yet. Remove this once the issue has been fixed.")
Copy link
Member

Choose a reason for hiding this comment

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

Really? Why is that the case? I mean, what's blocking us from setting up Operators on k8s cluster?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have to take a jab at it again, but the last time we tried, we had some problem related to annotations' character limit with a certain OLM version. I work with a minikube cluster locally, so I can ascertain that it works well on a vanilla K8s cluster, but I'll have to take a look at test cluster again.

}
commonVar = helper.CommonBeforeEach()
helper.Chdir(commonVar.Context)

// We make EXPLICITLY sure that we are outputting with NO COLOR
// this is because in some cases we are comparing the output with a colorized one
os.Setenv("NO_COLOR", "true")

// Ensure that the operators are installed
commonVar.CliRunner.EnsureOperatorIsInstalled("service-binding-operator")
commonVar.CliRunner.EnsureOperatorIsInstalled("cloud-native-postgresql")
Eventually(func() string {
out, _ := commonVar.CliRunner.GetBindableKinds()
return out
}, 120, 3).Should(ContainSubstring("Cluster"))
addBindableKind := commonVar.CliRunner.Run("apply", "-f", helper.GetExamplePath("manifests", "bindablekind-instance.yaml"))
Expect(addBindableKind.ExitCode()).To(BeEquivalentTo(0))
serviceName = "cluster-sample" // Hard coded from bindablekind-instance.yaml
})

// Clean up after the test
// This is run after every Spec (It)
var _ = AfterEach(func() {
helper.CommonAfterEach(commonVar)
})

When("the component is bootstrapped", func() {
var componentName = "mynode"
var bindingName string
BeforeEach(func() {
helper.Cmd("odo", "init", "--name", componentName, "--devfile-path", helper.GetExamplePath("source", "devfiles", "nodejs", "devfile.yaml"), "--starter", "nodejs-starter").ShouldPass()
bindingName = fmt.Sprintf("%s-%s", componentName, serviceName)
})

It("should successsfully add binding to the devfile (Bind as Environment Variables)", func() {
command := []string{"odo", "add", "binding"}

_, err := helper.RunInteractive(command, nil, func(ctx helper.InteractiveContext) {
helper.ExpectString(ctx, "Select service instance you want to bind to:")
helper.SendLine(ctx, "cluster-sample (Cluster.postgresql.k8s.enterprisedb.io)")

helper.ExpectString(ctx, "Enter the Binding's name")
helper.SendLine(ctx, "\n")

helper.ExpectString(ctx, "How do you want to bind the service?")
helper.SendLine(ctx, "Bind as Environment Variables")

helper.ExpectString(ctx, "Successfully added the binding to the devfile.")

helper.ExpectString(ctx, fmt.Sprintf("odo add binding --service cluster-sample.Cluster.postgresql.k8s.enterprisedb.io --name %s --bind-as-files=false", bindingName))
})

Expect(err).To(BeNil())
components := helper.GetDevfileComponents(filepath.Join(commonVar.Context, "devfile.yaml"), bindingName)
Expect(components).ToNot(BeNil())
})

It("should successsfully add binding to the devfile (Bind as Files)", func() {
command := []string{"odo", "add", "binding"}

_, err := helper.RunInteractive(command, nil, func(ctx helper.InteractiveContext) {
helper.ExpectString(ctx, "Select service instance you want to bind to:")
helper.SendLine(ctx, "cluster-sample (Cluster.postgresql.k8s.enterprisedb.io)")

helper.ExpectString(ctx, "Enter the Binding's name")
helper.SendLine(ctx, "\n")

helper.ExpectString(ctx, "How do you want to bind the service?")
helper.SendLine(ctx, "Bind as Files")

helper.ExpectString(ctx, "Successfully added the binding to the devfile.")
})

Expect(err).To(BeNil())
components := helper.GetDevfileComponents(filepath.Join(commonVar.Context, "devfile.yaml"), bindingName)
Expect(components).ToNot(BeNil())
})
})
})