Skip to content

Commit f957eb7

Browse files
committed
Merge branch 'develop', prepare 3.0
2 parents 4efaaf8 + 76d033c commit f957eb7

20 files changed

+1304
-309
lines changed

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<img alt="Exoframe" src="./logo/svg/exo_blue.svg" width="300">
1+
<img alt="Exoframe" src="./logo/png/exo_blue.png" width="300">
22

33
> Simple Docker deployment tool
44
@@ -22,6 +22,8 @@ Exoframe is a self-hosted tool that allows simple one-command deployments using
2222
* Multiple deployment endpoints and multi-user support
2323
* Simple update procedure for client, server and Traefik
2424
* Optional automatic subdomain assignment (i.e. every deployment gets its own subdomain)
25+
* Swarm mode deployments
26+
* Complex recipes support (i.e. deploy complex systems in one command)
2527

2628
\* Feature provided by [Traefik](https://traefik.io/)
2729

@@ -64,10 +66,11 @@ You can find a list of all commands and options in the [docs](./docs/README.md).
6466

6567
* [Basics](docs/Basics.md)
6668
* [FAQ](docs/FAQ.md)
67-
* [Contrubiton Guideline](docs/Contributing.md)
69+
* [Contribution Guideline](docs/Contributing.md)
6870
* [Templates guide](docs/TemplatesGuide.md)
71+
* [Recipes guide](docs/RecipesGuide.md)
6972
* [Using nightly versions](docs/Nightly.md)
70-
* [Articles, video and related links](docs/Links.md)
73+
* [Tutorials, articles, video and related links](docs/Links.md)
7174
* [Change Log](CHANGELOG.md)
7275

7376
## Special thanks

docs/Basics.md

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,26 @@ It is recommended to run Exoframe on a server with at least 1GB of RAM.
2323

2424
By default, Exoframe understands and can deploy the following project types:
2525

26-
1. Static html based projects - will be deployed using [nginx](http://hub.docker.com/_/nginx) image
27-
2. Node.js based projects - will be deployed using [node:latest](https://hub.docker.com/_/node) image \*
28-
3. Docker based project - will be deployed using your [Dockerfile](https://docs.docker.com/engine/reference/builder/)
29-
4. Docker-Compose based projects - will be deployed using your [docker-compose](https://docs.docker.com/compose/compose-file/) file
26+
1. Static html based projects - will be deployed using [nginx](http://hub.docker.com/_/nginx) image
27+
2. Node.js based projects - will be deployed using [node:latest](https://hub.docker.com/_/node) image \*
28+
3. Docker based project - will be deployed using your [Dockerfile](https://docs.docker.com/engine/reference/builder/)
29+
4. Docker-Compose based projects - will be deployed using your [docker-compose](https://docs.docker.com/compose/compose-file/) file
3030

3131
\* There are two things to keep in mind for Node.js projects: (1) they are started via `npm start`, so make sure you have specified start script in your `package.json`; (2) by default port 80 is exposed, so you need to make your app listen on that port. If you'd like to execute your app in any different way or expose more ports - please use Dockerfile deployment method.
3232

3333
This list can be extended via deployment templates.
3434
You can find the list of available templates [on npm](https://www.npmjs.com/search?q=exoframe-template).
3535
Templates can be installed by executing `exoframe template` command and entering complete template package name.
3636

37+
## Complex recipes
38+
39+
Exoframe also provides support for third-party complex deployment recipes.
40+
They allow to quickly and easily deploy complex projects.
41+
42+
For example, you can deploy Wordpress with PHPMyAdmin by simply executing `exoframe setup` and entering [exoframe-recipe-wordpress](https://github.com/exoframejs/exoframe-recipe-wordpress) as desired recipe.
43+
44+
You can find the list of available recipes [on npm](https://www.npmjs.com/search?q=exoframe-recipe).
45+
3746
## Commands
3847

3948
| Command | Description |
@@ -44,6 +53,7 @@ Templates can be installed by executing `exoframe template` command and entering
4453
| rm <id> | Remove existing deployment or project |
4554
| log <id> | Get logs for existing deployment or project |
4655
| template [ls, rm] | Add, list or remove deployment templates from the server |
56+
| setup | Setup a complex recipe deployment |
4757
| token [ls, rm] | Generate, list or remove deployment tokens |
4858
| login | Login into Exoframe server |
4959
| endpoint [url] | Selects or adds the endpoint of Exoframe server |
@@ -54,7 +64,7 @@ Templates can be installed by executing `exoframe template` command and entering
5464
## Project config file
5565

5666
All of the configuration for the deployed projects is done using `exoframe.json` config file.
57-
It can either be generated/updated using `exoframe config` command or created manually.
67+
It can either be generated/updated using `exoframe config` (or `exoframe init`) command or created manually.
5868
If it doesn't exist during deployment, Exoframe will generate simple config file that only contains name of the current project.
5969

6070
Config file has the following structure:
@@ -120,18 +130,18 @@ endpoint: 'http://localhost:8080' # your endpoint URL, defaults to localhost
120130
Sometimes you might need to deploy things from environments that don't have your private key (e.g. CI/CD services).
121131
For this cases you can use deployment tokens. Here's how it works:
122132
123-
1. Make sure you are logged in to your Exoframe server
124-
2. Generate new deployment token using `exoframe token` command
125-
3. Use the new token to deploy your service without need to authenticate: `exoframe deploy -t $TOKEN`
133+
1. Make sure you are logged in to your Exoframe server
134+
2. Generate new deployment token using `exoframe token` command
135+
3. Use the new token to deploy your service without need to authenticate: `exoframe deploy -t $TOKEN`
126136

127137
## Updating deployed project
128138

129139
Exoframe provides a way to easily update already deployed projects.
130140
This can be done by passing `--update` (or `-u`) flag to deploy command.
131141
The way it works is quite simple:
132142

133-
1. Exoframe deploys new version of the given project
134-
2. Exoframe then waits for them to start up
135-
3. Exoframe removes the old running deployments for current project
143+
1. Exoframe deploys new version of the given project
144+
2. Exoframe then waits for them to start up
145+
3. Exoframe removes the old running deployments for current project
136146

137147
This can be used together with deployment tokens to achieve simple continuous deployment for your projects.

docs/Links.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
# Articles, video and related links
1+
# Tutorials, articles, video and related links
2+
3+
## Tutorials
4+
5+
* [Tutorial: Deploy to AWS-based Swarm cluster with Exoframe](./TutorialSwarmAWS.md)
26

37
## Articles
48

docs/Nightly.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ First, you need to modify server config and add the following line:
2020
updateChannel: nightly
2121
```
2222
23-
Then, you need to run `exoframe server update` against the endpoint you've configured to update to latest nightly build of server.
23+
Then, you need to run `exoframe update server` against the endpoint you've configured to update to latest nightly build of server.

docs/RecipesGuide.md

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
# Templates guide
2+
3+
Exoframe allows doing complex deployments using third party recipes.
4+
This guide aims to explain basics you need to know to create your own recipes.
5+
If you are looking for recipe usage - please see [Basics](Basics.md) part of the docs.
6+
7+
## Basics
8+
9+
Exoframe uses [yarn](https://yarnpkg.com/) to install third-party recipes.
10+
The recipes then are executed on Exoframe server using Node.js `require()` method.
11+
So, make sure that your template's `package.json` has correct `main` attribute.
12+
13+
Your template main script needs to export the following variables and methods:
14+
15+
```js
16+
// function to get list of questions that should be presented
17+
// to user. uses Inquirer.js question format
18+
exports.getQuestions = async () => [];
19+
20+
// function to execute current recipe with user answers
21+
exports.runSetup = async props => {};
22+
```
23+
24+
## Recipe props
25+
26+
During the execution `runSetup` will get the properties object from Exoframe server.
27+
This object contains all data and methods required to build and execute new docker containers.
28+
Here's a snippet from the Exoframe server code that shows the props object being assembled:
29+
30+
```js
31+
// generate recipe props
32+
const recipeProps = {
33+
// user answers
34+
answers,
35+
// server config
36+
serverConfig,
37+
// current user username
38+
username,
39+
// temp dir that contains the project
40+
tempDockerDir,
41+
// docker-related things
42+
docker: {
43+
// docker daemon, instance of dockerode
44+
daemon: docker,
45+
// exoframe build function
46+
// has following signature: async ({username, resultStream}) => {}
47+
// executes `docker build` in project temp dir
48+
// returns following object: {log, image}
49+
build,
50+
// exoframe start function
51+
// has the following signature: async ({image, username, resultStream}) => {}
52+
// executes `docker start` with given image while setting all required labels, env vars, etc
53+
// returns inspect info from started container
54+
start,
55+
// exoframe startFromParams function
56+
// has the following signature:
57+
// async ({
58+
// image,
59+
// deploymentName,
60+
// projectName,
61+
// username,
62+
// backendName,
63+
// frontend,
64+
// hostname,
65+
// restartPolicy,
66+
// Env = [],
67+
// additionalLabels = {},
68+
// Mounts = [],
69+
// }) => {}
70+
// executes `docker start` with given image while setting all required labels, env vars, etc
71+
// returns inspect info from started container
72+
startFromParams,
73+
// exoframe image pulling function
74+
// has the following signature: async (tag) => {}
75+
// returns pull log on success
76+
pullImage,
77+
// exoframe network get function
78+
// returns currently used exoframe network
79+
getNetwork,
80+
},
81+
// exoframe utilities & logger
82+
// see code here: https://github.com/exoframejs/exoframe-server/blob/master/src/util/index.js
83+
util: Object.assign({}, util, {
84+
logger,
85+
}),
86+
};
87+
```
88+
89+
## Executing the recipe
90+
91+
Once user has answered all your questions, you will need to execute the recipe.
92+
It is up to you to build _and_ start all required docker images.
93+
Here's an example for the Wordpress recipe:
94+
95+
```js
96+
// image names
97+
const mysqlImage = 'mariadb:latest';
98+
const wordpressImage = 'wordpress:latest';
99+
const phpmyadminImage = 'phpmyadmin/phpmyadmin:latest';
100+
101+
// ask user to provide params for deployment
102+
exports.getQuestions = () => [
103+
{
104+
type: 'input',
105+
name: 'projectName',
106+
message: 'Wordpress project name:',
107+
},
108+
{
109+
type: 'input',
110+
name: 'mysqlPassword',
111+
message: 'MySQL root password:',
112+
},
113+
{
114+
type: 'input',
115+
name: 'wordpressDomain',
116+
message: 'Domain for Wordpress:',
117+
},
118+
{
119+
type: 'confirm',
120+
name: 'phpmyadminStart',
121+
message: 'Also start PHPMyAdmin?',
122+
},
123+
{
124+
type: 'input',
125+
name: 'phpmyadminDomain',
126+
message: 'Domain for PHPMyAdmin:',
127+
},
128+
];
129+
130+
// start mysql
131+
const startMysql = async ({util, answers, username, docker}) => {
132+
// generate new deployment name (optional, in this case we do this to have the same hostname)
133+
const deploymentName = util.nameFromImage(mysqlImage);
134+
// start new docker container / service
135+
return docker.startFromParams({
136+
// provide image name
137+
image: mysqlImage,
138+
// use project name from user answers
139+
projectName: answers.projectName,
140+
// pass in username
141+
username,
142+
// apply deployment name
143+
deploymentName,
144+
// also use deployment name for hostname
145+
hostname: deploymentName,
146+
// set restart policy
147+
restartPolicy: 'always',
148+
// create volume to persist data
149+
Mounts: [
150+
{
151+
Type: 'volume',
152+
Source: `${answers.projectName}-mysqldata`,
153+
Target: '/var/lib/mysql',
154+
},
155+
],
156+
// pass in env vars config
157+
Env: [`MYSQL_ROOT_PASSWORD=${answers.mysqlPassword}`],
158+
});
159+
};
160+
161+
// start wordpress
162+
const startWordpress = async ({util, answers, serverConfig, username, docker, mysql}) => {
163+
// generate new deployment name (optional)
164+
const deploymentName = util.nameFromImage(wordpressImage);
165+
166+
// get mysql hostname from inspect object
167+
const mysqlHost = serverConfig.swarm
168+
? mysql.Spec.Networks[0].Aliases
169+
: mysql.NetworkSettings.Networks.exoframe.Aliases[0];
170+
171+
// start new container / service
172+
return docker.startFromParams({
173+
// set image name
174+
image: wordpressImage,
175+
// set project name
176+
projectName: answers.projectName,
177+
// set username
178+
username,
179+
// set deployment name (can be omitted)
180+
deploymentName,
181+
// set frontend string for Traefik since we want
182+
// it to be accessible from web
183+
frontend: `Host:${answers.wordpressDomain}`,
184+
// set restart policy
185+
restartPolicy: 'always',
186+
// set env vars config
187+
Env: [`WORDPRESS_DB_HOST=${mysqlHost}`, `WORDPRESS_DB_PASSWORD=${answers.mysqlPassword}`],
188+
});
189+
};
190+
191+
// start phpmyadmin
192+
const startPhpmyadmin = async ({util, answers, serverConfig, username, docker, mysql}) => {
193+
// generate new deployment name (optional)
194+
const deploymentName = util.nameFromImage(phpmyadminImage);
195+
196+
// get mysql hostname from inspect object
197+
const mysqlHost = serverConfig.swarm
198+
? mysql.Spec.Networks[0].Aliases
199+
: mysql.NetworkSettings.Networks.exoframe.Aliases[0];
200+
201+
// start container / service
202+
return docker.startFromParams({
203+
// set image name
204+
image: phpmyadminImage,
205+
// set project name from user answers
206+
projectName: answers.projectName,
207+
// set username
208+
username,
209+
// set deployment name (can be omitted)
210+
deploymentName,
211+
// set frontend rule for Traefik since we want
212+
// it to be accessible from web
213+
frontend: `Host:${answers.phpmyadminDomain}`,
214+
// set restart policy
215+
restartPolicy: 'always',
216+
// set env vars config
217+
Env: [`PMA_HOST=${mysqlHost}`, `MYSQL_ROOT_PASSWORD=${answers.mysqlPassword}`],
218+
});
219+
};
220+
221+
exports.runSetup = async ({answers, serverConfig, username, docker, util}) => {
222+
// init log
223+
const log = [];
224+
225+
try {
226+
util.logger.debug('starting work..');
227+
// start mysql container
228+
util.logger.debug('starting mysql..');
229+
const mysql = await startMysql({util, answers, username, docker});
230+
log.push({message: 'Mysql container started', data: mysql, level: 'info'});
231+
util.logger.debug('created mysql container..');
232+
233+
// start wordpress container
234+
util.logger.debug('starting wordpress..');
235+
const wordpress = await startWordpress({util, answers, serverConfig, username, docker, mysql});
236+
log.push({message: 'Wordpress container started', data: wordpress, level: 'info'});
237+
util.logger.debug('created wordpress container..');
238+
239+
// start phpmyadmin if needed
240+
if (answers.phpmyadminStart) {
241+
util.logger.debug('starting phpmyadmin..');
242+
const phpmyadmin = await startPhpmyadmin({util, answers, serverConfig, username, docker, mysql});
243+
log.push({message: 'PHPMyAdmin container started', data: phpmyadmin, level: 'info'});
244+
util.logger.debug('created phpmyadmin container..');
245+
}
246+
} catch (e) {
247+
util.logger.error('error:', e);
248+
log.push({message: e.toString(), data: e, level: 'error'});
249+
}
250+
251+
// return log to user
252+
return log;
253+
};
254+
```
255+
256+
## Examples
257+
258+
* [Wordpress recipe](https://github.com/exoframejs/exoframe-recipe-wordpress) (incl. Wordpress, MariaDB and PHPMyAdmin)

0 commit comments

Comments
 (0)