Commit a17a603f authored by Alex Castaño's avatar Alex Castaño

Docker

parent aebf5b11
_build/
deps/
.git/
.gitignore
Dockerfile
Makefile
README*
test/
priv/static/
erlang 21.1
elixir 1.7.3-otp-21
elixir 1.7.4-otp-21
nodejs 10.13.0
# The version of Alpine to use for the final image
# This should match the version of Alpine that the `elixir:1.7.4-alpine` image uses
ARG ALPINE_VERSION=3.8
# The following are build arguments used to change variable parts of the image.
# The name of your application/release (required)
ARG APP_NAME
# The version of the application we are building (required)
ARG APP_VSN
FROM elixir:1.7.4-alpine as deps-getter
ENV HOME=/opt/app/ TERM=xterm MIX_ENV=prod
WORKDIR /opt/app
# dependencies for comeonin
RUN apk add --no-cache build-base cmake curl git
# Cache elixir deps
COPY mix.exs mix.lock ./
RUN mix do local.hex --force, local.rebar --force, deps.get, deps.compile
##################3
# Asset builder
##################3
FROM node:10.13.0 as asset-builder
ENV HOME=/opt/app
WORKDIR $HOME
COPY --from=deps-getter $HOME/deps $HOME/deps
WORKDIR $HOME/assets
COPY assets/package-lock.json assets/package.json ./
RUN npm install
COPY assets/ ./
RUN npm run-script deploy
##################3
# Builder
##################3
FROM deps-getter as builder
ENV HOME=/opt/app/ TERM=xterm MIX_ENV=prod
WORKDIR $HOME
COPY . .
# Digest precompiled assets
COPY --from=asset-builder $HOME/priv/static/ $HOME/priv/static/
RUN mix do phx.digest, release --env=prod --verbose --no-tar
# From this line onwards, we're in a new image, which will be the image used in production
FROM alpine:${ALPINE_VERSION}
# The name of your application/release (required)
ARG APP_NAME
ARG APP_VSN
RUN apk update && \
apk add --no-cache \
bash \
openssl-dev
ENV REPLACE_OS_VARS=true \
APP_NAME=${APP_NAME} \
APP_VSN=${APP_VSN}
WORKDIR /opt/app
COPY --from=builder /opt/app/_build/prod/rel/${APP_NAME} /opt/app
CMD trap 'exit' INT; /opt/app/bin/${APP_NAME} foreground
.PHONY: help
APP_NAME ?= `grep 'app:' mix.exs | sed -e 's/\[//g' -e 's/ //g' -e 's/app://' -e 's/[:,]//g'`
APP_VSN ?= `grep 'version:' mix.exs | cut -d '"' -f2`
APP_BUILD ?= `git rev-parse --short HEAD`
help:
@echo "$(APP_NAME):$(APP_VSN)-$(APP_BUILD)"
@perl -nle'print $& if m{^[a-zA-Z_-]+:.*?## .*$$}' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
build: ## Build the Docker image
docker build \
--no-cache \
--build-arg APP_NAME=$(APP_NAME) \
--build-arg APP_VSN=$(APP_VSN) \
--build-arg APP_BUILD=$(APP_BUILD) \
-t moodlenet:$(APP_VSN)-$(APP_BUILD) \
-t moodlenet:latest .
run: ## Run the app in Docker
docker run\
--env-file config/docker.env \
--expose 4000 -p 4000:4000 \
--rm -it moodlenet:latest
......@@ -19,8 +19,49 @@ The first projects using it are:
### With Docker (recommended)
See the [server-deploy repo](https://gitlab.com/OpenCoop/CommonsPub/server-deploy), for a docker-compose based setup process.
The docker image can be found in: https://hub.docker.com/r/moodlenet/moodlenet/
The docker images needs the environment variables to work.
An updated list of them can be found in the file `config/docker.env` in this same repository.
The easiest way to launch the docker image is using the `docker-compose` tool.
The `docker-compose.yml` uses the previous `config/docker.env` to launch a `moodlenet` container
and all the dependencies, currently, only a postgres container is needed it.
#### Docker commands
The first time you launch the docker instance the database is not created.
There are several commands to make the first launch easier.
We will use `docker-compose` to show the commands:
* `docker-compose run --rm web bin/moodle_net create_db` creates the database
* `docker-compose run --rm web bin/moodle_net migrate_db` creates the database and runs the migrations
* `docker-compose run --rm web bin/moodle_net drop_db` drops the database
Other important commands are:
* `docker-compose up` launches the service, by default at the port 4000.
* `docker-compose run --rm web /bin/sh` runs a simple shell inside of the container, useful to explore the image
* `docker-compose run --rm web bin/moodle_net console` runs an `iex` console
* `docker-compose exec web bin/moodle_net remote_console` runs an `iex` console when the service is already running.
* `docker-compose run --rm web bin/moodle_net help` returns all the possible commands
There is a command that currently is not working: `seed_db`.
The reason is that to generate ActivityPub ID we need the URL where the server is running,
but `Phoenix` is not launched in this command.
However, we can still do it.
To seed the database we can run the following command in an `iex` console:
`iex> MoodleNet.ReleaseTasks.seed_db([])`
#### Build Docker image
There is a `Makefile` with two commands:
* `make build` which builds the docker image in `moodlenet:latest` and `moodlenet:$VERSION-$BUILD`
* `make run` which can be used to run the docker built docker image without `docker-compose`
---
### Manual installation
......
......@@ -117,6 +117,8 @@ config :moodle_net, :suggestions,
limit: 23,
web: "https://vinayaka.distsn.org/?{{host}}+{{user}}"
config :moodle_net, MoodleNetWeb.Gettext, default_locale: "en", locales: ~w(en es)
# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs"
HOSTNAME=localhost
SECRET_KEY_BASE="U1QXlca4ZEZKb1o3HL/aUlznI1qstCNAQ6yme/lFbFIs0Iqiq/annZ+Ty8JyUCDc"
DATABASE_HOST=db
DATABASE_USER=postgres
DATABASE_PASS=postgres
DATABASE_NAME=moodle_net_prod
PORT=4000
LANG=en_US.UTF-8
REPLACE_OS_VARS=true
ERLANG_COOKIE=moodle_net_cookie
......@@ -14,8 +14,13 @@ use Mix.Config
# manifest is generated by the mix phoenix.digest task
# which you typically run after static files are built.
config :moodle_net, MoodleNetWeb.Endpoint,
http: [port: 4000],
protocol: "http"
http: [port: {:system, "PORT"}],
# This is critical for ensuring web-sockets properly authorize.
url: [host: {:system, "HOSTNAME"}, port: {:system, "PORT"}],
cache_static_manifest: "priv/static/cache_manifest.json",
server: true,
root: ".",
version: Application.spec(:moodle_net, :vsn)
# Do not print debug messages in production
config :logger, level: :info
......@@ -60,4 +65,4 @@ config :logger, level: :info
# Finally import the config/prod.secret.exs
# which should be versioned separately.
import_config "prod.secret.exs"
# import_config "prod.secret.exs"
version: '3.5'
services:
web:
# Pull the image from dockerhub
# image: "moodlenet/moodlenet:latest"
# Build the image first with: make build
image: "moodlenet:latest"
ports:
- "4000:4000"
env_file:
- config/docker.env
depends_on:
- db
db:
image: postgres:11-alpine
# volumes:
# - "./volumes/postgres:/var/lib/postgresql/data"
# ports:
# - "5432:5432"
env_file:
- config/docker.env
defmodule MoodleNet.ReleaseTasks do
@start_apps [
:crypto,
:ssl,
:postgrex,
:ecto,
:ecto_sql # If using Ecto 3.0 or higher
]
@repos Application.get_env(:moodle_net, :ecto_repos, [])
def create_db(_) do
start_apps()
Enum.each(@repos, &create_repo/1)
stop_services()
end
defp create_repo(repo) do
case repo.__adapter__.storage_up(repo.config) do
:ok ->
IO.puts("The database for #{inspect(repo)} has been created")
{:error, :already_up} ->
IO.puts("The database for #{inspect(repo)} has already been created")
{:error, term} when is_binary(term) ->
raise "The database for #{inspect(repo)} couldn't be created: #{term}"
end
end
def migrate_db(_) do
start_apps()
Enum.each(@repos, &create_repo/1)
start_repos()
Enum.each(@repos, &migrate_repo/1)
stop_services()
end
defp migrate_repo(repo) do
app = Keyword.get(repo.config, :otp_app)
IO.puts("Running migrations for #{app}")
migrations_path = priv_path_for(repo, "migrations")
Ecto.Migrator.run(repo, migrations_path, :up, all: true)
end
def rollback_db(_) do
start_apps()
start_repos()
Enum.each(@repos, &rollback_repo/1)
stop_services()
end
defp rollback_repo(repo) do
app = Keyword.get(repo.config, :otp_app)
IO.puts("Running rollback for #{app}")
migrations_path = priv_path_for(repo, "migrations")
Ecto.Migrator.run(repo, migrations_path, :down, step: 1)
end
def seed_db(_) do
start_apps()
Enum.each(@repos, &create_repo/1)
start_repos()
Enum.each(@repos, &migrate_repo/1)
Enum.each(@repos, &seed_repo/1)
stop_services()
end
defp seed_repo(repo) do
# Run the seed script if it exists
seed_script = priv_path_for(repo, "seeds.exs")
if File.exists?(seed_script) do
IO.puts("Running seed script..")
Code.eval_file(seed_script)
end
end
def drop_db([]) do
start_apps()
Enum.each(@repos, &drop_repo/1)
stop_services()
end
defp drop_repo(repo) do
case repo.__adapter__.storage_down(repo.config) do
:ok ->
IO.puts("The database for #{inspect(repo)} has been dropped")
{:error, :already_down} ->
IO.puts("The database for #{inspect(repo)} has already been dropped")
{:error, term} when is_binary(term) ->
raise "The database for #{inspect(repo)} couldn't be dropped: #{term}"
end
end
defp start_apps() do
IO.puts("Starting dependencies..")
# Start apps necessary for executing migrations
Enum.each(@start_apps, &Application.ensure_all_started/1)
end
defp start_repos() do
# Start the Repo(s) for app
IO.puts("Starting repos..")
# Switch pool_size to 2 for ecto > 3.0
Enum.each(@repos, & &1.start_link(pool_size: 2))
end
defp stop_services() do
IO.puts("Success!")
:init.stop()
end
defp priv_path_for(repo, filename) do
app = Keyword.get(repo.config, :otp_app)
repo_underscore =
repo
|> Module.split()
|> List.last()
|> Macro.underscore()
priv_dir = "#{:code.priv_dir(app)}"
Path.join([priv_dir, repo_underscore, filename])
end
end
<section class="phx-hero">
<h1><%= gettext "Welcome to %{name}!", name: "Phoenix" %></h1>
<h1><%= gettext "pages.index.welcome", name: "Phoenix" %></h1>
<p>A productive web framework that<br/>does not compromise speed and maintainability.</p>
</section>
......@@ -4,8 +4,8 @@ defmodule MoodleNet.Mixfile do
def project do
[
app: :moodle_net,
version: "0.9.0",
elixir: "~> 1.7.3",
version: "0.0.1",
elixir: "~> 1.7.4",
elixirc_paths: elixirc_paths(Mix.env()),
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
start_permanent: Mix.env() == :prod,
......@@ -54,6 +54,7 @@ defmodule MoodleNet.Mixfile do
{:ex_aws, "~> 2.0"},
{:ex_aws_s3, "~> 2.0"},
{:cors_plug, "~> 2.0"},
{:distillery, "~> 2.0"},
# FIXME using prod as well for the moment
# {:faker, "~> 0.11", only: [:dev, :test]},
{:faker, "~> 0.11"},
......
%{
"absinthe": {:hex, :absinthe, "1.4.13", "81eb2ff41f1b62cd6e992955f62c22c042d1079b7936c27f5f7c2c806b8fc436", [:mix], [{:dataloader, "~> 1.0.0", [hex: :dataloader, repo: "hexpm", optional: true]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
"absinthe_plug": {:hex, :absinthe_plug, "1.4.6", "ac5d2d3d02acf52fda0f151b294017ab06e2ed1c6c15334e06aac82c94e36e08", [:mix], [{:absinthe, "~> 1.4.11", [hex: :absinthe, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.2 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
"artificery": {:hex, :artificery, "0.2.6", "f602909757263f7897130cbd006b0e40514a541b148d366ad65b89236b93497a", [:mix], [], "hexpm"},
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
"cachex": {:hex, :cachex, "3.0.3", "4e2d3e05814a5738f5ff3903151d5c25636d72a3527251b753f501ad9c657967", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm"},
"calendar": {:hex, :calendar, "0.17.4", "22c5e8d98a4db9494396e5727108dffb820ee0d18fed4b0aa8ab76e4f5bc32f1", [:mix], [{:tzdata, "~> 0.5.8 or ~> 0.1.201603", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
......@@ -15,6 +16,7 @@
"db_connection": {:hex, :db_connection, "2.0.2", "440c05518b0bdca0469dafaf45403597430448c1281def14ef9ccaa41833ea1e", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"},
"decimal": {:hex, :decimal, "1.5.0", "b0433a36d0e2430e3d50291b1c65f53c37d56f83665b43d79963684865beab68", [:mix], [], "hexpm"},
"dialyxir": {:hex, :dialyxir, "1.0.0-rc.4", "71b42f5ee1b7628f3e3a6565f4617dfb02d127a0499ab3e72750455e986df001", [:mix], [{:erlex, "~> 0.1", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm"},
"distillery": {:hex, :distillery, "2.0.12", "6e78fe042df82610ac3fa50bd7d2d8190ad287d120d3cd1682d83a44e8b34dfb", [:mix], [{:artificery, "~> 0.2", [hex: :artificery, repo: "hexpm", optional: false]}], "hexpm"},
"earmark": {:hex, :earmark, "1.3.0", "17f0c38eaafb4800f746b457313af4b2442a8c2405b49c645768680f900be603", [:mix], [], "hexpm"},
"ecto": {:hex, :ecto, "3.0.3", "018a3df0956636f84eb3033d807485a7d3dea8474f47b90da5cb8073444c4384", [:mix], [{:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"},
"ecto_sql": {:hex, :ecto_sql, "3.0.2", "0e04cbc183b91ea0085c502226befcd237a4ac31c204fd4be8d4db6676b5f10d", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.0.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.9.1", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.14.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.2.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
......
## This file is a PO Template file.
##
## `msgid`s here are often extracted from source code.
## Add new translations manually only if they're dynamic
## translations that can't be statically extracted.
##
## Run `mix gettext.extract` to bring this file up to
## date. Leave `msgstr`s empty as changing them here as no
## effect: edit them in PO (`.po`) files instead.
msgid ""
msgstr ""
#, elixir-format
#: lib/moodle_net_web/page/templates/index.html.eex:2
msgid "pages.index.welcome"
msgstr ""
## `msgid`s in this file come from POT (.pot) files.
##
## Do not add, change, or remove `msgid`s manually here as
## they're tied to the ones in the corresponding POT file
## (with the same domain).
##
## Use `mix gettext.extract --merge` or `mix gettext.merge`
## to merge POT files into PO files.
msgid ""
msgstr ""
"Language: en\n"
"Plural-Forms: nplurals=2\n"
#, elixir-format
#: lib/moodle_net_web/page/templates/index.html.eex:2
msgid "pages.index.welcome"
msgstr "Welcome to %{name}"
......@@ -7,7 +7,6 @@
## Run `mix gettext.extract` to bring this file up to
## date. Leave `msgstr`s empty as changing them here as no
## effect: edit them in PO (`.po`) files instead.
## From Ecto.Changeset.cast/4
msgid "can't be blank"
msgstr ""
......
## `msgid`s in this file come from POT (.pot) files.
##
## Do not add, change, or remove `msgid`s manually here as
## they're tied to the ones in the corresponding POT file
## (with the same domain).
##
## Use `mix gettext.extract --merge` or `mix gettext.merge`
## to merge POT files into PO files.
msgid ""
msgstr ""
"Language: es\n"
"Plural-Forms: nplurals=2\n"
#, elixir-format
#: lib/moodle_net_web/page/templates/index.html.eex:2
msgid "pages.index.welcome"
msgstr "Bienvenido a %{name}"
## `msgid`s in this file come from POT (.pot) files.
##
## Do not add, change, or remove `msgid`s manually here as
## they're tied to the ones in the corresponding POT file
## (with the same domain).
##
## Use `mix gettext.extract --merge` or `mix gettext.merge`
## to merge POT files into PO files.
msgid ""
msgstr ""
"Language: es\n"
"Plural-Forms: nplurals=2\n"
msgid "can't be blank"
msgstr ""
msgid "has already been taken"
msgstr ""
msgid "is invalid"
msgstr ""
msgid "has invalid format"
msgstr ""
msgid "has an invalid entry"
msgstr ""
msgid "is reserved"
msgstr ""
msgid "does not match confirmation"
msgstr ""
msgid "is still associated with this entry"
msgstr ""
msgid "are still associated with this entry"
msgstr ""
msgid "should be %{count} character(s)"
msgid_plural "should be %{count} character(s)"
msgstr[0] ""
msgstr[1] ""
msgid "should have %{count} item(s)"
msgid_plural "should have %{count} item(s)"
msgstr[0] ""
msgstr[1] ""
msgid "should be at least %{count} character(s)"
msgid_plural "should be at least %{count} character(s)"
msgstr[0] ""
msgstr[1] ""
msgid "should have at least %{count} item(s)"
msgid_plural "should have at least %{count} item(s)"
msgstr[0] ""
msgstr[1] ""
msgid "should be at most %{count} character(s)"
msgid_plural "should be at most %{count} character(s)"
msgstr[0] ""
msgstr[1] ""
msgid "should have at most %{count} item(s)"
msgid_plural "should have at most %{count} item(s)"
msgstr[0] ""
msgstr[1] ""
msgid "must be less than %{number}"
msgstr ""
msgid "must be greater than %{number}"
msgstr ""
msgid "must be less than or equal to %{number}"
msgstr ""
msgid "must be greater than or equal to %{number}"
msgstr ""
msgid "must be equal to %{number}"
msgstr ""
......@@ -9,3 +9,15 @@
#
# We recommend using the bang functions (`insert!`, `update!`
# and so on) as they will fail if something goes wrong.
alias MoodleNet.Factory
communities = for _ <- 1..3, do: Factory.community()
collections = for _ <- 1..5, do: Factory.collection(Enum.random(communities))
_resources = for _ <- 1..10, do: Factory.resource(Enum.random(collections))
actors = for _ <- 1..5, do: Factory.actor()
commentables = communities ++ collections
_comments = for _ <- 1..10, do: Factory.comment(Enum.random(actors), Enum.random(commentables))
#!/bin/sh
release_ctl eval --mfa "MoodleNet.ReleaseTasks.create_db/1" --argv -- "$@"
#!/bin/sh
release_ctl eval --mfa "MoodleNet.ReleaseTasks.drop_db/1" --argv -- "$@"
#!/bin/sh
release_ctl eval --mfa "MoodleNet.ReleaseTasks.migrate_db/1" --argv -- "$@"
#!/bin/sh
release_ctl eval --mfa "MoodleNet.ReleaseTasks.seed_db/1" --argv -- "$@"
# Import all plugins from `rel/plugins`
# They can then be used by adding `plugin MyPlugin` to
# either an environment, or release definition, where
# `MyPlugin` is the name of the plugin module.
~w(rel plugins *.exs)
|> Path.join()
|> Path.wildcard()
|> Enum.map(&Code.eval_file(&1))
use Mix.Releases.Config,
# This sets the default release built by `mix release`
default_release: :default,
# This sets the default environment used by `mix release`
default_environment: Mix.env()
# For a full list of config options for both releases
# and environments, visit https://hexdocs.pm/distillery/config/distillery.html
# You may define one or more environments in this file,
# an environment's settings will override those of a release
# when building in that environment, this combination of release
# and environment configuration is called a profile
environment :prod do
set include_erts: true
set include_src: false
set cookie: :"f]:Y%u(BHQ^?HOM:GIZAhlMk/bYCf?Ig4Di7*T%hZkaB2vBgUb&/c}vZ.jTN$/A{"
set vm_args: "rel/vm.args"
end
# You may define one or more releases in this file.
# If you have not set a default release, or selected one
# when running `mix release`, the first release in the file
# will be used by default
release :moodle_net do
set version: current_version(:moodle_net)
set applications: [
:runtime_tools
]
set config_providers: [
{Mix.Releases.Config.Providers.Elixir, ["${RELEASE_ROOT_DIR}/etc/config.exs"]}
]
set overlays: [
{:copy, "rel/config/config.exs", "etc/config.exs"}
]
set commands: [
migrate_db: "rel/commands/migrate_db.sh",
create_db: "rel/commands/create_db.sh",
drop_db: "rel/commands/drop_db.sh",
seed_db: "rel/commands/seed_db.sh",
]
end
use Mix.Config
config :moodle_net, MoodleNet.Repo,
username: System.get_env("DATABASE_USER"),
password: System.get_env("DATABASE_PASS"),
database: System.get_env("DATABASE_NAME"),
hostname: System.get_env("DATABASE_HOST"),
pool_size: 15
port = String.to_integer(System.get_env("PORT") || "8080")
config :moodle_net, MoodleNetWeb.Endpoint,
http: [port: port],
url: [host: System.get_env("HOSTNAME"), port: port],
root: ".",
secret_key_base: System.get_env("SECRET_KEY_BASE")
*.*
!*.exs
!.gitignore
\ No newline at end of file
## This file provide the arguments provided to the VM at startup
## You can find a full list of flags and their behaviours at
## http://erlang.org/doc/man/erl.html
## Name of the node
-name <%= release_name %>@127.0.0.1
## Cookie for distributed erlang
-setcookie <%= release.profile.cookie %>
-kernel inet_dist_listen_min 9000 inet_dist_listen_max 9002
## Heartbeat management; auto-restarts VM if it dies or becomes unresponsive
## (Disabled by default..use with caution!)
##-heart
## Enable kernel poll and a few async threads
##+K true
##+A 5
## For OTP21+, the +A flag is not used anymore,
## +SDio replace it to use dirty schedulers
##+SDio 5
## Increase number of concurrent ports/sockets
##-env ERL_MAX_PORTS 4096
## Tweak GC to run more often
##-env ERL_FULLSWEEP_AFTER 10
# Enable SMP automatically based on availability
# On OTP21+, this is not needed anymore.
-smp auto
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment