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 18, 2024
1 parent 87d67d4 commit 006ecc8
Show file tree
Hide file tree
Showing 18 changed files with 676 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
47 changes: 47 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,47 @@
#
# 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

def exec(path) do
kill_zombie_process(path)

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 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#
# 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 Mix.Tasks.AstarteDevTool.System.Down do
use Mix.Task
alias AstarteDevTool.Commands.System.Down
alias AstarteDevTool.Utilities.Path

@shortdoc "Down the local Astarte system"

@aliases [
p: :path,
v: :volumes
]

@switches [
path: :string,
volumes: :boolean,
log_level: :string
]

@moduledoc """
Down the local Astarte system.
## Examples
$ mix system.down -p /path/astarte
$ mix system.down -v
## Command line options
* `-p` `--path` - (required) working Astarte project directory
* `--log-level` - the level to set for `Logger`. This task
does not start your application, so whatever level you have configured in
your config files will not be used. If this is not provided, no level
will be set, so that if you set it yourself before calling this task
then this won't interfere. Can be any of the `t:Logger.level/0` levels
"""

@impl true
def run(args) do
{opts, _} = OptionParser.parse!(args, strict: @switches, aliases: @aliases)

unless Keyword.has_key?(opts, :path), do: Mix.raise("The --path argument is required")

if log_level = opts[:log_level],
do: Logger.configure(level: String.to_existing_atom(log_level))

with path <- opts[:path],
{:ok, abs_path} <- Path.normalize_path(path),
_ = Mix.shell().info("Stopping astarte system..."),
:ok <- Down.exec(abs_path, opts[:volumes]) do
Mix.shell().info("Astarte's system stopped successfully.")
:ok
else
{:error, output} ->
Mix.raise("Failed to stop Astarte's system. Output: #{output}")
end
end
end
Loading

0 comments on commit 006ecc8

Please sign in to comment.