Skip to content

Commit

Permalink
Merge pull request #152 from esl/distribute-modules-as-binaries
Browse files Browse the repository at this point in the history
Distribute modules as binaries

This PR introduces the following changes:

- Cleanup of the integration testing
- Since we moved REST API out of this repo, all the leftovers are removed
- Extraction of amoc_code_server functionality from of amoc_scenario module:
    - now amoc_scenario is just a wrapper for behavior.
    - and amoc_code_server is responsible for modules' analysis and distribution of uploaded modules.
- Modules' distribution is now based on binary representation, so it's language agnostic and can be reused for elixir.
- comparison of the modules is based on MD5
- amoc_code_server_SUITE covers all the basic use cases and demonstrates some of the corner cases that are covered in amoc_code_server
  • Loading branch information
NelsonVides authored Nov 17, 2023
2 parents ffff0fe + 6d4d273 commit bc377e7
Show file tree
Hide file tree
Showing 41 changed files with 1,438 additions and 664 deletions.
19 changes: 6 additions & 13 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
ARG otp_vsn=25.3
FROM erlang:${otp_vsn}-slim AS builder
FROM erlang:${otp_vsn}
MAINTAINER Erlang Solutions <[email protected]>

WORKDIR /amoc_build
WORKDIR /amoc
COPY ./ ./

COPY ./rebar.config ./rebar.lock ./
RUN rebar3 deps && rebar3 compile -d
RUN make clean
RUN make rel

COPY ./integration_test integration_test
COPY ./scenarios scenarios
COPY ./priv priv
COPY ./rel rel
COPY ./src src

RUN rebar3 as demo release

ENV PATH "/amoc_build/_build/demo/rel/amoc/bin:${PATH}"
ENV PATH "/amoc/_build/default/rel/amoc/bin:${PATH}"

CMD ["amoc", "foreground"]
39 changes: 23 additions & 16 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,57 +1,64 @@
.PHONY: default rel compile clean ct test integration_test dialyzer xref console lint
.PHONY: default rel deps compile clean ct lint dialyzer xref console
.PHONY: test integration_test rerun_integration_test

REBAR = rebar3

ifdef SUITE
SUITE_OPTS = --suite $$SUITE
endif

## this target is triggered when amoc is used as an erlang.mk dependency
default:
$(REBAR) compile
default: compile

rel:
$(REBAR) as demo tar
$(REBAR) release

deps:
$(REBAR) deps
$(REBAR) compile --deps_only

compile:
$(REBAR) as demo compile
$(REBAR) compile

clean:
rm -rf _build
rm -rfv priv/scenarios_ebin/*.beam

ct:
rm -rfv priv/scenarios_ebin/*.beam
## eunit and ct commands always add a test profile to the run
$(REBAR) ct --verbose $(SUITE_OPTS)
## in order to run some specific test suite(s) you can override
## the SUITE variable from the command line or as env variable:
## make ct SUITE=some_test_SUITE
## make ct SUITE=some_test_SUITE,another_test_SUITE
## SUITE=some_test_SUITE make ct
## SUITE=some_test_SUITE,another_test_SUITE make ct
@ echo $(REBAR) ct --verbose $(SUITE_OPTS)
@ $(REBAR) ct --verbose $(SUITE_OPTS)

lint:
$(REBAR) as elvis lint

test: compile xref dialyzer ct lint

integration_test:
./integration_test/stop_demo_cluster.sh
./integration_test/stop_test_cluster.sh
./integration_test/build_docker_image.sh
./integration_test/start_demo_cluster.sh
./integration_test/start_test_cluster.sh
./integration_test/test_amoc_cluster.sh
./integration_test/test_distribute_scenario.sh
./integration_test/test_run_scenario.sh
./integration_test/test_add_new_node.sh

rerun_integration_test:
./integration_test/stop_demo_cluster.sh
./integration_test/start_demo_cluster.sh
./integration_test/stop_test_cluster.sh
./integration_test/start_test_cluster.sh
./integration_test/test_amoc_cluster.sh
./integration_test/test_distribute_scenario.sh
./integration_test/test_run_scenario.sh
./integration_test/test_add_new_node.sh

dialyzer:
$(REBAR) as demo dialyzer
$(REBAR) dialyzer

xref:
$(REBAR) as demo xref
$(REBAR) xref

console:
@echo "tests can be executed manually using ct:run/1 function:\n" \
Expand Down
11 changes: 8 additions & 3 deletions elvis.config
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
filter => "*.erl",
ruleset => erl_files,
rules => [
{elvis_style, invalid_dynamic_call, #{ignore => [amoc_user]}},
{elvis_style, invalid_dynamic_call,
#{ignore => [amoc_user, {amoc_code_server, get_md5, 1}]}},
{elvis_style, export_used_types, disable},
{elvis_style, no_throw, #{ignore => [{amoc_config, get, 2}] }},
{elvis_text_style, line_length, #{skip_comments => whole_line }},
Expand All @@ -14,10 +15,14 @@
filter => "*.erl",
ruleset => erl_files,
rules => [
{elvis_style, function_naming_convention, #{regex => "^[a-z]([a-z0-9]*_?)*$"}},
{elvis_style, atom_naming_convention, #{regex => "^[a-z]([a-z0-9]*_?)*(_SUITE)?$"}},
{elvis_style, function_naming_convention,
#{regex => "^[a-z]([a-z0-9]*_?)*$"}},
{elvis_style, atom_naming_convention,
#{regex => "^[a-z]([a-z0-9]*_?)*(_SUITE)?$"}},
{elvis_style, invalid_dynamic_call, #{ignore => [amoc_code_server_SUITE]}},
{elvis_style, dont_repeat_yourself, #{min_complexity => 50}},
{elvis_style, no_debug_call, disable},
{elvis_style, no_block_expressions, #{ignore => [amoc_code_server_SUITE]}},
{elvis_style, no_throw, disable},
{elvis_style, no_import, disable}
]},
Expand Down
4 changes: 2 additions & 2 deletions guides/coordinator.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ It’s guaranteed that all the *Coordination Actions* with `all` are executed af

## Example

This scenario will demonstrate how do the `users` interact with `amoc_coordinator`:
This scenario shows how the `users` interact with `amoc_coordinator`:

```erlang
-module(example).
Expand Down Expand Up @@ -114,7 +114,7 @@ User = 3
Three new users showed up

User = 4
% We have 4 and 4 rem 2 == 0 therefore users added to amoc_coordinator so all of the {3, _} actions are triggered:
% We have 4 and 4 rem 2 == 0 therefore users added to amoc_coordinator so all of the {2, _} actions are triggered:
Two new users showed up: Event = {coordinate,2}; User1 = {<0.1144.0>,4}; User2 = {<0.1143.0>,3}
Two new users showed up: Event = {coordinate,2}; ListOfUsers = [{<0.1144.0>,4},{<0.1143.0>,3}]
Two new users showed up: Event = {coordinate,2}
Expand Down
2 changes: 1 addition & 1 deletion guides/local-run.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Everything you need to do is to create the release. To achieve that run:
`make rel`. Now you are ready to test our scenario locally with one Amoc node;
to start the node run `_build/demo/rel/amoc/bin/amoc console`.
to start the node run `_build/default/rel/amoc/bin/amoc console`.

Start `my_scenario` spawning 10 amoc users with IDs from range (1,10) inclusive.
```erlang
Expand Down
6 changes: 4 additions & 2 deletions guides/scenario.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ It has to export two callback functions:
- `init/0` - called only once per test run on every node, at the very beginning.
It can be used for setting up initial (global) state: metrics, database connections, etc.
It can return an `ok` or a tuple `{ok, State}` from which the `State` may be passed to every user.
- `start/1` or `start/2` - describes the actual scenario and is executed for
- `start/1` or `start/2` - implements the actual scenario and is executed for
each user, in the context of that user's process.
The first argument is the given user's unique integer id.
The second, which is optional, is the state, as returned from the `init/0` function.
When the `start` function returns, it is executed again after some delay (60 seconds by default).

In addition to that, the scenario module can implement a `terminate/0,1` callback;
it has one optional argument – the scenario state, as returned from the `init/0` function.

A typical scenario file will look like this:

Expand Down
19 changes: 15 additions & 4 deletions guides/telemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,20 @@ metadata: #{type => add | remove}

## Throttle

### Init

Raised when a throttle mechanism is initialised.

```erlang
event_name: [amoc, throttle, init]
measurements: #{count => 1}
metadata: #{name => atom()}
```

### Rate

Raised when a throttle mechanism is initialised or its configured rate is changed.
This event is raised only on the master node.

```erlang
event_name: [amoc, throttle, rate]
Expand Down Expand Up @@ -50,13 +61,13 @@ measurements: #{count => 1}
metadata: #{name => atom()}
```

## Coordinate
## Coordinator

Indicates when a coordinating event was raised, like a callback index being reached or a timeout being triggered
Indicates when a coordinating event was raised, like a process being added for coordination or a timeout being triggered

### Event
```erlang
event_name: [amoc, coordinator, event]
event_name: [amoc, coordinator, start | stop | add | reset | timeout]
measurements: #{count => 1}
metadata: #{type => atom()}
metadata: #{name => atom()}
```
61 changes: 29 additions & 32 deletions integration_test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,33 @@ In the Amoc repo root directory run:

`./integration_test/build_docker_image.sh`

This command builds docker image `amoc:latest`.
This command builds the `amoc:latest` docker image.

### 2. Start amoc demo cluster
### 2. Start amoc test cluster

`./integration_test/start_demo_cluster.sh`
`./integration_test/start_test_cluster.sh`

More information about the demo cluster can be found further in this document.
This command requires the `amoc:latest` docker image to exist. More information about the test cluster can be found further in this document.

### 3. Check the amoc clustering is done properly
### 3. Check that clustering is done properly

`./integration_test/test_amoc_cluster.sh`

This command verifies that clustering is done properly and metrics are reported
from all the nodes.
This command verifies that clustering is done properly.

### 4. Test installation of user scenario in amoc cluster
### 4. Test distribution of a custom scenario in amoc cluster

`./integration_test/test_distribute_scenario.sh`

This command uploads a sample `dummy_scenario.erl` on the `amoc-master` node
using `curl` and after uploading verifies that scenario is propagated to the
worker nodes.

### 5. Run the uploaded scenario.
This command checks distribution of the sample `dummy_scenario.erl` from the `amoc-master` node
to the worker nodes.

### 5. Run the distributed scenario.

`./integration_test/test_run_scenario.sh`

This command starts execution of `dummy_scenario.erl` scenario (must be uploaded
before this action)
This command starts execution of `dummy_scenario.erl` scenario (it must be distributed
prior to this action)

### 6. Add additional node to the cluster

Expand All @@ -49,32 +47,31 @@ when the new amoc node joins.

### 7. Cleanup

To stop Amoc demo cluster run:
To stop Amoc test cluster run:

`./integration_test/stop_demo_cluster.sh`
`./integration_test/stop_test_cluster.sh`

## Demo cluster
## Test cluster

To start the demo cluster you can run these commands:
* To start the test cluster you can run these commands:

```
./integration_test/build_docker_image.sh
./integration_test/start_demo_cluster.sh
./integration_test/start_test_cluster.sh
```

When the demo cluster is up and running you can access its
different components using the following addresses:
* Amoc Swagger UI:
* [amoc-master](http://localhost:4000/api-docs/)
* [amoc-worker-1](http://localhost:4001/api-docs/)
* [amoc-worker-2](http://localhost:4002/api-docs/)
* [graphite](http://localhost:8080/) web interface
* [grafana](http://localhost:3000/) - default username and password is `admin`/`admin`
* To get the list of nodes in the amoc test cluster use the following command:

`docker compose -p "amoc-test-cluster" ps`

* To check the most recent `amoc-master` logs you can run this command:

`docker compose -p "amoc-test-cluster" logs --tail=100 amoc-master`

To check the most recent `amoc-master` logs you can run this command:
* In order to attach to the `amoc-master` erlang node run the following command:

`docker-compose -p "amoc-demo-cluster" logs --tail=100 amoc-master`
`docker compose -p "amoc-test-cluster" exec amoc-master amoc remote_console`

To attach to `amoc-master` node use the following command:
* To open a shell inside the `amoc-master` container use this command:

`docker-compose -p "amoc-demo-cluster" exec amoc-master /home/amoc/amoc/bin/amoc remote_console`
`docker compose -p "amoc-test-cluster" exec amoc-master bash`
6 changes: 5 additions & 1 deletion integration_test/build_docker_image.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,8 @@ cd "$git_root"
otp_vsn="${OTP_RELEASE:-25.3}"
echo "ERLANG/OTP '${otp_vsn}'"

docker_compose build --build-arg otp_vsn=${otp_vsn}
docker build \
-f Dockerfile \
-t amoc:latest \
--build-arg "otp_vsn=${otp_vsn}" \
.
7 changes: 3 additions & 4 deletions integration_test/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
x-amoc-defaults: &amoc-defaults
build:
context: ../
dockerfile: Dockerfile
image: "amoc:latest"
networks:
- amoc-test-network
volumes:
Expand All @@ -10,14 +8,15 @@ x-amoc-defaults: &amoc-defaults
target: /extra_code_paths
environment:
AMOC_NODES: "['amoc@amoc-master']"
AMOC_EXTRA_CODE_PATHS: '["/extra_code_paths/test1", "/extra_code_paths/test2"]'
healthcheck:
test: "amoc status"

services:
amoc-master:
<<: *amoc-defaults
hostname: "amoc-master"
environment:
AMOC_EXTRA_CODE_PATHS: '["/extra_code_paths/path1", "/extra_code_paths/path2"]'
amoc-worker-1: &amoc-worker
<<: *amoc-defaults
hostname: "amoc-worker-1"
Expand Down
File renamed without changes.
File renamed without changes.
10 changes: 0 additions & 10 deletions integration_test/extra_code_paths/test1/.gitignore

This file was deleted.

10 changes: 0 additions & 10 deletions integration_test/extra_code_paths/test2/.gitignore

This file was deleted.

Loading

0 comments on commit bc377e7

Please sign in to comment.