diff --git a/.artifactignore b/.artifactignore new file mode 100644 index 0000000..035d9a4 --- /dev/null +++ b/.artifactignore @@ -0,0 +1,5 @@ +.git +.idea +.DS_Store +.artifactignore +.gitignore diff --git a/.gitignore b/.gitignore index 3aa564d..497bddb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.DS_Store # Created by https://www.gitignore.io/api/node,intellij # Edit at https://www.gitignore.io/?templates=node,intellij @@ -165,6 +166,3 @@ typings/ .dynamodb/ # End of https://www.gitignore.io/api/node,intellij - -.DS_Store -/iofog-agent/id_ecdsa* diff --git a/README.md b/README.md index 2c2b073..732fc83 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,164 @@ # ioFog Demo -This repository orchestrates ioFog Agent, Controller, and Connector in a Docker Compose environment for the purpose of demonstrating a deployment of the ioFog stack. +This repository demonstrates the capabilities of ioFog. It spins up the ioFog stack (Agent, Controller, and Connector) on a local machine in a Docker Compose environment. This basic ioFog stack setup constitutes a small, but fully configured Edge Compute Network (ECN). -# Usage +Optionally, this demo also creates a sample application (microservices deployed on the ioFog stack). -To spin up Agent, Controller, and Connector containers within a virtual network: -``` +This demo repository is used as supplementary materials for [ioFog quickstart](https://iofog.org/docs/1.0.0/getting-started/core-concepts.html) and [ioFog tutorial](https://iofog.org/docs/1.0.0/tutorial/introduction.html) guides. + + +# Prerequisites + +The ioFog demo requires one of the following systems and tools to be installed. The scripts in this demo do not install any of these tools, but they check for sufficient versions. + +Supported operating systems: + +* Linux (kernel v3.10+) +* macOS 10.12+ +* Windows 7+ + +Requires tools: + +* Docker 1.10+ ([installation instructions](https://docs.docker.com/install/)) +* Docker-compose 1.22+ ([installation instructions](https://docs.docker.com/compose/install/)) + + +# Try ioFog - Simple Edge Compute Network + +The main interaction with this demo repository is encapsulated into a set of simple scripts: `start.sh`, `test.sh` and `stop.sh`. Interactions with the ioFog components can be done using a command line interface available in all the services of the stack, or using a REST API. + +## Spin Up The ioFog Stack + +Spin up the blank ioFog stack (Agent, Controller, and Connector) on the local machine. + +```sh ./start.sh ``` -To verify the services are provisioned correctly: -``` +Verify the iofog stack is provisioned correctly. The automated tests run a smoke test suite on the blank ioFog stack, testing basic operations. + +```sh ./test.sh ``` -To interact with Agent, Controller, and Connector you must exec into each respective container and use the CLI: +You can also verify manually that the ioFog stack containers are correctly started. +```sh +docker ps --filter "name=iofog" ``` -docker exec -it iofog-controller /bin/bash -iofog-controller help + +When you are finished, tear down the ioFog stack and all services deployed on it. + +```sh +./stop.sh ``` -Note that if you would like to use Agent, Controller, and Connector's REST APIs, you will have to modify the Compose environment to deploy with network_mode "host". +## Build from local packages + +If you have a local version of the Agent, Controller and Connector, you can chose to build the containers using those local packages. +To do so, you will need a debian package (`.deb`) for the Agent and the Connector and a tarball (`.tgz`) for the Controller. +You can provide `start.sh` with an option for each local package you want to use. -When you are finished, it is recommended that you teardown: +### Example +Folder structure: +```text +* services +* init +* test +* local-packages # Example folder where you would store your local packages + - iofog-agent_2.0.deb + - iofog-connector_2.0.deb + - iofog-controller_2.0.tgz +* start.sh +* stop.sh +* ... ``` + +Command: +```sh +./start.sh -a ./local-packages/iofog-agent_2.0.deb -cn ./local-packages/iofog-connector_2.0.deb -ct ./local-packages/iofog-controller_2.0.tgz +``` + +## Force rebuild +If you have previously built the containers using local packages, or remote packages and you want to ensure that running `start.sh` will rebuild the images, you can also provide the `--no-cache` option + + +```sh +./start.sh --no-cache +``` + +## ECN Status + +```sh +./status.sh # Will show you all iofog-related containers currently running. +``` + +## Interacting With The ioFog Stack - CLI + +The simplest way to interact with Agent, Controller, and Connector deployed on a machine you have access to is to use the command line interface. The main interaction point for users is the Controller. + +```sh +docker exec -it iofog-controller iofog-controller help +``` +For the purpose of this demo, all ioFog components are spun up in separate Docker containers. The Controller's container is called `iofog-controller` (the first occurrence in the above command) and the executable inside the container is also called `iofog-controller` (the second occurrence).)_ + +Names for all the containers created in the demo are `iofog-agent`, `iofog-controller` and `iofog-connector`. + +The initialization scripts used to setup the ioFog stack / ECN are using the CLI interface. Feel free to refer to these for more inspiration. + +Full reference of the CLI for all ioFog stack components is available at the ioFog website: + +* https://iofog.org/docs/1.0.0/controllers/cli-usage.html +* https://iofog.org/docs/1.0.0/agents/cli-usage.html +* https://iofog.org/docs/1.0.0/connectors/cli-usage.html + +## Interacting With The ioFog Stack - REST API + + +Full reference of the REST API for all ioFog stack components is available at the ioFog website: + +* https://iofog.org/docs/1.0.0/controllers/rest-api.html +* https://iofog.org/docs/1.0.0/agents/local-api.html +* https://iofog.org/docs/1.0.0/connectors/api-reference.html + +You can try using the REST API directly on your machine with the ioFog stack running. +```sh +curl --url 'http://0.0.0.0:51121/api/v3/status' +``` + + +# Try ioFog - Tutorial Application Deployed On ioFog + +Apart from creating just the ioFog stack, we can also deploy an ioFog application on the stack. Here we demonstrate it on the tutorial application from the ioFog website. + +First, create all services for a tutorial ioFog application. You don't have to start the iofog stack manually, it will be created if necessary. + +```sh +./start.sh tutorial +``` + +When you are done with the tutorial, you can tear down the sample application together with the ioFog stack. Note there is currently no wya in the demo to tear down just the tutorial application. +```sh ./stop.sh -``` \ No newline at end of file +``` + +# Structure Of This Repository +```text +* services # Service Dockerfiles and customization files + - iofog + + iofog-agent # Agent service files - part of the iofog stack + + iofog-connector # Connector service files - part of the iofog stack + + iofog-controller # Controller service files - part of the iofog stack +* init + - iofog # plain ioFog stack initialization service + - tutorial # tutorial initialization service +* test + + conf # generated test configuration files +* azure-pipelines.yml +* docker-compose-iofog.yml +* docker-compose-tutorial.yml +* docker-compose-test.yml +* start.sh +* stop.sh +* test.sh +* utils.sh +``` diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0caa70e..23bf058 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,8 +1,9 @@ trigger: -- master -- develop -- blank-environment -- demo-environment + - master + - develop +pr: + - master + - develop pool: vmImage: 'ubuntu-16.04' @@ -19,11 +20,6 @@ steps: echo $(gcp.svcacc) | docker login -u _json_key --password-stdin https://gcr.io displayName: 'Docker connect to Registry' -- script: | - sed -i "s|image:.*|image: $(images.runner)|" docker-compose-test.yml - cat docker-compose-test.yml - displayName: 'Configure Test Runner image' - - script: | ./start.sh displayName: 'Start Connector, Controller, and Agent' @@ -34,4 +30,18 @@ steps: - script: | ./stop.sh - displayName: 'Stop Connector, Controller, and Agent' \ No newline at end of file + displayName: 'Stop Connector, Controller, and Agent' + +- script: | + tar -c --transform 's,^\.,demo,' --exclude-from=.artifactignore -v --bzip2 -f $BUILD_ARTIFACTSTAGINGDIRECTORY/demo.tar.bz2 . + +- task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: '$(Build.ArtifactStagingDirectory)/demo.tar.bz2' + artifactName: 'demo.$(Build.BuildId).tar.bz2' + +- script: | + echo "===== IOFOG AGENT LOG =====" + docker exec iofog-agent cat /var/log/agent.out.log + displayName: 'Print logs' + condition: failed() diff --git a/conf/.gitignore b/conf/.gitignore deleted file mode 100644 index 86d0cb2..0000000 --- a/conf/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Ignore everything in this directory -* -# Except this file -!.gitignore \ No newline at end of file diff --git a/docker-compose-iofog.yml b/docker-compose-iofog.yml new file mode 100644 index 0000000..5d58dd2 --- /dev/null +++ b/docker-compose-iofog.yml @@ -0,0 +1,60 @@ +version: "3" +services: + iofog-connector: + image: iofog-connector:local + build: + context: ./services/iofog/iofog-connector + args: + - LOCAL_CONNECTOR_PACKAGE + container_name: iofog-connector + ports: + - "53321:8080" + network_mode: bridge + + iofog-controller: + image: iofog-controller:local + build: + context: ./services/iofog/iofog-controller + args: + - LOCAL_CONTROLLER_PACKAGE + container_name: iofog-controller + depends_on: + - iofog-connector + ports: + - "51121:51121" + environment: + - NODE_ENV=development + links: + - iofog-connector:iofog-connector + network_mode: bridge + + iofog-agent: + image: iofog-agent:local + build: + context: ./services/iofog/iofog-agent + args: + - LOCAL_AGENT_PACKAGE + depends_on: + - iofog-controller + privileged: true + volumes: + - /var/run/docker.sock:/var/run/docker.sock + container_name: iofog-agent + ports: + - "8081:22" + - "54321:54321" + links: + - iofog-controller:iofog-controller + network_mode: bridge + + iofog-init: + build: ./init/iofog + container_name: iofog-init + depends_on: + - iofog-controller + volumes: + - /var/run/docker.sock:/var/run/docker.sock + links: + - iofog-controller:iofog-controller + - iofog-connector:iofog-connector + network_mode: bridge diff --git a/docker-compose-test.yml b/docker-compose-test.yml index bcde80b..02650d4 100644 --- a/docker-compose-test.yml +++ b/docker-compose-test.yml @@ -1,15 +1,11 @@ -services: - test-runner: - container_name: test-runner - environment: - - LOCAL=1 - image: iofog/test-runner:latest - networks: - - iofog - volumes: - - ./conf:/conf version: '3' -volumes: - conf: null -networks: - iofog: null \ No newline at end of file +services: + # Test Harness image + test-runner: + container_name: test-runner + environment: + - LOCAL=1 + image: iofog/test-runner:latest + volumes: + - ./test/conf:/conf + network_mode: bridge diff --git a/docker-compose-tutorial.yml b/docker-compose-tutorial.yml new file mode 100644 index 0000000..36755dd --- /dev/null +++ b/docker-compose-tutorial.yml @@ -0,0 +1,12 @@ +version: "3" +services: + tutorial-init: + build: ./init/tutorial + container_name: tutorial-init + depends_on: + - iofog-controller + volumes: + - /var/run/docker.sock:/var/run/docker.sock + links: + - iofog-controller:iofog-controller + network_mode: bridge diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 727d79c..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,39 +0,0 @@ -version: "3" -services: - # ioFog Connector - iofog-connector: - build: ./iofog-connector - container_name: iofog-connector - ports: - - "53321:8080" - networks: - - iofog - # ioFog Controller, depends on Connector - iofog-controller: - build: ./iofog-controller - container_name: iofog-controller - depends_on: - - iofog-connector - ports: - - "51121:51121" - environment: - - NODE_ENV=development - networks: - - iofog - # ioFog Agent, depends on Connector and Controller - iofog-agent: - build: ./iofog-agent - container_name: iofog-agent - depends_on: - - iofog-controller - ports: - - "54321:54321" - - "8081:22" - privileged: true - volumes: - - /var/run/docker.sock:/var/run/docker.sock - networks: - - iofog - -networks: - iofog: \ No newline at end of file diff --git a/init/iofog/Dockerfile b/init/iofog/Dockerfile new file mode 100644 index 0000000..9449a73 --- /dev/null +++ b/init/iofog/Dockerfile @@ -0,0 +1,7 @@ +FROM docker:dind + +RUN apk add --no-cache curl jq + +ADD ./provision.sh / + +CMD ["sh", "-c", "/provision.sh"] diff --git a/init/iofog/provision.sh b/init/iofog/provision.sh new file mode 100755 index 0000000..d431a66 --- /dev/null +++ b/init/iofog/provision.sh @@ -0,0 +1,143 @@ +#!/usr/bin/env sh + +set -o errexit -o noclobber -o nounset +cd "$(dirname "$0")" + +function waitForController() { + while true; do + STATUS=$(curl --request GET --url "${CONTROLLER_HOST}/status" 2>/dev/null | jq -r ".status") + [[ "${STATUS}" == "online" ]] && break || echo "Waiting for Controller..." + sleep 2 + done +} + +function waitForAgent() { + while true; do + STATUS=$(docker exec iofog-agent iofog-agent status | awk -F': ' '/ioFog daemon/{print $2}') + [[ "${STATUS}" == "RUNNING" ]] && break || echo "Waiting for Agent..." + sleep 2 + done +} + +function waitForConnector() { + while true; do + STATUS=$(curl --request POST --url "${CONNECTOR_HOST}/status" \ + --header 'Content-Type: application/x-www-form-urlencoded' --data mappingid=all 2>/dev/null \ + | jq -r '.status') + [[ "${STATUS}" == "running" ]] && break || echo "Waiting for Connector..." + sleep 2 + done +} + +function createUser() { + echo -n "Creating user John Doe... " + local RET=$(curl --request POST --url "${CONTROLLER_HOST}/user/signup" \ + --header 'Content-Type: application/json' \ + --data '{"email":"user@domain.com","firstName":"John","lastName":"Doe","password":"#Bugs4Fun"}' \ + 2>/dev/null) + local RET_MSG=$(echo $RET | jq -r '.message') + local RET_UID=$(echo $RET | jq -r '.userId') + [[ "${RET_MSG}" == "null" ]] && echo "${RET_UID}" || echo "${RET_MSG}" +} + +function login() { + echo -n "Logging in as user user@domain.com... " + TOKEN=$(curl --request POST --url "${CONTROLLER_HOST}/user/login" \ + --header 'Content-Type: application/json' \ + --data '{"email":"user@domain.com","password":"#Bugs4Fun"}' 2>/dev/null \ + | jq -r '.accessToken') + echo "${TOKEN}" +} + +function createDefaultFog() { + # Delete all fogs with the same default fog name. This is in case the script is run repeatedly. + local DEFAULT_FOGS=$(curl --request GET --url "${CONTROLLER_HOST}/iofog-list" \ + --header "Authorization: ${TOKEN}" \ + --header 'Content-Type: application/json' 2>/dev/null \ + | jq -r ".fogs[] | select(.name == \"${DEFAULT_FOG}\") | .uuid") + for FOG_UUID in ${DEFAULT_FOGS}; do + echo "Deleting pre-existing default fog: ${FOG_UUID}..." + curl --request DELETE --url "${CONTROLLER_HOST}/iofog/${FOG_UUID}" \ + --header "Authorization: ${TOKEN}" --header 'Content-Type: application/json' 2>/dev/null + done + + # Wait for all the deleted fogs to be actually delete. + while true; do + DEFAULT_FOGS=$(curl --request GET --url "${CONTROLLER_HOST}/iofog-list" \ + --header "Authorization: ${TOKEN}" \ + --header 'Content-Type: application/json' 2>/dev/null \ + | jq -r ".fogs[] | select(.name == \"${DEFAULT_FOG}\") | .uuid") + [[ -z "${DEFAULT_FOGS}" ]] && break || echo "Waiting for pre-existing default fogs to be deleted..." + sleep 2 + done + + echo -n "Creating default fog... " + curl --request POST --url "${CONTROLLER_HOST}/iofog" \ + --header "Authorization: ${TOKEN}" --header 'Content-Type: application/json' \ + --data "{\"name\": \"${DEFAULT_FOG}\",\"fogType\": 1}" 2> /dev/null 1>&2 + echo "${DEFAULT_FOG}" + + echo -n 'Retrieving UUID of default fog... ' + FOG_UUID=$(curl --request GET --url "${CONTROLLER_HOST}/iofog-list" \ + --header "Authorization: ${TOKEN}" --header 'Content-Type: application/json' 2> /dev/null \ + | jq -r ".fogs[] | select(.name == \"${DEFAULT_FOG}\") | .uuid") + echo "${FOG_UUID}" +} + +function configureAgent() { + echo "Configuring Agent..." + docker exec iofog-agent iofog-agent config -idc off > /dev/null + docker exec iofog-agent iofog-agent config -a "${CONTROLLER_HOST}" > /dev/null +} + +function configureController() { + echo "Configuring Controller..." + CONNECTOR_IP=$(getent hosts iofog-connector | awk '{ print $1 }') + # TODO this is the only "docker exec" for controller - needs to be replaced with REST API + docker exec iofog-controller \ + iofog-controller connector add -n iofog-connector -d "${CONNECTOR_IP}" -i "${CONNECTOR_IP}" -H +} + +function provisionAgent() { + echo -n "Retrieving provisioning key for default fog... " + PROVISION_KEY=$(curl --request GET --url "${CONTROLLER_HOST}/iofog/${FOG_UUID}/provisioning-key" \ + --header "Authorization: ${TOKEN}" --header 'Content-Type: application/json' 2> /dev/null \ + | jq -r .key) + echo "${PROVISION_KEY}" + + docker exec iofog-agent iofog-agent provision "${PROVISION_KEY}" +} + +function createDefaultFlow() { + echo -n "Creating default flow... " + curl --request POST --url "${CONTROLLER_HOST}/flow" \ + --header "Authorization: ${TOKEN}" --header 'Content-Type: application/json' \ + --data "{\"name\": \"${DEFAULT_FLOW}\",\"isActivated\":true}" 2> /dev/null 1>&2 + + # Retrieve just created flow's id + FLOW_ID=$(curl --request GET --url "${CONTROLLER_HOST}/flow" \ + --header "Authorization: ${TOKEN}" --header 'Content-Type: application/json' 2> /dev/null \ + | jq -r ".flows[] | select(.name == \"${DEFAULT_FLOW}\") | .id") + echo "${FLOW_ID}" +} + +echo "Initializing ioFog stack. This may take a minute or two." + +docker --version # Check if docker is available. Needs docker socket mapped in order to talk to the Agent +CONTROLLER_HOST="http://iofog-controller:51121/api/v3" +CONNECTOR_HOST="http://iofog-connector:8080/api/v2" +DEFAULT_FOG="ioFog Agent" +DEFAULT_FLOW="Default Flow" + +waitForConnector +waitForController +waitForAgent +configureController +createUser +login +createDefaultFog +configureAgent +provisionAgent +createDefaultFlow + +echo "Successfully initialized ioFog stack." diff --git a/init/tutorial/Dockerfile b/init/tutorial/Dockerfile new file mode 100644 index 0000000..9449a73 --- /dev/null +++ b/init/tutorial/Dockerfile @@ -0,0 +1,7 @@ +FROM docker:dind + +RUN apk add --no-cache curl jq + +ADD ./provision.sh / + +CMD ["sh", "-c", "/provision.sh"] diff --git a/init/tutorial/provision.sh b/init/tutorial/provision.sh new file mode 100755 index 0000000..54d22e6 --- /dev/null +++ b/init/tutorial/provision.sh @@ -0,0 +1,135 @@ +#!/usr/bin/env sh + +# TODO finish tutorial setup +# TODO split --data into multiple lines + +set -o errexit -o noclobber -o nounset +cd "$(dirname "$0")" + +function waitForController() { + while true; do + local STATUS=$(curl --request GET --url "${CONTROLLER_HOST}/status" 2>/dev/null | jq -r ".status") + [[ "${STATUS}" == "online" ]] && break || echo "Waiting for Controller..." + sleep 2 + done +} + +function login() { + echo -n "Logging in as user user@domain.com... " + TOKEN=$(curl --request POST --url "${CONTROLLER_HOST}/user/login" \ + --header 'Content-Type: application/json' \ + --data '{"email":"user@domain.com","password":"#Bugs4Fun"}' 2>/dev/null \ + | jq -r '.accessToken') + echo "${TOKEN}" +} + +function getDemoFogId() { + echo -n "Looking up default fog... " + FOG_ID=$(curl --request GET --url "${CONTROLLER_HOST}/iofog-list" \ + --header "Authorization: ${TOKEN}" \ + --header 'Content-Type: application/json' 2>/dev/null \ + | jq -r ".fogs[] | select(.name == \"${DEFAULT_FOG}\") | .uuid") + echo "${FOG_ID}" +} + +function getDemoFlowId() { + echo -n "Looking up default flow... " + FLOW_ID=$(curl --request GET --url "${CONTROLLER_HOST}/flow" \ + --header "Authorization: ${TOKEN}" \ + --header 'Content-Type: application/json' 2>/dev/null \ + | jq -r ".flows[] | select(.name == \"${DEFAULT_FLOW}\") | .id") + echo "${FLOW_ID}" +} + +function startMicroserviceSensors() { + echo -n 'Registering Sensor microservice in the catalog... ' + SENSORS_CATALOG_ID=$(curl --request POST --url $CONTROLLER_HOST/catalog/microservices \ + --header "Authorization: ${TOKEN}" --header 'Content-Type: application/json' \ + --data '{"name":"Sensors","category": "DEMO","publisher":"Edgeworx","registryId":1,"images":[{"containerImage":"iofog/sensors:latest","fogTypeId":1}]}' \ + 2> /dev/null | jq -r .id) + echo "${SENSORS_CATALOG_ID}" + + echo -n 'Creating Sensor microservice... ' + SENSORS_UUID=$(curl --request POST --url $CONTROLLER_HOST/microservices \ + --header "Authorization: ${TOKEN}" --header 'Content-Type: application/json' \ + --data '{"name":"Sensors","config":"{}","catalogItemId":'"${SENSORS_CATALOG_ID}"',"flowId":'"${FLOW_ID}"',"iofogUuid":"'"${FOG_ID}"'","rootHostAccess":false,"logSize":5,"volumeMappings":[],"ports":[],"routes":[]}' \ + 2> /dev/null | jq -r .uuid) + echo "${SENSORS_UUID}" +} + +function startMicroserviceRestApi() { + echo -n 'Registering REST API microservice in the catalog... ' + RESTAPI_CATALOG_ID=$(curl --request POST --url $CONTROLLER_HOST/catalog/microservices \ + --header "Authorization: ${TOKEN}" --header 'Content-Type: application/json' \ + --data '{"name":"Rest API","category": "DEMO","publisher":"Edgeworx","registryId":1,"images":[{"containerImage":"iofog/freeboard-api:latest","fogTypeId":1}]}' \ + 2> /dev/null | jq -r .id) + echo "${RESTAPI_CATALOG_ID}" + + echo -n 'Creating REST API microservice... ' + RESTAPI_UUID=$(curl --request POST --url $CONTROLLER_HOST/microservices \ + --header "Authorization: ${TOKEN}" --header 'Content-Type: application/json' \ + --data '{"name":"Rest API","config":"{}","catalogItemId":'"${RESTAPI_CATALOG_ID}"',"flowId":'"${FLOW_ID}"',"iofogUuid":"'"${FOG_ID}"'","rootHostAccess":false,"logSize":5,"volumeMappings":[],"ports":[{"internal":80,"external":10101,"publicMode":false}],"routes":[]}' \ + 2> /dev/null | jq -r .uuid) + echo "${RESTAPI_UUID}" +} + +function startMicroserviceFreeboard() { + echo -n 'Registering Freeboard microservice in the catalog... ' + FREEBOARD_CATALOG_ID=$(curl --request POST --url $CONTROLLER_HOST/catalog/microservices \ + --header "Authorization: ${TOKEN}" --header 'Content-Type: application/json' \ + --data '{"name":"freeboard","category": "DEMO","publisher":"Edgeworx","registryId":1,"images":[{"containerImage":"iofog/freeboard:latest","fogTypeId":1}]}' \ + 2> /dev/null | jq -r .id) + echo "${FREEBOARD_CATALOG_ID}" + + echo -n 'Creating Freeboard microservice... ' + FREEBOARD_UUID=$(curl --request POST --url $CONTROLLER_HOST/microservices \ + --header "Authorization: ${TOKEN}" --header 'Content-Type: application/json' \ + --data '{"name":"Freeboard","config":"{}","catalogItemId":'"${FREEBOARD_CATALOG_ID}"',"flowId":'"${FLOW_ID}"',"iofogUuid":"'"${FOG_ID}"'","rootHostAccess":false,"logSize":5,"volumeMappings":[],"ports":[{"internal":80,"external":10102,"publicMode":false}],"routes":[]}' \ + 2> /dev/null | jq -r .uuid) + echo "${FREEBOARD_UUID}" +} + +function routeSensorsToApi() { + echo 'Creating route between Sensors and REST API microservices...' + curl --request POST --url "${CONTROLLER_HOST}/microservices/${SENSORS_UUID}/routes/${RESTAPI_UUID}" \ + --header "Authorization: ${TOKEN}" --header 'Content-Type: application/json' 2> /dev/null +} + +function waitForService() { + local IMAGE="$1" + echo -n "Waiting service $IMAGE..." + while true; do + local STATUS=$(docker inspect -f {{.State.Running}} $(docker ps -q --filter "ancestor=$IMAGE") 2>/dev/null) + [[ "${STATUS}" == "true" ]] && break || echo -n "." + sleep 2 + done + echo " OK" +} + +function waitForAllServices() { + waitForService "iofog/sensors:latest" + waitForService "iofog/freeboard-api:latest" + waitForService "iofog/freeboard:latest" +} + +echo "Initializing tutorial. This may take a minute or two." + +CONTROLLER_HOST="http://iofog-controller:51121/api/v3" +DEFAULT_FOG="ioFog Agent" +DEFAULT_FLOW="Default Flow" + +waitForController +login +getDemoFogId +getDemoFlowId +startMicroserviceSensors +startMicroserviceRestApi +startMicroserviceFreeboard +routeSensorsToApi +waitForAllServices + +echo "Successfully initialized tutorial." + + + + diff --git a/iofog-agent/Dockerfile b/iofog-agent/Dockerfile deleted file mode 100644 index 6fc87c9..0000000 --- a/iofog-agent/Dockerfile +++ /dev/null @@ -1,42 +0,0 @@ -FROM iofog/ubuntu-16.04-java8 as javaSource -FROM jpetazzo/dind - -# Set up Java -COPY --from=javaSource /usr/lib/jvm/java-8-oracle /usr/lib/jvm/java-8-oracle -ENV JAVA_HOME /usr/lib/jvm/java-8-oracle - -RUN update-alternatives --install /usr/bin/java java /usr/lib/jvm/java-8-oracle/bin/java 1 - -# Install our deps -RUN apt-get update -qq && apt-get install -qqy \ - sudo \ - curl \ - jq \ - software-properties-common \ - openssh-server \ - supervisor - -RUN curl -s https://packagecloud.io/install/repositories/computology/apt-backport/script.deb.sh | sudo bash -RUN sudo apt-get install -y apt=1.2.10 - -# Install iofog-agent -RUN curl -s https://packagecloud.io/install/repositories/iofog/iofog-agent/script.deb.sh | sudo bash -RUN sudo apt-get install iofog-agent -RUN touch /first_run.tmp - -# Copy our config and start scripts -COPY start.sh /start.sh -COPY config.xml /etc/iofog-agent/config.xml - -# SSH Magic -COPY id_ecdsa.pub / -RUN mkdir -p /root/.ssh -RUN cat /id_ecdsa.pub > /root/.ssh/authorized_keys -RUN mkdir /var/run/sshd -RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd -ENV NOTVISIBLE "in users profile" -RUN echo "export VISIBLE=now" >> /etc/profile -EXPOSE 22 -COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf - -CMD ["/usr/bin/supervisord"] \ No newline at end of file diff --git a/iofog-agent/start.sh b/iofog-agent/start.sh deleted file mode 100755 index 70c852a..0000000 --- a/iofog-agent/start.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env bash -CONTROLLER_HOST="http://iofog-controller:51121/api/v3" - -function wait() { - while true; do - str=`eval "$1"` - if [[ ! $str =~ $2 ]]; then - break - fi - sleep .5 - done -} - -service iofog-agent start -if [ -f /first_run.tmp ]; then - wait "iofog-agent status" "ioFog Agent is not running." - iofog-agent config -idc off - iofog-agent config -a $CONTROLLER_HOST - wait "curl --request GET --url $CONTROLLER_HOST/status" "Failed" - - sleep 10 - token="" - while true; do - login=$(curl --request POST \ - --url $CONTROLLER_HOST/user/login \ - --header 'Content-Type: application/json' \ - --data '{"email":"user@domain.com","password":"#Bugs4Fun"}') - token=$(echo $login | jq -r .accessToken) - - if [ ! -z "$token" ]; then - break - fi - sleep .5 - done - - uuid="" - echo "Waiting for ioFog Controller..." - while true; do - item=$(curl --request GET \ - --url $CONTROLLER_HOST/iofog-list \ - --header "Authorization: $token" \ - --header 'Content-Type: application/json') - echo $item - uuid=$(echo $item | jq -r '.fogs[] | select(.name == "ioFog Node") | .uuid') - - if [ ! -z "$uuid" ]; then - break - fi - sleep .5 - done - - provisioning=$(curl --request GET \ - --url $CONTROLLER_HOST/iofog/$uuid/provisioning-key \ - --header "Authorization: $token" \ - --header 'Content-Type: application/json') - key=$(echo $provisioning | jq -r .key) - - iofog-agent provision $key - - rm /first_run.tmp -fi -tail -f /dev/null \ No newline at end of file diff --git a/iofog-agent/supervisord.conf b/iofog-agent/supervisord.conf deleted file mode 100644 index d6e8f92..0000000 --- a/iofog-agent/supervisord.conf +++ /dev/null @@ -1,8 +0,0 @@ -[supervisord] -nodaemon=true - -[program:sshd] -command=/usr/sbin/sshd -D - -[program:agent] -command=/bin/bash -c "/start.sh" \ No newline at end of file diff --git a/iofog-connector/Dockerfile b/iofog-connector/Dockerfile deleted file mode 100644 index 68c85de..0000000 --- a/iofog-connector/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -FROM iofog/ubuntu-16.04-java8 - -# Install all our deps -RUN apt-get update -qq && apt-get install -qqy \ - sudo \ - curl \ - software-properties-common - -# Install iofog-connector -RUN curl -s https://packagecloud.io/install/repositories/iofog/iofog-connector/script.deb.sh | sudo bash -RUN sudo apt-get install iofog-connector -RUN touch /first_run.tmp - -# Copy in the files we need -COPY start.sh /start.sh - -ENTRYPOINT [ "/start.sh" ] \ No newline at end of file diff --git a/iofog-connector/start.sh b/iofog-connector/start.sh deleted file mode 100755 index e00951f..0000000 --- a/iofog-connector/start.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash - -if [ -f /first_run.tmp ]; then - echo '{ - "ports": [ - "6000-9999", - "30000-49999" - ], - "exclude": [], - "broker":12345, - "address":"127.0.0.1", - "dev":true - }' > /etc/iofog-connector/iofog-connector.conf - rm /first_run.tmp -fi -service iofog-connector start -tail -f /dev/null \ No newline at end of file diff --git a/iofog-controller/Dockerfile b/iofog-controller/Dockerfile deleted file mode 100644 index eab1d7b..0000000 --- a/iofog-controller/Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -FROM node:8.16.0-alpine - -# Install all our deps -RUN apk add curl jq python bash - -# Install iofog-controller -RUN npm i -g iofogcontroller --unsafe-perm - -RUN touch /first_run.tmp - -# Copy in the files we need -COPY start.sh /start.sh - -ENTRYPOINT [ "/start.sh" ] diff --git a/iofog-controller/start.sh b/iofog-controller/start.sh deleted file mode 100755 index d2cd2b1..0000000 --- a/iofog-controller/start.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env sh - -export NODE_ENV=development - -CONTROLLER_HOST="http://localhost:51121/api/v3" - -cd /usr/local/lib/node_modules/iofogcontroller/src/sequelize -if [ ! -f ./dev_database.sqlite ]; then - sh ./rebuild_dev_db.sh -fi - -iofog-controller start -if [ -f /first_run.tmp ]; then - iofog-controller user add -f John -l Doe -e user@domain.com -p "#Bugs4Fun" - - connector_ip=$(getent hosts iofog-connector | awk '{ print $1 }') - iofog-controller connector add -n iofog-connector -d $connector_ip -i $connector_ip -H - - login=$(curl --request POST \ - --url $CONTROLLER_HOST/user/login \ - --header 'Content-Type: application/json' \ - --data '{"email":"user@domain.com","password":"#Bugs4Fun"}') - token=$(echo $login | jq -r .accessToken) - - item=$(curl --request POST \ - --url $CONTROLLER_HOST/iofog \ - --header "Authorization: $token" \ - --header 'Content-Type: application/json' \ - --data '{"name": "ioFog Node","fogType": 1}') - - rm /first_run.tmp -fi -tail -f /dev/null \ No newline at end of file diff --git a/services/iofog/iofog-agent/.gitignore b/services/iofog/iofog-agent/.gitignore new file mode 100644 index 0000000..cc6078c --- /dev/null +++ b/services/iofog/iofog-agent/.gitignore @@ -0,0 +1 @@ +id_ecdsa.pub diff --git a/services/iofog/iofog-agent/Dockerfile b/services/iofog/iofog-agent/Dockerfile new file mode 100644 index 0000000..1cc3b83 --- /dev/null +++ b/services/iofog/iofog-agent/Dockerfile @@ -0,0 +1,35 @@ +FROM iofog/ubuntu-16.04-java8 + +ARG LOCAL_AGENT_PACKAGE +# Copy install script and local package if there is one (No conditional copy in docker) +COPY ./install.sh ${LOCAL_AGENT_PACKAGE} /opt/iofog-agent/ + +# Install our deps +RUN apt-get update -qq && apt-get install -qqy \ + sudo \ + curl \ + software-properties-common \ + openssh-server \ + supervisor \ + && rm -rf /var/lib/apt/lists/* + +# Install iofog-agent (script requires sudo, curl and software-properties-common) +RUN /opt/iofog-agent/install.sh ${LOCAL_AGENT_PACKAGE} +RUN rm /opt/iofog-agent/install.sh + +# SSH Magic +# Allows the test's container "test-runner" access to iofog-agent, due to the agent's lack of REST API +COPY id_ecdsa.pub / +RUN mkdir -p /root/.ssh +RUN cat /id_ecdsa.pub > /root/.ssh/authorized_keys +RUN mkdir /var/run/sshd +RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd +ENV NOTVISIBLE "in users profile" +RUN echo "export VISIBLE=now" >> /etc/profile +EXPOSE 22 + +# Copy our configs +COPY config.xml /etc/iofog-agent/config.xml +COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf + +CMD ["/usr/bin/supervisord"] diff --git a/iofog-agent/config.xml b/services/iofog/iofog-agent/config.xml similarity index 75% rename from iofog-agent/config.xml rename to services/iofog/iofog-agent/config.xml index 29845d9..5c55bd5 100644 --- a/iofog-agent/config.xml +++ b/services/iofog/iofog-agent/config.xml @@ -2,19 +2,17 @@ - https://iofog-controller/api/v3/ - - - - /etc/iofog/cert.crt - + http://iofog-controller:51121/api/v3/ + + /etc/iofog-agent/cert.crt + eth0 unix:///var/run/docker.sock 50 - /var/lib/iofog/ + /var/lib/iofog-agent/ 4096 @@ -22,7 +20,7 @@ 10.0 - /var/log/iofog/ + /var/log/iofog-agent/ 10 diff --git a/services/iofog/iofog-agent/install.sh b/services/iofog/iofog-agent/install.sh new file mode 100755 index 0000000..83c98bc --- /dev/null +++ b/services/iofog/iofog-agent/install.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# If local agent provided, install from local package, otherwise go to packagecloud +if [ ! -z "$1" ] && [ ! "$1" = "./install.sh" ]; then + echo "============> Installing local agent" + dpkg -i /opt/iofog-agent/"$1" + rm -f /opt/iofog-agent/"$1" +else + wget -q -O - https://packagecloud.io/install/repositories/iofog/iofog-agent/script.deb.sh | bash + apt-get install iofog-agent +fi \ No newline at end of file diff --git a/services/iofog/iofog-agent/supervisord.conf b/services/iofog/iofog-agent/supervisord.conf new file mode 100644 index 0000000..5d55f75 --- /dev/null +++ b/services/iofog/iofog-agent/supervisord.conf @@ -0,0 +1,12 @@ +[supervisord] +nodaemon=true + +[program:sshd] +command=/usr/sbin/sshd -D + +; TODO supervisor expects non-daemonized program, iofog-agent is daemonized +; http://supervisord.org/subprocess.html#nondaemonizing-of-subprocesses +[program:agent] +command=bash -c 'service iofog-agent start && tail -f /dev/null' +stderr_logfile=/var/log/agent.err.log +stdout_logfile=/var/log/agent.out.log diff --git a/services/iofog/iofog-connector/Dockerfile b/services/iofog/iofog-connector/Dockerfile new file mode 100644 index 0000000..757dbe3 --- /dev/null +++ b/services/iofog/iofog-connector/Dockerfile @@ -0,0 +1,21 @@ +FROM iofog/ubuntu-16.04-java8 + +ARG LOCAL_CONNECTOR_PACKAGE +# Copy local package, or default install script (No conditional copy in docker) +COPY ./install.sh ${LOCAL_CONNECTOR_PACKAGE} /opt/iofog-connector/ + +# Install all our deps +RUN apt-get update -qq && apt-get install -qqy \ + sudo \ + curl \ + software-properties-common \ + && rm -rf /var/lib/apt/lists/* + +# Install iofog-connector (script requires sudo, curl and software-properties-common) +RUN /opt/iofog-connector/install.sh ${LOCAL_CONNECTOR_PACKAGE} +RUN rm /opt/iofog-connector/install.sh + +# Copy in the connector configuration +COPY iofog-connector.conf /etc/iofog-connector/iofog-connector.conf + +ENTRYPOINT [ "sh", "-c", "service iofog-connector start && tail -f /dev/null" ] diff --git a/services/iofog/iofog-connector/install.sh b/services/iofog/iofog-connector/install.sh new file mode 100755 index 0000000..8ac9b94 --- /dev/null +++ b/services/iofog/iofog-connector/install.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# If local connector provided, install from local package, otherwise go to packagecloud +if [ ! -z "$1" ] && [ ! "$1" = "./install.sh" ]; then + echo "============> Installing local connector" + dpkg -i /opt/iofog-connector/"$1" + rm -f /opt/iofog-connector/"$1" +else + wget -q -O - https://packagecloud.io/install/repositories/iofog/iofog-connector/script.deb.sh | bash + apt-get install iofog-connector +fi \ No newline at end of file diff --git a/services/iofog/iofog-connector/iofog-connector.conf b/services/iofog/iofog-connector/iofog-connector.conf new file mode 100644 index 0000000..c2d575c --- /dev/null +++ b/services/iofog/iofog-connector/iofog-connector.conf @@ -0,0 +1,10 @@ +{ + "ports": [ + "6000-9999", + "30000-49999" + ], + "exclude": [], + "broker": 12345, + "address": "127.0.0.1", + "dev": true +} diff --git a/services/iofog/iofog-controller/Dockerfile b/services/iofog/iofog-controller/Dockerfile new file mode 100644 index 0000000..2a8dab1 --- /dev/null +++ b/services/iofog/iofog-controller/Dockerfile @@ -0,0 +1,11 @@ +FROM node:8.16.0-alpine + +ARG LOCAL_CONTROLLER_PACKAGE +# Copy local package, or default install script (No conditional copy in docker) +COPY ./install.sh ${LOCAL_CONTROLLER_PACKAGE} /opt/iofog-controller/ + +# Install iofog-controller +RUN /opt/iofog-controller/install.sh ${LOCAL_CONTROLLER_PACKAGE} +RUN rm /opt/iofog-controller/install.sh + +ENTRYPOINT [ "sh", "-c", "iofog-controller start && tail -f /dev/null" ] diff --git a/services/iofog/iofog-controller/install.sh b/services/iofog/iofog-controller/install.sh new file mode 100755 index 0000000..3edfc30 --- /dev/null +++ b/services/iofog/iofog-controller/install.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +# If local controller provided, install from local package, otherwise go to npm repository +if [ ! -z "$1" ] && [ ! "$1" = "./install.sh" ]; then + echo "============> Installing local controller" + npm install --unsafe-perm -g /opt/iofog-controller/"$1" + rm -f /opt/iofog-controller/"$1" +else + npm i -g iofogcontroller --unsafe-perm +fi \ No newline at end of file diff --git a/start.sh b/start.sh index 7ebdb38..718ec52 100755 --- a/start.sh +++ b/start.sh @@ -1,9 +1,161 @@ -#!/usr/bin/env sh +#!/usr/bin/env bash +# +# start.sh - is the main start script for our Demo cluster. Running it will launch a docker-compose environment +# that contains a fully connected ioFog ECN. Optionally, you can launch a different compose, e.g. tutorial. +# +# Usage : ./start.sh -h +# -set -e +set -o errexit -o pipefail -o noclobber -o nounset -rm conf/id_ecdsa* || true -ssh-keygen -t ecdsa -N "" -f conf/id_ecdsa -q -cp conf/id_ecdsa.pub iofog-agent +cd "$(dirname "$0")" -docker-compose -f docker-compose.yml up --build --detach \ No newline at end of file +# Import our helper functions +. ./utils.sh + +printHelp() { + echo "Usage: ./start.sh [opts] [environment]" + echo "Starts ioFog environments and optionally sets up demo and tutorial environment" + echo "" + echo "Options:" + echo " -h, --help print this help / usage" + echo " -a, --agent specify a local agent package" + echo " -ct, --controller specify a local controller package" + echo " -cn, --connector specify a local connector package" + echo " --no-cache prevent the usage of cache during the build step" + echo "" + echo "Arguments:" + echo " [environment] setup demo application, optional, default: iofog" + echo " supported values: iofog, tutorial" +} + +checkForComposeFile() { + local ENVIRONMENT="$1" + local COMPOSE_FILE="docker-compose-${ENVIRONMENT}.yml" + if [[ ! -f "${COMPOSE_FILE}" ]]; then + echoError "Environment configuration for \"${ENVIRONMENT}\" does not exist!" + exit 2 + fi +} + +startIofog() { + echoInfo "Building containers for ioFog stack..." + local BUILD_ARGS="--build-arg LOCAL_CONTROLLER_PACKAGE=${CONTROLLER_PACKAGE} --build-arg LOCAL_CONNECTOR_PACKAGE=${CONNECTOR_PACKAGE} --build-arg LOCAL_AGENT_PACKAGE=${AGENT_PACKAGE}" + local COMPOSE_BUILD_ARGS="${IOFOG_BUILD_NO_CACHE} ${BUILD_ARGS:=""}" + local SERVICE_LIST="iofog-connector iofog-controller iofog-agent iofog-init" + docker-compose -f "docker-compose-iofog.yml" build ${COMPOSE_BUILD_ARGS} ${SERVICE_LIST} > /dev/null + + echoInfo "Spinning up containers for ioFog stack..." + docker-compose -f "docker-compose-iofog.yml" up --detach --no-recreate + + echoInfo "Initializing iofog stack..." + docker logs -f "iofog-init" # wait for the ioFog stack initialization + RET=$(docker wait "iofog-init") + if [[ "$RET" != "0" ]]; then + echoError "Failed to initialize ioFog stack!" + exit 3 + fi + echoInfo "Successfully initialized ioFog stack." +} + +startEnvironment() { + local ENVIRONMENT="$1" + local COMPOSE_PARAM="-f docker-compose-${ENVIRONMENT}.yml" + + echoInfo "Building containers for ${ENVIRONMENT} environment..." + docker-compose -f "docker-compose-iofog.yml" -f "docker-compose-${ENVIRONMENT}.yml" \ + build "${ENVIRONMENT}-init" > /dev/null + + echoInfo "Spinning up containers for ${ENVIRONMENT} environment..." + docker-compose -f "docker-compose-iofog.yml" -f "docker-compose-${ENVIRONMENT}.yml" \ + up --detach --no-recreate "${ENVIRONMENT}-init" + + echoInfo "Initializing ${ENVIRONMENT} environment..." + docker logs -f "${ENVIRONMENT}-init" # wait for the ioFog stack initialization + RET=$(docker wait "${ENVIRONMENT}-init") + if [[ "$RET" != "0" ]]; then + echoError "Failed to initialize ${ENVIRONMENT} environment!" + exit 3 + fi + echoInfo "Successfully setup ${ENVIRONMENT} environment." + echoInfo "It may take a while before ioFog stack creates all ${ENVIRONMENT} microservices." +} + +ENVIRONMENT='' +IOFOG_BUILD_NO_CACHE='' +AGENT_PACKAGE='' +CONTROLLER_PACKAGE='' +CONNECTOR_PACKAGE='' +while [[ "$#" -ge 1 ]]; do + case "$1" in + -h|--help) + printHelp + exit 0 + ;; + --no-cache) + IOFOG_BUILD_NO_CACHE="--no-cache" + shift + ;; + -a|--agent) + AGENT_PACKAGE="local-agent-package.deb" + cp -i "$2" "./services/iofog/iofog-agent/$AGENT_PACKAGE" || true + shift + shift + ;; + -ct|--controller) + CONTROLLER_PACKAGE="local-controller-package.tgz" + cp -i "$2" "./services/iofog/iofog-controller/$CONTROLLER_PACKAGE" || true + shift + shift + ;; + -cn|--connector) + CONNECTOR_PACKAGE="local-connector-package.deb" + cp -i "$2" "./services/iofog/iofog-connector/$CONNECTOR_PACKAGE" || true + shift + shift + ;; + *) + if [[ -n "${ENVIRONMENT}" ]]; then + echoError "Cannot specify more than one environment!" + printHelp + exit 1 + fi + ENVIRONMENT=$1 + shift + ;; + esac +done + +prettyHeader "Starting ioFog Demo" + +# Figure out which environment we are going to be starting. By default, setup only the ioFog stack +ENVIRONMENT=${ENVIRONMENT:="iofog"} + +checkForComposeFile "iofog" + +if [[ "${ENVIRONMENT}" != "iofog" ]]; then checkForComposeFile "${ENVIRONMENT}"; fi + +echoInfo "Starting \"${ENVIRONMENT}\" demo environment..." + +# Create a new ssh key +echoInfo "Creating new ssh key for tests..." +rm -f test/conf/id_ecdsa* +ssh-keygen -t ecdsa -N "" -f test/conf/id_ecdsa -q +cp -f test/conf/id_ecdsa.pub services/iofog/iofog-agent/ + +# Start ioFog stack +# TODO check if this environment is up or not +startIofog + +# Optionally start another environment +if [[ "${ENVIRONMENT}" != "iofog" ]]; then + # TODO check if this environment is up or not + startEnvironment "${ENVIRONMENT}"; +fi + +# Display the running environment +./status.sh + +if [[ "${ENVIRONMENT}" == "tutorial" ]]; then + echoSuccess "## Visit https://iofog.org/docs/1.0.0/tutorial/introduction.html to continue with the ioFog tutorial." +fi diff --git a/status.sh b/status.sh new file mode 100755 index 0000000..82f725d --- /dev/null +++ b/status.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# +# status.sh - Print the status of the Demo cluster and its components +# +# Usage: ./status.sh +# + +set -e +cd "$(dirname "$0")" + +# Import our helper functions +. ./utils.sh + +# Display the running environment +prettyTitle "ioFog Demo Environment Status" +echoInfo " $(docker ps --filter 'name=iofog')" +echo + +CONTROLLER_PORT="$(docker port iofog-controller | awk '{print $1}' | cut -b 1-5)" + +if [ ! -z ${CONTROLLER_PORT} ]; then + echoSuccess "## iofog-controller is running at http://localhost:${CONTROLLER_PORT}" +else + echoError "No iofog-controller container was found" +fi diff --git a/stop.sh b/stop.sh index 41d240b..b27f875 100755 --- a/stop.sh +++ b/stop.sh @@ -1,5 +1,54 @@ -#!/usr/bin/env sh +#!/usr/bin/env bash +# +# stop.sh - will tear down a running docker-compose Demo environment +# +# Usage : ./stop.sh -h +# -set -e +set -o errexit -o pipefail -o noclobber -o nounset -docker-compose down -v \ No newline at end of file +cd "$(dirname "$0")" + +# Import our helper functions +. ./utils.sh + +printHelp() { + echo "Usage: ./stop.sh" + echo "Stops ioFog environments and optionally sets up demo and tutorial environment" + echo "" + echo "Arguments:" + echo " -h, --help print this help / usage" +} + +while [[ "$#" -ge 1 ]]; do + case "$1" in + -h|--help) + printHelp + exit 0 + ;; + *) + echoError "Unrecognized argument: \"$1\"" + printHelp + exit 1 + ;; + esac +done + +prettyHeader "Stopping ioFog Demo..." + +# Stop ioFog stack +echoInfo "Stopping all containers..." +docker-compose -f "docker-compose-iofog.yml" -f "docker-compose-tutorial.yml" down -v + +# TODO stopping the ioFog stack leaves its microservices running - fix this properly +REMAINING_MSVC=`docker ps -q --filter 'name=iofog*'` + +if [ ! -z "${REMAINING_MSVC}" ]; then + docker rm -f ${REMAINING_MSVC} +fi + +# Remove generated files +find test/conf -type f -not -name ".gitignore" -exec rm -f {} \; +rm -f "services/iofog/iofog-agent/id_ecdsa.pub" + +prettyTitle "ioFog demo is stopped" diff --git a/test.sh b/test.sh index 6c94d16..947f1ab 100755 --- a/test.sh +++ b/test.sh @@ -1,16 +1,59 @@ -#!/usr/bin/env sh +#!/usr/bin/env bash +# +# test.sh - will pull and run the TestRunner suite against an already Demo compose cluster. +# +# Usage : ./test.sh -h +# -set -e +set -o errexit -o pipefail -o noclobber -o nounset +cd "$(dirname "$0")" + +# Import our helper functions +. ./utils.sh + +printHelp() { + echo "Usage: ./test.sh" + echo "Tests ioFog environment" + echo "" + echo "Arguments:" + echo " -h, --help print this help / usage" +} + +if [[ $# -gt 0 ]]; then + printHelp + exit 1 +fi + +prettyHeader "Running Test Runner Suite" + +# Check to see if we are already running the Demo +IOFOG_RUNNING=$(docker inspect -f '{{.State.Running}}' iofog-agent iofog-connector iofog-controller | tr -d "[:space:]") +if [[ "${IOFOG_RUNNING}" == "truetruetrue" ]]; then + echoInfo "ioFog stack is running" +else + echoError 'ioFog stack is not running! Please run `./start.sh iofog` first' + exit 2 +fi + +echoInfo "Retrieving endpoints for ioFog stack" +AGENT_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' iofog-agent) +CONTROLLER_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' iofog-controller) +CONNECTOR_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' iofog-connector) # Output the config for our Test suite config -echo 'iofog-connector:8080' > conf/connector.conf -echo 'iofog-controller:51121' > conf/controller.conf -echo 'root@iofog-agent' > conf/agents.conf +echo "${CONNECTOR_IP}:8080" >| test/conf/connector.conf +echo "${CONTROLLER_IP}:51121" >| test/conf/controller.conf +echo "root@${AGENT_IP}" >| test/conf/agents.conf +echoInfo "Pulling Test Runner Image" docker-compose -f docker-compose-test.yml pull test-runner + +echoInfo "Running Test Runner suite" docker-compose -f docker-compose-test.yml up \ --build \ --abort-on-container-exit \ --exit-code-from test-runner \ --force-recreate \ - --renew-anon-volumes \ No newline at end of file + --renew-anon-volumes + +echoNotify "## Test Runner Tests complete" diff --git a/test/conf/.gitignore b/test/conf/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/test/conf/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/utils.sh b/utils.sh new file mode 100755 index 0000000..a58b17d --- /dev/null +++ b/utils.sh @@ -0,0 +1,141 @@ +#!/usr/bin/env sh +# +# utils.sh - a simple set of functions for use throughout our bash scripts +# +# Usage : source utils.sh +# + +# Export the name of the script for later use +THIS_SCRIPT="$(basename "${0}")" +export THIS_SCRIPT + +# +# Check for DEBUG - looks for the DEBUG environment variable. If it +# is found, it enables more debugging output. +# +checkForDebug() { + + # Allow anyone to set a DEBUG environment variable for extra output + if env | grep -q "^DEBUG=" ; then + echoNotify " :: DEBUG is set ::" + set -x + fi +} + +# +# Display a nice title line for any output. You can optionally populate it with a string +# +# Usage: prettyTitle "Bootstrapping ioFog" +# +prettyTitle() { + echoInfo "## $1 ####################################################" +} + +# +# Display a nice header for any command line script. You can optionally populate it with a string +# +# Usage: prettyHeader "Bootstrapping ioFog" +# +prettyHeader() { + echoInfo "## $1 ####################################################" + echoInfo "## Copyright (C) 2019, Edgeworx, Inc." + echo +} + +# +# Extract and display our script options in a pretty manner +# +# Usage: prettyUsage ${THIS_SCRIPT} +# +prettyUsage() { + + grep -e '--.*")' <"${1}" | grep -v 'grep -e' | tr -d '"' | tr ')' '\t' +} + +# +# Check for Installation will check to see whether a particular command exists in +# the $PATH of the current shell. Optionally, you can check for a specific version. +# +# Usage: checkForInstallation protoc "libprotoc 3.6.1" +# +checkForInstallation() { + + # Does the command exist? + if [[ ! "$(command -v "$1")" ]]; then + echoError " [!] $1 not found" + return 1 + else + # Are we looking for a specific version? + if [[ ! -z "$2" ]]; then + if [[ "$2" != "$($1 --version)" ]]; then + echoError " !! $1 is the wrong version. Found $($1 --version) but expected $2" + return 1 + fi + fi + echoSuccess " [x] $1 $2 found at $(command -v "$1")" + return 0 + fi +} + +# +# Check OS Platform attempts to determine which platform we're running on currently. +# It will export the results into two env variables that can be used in your scripts: +# $OS_ID - the name of the host os. E.g. ubuntu +# $D_NUM - the version number of the os E.g. 16 +# +checkOSPlatform() { + if [[ "$(uname -s)" = "Darwin" ]]; then + ID=macos + D_NUM="$(sysctl kern.osproductversion | awk '{print $2}' | awk -F '.' '{print $1}')" + else + . /etc/os-release + D_NUM="$(echo ${VERSION_ID} | awk -F '.' '{print $1}')" + + # Push us into privileged if we aren't already + if [[ ! "$(id -u)" = "0" ]] && [[ ! "$1" = "--help" ]]; then + echoNotify " If you are a new developer please run this script with --help" + exec sudo -E "PATH=$PATH" sh "${THIS_SCRIPT}" "$@" + fi + fi + + export ID D_NUM +} + +# +# The following are a bunch or pretty printing echo methods +# + +# This is the list of colors used in our messages +NO_FORMAT="\\033[0m" +C_SKYBLUE1="\\033[38;5;117m" +C_DEEPSKYBLUE4="\\033[48;5;25m" +RED="\\033[38;5;1m" +GREEN="\\033[38;5;28m" + +# Need this as bash and sh require different args for the echo command +if [ "${BASH_VERSION}" ]; then + PRINTARGS="-e" +fi + +# Basic subtle output +echoInfo() { + echo ${PRINTARGS} "${C_SKYBLUE1}$1 ${NO_FORMAT}" +} + +# Highlighted output with a background +echoNotify() { + echo ${PRINTARGS} "${C_DEEPSKYBLUE4}${1} ${NO_FORMAT}" +} + +# Hurrah! +echoSuccess() { + echo ${PRINTARGS} "${GREEN}$1 ${NO_FORMAT}" +} + +# Houston, we have a problem! +echoError() { + echo ${PRINTARGS} "${RED} :: $1 ${NO_FORMAT}" +} + +# Are we in debug mode? +checkForDebug