/cf-usb

Universal Service Broker for Cloud Foundry

Primary LanguageGoOtherNOASSERTION

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.

Related Projects

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:

  • Go 1.6

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}