Skip to content
This repository was archived by the owner on Dec 24, 2025. It is now read-only.

Commit 1eab8bb

Browse files
committed
Accept private docker registries
1 parent 5b87d56 commit 1eab8bb

File tree

10 files changed

+156
-17
lines changed

10 files changed

+156
-17
lines changed

.formatter.exs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ locals_without_parens = [
55
standalone: 1,
66
app: 2,
77
from_docker_image: 1,
8+
docker_registry: 1,
89
volume: 2,
10+
env: 2,
911
expose_port: 2
1012
]
1113

1214
[
13-
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
15+
inputs: ["{mix,.formatter,Makinafile}.exs", "{config,lib,test}/**/*.{ex,exs}"],
1416
locals_without_parens: locals_without_parens,
1517
export: [
1618
locals_without_parens: locals_without_parens

lib/makina/applications.ex

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,33 @@ defmodule Makina.Applications do
4242
defp deploy_application_on_server(%Server{} = server, %Application{} = app) do
4343
IO.puts("Deploying \"#{app.name}\"...")
4444

45-
case Docker.inspect(server, app) |> SSH.execute() do
46-
{:ok, nil} ->
47-
Logger.debug("No current instances of #{app.name} running, deploying")
48-
49-
Docker.run(server, app) |> SSH.execute()
45+
with {:ok, _} <- maybe_login_to_registry(server, app),
46+
{:ok, nil} <- ensure_app_not_running(server, app) do
47+
Logger.debug("No current instances of #{app.name} running, deploying")
5048

49+
Docker.run(server, app) |> SSH.execute()
50+
else
5151
{:ok, _container} ->
5252
Logger.debug("A version of #{app.name} is already running, skipping.")
5353
{:ok, :skipping}
5454

5555
{:error, reason} ->
5656
{:error, reason}
57+
58+
err ->
59+
err
60+
end
61+
end
62+
63+
defp ensure_app_not_running(server, app) do
64+
Docker.inspect(server, app) |> SSH.execute()
65+
end
66+
67+
defp maybe_login_to_registry(server, app) do
68+
if Application.private_docker_registry?(app) do
69+
Docker.login(server, app) |> SSH.execute()
70+
else
71+
{:ok, :no_login}
5772
end
5873
end
5974
end

lib/makina/docker.ex

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
defmodule Makina.Docker do
2-
@doc """
2+
@moduledoc """
33
Module that contains all docker-related cli command building functions.
44
On their own these do not do anything except returning a correctly formatted
55
command that should then be executed over SSH.
@@ -8,6 +8,9 @@ defmodule Makina.Docker do
88
alias Makina.Models.Server
99
alias Makina.Models.Application
1010

11+
@doc """
12+
Prepares `docker run` command based on the application being deployed
13+
"""
1114
def run(%Server{} = server, %Application{} = app) do
1215
docker(server, "run", [
1316
"-d",
@@ -26,6 +29,9 @@ defmodule Makina.Docker do
2629
])
2730
end
2831

32+
@doc """
33+
Prepares the `docker inspect` command for a give container
34+
"""
2935
def inspect(%Server{} = server, %Application{} = app) do
3036
docker(server, "inspect", [app_name(app)], fn
3137
{:ok, result} ->
@@ -41,6 +47,16 @@ defmodule Makina.Docker do
4147
end)
4248
end
4349

50+
def login(%Server{} = server, %Application{} = app) do
51+
docker(server, "login", [
52+
app.docker_registry.host,
53+
"-u",
54+
app.docker_registry.user,
55+
"-p",
56+
app.docker_registry.password
57+
])
58+
end
59+
4460
defp app_name(%Application{__scope__: []} = app) do
4561
app.name
4662
end
@@ -96,7 +112,7 @@ defmodule Makina.Docker do
96112
defp ports(%Application{} = app) do
97113
app.exposed_ports
98114
|> Enum.flat_map(fn p ->
99-
["-p", "#{p.internal}:#{p.external}"]
115+
["-p", "#{p.external}:#{p.internal}"]
100116
end)
101117
end
102118

lib/makina/dsl/app.ex

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,32 @@ defmodule Makina.DSL.App do
3535
end
3636
end
3737

38+
@docker_registry_opts [
39+
host: [type: :string, required: true],
40+
user: [type: :string, required: true],
41+
password: [type: :string, required: true]
42+
]
43+
44+
defmacro docker_registry(opts) do
45+
schema = @docker_registry_opts
46+
47+
quote do
48+
validation = NimbleOptions.validate(unquote(opts), unquote(schema))
49+
50+
case validation do
51+
{:ok, opts} ->
52+
@current_application Application.set_docker_registry(@current_application, opts)
53+
54+
{:error, error} ->
55+
raise """
56+
The parameters provided to the `docker_registry` statement are not correct:
57+
58+
#{Exception.message(error)}
59+
"""
60+
end
61+
end
62+
end
63+
3864
@doc """
3965
Mounts a volume to the given app.
4066
@@ -73,13 +99,35 @@ defmodule Makina.DSL.App do
7399
end
74100
```
75101
"""
76-
defmacro expose_port(internal, external)
77-
when is_number(internal) and is_number(external) do
102+
defmacro expose_port(external, internal) do
78103
quote bind_quoted: [internal: internal, external: external] do
79104
@current_application Application.put_exposed_port(@current_application,
80105
internal: internal,
81106
external: external
82107
)
83108
end
84109
end
110+
111+
@doc """
112+
Adds an environment variable to the running application
113+
114+
## Usage
115+
```elixir
116+
makina "example" do
117+
app name: "foo" do
118+
119+
env "FOO", "BAR"
120+
121+
end
122+
end
123+
```
124+
"""
125+
defmacro env(key, value) do
126+
quote bind_quoted: [key: key, value: value] do
127+
@current_application Application.put_environment(@current_application,
128+
key: key,
129+
value: value
130+
)
131+
end
132+
end
85133
end

lib/makina/dsl/secrets.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ defmodule Makina.DSL.Secrets do
99
Note: This refers to the environment in which the `makina` command is run.
1010
"""
1111
],
12-
"1password": [
12+
one_password: [
1313
type: :non_empty_keyword_list,
1414
keys: [
1515
vault: [type: :string],

lib/makina/models/application.ex

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ defmodule Makina.Models.Application do
3232

3333
alias Makina.Models.Internal
3434

35-
@hashable_keys ~w[name docker_image env_vars volumes exposed_ports]a
35+
@hashable_keys ~w[name docker_image docker_registry env_vars volumes exposed_ports]a
3636

3737
@derive {JSON.Encoder, []}
3838
defstruct __hash__: nil,
@@ -43,6 +43,7 @@ defmodule Makina.Models.Application do
4343
__scope__: [],
4444
name: nil,
4545
docker_image: nil,
46+
docker_registry: nil,
4647
volumes: [],
4748
env_vars: [],
4849
exposed_ports: []
@@ -77,12 +78,27 @@ defmodule Makina.Models.Application do
7778
end
7879

7980
def set_docker_image(%__MODULE__{} = app, image) when is_list(image) do
80-
image = Enum.into(image, %{})
81+
image = image |> Enum.into(%{})
8182
app = %__MODULE__{app | docker_image: image}
8283

8384
set_private(app, :__hash__, hash(app))
8485
end
8586

87+
def set_docker_registry(%__MODULE__{} = app, registry) when is_list(registry) do
88+
registry = registry |> Enum.into(%{})
89+
app = %__MODULE__{app | docker_registry: registry}
90+
91+
set_private(app, :__hash__, hash(app))
92+
end
93+
94+
def private_docker_registry?(%__MODULE__{docker_registry: nil}) do
95+
false
96+
end
97+
98+
def private_docker_registry?(%__MODULE__{docker_registry: docker_registry}) do
99+
not (is_nil(docker_registry.user) and is_nil(docker_registry.password))
100+
end
101+
86102
def set_private(%__MODULE{} = app, key, value) do
87103
app |> Map.put(key, value)
88104
end

lib/makina/secret_provider.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ defmodule Makina.SecretProvider do
1111

1212
@providers %{
1313
environment: Makina.SecretProvider.Environment,
14-
"1password": Makina.SecretProvider.OnePassword
14+
one_password: Makina.SecretProvider.OnePassword
1515
}
1616

1717
@doc """

test/makina/dsl_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ defmodule Makina.DSLTest do
146146

147147
port = List.first(app.exposed_ports)
148148

149-
assert port == %{internal: 8080, external: 80}
149+
assert port == %{internal: 80, external: 8080}
150150
end
151151

152152
test "raises if app block is not invoked correctly" do

test/makina/models/application_test.exs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,29 @@ defmodule Makina.Models.ApplicationTest do
4848
end
4949
end
5050

51+
describe "set_docker_registry/2" do
52+
test "sets the docker registry with credentials" do
53+
params = [name: "foo"]
54+
55+
app = Application.new(params)
56+
init_hash = app.__hash__
57+
58+
assert app.docker_image == nil
59+
60+
app =
61+
app
62+
|> Application.set_docker_registry(host: "ghcr.io", user: "foo", password: "bar")
63+
64+
assert app.docker_registry == %{
65+
host: "ghcr.io",
66+
user: "foo",
67+
password: "bar"
68+
}
69+
70+
assert app.__hash__ != init_hash
71+
end
72+
end
73+
5174
describe "put_environment/2" do
5275
test "adds environment variables to the application" do
5376
params = [name: "foo"]

test/makina/models/docker_test.exs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,31 @@ defmodule Makina.Models.DockerTest do
113113
app =
114114
Application.new(name: "foo")
115115
|> Application.set_docker_image(name: "nginx", tag: "1.16")
116-
|> Application.put_exposed_port(internal: 80, external: 80)
116+
|> Application.put_exposed_port(internal: 80, external: 8080)
117117

118118
cmd = Docker.run(server, app)
119119

120120
assert cmd.cmd ==
121-
"docker run -d --restart unless-stopped --name foo --label org.makina.app.hash=#{app.__hash__} -p 80:80 nginx:1.16"
121+
"docker run -d --restart unless-stopped --name foo --label org.makina.app.hash=#{app.__hash__} -p 8080:80 nginx:1.16"
122+
end
123+
end
124+
125+
describe "login/2" do
126+
test "returns a command to login into a private registry" do
127+
server =
128+
Server.new(host: "example.com")
129+
|> Server.put_private(:conn_ref, self())
130+
131+
app =
132+
Application.new(name: "foo")
133+
|> Application.set_docker_registry(host: "ghcr.io", user: "user", password: "password")
134+
|> Application.set_docker_image(name: "nginx", tag: "1.16")
135+
136+
cmd = Docker.login(server, app)
137+
138+
assert is_struct(cmd, Makina.Command)
139+
140+
assert cmd.cmd == "docker login ghcr.io -u user -p password"
122141
end
123142
end
124143
end

0 commit comments

Comments
 (0)