Skip to content

Commit

Permalink
astarte_dev_tool first commit
Browse files Browse the repository at this point in the history
The branch is based on #956, which is necessary to start
`docker compose` in development mode

Addition of `astarte_dev_tool` functionalities: the functionalities are stateless
and based on the `mix task` mechanism
- [X] `mix astarte_dev_tool.system.up`: start `docker compose` in dev mode
- [X] `mix astarte_dev_tool.system.down`: stop `docker compose` in dev mode
- [X] `mix astarte_dev_tool.system.watch`: watch mode to enable hot-code-reloading

Signed-off-by: Gabriele Ghio <gabriele.ghio@secomind.com>
  • Loading branch information
shinnokdisengir committed Jul 17, 2024
1 parent 87d67d4 commit ff08b56
Show file tree
Hide file tree
Showing 18 changed files with 717 additions and 0 deletions.
4 changes: 4 additions & 0 deletions tools/astarte_dev_tool/.formatter.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]
26 changes: 26 additions & 0 deletions tools/astarte_dev_tool/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# The directory Mix will write compiled artifacts to.
/_build/

# If you run "mix test --cover", coverage assets end up here.
/cover/

# The directory Mix downloads your dependencies sources to.
/deps/

# Where third-party dependencies like ExDoc output generated docs.
/doc/

# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch

# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump

# Also ignore archive artifacts (built via "mix archive.build").
*.ez

# Ignore package tarball (built via "mix hex.build").
astarte_dev_tool-*.tar

# Temporary files, for example, from tests.
/tmp/
62 changes: 62 additions & 0 deletions tools/astarte_dev_tool/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# AstarteDevTool

```mermaid
---
title: State diagram
---
stateDiagram-v2
direction TB
%% Definitions
state ConfigCheck <<choice>>
state UpCheck <<choice>>
classDef interrupt font-weight:bold,fill:white
%% Links
[*] --> ConfigCheck
ConfigCheck --> Break
ConfigCheck --> Config: Not configured yet
ConfigCheck --> UpCheck: Already configured
UpCheck --> Break
UpCheck --> Idle: Env down
UpCheck --> Up: Env up
Config --> Break
Config --> Idle
Idle --> Up
Idle --> Break
Idle --> [*]
Up --> Realm
Up --> [*]
Up --> Break
note right of Up
Astarte dev mode up & ready
end note
Realm --> Interface
Realm --> Device
Realm --> Break
Realm --> [*]
Break:::interrupt --> [*]
```

## Installation

If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `astarte_dev_tool` to your list of dependencies in `mix.exs`:

```elixir
def deps do
[
{:astarte_dev_tool, "~> 0.1.0"}
]
end
```

Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at <https://hexdocs.pm/astarte_dev_tool>.

24 changes: 24 additions & 0 deletions tools/astarte_dev_tool/config/config.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#
# This file is part of Astarte.
#
# Copyright 2024 SECO Mind Srl
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import Config

config :logger, :console,
format: "[$level] $message $metadata\n",
metadata: [:error_code, :file],
level: :warning
30 changes: 30 additions & 0 deletions tools/astarte_dev_tool/lib/application.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#
# This file is part of Astarte.
#
# Copyright 2024 SECO Mind Srl
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

defmodule AstarteDevTool.Application do
@moduledoc false

use Application

@impl true
def start(_type, args) do
{opts, _, _} = OptionParser.parse(args, switches: [verbose: :boolean], aliases: [v: :verbose])
# TODO start application
{:ok, self()}
end
end
34 changes: 34 additions & 0 deletions tools/astarte_dev_tool/lib/commands/system/down.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#
# This file is part of Astarte.
#
# Copyright 2024 SECO Mind Srl
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

defmodule AstarteDevTool.Commands.System.Down do
@moduledoc false
use AstarteDevTool.Constants.System

def exec(path, volumes \\ false) do
args = if volumes, do: @command_down_args ++ ["-v"], else: @command_down_args

with {_result, 0} <-
System.cmd(@command, args, @options ++ [cd: path]) do
:ok
else
{:error, reason} -> {:error, "System is not up and running: #{reason}"}
{result, exit_code} -> {:error, "Cannot exec system.down: #{result}, #{exit_code}"}
end
end
end
32 changes: 32 additions & 0 deletions tools/astarte_dev_tool/lib/commands/system/up.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#
# This file is part of Astarte.
#
# Copyright 2024 SECO Mind Srl
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

defmodule AstarteDevTool.Commands.System.Up do
@moduledoc false
use AstarteDevTool.Constants.System

def exec(path) do
with {_result, 0} <-
System.cmd(@command, @command_up_args, @options ++ [cd: path]) do
:ok
else
{:process, _} -> {:error, "The system is already running"}
{result, exit_code} -> {:error, "Cannot exec system.up: #{result}, #{exit_code}"}
end
end
end
88 changes: 88 additions & 0 deletions tools/astarte_dev_tool/lib/commands/system/watch.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#
# This file is part of Astarte.
#
# Copyright 2024 SECO Mind Srl
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

defmodule AstarteDevTool.Commands.System.Watch do
@moduledoc false
use AstarteDevTool.Constants.System
require Logger
alias AstarteDevTool.Utilities.Process, as: AstarteProcess

## Old implementation
# def exec(path) do
# with {_result, 0} <-
# System.cmd(@command, @command_watch_args, @options ++ [cd: path]) do
# :ok
# else
# {:error, reason} -> {:error, "Cannot run system watching: #{reason}"}
# {result, exit_code} -> {:error, "Cannot exec system.watch: #{result}, #{exit_code}"}
# end
# end

def exec(path) do
kill_zombie_process(path)
pid = spawn_link(fn -> execute_command(path) end)
Process.monitor(pid)
handle_signals(pid)
end

defp execute_command(path) do
with {_result, 0} <-
System.cmd(@command, @command_watch_args, @options ++ [cd: path]) do
:ok
else
{:error, reason} -> {:error, "Cannot run system watching: #{reason}"}
{result, exit_code} -> {:error, "Cannot exec system.watch: #{result}, #{exit_code}"}
end
end

defp handle_signals(child_pid) do
receive do
{:DOWN, _ref, :process, ^child_pid, _reason} ->
Logger.info("Child process has terminated.")
:ok

{:EXIT, _from, reason} ->
Logger.info("Received EXIT signal with reason: #{inspect(reason)}")
terminate_child(child_pid)
exit(reason)

:shutdown ->
Logger.info("Received shutdown signal")
terminate_child(child_pid)
:ok
end
end

defp terminate_child(pid) do
if Process.alive?(pid) do
Logger.info("Terminating child process...")
Process.exit(pid, :kill)
end
end

defp kill_zombie_process(path) do
# Kill zombie watching process
with {:ok, pid} <- AstarteProcess.check_process(@command, @command_watch_args, path),
{_result, 0} <- System.cmd("kill", [pid]) do
Logger.info("Watching zombie process ##{pid} killed")
else
{:ok, nil} -> :ok
_ -> {:error, "Cannot kill zombie process"}
end
end
end
54 changes: 54 additions & 0 deletions tools/astarte_dev_tool/lib/constants/system.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#
# This file is part of Astarte.
#
# Copyright 2024 SECO Mind Srl
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

defmodule AstarteDevTool.Constants.System do
defmacro __using__(_opts) do
quote do
@command "docker"
@command_up_args [
"compose",
"-f",
"docker-compose.yml",
"-f",
"docker-compose.dev.yml",
"up",
"--build",
"--watch",
"-d"
]
@command_down_args [
"compose",
"-f",
"docker-compose.yml",
"-f",
"docker-compose.dev.yml",
"down"
]
@command_watch_args [
"compose",
"-f",
"docker-compose.yml",
"-f",
"docker-compose.dev.yml",
"watch",
"--no-up"
]
@options [stderr_to_stdout: true, into: IO.stream(:stdio, :line)]
end
end
end
Loading

0 comments on commit ff08b56

Please sign in to comment.