github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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 a model today, and be
    63    able to trust that in 2 years I will still be able to access/control/update that 
    64    model 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    model, and trust that all clients are still able to connect and manage the
    67    model.
    68  - As a user with a new Juju client, I want to be able to bootstrap a new model
    69    and have access to all the latest features for that model. (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 model. In case of high-availability a model 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  ### HTTP Requests
   104  
   105  Beside the communication using WebSockets there are several parts of the
   106  API using standard HTTP requests. Individual handlers registered for the
   107  according paths care for those requests.
   108  
   109  The first one is the charm handler, which supports HTTP POST to add a local 
   110  charm to the store provider and HTTP GET to retrieve or list charm files. The
   111  second one is the tools handler, which supports HTTP POST for tje upload of
   112  tools to the API server. Last but not least the API provides a backup handler
   113  which allows to use the storage for the backup of files via HTTP POST.
   114  
   115  ## System Architecture
   116  
   117  Core part of the API architecture is the *RPC* subsystem providing the functionality
   118  for communication and request dispatching. The latter isn't done statically. Instead
   119  a registered root object is is responsible for this task. This way different implementations
   120  can be used for tests and production. The subsystem also provides helpers to dynamically
   121  resolve the request to a method of a responsible instance and call it with possible
   122  request parameters. In case of a returned value the subsystem also marshals this and
   123  send it back as an answer to the caller.
   124  
   125  The *API server* subsystem implements the server side of the API. It provides a root
   126  object for the *RPC* subsystem using a global registry. Here factory methods are registered
   127  for request types and versions. 
   128  
   129  When receiving a request the *RPC* uses a defined finder method of the root object 
   130  interface to retrieve a *method caller* instance matching to the type, version and 
   131  request information that are part of the request. In case of success the *RPC* executes 
   132  a call of that method together with an optional ID of the typed instance and also
   133  optional arguments. The root object of the *API server* returns a method caller 
   134  which uses the factory method registered for type and version. This factory method
   135  is called with the *state*, *resources* and an *authorizer* as arguments. The resources 
   136  hold all the resources for a connection and will be cleaned up when the connection 
   137  terminates. The authorizer represents a value that can be asked for authorization 
   138  information on its associated authenticated entity. It allows an API 
   139  implementation to ask questions about the client that is currently connected.
   140  
   141  The result of calling the factory method is an initialized new instance of the request
   142  type to handle the request itself. The *RPC* subsystem maps the request name, which is
   143  part of the request, to one of the methods of this instance. There is a number of valid 
   144  methods signatures, depending on the possible combination of calling parameters, responses, 
   145  and errors (see description in *Component Design*). 
   146  
   147  *TODO: Go API client description.*
   148  
   149  ## Data Design
   150  
   151  ### Message
   152  
   153  API messages are encoded in JSON. The type `rpc.Request` represents
   154  a remote procedure call to be performed, absent its parameters. Those
   155  can be found in the type `rpc.Call`, which represents an active
   156  remote procedue call and embeds the request as well as other important
   157  parts of the request.
   158  
   159  #### Request
   160  
   161  - **RequestId** (Number) holds the sequence number of the request.
   162  - **Type** (String) holds the type of object to act on.
   163  - **Version** (Number) holds the version of Type we will be acting on.
   164  - **Id** (String) is the ID of a watched object; future implementations
   165    pass one ore more IDs as parameters.
   166  - **Request** (String) holds the action to perform on the object.
   167  - **Params** (JSON) holds the parameters as JSON structure, each request
   168    implementation out to accept bulk requests.
   169  
   170  #### Response
   171  
   172  - **RequestId** (Number) holds the sequence number of the request.
   173  - **Error** (String) holds the error, if any.
   174  - **ErrorCode** (String) holds the code of the error, if any.
   175  - **Response** (JSON) holds an optional response as JSON structure.
   176  
   177  ## Component Design
   178  
   179  ### RPC
   180  
   181  Core package for the API is [rpc](https://github.com/juju/juju/tree/master/rpc).
   182  It defines the `Codec` interface for the reading and writing of messages in an RPC
   183  session and the `MethodFinder` interface to retrieve the method to call for a request.
   184  The endpoint type `Conn` uses implementations of `Codec` and `MethodFinder`. This way
   185  diferent implementations can be used. 
   186  
   187  The standard `Codec` is implemented in [jsoncodec](https://github.com/juju/juju/tree/master/rpc/jsoncodec).
   188  It uses WebSockets for communication and JSON for encoding. The standard `MethodFinder`
   189  is implemented in [apiserver](https://github.com/juju/juju/tree/master/state/apiserver) (see
   190  below).
   191  
   192  The `MethodFinder` has to implement a method with the signature
   193  
   194  ```
   195  FindMethod(typeName string, version int, methodName string) (rpcreflect.MethodCaller, error)
   196  ```
   197  
   198  The returned `MethodCaller` is an interface implementing the method
   199  
   200  ```
   201  Call(objId string, arg reflect.Value) (reflect.Value, error)
   202  ```
   203  
   204  beside helpers returning the parameter and the result type. It executes the calling of the
   205  request method on an initialized instance matching to type, version and request name
   206  together with the request parameters and returning the request result. Those request
   207  methods implement the business logic and must follow on of the following signatures
   208  depending on the combination of parameter, result, and error.
   209  
   210  - `RequestMethod()`
   211  - `RequestMethod() ResultType`
   212  - `RequestMethod() (ResultType, error)`
   213  - `RequestMethod() error`
   214  - `RequestMethod(ParameterType)`
   215  - `RequestMethod(ParameterType) ResultType`
   216  - `RequestMethod(ParameterType) (ResultType, error)`
   217  - `RequestMethod(ParameterType) error`
   218  
   219  Both `ParameterType` and `ResultType` have to be structs. Possible results and
   220  errors are marshaled again to JSON and wrapped into an envelope containing also the 
   221  request identifier.
   222  
   223  #### Example
   224  
   225  If the client wants to change the addresses of a number of machines, it sends the
   226  request:
   227  
   228  ```
   229  {
   230  	RequestId: 1234,
   231  	Type: "Machiner",
   232  	Version: 0,
   233  	Request: "SetMachineAddresses",
   234  	Params: {
   235  		MachineAddresses: [
   236  			{Tag: "machine-1", Addresses: ...},
   237  			{Tag: "machine-2", Addresses: ...}
   238  		]
   239  	}
   240  }
   241  ```
   242  
   243  In this case the RPC will create and instance of `apiserver.MachinerAPI` in the
   244  requested version. This type provides a method `SetMachineAddresses()` with
   245  `params.SetMachinesAddresses` as argument, and `params.ErrorResults` and `error`
   246  as results. The parameters will be unmarshalled to the argument type and
   247  the method called with it.
   248  
   249  ### API Server
   250  
   251  The API server is implemented in the package [apiserver](https://github.com/juju/juju/tree/master/state/apiserver)
   252  and its sub-packages. It is started inside the *machine agent* by calling `apiserver.NewServer()`.
   253  The returned `Server` instance holds the server side of the API. After starting
   254  the server several handlers are registered. One of them is the API server
   255  by registering `Server.apiHandler()` as handler function. It receives the
   256  HTTP request and starts the individual WebSockets connection. Inside of
   257  `Server.serveConn()` it uses the WebSocket to etablish the RPC using 
   258  the JSON codec. 
   259  
   260  In case of a valid model UUID a new server controller instance is
   261  created using `apiserver.initialRoot` with an `apiserver.srvAdmin` for the
   262  login process. If this is successful the root is changed to `apiserver.srvRoot`
   263  for the real API request handling. It implements `FindMethod()` which is needed
   264  by the RPC to dispatch request to the according methods like described above.
   265  
   266  #### Facade Registration
   267  
   268  The registry for facades is implemented in the package
   269  [apiserver/common](https://github.com/juju/juju/tree/master/state/apiserver/common). It provides
   270  a function for the registration of facade constructor functions together with
   271  their name and a version number. They are called in an `init()` function in
   272  their respective packages.
   273  
   274  ```
   275  func init() {
   276  	common.RegisterStandardFacade("Machiner", 0, NewMachinerAPI)
   277  	common.RegisterStandardFacade("Machiner", 1, NewMachinerAPIv1)
   278  }
   279  ```
   280  
   281  ### API Client
   282  
   283  The according client logic used by the Juju CLI and the Juju daemon, which are also
   284  developed in Go, is located in [api](https://github.com/juju/juju/tree/master/state/api).
   285  
   286  Clients connect with `api.Open()` which returns a `api.State` instance as entry point.
   287  This instance can perform low level RPC calls with the method `Call()`. It also provide
   288  the methods `AllFacadeVersion()` to retrieve a map of all facades with slices of all
   289  their versions. More useful is the method `BestFacadeVersion()` to retrieve the best
   290  available version for a named facade. So in case of no matching version a client can
   291  react with an error instead of performing an invalid call.
   292  
   293  *TODO: Describe how individual client calls are implemented.*
   294  
   295  ### Watching
   296  
   297  *TODO: Describe watching using the API.*