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.*