Commit f49e7e49 authored by Pascal Déchamboux's avatar Pascal Déchamboux
Browse files

feat: add support for managing service instance lifecycle (inc. doc)

parent 2d4bfd82
Loading
Loading
Loading
Loading
+71 −4
Original line number Diff line number Diff line
# GitLab CI template for Cloud Foundry

This project implements a GitLab CI/CD template to deploy your application to a [Cloud Foundry](https://www.cloudfoundry.org/) platform.
This project implements a GitLab CI/CD template to deploy your application to a [Cloud Foundry](https://www.cloudfoundry.org/) platform as well as to manage service instances that can be bound to those applications.

## Overview

@@ -11,7 +11,7 @@ This template implements continuous delivery/continuous deployment for projects
The template supports **review** environments: those are dynamic and ephemeral environments to deploy your
_ongoing developments_ (a.k.a. _feature_ or _topic_ branches).

When enabled, it deploys the result from upstream build stages to a dedicated and temporary environment.
When enabled, it deploys the result from upstream build stages to a dedicated and temporary environment (including service instances).
It is only active for non-production, non-integration branches.

It is a strict equivalent of GitLab's [Review Apps](https://docs.gitlab.com/ee/ci/review_apps/) feature.
@@ -23,7 +23,7 @@ It also comes with a _cleanup_ job (accessible either from the _environments_ pa
If you're using a Git Workflow with an integration branch (such as [Gitflow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow)),
the template supports an **integration** environment. 

When enabled, it deploys the result from upstream build stages to a dedicated environment.
When enabled, it deploys the result from upstream build stages to a dedicated environment (including service instances).
It is only active for your integration branch (`develop` by default).

### Production environments
@@ -324,6 +324,73 @@ feature branch.
:warning: in case of scheduling the cleanup, you'll probably have to create an almost empty branch without any other 
template (no need to build/test/analyse your code if your only goal is to cleanup environments).

### Service instances management

Deployment jobs also provide a means to manage the lifecycle of service instances along with application deployment. This means that a service instance or the information to access an external service instance (aka "user provided service" in CloudFoundry), can be provisioned before the application is deployed (if it does not exist, it is created). Furthermore, in case of cleanup, they are deleted after the application is stopped/deleted itself. Concerning deletion, there is an exception: in case of `production` environment, automatic deletion is not supported (for safety reason).

For that purpose, the project has only to supply one json file per service instance that describes its characteristics. In order to identify such files, they should all have the same suffix: `cf-service.json`.

There are to cases to locate these files.

1. There is a sub-directory with the name of the type of environment to be managed by the pipeline (i.e. `review`, `integration`, `staging`, or `production`) present in the `$CF_SCRIPTS_DIR` directory. In that case, the files are looked up in this directory and only in this one.

2. There is no such sub-directory and in that case, the files are looked up directly in the `$CF_SCRIPTS_DIR` directory. This means that these files (and the service instances they represent) are probably shared by all types of environment.

Concerning the format of such a file describing a service instance it is quite straightforward. This is a json record with the following fields:
- `cfServiceName`: this is the name of the service instance to be created
- `cfServiceOffering`: this is the service offering to be used to create the service instance
- `cfServiceBroker`: this is the name of the broker to be used in case there are several service offerings with the same name
- `cfServicePlan`: this the service plan to be used within this service offering
- `cfServiceArgs`: this is a json record that contains the parameters to be passed to the service offering to parameterize the creation

Let us take an example:

```
{
	"cfServiceName": "logarythm_drain_catalog",
	"cfServiceOffering": "logarythm_drain_prod",
	"cfServicePlan": "httpdrain",
	"cfServiceArgs": {
		"cloudid": "stg_ods_m_00.aed.lizard.o6o3knwikgjewx4il6rq",
		"component": "int",
		"env": "ep1"
	}
}
```

It describes a service instance of the `logarythm_drain_prod` service created with the `httpdrain` plan. It is named `logarythm_drain_catalog`. Finally, it provides the parameters to be used at creation time in the form of a json record.

The equivalent `cf` command to create this service instance is the following:

```
cf create-service logarythm_drain_prod httpdrain logarythm_drain_catalog -c '{"cloudid": "stg_ods_m_00.aed.lizard.o6o3knwikgjewx4il6rq","component":"int","env":"ep1"}'
```

Not only service instance from service of the Cloud Foundry marketplace can be managed. User provided services are also supported. For that purpose, the same file structure is used. To define a user provided service instance, the same file format is used while positioning both `cfServiceOffering` and `cfServicePlan` fields to the specific value `CUPS` ou `cups`. Then the arguments passed through the `cfServiceArgs` field are provided to the application when binding to this service instance.

Let us take an example:

```
{
	"cfServiceName": "my-ups",
	"cfServiceOffering": "CUPS",
	"cfServicePlan": "CUPS",
	"cfServiceArgs": {
		"user": "service-user",
		"password": "service-password",
		"url": "https://my-service-endpoint.domain.com"
	}
}
```

The equivalent `cf` command to create this user provided service instance is the following:

```
cf cups my-ups -p '{"user":"service-user","password":"service-password","url":"https://my-service-endpoint.domain.com"}'
```

Then at service binding, the application gets the three credential parameters as specified in the descriptor file, that are `user`, `password` and `url`.

## Variants

### Vault variant
+7 −5
Original line number Diff line number Diff line
@@ -755,7 +755,7 @@ stages:
  }

  function get_desc_field() {
    local field_value=$(cat ${CF_SCRIPTS_DIR}/$1 | jq .${2})
    local field_value=$(cat $1 | jq .${2})
    local notnull=$3
    export desc_field_error=""
    if [[ "${field_value}" == "null" ]]; then
@@ -844,12 +844,12 @@ stages:
  }

  function delete_service() {
    local service_desc=$1
    case ${ENV_TYPE} in
    staging | production)
      log_info "Delete service instance ${COLOR_YELLOW}${service_desc}${COLOR_NONE}: ignored (env=${ENV_TYPE})"
      ;;
    *)
      local service_desc=$1
      smsg="Delete service instance ${COLOR_YELLOW}${service_desc}${COLOR_NONE}: "
      get_desc_field $service_desc "cfServiceName" "true"
      if [[ ! -z "$desc_field_error" ]]; then
@@ -886,15 +886,17 @@ stages:
    then
      log_info "No service found to manage, expected files are: \\e[33;1m${service_dir}/*.cf-service.json\\e[0m"
      return 0
    else
      log_info "Services to be deleted: \\e[33;1m${service_files}\\e[0m"
    fi
    for service_desc in $service_files
    do
      case ${mgt_action} in
      create)
        create_service "$service_desc"
        create_service "${service_dir}/${service_desc}"
        ;;
      delete)
        delete_service "$service_desc"
        delete_service "${service_dir}/${service_desc}"
        ;;
      esac
    done