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