github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/doc/design/juju-api-design-specification.md (about)

     1  # Juju API Design Specification
     2  
     3  ## Status
     4  
     5  *Work in Progress*
     6  
     7  ## Contents
     8  
     9  1. [Introduction](#introduction)
    10  2. [Requirements](#requirements)
    11  3. [System Overview](#system-overview)
    12  4. [System Architecture](#system-architecture)
    13  5. [Data Design](#data-design)
    14  6. [Component Design](#component-design)
    15  
    16  ## Introduction
    17  
    18  ### Purpose
    19  
    20  The *Juju API* is the central interface to control the functionality of Juju. It 
    21  is used by the command line tools as well as by the Juju GUI. This document 
    22  describes the API design for developers maintaining and extending the API.
    23  
    24  ### Scope
    25  
    26  Juju's working model is based on the description of a wanted environment in a 
    27  DBMS. Different workers observe the changes of this description and perform the 
    28  needed actions so that the reality is matching. Goal of the API is to provide a 
    29  secure, scalable, and high-available way to perform the needed changes or queries 
    30  with different clients like the Juju CLI, which is written in Go, or the Juju 
    31  GUI, which is written in JavaScript. Additionally the API provides multiple versions 
    32  of its functionality for a predictable behavior in environments with a mixed set of 
    33  clients.
    34  
    35  ### Overview
    36  
    37  This document provides a detailed specification of 
    38  
    39  - the basic components of the API, 
    40  - the types of requests,
    41  - the data format of requests and responses, 
    42  - the marshaling and unmarshaling of requests and responses, 
    43  - the dispatching of requests to their according business logic, and 
    44  - the versioning of the API functionality.
    45  
    46  ### Reference Material
    47  
    48  *TODO: Check if there are documents or links to reference.*
    49  
    50  ### Definitions and Acronyms
    51  
    52  - **Facade** A registered and versioned type which is addressed by API 
    53    requests. It creates an instance providing a set of methods for one 
    54    entity or a cohesive functionality.
    55  - **JSON** JavaScript Object Notation
    56  - **RPC** Remote Procedure Calls
    57  - **WebSocket** A full-duplex TCP communication protocol standardized by
    58    the W3C ([see RfC 6455](http://tools.ietf.org/html/rfc6455)).
    59  
    60  ## Requirements
    61  
    62  - As a user of Trusty, I want to be able to bootstrap an environment today, and be 
    63    able to trust that in 2 years I will still be able to access/control/update that 
    64    environment even when I’ve upgraded my juju client tools.
    65  - As a group with heterogeneous clients, we want to be able to bootstrap a new 
    66    environment, and trust that all clients are still able to connect and manage the 
    67    environment.
    68  - As a user with a new Juju client, I want to be able to bootstrap a new environment 
    69    and have access to all the latest features for that environment. (HA, User accounts, 
    70    etc.)
    71  - As an Agent, I want to be able to communicate with the API server to be able to perform 
    72    my regular tasks, and be able to upgrade to stay in sync with the desired agent version. 
    73    It is expected that we won’t always be able to stay in perfect synchronization, 
    74    especially in an environment with heterogeneous architectures and platforms.
    75  - As a developer, I want to be able to make a change to an existing named API in such 
    76    a fashion that new clients are able to make use of the new functionality, but older 
    77    clients can still use the API in a compatible fashion.
    78  
    79  ## System Overview
    80  
    81  ### WebSockets
    82  
    83  The major functionality of the Juju API is based on a request/response model 
    84  using WebSockets as communication layer. Each client connects to the API server 
    85  of the environment. In case of high-availability an environment provides multiple 
    86  API servers and a client connects to one of them.
    87  
    88  The requests sent via this connection are encoded in JSON. They contain a type
    89  responsible for anwering the request, the individual request method and possible
    90  parameters. Additionally they contain a unique identifier which allows the caller
    91  to identify responses to asynchronous requests.
    92  
    93  This part of the API handles two different major types of requests:
    94  
    95  1. simple requests, which may return a response, and
    96  2. watcher requests for the observation of changes to collections or individual 
    97     entities.
    98  
    99  Watcher requests are also request/response calls. But they create server-side
   100  resources which respond to future `Next()` calls. Those retrieve already happened
   101  changes or wait for the next ones and return them to the caller.
   102  
   103  Another handler using WebSockets for delivering a stream of data is the
   104  debug log handler. It opens the `all-machines.log` and continuously streams
   105  the data to the client.
   106  
   107  ### HTTP Requests
   108  
   109  Beside the communication using WebSockets there are several parts of the
   110  API using standard HTTP requests. Individual handlers registered for the
   111  according paths care for those requests.
   112  
   113  The first one is the charm handler, which supports HTTP POST to add a local 
   114  charm to the store provider and HTTP GET to retrieve or list charm files. The
   115  second one is the tools handler, which supports HTTP POST for tje upload of
   116  tools to the API server. Last but not least the API provides a backup handler
   117  which allows to use the storage for the backup of files via HTTP POST.
   118  
   119  ## System Architecture
   120  
   121  Core part of the API architecture is the *RPC* subsystem providing the functionality
   122  for communication and request dispatching. The latter isn't done statically. Instead
   123  a registered root object is is responsible for this task. This way different implementations
   124  can be used for tests and production. The subsystem also provides helpers to dynamically
   125  resolve the request to a method of a responsible instance and call it with possible
   126  request parameters. In case of a returned value the subsystem also marshals this and
   127  send it back as an answer to the caller.
   128  
   129  The *API server* subsystem implements the server side of the API. It provides a root
   130  object for the *RPC* subsystem using a global registry. Here factory methods are registered
   131  for request types and versions. 
   132  
   133  When receiving a request the *RPC* uses a defined finder method of the root object 
   134  interface to retrieve a *method caller* instance matching to the type, version and 
   135  request information that are part of the request. In case of success the *RPC* executes 
   136  a call of that method together with an optional ID of the typed instance and also
   137  optional arguments. The root object of the *API server* returns a method caller 
   138  which uses the factory method registered for type and version. This factory method
   139  is called with the *state*, *resources* and an *authorizer* as arguments. The resources 
   140  hold all the resources for a connection and will be cleaned up when the connection 
   141  terminates. The authorizer represents a value that can be asked for authorization 
   142  information on its associated authenticated entity. It allows an API 
   143  implementation to ask questions about the client that is currently connected.
   144  
   145  The result of calling the factory method is an initialized new instance of the request
   146  type to handle the request itself. The *RPC* subsystem maps the request name, which is
   147  part of the request, to one of the methods of this instance. There is a number of valid 
   148  methods signatures, depending on the possible combination of calling parameters, responses, 
   149  and errors (see description in *Component Design*). 
   150  
   151  *TODO: Go API client description.*
   152  
   153  ## Data Design
   154  
   155  ### Message
   156  
   157  API messages are encoded in JSON. The type `rpc.Request` represents
   158  a remote procedure call to be performed, absent its parameters. Those
   159  can be found in the type `rpc.Call`, which represents an active
   160  remote procedue call and embeds the request as well as other important
   161  parts of the request.
   162  
   163  #### Request
   164  
   165  - **RequestId** (Number) holds the sequence number of the request.
   166  - **Type** (String) holds the type of object to act on.
   167  - **Version** (Number) holds the version of Type we will be acting on.
   168  - **Id** (String) is the ID of a watched object; future implementations
   169    pass one ore more IDs as parameters.
   170  - **Request** (String) holds the action to perform on the object.
   171  - **Params** (JSON) holds the parameters as JSON structure, each request
   172    implementation out to accept bulk requests.
   173  
   174  #### Response
   175  
   176  - **RequestId** (Number) holds the sequence number of the request.
   177  - **Error** (String) holds the error, if any.
   178  - **ErrorCode** (String) holds the code of the error, if any.
   179  - **Response** (JSON) holds an optional response as JSON structure.
   180  
   181  ## Component Design
   182  
   183  ### RPC
   184  
   185  Core package for the API is [rpc](https://github.com/juju/juju/tree/master/rpc).
   186  It defines the `Codec` interface for the reading and writing of messages in an RPC
   187  session and the `MethodFinder` interface to retrieve the method to call for a request.
   188  The endpoint type `Conn` uses implementations of `Codec` and `MethodFinder`. This way
   189  diferent implementations can be used. 
   190  
   191  The standard `Codec` is implemented in [jsoncodec](https://github.com/juju/juju/tree/master/rpc/jsoncodec).
   192  It uses WebSockets for communication and JSON for encoding. The standard `MethodFinder`
   193  is implemented in [apiserver](https://github.com/juju/juju/tree/master/state/apiserver) (see
   194  below).
   195  
   196  The `MethodFinder` has to implement a method with the signature
   197  
   198  ```
   199  FindMethod(typeName string, version int, methodName string) (rpcreflect.MethodCaller, error)
   200  ```
   201  
   202  The returned `MethodCaller` is an interface implementing the method
   203  
   204  ```
   205  Call(objId string, arg reflect.Value) (reflect.Value, error)
   206  ```
   207  
   208  beside helpers returning the parameter and the result type. It executes the calling of the
   209  request method on an initialized instance matching to type, version and request name
   210  together with the request parameters and returning the request result. Those request
   211  methods implement the business logic and must follow on of the following signatures
   212  depending on the combination of parameter, result, and error.
   213  
   214  - `RequestMethod()`
   215  - `RequestMethod() ResultType`
   216  - `RequestMethod() (ResultType, error)`
   217  - `RequestMethod() error`
   218  - `RequestMethod(ParameterType)`
   219  - `RequestMethod(ParameterType) ResultType`
   220  - `RequestMethod(ParameterType) (ResultType, error)`
   221  - `RequestMethod(ParameterType) error`
   222  
   223  Both `ParameterType` and `ResultType` have to be structs. Possible results and
   224  errors are marshaled again to JSON and wrapped into an envelope containing also the 
   225  request identifier.
   226  
   227  #### Example
   228  
   229  If the client wants to change the addresses of a number of machines, it sends the
   230  request:
   231  
   232  ```
   233  {
   234  	RequestId: 1234,
   235  	Type: "Machiner",
   236  	Version: 0,
   237  	Request: "SetMachineAddresses",
   238  	Params: {
   239  		MachineAddresses: [
   240  			{Tag: "machine-1", Addresses: ...},
   241  			{Tag: "machine-2", Addresses: ...}
   242  		]
   243  	}
   244  }
   245  ```
   246  
   247  In this case the RPC will create and instance of `apiserver.MachinerAPI` in the
   248  requested version. This type provides a method `SetMachineAddresses()` with
   249  `params.SetMachinesAddresses` as argument, and `params.ErrorResults` and `error`
   250  as results. The parameters will be unmarshalled to the argument type and
   251  the method called with it.
   252  
   253  ### API Server
   254  
   255  The API server is implemented in the package [apiserver](https://github.com/juju/juju/tree/master/state/apiserver)
   256  and its sub-packages. It is started inside the *machine agent* by calling `apiserver.NewServer()`.
   257  The returned `Server` instance holds the server side of the API. After starting
   258  the server several handlers are registered. One of them is the API server
   259  by registering `Server.apiHandler()` as handler function. It receives the
   260  HTTP request and starts the individual WebSockets connection. Inside of
   261  `Server.serveConn()` it uses the WebSocket to etablish the RPC using 
   262  the JSON codec. 
   263  
   264  In case of a valid environment UUID a new server state server instance is
   265  created using `apiserver.initialRoot` with an `apiserver.srvAdmin` for the
   266  login process. If this is successful the root is changed to `apiserver.srvRoot`
   267  for the real API request handling. It implements `FindMethod()` which is needed
   268  by the RPC to dispatch request to the according methods like described above.
   269  
   270  #### Facade Registration
   271  
   272  The registry for facades is implemented in the package
   273  [apiserver/common](https://github.com/juju/juju/tree/master/state/apiserver/common). It provides
   274  a function for the registration of facade constructor functions together with
   275  their name and a version number. They are called in an `init()` function in
   276  their respective packages.
   277  
   278  ```
   279  func init() {
   280  	common.RegisterStandardFacade("Machiner", 0, NewMachinerAPI)
   281  	common.RegisterStandardFacade("Machiner", 1, NewMachinerAPIv1)
   282  }
   283  ```
   284  
   285  ### API Client
   286  
   287  The according client logic used by the Juju CLI and the Juju daemon, which are also
   288  developed in Go, is located in [api](https://github.com/juju/juju/tree/master/state/api).
   289  
   290  Clients connect with `api.Open()` which returns a `api.State` instance as entry point.
   291  This instance can perform low level RPC calls with the method `Call()`. It also provide
   292  the methods `AllFacadeVersion()` to retrieve a map of all facades with slices of all
   293  their versions. More useful is the method `BestFacadeVersion()` to retrieve the best
   294  available version for a named facade. So in case of no matching version a client can
   295  react with an error instead of performing an invalid call.
   296  
   297  *TODO: Describe how individual client calls are implemented.*
   298  
   299  ### Watching
   300  
   301  *TODO: Describe watching using the API.*