Skip to content

Docker Wrapper Image

gx1 edited this page Dec 31, 2020 · 3 revisions

This guide explain the docker wrapper image convention, and gives useful tricks to create new Docker Image Wrapper images.

Note: in order to create Docker Image Wrapper images you should know how Docker works (You know what is a Dockerfile ? How to build a new image? ) If you don't have these prerequisites please read the docker documentation first:

https://docs.docker.com/develop/develop-images/baseimages/

There are two problems that lead to create the Docker Wrapper Image standard.

During lab creation the user creates new containers. Each container has an associated image. This image can offer a lot of actions, for example to create new users, to start a sql server or a web server. These actions can be executed after that the container starts, furthermore these action are parametric: if the user want to create two different users he want to use the “create user” action with different input arguments. An option to resolve this problem would be to create custom images for each situation (for example an image with a user jack installed in it, an image for a user george installed in it, etc.) . This is not an efficient solution because, in spite of Docker Image dimension is very light, for a little change we spend many megabytes in order to create a new image. In order to resolve this problem Docker Wrapper Image is used.

The second problem is that when the user creates the network and choices the images for the container he have to know informations about the selected image, the actions and port services that the image exposes (a web server for example exposes the port 80, an ftp server the port 21, etc.).

Docker image creation is done by writing a Dockerfile. In Dockerfile you can set all the operation that will be executed when the container will run, as also all the software that have to be install by default in docker container.

Dockerfile has a well-defined syntax with many options (look at the Docker documentation for more informations). There is a great feature called label. In your Dockerfile you can define labels that are metadata that can be useful to describe the image. Docker Wrapper Image defines a standard notation for the labels, so DSP application can write Docker image labels, understand and parse them and forward them to user. A label is composed by a pair key-value. An example of a Dockerfile that uses Docker Image Wrapper notation is the following:

In this Dockerfile there is the image’s definition on the top, first instructions update the apk repository and download some apk packages.

Then some scripts are loaded in the image: how can DSP expose these scripts to user during image selection in graphic compose-file creation? Labels are used (see actions label below in the image).

Lastly labels are use with Docker Wrapper Image notation, so DSP application can read and interprete them:

       actions.adduser.command="/adduser.sh" \ 
       actions.adduser.description="Add a new username : <name> <password> " \ 
       actions.adduser.args.name.val="user" \
       actions.adduser.args.name.type="text" \
       actions.adduser.args.name.rule.pattern="^[a-zA-Z0-9_-]+$" \
       actions.adduser.args.password.val="password" \
       actions.adduser.args.password.type="text" \
       actions.adduser.args.password.rule.pattern="^[a-zA-Z0-9_-]+$" \
  • type of image: by using this label DSP can understand which type of image is used by a container, so when you draw it a different icon will be showed ( if a server has been selected a server icon will be showed, if a router has been selected a router icon will be showed, etc.)

  • caps_add: when the docker-compose file is written, some containers requires special privileges in order to use the loaded scripts. So Docker Wrapper Image reads the image label and understand that when the user has chosen an image for a container, this container has these capabilities.

  • actions: actions is the most important used label in Docker Wrapper Image notation: with this label you can expose the loaded scripts in the image, so when the user select an image for a container these actions are selectable by user. In this example four actions are loaded (exec, adduser, addctf, setgw). Docker Wrapper Image uses a hierarchical and dotted-separated convention for the actions : each action has a name that is defined in the label name after the first dot. Then arguments’ action are defined: an action is associated to a particular command (for example exec label is associated to exec.sh command), has a description and command-line argumetns. “args” is another “root label” that has “child labels”: each argument has a name that is defined after the dot present after the “args” label, and for each argument there is a description, a default value, a type, a rule that is interpreted by DSP in order to do input checking

Script template and example of actions

When you load scripts you have to follow the Docker Wrapper Image convention: a script can accept different arguments, DWI need that arguments are sent with the getops with long argument syntax.

An example can help reader to understand: let’s suppose you want to add a useradd.sh script inside the image that will be exposed by labels to DSP application; this script add a new user to the container. It has two arguments:

  • username: the name of user to add;

  • user password: the password of added user.

Let’s suppose that you want to add a user named “jack” with “password123” password.

During the network creation, after that you’ve created a container with the image that has adduser action, when you select the action adduser, DSP provides you two argument input: a “name” and a “password” argument input. You set these arguments then load the action with “jack” and “password123” values. The executed action will be the following:

bash sh -c "adduser.sh --name "jack" --password "password123"

As you can see the argument name input in GUI interface becomes the argument name input of the command line, the values of argument input in the GUI interface become the values of arguments in command line. By looking at the action labels:

you can see that the script name is the value of the “command” label, the argument names are specific named labels under the “args” label (in this case args.name and args.password). The val is the loaded value through the GUI interface.

An example of “assuder.sh” script that follows this convention is the following:

 #!/bin/bash
# Author:       giper
# Email:        g.per45@gmail.com
# Date:        	 
# Usage:        adduser.sh [--name=val] [ --password=val]
# Description:


usage() {
echo $1
echo "Usage adduser.sh [--name val] [ --password val]"
exit 1
}
NAME_BOOL=false
PASSWORD_BOOL=false
function assertNoSpaces {
    if [[ "$1" != "${1/ /}" ]]
    then
	usage 'Parameter cannot have spaces';
    fi
}



optspec=":-:"
while getopts "$optspec" optchar; do
  case "${optchar}" in
  -)
    case "${OPTARG}" in
    name)
    NAME="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
    [[ ! -z "${NAME// }" ]] && NAME_BOOL=true || usage 'Empty Name'
    ;;
    password)
    PASSWORD="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
    [[ ! -z "${PASSWORD// }" ]] && PASSWORD_BOOL=true || usage 'Empty Password'
    ;;
    *)
    usage
    ;;
    esac;;
  *)
    usage
    ;;
  esac
done

assertNoSpaces "$NAME"
assertNoSpaces "$PASSWORD"

if [ $NAME_BOOL != true ] ; then
	usage 'no name';
fi
if [ $PASSWORD_BOOL != true ] ; then
	usage 'no password';
fi

adduser -D $NAME
echo $NAME:$PASSWORD | chpasswd