cs

package module
v0.0.0-...-e3b4067 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 11, 2019 License: CC0-1.0 Imports: 16 Imported by: 0

README

Container Solution API-exercise

Container Solution API-exercise, to assess technical proficiency with Software Engineering, DevOps, and Infrastructure tasks.

The Titanic API

The Titanic API is written in golang. It leverages go-kit to grant better modularity and micro-services support out-of-the-box.

The data layer is designed around CockroachDB, deployed to the K8S cluster leveraging a StatefulSet configuration.

Build the API

There are two ways here available to build the API code; a targetted method and a cross-plattform builder script; both allow to create portable executables, compatible with Alpine Linux, compiled statically linking C bindings -installsuffix 'static', and omitting the symbol and debug info -ldflags "-s -w".

Targetted build, based on your system/architecture
# change according to your system/architecture
CGO_ENABLED=0 GOARCH=[amd64|386] GOOS=[linux|darwin] go build -ldflags="-w -s" -a -installsuffix 'static' -o cs cmd/cs/main.go
Cross-platform build, leveraging the build.sh script
chmod +x build.sh && ./build.sh

The build.sh script will also re-build and push the docker images to our private GCR.

Currently, the builds in the releases folder are available for the following platforms and architectures:

  • darwin / amd64;
  • darwin / 386;
  • linux / amd64;
  • linux / 386.
Build the Docker images locally

The Docker images are in the docker folder. To build the production API Docker image, run these commands in your terminal:

cd docker
docker build --no-cache --file Dockerfile -t gcr.io/$PROJECT_ID/cs-api:latest .

To build the devlopment Docker image you must extend the build context to the top-level folder of this repo, and include the files go.mod and go.sum, along with the source code and run the API:

# the Dockerfile is outside the build context
docker build --no-cache --file ./docker/dev.Dockerfile -t gcr.io/$PROJECT_ID/cs-api:dev .
Run the API locally in Docker

Running the API locally is quite simple; it does not require any particular language or library installed on your system, other than Docker. The golang server is listening both on port 8443/TCP over TLS and 3000/TCP over http; to properly run the API locally, before spawning the docker image, you need to generate the certs as follow, from the top dir of this repo:

mkdir -p $(pwd)/tls; \
openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 \
-subj "/C=NL/ST=Amsterdam/L=Amsterdam/O=hyperd/CN=cs-api.hyperd.sh" \
-keyout $(pwd)/tls/tls.key -out $(pwd)/tls/tls.crt; \
chmod 444 tls/*

Once the cert and the key are in place, it's enough to lift the docker image with the command described here below, mounting the correct volume, to have the TLS configured correctly:

# production | develpment
docker run -d --name cs-api -v $(pwd)/tls:/etc/tls/certs -p 3000:3000 -p 8443:8443  gcr.io/$PROJECT_ID/cs-api:[latest|dev]
API Walkthrough

The Titanic API exposes the following methods:

POST /people/ adds another passenger to the people collection:

payload='
{
  "survived": true,
  "pclass": 1,
  "name": "Francesco",
  "sex": "M",
  "age": 30,
  "siblings_spouses_abroad": false,
  "parents_children_aboard": false,
  "fare": 7.34
}
'
curl -d "$payload" -H "Content-Type: application/json" -X POST http://localhost:3000/people/
# {}

GET /people/:uuid retrieves the given passenger by uuid from the people collection:

curl http://localhost:3000/people/35d4ab59-fa9d-478d-a57e-61b526ee0a33 | jq
{
  "people": {
    "uuid": "35d4ab59-fa9d-478d-a57e-61b526ee0a33",
    "survived": true,
    "pclass": 1,
    "name": "Francesco",
    "sex": "M",
    "age": 30,
    "siblings_spouses_abroad": false,
    "parents_children_aboard": false,
    "fare": 7.34
  }
}

DELETE /people/:uuid removes the given passenger:

curl -X "DELETE" http://localhost:3000/people/35d4ab59-fa9d-478d-a57e-61b526ee0a33
# {}

PATCH /people/:uuid partial update of the passenger information:

payload='
{
  "siblings_spouses_abroad": true,
  "parents_children_aboard": true
}
'
curl -d "$payload" -H "Content-Type: application/json" -X PATCH -k http://localhost/people/35d4ab59-fa9d-478d-a57e-61b526ee0a33

PUT /people/:uuid posts updated information about a given passenger:

payload='
{
  "survived": true,
  "pclass": 1,
  "name": "Francesco",
  "sex": "M",
  "age": 30,
  "siblings_spouses_abroad": true,
  "parents_children_aboard": true,
  "fare": 9.81
}
'
curl -d "$payload" -H "Content-Type: application/json" -X PUT http://localhost:3000/people/35d4ab59-fa9d-478d-a57e-61b526ee0a33

GET /people/ retrieves all the passengers of the Titanic:

curl http://localhost:3000/people/ | jq
{
  "people": [
    {
      "uuid": "30615024-ada8-4af6-8611-882c006d17f4",
      "survived": true,
      "pclass": 1,
      "name": "Francesco",
      "sex": "M",
      "age": 30,
      "siblings_spouses_abroad": false,
      "parents_children_aboard": false,
      "fare": 7.34
    },
    {
      "uuid": "363f558a-eeb1-4bf6-b570-33e61e60b867",
      "survived": true,
      "pclass": 1,
      "name": "Anne McLeod",
      "sex": "F",
      "age": 49,
      "siblings_spouses_abroad": true,
      "parents_children_aboard": true,
      "fare": 9.34
    },
    ...
  ]
}

Deploy the API to GCP

To deploy the stack to GKE on GCP follow this documentation.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrInconsistentUUIDs = errors.New("inconsistent UUIDs")
	ErrAlreadyExists     = errors.New("already exists")
	ErrNotFound          = errors.New("not found")
)

Response errors

View Source
var (
	// ErrBadRouting is returned when an expected path variable is missing.
	// It always indicates programmer error.
	ErrBadRouting = errors.New("inconsistent mapping between route and handler (programmer error)")
)

Functions

func MakeDeletePeopleEndpoint

func MakeDeletePeopleEndpoint(s Service) endpoint.Endpoint

MakeDeletePeopleEndpoint returns an endpoint via the passed service. Primarily useful in a server.

func MakeGetAPIStatusEndpoint

func MakeGetAPIStatusEndpoint(s Service) endpoint.Endpoint

MakeGetAPIStatusEndpoint returns an endpoint via the passed service. Primarily useful in a server.

func MakeGetAllPeopleEndpoint

func MakeGetAllPeopleEndpoint(s Service) endpoint.Endpoint

MakeGetAllPeopleEndpoint returns an endpoint via the passed service. Primarily useful in a server.

func MakeGetPeopleEndpoint

func MakeGetPeopleEndpoint(s Service) endpoint.Endpoint

MakeGetPeopleEndpoint returns an endpoint via the passed service. Primarily useful in a server.

func MakeHTTPHandler

func MakeHTTPHandler(s Service, logger log.Logger) http.Handler

MakeHTTPHandler mounts all of the service endpoints into an http.Handler.

func MakePatchPeopleEndpoint

func MakePatchPeopleEndpoint(s Service) endpoint.Endpoint

MakePatchPeopleEndpoint returns an endpoint via the passed service. Primarily useful in a server.

func MakePostPeopleEndpoint

func MakePostPeopleEndpoint(s Service) endpoint.Endpoint

MakePostPeopleEndpoint returns an endpoint via the passed service. Primarily useful in a server.

func MakePutPeopleEndpoint

func MakePutPeopleEndpoint(s Service) endpoint.Endpoint

MakePutPeopleEndpoint returns an endpoint via the passed service. Primarily useful in a server.

Types

type Endpoints

type Endpoints struct {
	PostPeopleEndpoint   endpoint.Endpoint
	GetPeopleEndpoint    endpoint.Endpoint
	PutPeopleEndpoint    endpoint.Endpoint
	PatchPeopleEndpoint  endpoint.Endpoint
	DeletePeopleEndpoint endpoint.Endpoint
	GetAllPeopleEndpoint endpoint.Endpoint
	GetAPIStatusEndpoint endpoint.Endpoint
}

Endpoints collects all of the endpoints that compose a People service. It's meant to be used as a helper struct, to collect all of the endpoints into a single parameter.

In a server, it's useful for functions that need to operate on a per-endpoint basis. For example, you might pass an Endpoints to a function that produces an http.Handler, with each method (endpoint) wired up to a specific path. (It is probably a mistake in design to invoke the Service methods on the Endpoints struct in a server.)

In a client, it's useful to collect individually constructed endpoints into a single type that implements the Service interface. For example, you might construct individual endpoints using transport/http.NewClient, combine them into an Endpoints, and return it to the caller as a Service.

func MakeClientEndpoints

func MakeClientEndpoints(instance string) (Endpoints, error)

MakeClientEndpoints returns an Endpoints struct where each endpoint invokes the corresponding method on the remote instance, via a transport/http.Client. Useful in a cs client.

func MakeServerEndpoints

func MakeServerEndpoints(s Service) Endpoints

MakeServerEndpoints returns an Endpoints struct where each endpoint invokes the corresponding method on the provided service. Useful in a cs service server.

func (Endpoints) DeletePeople

func (e Endpoints) DeletePeople(ctx context.Context, uuid uuid.UUID) error

DeletePeople implements Service. Primarily useful in a client.

func (Endpoints) GetAPIStatus

func (e Endpoints) GetAPIStatus(ctx context.Context) (string, error)

GetAPIStatus implements Service. Primarily useful in a client.

func (Endpoints) GetAllPeople

func (e Endpoints) GetAllPeople(ctx context.Context) ([]People, error)

GetAllPeople implements Service. Primarily useful in a client.

func (Endpoints) GetPeople

func (e Endpoints) GetPeople(ctx context.Context, uuid uuid.UUID) (People, error)

GetPeople implements Service. Primarily useful in a client.

func (Endpoints) PatchPeople

func (e Endpoints) PatchPeople(ctx context.Context, uuid uuid.UUID, p People) error

PatchPeople implements Service. Primarily useful in a client.

func (Endpoints) PostPeople

func (e Endpoints) PostPeople(ctx context.Context, p People) error

PostPeople implements Service. Primarily useful in a client.

func (Endpoints) PutPeople

func (e Endpoints) PutPeople(ctx context.Context, uuid uuid.UUID, p People) error

PutPeople implements Service. Primarily useful in a client.

type Middleware

type Middleware func(Service) Service

Middleware describes the cs service (as opposed to endpoint) middleware.

func LoggingMiddleware

func LoggingMiddleware(logger log.Logger) Middleware

LoggingMiddleware provides basic logging Middleware

type People

type People struct {
	UUID                  uuid.UUID `json:"uuid,omitempty"`
	Survived              *bool     `json:"survived,omitempty"`
	Pclass                *int      `json:"pclass,omitempty"`
	Name                  string    `json:"name,omitempty"`
	Sex                   string    `json:"sex,omitempty"`
	Age                   *int      `json:"age,omitempty"`
	SiblingsSpousesAbroad *bool     `json:"siblings_spouses_abroad,omitempty"`
	ParentsChildrenAboard *bool     `json:"parents_children_aboard,omitempty"`
	Fare                  *float32  `json:"fare,omitempty"`
}

People represents a single passenger (People). UUID should be globally unique.

type Service

type Service interface {
	PostPeople(ctx context.Context, p People) error
	GetPeople(ctx context.Context, uuid uuid.UUID) (People, error)
	PutPeople(ctx context.Context, uuid uuid.UUID, p People) error
	PatchPeople(ctx context.Context, uuid uuid.UUID, p People) error
	DeletePeople(ctx context.Context, uuid uuid.UUID) error
	GetAllPeople(ctx context.Context) ([]People, error)
	GetAPIStatus(ctx context.Context) (string, error)
}

Service is a CRUD interface for People in the Titanic collection.

func NewInmemService

func NewInmemService() Service

NewInmemService returns an in-memory storage

Directories

Path Synopsis
cmd
cs
config module
dal module
models module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL