built with concourse

OVERVIEW

Think of a group of people on a playground playing the game catch. There is one ball being thrown around randomly from person to person. People can come and go as they please. If there is one person left, they will toss the ball to themselves until another person joins the game.

Any person that joins must be introduced to the entire group via a friend. If a person has the ball and leave the game, another person will pick it up and continue playing catch.

PREREQUISITES

You will need the following go packages,

go get -u -v github.com/sirupsen/logrus
go get -u -v github.com/cweill/gotests/...

SOFTWARE STACK

Where,

HOW IT WORKS

Each “person” is an instance of the catch-microservice DockerHub image.

Each instance (i.e. people) has the following features:

Each person has the following State Table:

STARTING AND PLAYING THE GAME

To deploy the first person (lets call him Steve):

docker run jeffdecola/catch-microservice StevesID

Because Steve is the first kid and all by himself, his State Table shall look like:

Steve will play catch with himself until another person joins the game.

To deploy another person (e.g. Julie), she must know another person (e.g. Steve):

docker run jeffdecola/catch-microservice larryID steveID

Hence, Julies’s State Table shall look like.

Steve will immediately throw the ball to julie.

Steve will update Larry’s State Table with the current states (updatestate).

Steve will also introduce Larry to all the other kids via his friendslist if other kids are present (updatestate).

When a kid catches the ball (throw) he tells everyone in his friendslist that he has the ball (updatestate). Everyone will update their whohasball state.

RETSful API using JSON

To accomplish the above logic, a RESTful API with json shall be used.

In gom, the http package lets us map request paths to functions.

There are 4 basic commands:

CANIPLAY - PUT /state

When a new kid (Larry) wants to play, he must ask his friend (Steve) if he can play.

PUT uri/state

{
    "cmd": "caniplay",
    "uri": "larryURI"
}

Reponse:

{
  "response": "success"
}

If Larry does not get a response from Steve, then he can’t play catch and will leave (i.e. exit).

If success Steve will updates his freindslist and tells all of the other kids about Larry so they can update their respective friendslist.

If Steve does not get a response while updating the other kids, he issues a kick command.

Steve will also update Larry’s State Table with his states. Now Larry is up to date and in the game.

UPDATESTATE - PUT /state

When a kids wants ot update a friends state.

PUT uri/state

{
    "cmd": "updatestate",
    "friendslist": "URI",
    "addtofriendslist": "URI",
    "whohasball" : {"URI", "URI"}
}

Reponse:

{
  "response": "success"
}

THROW BALL - PUT /state

When a kid has the ball and wants to throw it, he randomwly picks someone from his friendslist and throws it via:

PUT uri/state

{
    "cmd": "throw"
}

Response:

{
  "response": "success"
}

If he does not get a reposnse (fail), he first kicks the kid frmo the game and then throws the ball to another kid.

If success, the thrower updates his whohasball state. The catcher subsequently tells everyone in his friendslist who has the ball as follow:

PUT uri/state

{
    "cmd": "ihaveball",
    "uri": "catcherURI"
}

Response:

{
  "response": "success"
}

On success of updating all kids, the catchre is ready to throw the ball.

If the catcher does not get a response (fail) from a kid, he kicks that kid from the game.

KICK FROM GAME- PUT /state

When a kid does not respond, it is assumed he left the game. The kid who got the non-response tell all the other kids who it is so they can purge him from their state.

PUT uri/state

{
    "cmd": "kick",
    "uri": "kickURI"
}

Response:

{
  "response": "success"
}

KID NOT RECEIVING ANY INFO - PUT /state

If a kid left and came back, and does not receive any info, he assumes he’s been kicked and starts to go through his friends list to ask if he can join the game as a new kid.

CREATE THE DOCKER IMAGE

How I created, tested, and deployed the docker image.

RUN

To run.sh,

cd catch-microservice-code
go run main.go

As a placeholder, every 2 seconds it will print,

    INFO[0000] Let's Start this!
    Hello everyone, count is: 1
    Hello everyone, count is: 2
    Hello everyone, count is: 3
    etc...

CREATE BINARY

To create-binary.sh,

cd catch-microservice-code/bin
go build -o catch-microservice ../main.go
./catch-microservice

This binary will not be used during a docker build since it creates it’s own.

STEP 1 - TEST

To create unit _test files,

cd catch-microservice-code
gotests -w -all main.go

To run unit-tests.sh,

go test -cover ./... | tee test/test_coverage.txt
cat test/test_coverage.txt

STEP 2 - BUILD (DOCKER IMAGE VIA DOCKERFILE)

To build.sh with a Dockerfile,

cd catch-microservice-code
docker build -f build/Dockerfile -t jeffdecola/catch-microservice .

You can check and test this docker image,

docker images jeffdecola/catch-microservice:latest
docker run --name catch-microservice -dit jeffdecola/catch-microservice
docker exec -i -t catch-microservice /bin/bash
docker logs catch-microservice
docker rm -f catch-microservice

In stage 1, rather than copy a binary into a docker image (because that can cause issues), the Dockerfile will build the binary in the docker image,

FROM golang:alpine AS builder
RUN go get -d -v
RUN go build -o /go/bin/catch-microservice main.go

In stage 2, the Dockerfile will copy the binary created in stage 1 and place into a smaller docker base image based on alpine, which is around 13MB.

STEP 3 - PUSH (TO DOCKERHUB)

You must be logged in to DockerHub,

docker login

To push.sh,

docker push jeffdecola/catch-microservice

Check the catch-microservice docker image at DockerHub.

STEP 4 - DEPLOY (TO DOCKER)

To deploy.sh,

cd catch-microservice-code
docker run --name catch-microservice -dit jeffdecola/catch-microservice
docker exec -i -t catch-microservice /bin/bash
docker logs catch-microservice
docker rm -f catch-microservice

CONTINUOUS INTEGRATION & DEPLOYMENT

Refer to ci-README.md on how I automated the above steps.