Skip to content

Commit

Permalink
Implemented more RESTful API methods and endpoints for client and ser…
Browse files Browse the repository at this point in the history
…ver (#179)

* uri -> base_url

* added more endpoints to API spec

* added APIClient class

* removed server.daemon module

* added client errors module

* added WIP BugManager to client module

* updated client module

* added client module to setup.py

* updated API client to accept path strings rather than path components

* added from_dict and to_dict to Bug

* added to_dict to compiler objects

* added to_dict methods to testing module

* removed debugging statement from Bug.from_dict

* implemented bug.build endpoint

* implemented /bugs/<uid>/installed endpoint

* implemented /bugs/<uid>/coverage endpoint

* added basic container manager to client module

* added container provision endpoint

* implemented is_installed endpoint

* added extra error codes

* added image section to API spec.

* added to_dict to Container

* added from_dict to Container

* fixed name of method that implements an endpoint

* modified Container.tools to return a list

* modified provision to return 200 status code

* updated provision endpoint in client module

* implemented GET /containers/<uid> route

* added __getitem__ to container manager in client module

* added container deletion to client module

* added __contains__ to container manager in client module

* added is_alive endpoint to server module

* added is_alive method to container manager in client module

* updated API spec with GET /containers/<id>/alive

* added json keyword argument to APIClient

* added exec method to container manager in client module

* implemented exec endpoint in server module

* implemented exec endpoint in client module

* added exec to API spec.

* added WIP test endpoint to container manager in client module

* added TEST_NOT_FOUND to error codes

* added alias for command in container manager

* added missing type sig

* fixed aliases in container manager

* implemented test execution endpoint in server module

* added simple client example script

* removed POST /containers/:id from API spec.

* added extra documentation to ContainerManager in client module

* docs: documented exec method for api.client module

* implemented compile method in client module

* added to_dict to Compiler

* added missing -> None to constructor

* fixed typing issues in client errors module

* added NotImplementedError to unimplemented client methods

* added missing -> None to constructor

* tweaked return type for Client.__delitem__

* tweaked exceptions in client bug manager

* added to/from dict to CompilationOutcome

* fixed bad var access

* fixed bad return type

* updated version to 2.0.14
  • Loading branch information
ChrisTimperley authored Apr 20, 2018
1 parent 1ff0397 commit df042e1
Show file tree
Hide file tree
Showing 19 changed files with 839 additions and 160 deletions.
115 changes: 100 additions & 15 deletions api-specification.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ info:
title: BugZoo - API Specification
contact:
name: Christopher Timperley
url: https://github.com/ChrisTimperley/bugzoo-api-docs
url: https://github.com/squaresLab/BugZoo
host: localhost
basePath: /bugzoo

Expand All @@ -16,6 +16,8 @@ tags:
description: All operations related to containers
- name: bug
description: All operations related to bugs
- name: image
description: All operations related to Docker images


definitions:
Expand All @@ -41,6 +43,28 @@ definitions:
the user upon the creation of the container, an ID will automatically
be generated instead.
ExecResponse:
type: object
required:
- code
- duration
- output
properties:
code:
type: number
format: int
description: >-
The exit code produced by the command.
duration:
type: number
format: float
description: >-
The number of seconds taken to execute the command.
output:
type: string
description: >-
The output produced by the command.
paths:
###############################################################################
Expand Down Expand Up @@ -88,10 +112,12 @@ paths:
description: OK
schema:
$ref: '#/definitions/Bug'
404:
description: Bug not found.

/bugs/${id}/build:
post:
summary: Builds a bug.
summary: Builds the Docker image for a bug from its blueprint.
description: Builds the Docker image for a given bug.
tags:
- bug
Expand All @@ -111,6 +137,29 @@ paths:
404:
description: Bug not found.

/bugs/${id}/download:
post:
summary: Downloads the Docker image for a bug.
description: Downloads the Docker image for a bug from DockerHub.
tags:
- bug
produces:
- application/json
parameters:
- in: path
name: id
type: string
description: The unique identifier of the bug.
required: true
responses:
204:
description: Successfully downloaded Docker image.
200:
description: Docker image already installed.
404:
description: Bug not found.


###############################################################################
#
# Containers
Expand Down Expand Up @@ -159,19 +208,6 @@ paths:
404:
description: Container not found.

post:
summary: Provisions a container.
description: Provisions a new container for a given bug.
tags:
- container
produces:
- application/json
responses:
204:
description: OK
404:
description: No bug exists with specified UID.

delete:
summary: Destroy container.
description: Destroys a given container.
Expand Down Expand Up @@ -246,6 +282,55 @@ paths:
404:
description: Container not found.

/containers/${id}/exec:
post:
summary: Executes a command inside the container.
description: >-
Executes a given command inside the container.
tags:
- container
produces:
- application/json
parameters:
- in: path
name: id
type: string
description: The unique identifier of the container.
required: true
responses:
200:
description: OK.
schema:
$ref: '#/definitions/ExecResponse'
404:
description: Container not found.

/containers/${container-id}/alive:
get:
summary: Checks whether container is alive.
description: >-
Checks whether the underlying Docker container for a given BugZoo
container is alive.
tags:
- container
produces:
- application/json
parameters:
- in: path
name: container-id
type: string
description: The unique identifier of the container.
required: true
responses:
200:
description: OK
schema:
type: boolean
description: >-
A flag indicating whether or not the container is alive.
404:
description: Container not found.

/containers/${container-id}/patch:
patch:
summary: Patches source code inside container.
Expand Down
97 changes: 20 additions & 77 deletions bugzoo/client/__init__.py
Original file line number Diff line number Diff line change
@@ -1,79 +1,22 @@
from urllib.parse import urljoin
from .api import APIClient
from .bug import BugManager
from .container import ContainerManager

import requests

import bugzoo.core.errors as errors
#
# class Client(object):
# def __init__(self,
# uri: str = None):
# if uri is None:
# uri = "https://127.0.0.1:6060"
# self.__uri = uri
#
# @property
# def uri(self) -> str:
# """
# The server URI.
# """
# return self.__uri
#
# @property
# def base_url(self) -> str:
# """
# The base URL of the BugZoo API exposed by the server.
# """
# return self.__uri
#
# # TODO: add BuildOutcome to 'core' module
# def build_bug(self, bug_id: str) -> BuildOutcome:
# """
# Builds the Docker image for a given bug scenario.
#
# Parameters:
# bug_id: The unique identifier for the bug scenario.
#
# Returns:
# A summary of the build outcome.
# """
# url = urljoin(self.base_url, 'bug', bug_id, 'build')
# r = requests.post(url)
#
# # TODO: implement ImageBuildFailed.from_dict
# if r.status_code == 200:
# raise errors.ImageBuildFailed.from_dict(r.json())
#
# if r.status_code == 400:
# raise errors.BugAlreadyBuilt(bug_id)
#
# if r.status_code == 404:
# return BuildOutcome.from_dict(r.json())
#
# # TODO: add Container to 'client' module
# def provision(self, bug_id: str) -> Container:
# """
# Attempts to provision a container for a given bug.
#
# Parameters:
# bug_id: The unique identifier for the bug scenario.
#
# Returns:
# A description of the provisioned container.
#
# Raises:
# BugNotFound: If no bug is found with the given name.
# """
# url = urljoin(self.base_url, 'container')
# r = requests.post(url)
#
# if r.status_code == 404:
# raise errors.BugNotFound(bug_id)
#
# if r.status_code == 204:
# return Container.from_dict(r.json())
#
# raise errors.UnexpectedStatusCode(r.status_code)
#
# # TODO: add TestOutcome to 'core' module
# def execute_test(self, container_id: str) -> TestOutcome:
# pass
class Client(object):
def __init__(self,
base_url: str = None
) -> None:
if base_url is None:
base_url = "http://127.0.0.1:6060"
self.__api = APIClient(base_url)
self.__bugs = BugManager(self.__api)
self.__containers = ContainerManager(self.__api)

@property
def bugs(self) -> BugManager:
return self.__bugs

@property
def containers(self) -> ContainerManager:
return self.__containers
62 changes: 45 additions & 17 deletions bugzoo/client/api.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,45 @@
# class APIClient(object):
# def __init__(self, base_url: str):
# self.__base_url = base_url
#
# def _url(self, path: str) -> str:
# """
# Computes the URL for a resource located at a given path on the server.
# """
#
#
# def _get(self, path: str) ->
#
# def tool_upload(self, tool_name: str) -> None:
# pass
#
# def tool_download(self, tool_name: str) -> None:
# pass
from typing import Optional, Any

import requests
import urllib.parse
import logging


class APIClient(object):
def __init__(self, base_url: str) -> None:
logging.basicConfig(level=logging.DEBUG)
self.__logger = logging.getLogger('api')
self.__base_url = base_url

def _url(self, path: str) -> str:
"""
Computes the URL for a resource located at a given path on the server.
"""
return urllib.parse.urljoin(self.__base_url, path)

def get(self,
path: str,
*,
json: Optional[Any] = None
) -> requests.Response:
url = self._url(path)
self.__logger.info('GET: %s', url)
return requests.get(url, json=json)

def post(self,
path: str,
*,
json: Optional[Any] = None
) -> requests.Response:
url = self._url(path)
self.__logger.info('POST: %s', url)
return requests.post(url, json=json)

def delete(self,
path: str,
*,
json: Optional[Any] = None
) -> requests.Response:
url = self._url(path)
self.__logger.info('DELETE: %s', url)
return requests.delete(url, json=json)
Loading

0 comments on commit df042e1

Please sign in to comment.