这是indexloc提供的服务,不要输入任何密码
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
2c03567
Add an 'add-ons' plugin, which provides heroku style add-ons.
plietar Nov 4, 2013
41a02fa
Add an ADDONS.md file to describe how to use the plugin.
plietar Nov 5, 2013
31f39d8
Fix indentation, refactor app/addon checking and add add-on private d…
plietar Nov 5, 2013
a18ad34
Add a help message to the add-on plugin.
plietar Nov 5, 2013
2ba1286
Sort add-ons list and remove trailing whitespaces in pre-release.
plietar Nov 5, 2013
85fed4b
Unprovision all add-ons in 'pre-delete'.
plietar Nov 5, 2013
75ed755
Fix a bug where the add-ons plugin would delete all ENV file.
plietar Nov 5, 2013
f47c91c
Add enable/disable to the add-on plugin.
plietar Nov 5, 2013
058a556
export ADDON_ROOT in the pre-release hook
plietar Nov 6, 2013
60f5756
Refactorize a lot of code, and rename ADDON_ROOT to ADDON_DATA.
plietar Nov 7, 2013
e6b58b7
Replace the semicolon by a colon in the ADDONS file.
plietar Nov 15, 2013
95f95b6
Add a few comments in addons-common.
plietar Nov 25, 2013
b7b3fad
Move and split the ADDONS.md file.
plietar Nov 25, 2013
d1cd219
Add the addons directory to gitignore.
plietar Nov 26, 2013
20c55c8
Fail when disabling an add-on which is still in use.
plietar Nov 27, 2013
418a595
Add a bit of output.
plietar Nov 27, 2013
de96305
Don't fail when trying to renable or reprovision.
plietar Nov 29, 2013
a892fe7
Restart the application once we've added/removed an addon.
plietar Nov 29, 2013
c65e342
Make test_deploy more flexible.
plietar Nov 29, 2013
914d4d1
Integrate add ons with the backup plugin.
plietar Dec 1, 2013
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.vagrant
.DS_Store
stack.tgz
/addons
50 changes: 50 additions & 0 deletions docs/using-addons.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Addons
Addons provide services. They are similar to heroku's addons.

Heroku provides a good description of how to use and manage addons.
Most commands are similar in dokku.

https://devcenter.heroku.com/articles/managing-add-ons

## Usage
In the following examples, `myapp` is the name of an application, `mariadb` is the addon's name.

First, you should list all available addons :

dokku addons -a

An addon marked as disabled may not be used by applications. You must enable it first :

dokku addons:enable mariadb

Depending on the addon, you might need to type your password into sudo.

Once the addon is enabled, add it to your application :

dokku addons:add myapp mariadb

Congratulations, you can now use a mariadb database in your application.
The url to which your application should connect is located in the `DOKKU_MARIADB_URL`.
You can visualize this url by running the `addons:url` command.

dokku addons:url myapp mariadb

The contents and format of the url is addon-specific.
Note that the indicated host is from the application's container point of view.
It may not be accessible from outside.
In the case of the mariadb addon, it has the following format :

mysql://USER@PASSWORD/HOST:PORT/DATABASE


You can remove an addon from your application :

dokku addons:remove myapp mariadb

WARNING: This removes all data (such as a database) associated with this application.
Use with care.

If none of your applications uses an addon, you can disable it :

dokku addons:disable mariadb

30 changes: 30 additions & 0 deletions docs/writing-addons.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Add-on development
Add-ons are located in `/var/lib/dokku/add-ons/`. Every subfolder is an add-on.
When they provision an app, add-ons should generate a unique ID, and a "private" value.
The ID is used to identify the app within the add-on. This would typically be a username or database name.
The "private" value is used to generate the URL. This would be a password for example.

IDs and private values can take any value. They however must not include colons, as it is used internally by the plugin.

Add-ons must provide a few executable files :
* `enable` This script takes no arguments. It should setup everything the add-on needs, eg install dependencies, create docker containers, ...
* `disable` This script takes no arguments. It should clean up all the data used by the add-on, eg destroy docker containers. All applications have already been unprovisonised before this script is called.
* `provision` This script takes one argument, the app name. It should only be used to generate understandable IDs, which contain the app name. Add-ons shouldn't use it the access the app's config files, ...
The script should output the generated id and private value on stdout, separated by a colon.
* `unprovision` This script takes one argument, the ID. It should destroy all resources associated with this ID.
* `url` This script takes two arguments, the ID and the private value. It should output the url on stdout.

Apart from `$ADDON/enable` all scripts are run only with the add-on in enabled state.
Add-on's are free to run the service in the way they like. They can run it on the cloud, on the host, on a single docker container or on a container per provisioned app. The plugin doesn't care, as long as the add-on provides a URL which is accessible.
Because URLs might change (docker can assign different IPs/ports after reboot), the `url` script is called each time the app is released.

## Guidelines
### Add-ons internal files
Add-ons may want to store files for their own internal use, for eg. database storage.
Before any add-on script is ran, a `$ADDON_DATA` environment variable is exported, which contains the path to a directory where the add-on can safely save any file. The directory is guaranteed to exist before add-on scripts are ran.
Currently it is set to `$DOKKU_ROOT/.add-ons/$ADDON` where `$ADDON` is the add-on's name. However, this might change, therefore add-ons should use the `$ADDON_DATA` variable rather than hardcoding it.

The add-on plugin automatically creates and removes a `$ADDON_DATA/enabled` file to keep track of which plugin is installed. Add-ons mustn't create, modify nor delete this file themselves.
It is created after the `$ADDON/enable` script completes, and deleted after the `$ADDON/disabled` script completes.

Moreover, addons may contain extra immutable data files. The `$ADDON_ROOT` variable contains the addon's installation path. This would typically be `/var/lib/dokku/addons/$ADDONS`
128 changes: 128 additions & 0 deletions plugins/addons/addons-common.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@

ADDONS_PATH=/var/lib/dokku/addons

function die
{
$QUIET || echo $* >&2
exit 1
}

function quit
{
$QUIET || echo $* >&2
exit 0
}

function export_addon_vars()
{
export ADDON="$1"
export ADDON_DATA="$DOKKU_ROOT/.addons/$ADDON"
export ADDON_ROOT="$ADDONS_PATH/$ADDON"
}

# Check if an app exists.
# If it does export APP, ADDONS_FILE and ENV_FILE
function check_app
{
if [[ -z $1 ]]; then
die "You must specify an app name"
else
APP="$1"
ADDONS_FILE="$DOKKU_ROOT/$APP/ADDONS"
ENV_FILE="$DOKKU_ROOT/$APP/ENV"

# Check if app exists with the same name
if [ ! -d "$DOKKU_ROOT/$APP" ]; then
die "App $APP does not exist"
fi

[ -f $ADDONS_FILE ] || {
$QUIET || echo "-----> Creating $ADDONS_FILE"
touch $ADDONS_FILE
}
[ -f $ENV_FILE ] || {
$QUIET || echo "-----> Creating $ENV_FILE"
touch $ENV_FILE
}
fi
}

# Check if an addon exists.
# if it does, export ADDON, ADDON_DATA and ADDON_ROOT
function check_addon
{
if [[ -z $1 ]]; then
die "You must specify an addon name"
elif [ ! -d "$ADDONS_PATH/$1" ]; then
die "Addon $1 does not exist"
fi
export_addon_vars $1

mkdir -p $ADDON_DATA
}

# Check if an addon exists and is enabled.
# See check_addon for exported variables
function check_addon_enabled
{
check_addon $1
if [ ! -f $ADDON_DATA/enabled ]; then
die "Add-on $ADDON is not enabled"
fi
}

# Check if an addon exists and is disabled.
# See check_addon for exported variables
function check_addon_disabled
{
check_addon $1
if [ -f $ADDON_DATA/enabled ]; then
quit "Add-on $ADDON is already enabled"
fi
}

# Make sure the addon is provisioned.
# check_app and check_addon must be called first.
# Exports ADDON_ID and ADDON_PRIVATE
function check_addon_provisioned
{
local line
line=$(grep "^$ADDON:" $ADDONS_FILE) || {
die "App $APP does not have addon $ADDON"
}

split_addon_line $line _ ADDON_ID ADDON_PRIVATE
}

# Make sure the addon is not provisioned.
# check_app and check_addon must be called first.
function check_addon_unprovisioned
{
if grep -q "^$ADDON:" $ADDONS_FILE; then
quit "App $APP already has addon $ADDON"
fi
}

function split_addon_line
{
parts=($(echo $1 | sed 's/:/ /g'))
if [[ ! -z $2 && $2 != "_" ]]; then
eval "$2=${parts[0]}"
fi
if [[ ! -z $3 && $3 != "_" ]]; then
eval "$3=${parts[1]}"
fi
if [[ ! -z $4 && $4 != "_" ]]; then
eval "$4=${parts[2]}"
fi
}

function restart_app {
IMAGE="app/$APP"

echo "-----> Releasing $APP ..."
dokku release $APP $IMAGE
echo "-----> Deploying $APP ..."
dokku deploy $APP $IMAGE
}

21 changes: 21 additions & 0 deletions plugins/addons/backup-check
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
VERSION="$1"
IMPORT_DIR="$2"
TARGET_DIR="$3"

QUIET=true
source $(dirname ${BASH_SOURCE[0]})/addons-common.sh

[[ -f $IMPORT_DIR/.dokku_addons ]] || exit 0

ret=0
while read ADDON; do
if [[ ! -d $ADDONS_PATH/$ADDON ]]; then
ret=1
echo "Addon $ADDON missing" 2>&1
fi
done < $IMPORT_DIR/.dokku_addons

exit $ret

38 changes: 38 additions & 0 deletions plugins/addons/backup-export
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
VERSION="$1"
BASE_DIR="$2"
TMP_DIR="$3"

QUIET=true
source $(dirname ${BASH_SOURCE[0]})/addons-common.sh

cat;

for addon in $BASE_DIR/.addons/*; do
if [[ -f $addon/enabled ]]; then
echo $(basename $addon)
fi
done > "$TMP_DIR/.dokku_addons"

echo .dokku_addons

for appdir in $BASE_DIR/*; do
APP=$(basename $appdir)
if [[ -f $appdir/ADDONS ]]; then
echo $appdir/ADDONS

while read line; do
split_addon_line $line ADDON ADDON_ID ADDON_PRIVATE
export_addon_vars $ADDON

mkdir -p "$TMP_DIR/.dokku_addons_dump/$ADDON"

if [[ -x $ADDON_ROOT/dump ]]; then
$ADDON_ROOT/dump $ADDON_ID $ADDON_PRIVATE | gzip > $TMP_DIR/.dokku_addons_dump/$ADDON/${APP}.gz
echo .dokku_addons_dump/$ADDON/${APP}.gz
fi
done < $appdir/ADDONS
fi
done

49 changes: 49 additions & 0 deletions plugins/addons/backup-import
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
VERSION="$1"
IMPORT_DIR="$2"
TARGET_DIR="$3"

BACKUP_TMP_DIR="$2/../../" # TODO: Make this less hacky

QUIET=true
source $(dirname ${BASH_SOURCE[0]})/addons-common.sh

if [[ ! -f $BACKUP_TMP_DIR/.dokku_addons ]]; then
exit 0
fi

while read ADDON; do
mkdir -p $DOKKU_ROOT/.addons/$ADDON
export_addon_vars $ADDON

if [[ ! -f $ADDON_DATA/enabled ]]; then
$ADDON_ROOT/enable
touch $ADDON_DATA/enabled
fi
done < $BACKUP_TMP_DIR/.dokku_addons

sleep 5 # Wait for freshly enabled addons to start

for appdir in $IMPORT_DIR/*; do
APP=$(basename $appdir)

if [[ -f $appdir/ADDONS ]]; then
while read line; do
split_addon_line $line ADDON ADDON_ID ADDON_PRIVATE
export_addon_vars $ADDON

id_private=$($ADDON_ROOT/provision $APP)
line="$ADDON:$id_private"
echo $line >> $TARGET_DIR/$APP/ADDONS
split_addon_line $line ADDON ADDON_ID ADDON_PRIVATE

dump=$BACKUP_TMP_DIR/.dokku_addons_dump/$ADDON/${APP}.gz

if [[ -f $dump && -x $ADDON_ROOT/restore ]]; then
zcat $dump | $ADDON_ROOT/restore $ADDON_ID $ADDON_PRIVATE
fi
done < $appdir/ADDONS
fi
done

Loading