This commit is contained in:
Girish Ramakrishnan
2016-08-17 10:59:09 -07:00
parent 2f7fa54fc8
commit 56618cab23
29 changed files with 4439 additions and 6 deletions

621
docs/tutorials/node.md Normal file
View File

@@ -0,0 +1,621 @@
# Overview
This tutorial provides an introduction to developing applications
for the Cloudron using node.js.
# Installation
## Install CLI tool
The Cloudron CLI tool allows you to install, configure and test apps on your Cloudron.
Installing the CLI tool requires [node.js](https://nodejs.org/) and
[npm](https://www.npmjs.com/). You can then install the CLI tool using the following
command:
```
sudo npm install -g cloudron
```
Note: Depending on your setup, you can run the above command without `sudo`.
## Testing your installation
The `cloudron` command should now be available in your path.
Let's login to the Cloudron as follows:
```
$ cloudron login
Cloudron Hostname: craft.selfhost.io
Enter credentials for craft.selfhost.io:
Username: girish
Password:
Login successful.
```
## Your First Application
Creating an application for Cloudron can be summarized as follows:
1. Create a web application using any language/framework. This web application must run a HTTP server
and can optionally provide other services using custom protocols (like git, ssh, TCP etc).
2. Create a [Dockerfile](http://docs.docker.com/engine/reference/builder/) that specifies how to create
an application ```image```. An ```image``` is essentially a bundle of the application source code
and it's dependencies.
3. Create a [CloudronManifest.json](/references/manifest.html) file that provides essential information
about the app. This includes information required for the Cloudron Store like title, version, icon and
runtime requirements like `addons`.
## Simple Web application
To keep things simple, we will start by deploying a trivial node.js server running on port 8000.
Create a new project folder `tutorial/` and add a file named `tutorial/server.js` with the following content:
```javascript
var http = require("http");
var server = http.createServer(function (request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.end("Hello World\n");
});
server.listen(8000);
console.log("Server running at port 8000");
```
## Dockerfile
A Dockerfile contains commands to assemble an image.
Create a file named `tutorial/Dockerfile` with the following content:
```dockerfile
FROM cloudron/base:0.8.1
ADD server.js /app/code/server.js
CMD [ "/usr/local/node-0.12.7/bin/node", "/app/code/server.js" ]
```
The `FROM` command specifies that we want to start off with Cloudron's [base image](/references/baseimage.html).
All Cloudron apps **must** start from this base image.
The `ADD` command copies the source code of the app into the directory `/app/code`.
While this example only copies a single file, the ADD command can be used to copy directory trees as well.
See the [Dockerfile](https://docs.docker.com/reference/builder/#add) documentation for more details.
The `CMD` command specifies how to run the server. There are multiple versions of node available under `/usr/local`. We
choose node v0.12.7 for our app.
## CloudronManifest.json
The `CloudronManifest.json` specifies
* Information about displaying the app on the Cloudron Store. For example,
the title, author information, description etc
* Information for installing the app on the Cloudron. This includes fields
like httpPort, tcpPorts.
Create the CloudronManifest.json using the following command:
```
$ cloudron init
id: io.cloudron.tutorial # unique id for this app. use reverse domain name convention
author: John Doe # developer or company name of the for user <email>
title: Tutorial App # Cloudron Store title of this app
description: App that uses node.js # A string or local file reference like file://DESCRIPTION.md
tagline: Changing the world one app at a time # A tag line for this app for the Cloudron Store
website: https://cloudron.io # A link to this app's website
contactEmail: support@cloudron.io # Contact email of developer or company
httPort: 8000 # The http port on which this application listens to
```
The above command creates a CloudronManifest.json:
File ```tutorial/CloudronManifest.json```
```json
{
"id": "io.cloudron.tutorial",
"author": "John Doe",
"title": "Tutorial App",
"description": "App that uses node.js",
"tagline": "Changing the world one app at a time",
"version": "0.0.1",
"healthCheckPath": "/",
"httpPort": 8000,
"addons": {
"localstorage": {}
},
"minBoxVersion": "0.0.1",
"manifestVersion": 1,
"website": "https://cloudron.io",
"contactEmail": "support@cloudron.io",
"icon": "",
"mediaLinks": []
}
```
You can read in more detail about each field in the [Manifest reference](/references/manifest.html).
# Installing
## Building
We now have all the necessary files in place to build and deploy the app to the Cloudron.
Building creates an image of the app using the Dockerfile which can then be used to deploy
to the Cloudron.
Building, pushing and pulling docker images is very bandwidth and CPU intensive. To alleviate this
problem, apps are built using the `build service` which uses `cloudron.io` account credentials.
**Warning**: As of this writing, the build service uses the public Docker registry and the images that are built
can be downloaded by anyone. This means that your source code will be viewable by others.
Initiate a build using ```cloudron build```:
```
$ cloudron build
Building io.cloudron.tutorial@0.0.1
Appstore login:
Email: ramakrishnan.girish@gmail.com # cloudron.io account
Password: # Enter password
Login successful.
Build scheduled with id 76cebfdd-7822-4f3d-af17-b3eb393ae604
Downloading source
Building
Step 0 : FROM cloudron/base:0.8.1
---> 97583855cc0c
Step 1 : ADD server.js /app/code
---> b09b97ecdfbc
Removing intermediate container 03c1e1f77acb
Step 2 : CMD /usr/local/node-0.12.7/bin/node /app/code/main.js
---> Running in 370f59d87ab2
---> 53b51eabcb89
Removing intermediate container 370f59d87ab2
Successfully built 53b51eabcb89
The push refers to a repository [cloudron/img-2074d69134a7e0da3d6cdf3c53e241c4] (len: 1)
Sending image list
Pushing repository cloudron/img-2074d69134a7e0da3d6cdf3c53e241c4 (1 tags)
Image already pushed, skipping 57f52d167bbb
Image successfully pushed b09b97ecdfbc
Image successfully pushed 53b51eabcb89
Pushing tag for rev [53b51eabcb89] on {https://cdn-registry-1.docker.io/v1/repositories/cloudron/img-2074d69134a7e0da3d6cdf3c53e241c4/tags/76cebfdd-7822-4f3d-af17-b3eb393ae604}
Build succeeded
```
## Installing
Now that we have built the image, we can install our latest build on the Cloudron
using the following command:
```
$ cloudron install
Using cloudron craft.selfhost.io
Using build 76cebfdd-7822-4f3d-af17-b3eb393ae604 from 1 hour ago
Location: tutorial # This is the location into which the application installs
App is being installed with id: 4dedd3bb-4bae-41ef-9f32-7f938995f85e
=> Waiting to start installation
=> Registering subdomain .
=> Verifying manifest .
=> Downloading image ..............
=> Creating volume .
=> Creating container
=> Setting up collectd profile ................
=> Waiting for DNS propagation ...
App is installed.
```
This makes the app available at https://tutorial-craft.selfhost.io.
Open the app in your default browser:
```
cloudron open
```
You should see `Hello World`.
# Testing
The application testing cycle involves `cloudron build` and `cloudron install`.
Note that `cloudron install` updates an existing app in place.
You can view the logs using `cloudron logs`. When the app is running you can follow the logs
using `cloudron logs -f`.
For example, you can see the console.log output in our server.js with the command below:
```
$ cloudron logs
Using cloudron craft.selfhost.io
2015-05-08T03:28:40.233940616Z Server running at port 8000
```
It is also possible to run a *shell* and *execute* arbitrary commands in the context of the application
process by using `cloudron exec`. By default, exec simply drops you into an interactive bash shell with
which you can inspect the file system and the environment.
```
$ cloudron exec
```
You can also execute arbitrary commands:
```
$ cloudron exec env # display the env variables that your app is running with
```
# Storing data
For file system storage, an app can use the `localstorage` addon to store data under `/app/data`.
When the `localstorage` addon is active, any data under /app/data is automatically backed up. When an
app is updated, /app/data already contains the data generated by the previous version.
*Note*: For convenience, the initial CloudronManifest.json generated by `cloudron init` already contains this
addon.
Let us put this theory into action by saving a *visit counter* as a file.
*server.js* has been modified to count the number of visitors on the site by storing a counter
in a file named ```counter.dat```.
File ```tutorial/server.js```
```javascript
var http = require('http'),
fs = require('fs'),
util = require('util');
var COUNTER_FILE = '/app/data/counter.dat';
var server = http.createServer(function (request, response) {
var counter = 0;
if (fs.existsSync(COUNTER_FILE)) {
// read existing counter if it exists
counter = parseInt(fs.readFileSync(COUNTER_FILE, 'utf8'), 10);
}
response.writeHead(200, {"Content-Type": "text/plain"});
response.end(util.format("Hello World. %s visitors have visited this page\n", counter));
++counter; // bump the counter
fs.writeFileSync(COUNTER_FILE, counter + '', 'utf8'); // save back counter
});
server.listen(8000);
console.log("Server running at port 8000");
```
Now every time you refresh the page you will notice that the counter bumps up. You will
also notice that if you make changes to the app and do a `cloudron install`, the `counter.dat`
is *retained* across updates.
# Database
Most web applications require a database of some form. In theory, it is possible to run any
database you want as part of the application image. This is, however, a waste of server resources
should every app runs it's own database server.
To solve this, the Cloudron provides shareable resources like databases in form of ```addons```.
The database server is managed by the Cloudron and the application simply needs to request access to
the database in the CloudronManifest.json. While the database server itself is a shared resource, the
databases are exclusive to the application. Each database is password protected and accessible only
to the application. Databases and tables can be configured without restriction as the application
requires.
Cloudron currently provides `mysql`, `postgresql`, `mongodb`, `redis` database addons.
For this tutorial, let us try to save the counter in `redis` addon. For this, we make use of the
[redis](https://www.npmjs.com/package/redis) module.
Since this is a node.js app, let's add a very basic `package.json` containing the `redis` module dependency.
File `tutorial/package.json`
```json
{
"name": "tutorial",
"version": "1.0.0",
"dependencies": {
"redis": "^0.12.1"
}
}
```
and modify our Dockerfile to look like this:
File `tutorial/Dockerfile`
```dockerfile
FROM cloudron/base:0.8.1
ADD server.js /app/code/server.js
ADD package.json /app/code/package.json
WORKDIR /app/code
RUN npm install --production
CMD [ "/usr/local/node-0.12.7/bin/node", "/app/code/server.js" ]
```
Notice the new `RUN` command which installs the node module dependencies in package.json using `npm install`.
Since we want to use redis, we have to modify the CloudronManifest.json to make redis available for this app.
File `tutorial/CloudronManifest.json`
```json
{
"id": "io.cloudron.tutorial",
"author": "John Doe",
"title": "Tutorial App",
"description": "App that uses node.js",
"tagline": "Changing the world one app at a time",
"version": "0.0.1",
"healthCheckPath": "/",
"httpPort": 8000,
"addons": {
"localstorage": {},
"redis": {}
},
"minBoxVersion": "0.0.1",
"manifestVersion": 1,
"website": "https://cloudron.io",
"contactEmail": "support@cloudron.io",
"icon": "",
"mediaLinks": []
}
```
When the application runs, environment variables `REDIS_HOST`, `REDIS_PORT` and
`REDIS_PASSWORD` are injected. You can read about the environment variables in the
[Redis reference](/references/addons.html#redis).
Let's change `server.js` to use redis instead of file backed counting:
File ```tutorial/server.js```
```javascript
var http = require('http'),
fs = require('fs'),
util = require('util'),
redis = require('redis');
var redisClient = redis.createClient(process.env.REDIS_PORT, process.env.REDIS_HOST);
redisClient.auth(process.env.REDIS_PASSWORD);
redisClient.on("error", function (err) {
console.log("Redis Client Error " + err);
});
var COUNTER_KEY = 'counter';
var server = http.createServer(function (request, response) {
redisClient.get(COUNTER_KEY, function (err, reply) {
var counter = (!err && reply) ? parseInt(reply, 10) : 0;
response.writeHead(200, {"Content-Type": "text/plain"});
response.end(util.format("Hello World. %s visitors have visited this page\n", counter));
redisClient.incr(COUNTER_KEY);
});
});
server.listen(8000);
console.log("Server running at port 8000");
```
Simply `cloudron build` and `cloudron install` to test your app!
# Authentication
The Cloudron has a centralized panel for managing users and groups. Apps can integrate Single Sign-On
authentication using LDAP or OAuth.
Note that apps that are single user can skip Single Sign-On support. The Cloudron implements an `OAuth
proxy` (accessed through the app configuration dialog) that optionally lets the Cloudron admin make the
app visible only for logged in users.
## LDAP
Let's start out by adding the [ldap](/references/addons.html#ldap) addon to the manifest.
File `tutorial/CloudronManifest.json`
```json
{
"id": "io.cloudron.tutorial",
"author": "John Doe",
"title": "Tutorial App",
"description": "App that uses node.js",
"tagline": "Changing the world one app at a time",
"version": "0.0.1",
"healthCheckPath": "/",
"httpPort": 8000,
"addons": {
"localstorage": {},
"ldap": {}
},
"minBoxVersion": "0.0.1",
"manifestVersion": 1,
"website": "https://cloudron.io",
"contactEmail": "support@cloudron.io",
"icon": "",
"mediaLinks": []
}
```
Building and installing the app shows that the app gets new LDAP specific environment variables.
```
$ cloudron build
$ cloudron install
$ cloudron exec env | grep LDAP
LDAP_SERVER=172.17.42.1
LDAP_PORT=3002
LDAP_URL=ldap://172.17.42.1:3002
LDAP_USERS_BASE_DN=ou=users,dc=cloudron
LDAP_GROUPS_BASE_DN=ou=groups,dc=cloudron
```
Let's test the environment variables to use by using the [ldapjs](http://www.ldapjs.org) npm module.
We start by adding ldapjs to package.json.
File `tutorial/package.json`
```json
{
"name": "tutorial",
"version": "1.0.0",
"dependencies": {
"ldapjs": "^0.7.1"
}
}
```
The server code has been modified to authenticate using the `X-Username` and `X-Password` headers for
any path other than '/'.
File `tutorial/server.js`
```javascript
var http = require("http"),
ldap = require('ldapjs');
var ldapClient = ldap.createClient({ url: process.env.LDAP_URL });
var server = http.createServer(function (request, response) {
if (request.url === '/') {
response.writeHead(200, {"Content-Type": "text/plain"});
return response.end();
}
var username = request.headers['x-username'] || '';
var password = request.headers['x-password'] || '';
var ldapDn = 'cn=' + username + ',' + process.env.LDAP_USERS_BASE_DN;
ldapClient.bind(ldapDn, password, function (error) {
if (error) {
response.writeHead(401, {"Content-Type": "text/plain"});
response.end('Failed to authenticate: ' + error);
} else {
response.writeHead(200, {"Content-Type": "text/plain"});
response.end('Successfully authenticated');
}
});
});
server.listen(8000);
console.log("Server running at port 8000");
```
Once we have used `cloudron build` and `cloudron install`, you can use `curl` to test
credentials as follows:
```bash
# Test with various credentials here. Your cloudon admin username and password should succeed.
curl -X 'X-Username: admin' -X 'X-Password: pass' https://tutorial-craft.selfhost.io/login
```
## OAuth
An app can integrate with OAuth 2.0 Authorization code grant flow by adding
[oauth](/references/addons.html#oauth) to CloudronManifest.json `addons` section.
Doing so will get the following environment variables:
```
$ cloudron exec env
OAUTH_CLIENT_ID=cid-addon-4089f65a-2adb-49d2-a6d1-e519b7d85e8d
OAUTH_CLIENT_SECRET=5af99a9633283aa15f5e6df4a108ff57f82064e4845de8bce8ad3af54dfa9dda
OAUTH_ORIGIN=https://my-craft.selfhost.io
API_ORIGIN=https://my-craft.selfhost.io
HOSTNAME=tutorial-craft.selfhost.io
```
OAuth Authorization code grant flow works as follows:
* App starts the flow by redirecting the user to Cloudron authorization endpoint of the following format:
```
https://API_ORIGIN/api/v1/oauth/dialog/authorize?response_type=code&client_id=OAUTH_CLIENT_ID&redirect_uri=CALLBACK_URL&scope=profile
```
In the above URL, API_ORIGIN and OAUTH_CLIENT_ID are environment variables. CALLBACK_URL is a url of the app
to which the user will be redirected back to after successful authentication. CALLBACK_URL has to have the
same origin as the app.
* The Cloudron OAuth server authenticates the user (using a password form) at the above URL. It also establishes
that the user grants the client's access request.
* If the user authenticated successfully, it will redirect the browser to CALLBACK_URL with a `code` query parameter.
* The app can exchange the `code` above for a `access token` by using the `OAUTH_CLIENT_SECRET`. It does so by making
a _POST_ request to the following url:
```
https://API_ORIGIN/api/v1/oauth/token?response_type=token&client_id=OAUTH_CLIENT_ID
```
with the following request body (json):
```json
{
"grant_type": "authorization_code",
"code": "<the code received in CALLBACK_URL query parameter>",
"redirect_uri": "https://<HOSTNAME>",
"client_id": "<OAUTH_CLIENT_ID>",
"client_secret": "<OAUTH_CLIENT_SECRET>"
}
```
In the above URL, API_ORIGIN, OAUTH_CLIENT_ID and HOSTNAME are environment variables. The response contains
the `access_token` in the body.
* The `access_token` can be used to get the [user's profile](/references/api.html#profile) using the following url:
```
https://API_ORIGIN/api/v1/profile?access_token=ACCESS_TOKEN
```
The `access_token` may also be provided in the `Authorization` header as `Bearer: <token>`.
An implementation of the above OAuth logic is at [ircd-app](https://github.com/cloudron-io/ircd-app/blob/master/settings/app.js).
The following libraries implement Cloudron OAuth for Ruby and Javascript.
* [omniauth-cloudron](https://github.com/cloudron-io/omniauth-cloudron)
* [passport-cloudron](https://github.com/cloudron-io/passport-cloudron)
# Beta Testing
Once your app is ready, you can upload it to the store for `beta testing` by
other Cloudron users. This can be done using:
```
cloudron upload
```
The app should now be visible in the Store view of your cloudron under
the 'Testing' section. You can check if the icon, description and other details
appear correctly.
Other Cloudron users can install your app on their Cloudron's using
`cloudron install --appstore-id <appid@version>`. Note that this currently
requires your beta testers to install the CLI tool and put their Cloudron in
developer mode.
# Publishing
Once you are satisfied with the beta testing, you can submit it for review.
```
cloudron submit
```
The cloudron.io team will review the app and publish the app to the store.
# Next steps
Congratulations! You are now well equipped to build web applications for the Cloudron.
# Samples
* [Lets Chat](https://github.com/cloudron-io/letschat-app)
* [Haste bin](https://github.com/cloudron-io/haste-app)
* [Pasteboard](https://github.com/cloudron-io/pasteboard-app)

483
docs/tutorials/packaging.md Normal file
View File

@@ -0,0 +1,483 @@
# Overview
This tutorial outlines how to package an existing web application for the Cloudron.
If you are aware of Docker and Heroku, you should feel at home packaging for the
Cloudron. Roughly, the steps involved are:
* Create a Dockerfile for your application. If your application already has
a Dockerfile, you should able to reuse most of it. By virtue of Docker, the Cloudron
is able to run apps written in any language/framework.
* Create a CloudronManifest.json that provides information like title, author, description
etc. You can also specify the addons (like database) required
to run your app. When the app runs on the Cloudron, it will have environment
variables set for connecting to the addon.
* Test the app on your Cloudron with the CLI tool.
* Optionally, submit the app to [Cloudron Store](/appstore.html).
# Prerequisites
## Install CLI tool
The Cloudron CLI tool allows you to install, configure and test apps on your Cloudron.
Installing the CLI tool requires [node.js](https://nodejs.org/) and
[npm](https://www.npmjs.com/). You can then install the CLI tool using the following
command:
```
sudo npm install -g cloudron
```
Note: Depending on your setup, you can run the above command without `sudo`.
## Login to Cloudron
The `cloudron` command should now be available in your path.
You can login to your Cloudron now:
```
$ cloudron login
Cloudron Hostname: craft.selfhost.io
Enter credentials for craft.selfhost.io:
Username: girish
Password:
Login successful.
```
# Basic app
We will first package a very simple app to understand how the packaging works.
You can clone this app from https://git.cloudron.io/cloudron/tutorial-basic.
## The server
The basic app server is a very simple HTTP server that runs on port 8000.
While the server in this tutorial uses node.js, you can write your server
in any language you want.
```server.js
var http = require("http");
var server = http.createServer(function (request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.end("Hello World\n");
});
server.listen(8000);
console.log("Server running at port 8000");
```
## Dockerfile
The Dockerfile contains instructions on how to create an image for your application.
```Dockerfile
FROM cloudron/base:0.8.1
ADD server.js /app/code/server.js
CMD [ "/usr/local/node-4.2.1/bin/node", "/app/code/server.js" ]
```
The `FROM` command specifies that we want to start off with Cloudron's [base image](/references/baseimage.html).
All Cloudron apps **must** start from this base image. This approach conserves space on the Cloudron since
Docker images tend to be quiet large.
The `ADD` command copies the source code of the app into the directory `/app/code`. There is nothing special
about the `/app/code` directory and it is merely a convention we use to store the application code.
The `CMD` command specifies how to run the server. The base image already contains many different versions of
node.js. We use Node 4.2.1 here.
This Dockerfile can be built and run locally as:
```
docker build -t tutorial .
docker run -p 8000:8000 -ti tutorial
```
## Manifest
The `CloudronManifest.json` specifies
* Information for installing and running the app on the Cloudron. This includes fields like addons, httpPort, tcpPorts.
* Information about displaying the app on the Cloudron Store. For example, fields like title, author, description.
Create the CloudronManifest.json using `cloudron init` as follows:
```
$ cloudron init
id: io.cloudron.tutorial # unique id for this app. use reverse domain name convention
author: John Doe # developer or company name of the for user <email>
title: Tutorial App # Cloudron Store title of this app
description: App that uses node.js # A string or local file reference like file://DESCRIPTION.md
tagline: Changing the world one app at a time # A tag line for this app for the Cloudron Store
website: https://cloudron.io # A link to this app's website
contactEmail: support@cloudron.io # Contact email of developer or company
httPort: 8000 # The http port on which this application listens to
```
The above command creates a CloudronManifest.json:
File ```tutorial/CloudronManifest.json```
```json
{
"id": "io.cloudron.tutorial",
"title": "Tutorial App",
"author": "John Doe",
"description": "file://DESCRIPTION.md",
"changelog": "file://CHANGELOG",
"tagline": "Changing the world one app at a time",
"version": "0.0.1",
"healthCheckPath": "/",
"httpPort": 8000,
"addons": {
"localstorage": {}
},
"manifestVersion": 1,
"website": "https://cloudron.io",
"contactEmail": "support@cloudron.io",
"icon": "",
"tags": [
"changme"
],
"mediaLinks": [ ]
}
```
You can read in more detail about each field in the [Manifest reference](/references/manifest.html). The
`localstorage` addon allows the app to store files in `/app/data`. We will explore addons further further
down in this tutorial.
Additional files created by `init` are:
* `DESCRIPTION.md` - A markdown file providing description of the app for the Cloudron Store.
* `CHANGELOG` - A file containing change information for each version released to the Cloudron Store. This
information is shown when the user updates the app.
# Installing
We now have all the necessary files in place to build and deploy the app to the Cloudron.
## Building
Building, pushing and pulling docker images can be very bandwidth and CPU intensive. To alleviate this
problem, apps are built using the `build service` which uses `cloudron.io` account credentials.
**Warning**: As of this writing, the build service uses the public Docker registry and the images that are built
can be downloaded by anyone. This means that your source code will be viewable by others.
Initiate a build using ```cloudron build```:
```
$ cloudron build
Building io.cloudron.tutorial@0.0.1
Appstore login:
Email: ramakrishnan.girish@gmail.com # cloudron.io account
Password: # Enter password
Login successful.
Build scheduled with id e7706847-f2e3-4ba2-9638-3f334a9453a5
Waiting for build to begin, this may take a bit...
Downloading source
Building
Step 1 : FROM cloudron/base:0.8.1
---> be9fc6312b2d
Step 2 : ADD server.js /app/code/server.js
---> 10513e428d7a
Removing intermediate container 574573f6ed1c
Step 3 : CMD /usr/local/node-4.2.1/bin/node /app/code/server.js
---> Running in b541d149b6b9
---> 51aa796ea6e5
Removing intermediate container b541d149b6b9
Successfully built 51aa796ea6e5
Pushing
The push refers to a repository [docker.io/cloudron/img-062037096d69bbf3ffb5b9316ad89cb9] (len: 1)
Pushed 51aa796ea6e5
Pushed 10513e428d7a
Image already exists be9fc6312b2d
Image already exists a0261a2a7c75
Image already exists f9d4f0f1eeed
Image already exists 2b650158d5d8
e7706847-f2e3-4ba2-9638-3f334a9453a5: digest: sha256:8241d68b65874496191106ecf2ee8f3df2e05a953cd90ff074a6f8815a49389c size: 26098
Build succeeded
Success
```
## Installing
Now that we have built the image, we can install our latest build on the Cloudron
using the following command:
```
$ cloudron install
Using cloudron craft.selfhost.io
Using build 76cebfdd-7822-4f3d-af17-b3eb393ae604 from 1 hour ago
Location: tutorial # This is the location into which the application installs
App is being installed with id: 4dedd3bb-4bae-41ef-9f32-7f938995f85e
=> Waiting to start installation
=> Registering subdomain .
=> Verifying manifest .
=> Downloading image ..............
=> Creating volume .
=> Creating container
=> Setting up collectd profile ................
=> Waiting for DNS propagation ...
App is installed.
```
Open the app in your default browser:
```
cloudron open
```
You should see `Hello World`.
# Testing
The application testing cycle involves `cloudron build` and `cloudron install`.
Note that `cloudron install` updates an existing app in place.
You can view the logs using `cloudron logs`. When the app is running you can follow the logs
using `cloudron logs -f`.
For example, you can see the console.log output in our server.js with the command below:
```
$ cloudron logs
Using cloudron craft.selfhost.io
16:44:11 [main] Server running at port 8000
```
It is also possible to run a *shell* and *execute* arbitrary commands in the context of the application
process by using `cloudron exec`. By default, exec simply drops you into an interactive bash shell with
which you can inspect the file system and the environment.
```
$ cloudron exec
```
You can also execute arbitrary commands:
```
$ cloudron exec env # display the env variables that your app is running with
```
### DevelopmentMode
When debugging complex startup scripts, one can specify `"developmentMode": true,` in the CloudronManifest.json.
This will ignore the `RUN` command, specified in the Dockerfile and allows the developer to interactively test
the startup scripts using `cloudron exec`.
**Note:** that an app running in this mode has full read/write access to the filesystem and all memory limits are lifted.
# Addons
## Filesystem
The application container created on the Cloudron has a `readonly` file system. Writing to any location
other than the below will result in an error:
* `/tmp` - Use this location for temporary files. The Cloudron will cleanup any files in this directory
periodically.
* `/run` - Use this location for runtime configuration and dynamic data. These files should not be expected
to persist across application restarts (for example, after an update or a crash).
* `/app/data` - Use this location to store application data that is to be backed up. To use this location,
you must use the [localstorage](/references/addons.html#localstorage) addon. For convenience, the initial CloudronManifest.json generated by
`cloudron init` already contains this addon.
## Database
Most web applications require a database of some form. In theory, it is possible to run any
database you want as part of the application image. This is, however, a waste of server resources
should every app runs it's own database server.
Cloudron currently provides [mysql](/references/addons.html#mysql), [postgresql](/references/addons.html#postgresql),
[mongodb](/references/addons.html#mongodb), [redis](/references/addons.html#redis) database addons. When choosing
these addons, the Cloudron will inject environment variables that contain information on how to connect
to the addon.
See https://git.cloudron.io/cloudron/tutorial-redis for a simple example of how redis can be used by
an application. The server simply uses the environment variables to connect to redis.
## Email
Cloudron applications can send email using the `sendmail` addon. Using the `sendmail` addon provides
the SMTP server and authentication credentials in environment variables.
Cloudron applications can also receive mail via IMAP using the `recvmail` addon.
## Authentication
The Cloudron has a centralized panel for managing users and groups. Apps can integrate Single Sign-On
authentication using LDAP or OAuth.
Apps can integrate with the Cloudron authentication system using LDAP, OAuth or Simple Auth. See the
[authentication](/references/authentication.html) reference page for more details.
See https://git.cloudron.io/cloudron/tutorial-ldap for a simple example of how to authenticate via LDAP.
For apps that are single user can skip Single Sign-On support by setting the `"singleUser": true`
in the manifest. By doing so, the Cloudron will installer will show a dialog to choose a user.
For app that have no user management at all, the Cloudron implements an `OAuth proxy` that
optionally lets the Cloudron admin make the app visible only for logged in users.
# Best practices
## No Setup
A Cloudron app is meant to instantly usable after installation. For this reason, Cloudron apps must not
show any setup screen after installation and should simply choose reasonable defaults.
Databases, email configuration should be automatically picked up from the environment variables using
addons.
## Dockerfile
The app is run as a read-only docker container. Because of this:
* Install any required packages in the Dockerfile.
* Create static configuration files in the Dockerfile.
* Create symlinks to dynamic configuration files under /run in the Dockerfile.
## Process manager
Docker supports restarting processes natively. Should your application crash, it will be restarted
automatically. If your application is a single process, you do not require any process manager.
Use supervisor, pm2 or any of the other process managers if you application has more then one component.
This **excludes** web servers like apache, nginx which can already manage their children by themselves.
Be sure to pick a process manager that forwards signals to child processes.
## Automatic updates
Some apps support automatic updates by overwriting themselves. A Cloudron app cannot overwrite itself
because of the read-only file system. For this reason, disable auto updates for app and let updates be
triggered through the Cloudron Store. This ties in better to the Cloudron's update and restore approach
should something go wrong with the update.
## Logging
Cloudron applications stream their logs to stdout and stderr. In practice, this ideal is hard to achieve.
Some programs like apache simply don't log to stdout. In those cases, simply log to `/tmp` or `/run`.
Logging to stdout has many advantages:
* App does not need to rotate logs and the Cloudron takes care of managing logs.
* App does not need special mechanism to release log file handles (on a log rotate).
* Integrates better with tooling like cloudron cli.
## Memory
By default, applications get 200MB RAM (including swap). This can be changed using the `memoryLimit`
field in the manifest.
Design your application runtime for concurrent use by 50 users. The Cloudron is not designed for
concurrent access by 100s or 1000s of users.
## Authentication
Apps should integrate with one of the [authentication strategies](/references/authentication.html).
This saves the user from having to manage separate set of credentials for each app.
## Startup Script
Many apps do not launch the server directly, as we did in our basic example. Instead, they execute
a `start.sh` script (named so by convention) which launches the server. Before starting the server,
the `start.sh` script does the following:
* When using the `localstorage` addon, it changes the ownership of files in `/app/data` as desired using `chown`. This
is necessary because file permissions may not be correctly preserved across backup, restore, application and base image
updates.
* Addon information (mail, database) exposed as environment are subject to change across restarts and an application
must use these values directly (i.e not cache them across restarts). For this reason, it usually regenerates
any config files with the current database settings on each invocation.
* Finally, it starts the server as a non-root user.
The app's main process must handle SIGTERM and forward it as required to child processes. bash does not
automatically forward signals to child processes. For this reason, when using a startup shell script,
remember to use exec <app> as the last line. Doing so will replace bash with your program and allows
your program to handle signals as required.
# Beta Testing
Once your app is ready, you can upload it to the store for `beta testing` by
other Cloudron users. This can be done using:
```
cloudron upload
```
The app should now be visible in the Store view of your cloudron under
the 'Testing' section. You can check if the icon, description and other details
appear correctly.
Other Cloudron users can install your app on their Cloudron's using
`cloudron install --appstore-id <appid@version>`.
# Publishing
Once you are satisfied with the beta testing, you can submit it for review.
```
cloudron submit
```
The cloudron.io team will review the app and publish the app to the store.
# Updating the app
## Versioning
To create an update for an app, simply bump up the [semver version](/references/manifest.html#version) field in
the manifest and publish a new version to the store.
The Cloudron chooses the next app version to update to based on the following algorithm:
* Choose the maximum `patch` version matching the app's current `major` and `minor` version.
* Failing the above, choose the maximum patch version of the next minor version matching the app's current `major` version.
* Failing the above, choose the maximum patch and minor version of the next major version
For example, let's assume the versions 1.1.3, 1.1.4, 1.1.5, 1.2.4, 1.2.6, 1.3.0, 2.0.0 are published.
* If the app is running 1.1.3, then app will directly update to 1.1.5 (skipping 1.1.4)
* Once in 1.1.5, the app will update to 1.2.6 (skipping 1.2.4)
* Once in 1.2.6, the app will update to 1.3.0
* Once in 1.3.0, the app will update to 2.0.0
The Cloudron admins get notified by email for any major or minor app releases.
## Failed updates
The Cloudron always makes a backup of the app before making an update. Should the
update fail, the user can restore to the backup (which will also restore the app's
code to the previous version).
# Cloudron Button
The [Cloudron Button](/references/button.html) allows anyone to install your application with the click of a button
on their Cloudron.
The button can be added to just about any website including the application's website
and README.md files in GitHub repositories.
# Next steps
Congratulations! You are now well equipped to build web applications for the Cloudron.
You can see some examples of how real apps are packaged here:
* [Lets Chat](https://git.cloudron.io/cloudron/letschat-app)
* [Haste bin](https://git.cloudron.io/cloudron/haste-app)
* [Pasteboard](https://git.cloudron.io/cloudron/pasteboard-app)