Universal Service Broker
Summary
The cf-usb project implements and exposes the Open Service Broker API.
It uses plugins (drivers) to connect to different services.
Configuration and Usage
The Universal Service Broker has three main components:
- Universal Service Broker
- Sidecars
- cf CLI plugin
The USB runs as a component within Cloud Foundry. Its BOSH release
is inside the USB main repo. This is included as a submodule in the SUSE CF project, and runs by default.
These usage instructions assume:
- A working SUSE CAP instance is available
- The SUSE CAP instance is able to connect to a Docker registry
- The user is able to access the Kubernetes cluster with the
kubectl
tool
- The user has admin access to the SUSE CAP cluster
- A MySQL or Postgres server is available (manual sidecar setup)
Sidecar Setup
The USB itself is just a broker, and doesn't run any actual services. These are
provided by the sidecars, and run outside of the CF cluster.
To build the sidecar, check out the sidecar project:
mkdir -p $GOPATH/src/github.com/SUSE
git clone https://github.com/SUSE/cf-usb-sidecar $GOPATH/src/github.com/SUSE/cf-usb-sidecar
cd $GOPATH/src/github.com/SUSE/cf-usb-sidecar
git submodule update --init --recursive
Configure your Docker repository information:
export DOCKER_REPOSITORY=docker.io
export DOCKER_ORGANIZATION=splatform
docker login # if authorization is required
Then build the top level dependencies and the sidecar:
make tools
make build-image
cd csm-extensions/services/dev-mysql
make build-image build-service-image publish-image helm
The generated Helm chart will be available in the output/helm
directory.
There are two ways to set up the sidecar: automatic, or manual.
- Automatic is 'dev-grade', which creates a database instance as part of the setup.
This is useful for testing, but is not meant for production.
- Manual is meant for a production cluster, which will point to a
database instance that is already configured.
Automatic
# You will need to know the namespaces and domain for your cluster:
UAA_NAMESPACE=uaa
CF_NAMESPACE=cf
CF_DOMAIN=cf-dev.io
SIDECAR_NAMESPACE=mysql
UAA_CA_CERT="$(kubectl get secret secret --namespace ${UAA_NAMESPACE} -o jsonpath="{.data['internal-ca-cert']}" | base64 --decode -)"
CF_CA_CERT="$(kubectl get secret secret --namespace ${CF_NAMESPACE} -o jsonpath="{.data['internal-ca-cert']}" | base64 --decode -)"
CF_PASSWORD="$(kubectl get secret secret --namespace ${CF_NAMESPACE} -o jsonpath="{.data['cluster-admin-password']}" | base64 --decode -)"
helm install ./output/helm --name mysql-instance --namespace ${SIDECAR_NAMESPACE} \
--set "env.UAA_CA_CERT=${UAA_CA_CERT}" \
--set "env.CF_CA_CERT=${CF_CA_CERT}" \
--set "env.SERVICE_LOCATION=http://cf-usb-sidecar-mysql.${SIDECAR_NAMESPACE}.svc.cluster.local:8081" \
--set "env.SERVICE_MYSQL_HOST=AUTO" \
--set "env.CF_ADMIN_USER=admin" \
--set "env.CF_ADMIN_PASSWORD=${CF_PASSWORD}" \
--set "env.CF_DOMAIN=${CF_DOMAIN}" \
--set "kube.organization=${DOCKER_ORGANIZATION}" \
--set "kube.registry.hostname=${DOCKER_REPOSITORY}"
Eventually you should see two pods start in the SIDECAR_NAMESPACE
:
$ kubectl get pods --namespace ${SIDECAR_NAMESPACE}
NAME READY STATUS RESTARTS AGE
cf-usb-sidecar-mysql-b44d4d66f-d27qb 1/1 Running 0 2m
mysql-0 1/1 Running 0 2m
There will also be a 'setup' pod that starts an errand, but it will finish and exit.
Manual
# You will need to know the namespaces and domain for your cluster:
UAA_NAMESPACE=uaa
CF_NAMESPACE=cf
CF_DOMAIN=cf-dev.io
# Authentication details are needed for the external database service:
MYSQL_HOST=mysql-host
MYSQL_PASS=mysql-password
MYSQL_PORT=3306
MYSQL_USER=root
SIDECAR_NAMESPACE=mysql
UAA_CA_CERT="$(kubectl get secret secret --namespace ${UAA_NAMESPACE} -o jsonpath="{.data['internal-ca-cert']}" | base64 --decode -)"
CF_CA_CERT="$(kubectl get secret secret --namespace ${CF_NAMESPACE} -o jsonpath="{.data['internal-ca-cert']}" | base64 --decode -)"
CF_PASSWORD="$(kubectl get secret secret --namespace ${CF_NAMESPACE} -o jsonpath="{.data['cluster-admin-password']}" | base64 --decode -)"
helm install ./output/helm --name mysql-instance --namespace ${SIDECAR_NAMESPACE} \
--set "env.UAA_CA_CERT=${UAA_CA_CERT}" \
--set "env.CF_CA_CERT=${CF_CA_CERT}" \
--set "env.SERVICE_LOCATION=http://cf-usb-sidecar-mysql.${SIDECAR_NAMESPACE}.svc.cluster.local:8081" \
--set "env.SERVICE_MYSQL_HOST=${MYSQL_HOST}" \
--set "env.SERVICE_MYSQL_PASS=${MYSQL_PASS}" \
--set "env.SERVICE_MYSQL_PORT=${MYSQL_PORT}" \
--set "env.SERVICE_MYSQL_USER=${MYSQL_USER}" \
--set "env.CF_ADMIN_USER=admin" \
--set "env.CF_ADMIN_PASSWORD=${CF_PASSWORD}" \
--set "env.CF_DOMAIN=${CF_DOMAIN}" \
--set "kube.organization=${DOCKER_ORGANIZATION}" \
--set "kube.registry.hostname=${DOCKER_REPOSITORY}"
Eventually you should see one pod start in the SIDECAR_NAMESPACE
:
$ kubectl get pods --namespace ${SIDECAR_NAMESPACE}
NAME READY STATUS RESTARTS AGE
cf-usb-sidecar-mysql-75947994f9-ffh6l 1/1 Running 0 1m
There will also be a 'setup' pod that starts an errand, but it will finish and exit.
Marketplace
Once the pods are ready, it should be available in the marketplace:
$ cf marketplace
Getting services from marketplace in org org / space space as admin...
OK
service plans description
postgres default Default service
mysql default Default service
TIP: Use 'cf marketplace -s SERVICE' to view descriptions of individual plans of a given service.
Using the service with an app
At this point, services can be made available to apps. In this case we're going to use the django-cms app.
git clone https://github.com/scf-samples/django-cms -b scf
cd django-cms
cf create-service postgres default django-cms-db
cf push --no-start django-cms
cf set-env django-cms DISABLE_COLLECTSTATIC 1
cf set-env django-cms DJANGO_SETTINGS_MODULE settings
cf start django-cms
Unmanaged USB
Unmanaged USB provides provides a limited number of features and implements the basic functionality of a Cloud Foundry Service Broker.
Constraints:
- it does not provide functionality for upgrading drivers, services, plans, etc.
- does not automatically register to the Cloud Controller.
- only
fileConfigProvider
can be used as a configuration provider.
- it does not expose a management API.
- if a
driver
fails to start, the connection between the driver
and the server cannot be establised or if the configuration/dials schema can not be validated, the USB exits with an exitcode != 0.
Deployment strategies
1. Cloud Foundry application
The unmanaged USB can be deployed as an app to a Cloud Foundry deployment. All the services managed must be external services.
2. fissile
TODO.
Managed USB
The managed USB provides a management API for configuration and update.
Constraints:
- it can not use
fileConfigProvider
as a configuration provider
Management API
Authorization
1. UAA
USB uses UAA as an authorization provider. It requires the cc_usb_management
OAuth client to be configured with the following properties:
- secret: {clientsecret}
- scope: cloud_controller.write,openid,cloud_controller.read,cloud_controller_service_permissions.read, cloud_controller_service_permissions.write
- authorities: usb.management.admin
- authorized-grant-types: client_credentials
2. Basic auth
Basic auth can be used when making calls to the USB management API.
API Definition
The usb management API is described here
fissile
TODO:
Configuration
cf-usb works with multiple configuration providers:
File configuration provider
cf-usb can take it's configuration from a .json file. The json file format is similar to a standard broker configuration file.
To start the broker with a file configuration provider you must provide the following cli options:
./usb fileConfigProvider --path {path_to_jsonfile}
dials
driver_configs
MySQL configuration provider
The state and the configuration of USB can be stored in a MySQL database.
To start the broker using a MySQL provider you must provide the following cli options:
Option |
Description |
--address , -a |
server address and port (mandatory) |
--database , -db |
database name |
--username , -u |
username |
--password , -p |
password |
Drivers
Folder structure
|-- cmd
| |-- main.go
|-- drivers
| |-- {driver_type}
| |-- data
| |-- schemas.go | Generated by usb Makefile
| |-- schemas
| |-- dails.json | dails JSON schema definition
| |-- config.json | driver_config JSON schema definition
Principals
- Each driver call must be represented by an atomic operation.
- USB must be able to validate all incoming CC requests using the driver interface.
Driver Interface
Ping
Checks if the driver can reach the server.
Request
Type |
Description |
*json.RawMessage |
Driver configuration object |
Response
Type |
Description |
bool |
Returns true if the driver can reach the server, false otherwise |
error |
Service connection error |
GetDialsSchema
Request
Type |
Description |
string |
empty string |
Response
Type |
Description |
string |
the json schema for the dials supported by the driver |
error |
GetDialsSchema error |
Example schema for MSSQL dials
{
"type": "object",
"properties": {
"max_dbsize_mb": {
"type": "integer",
"minimum": 0
}
},
"required": ["max_dbsize_mb"]
}
GetConfigSchema
Gets the JSON schema for the driver_config
Request
Type |
Description |
string |
empty string |
Response
Type |
Description |
string |
the json schema for the driver_config of the driver |
error |
GetDialsSchema error |
Example schema for MSSQL driver_config
{
"type": "object",
"properties": {
"brokerGoSqlDriver": {
"type": "string"
},
"brokerMssqlConnection": {
"type": "object",
"properties": {
"server": {
"type": "string"
},
"port": {
"type": "string"
},
"database": {
"type": "string"
},
"user id": {
"type": "string"
},
"password": {
"type": "string"
}
}
}
},
"required": [
"brokerGoSqlDriver",
"brokerMssqlConnection"
]
}
ProvisionInstance
Creates a service instance
Request
Type |
Description |
ProvisionInstanceRequest |
Object containing the instanceID, config object and dails object |
Dials are restrictions that can be applied to instances.
Example for MSSQL:
{
"max_dbsize_mb": 1500
}
Response
Type |
Description |
Instance |
Instance type object containing instanceID, status and description |
error |
Provision Instance error |
GetInstance
Retrieves an existing instance.
Request
Type |
Description |
GetInstanceRequest |
Object containing the instanceID and a config object |
Response |
|
Type |
Description |
Instance |
Instance type object containing instanceID, status and description |
error |
Instance Exists error |
GenerateCredentials
Generates for the for the specified instance
Request
Type |
Description |
GenerateCredentialsRequest |
Object containing the instanceID, the credentialsID and the config object |
Response
Type |
Description |
interface{} |
Connection information for the specified service |
error |
GenerateCredentials error |
Example for MSSQL:
type MssqlCredentials struct {
Hostname string `json:"hostname"`
Port int `json:"port"`
Name string `json:"name"`
Username string `json:"username"`
Password string `json:"password"`
ConnectionString string `json:"connectionString"`
}
GetCredentials
Retrieves existing credentials.
Request
Type |
Description |
GetCredentialsRequest |
Object containing the instanceID, the credentialsID and the config object |
Response |
|
Type |
Description |
Credentials |
Credentials type object containing credentialsID, status and description |
error |
CredentialsExists error |
RevokeCedentials
Revoke the credentials of the specified credentialsID
Request
Type |
Description |
RevokeCredentialsRequest |
Object containing the instanceID, the credentialsID and the config object |
Response |
|
Type |
Description |
Credentials |
Credentials type object containing credentialsID, status and description |
error |
RevokeCedentials error |
DeprovisionInstance
Request
Deprovisions the instance having the specified instanceID
Type |
Description |
DeprovisionInstanceRequest |
Object containing the instanceID and the config object |
Response
Type |
Description |
Instance |
Instance type object containing instanceID, status and description |
error |
DeprovisionInstance error |
Building and running
Requirements:
Building:
mkdir -p $GOPATH/src/github.com/SUSE
cd $GOPATH/src/github.com/SUSE
git clone git@github.com:SUSE/cf-usb.git
make tools
godep restore
make
Running:
To run one or more drivers, you need to copy the driver executable to {path_to_usb}/drivers/ or set the USB_DRIVER_PATH environment variable with the path to the driver executable
./usb {ConfigProvider} --{options}