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

Blog: Connecting to a service without SBO #5915

Merged
merged 12 commits into from
Jul 7, 2022
326 changes: 326 additions & 0 deletions docs/website/blog/2022-06-30-binding-database-service-without-sbo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,326 @@
---
title: Binding to a database service without SBO
valaparthvi marked this conversation as resolved.
Show resolved Hide resolved
author: Parthvi Vala
author_url: https://github.com/valaparthvi
author_image_url: https://github.com/valaparthvi.png
tags: ["binding"]
slug: binding-database-service-without-sbo
---

How to bind to your application to a database service without the Service Binding Operator.
valaparthvi marked this conversation as resolved.
Show resolved Hide resolved

<!--truncate-->

There are a few ways of binding your application to a database service with the help of odo. The recommended way is with the help of Service Binding Operator(SBO), but it is also possible to bind without it, and this blog will show you how.


## Architecture
We have a simple CRUD application built in Go that can create/list/update/delete a place. This application requires connecting to a MongoDB database in order to function correctly, which will be deployed as a microservice on the cluster.

## Prerequisites:
This blog assumes:
- [odo v3.0.0-beta1](https://github.com/redhat-developer/odo/releases/tag/v3.0.0-beta1)
- you have admin access to a Kubernetes or OpenShift cluster to be able to install the MongoDB operator.
valaparthvi marked this conversation as resolved.
Show resolved Hide resolved
valaparthvi marked this conversation as resolved.
Show resolved Hide resolved
- you have _Helm_ installed on your system. See https://helm.sh/docs/intro/install/ for installation instructions.

## (Optional) Setting up the namespace
0. We will create a new namespace to deploy our application in, with the help of odo.
```sh
odo create namespace restapi-mongodb
```

## Setting up the application
1. Clone the repository, and cd into it.
```sh
git clone https://github.com/valaparthvi/restapi-mongodb-odo.git && cd restapi-mongodb-odo
dharmit marked this conversation as resolved.
Show resolved Hide resolved
```


## Export environment variables
2. Export the necessary environment variables:
Copy link
Contributor

Choose a reason for hiding this comment

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

I find that exporting these variables at this point is premature. We cannot understand what they are for. I think it would be better to introduce the helm chart before to export them

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 agree, but then if we want to maintain the order of odo init > odo dev > deploy charts > wait for odo dev to detect changes, it becomes necessary to export these variables at this point

Copy link
Contributor

Choose a reason for hiding this comment

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

The order

  • odo init
  • odo dev
  • deploy chart
    is just an example provided by Tomas, I'm not sure it must be respected at all price.

By first deploying the chart (including exportint variables), then running odo init and odo dev, the flow seems more understandable to me

Copy link
Member

Choose a reason for hiding this comment

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

is just an example provided by Tomas, I'm not sure it must be respected at all price.

By first deploying the chart (including exportint variables), then running odo init and odo dev, the flow seems more understandable to me

I like the current approach of enabling Helm repo first. But I don't agree with the idea of spinning up Helm chart using it before doing odo init. If it were me, I would first show that your Go application is deployed using odo dev, but curl request made to it is throwing 404 or some error. Thereafter, we spin up the MongoDB database required by the application.

Or maybe, do an odo dev without all the variables which would help one connect to the MongoDB database. Make a curl call that returns 404 or some error. Now add information about connection to MongoDB and see how the application returns expected output.

I'm saying this because that's how I think an application developer's flow would be like. But then I have not a lot of application development myself, so correct me if I'm mistaken.

Copy link
Contributor Author

@valaparthvi valaparthvi Jul 6, 2022

Choose a reason for hiding this comment

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

Or maybe, do an odo dev without all the variables which would help one connect to the MongoDB database. Make a curl call that returns 404 or some error. Now add information about connection to MongoDB and see how the application returns expected output.

I'm saying this because that's how I think an application developer's flow would be like. But then I have not a lot of application development myself, so correct me if I'm mistaken.

I agree with you, but I can't find a flow that works for everyone. For now, I have added a note stating that odo dev will fail if run before adding connection information(bf76584).

Copy link
Member

Choose a reason for hiding this comment

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

Works for me. I discussed this with @feloy and agree with the flow we have now.

```sh
export MY_MONGODB_ROOT_USERNAME=root
valaparthvi marked this conversation as resolved.
Show resolved Hide resolved
export MY_MONGODB_ROOT_PASSWORD=my-super-secret-root-password
export MY_MONGODB_USERNAME=my-app-username
export MY_MONGODB_PASSWORD=my-app-super-secret-password
export MY_MONGODB_DATABASE=my-app
```


## Download the devfile.yaml
3. Run `odo init` to fetch the necessary devfile.
```sh
odo init --devfile go --name places
valaparthvi marked this conversation as resolved.
Show resolved Hide resolved
```


## Deploy the application
4. Run `odo dev` to deploy the application on the cluster.
```sh
odo dev --var PASSWORD=$MY_MONGODB_ROOT_PASSWORD --var USERNAME=$MY_MONGODB_ROOT_USERNAME
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here, I think it is premature to pass these variables, as they are not existing in the Devfile yet and we are not using them. I think it would be better to wait for steps 7.x before to do this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Then I can simply not do odo dev first and put it after deploying the microservice.

```
<details>
<summary>Expected output:</summary>

```sh
$ odo dev --var PASSWORD=$MY_MONGODB_ROOT_PASSWORD --var USERNAME=$MY_MONGODB_ROOT_USERNAME
__
/ \__ Developing using the restapi Devfile
\__/ \ Namespace: restapi-mongodb
/ \__/ odo version: v3.0.0-alpha3
\__/

↪ Deploying to the cluster in developer mode
✓ Waiting for Kubernetes resources [52s]
✓ Syncing files into the container [844ms]
✓ Building your application in container on cluster (command: build) [5s]
• Executing the application (command: run) ...
✗ Executing the application (command: run) [186ms]
⚠ Devfile command "run" exited with an error status in 20 second(s)
⚠ Last 100 lines of log:
go: downloading github.com/spf13/viper v1.11.0
...
...
2022/07/05 05:39:47 No binding username found

Your application is now running on the cluster

- Forwarding from 127.0.0.1:40001 -> 3000

Watching for changes in the current directory /home/pvala/restapi-mongodb-odo
Press Ctrl+c to exit `odo dev` and delete resources from the cluster
```
</details>

You will notice that the 'run' command has failed with some logs, this is expected, because like we mentioned before, our Go application is dependent on the MongoDB service and will not function unless it is connected to it.

## Setting up the MongoDB microservice
We are going to use the Bitnami's helm charts for creating our MongoDB database.

5. Add the Bitnami's Helm charts repository and make your Helm client up to date with it:
```sh
helm repo add bitnami https://charts.bitnami.com/bitnami && helm repo update
```

6. Create the MongoDB service.
```sh
helm install mongodb bitnami/mongodb \
--set auth.rootPassword=$MY_MONGODB_ROOT_PASSWORD \
--set auth.username=$MY_MONGODB_USERNAME \
--set auth.password=$MY_MONGODB_PASSWORD \
--set auth.database=$MY_MONGODB_DATABASE
valaparthvi marked this conversation as resolved.
Show resolved Hide resolved
```

<details>
<summary>Expected output:</summary>

```sh
$ helm install mongodb bitnami/mongodb \
--set auth.rootPassword=$MY_MONGODB_ROOT_PASSWORD \
--set auth.username=$MY_MONGODB_USERNAME \
--set auth.password=$MY_MONGODB_PASSWORD \
--set auth.database=$MY_MONGODB_DATABASE
NAME: mongodb
LAST DEPLOYED: Tue Jul 5 15:53:40 2022
NAMESPACE: restapi-mongodb
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
CHART NAME: mongodb
CHART VERSION: 12.1.24
APP VERSION: 5.0.9

** Please be patient while the chart is being deployed **

MongoDB&reg; can be accessed on the following DNS name(s) and ports from within your cluster:

mongodb.restapi-mongodb.svc.cluster.local

To get the root password run:

export MONGODB_ROOT_PASSWORD=$(kubectl get secret --namespace restapi-mongodb mongodb -o jsonpath="{.data.mongodb-root-password}" | base64 -d)
feloy marked this conversation as resolved.
Show resolved Hide resolved

To get the password for "my-app-username" run:

export MONGODB_PASSWORD=$(kubectl get secret --namespace restapi-mongodb mongodb -o jsonpath="{.data.mongodb-passwords}" | base64 -d | awk -F'
,' '{print $1}')

To connect to your database, create a MongoDB&reg; client container:

kubectl run --namespace restapi-mongodb mongodb-client --rm --tty -i --restart='Never' --env="MONGODB_ROOT_PASSWORD=$MONGODB_ROOT_PASSWORD" --
image docker.io/bitnami/mongodb:5.0.9-debian-11-r1 --command -- bash

Then, run the following command:
mongosh admin --host "mongodb" --authenticationDatabase admin -u root -p $MONGODB_ROOT_PASSWORD

To connect to your database from outside the cluster execute the following commands:

kubectl port-forward --namespace restapi-mongodb svc/mongodb 27017:27017 &
mongosh --host 127.0.0.1 --authenticationDatabase admin -p $MONGODB_ROOT_PASSWORD

```
</details>

Notice the resources(sevice, deployment, and secrets) that are deployed.

Wait for the pods to come up, this might take a few minutes:
```sh
$ kubectl get pods
valaparthvi marked this conversation as resolved.
Show resolved Hide resolved
NAME READY STATUS RESTARTS AGE
mongodb-85fff797f6-fnwvl 1/1 Running 0 63s
```

## Adding the connection information to devfile.yaml
There are 3 changes that we will need to make to our devfile:

7.1 Change the `schemaVersion` of devfile to 2.2.0. This change is required to use certain features of the devfile.
```yaml
schemaVersion: 2.2.0
```
Please note that this change is only necessary because we are using [devfile variable substitution](https://odo.dev/docs/command-reference/dev/#substituting-variables).

7.2 Add a `variables` field in the devfile.
```yaml
variables:
PASSWORD: password
USERNAME: user
```
7.3 Edit the 'runtime' container component in devfile to add information such as username, password, and host required to connect to the MongoDB service.
```yaml
components:
- container:
...
...
env:
- name: username
value: "{{USERNAME}}"
- name: password
value: "{{PASSWORD}}"
- name: host
value: "mongodb"
valaparthvi marked this conversation as resolved.
Show resolved Hide resolved
name: runtime
```

The _username_, and _password_ values were passed to devfile.yaml when we ran the `odo dev` command.

The value for _host_ is name of the service that belongs to our database application, in this case it is a service resource called "mongodb", you might have noticed it when we deployed the helm chart.

<details>
<summary>Your final devfile.yaml should look something like this:</summary>

```yaml
commands:
- exec:
commandLine: GOCACHE=${PROJECT_SOURCE}/.cache go build main.go
component: runtime
group:
isDefault: true
kind: build
hotReloadCapable: false
workingDir: ${PROJECT_SOURCE}
id: build
- exec:
commandLine: ./main
component: runtime
group:
isDefault: true
kind: run
hotReloadCapable: false
workingDir: ${PROJECT_SOURCE}
id: run
components:
- container:
dedicatedPod: false
endpoints:
- name: port-3000-tcp
protocol: tcp
secure: false
targetPort: 3000
valaparthvi marked this conversation as resolved.
Show resolved Hide resolved
image: golang:latest
memoryLimit: 1024Mi
mountSources: true
env:
- name: username
value: "{{USERNAME}}"
- name: password
value: "{{PASSWORD}}"
- name: host
value: mongodb
name: runtime
variables:
PASSWORD: password
USERNAME: user
metadata:
description: Stack with the latest Go version
displayName: Go Runtime
icon: https://raw.githubusercontent.com/devfile-samples/devfile-stack-icons/main/golang.svg
language: go
name: restapi
projectType: go
tags:
- Go
version: 1.0.0
schemaVersion: 2.2.0
starterProjects:
- git:
checkoutFrom:
revision: main
remotes:
origin: https://github.com/devfile-samples/devfile-stack-go.git
name: go-starter
```
</details>

Changing the devfile.yaml will trigger the `odo dev` command to push the changes and execute the run commands again, wait for it to finish execution.
```sh
$ odo dev --var PASSWORD=$MY_MONGODB_ROOT_PASSWORD --var USERNAME=$MY_MONGODB_ROOT_USERNAME
...
2022/07/05 05:39:47 No binding username found
...
...
File /home/pvala/restapi-mongodb-odo/devfile.yaml changed
Pushing files...

✓ Waiting for Kubernetes resources [35s]
✓ Syncing files into the container [173ms]
✓ Building your application in container on cluster (command: build) [13s]
• Executing the application (command: run) ...
- Forwarding from 127.0.0.1:40001 -> 3000

Watching for changes in the current directory /home/pvala/restapi-mongodb-odo
Press Ctrl+c to exit `odo dev` and delete resources from the cluster
```

## Accessing the application
8. Run the following curl command to test the application:
```sh
curl 127.0.0.1:40001/api/places
```
This will return a _null_ response since the database is currently empty, but it also means that we have successfully connected to our database application.

9. Add some data to the database:
```sh
curl -sSL -XPOST -d '{"title": "Agra", "description": "Land of Tajmahal"}' 127.0.0.1:40001/api/places
```

10. Fetch the list of places again:
```sh
$ curl 127.0.0.1:40001/api/places

{"id":"62c2a0659fa147e382a4db31","title":"Agra","description":"Land of Tajmahal"}
```

### List of available API endpoints
- GET `/api/places` - List all places
- POST `/api/places` - Add a new place
- PUT `/api/places` - Update a place
- GET `/api/places/<id>` - Fetch place with id `<id>`
- DELETE `/api/places/<id>` - Delete place with id `<id>`

## Conclusion
To conclude this blog, it is possible to connect your application with another microservice without the Service Binding Operator if you have the correct connection information. Using the Service Binding Operator with a [Bindable Operator](https://github.com/redhat-developer/service-binding-operator#known-bindable-operators) makes it easy for you to not care about finding the connection information and ease the binding.
Copy link
Contributor

Choose a reason for hiding this comment

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

You could add to the conclusion that it is up to the reader to manage the secrets stored in the environment variables MY_MONGODB_ROOT_PASSWORD and MY_MONGODB_ROOT_USERNAME, so they are accessible every time the following command is executed:

odo dev --var PASSWORD=$MY_MONGODB_ROOT_PASSWORD --var USERNAME=$MY_MONGODB_ROOT_USERNAME --var HOST="mongodb"

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've added this note in the section where we introduce these variables for the first time, I think it makes more sense to put them there than in the conclusion.


### Related articles on binding:
* [Binding an external service with odo v3](../blog/2022-06-14-binding-external-service.md)
* [odo add binding](../../website/docs/command-reference/add-binding.md)