|
| 1 | +defmodule Makina.Cli.Commands.Server do |
| 2 | + @behaviour Makina.Cli.Command |
| 3 | + |
| 4 | + import Makina.Cli.Utils |
| 5 | + |
| 6 | + require Logger |
| 7 | + alias Makina.SSH |
| 8 | + alias Makina.IO |
| 9 | + alias Makina.Applications |
| 10 | + alias Makina.Models.Application |
| 11 | + |
| 12 | + @sub_commands ~w[test prepare]a |
| 13 | + |
| 14 | + def name(), do: "server" |
| 15 | + |
| 16 | + def short_description(), |
| 17 | + do: "Tests whether nodes configured inside the Makinafile can be successully reached" |
| 18 | + |
| 19 | + def help, |
| 20 | + do: """ |
| 21 | + Makina |
| 22 | + Server command |
| 23 | +
|
| 24 | + Exposes server related commands. |
| 25 | +
|
| 26 | + Usage: |
| 27 | + makina server <SUB COMMAND> [OPTIONS] |
| 28 | +
|
| 29 | + Sub-commands: |
| 30 | + test Tests the connection between the current host and all servers defined in the Makinafile. |
| 31 | +
|
| 32 | + prepare Installs system services on each server in the list if they are not there already. |
| 33 | +
|
| 34 | + Global options: |
| 35 | + * --file - The path to a Makinafile, if not provided the command will look for it in the current folder. |
| 36 | + """ |
| 37 | + |
| 38 | + def exec(arguments, options) do |
| 39 | + extract_subcommand(arguments) |
| 40 | + |> sub_command(options) |
| 41 | + end |
| 42 | + |
| 43 | + def options() do |
| 44 | + [file: :string] |
| 45 | + end |
| 46 | + |
| 47 | + defp sub_command(:prepare, options) do |
| 48 | + ctx = |
| 49 | + makinafile(options) |
| 50 | + |> fetch_context() |
| 51 | + |
| 52 | + servers = ctx.servers |
| 53 | + |
| 54 | + system_applications = [ |
| 55 | + reverse_proxy() |
| 56 | + ] |
| 57 | + |
| 58 | + # Create network if doesn't exist |
| 59 | + # Create System application if they do not exist |
| 60 | + Applications.deploy_standalone_applications(servers, system_applications) |
| 61 | + |
| 62 | + :ok |
| 63 | + end |
| 64 | + |
| 65 | + defp sub_command(:test, options) do |
| 66 | + ctx = |
| 67 | + makinafile(options) |
| 68 | + |> fetch_context() |
| 69 | + |
| 70 | + servers = ctx.servers |
| 71 | + |
| 72 | + Owl.ProgressBar.start( |
| 73 | + id: :server_tests, |
| 74 | + label: "Testing servers connectivity", |
| 75 | + total: Enum.count(servers) |
| 76 | + ) |
| 77 | + |
| 78 | + results = |
| 79 | + for server <- servers do |
| 80 | + response = |
| 81 | + SSH.connect( |
| 82 | + server.host, |
| 83 | + user: server.user, |
| 84 | + password: server.password, |
| 85 | + port: server.port |
| 86 | + ) |
| 87 | + |
| 88 | + case response do |
| 89 | + {:ok, conn_ref} -> |
| 90 | + SSH.disconnect(conn_ref) |
| 91 | + Owl.ProgressBar.inc(id: :server_tests) |
| 92 | + |
| 93 | + {:ok, server} |
| 94 | + |
| 95 | + {:error, _reason} -> |
| 96 | + Owl.ProgressBar.inc(id: :server_tests) |
| 97 | + |
| 98 | + {:error, server} |
| 99 | + end |
| 100 | + end |
| 101 | + |
| 102 | + Owl.LiveScreen.await_render() |
| 103 | + |
| 104 | + if test_with_errors?(results) do |
| 105 | + IO.puts_error("Cannot reach some servers") |
| 106 | + |
| 107 | + :error |
| 108 | + else |
| 109 | + IO.puts_success("All servers are reachable ✅") |
| 110 | + |
| 111 | + :ok |
| 112 | + end |
| 113 | + end |
| 114 | + |
| 115 | + defp sub_command(:help, _options) do |
| 116 | + IO.puts(help()) |
| 117 | + end |
| 118 | + |
| 119 | + defp extract_subcommand([]) do |
| 120 | + :help |
| 121 | + end |
| 122 | + |
| 123 | + defp extract_subcommand([sub_command | _rest]) do |
| 124 | + sub_command = sub_command |> String.to_atom() |
| 125 | + |
| 126 | + if Enum.member?(@sub_commands, sub_command), do: sub_command, else: :help |
| 127 | + end |
| 128 | + |
| 129 | + defp test_with_errors?(results), |
| 130 | + do: Enum.any?(results, fn r -> elem(r, 0) == :error end) |
| 131 | + |
| 132 | + defp reverse_proxy() do |
| 133 | + app = |
| 134 | + Application.new(name: "makina-proxy") |
| 135 | + |> Application.set_docker_image(name: "traefik", tag: "v3.3") |
| 136 | + |> Application.put_exposed_port(internal: 80, external: 80) |
| 137 | + |> Application.put_exposed_port(internal: 8080, external: 8080) |
| 138 | + |> Application.put_volume( |
| 139 | + source: "/var/run/docker.sock", |
| 140 | + destination: "/var/run/docker.sock" |
| 141 | + ) |
| 142 | + |
| 143 | + Application.set_private(app, :__docker__, %{ |
| 144 | + app.__docker__ |
| 145 | + | command: [ |
| 146 | + "--entryPoints.web.address=:80", |
| 147 | + "--api.insecure=true", |
| 148 | + "--providers.docker" |
| 149 | + ] |
| 150 | + }) |
| 151 | + end |
| 152 | +end |
0 commit comments