Project

hyperdock

0.0
No commit activity in last 3 years
No release in over 3 years
Hypermedia API for Docker
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Runtime

>= 0.14.0, ~> 0.14
>= 1.31.0, ~> 1.31
>= 1.0.1, ~> 1.0
 Project Readme

HyperDock: A Hypermedia API for Docker

Overview

HyperDock is a Hypermedia API for Docker. It augments the standard Docker API with a discoverable, link-driven API conforming to the HAL Specification.

Why does this exist?

Docker’s internal structure is very graph-oriented, but the API is less so. It should be possible for most clients to “write themselves” (or at least configure themselves) using the data available through the API. At present, this is cumbersome.

Status

HyperDock is probably not suitable for production use yet.

Installation

gem install hyperdock

Usage

To launch it, just run:

hyperdock-api

The API will be available on port 8080.

Launch something with docker-compose (example)

For the purpose of documentation, we’ll use cloud-surveyor.

git clone https://github.com/colstrom/cloud-surveyor
cd cloud-surveyor
docker-compose up -d

This should run two service containers for cloud-surveyor:

  • redis
  • elasticmq

Explore!

The Root Resource

Let’s see what we have at the root resource (this should be a self-describing gateway to the rest of the API:

curl -s http://localhost:8080/ | jq .
{
  "_links": {
    "self": {
      "href": "/"
    },
    "projects": {
      "href": "/projects"
    },
    "project": {
      "href": "/project/{project}",
      "templated": true
    },
    "project:services": {
      "href": "/project/{project}/services",
      "templated": true
    },
    "containers": {
      "href": "/containers"
    },
    "container": {
      "href": "/container/{container}",
      "templated": true
    },
    "container:ports": {
      "href": "/container/{container}/ports",
      "templated": true
    }
  },
  "version": 1
}

What we see here (and in all the responses) is a list of resources reachable from this resource. Since this is the root level, we have a rough map of the main API.

/containers seems pretty standard for Docker, so let’s check out projects instead.

Projects

/projects lists all docker-compose projects running on this docker host.

curl -s localhost:8080/projects | jq .
{
  "_links": {
    "self": {
      "href": "/projects"
    },
    "project:cloudsurveyor": {
      "href": "/project/cloudsurveyor"
    }
  },
  "projects": [
    "cloudsurveyor"
  ]
}

Let’s follow the links, and see where we end up!

Project

/project/{project} returns a project. Projects have things like services, so we should expect a link for those.

curl -s localhost:8080/projects/cloudsurveyor | jq .
{
  "_links": {
    "self": {
      "href": "/project/cloudsurveyor"
    },
    "services": {
      "href": "/project/cloudsurveyor/services"
    }
  }
}

Services

/project/{project}/services returns a list of services for a project. These should correspond to the services described in your docker-compose.yaml.

curl -s localhost:8080/projects/cloudsurveyor/services | jq .
{
  "_links": {
    "self": {
      "href": "/project/cloudsurveyor/services"
    },
    "service:redis": {
      "href": "/project/cloudsurveyor/service/redis"
    },
    "service:elasticmq": {
      "href": "/project/cloudsurveyor/service/elasticmq"
    }
  },
  "services": [
    "redis",
    "elasticmq"
  ]
}

We can see two services here: redis and elasticmq. Compare this to the docker-compose.yaml in the cloud-surveyor repository:

---
version: "2"
services:
  elasticmq:
    image: colstrom/elasticmq
    ports:
      - "9324:9324"
  redis:
    image: redis:alpine
    ports:
      - "6379"

Let’s have a look at that redis service, shall we?

Service

/project/{project}/service/{service} returns a list of containers for the specified service.

curl -s localhost:8080/projects/cloudsurveyor/service/redis | jq .
{
  "_links": {
    "self": {
      "href": "/project/cloudsurveyor/service/redis"
    },
    "containers": [
      {
        "href": "/container/27d6320f12e28b57ea7b2cbf423e647ab7f56793d7622069c9dc1d2f7a8d362b"
      }
    ]
  }
}

Well, look at that: something that isn’t nested deeper under the same path!

What happens if we scale up the redis containers a bit?

docker-compose scale redis=3

And then check the service links again?

curl -s localhost:8080/projects/cloudsurveyor/service/redis | jq .
{
  "_links": {
    "self": {
      "href": "/project/cloudsurveyor/service/redis"
    },
    "containers": [
      {
        "href": "/container/a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351"
      },
      {
        "href": "/container/5dd8c8db1b7e767ca5529165bf337e70096e32e635346208eda173a0a1eb6c3c"
      },
      {
        "href": "/container/27e0bc50a08b7f54f375ba098a64733720e1287e0f18763c56150feb4869bd1b"
      }
    ]
  }
}

But what do those containers look like?

Container

/container/{container} returns information about a specific container, as you might expect.

curl -s localhost:8080/container/a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351 | jq .
{
  "_links": {
    "self": {
      "href": "/container/a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351"
    },
    "mounts": {
      "href": "/container/a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351/mounts"
    },
    "networks": {
      "href": "/container/a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351/networks"
    },
    "ports": {
      "href": "/container/a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351/ports"
    }
  },
  "info": {
    "Names": [
      "/cloudsurveyor_redis_2"
    ],
    "Image": "redis:alpine",
    "ImageID": "sha256:2aabafe89cbffe63a812e3965137f36df73488488a6ad4ba641272a3cf384cd1",
    "Command": "docker-entrypoint.sh redis-server",
    "Created": 1471154629,
    "Ports": [
      {
        "IP": "0.0.0.0",
        "PrivatePort": 6379,
        "PublicPort": 32770,
        "Type": "tcp"
      }
    ],
    "Labels": {
      "com.docker.compose.config-hash": "7823e6dcfbb9d488dc1be2e26cff00c9019451db8fcc5187f711f17e4a161a4f",
      "com.docker.compose.container-number": "2",
      "com.docker.compose.oneoff": "False",
      "com.docker.compose.project": "cloudsurveyor",
      "com.docker.compose.service": "redis",
      "com.docker.compose.version": "1.8.0"
    },
    "State": "running",
    "Status": "Up 4 minutes",
    "HostConfig": {
      "NetworkMode": "cloudsurveyor_default"
    },
    "NetworkSettings": {
      "Networks": {
        "cloudsurveyor_default": {
          "IPAMConfig": null,
          "Links": null,
          "Aliases": null,
          "NetworkID": "b6665ba6532fb26f8595c2155c1a5ca6fd9397b69c1033dc39ddfecc7abe470b",
          "EndpointID": "f416509bd5386316bfb84197d01ee173e41b741a6ef4b3a3545e7c03ead48a11",
          "Gateway": "172.19.0.1",
          "IPAddress": "172.19.0.5",
          "IPPrefixLen": 16,
          "IPv6Gateway": "",
          "GlobalIPv6Address": "",
          "GlobalIPv6PrefixLen": 0,
          "MacAddress": "02:42:ac:13:00:05"
        }
      }
    },
    "Mounts": [
      {
        "Name": "eba94001326155110ed4901f12936c5a0c05c79ca550fd023fa55e12df965a13",
        "Source": "/var/lib/docker/volumes/eba94001326155110ed4901f12936c5a0c05c79ca550fd023fa55e12df965a13/_data",
        "Destination": "/data",
        "Driver": "local",
        "Mode": "",
        "RW": true,
        "Propagation": ""
      }
    ],
    "id": "a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351"
  }
}

Ports

/container/{container}/ports returns port information in various configurations, for convenient discovery.

curl -s localhost:8080/container/a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351/ports | jq .
{
  "_links": {
    "self": {
      "href": "/container/a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351/ports"
    }
  },
  "all": [
    {
      "IP": "0.0.0.0",
      "PrivatePort": 6379,
      "PublicPort": 32770,
      "Type": "tcp"
    }
  ],
  "PublicPort": {
    "32770": [
      {
        "IP": "0.0.0.0",
        "PrivatePort": 6379,
        "PublicPort": 32770,
        "Type": "tcp"
      }
    ]
  },
  "PrivatePort": {
    "6379": [
      {
        "IP": "0.0.0.0",
        "PrivatePort": 6379,
        "PublicPort": 32770,
        "Type": "tcp"
      }
    ]
  },
  "IP": {
    "0.0.0.0": [
      {
        "IP": "0.0.0.0",
        "PrivatePort": 6379,
        "PublicPort": 32770,
        "Type": "tcp"
      }
    ]
  },
  "Type": {
    "tcp": [
      {
        "IP": "0.0.0.0",
        "PrivatePort": 6379,
        "PublicPort": 32770,
        "Type": "tcp"
      }
    ]
  },
  "tcp": [
    {
      "IP": "0.0.0.0",
      "PrivatePort": 6379,
      "PublicPort": 32770,
      "Type": "tcp"
    }
  ],
  "udp": []
}

So, from the top, we’ve found the ports bound on the host for a service container associated with a project. In the simple example here, that’s easy enough to dig out of the Docker API, but navigating by relationships gives us more flexibility and helps keep things manageable at scale.

Containers

/containers/ returns a list of all containers running on the host, with no particular ordering or filters.

curl -s localhost:8080/containers | jq .
{
  "_links": {
    "self": {
      "href": "/containers"
    },
    "container:a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351": {
      "href": "/container/a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351"
    },
    "container:5dd8c8db1b7e767ca5529165bf337e70096e32e635346208eda173a0a1eb6c3c": {
      "href": "/container/5dd8c8db1b7e767ca5529165bf337e70096e32e635346208eda173a0a1eb6c3c"
    },
    "container:27e0bc50a08b7f54f375ba098a64733720e1287e0f18763c56150feb4869bd1b": {
      "href": "/container/27e0bc50a08b7f54f375ba098a64733720e1287e0f18763c56150feb4869bd1b"
    },
    "container:47c9f36e225e722380ce0bf3a7a41365b754fad8df8c1a3c64b2223a6d6e3548": {
      "href": "/container/47c9f36e225e722380ce0bf3a7a41365b754fad8df8c1a3c64b2223a6d6e3548"
    }
  },
  "containers": [
    "a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351",
    "5dd8c8db1b7e767ca5529165bf337e70096e32e635346208eda173a0a1eb6c3c",
    "27e0bc50a08b7f54f375ba098a64733720e1287e0f18763c56150feb4869bd1b",
    "47c9f36e225e722380ce0bf3a7a41365b754fad8df8c1a3c64b2223a6d6e3548"
  ]
}

License

hyperdock is available under the MIT License. See LICENSE.txt for the full text.

Contributors