CAMP

Amplify your testing with more configurations!

View the Project on GitHub STAMP-project/camp

CAMP Realize

CAMP also builds the docker images and the docker-compose file that we need to run the configurations that we generated using camp generate. We explain here:

  1. How to use the camp realize command;
  2. How to organize the template orchestration;
  3. How to specify the components’ implementation ;
  4. How to specify component-level realization;
  5. How to specify variable-level realization.

Usage

To implement the configurations we have generated, and provided we have completed the model.yml file, we simply invoke the command:

$> camp realize -d .

CAMP then generates new Dockerfiles, properly chained together according to the configurations generated by camp generate. In addition, CAMP injects the proper variables’ value in each configuration. It generates as well the service orchestration as a docker-compose file.

To work, camp realize needs three things:

  1. Some configurations generated using camp generate. CAMP will search for directories named like ./config_1, ./config_2, that must contains a file configuration.yml that CAMP generates.

  2. A camp.yml files that includes information about the implementation of each component. This information includes:

    • A template deployment, including the implementation of each component, and the configuration of the orchestration. So far, CAMP only supports the Docker technology.

    • Details about how CAMP must realize the variables.

Template Deployment

Below is a sample directory structure for a CAMP project. Note that the command camp generate would add another out directory, which we did not show here.

$ tree 
├── camp.yml
└── template
    ├── docker-compose.yml
    ├── postgres
    │   ├── Dockerfile
    │   └── postgresql-template.conf
    └── showcase
        ├── Dockerfile
        ├── mpm_prefork-template.conf
        └── mpm_worker-template.conf

The template directory contains a directory named after each component defined in the CAMP model (i.e., the file camp.yml), that contains a Dockerfile. It also contains a docker-compose.yml as a template orchestration.

Note that additional file may be added along with the Dockerfile, for confiuration purpose for instance.


Note. The names of the files directories matter. CAMP expects to find directory named template inside which it will search for a docker-compose.yml file and directory ndmes after the components defined in the CAMP model, each containing a Dockerfile.


Component Implementation

Using Dockerfile

The first thing we must add in our model.yml is the implementation of each component. For instance, if we have implemented the tests component with a Dockerfile, we must specify this as follows:

components:
   tests:
      provide_services: [ Tests ]
      require_services: [ Awesome ]
      variables:
         threads:
            values:
               range: [10, 50] # in GB
               coverage: 40
      implementation:
         docker:
            file: repo/tests/Dockerfile

Note that if there are other resources such as configuration files, they could stay with the Dockerfile and they would be copied in each generated configuration.

Using a Docker Image

Alternatively, some components may be implemented by existing docker images, such as database or application servers for instance. In our Awesome example, we added the following implementation:

components:
   postgres:
      provide_services: [ DB ]
      implementation:
         docker:
            image: postgres:10.6-alpine

CAMP will fetch the image postgres:10.6-alpine from Docker Hub.

Component Realization

We may attach realization actions, directly onto components. CAMP triggers these actions when it realizes every instances.

For instance, consider a case where two components offer the same service, say “WebProxy” for instance. Our orchestration could use either Apache and NGinx as proxies. We can ask CAMP to select a specific orchestration descriptor (i.e., a docker-compose file) depending on which web proxy implementation CAMP chooses.

To do so, we define a realization action in each possible component, at the component level (as opposed to variable-level). This action specifies which orchestration descriptor CAMP must include and which ones it must delete.

components:

    # This is the first candidate web proxy
    nginx:
       provide_services: [ WebProxy ]
       realization:
        - select: nginx_docker-compose.yml
          instead_of:
           - apache_docker-compose.yml
          as: docker-compose.yml

    # Here comes the second one.
    apache:
       provide_services: [ WebProxy ]
       realization:
        - select: apache_docker-compose.yml
          instead_of:
            - nginx_docker-compose.yml
          as: docker-compose.yml

Depending on the component that CAMP includes in the configuration, it will delete the useless alternatives (specified by the instead_ofclause) and rename the adequate one (clause select) using the given alias (clause as).

Variable Realization

As we’ve seen CAMP let you define variables whose value may vary from one configuration to another. For instance, in our Awesome example, we found the following definition:

components:
   tests:
      provide_services: [ Tests ]
      require_services: [ Awesome ]
      variables:
         threads:
            values:
               range: [10, 50] # in GB
               coverage: 40

This defines a variable named threads whose value ranges from 10 to 50. See the camp generate documentation for more details.

Text Substitution

In order to realize the tests component, we must tell CAMP where it must place the values of this threads variable. In the following example, we ask CAMP to substitute the threads value in the Dockerfile.

components:
   tests:
      provide_services: [ Tests ]
      require_services: [ Awesome ]
      variables:
         threads:
            values:
               range: [10, 50] # in GB
               coverage: 40
            realization:
               - targets: [ repor/tests/Dockerfile ]
                 pattern: MAX_THREADS=4
                 replacements: [ "MAX_THREADS={value}" ]

Note that the realization entry may lists several substitutions (though only one is given here). Each substitution will be carried out on every file listed in the targets entry. Each time, CAMP will replace the given pattern with the given replacement, where {value} stands for the actual value. For instance, if CAMP assigns 10 to the threads variable, the Dockerfile will eventually contains the text “MAX_THREAD=10”.


Note Substitutions may target any files, including the docker-compose file, or other configurations files.


Resource Selection

We can also select specific files from the template, depending on the value that CAMP sets in the current configuration. Consider the following example:

    variables:
      front_end:
        values: [ apache, nginx ]
        realization:
         - select:
            - apache-docker-compose.yml
            - nginx-docker-compose.yml
           as: docker-compose.yml

When CAMP builds a configuration where the variable front-end is set to apache, CAMP will include the file apache-docker-compose.yml but remove the file nginx-docker-compose.yml from the generated configuration (but not from the template, which never changes). Conversely, in a configuration where the variable front-end is set to nginx, CAMP would keep the file nginx-docker-compose.yml and delete apache-docker-compose.yml. In any case, the selected file will be renamed as docker-compose.yml in the configuration files that CAMP generates.

Note that the as clause is optional and amy be omitted if there is no need to rename the selected resource.