Skip to content
This repository was archived by the owner on Mar 27, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4d01277
Fix wrong type format
Jul 10, 2020
f8ceafd
Add .devcontainer to gitignore
Jul 13, 2020
2a29eee
Adjust import of mqtt
Jul 15, 2020
071c91b
Add SendInfo function with configurable arguments
Jul 15, 2020
8c7b995
Add first functional test about docker ps command
Jul 16, 2020
99d1df3
Update date in copyright
Jul 16, 2020
381a762
Add MqttSendAndReceiveTimeout test helper
Jul 17, 2020
8fca4f5
Update mod
Jul 17, 2020
a336037
Refactor test TestDockerPsApi
Jul 17, 2020
a4dfd77
Add check for bash command "docker ps -a"
Jul 17, 2020
4faa990
Add Close funtion for mqtt client test
Jul 20, 2020
65c3249
Add doc about functional tests
Jul 20, 2020
9787b05
Add first draft of pipeline with docker image
Jul 16, 2020
80da49f
Add step to run tests
Jul 20, 2020
e917de8
Add a trap to func tests script to kill mqtt broker
Jul 20, 2020
28c7c2e
Give MQTT broker some time to answer test request
Jul 20, 2020
229967d
Mark MqttSendAndReceiveTimeout as test helper function
Jul 20, 2020
1277820
Try to remove sleep before starting func tests
Jul 20, 2020
f5f722d
Fix docker arguments in func test
Jul 20, 2020
ef60ad0
Add check on return error of func SendInfo
Jul 21, 2020
c82aafb
Refactor ContainersPsEvent using pertinence topic
Jul 21, 2020
7c6dfc9
Refactor using topicPertinence info when subscribe
Jul 21, 2020
1cd2ab7
Change TestDockerPsApi checking real docker ps -a and mqtt msg
Jul 23, 2020
70f3128
Trim JSON reponse from MQTT broker to check returned data
Jul 23, 2020
6f09c15
Add assert to check every id of container in machine
Jul 23, 2020
43f8203
Improve docs fixing english
Jul 23, 2020
ea9a3b0
Redirect to null the output of mosquitto running script for tests
Jul 23, 2020
0169027
Rename for readability
Jul 23, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: tests

on: [push, pull_request]

jobs:
CI:
name: tests
runs-on: ubuntu-latest
container: guerra1994/go-mqtt-docker-env

steps:
- name: check out code
uses: actions/checkout@v2

- name: download mod
run: go mod download

- name: run functional tests
run: ./scripts/functional-tests.sh
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,7 @@ arduino-connector-arm
.idea
setup_host_test_env.sh

# End
### Remote vscode container
.devcontainer/*

# End
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ go get github.com/sanbornm/go-selfupdate

See [API](./API.md)

## Functional tests

This type of tests are executed all locally and you need to configure a dedicated docker container:
- `docker pull guerra1994/go-mqtt-docker-env`
- follow the steps [here](https://hub.docker.com/r/guerra1994/go-mqtt-docker-env) to run it
- run mosquitto broker in background mode using `mosquitto &`
- then run your test, for example `go test -v --run="TestDockerPsApi"`


## Integration tests disclaimer

You will see in the following paragraphs that the testing environment and procedures are strictly coupled with the
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ require (
github.com/shirou/gopsutil v2.17.13-0.20180801053943-8048a2e9c577+incompatible
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect
github.com/sirupsen/logrus v1.1.0 // indirect
github.com/stretchr/testify v1.2.2
github.com/stretchr/testify v1.3.0
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
golang.org/x/net v0.0.0-20190311183353-d8887717615a
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/containerd/continuity v0.0.0-20181003075958-be9bd761db19 h1:HSgjWPBWohO3kHDPwCPUGSLqJjXCjA7ad5057beR2ZU=
github.com/containerd/continuity v0.0.0-20181003075958-be9bd761db19/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/cli v0.0.0-20180905184309-44371c7c34d5 h1:FGNHsOn20/i4y8Ck+qQ8rXSN9j7IuBhqcMC+HGpbTHE=
Expand Down Expand Up @@ -121,8 +122,12 @@ github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 h1:udFKJ0aHUL60LboW/A+D
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
github.com/sirupsen/logrus v1.1.0 h1:65VZabgUiV9ktjGM5nTq0+YurgTyX+YI2lSSfDjI+qU=
github.com/sirupsen/logrus v1.1.0/go.mod h1:zrgwTnHtNr00buQ1vSptGe8m1f/BbgsPukg8qsT7A+A=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
Expand Down
2 changes: 1 addition & 1 deletion handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func (status *Status) UploadEvent(client mqtt.Client, msg mqtt.Message) {
if _, err = os.Stat(sketchPath); !os.IsNotExist(err) {
err = os.Remove(sketchPath)
if err != nil {
status.Error("/upload", errors.Wrapf(err, "remove %d", sketch.Name))
status.Error("/upload", errors.Wrapf(err, "remove %s", sketch.Name))
return
}
}
Expand Down
12 changes: 7 additions & 5 deletions handlers_containers.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//
// This file is part of arduino-connector
//
// Copyright (C) 2017-2018 Arduino AG (http://www.arduino.cc/)
// Copyright (C) 2017-2020 Arduino AG (http://www.arduino.cc/)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -37,7 +37,7 @@ import (
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/network"
docker "github.com/docker/docker/client"
"github.com/eclipse/paho.mqtt.golang"
mqtt "github.com/eclipse/paho.mqtt.golang"
"github.com/pkg/errors"
"github.com/shirou/gopsutil/host"
"golang.org/x/net/context"
Expand Down Expand Up @@ -71,7 +71,7 @@ type ChangeNamePayload struct {
ContainerName string `json:"name"`
}

// ContainersPsEvent implements docker ps -a
// ContainersPsEvent send info about "docker ps -a" command
func (s *Status) ContainersPsEvent(client mqtt.Client, msg mqtt.Message) {
psPayload := PsPayload{}
err := json.Unmarshal(msg.Payload(), &psPayload)
Expand All @@ -91,13 +91,15 @@ func (s *Status) ContainersPsEvent(client mqtt.Client, msg mqtt.Message) {
return
}

// Send result
data, err := json.Marshal(containers)
if err != nil {
s.Error("/containers/ps", fmt.Errorf("Json marsahl result: %s", err))
return
}
s.Info("/containers/ps", string(data)+"\n")

if !s.SendInfo(s.topicPertinence+"/containers/ps", string(data)+"\n") {
fmt.Println("error sending info")
}
}

// ContainersListImagesEvent implements docker images
Expand Down
51 changes: 49 additions & 2 deletions handlers_containers_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//
// This file is part of arduino-connector
//
// Copyright (C) 2017-2018 Arduino AG (http://www.arduino.cc/)
// Copyright (C) 2017-2020 Arduino AG (http://www.arduino.cc/)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -22,16 +22,63 @@ import (
"encoding/json"
"fmt"
"os"
"os/exec"
"strings"
"testing"
"time"

"github.com/docker/docker/api/types"
docker "github.com/docker/docker/client"
mqtt "github.com/eclipse/paho.mqtt.golang"

"github.com/stretchr/testify/assert"
)

// tests
func TestDockerPsApi(t *testing.T) {
ui := NewMqttTestClientLocal()
defer ui.Close()

status := NewStatus(program{}.Config, nil, nil, "")
status.dockerClient, _ = docker.NewClientWithOpts(docker.WithVersion("1.38"))
acOptions := mqtt.NewClientOptions().AddBroker("tcp://localhost:1883").SetClientID("arduino-connector")
status.mqttClient = mqtt.NewClient(acOptions)

if token := status.mqttClient.Connect(); token.Wait() && token.Error() != nil {
t.Fatal(token.Error())
}

subscribeTopic(status.mqttClient, "0", "/containers/ps/post", status, status.ContainersPsEvent, false)

resp := ui.MqttSendAndReceiveTimeout(t, "/containers/ps", "{}", 50*time.Millisecond)

// ask Docker about containers effectively running
cmd := exec.Command("bash", "-c", "docker ps -a")
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatal(err)
}

lines := strings.Split(string(out), "\n")
// Remove the first line (command output header) and the last line (empty line)
lines = lines[1 : len(lines)-1]

// Take json without INFO tag
resp = strings.TrimPrefix(resp, "INFO: ")
resp = strings.TrimSuffix(resp, "\n\n")
var result []types.Container
if err := json.Unmarshal([]byte(resp), &result); err != nil {
t.Fatal(err)
}

assert.Equal(t, len(result), len(lines))
for i, line := range lines {
containerId := strings.Fields(line)[0]
assert.True(t, strings.HasPrefix(result[i].ID, containerId))
}

status.mqttClient.Disconnect(100)
}

func TestConnectorProcessIsRunning(t *testing.T) {
outputMessage, err := ExecAsVagrantSshCmd("systemctl status ArduinoConnector | grep running")
if err != nil {
Expand Down
46 changes: 44 additions & 2 deletions handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,8 @@ import (
"testing"
"time"

mqtt "github.com/eclipse/paho.mqtt.golang"
"github.com/stretchr/testify/assert"

"github.com/eclipse/paho.mqtt.golang"
)

// ExecAsVagrantSshCmd "wraps vagrant ssh -c
Expand All @@ -52,6 +51,19 @@ type MqttTestClient struct {
thingToTestId string
}

func NewMqttTestClientLocal() *MqttTestClient {
uiOptions := mqtt.NewClientOptions().AddBroker("tcp://localhost:1883").SetClientID("UI")
ui := mqtt.NewClient(uiOptions)
if token := ui.Connect(); token.Wait() && token.Error() != nil {
panic(token.Error())
}

return &MqttTestClient{
ui,
"",
}
}

func NewMqttTestClient() *MqttTestClient {
cert := "test/cert.pem"
key := "test/privateKey.pem"
Expand Down Expand Up @@ -105,6 +117,36 @@ func (tmc *MqttTestClient) Close() {
tmc.client.Disconnect(100)
}

func (tmc *MqttTestClient) MqttSendAndReceiveTimeout(t *testing.T, topic, request string, timeout time.Duration) string {
t.Helper()

respChan := make(chan string)
if token := tmc.client.Subscribe(topic, 0, func(client mqtt.Client, msg mqtt.Message) {
respChan <- string(msg.Payload())
}); token.Wait() && token.Error() != nil {
t.Fatal(token.Error())
}

postTopic := strings.Join([]string{topic, "post"}, "/")
if token := tmc.client.Publish(postTopic, 0, false, request); token.Wait() && token.Error() != nil {
t.Fatal(token.Error())
}

select {
case <-time.After(timeout):
if token := tmc.client.Unsubscribe(topic); token.Wait() && token.Error() != nil {
t.Fatal(token.Error())
}
close(respChan)

t.Fatalf("MqttSendAndReceiveTimeout() timeout for topic %s", topic)

return ""
case resp := <-respChan:
return resp
}
}

func (tmc *MqttTestClient) MqttSendAndReceiveSync(t *testing.T, topic, request string) string {

iotTopic := strings.Join([]string{"$aws/things", tmc.thingToTestId, topic}, "/")
Expand Down
19 changes: 12 additions & 7 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//
// This file is part of arduino-connector
//
// Copyright (C) 2017-2018 Arduino AG (http://www.arduino.cc/)
// Copyright (C) 2017-2020 Arduino AG (http://www.arduino.cc/)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -215,7 +215,7 @@ func (p program) run() {
}

// Create global status
status := NewStatus(p.Config, nil, nil)
status := NewStatus(p.Config, nil, nil, "$aws/things/"+p.Config.ID)
status.Update(p.Config)

// Setup MQTT connection
Expand Down Expand Up @@ -330,25 +330,30 @@ func subscribeTopics(mqttClient mqtt.Client, id string, status *Status) {
subscribeTopic(mqttClient, id, "/containers/rename/post", status, status.ContainersRenameEvent, true)
}

func subscribeTopic(mqttClient mqtt.Client, id, topic string, status *Status, statusHandler mqtt.MessageHandler, isWriteFsRequiredForTopic bool) {
func subscribeTopic(client mqtt.Client, id, topic string, s *Status, statusHandler mqtt.MessageHandler, isWriteFsRequiredForTopic bool) {
handler := statusHandler

if status.config.CheckRoFs && isWriteFsRequiredForTopic {
if s.config.CheckRoFs && isWriteFsRequiredForTopic {
handler = func(client mqtt.Client, msg mqtt.Message) {
mountRootFilesystemRw()
statusHandler(client, msg)
mountRootFilesystemRo()
}
}

completeTopic := s.topicPertinence + topic
if debugMqtt {
debugHandler := func(client mqtt.Client, msg mqtt.Message) {
fmt.Println("MQTT IN:", string(msg.Topic()), string(msg.Payload()))
handler(client, msg)
}
mqttClient.Subscribe("$aws/things/"+id+topic, 1, debugHandler)

if token := client.Subscribe(completeTopic, 1, debugHandler); token.Wait() && token.Error() != nil {
fmt.Println(token.Error())
}
} else {
mqttClient.Subscribe("$aws/things/"+id+topic, 1, handler)
if token := client.Subscribe(completeTopic, 1, handler); token.Wait() && token.Error() != nil {
fmt.Println(token.Error())
}
}
}

Expand Down
8 changes: 8 additions & 0 deletions scripts/functional-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env bash

set -euo pipefail

trap 'kill "$(pidof mosquitto)"' EXIT

mosquitto > /dev/null &
go test -v --run="TestDockerPsApi"
Loading