├── .gitignore ├── .npmignore ├── COMPAT.md ├── LICENSE ├── README.md ├── index.js ├── lib ├── collections.js ├── errors.js ├── k8s.js └── request.js ├── package-lock.json ├── package.json └── test ├── config.json ├── guestbook-go ├── README.md ├── guestbook-controller.json ├── guestbook-service.json ├── redis-master-controller.json ├── redis-master-service.json ├── redis-slave-controller.json └── redis-slave-service.json ├── guestbook_go_test.js ├── integrate.js ├── json ├── horizontalpodautoscaler.json ├── namespace.json ├── pod.json ├── rc.json ├── service.json ├── test.json ├── wordpress-controller.json └── wordpress-service.json ├── results ├── pod.json └── pods.json ├── test-containerService.js ├── test-endpoints.js ├── test-events.js ├── test-horizontalpodautoscaler.js ├── test-namespaces.js ├── test-nodes.js ├── test-pods.js ├── test-services.js └── test-watch-pods.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .npmignore 3 | node_modules/ 4 | test/results/ 5 | test/config.json 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.un~ 2 | .git 3 | *.swp 4 | test 5 | -------------------------------------------------------------------------------- /COMPAT.md: -------------------------------------------------------------------------------- 1 | # Kubernetes API Compatibility Status 2 | [Kubernetes Swagger Specification](http://kubernetes.io/third_party/swagger-ui/) 3 | 4 | This document details the client's known compatibility with version `v1` 5 | of the Kubernetes API endpoints. It shall be updated continuously as new 6 | testing information is collected. 7 | 8 | Independent testing is appreciated. If tests are performed on any API 9 | endpoints that have not yet been verified, a pull request should be 10 | submitted to add the test results to the appropriate table. 11 | 12 | #### Legend 13 | | Status | Description of status key 14 | |:------:|:--------------------------- 15 | | OK | Main endpoint and all tested parameters are fully functional 16 | | PART | Main endpoint works but some optional parameters do not 17 | | NONE | Main endpoint does not work 18 | | ? | Unknown, not yet tested 19 | 20 | 21 | ## Primary API Endpoints 22 | 23 | | | Events | Status | 24 | |:---------:|:--------------------------------|:------:| 25 | | `GET` | events | OK 26 | | `GET` | namespaces/{ns}/events | OK 27 | | `POST` | namespaces/{ns}/events | ? 28 | | `GET` | namespaces/{ns}/events/{name} | OK 29 | | `PUT` | namespaces/{ns}/events/{name} | ? 30 | | `PATCH` | namespaces/{ns}/events/{name} | ? 31 | | `DELETE` | namespaces/{ns}/events/{name} | ? 32 | 33 | 34 | | | Endpoints | Status | 35 | |:---------:|:------------------------------------|:------:| 36 | | `GET` | endpoints | OK 37 | | `GET` | namespaces/{ns}/endpoints | OK 38 | | `POST` | namespaces/{ns}/endpoints | ? 39 | | `GET` | namespaces/{ns}/endpoints/{name} | OK 40 | | `PUT` | namespaces/{ns}/endpoints/{name} | ? 41 | | `PATCH` | namespaces/{ns}/endpoints/{name} | ? 42 | | `DELETE` | namespaces/{ns}/endpoints/{name} | ? 43 | 44 | 45 | | | Limit Ranges | Status | 46 | |:---------:|:------------------------------------|:------:| 47 | | `GET` | limitranges | OK 48 | | `GET` | namespaces/{ns}/limitranges | OK 49 | | `POST` | namespaces/{ns}/limitranges | ? 50 | | `GET` | namespaces/{ns}/limitranges/{name} | OK 51 | | `PUT` | namespaces/{ns}/limitranges/{name} | ? 52 | | `PATCH` | namespaces/{ns}/limitranges/{name} | ? 53 | | `DELETE` | namespaces/{ns}/limitranges/{name} | ? 54 | 55 | 56 | | | Namespaces | Status | 57 | |:---------:|:----------------------------|:------:| 58 | | `GET` | namespaces | OK 59 | | `POST` | namespaces | OK 60 | | `GET` | namespaces/{name} | OK 61 | | `PUT` | namespaces/{name} | OK 62 | | `PATCH` | namespaces/{name} | OK 63 | | `DELETE` | namespaces/{name} | OK 64 | | `PUT` | namespaces/{name}/finalize | ? 65 | | `PUT` | namespaces/{name}/status | ? 66 | 67 | 68 | | | Nodes | Status | 69 | |:---------:|:----------------------|:------:| 70 | | `GET` | nodes | OK 71 | | `POST` | nodes | ? 72 | | `GET` | nodes/{name} | OK 73 | | `PUT` | nodes/{name} | ? 74 | | `PATCH` | nodes/{name} | OK 75 | | `DELETE` | nodes/{name} | ? 76 | | `PUT` | nodes/{name}/status | ? 77 | 78 | 79 | | | Persistent Volume Claims | Status | 80 | |:---------:|:------------------------------------------------------|:------:| 81 | | `GET` | persistentvolumeclaims | ? 82 | | `GET` | namespaces/{ns}/persistentvolumeclaims | ? 83 | | `POST` | namespaces/{ns}/persistentvolumeclaims | ? 84 | | `GET` | namespaces/{ns}/persistentvolumeclaims/{name} | ? 85 | | `PUT` | namespaces/{ns}/persistentvolumeclaims/{name} | ? 86 | | `PATCH` | namespaces/{ns}/persistentvolumeclaims/{name} | ? 87 | | `DELETE` | namespaces/{ns}/persistentvolumeclaims/{name} | ? 88 | | `PUT` | namespaces/{ns}/persistentvolumeclaims/{name}/status | ? 89 | 90 | 91 | | | Persistent Volumes | Status | 92 | |:---------:|:----------------------------------|:------:| 93 | | `GET` | persistentvolumes | ? 94 | | `POST` | persistentvolumes | ? 95 | | `GET` | persistentvolumes/{name} | ? 96 | | `PUT` | persistentvolumes/{name} | ? 97 | | `PATCH` | persistentvolumes/{name} | ? 98 | | `DELETE` | persistentvolumes/{name} | ? 99 | | `PUT` | persistentvolumes/{name}/status | ? 100 | 101 | 102 | | | Pod Templates | Status | 103 | |:---------:|:--------------------------------------|:------:| 104 | | `GET` | podtemplates | ? 105 | | `GET` | namespaces/{ns}/podtemplates | ? 106 | | `POST` | namespaces/{ns}/podtemplates | ? 107 | | `GET` | namespaces/{ns}/podtemplates/{name} | ? 108 | | `PUT` | namespaces/{ns}/podtemplates/{name} | ? 109 | | `PATCH` | namespaces/{ns}/podtemplates/{name} | ? 110 | | `DELETE` | namespaces/{ns}/podtemplates/{name} | ? 111 | 112 | 113 | | | Pods | Status | 114 | |:---------:|:----------------------------------------------|:------:| 115 | | `GET` | pods | OK 116 | | `GET` | namespaces/{ns}/pods | OK 117 | | `POST` | namespaces/{ns}/pods | ? 118 | | `GET` | namespaces/{ns}/pods/{name} | OK 119 | | `PUT` | namespaces/{ns}/pods/{name} | ? 120 | | `PATCH` | namespaces/{ns}/pods/{name} | ? 121 | | `DELETE` | namespaces/{ns}/pods/{name} | ? 122 | | `GET` | namespaces/{ns}/pods/{name}/attach | ? 123 | | `POST` | namespaces/{ns}/pods/{name}/attach | ? 124 | | `POST` | namespaces/{ns}/pods/{name}/binding | ? 125 | | `GET` | namespaces/{ns}/pods/{name}/exec | ? 126 | | `POST` | namespaces/{ns}/pods/{name}/exec | ? 127 | | `GET` | namespaces/{ns}/pods/{name}/log | ? 128 | | `GET` | namespaces/{ns}/pods/{name}/portforward | ? 129 | | `POST` | namespaces/{ns}/pods/{name}/portforward | ? 130 | | `GET` | namespaces/{ns}/pods/{name}/proxy | ? 131 | | `PUT` | namespaces/{ns}/pods/{name}/proxy | ? 132 | | `POST` | namespaces/{ns}/pods/{name}/proxy | ? 133 | | `DELETE` | namespaces/{ns}/pods/{name}/proxy | ? 134 | | `OPTIONS` | namespaces/{ns}/pods/{name}/proxy | ? 135 | | `HEAD` | namespaces/{ns}/pods/{name}/proxy | ? 136 | | `GET` | namespaces/{ns}/pods/{name}/proxy/{path:*} | ? 137 | | `PUT` | namespaces/{ns}/pods/{name}/proxy/{path:*} | ? 138 | | `POST` | namespaces/{ns}/pods/{name}/proxy/{path:*} | ? 139 | | `DELETE` | namespaces/{ns}/pods/{name}/proxy/{path:*} | ? 140 | | `OPTIONS` | namespaces/{ns}/pods/{name}/proxy/{path:*} | ? 141 | | `HEAD` | namespaces/{ns}/pods/{name}/proxy/{path:*} | ? 142 | | `PUT` | namespaces/{ns}/pods/{name}/status | ? 143 | 144 | 145 | | | Replication Controllers | Status | 146 | |:---------:|:------------------------------------------------------|:------:| 147 | | `GET` | replicationcontrollers | OK 148 | | `GET` | namespaces/{ns}/replicationcontrollers | OK 149 | | `POST` | namespaces/{ns}/replicationcontrollers | ? 150 | | `GET` | namespaces/{ns}/replicationcontrollers/{name} | ? 151 | | `PUT` | namespaces/{ns}/replicationcontrollers/{name} | ? 152 | | `PATCH` | namespaces/{ns}/replicationcontrollers/{name} | ? 153 | | `DELETE` | namespaces/{ns}/replicationcontrollers/{name} | ? 154 | | `PUT` | namespaces/{ns}/replicationcontrollers/{name}/status | ? 155 | 156 | 157 | | | Resource Quotas | Status | 158 | |:---------:|:----------------------------------------------|:------:| 159 | | `GET` | resourcequotas | ? 160 | | `GET` | namespaces/{ns}/resourcequotas | ? 161 | | `POST` | namespaces/{ns}/resourcequotas | ? 162 | | `GET` | namespaces/{ns}/resourcequotas/{name} | ? 163 | | `PUT` | namespaces/{ns}/resourcequotas/{name} | ? 164 | | `PATCH` | namespaces/{ns}/resourcequotas/{name} | ? 165 | | `DELETE` | namespaces/{ns}/resourcequotas/{name} | ? 166 | | `PUT` | namespaces/{ns}/resourcequotas/{name}/status | ? 167 | 168 | 169 | | | Secrets | Status | 170 | |:---------:|:----------------------------------|:------:| 171 | | `GET` | secrets | ? 172 | | `GET` | namespaces/{ns}/secrets | ? 173 | | `POST` | namespaces/{ns}/secrets | ? 174 | | `GET` | namespaces/{ns}/secrets/{name} | ? 175 | | `PUT` | namespaces/{ns}/secrets/{name} | ? 176 | | `PATCH` | namespaces/{ns}/secrets/{name} | ? 177 | | `DELETE` | namespaces/{ns}/secrets/{name} | ? 178 | 179 | 180 | | | Service Accounts | Status | 181 | |:---------:|:------------------------------------------|:------:| 182 | | `GET` | serviceaccounts | ? 183 | | `GET` | namespaces/{ns}/serviceaccounts | ? 184 | | `POST` | namespaces/{ns}/serviceaccounts | ? 185 | | `GET` | namespaces/{ns}/serviceaccounts/{name} | ? 186 | | `PUT` | namespaces/{ns}/serviceaccounts/{name} | ? 187 | | `PATCH` | namespaces/{ns}/serviceaccounts/{name} | ? 188 | | `DELETE` | namespaces/{ns}/serviceaccounts/{name} | ? 189 | 190 | 191 | | | Services | Status | 192 | |:---------:|:----------------------------------|:------:| 193 | | `GET` | services | OK 194 | | `GET` | namespaces/{ns}/services | OK 195 | | `POST` | namespaces/{ns}/services | ? 196 | | `GET` | namespaces/{ns}/services/{name} | OK 197 | | `PUT` | namespaces/{ns}/services/{name} | ? 198 | | `PATCH` | namespaces/{ns}/services/{name} | ? 199 | | `DELETE` | namespaces/{ns}/services/{name} | ? 200 | 201 | 202 | ------------------------------------------------------------------------ 203 | 204 | 205 | ## Proxy API Endpoints 206 | 207 | | | Proxy Nodes | Status | 208 | |:---------:|:------------------------------|:------:| 209 | | `GET` | proxy/nodes/{name} | ? 210 | | `PUT` | proxy/nodes/{name} | ? 211 | | `POST` | proxy/nodes/{name} | ? 212 | | `DELETE` | proxy/nodes/{name} | ? 213 | | `OPTIONS` | proxy/nodes/{name} | ? 214 | | `HEAD` | proxy/nodes/{name} | ? 215 | | `GET` | proxy/nodes/{name}/{path:*} | ? 216 | | `PUT` | proxy/nodes/{name}/{path:*} | ? 217 | | `POST` | proxy/nodes/{name}/{path:*} | ? 218 | | `DELETE` | proxy/nodes/{name}/{path:*} | ? 219 | | `OPTIONS` | proxy/nodes/{name}/{path:*} | ? 220 | | `HEAD` | proxy/nodes/{name}/{path:*} | ? 221 | 222 | 223 | | | Proxy Pods | Status | 224 | |:---------:|:----------------------------------------------|:------:| 225 | | `GET` | proxy/namespaces/{ns}/pods/{name} | ? 226 | | `PUT` | proxy/namespaces/{ns}/pods/{name} | ? 227 | | `POST` | proxy/namespaces/{ns}/pods/{name} | ? 228 | | `DELETE` | proxy/namespaces/{ns}/pods/{name} | ? 229 | | `OPTIONS` | proxy/namespaces/{ns}/pods/{name} | ? 230 | | `HEAD` | proxy/namespaces/{ns}/pods/{name} | ? 231 | | `GET` | proxy/namespaces/{ns}/pods/{name}/{path:*} | ? 232 | | `PUT` | proxy/namespaces/{ns}/pods/{name}/{path:*} | ? 233 | | `POST` | proxy/namespaces/{ns}/pods/{name}/{path:*} | ? 234 | | `DELETE` | proxy/namespaces/{ns}/pods/{name}/{path:*} | ? 235 | | `OPTIONS` | proxy/namespaces/{ns}/pods/{name}/{path:*} | ? 236 | | `HEAD` | proxy/namespaces/{ns}/pods/{name}/{path:*} | ? 237 | 238 | 239 | | | Proxy Services | Status | 240 | |:---------:|:--------------------------------------------------|:------:| 241 | | `GET` | proxy/namespaces/{ns}/services/{name} | ? 242 | | `PUT` | proxy/namespaces/{ns}/services/{name} | ? 243 | | `POST` | proxy/namespaces/{ns}/services/{name} | ? 244 | | `DELETE` | proxy/namespaces/{ns}/services/{name} | ? 245 | | `OPTIONS` | proxy/namespaces/{ns}/services/{name} | ? 246 | | `HEAD` | proxy/namespaces/{ns}/services/{name} | ? 247 | | `GET` | proxy/namespaces/{ns}/services/{name}/{path:*} | ? 248 | | `PUT` | proxy/namespaces/{ns}/services/{name}/{path:*} | ? 249 | | `POST` | proxy/namespaces/{ns}/services/{name}/{path:*} | ? 250 | | `DELETE` | proxy/namespaces/{ns}/services/{name}/{path:*} | ? 251 | | `OPTIONS` | proxy/namespaces/{ns}/services/{name}/{path:*} | ? 252 | | `HEAD` | proxy/namespaces/{ns}/services/{name}/{path:*} | ? 253 | 254 | 255 | ------------------------------------------------------------------------ 256 | 257 | 258 | #### Watch API Endpoints 259 | 260 | | | Watch Endpoints | Status | 261 | |:---------:|:------------------------------------------|:------:| 262 | | `GET` | watch/endpoints | ? 263 | | `GET` | watch/namespaces/{ns}/endpoints | ? 264 | | `GET` | watch/namespaces/{ns}/endpoints/{name} | ? 265 | 266 | 267 | | | Watch Events | Status | 268 | |:---------:|:--------------------------------------|:------:| 269 | | `GET` | watch/events | ? 270 | | `GET` | watch/namespaces/{ns}/events | ? 271 | | `GET` | watch/namespaces/{ns}/events/{name} | ? 272 | 273 | 274 | | | Watch Limit Ranges | Status | 275 | |:---------:|:------------------------------------------|:------:| 276 | | `GET` | watch/limitranges | ? 277 | | `GET` | watch/namespaces/{ns}/limitranges | ? 278 | | `GET` | watch/namespaces/{ns}/limitranges/{name} | ? 279 | 280 | 281 | | | Watch Namespaces | Status | 282 | |:---------:|:--------------------------|:------:| 283 | | `GET` | watch/namespaces | ? 284 | | `GET` | watch/namespaces/{name} | ? 285 | 286 | 287 | | | Watch Nodes | Status | 288 | |:---------:|:----------------------|:------:| 289 | | `GET` | watch/nodes | ? 290 | | `GET` | watch/nodes/{name} | ? 291 | 292 | 293 | | | Watch Persistent Volume Claims | Status | 294 | |:---------:|:------------------------------------------------------|:------:| 295 | | `GET` | watch/persistentvolumeclaims | ? 296 | | `GET` | watch/namespaces/{ns}/persistentvolumeclaims | ? 297 | | `GET` | watch/namespaces/{ns}/persistentvolumeclaims/{name} | ? 298 | 299 | 300 | | | Watch Persistent Volumes | Status | 301 | |:---------:|:----------------------------------|:------:| 302 | | `GET` | watch/persistentvolumes | ? 303 | | `GET` | watch/persistentvolumes/{name} | ? 304 | 305 | 306 | | | Watch Pod Templates | Status | 307 | |:---------:|:------------------------------------------|:------:| 308 | | `GET` | watch/podtemplates | ? 309 | | `GET` | watch/namespaces/{ns}/podtemplates | ? 310 | | `GET` | watch/namespaces/{ns}/podtemplates/{name} | ? 311 | 312 | 313 | | | Watch Pods | Status | 314 | |:---------:|:----------------------------------|:------:| 315 | | `GET` | watch/pods | ? 316 | | `GET` | watch/namespaces/{ns}/pods | ? 317 | | `GET` | watch/namespaces/{ns}/pods/{name} | ? 318 | 319 | 320 | | | Watch Replication Controllers | Status | 321 | |:---------:|:------------------------------------------------------|:------:| 322 | | `GET` | watch/replicationcontrollers | ? 323 | | `GET` | watch/namespaces/{ns}/replicationcontrollers | ? 324 | | `GET` | watch/namespaces/{ns}/replicationcontrollers/{name} | ? 325 | 326 | 327 | | | Watch Resource Quotas | Status | 328 | |:---------:|:----------------------------------------------|:------:| 329 | | `GET` | watch/resourcequotas | ? 330 | | `GET` | watch/namespaces/{ns}/resourcequotas | ? 331 | | `GET` | watch/namespaces/{ns}/resourcequotas/{name} | ? 332 | 333 | 334 | | | Watch Secrets | Status | 335 | |:---------:|:--------------------------------------|:------:| 336 | | `GET` | watch/secrets | ? 337 | | `GET` | watch/namespaces/{ns}/secrets | ? 338 | | `GET` | watch/namespaces/{ns}/secrets/{name} | ? 339 | 340 | 341 | | | Watch Service Accounts | Status | 342 | |:---------:|:----------------------------------------------|:------:| 343 | | `GET` | watch/serviceaccounts | ? 344 | | `GET` | watch/namespaces/{ns}/serviceaccounts | ? 345 | | `GET` | watch/namespaces/{ns}/serviceaccounts/{name} | ? 346 | 347 | 348 | | | Watch Services | Status | 349 | |:---------:|:--------------------------------------|:------:| 350 | | `GET` | watch/services | ? 351 | | `GET` | watch/namespaces/{ns}/services | ? 352 | | `GET` | watch/namespaces/{ns}/services/{name} | ? 353 | 354 | 355 | ------------------------------------------------------------------------ 356 | 357 | 358 | | | Miscellaneous Endpoints | Status | 359 | |:---------:|:------------------------------------------|:------:| 360 | | `GET` | /api/v1 | ? 361 | | `POST` | namespaces/{ns}/bindings | ? 362 | | `GET` | componentstatuses | ? 363 | | `GET` | componentstatuses/{name} | ? 364 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016 Cisco Systems, Inc. 2 | https://github.com/ryclarke/node-kubernetes-client 3 | 4 | Copyright 2015 Tenxcloud.com 5 | https://github.com/tenxcloud/node-kubernetes-client 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files (the 9 | "Software"), to deal in the Software without restriction, including 10 | without limitation the rights to use, copy, modify, merge, publish, 11 | distribute, sublicense, and/or sell copies of the Software, and to 12 | permit persons to whom the Software is furnished to do so, subject to 13 | the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 22 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Google Kubernetes client 2 | 3 | Node.js client library for Google's Kubernetes API (https://github.com/GoogleCloudPlatform/kubernetes) 4 | You can use this library to call a Kubernetes API hosted in a Kubernetes master node using Node.js. 5 | 6 | Install: 7 | 8 | npm install node-kubernetes-client 9 | 10 | Current endpoint support includes: 11 | 12 | * events 13 | * endpoints 14 | * namespaces 15 | * pods 16 | * minions 17 | * services 18 | * replicationControllers 19 | * nodes 20 | * proxyMinions 21 | * proxyNodes 22 | * proxyPods 23 | * proxyServices 24 | * watchPods 25 | 26 | Interaction is accomplished via `client..`. (see examples below) 27 | 28 | # Usage 29 | 30 | ## Create client 31 | 32 | Authentication can be done via either token or login. If, however, the token 33 | expires, the login info will be used to acquire a new token. 34 | 35 | ```js 36 | var Client = require('node-kubernetes-client'); 37 | 38 | var client = new Client({ 39 | host: 'xx.xx.xx.xx', 40 | protocol: 'https', 41 | version: 'v1beta2', 42 | token: 'XYZ' 43 | }); 44 | ``` 45 | 46 | ### Optional params: 47 | Some optional params also exist on initialising the client. 48 | ```js 49 | { 50 | namespace: 'someNamespace', // filter all client requests by a namespace - default is no namespace 51 | timeout: 20000 // A timeout (in ms) for requests to k8 apis 52 | reqOptions: {} // array of options used to override the npm request module for this client proxy, auth, etc. 53 | } 54 | ``` 55 | 56 | ## Getting from pods 57 | 58 | Paging is accomplished automatically. For example, a request for `pods` will 59 | return all pods, not just those returned on the first page. 60 | 61 | For example, to get all pods: 62 | 63 | ```js 64 | client.pods.get(function (err, pods) { 65 | console.log('pods:', pods); 66 | }); 67 | ``` 68 | ## Retrieving from Custom Collections 69 | 70 | Retrieving from custom k8 collections is enabled by using the `createCollection` functionality. 71 | 72 | For example, to create a custom collection called "routes": 73 | ```js 74 | var schema = null, //optional param 75 | innerCollections = null, // optional param 76 | options = { apiPrefix : 'api2' }; // optionally set a per-collection api prefix 77 | client.routes = client.createCollection('routes', schema, innerCollections, options); 78 | // then use the routes collection like any other 79 | ``` 80 | 81 | ## createCollection options 82 | 83 | ``` 84 | apiPrefix: "apis" // Sets the prefix to the api path for the new collection. 85 | namespaced: true // Controls if paths include "/namespaces/${namespace}". 86 | ``` 87 | 88 | ## Custom Collection for k8s deployments 89 | 90 | ```js 91 | client = new Client({ 92 | host: 'xx.xx.xx.xx', 93 | protocol: 'https', 94 | version: 'extensions/v1beta1', 95 | token: 'XYZ', 96 | namespace: 'mynamespace', 97 | reqOptions: {proxy: configLocation.proxy || null}, 98 | timeout: 20000 99 | }); 100 | // add deployments to the api 101 | client.deployments = client.createCollection('deployments', null, null, { apiPrefix : 'apis' }); 102 | 103 | var deploymentJSON = { 104 | "apiVersion": "extensions/v1beta1", 105 | "kind": "Deployment", 106 | ... 107 | } 108 | 109 | // then use the deployments collection like any other 110 | client.deployments.get(deploymentJSON.metadata.name, function (err, data) { 111 | if (err && err.statusCode != 404) { 112 | //something is wrong, bail 113 | console.log("error checking for deployment:", err); 114 | return; 115 | } else if (err && err.statusCode == 404) { 116 | //create if not found 117 | client.deployments.create(deploymentJSON, function (err, data) { 118 | if (err) { 119 | console.log("error updating deployment:", err); 120 | return; 121 | } else { 122 | console.log("deployment created:", deploymentJSON.metadata.name); 123 | return; 124 | } 125 | }); 126 | } else { 127 | //update since it did not exist 128 | client.deployments.update(deploymentJSON.metadata.name, deploymentJSON, function (err, data) { 129 | if (err) { 130 | console.log("error updating deployment:", err); 131 | return; 132 | } 133 | console.log("deployment updated:", deploymentJSON.metadata.name); 134 | return; 135 | }); 136 | } 137 | }); 138 | ``` 139 | 140 | # How to run the test cases 141 | ## install mocha 142 | ```js 143 | npm install mocha 144 | ``` 145 | ## run testcase 146 | ```bash 147 | minikube start 148 | kubectl proxy 149 | mocha test/test-* 150 | ``` 151 | The results will be output to test/results/ directory with formatted JSON. 152 | 153 | # Roadmap 154 | 155 | See issues. 156 | 157 | # Others 158 | 159 | You may interested in kubernetes client library using other programming languanges, please check the below link 160 | (https://github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/client-libraries.md) 161 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/k8s'); 2 | -------------------------------------------------------------------------------- /lib/collections.js: -------------------------------------------------------------------------------- 1 | var errors = require('./errors') 2 | , createSchema = require('json-gate').createSchema 3 | , async = require('async') 4 | , util = require('util'); 5 | 6 | require('sugar'); 7 | 8 | var factory = module.exports = function (request) { 9 | var getPath = function () { 10 | return [].slice.call(arguments).filter(function (each) { 11 | return each !== null && typeof each !== 'undefined'; 12 | }).join('/'); 13 | }; 14 | 15 | var InnerCollection = function (parentCollection, id, collection, options) { 16 | this.get = function (callback) { 17 | request({ 18 | endpoint: getPath(parentCollection, id, collection) 19 | , method: 'GET' 20 | , options : options 21 | }, function (err, result) { 22 | callback(err, result && (result.resources || result)); 23 | }); 24 | }; 25 | 26 | this.post = function (innerId, callback) { 27 | request({ 28 | endpoint: getPath(parentCollection, id, collection, innerId) 29 | , method: 'POST' 30 | , options : options 31 | }, function (err, result) { 32 | callback(err, result && (result.resources || result)); 33 | }); 34 | }; 35 | 36 | this.put = function (innerId, callback) { 37 | request({ 38 | endpoint: getPath(parentCollection, id, collection, innerId) 39 | , method: 'PUT' 40 | , options : options 41 | }, function (err, result) { 42 | callback(err, result && (result.resources || result)); 43 | }); 44 | }; 45 | 46 | //added by Ryan Clarke (ryclarke@cisco.com) 47 | this.patch = function (innerId, callback) { 48 | request({ 49 | endpoint: getPath(parentCollection, id, collection, innerId) 50 | , method: 'PATCH' 51 | , options: options 52 | }, function (err, result) { 53 | callback(err, result && (result.resources || result)); 54 | }); 55 | }; 56 | 57 | this.delete = function (innerId, callback) { 58 | request({ 59 | endpoint: getPath(parentCollection, id, collection, innerId) 60 | , method: 'DELETE' 61 | , options : options 62 | }, function (err, result) { 63 | callback(err, result && (result.resources || result)); 64 | }); 65 | }; 66 | }; 67 | 68 | var Collection = function (collection, schema, innerCollections, options) { 69 | //Collection manages interaction with RESTful collection on kubernetes 70 | var makeObject = function (id) { 71 | var obj = {}; 72 | 73 | if (typeof id === 'object') { 74 | Object.merge(obj, id); 75 | if (id.metadata) { 76 | id = id.metadata.guid || id.metadata.name; 77 | } else if (typeof id.index !== 'undefined') { 78 | id = id.index; 79 | }else if (typeof id.id !== 'undefined') { 80 | id = id.id; 81 | } 82 | } 83 | 84 | (innerCollections || []).each(function (each) { 85 | var method 86 | , innerCollectionName 87 | , isNestedCollection = false; 88 | options = options || {}; 89 | 90 | if (typeof each === 'object') { 91 | method = each.method; 92 | innerCollectionName = each.endpoint || method; 93 | isNestedCollection = !! each.nested; 94 | if (each.resultsMap) { 95 | options.resultsMap = each.resultsMap; 96 | } 97 | } else { 98 | method = each.camelize(false); 99 | innerCollectionName = each; 100 | } 101 | 102 | if (isNestedCollection) { 103 | obj[method] = 104 | new Collection(util.format('%s/%s/%s', collection, id, 105 | innerCollectionName), 106 | {}, 107 | each.nested, options); 108 | } else { 109 | obj[method] = new InnerCollection(collection, id, innerCollectionName, options); 110 | } 111 | }); 112 | 113 | return obj; 114 | }; 115 | 116 | if (schema) { 117 | Object.values(schema, function (value) { 118 | value.required = true; 119 | }); 120 | schema = { 121 | properties: schema 122 | }; 123 | 124 | schema = schema && createSchema(schema); 125 | } 126 | 127 | this.get = function (query, callback) { 128 | if (! callback && typeof query !== 'function') { 129 | /** 130 | * Client is using shortcut on the get. I.e. they 131 | * are using a form like: 132 | * 133 | * collection.get(id).subCollection(...); 134 | */ 135 | return makeObject(query); 136 | } 137 | 138 | var multiResults = false; 139 | 140 | if (typeof query === 'function') { 141 | callback = query; 142 | query = null; 143 | multiResults = true; 144 | } else if (typeof query === 'object') { 145 | multiResults = true; 146 | } 147 | 148 | var finished = false 149 | , results = []; 150 | 151 | var requestObject = { 152 | endpoint: getPath(collection, query) 153 | , page: multiResults ? 1 : null // don't page if we're 154 | , method: 'GET' // looking up one item 155 | , options : options 156 | }; 157 | 158 | if (query && typeof query === 'object') { 159 | requestObject.endpoint = getPath(collection); 160 | } 161 | 162 | async.until(function () { 163 | return finished; 164 | }, function (callback) { 165 | request(requestObject, function (err, result) { 166 | if (err) { 167 | return callback(err); 168 | } 169 | 170 | finished = ! result.next_url; 171 | requestObject.page += 1; 172 | 173 | results.add(result.resources || result); 174 | 175 | callback(); 176 | }); 177 | }, function (err) { 178 | if (err) { 179 | return callback(err); 180 | } 181 | 182 | if (options && options.resultsMap) { 183 | results = options.resultsMap(results); 184 | } 185 | 186 | results = results.map(makeObject); 187 | 188 | if (! multiResults) { 189 | results = results[0]; 190 | } 191 | 192 | callback(null, results); 193 | }); 194 | }; 195 | /** 196 | this.getBy = function (query, callback) { 197 | this.get(function (err, result) { 198 | 199 | if (err) { 200 | return callback(err); 201 | } 202 | 203 | var match = result.find(query); 204 | 205 | callback(match ? null : new errors.NotFoundError(query), match); 206 | }); 207 | }; 208 | **/ 209 | this.getBy = function (query, callback) { 210 | /** 211 | var qstring = ''; 212 | if(typeof query === 'object') { 213 | Object.keys(query, function (key, value) { 214 | qstring += '&' + key + '=' + value; 215 | }); 216 | qstring = qstring.substring(1); 217 | } else { 218 | qstring = query; 219 | } 220 | **/ 221 | var endpoint = collection + '?namespace=' + query.namespace; 222 | if (query.name) { 223 | endpoint = collection + '/' + query.name + '?namespace=' + query.namespace; 224 | } 225 | request({ 226 | endpoint: endpoint 227 | , method: 'GET' 228 | , options : options 229 | }, callback); 230 | }; 231 | 232 | this.getByName = function (name, callback) { 233 | this.get({name: name}, callback); 234 | }; 235 | 236 | this.create = function (body, callback) { 237 | if (schema) { 238 | try { 239 | schema.validate(body); 240 | } catch (err) { 241 | return callback(err); 242 | } 243 | } 244 | 245 | request({ 246 | endpoint: getPath(collection) 247 | , method: 'POST' 248 | , json: body 249 | , options : options 250 | }, callback); 251 | }; 252 | 253 | this.update = function (id, body, callback) { 254 | request({ 255 | endpoint: getPath(collection, id) 256 | , method: 'PUT' 257 | , json: body 258 | , options : options 259 | }, callback); 260 | }; 261 | 262 | //added by Ryan Clarke (ryclarke@cisco.com) 263 | this.patch = function (id, body, callback) { 264 | request({ 265 | endpoint: getPath(collection, id) 266 | , method: 'PATCH' 267 | , json: body 268 | , options : options 269 | }, callback); 270 | }; 271 | 272 | this.delete = function (id, recursive, callback) { 273 | if (typeof recursive === 'function') { 274 | callback = recursive; 275 | recursive = false; 276 | } 277 | 278 | var object = { 279 | endpoint: getPath(collection, id) 280 | , method: 'DELETE' 281 | , options : options 282 | }; 283 | 284 | if (recursive) { 285 | object.json = {"kind":"DeleteOptions", "apiVersion":"v1", "propagationPolicy":"Foreground"}; 286 | object.qs = {recursive: true}; 287 | } 288 | 289 | request(object, callback); 290 | }; 291 | }; 292 | 293 | this.create = function (collection, schema, innerCollections, options) { 294 | return new Collection(collection, schema, innerCollections, options); 295 | }; 296 | }; 297 | -------------------------------------------------------------------------------- /lib/errors.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | 3 | var ClientError = module.exports.ClientError = function (body, statusCode) { 4 | this.message = body; 5 | this.statusCode = statusCode; 6 | }; 7 | 8 | util.inherits(ClientError, Error); 9 | 10 | var InvalidDataError = module.exports.InvalidDataError = function (body) { 11 | ClientError.call(this, body, 400); 12 | }; 13 | 14 | util.inherits(InvalidDataError, ClientError); 15 | 16 | var AuthorizationError = module.exports.AuthorizationError = function (body, statusCode) { 17 | ClientError.call(this, body, statusCode); 18 | }; 19 | 20 | util.inherits(AuthorizationError, ClientError); 21 | 22 | var NotFoundError = module.exports.NotFoundError = function (body) { 23 | ClientError.call(this, body, 404); 24 | }; 25 | 26 | util.inherits(NotFoundError, ClientError); 27 | 28 | module.exports.get = function (resp) { 29 | switch (resp.statusCode) { 30 | case 400: 31 | return new InvalidDataError(resp.body); 32 | case 401: 33 | case 403: 34 | return new AuthorizationError(resp.body, resp.statusCode); 35 | case 404: 36 | return new NotFoundError(resp.body); 37 | default: 38 | return new ClientError(resp.body, resp.statusCode); 39 | } 40 | 41 | }; 42 | -------------------------------------------------------------------------------- /lib/k8s.js: -------------------------------------------------------------------------------- 1 | var FormData = require('form-data') 2 | , url = require('url') 3 | , util = require('util'); 4 | 5 | require('sugar'); 6 | 7 | /** 8 | * kubernetes client. 9 | * 10 | * Parameters 11 | * @protocol http or https 12 | * @host k8s API server host 13 | * @version k8s API server version 14 | * @token k8s API server token 15 | */ 16 | 17 | var VcapClient = module.exports = function (info) { 18 | 19 | // ~~~~~ PRIVATE 20 | var self = this; 21 | 22 | if (! info.host) { 23 | return new TypeError('host must be provided'); 24 | } 25 | 26 | if (! info.version) { 27 | return new TypeError('version must be provided'); 28 | } 29 | 30 | var request = require('./request')(info); 31 | 32 | // ~~~~~ PUBLIC 33 | 34 | var Collections = require('./collections'); 35 | var collections = new Collections(request); 36 | 37 | // ~ minions 38 | this.minions = collections.create('minions'); 39 | 40 | // ~ events 41 | this.events = collections.create('events'); 42 | 43 | // ~ namespaces 44 | this.namespaces = collections.create('namespaces'); 45 | 46 | // ~ pods 47 | this.pods = collections.create('pods', null, [{ method: 'log', nested: false }], null); 48 | 49 | // ~ services 50 | this.services = collections.create('services'); 51 | 52 | // ~ replicationControllers 53 | this.replicationControllers = collections.create('replicationControllers'); 54 | 55 | // ~ nodes 56 | this.nodes = collections.create('nodes'); 57 | 58 | // ~ endpoints 59 | this.endpoints = collections.create('endpoints'); 60 | 61 | // ~ proxy minions 62 | this.proxyMinions = collections.create('proxy/minions'); 63 | 64 | // ~ proxy nodes 65 | this.proxyNodes = collections.create('proxy/nodes'); 66 | 67 | // ~ proxy pods 68 | this.proxyPods = collections.create('proxy/pods'); 69 | 70 | // ~ proxy services 71 | this.proxyServices = collections.create('proxy/services'); 72 | 73 | // ~ watch pods 74 | this.watchPods = collections.create('watch/pods'); 75 | 76 | // Allow users to create custom collections also 77 | this.createCollection = collections.create; 78 | }; 79 | -------------------------------------------------------------------------------- /lib/request.js: -------------------------------------------------------------------------------- 1 | var errors = require('./errors') 2 | , path = require('path') 3 | , request = require('request') 4 | , url = require('url') 5 | , util = require('util'); 6 | 7 | require('sugar'); 8 | 9 | module.exports = function (info) { 10 | var protocol = info.protocol || 'https' 11 | , host = info.host 12 | , version = info.version 13 | , token = info.token 14 | , timeout = info.timeout 15 | , reqOptions = info.reqOptions 16 | , namespace = info.namespace; 17 | 18 | var getUrl = function (object) { 19 | var prefix = 'api', namespaced = false; 20 | 21 | // Allow options to override default API prefix. 22 | if (object.options && object.options.apiPrefix) { 23 | prefix = object.options.apiPrefix; 24 | } 25 | 26 | //Add option to override namespaces in the URI 27 | if (object.options && object.options.namespaced) { 28 | namespaced = true; 29 | } 30 | 31 | // v1beta3 and greater uses lowercase endpoints instead of 32 | // camelCase and defines namespaces in the query URL. 33 | if ((version === 'v1beta3' || version === 'v1' || version === 'extensions/v1beta1')) { 34 | namespaced = true; 35 | } 36 | 37 | // Define base URL for the query. 38 | var query = protocol + '://' + path.join(host, prefix, version); 39 | 40 | // Fix query URL handling for proxy and watch endpoints. 41 | if (object.endpoint.match(/^proxy/)) { 42 | query = query + '/proxy'; 43 | var endpoint = object.endpoint.replace('proxy/', ''); 44 | } else if (object.endpoint.match(/^watch/)) { 45 | query = query + '/watch'; 46 | var endpoint = object.endpoint.replace('watch/', ''); 47 | } else { 48 | var endpoint = object.endpoint; 49 | } 50 | 51 | if ( namespaced ) { 52 | endpoint = endpoint.toLowerCase(); 53 | // Never use URL namespacing for namespace or node endpoints. 54 | if (namespace && !endpoint.match(/^namespaces/) && !endpoint.match(/^nodes/)){ 55 | return query + '/' + path.join('namespaces', namespace, endpoint); 56 | } 57 | } 58 | return query + '/' + endpoint; 59 | }; 60 | 61 | var getAuthUrl = function (callback) { 62 | request({ 63 | url: getUrl({endpoint: 'info'}) 64 | , json: true 65 | , strictSSL: false 66 | }, function (err, resp, body) { 67 | if (err) { 68 | return callback(err); 69 | } 70 | 71 | if (resp.statusCode !== 200) { 72 | return callback(resp.statusCode, body); 73 | } 74 | 75 | callback(null, body.authorization_endpoint + '/oauth/token'); 76 | }); 77 | }; 78 | var isSuccess = function (code) { 79 | return (code - (code % 200)) === 200; 80 | }; 81 | 82 | var makeRequest = function (object, callback) { 83 | var object = Object.clone(object); 84 | object.url = getUrl(object); 85 | delete object.endpoint; 86 | if (!object.json) { 87 | object.json = true; 88 | } 89 | object.timeout = timeout; 90 | if (object.json) { 91 | if (['object', 'boolean'].none(typeof object.json)) { 92 | object.body = object.json; 93 | object.json = undefined; 94 | } 95 | } 96 | // Define paging options. 97 | if (object.page) { 98 | if (!object.qs) { 99 | object.qs = {}; 100 | } 101 | object.qs.page = object.page; 102 | delete object.page; 103 | } 104 | if (!info.ca) { 105 | object.strictSSL = false; 106 | } 107 | if (info.cert) { 108 | object.cert = info.cert; 109 | } 110 | if (info.key) { 111 | object.key = info.key; 112 | } 113 | if (info.ca) { 114 | object.ca = info.ca; 115 | } 116 | 117 | return request(object, function (err, resp, body) { 118 | if (err) { 119 | return callback(err); 120 | } 121 | 122 | if (isSuccess(resp.statusCode)) { 123 | return callback(null, body); 124 | } 125 | 126 | return callback(errors.get(resp)); 127 | }); 128 | }; 129 | 130 | return function (object, callback) { 131 | // Set request authorization token if it is defined. 132 | if (token) { 133 | object.auth = {bearer: token}; 134 | } 135 | // ovverride options for this request if reqOptions exists 136 | if (reqOptions) { 137 | Object.assign(object, reqOptions); 138 | } 139 | // Fix Content-Type header for PATCH methods. 140 | if (object.method === 'PATCH') { 141 | if (!object.headers) { 142 | object.headers = {}; 143 | } 144 | object.headers['Content-Type'] = 'application/strategic-merge-patch+json'; 145 | } 146 | // Use namespace querystring for older versions of kubernetes. 147 | if (namespace && version.match(/v1beta(1|2)/)) { 148 | if (!object.qs) { 149 | object.qs = {}; 150 | } 151 | object.qs.namespace = namespace; 152 | } 153 | return makeRequest(object, callback); 154 | }; 155 | }; 156 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-kubernetes-client", 3 | "version": "0.3.2", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ajv": { 8 | "version": "4.11.8", 9 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", 10 | "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", 11 | "requires": { 12 | "co": "4.6.0", 13 | "json-stable-stringify": "1.0.1" 14 | } 15 | }, 16 | "asn1": { 17 | "version": "0.2.3", 18 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", 19 | "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" 20 | }, 21 | "assert-plus": { 22 | "version": "0.2.0", 23 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", 24 | "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=" 25 | }, 26 | "async": { 27 | "version": "2.5.0", 28 | "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", 29 | "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", 30 | "requires": { 31 | "lodash": "4.17.4" 32 | } 33 | }, 34 | "asynckit": { 35 | "version": "0.4.0", 36 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 37 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 38 | }, 39 | "aws-sign2": { 40 | "version": "0.6.0", 41 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", 42 | "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=" 43 | }, 44 | "aws4": { 45 | "version": "1.6.0", 46 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", 47 | "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" 48 | }, 49 | "balanced-match": { 50 | "version": "1.0.0", 51 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 52 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 53 | }, 54 | "bcrypt-pbkdf": { 55 | "version": "1.0.1", 56 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", 57 | "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", 58 | "optional": true, 59 | "requires": { 60 | "tweetnacl": "0.14.5" 61 | } 62 | }, 63 | "boom": { 64 | "version": "2.10.1", 65 | "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", 66 | "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", 67 | "requires": { 68 | "hoek": "2.16.3" 69 | } 70 | }, 71 | "brace-expansion": { 72 | "version": "1.1.8", 73 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", 74 | "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", 75 | "requires": { 76 | "balanced-match": "1.0.0", 77 | "concat-map": "0.0.1" 78 | } 79 | }, 80 | "browser-stdout": { 81 | "version": "1.3.0", 82 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", 83 | "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", 84 | "dev": true 85 | }, 86 | "caseless": { 87 | "version": "0.12.0", 88 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 89 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" 90 | }, 91 | "co": { 92 | "version": "4.6.0", 93 | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", 94 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" 95 | }, 96 | "combined-stream": { 97 | "version": "1.0.5", 98 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", 99 | "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", 100 | "requires": { 101 | "delayed-stream": "1.0.0" 102 | } 103 | }, 104 | "commander": { 105 | "version": "2.9.0", 106 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", 107 | "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", 108 | "dev": true, 109 | "requires": { 110 | "graceful-readlink": "1.0.1" 111 | } 112 | }, 113 | "concat-map": { 114 | "version": "0.0.1", 115 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 116 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 117 | }, 118 | "core-util-is": { 119 | "version": "1.0.2", 120 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 121 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 122 | }, 123 | "cryptiles": { 124 | "version": "2.0.5", 125 | "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", 126 | "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", 127 | "requires": { 128 | "boom": "2.10.1" 129 | } 130 | }, 131 | "dashdash": { 132 | "version": "1.14.1", 133 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 134 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 135 | "requires": { 136 | "assert-plus": "1.0.0" 137 | }, 138 | "dependencies": { 139 | "assert-plus": { 140 | "version": "1.0.0", 141 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 142 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 143 | } 144 | } 145 | }, 146 | "debug": { 147 | "version": "2.6.8", 148 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", 149 | "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", 150 | "dev": true, 151 | "requires": { 152 | "ms": "2.0.0" 153 | } 154 | }, 155 | "delayed-stream": { 156 | "version": "1.0.0", 157 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 158 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 159 | }, 160 | "diff": { 161 | "version": "3.2.0", 162 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", 163 | "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=", 164 | "dev": true 165 | }, 166 | "ecc-jsbn": { 167 | "version": "0.1.1", 168 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", 169 | "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", 170 | "optional": true, 171 | "requires": { 172 | "jsbn": "0.1.1" 173 | } 174 | }, 175 | "escape-string-regexp": { 176 | "version": "1.0.5", 177 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 178 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 179 | "dev": true 180 | }, 181 | "extend": { 182 | "version": "3.0.1", 183 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", 184 | "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" 185 | }, 186 | "extsprintf": { 187 | "version": "1.3.0", 188 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 189 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" 190 | }, 191 | "forever-agent": { 192 | "version": "0.6.1", 193 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 194 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" 195 | }, 196 | "form-data": { 197 | "version": "2.2.0", 198 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.2.0.tgz", 199 | "integrity": "sha1-ml47kpX5gLJiPPZPojixTOvKcHs=", 200 | "requires": { 201 | "asynckit": "0.4.0", 202 | "combined-stream": "1.0.5", 203 | "mime-types": "2.1.16" 204 | } 205 | }, 206 | "fs.realpath": { 207 | "version": "1.0.0", 208 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 209 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" 210 | }, 211 | "getpass": { 212 | "version": "0.1.7", 213 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 214 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 215 | "requires": { 216 | "assert-plus": "1.0.0" 217 | }, 218 | "dependencies": { 219 | "assert-plus": { 220 | "version": "1.0.0", 221 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 222 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 223 | } 224 | } 225 | }, 226 | "glob": { 227 | "version": "7.1.2", 228 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 229 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 230 | "requires": { 231 | "fs.realpath": "1.0.0", 232 | "inflight": "1.0.6", 233 | "inherits": "2.0.3", 234 | "minimatch": "3.0.4", 235 | "once": "1.4.0", 236 | "path-is-absolute": "1.0.1" 237 | } 238 | }, 239 | "graceful-readlink": { 240 | "version": "1.0.1", 241 | "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", 242 | "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", 243 | "dev": true 244 | }, 245 | "growl": { 246 | "version": "1.9.2", 247 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", 248 | "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", 249 | "dev": true 250 | }, 251 | "har-schema": { 252 | "version": "1.0.5", 253 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", 254 | "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=" 255 | }, 256 | "har-validator": { 257 | "version": "4.2.1", 258 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", 259 | "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", 260 | "requires": { 261 | "ajv": "4.11.8", 262 | "har-schema": "1.0.5" 263 | } 264 | }, 265 | "has-flag": { 266 | "version": "1.0.0", 267 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", 268 | "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", 269 | "dev": true 270 | }, 271 | "hawk": { 272 | "version": "3.1.3", 273 | "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", 274 | "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", 275 | "requires": { 276 | "boom": "2.10.1", 277 | "cryptiles": "2.0.5", 278 | "hoek": "2.16.3", 279 | "sntp": "1.0.9" 280 | } 281 | }, 282 | "hoek": { 283 | "version": "2.16.3", 284 | "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", 285 | "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" 286 | }, 287 | "http-signature": { 288 | "version": "1.1.1", 289 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", 290 | "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", 291 | "requires": { 292 | "assert-plus": "0.2.0", 293 | "jsprim": "1.4.1", 294 | "sshpk": "1.13.1" 295 | } 296 | }, 297 | "inflight": { 298 | "version": "1.0.6", 299 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 300 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 301 | "requires": { 302 | "once": "1.4.0", 303 | "wrappy": "1.0.2" 304 | } 305 | }, 306 | "inherits": { 307 | "version": "2.0.3", 308 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 309 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 310 | }, 311 | "is-typedarray": { 312 | "version": "1.0.0", 313 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 314 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 315 | }, 316 | "isstream": { 317 | "version": "0.1.2", 318 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 319 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 320 | }, 321 | "jsbn": { 322 | "version": "0.1.1", 323 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 324 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", 325 | "optional": true 326 | }, 327 | "json-gate": { 328 | "version": "0.8.23", 329 | "resolved": "https://registry.npmjs.org/json-gate/-/json-gate-0.8.23.tgz", 330 | "integrity": "sha1-0nTqa3tefvV/RuKA38OcCq6T6FY=" 331 | }, 332 | "json-schema": { 333 | "version": "0.2.3", 334 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 335 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" 336 | }, 337 | "json-stable-stringify": { 338 | "version": "1.0.1", 339 | "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", 340 | "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", 341 | "requires": { 342 | "jsonify": "0.0.0" 343 | } 344 | }, 345 | "json-stringify-safe": { 346 | "version": "5.0.1", 347 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 348 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 349 | }, 350 | "json3": { 351 | "version": "3.3.2", 352 | "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", 353 | "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", 354 | "dev": true 355 | }, 356 | "jsonify": { 357 | "version": "0.0.0", 358 | "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", 359 | "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" 360 | }, 361 | "jsprim": { 362 | "version": "1.4.1", 363 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 364 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 365 | "requires": { 366 | "assert-plus": "1.0.0", 367 | "extsprintf": "1.3.0", 368 | "json-schema": "0.2.3", 369 | "verror": "1.10.0" 370 | }, 371 | "dependencies": { 372 | "assert-plus": { 373 | "version": "1.0.0", 374 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 375 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 376 | } 377 | } 378 | }, 379 | "lodash": { 380 | "version": "4.17.4", 381 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", 382 | "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" 383 | }, 384 | "lodash._baseassign": { 385 | "version": "3.2.0", 386 | "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", 387 | "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", 388 | "dev": true, 389 | "requires": { 390 | "lodash._basecopy": "3.0.1", 391 | "lodash.keys": "3.1.2" 392 | } 393 | }, 394 | "lodash._basecopy": { 395 | "version": "3.0.1", 396 | "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", 397 | "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", 398 | "dev": true 399 | }, 400 | "lodash._basecreate": { 401 | "version": "3.0.3", 402 | "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", 403 | "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=", 404 | "dev": true 405 | }, 406 | "lodash._getnative": { 407 | "version": "3.9.1", 408 | "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", 409 | "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", 410 | "dev": true 411 | }, 412 | "lodash._isiterateecall": { 413 | "version": "3.0.9", 414 | "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", 415 | "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", 416 | "dev": true 417 | }, 418 | "lodash.create": { 419 | "version": "3.1.1", 420 | "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", 421 | "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", 422 | "dev": true, 423 | "requires": { 424 | "lodash._baseassign": "3.2.0", 425 | "lodash._basecreate": "3.0.3", 426 | "lodash._isiterateecall": "3.0.9" 427 | } 428 | }, 429 | "lodash.isarguments": { 430 | "version": "3.1.0", 431 | "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", 432 | "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", 433 | "dev": true 434 | }, 435 | "lodash.isarray": { 436 | "version": "3.0.4", 437 | "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", 438 | "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", 439 | "dev": true 440 | }, 441 | "lodash.keys": { 442 | "version": "3.1.2", 443 | "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", 444 | "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", 445 | "dev": true, 446 | "requires": { 447 | "lodash._getnative": "3.9.1", 448 | "lodash.isarguments": "3.1.0", 449 | "lodash.isarray": "3.0.4" 450 | } 451 | }, 452 | "mime-db": { 453 | "version": "1.29.0", 454 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.29.0.tgz", 455 | "integrity": "sha1-SNJtI1WJZRcErFkWygYAGRQmaHg=" 456 | }, 457 | "mime-types": { 458 | "version": "2.1.16", 459 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.16.tgz", 460 | "integrity": "sha1-K4WKUuXs1RbbiXrCvodIeDBpjiM=", 461 | "requires": { 462 | "mime-db": "1.29.0" 463 | } 464 | }, 465 | "minimatch": { 466 | "version": "3.0.4", 467 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 468 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 469 | "requires": { 470 | "brace-expansion": "1.1.8" 471 | } 472 | }, 473 | "minimist": { 474 | "version": "0.0.8", 475 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 476 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 477 | "dev": true 478 | }, 479 | "mkdirp": { 480 | "version": "0.5.1", 481 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 482 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 483 | "dev": true, 484 | "requires": { 485 | "minimist": "0.0.8" 486 | } 487 | }, 488 | "mocha": { 489 | "version": "3.5.0", 490 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.0.tgz", 491 | "integrity": "sha512-pIU2PJjrPYvYRqVpjXzj76qltO9uBYI7woYAMoxbSefsa+vqAfptjoeevd6bUgwD0mPIO+hv9f7ltvsNreL2PA==", 492 | "dev": true, 493 | "requires": { 494 | "browser-stdout": "1.3.0", 495 | "commander": "2.9.0", 496 | "debug": "2.6.8", 497 | "diff": "3.2.0", 498 | "escape-string-regexp": "1.0.5", 499 | "glob": "7.1.1", 500 | "growl": "1.9.2", 501 | "json3": "3.3.2", 502 | "lodash.create": "3.1.1", 503 | "mkdirp": "0.5.1", 504 | "supports-color": "3.1.2" 505 | }, 506 | "dependencies": { 507 | "glob": { 508 | "version": "7.1.1", 509 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", 510 | "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", 511 | "dev": true, 512 | "requires": { 513 | "fs.realpath": "1.0.0", 514 | "inflight": "1.0.6", 515 | "inherits": "2.0.3", 516 | "minimatch": "3.0.4", 517 | "once": "1.4.0", 518 | "path-is-absolute": "1.0.1" 519 | } 520 | } 521 | } 522 | }, 523 | "mockery": { 524 | "version": "2.1.0", 525 | "resolved": "https://registry.npmjs.org/mockery/-/mockery-2.1.0.tgz", 526 | "integrity": "sha512-9VkOmxKlWXoDO/h1jDZaS4lH33aWfRiJiNT/tKj+8OGzrcFDLo8d0syGdbsc3Bc4GvRXPb+NMMvojotmuGJTvA==", 527 | "dev": true 528 | }, 529 | "ms": { 530 | "version": "2.0.0", 531 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 532 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 533 | "dev": true 534 | }, 535 | "oauth-sign": { 536 | "version": "0.8.2", 537 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", 538 | "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" 539 | }, 540 | "once": { 541 | "version": "1.4.0", 542 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 543 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 544 | "requires": { 545 | "wrappy": "1.0.2" 546 | } 547 | }, 548 | "path-is-absolute": { 549 | "version": "1.0.1", 550 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 551 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" 552 | }, 553 | "performance-now": { 554 | "version": "0.2.0", 555 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", 556 | "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=" 557 | }, 558 | "punycode": { 559 | "version": "1.4.1", 560 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 561 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" 562 | }, 563 | "qs": { 564 | "version": "6.4.0", 565 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", 566 | "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=" 567 | }, 568 | "request": { 569 | "version": "2.81.0", 570 | "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", 571 | "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", 572 | "requires": { 573 | "aws-sign2": "0.6.0", 574 | "aws4": "1.6.0", 575 | "caseless": "0.12.0", 576 | "combined-stream": "1.0.5", 577 | "extend": "3.0.1", 578 | "forever-agent": "0.6.1", 579 | "form-data": "2.1.4", 580 | "har-validator": "4.2.1", 581 | "hawk": "3.1.3", 582 | "http-signature": "1.1.1", 583 | "is-typedarray": "1.0.0", 584 | "isstream": "0.1.2", 585 | "json-stringify-safe": "5.0.1", 586 | "mime-types": "2.1.16", 587 | "oauth-sign": "0.8.2", 588 | "performance-now": "0.2.0", 589 | "qs": "6.4.0", 590 | "safe-buffer": "5.1.1", 591 | "stringstream": "0.0.5", 592 | "tough-cookie": "2.3.2", 593 | "tunnel-agent": "0.6.0", 594 | "uuid": "3.1.0" 595 | }, 596 | "dependencies": { 597 | "form-data": { 598 | "version": "2.1.4", 599 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", 600 | "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", 601 | "requires": { 602 | "asynckit": "0.4.0", 603 | "combined-stream": "1.0.5", 604 | "mime-types": "2.1.16" 605 | } 606 | } 607 | } 608 | }, 609 | "safe-buffer": { 610 | "version": "5.1.1", 611 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", 612 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" 613 | }, 614 | "should": { 615 | "version": "11.2.1", 616 | "resolved": "https://registry.npmjs.org/should/-/should-11.2.1.tgz", 617 | "integrity": "sha1-kPVRRVUtAc/CAGZuToGKHJZw7aI=", 618 | "dev": true, 619 | "requires": { 620 | "should-equal": "1.0.1", 621 | "should-format": "3.0.3", 622 | "should-type": "1.4.0", 623 | "should-type-adaptors": "1.0.1", 624 | "should-util": "1.0.0" 625 | } 626 | }, 627 | "should-equal": { 628 | "version": "1.0.1", 629 | "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-1.0.1.tgz", 630 | "integrity": "sha1-C26VFvJgGp+wuy3MNpr6HH4gCvc=", 631 | "dev": true, 632 | "requires": { 633 | "should-type": "1.4.0" 634 | } 635 | }, 636 | "should-format": { 637 | "version": "3.0.3", 638 | "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", 639 | "integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=", 640 | "dev": true, 641 | "requires": { 642 | "should-type": "1.4.0", 643 | "should-type-adaptors": "1.0.1" 644 | } 645 | }, 646 | "should-type": { 647 | "version": "1.4.0", 648 | "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", 649 | "integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=", 650 | "dev": true 651 | }, 652 | "should-type-adaptors": { 653 | "version": "1.0.1", 654 | "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.0.1.tgz", 655 | "integrity": "sha1-7+VVPN9oz/ZuXF9RtxLcNRx3vqo=", 656 | "dev": true, 657 | "requires": { 658 | "should-type": "1.4.0", 659 | "should-util": "1.0.0" 660 | } 661 | }, 662 | "should-util": { 663 | "version": "1.0.0", 664 | "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.0.tgz", 665 | "integrity": "sha1-yYzaN0qmsZDfi6h8mInCtNtiAGM=", 666 | "dev": true 667 | }, 668 | "sntp": { 669 | "version": "1.0.9", 670 | "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", 671 | "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", 672 | "requires": { 673 | "hoek": "2.16.3" 674 | } 675 | }, 676 | "sshpk": { 677 | "version": "1.13.1", 678 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", 679 | "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", 680 | "requires": { 681 | "asn1": "0.2.3", 682 | "assert-plus": "1.0.0", 683 | "bcrypt-pbkdf": "1.0.1", 684 | "dashdash": "1.14.1", 685 | "ecc-jsbn": "0.1.1", 686 | "getpass": "0.1.7", 687 | "jsbn": "0.1.1", 688 | "tweetnacl": "0.14.5" 689 | }, 690 | "dependencies": { 691 | "assert-plus": { 692 | "version": "1.0.0", 693 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 694 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 695 | } 696 | } 697 | }, 698 | "stringstream": { 699 | "version": "0.0.5", 700 | "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", 701 | "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" 702 | }, 703 | "sugar": { 704 | "version": "1.5.0", 705 | "resolved": "https://registry.npmjs.org/sugar/-/sugar-1.5.0.tgz", 706 | "integrity": "sha1-2dP7oQ96iH4G5q37B4onrLH8BVY=" 707 | }, 708 | "supports-color": { 709 | "version": "3.1.2", 710 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", 711 | "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", 712 | "dev": true, 713 | "requires": { 714 | "has-flag": "1.0.0" 715 | } 716 | }, 717 | "tough-cookie": { 718 | "version": "2.3.2", 719 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", 720 | "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", 721 | "requires": { 722 | "punycode": "1.4.1" 723 | } 724 | }, 725 | "tunnel-agent": { 726 | "version": "0.6.0", 727 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 728 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 729 | "requires": { 730 | "safe-buffer": "5.1.1" 731 | } 732 | }, 733 | "tweetnacl": { 734 | "version": "0.14.5", 735 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 736 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", 737 | "optional": true 738 | }, 739 | "uuid": { 740 | "version": "3.1.0", 741 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", 742 | "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" 743 | }, 744 | "verror": { 745 | "version": "1.10.0", 746 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 747 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 748 | "requires": { 749 | "assert-plus": "1.0.0", 750 | "core-util-is": "1.0.2", 751 | "extsprintf": "1.3.0" 752 | }, 753 | "dependencies": { 754 | "assert-plus": { 755 | "version": "1.0.0", 756 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 757 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 758 | } 759 | } 760 | }, 761 | "wrappy": { 762 | "version": "1.0.2", 763 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 764 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 765 | } 766 | } 767 | } 768 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-kubernetes-client", 3 | "version": "0.3.2", 4 | "description": "Kubernetes client of Node.js", 5 | "main": "index.js", 6 | "scripts": { 7 | "pretest": "jshint .", 8 | "test": "mocha ./test -R spec", 9 | "integration-tests": "mocha ./test/integration/test.js -R spec --bail --timeout 5s" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/tenxcloud/node-kubernetes-client.git" 14 | }, 15 | "keywords": [ 16 | "kubernetes", 17 | "k8s", 18 | "docker" 19 | ], 20 | "author": "huangqg", 21 | "license": "MIT", 22 | "bugs": { 23 | "url": "https://github.com/tenxcloud/node-kubernetes-client/issues" 24 | }, 25 | "dependencies": { 26 | "async": "^2.5.0", 27 | "form-data": "^2.2.0", 28 | "glob": "*", 29 | "json-gate": "^0.8.21", 30 | "request": "^2.81.0", 31 | "sugar": "^1.5.0" 32 | }, 33 | "devDependencies": { 34 | "mocha": "^3.0", 35 | "should": "^11.0", 36 | "mockery": "^2.0" 37 | }, 38 | "homepage": "https://github.com/tenxcloud/node-kubernetes-client", 39 | "_from": "node-kubernetes-client@*" 40 | } 41 | -------------------------------------------------------------------------------- /test/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "k8s": { 3 | "protocol": "http", 4 | "host": "127.0.0.1:8001", 5 | "version": "v1", 6 | "reqOptions": {}, 7 | "namespace": "default" 8 | }, 9 | "pattern" : "test-*.js", 10 | "skip" : [] 11 | } -------------------------------------------------------------------------------- /test/guestbook-go/README.md: -------------------------------------------------------------------------------- 1 | ## GuestBook example 2 | 3 | This example shows how to build a simple multi-tier web application using Kubernetes and Docker. 4 | 5 | The example combines a web frontend, a redis master for storage and a replicated set of redis slaves. 6 | 7 | ### Step Zero: Prerequisites 8 | 9 | This example assumes that you have forked the repository and [turned up a Kubernetes cluster](../../docs/getting-started-guides): 10 | 11 | ```shell 12 | $ cd kubernetes 13 | $ hack/dev-build-and-up.sh 14 | ``` 15 | 16 | ### Step One: Turn up the redis master. 17 | 18 | Use the file `examples/guestbook-go/redis-master-controller.json` to create a replication controller which manages a single pod. The pod runs a redis key-value server in a container. Using a replication controller is the preferred way to launch long-running pods, even for 1 replica, so the pod will benefit from self-healing mechanism in kubernetes. 19 | 20 | Create the redis master replication controller in your Kubernetes cluster using the `kubectl` CLI: 21 | 22 | ```shell 23 | $ cluster/kubectl.sh create -f examples/guestbook-go/redis-master-controller.json 24 | ``` 25 | 26 | Once that's up you can list the replication controllers in the cluster: 27 | ```shell 28 | $ cluster/kubectl.sh get rc 29 | CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS 30 | redis-master-controller redis-master gurpartap/redis name=redis,role=master 1 31 | ``` 32 | 33 | List pods in cluster to verify the master is running. You'll see a single redis master pod. It will also display the machine that the pod is running on once it gets placed (may take up to thirty seconds). 34 | 35 | ```shell 36 | $ cluster/kubectl.sh get pods 37 | POD IP CONTAINER(S) IMAGE(S) HOST LABELS STATUS 38 | redis-master-pod-hh2gd 10.244.3.7 redis-master gurpartap/redis kubernetes-minion-4.c.lucid-walker-725.internal/104.154.49.134 name=redis,role=master Running 39 | ``` 40 | 41 | If you ssh to that machine, you can run `docker ps` to see the actual pod: 42 | 43 | ```shell 44 | me@workstation$ gcloud compute ssh --zone us-central1-b kubernetes-minion-4 45 | 46 | me@kubernetes-minion-3:~$ sudo docker ps 47 | CONTAINER ID IMAGE COMMAND CREATED STATUS 48 | d5c458dabe50 gurpartap/redis:latest "/usr/local/bin/redi 5 minutes ago Up 5 minutes 49 | ``` 50 | 51 | (Note that initial `docker pull` may take a few minutes, depending on network conditions.) 52 | 53 | ### Step Two: Turn up the master service. 54 | A Kubernetes 'service' is a named load balancer that proxies traffic to one or more containers. The services in a Kubernetes cluster are discoverable inside other containers via environment variables. Services find the containers to load balance based on pod labels. 55 | 56 | The pod that you created in Step One has the label `name=redis` and `role=master`. The selector field of the service determines which pods will receive the traffic sent to the service. Use the file `examples/guestbook-go/redis-master-service.json` to create the service in the `kubectl` cli: 57 | 58 | ```shell 59 | $ cluster/kubectl.sh create -f examples/guestbook-go/redis-master-service.json 60 | 61 | $ cluster/kubectl.sh get services 62 | NAME LABELS SELECTOR IP PORT 63 | redis-master name=redis,role=master 10.0.186.234 6379 64 | ``` 65 | 66 | This will cause all new pods to see the redis master apparently running on $REDIS_MASTER_SERVICE_HOST at port 6379. Once created, the service proxy on each node is configured to set up a proxy on the specified port (in this case port 6379). 67 | 68 | ### Step Three: Turn up the replicated slave pods. 69 | Although the redis master is a single pod, the redis read slaves are a 'replicated' pod. In Kubernetes, a replication controller is responsible for managing multiple instances of a replicated pod. 70 | 71 | Use the file `examples/guestbook-go/redis-slave-controller.json` to create the replication controller: 72 | 73 | ```shell 74 | $ cluster/kubectl.sh create -f examples/guestbook-go/redis-slave-controller.json 75 | 76 | $ cluster/kubectl.sh get rc 77 | CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS 78 | redis-master-controller redis-master gurpartap/redis name=redis,role=master 1 79 | redis-slave-controller redis-slave gurpartap/redis name=redis,role=slave 2 80 | ``` 81 | 82 | The redis slave configures itself by looking for the Kubernetes service environment variables in the container environment. In particular, the redis slave is started with the following command: 83 | 84 | ```shell 85 | redis-server --slaveof $REDIS_MASTER_SERVICE_HOST $REDIS_MASTER_SERVICE_PORT 86 | ``` 87 | 88 | Once that's up you can list the pods in the cluster, to verify that the master and slaves are running: 89 | 90 | ```shell 91 | $ cluster/kubectl.sh get pods 92 | POD IP CONTAINER(S) IMAGE(S) HOST LABELS STATUS 93 | redis-master-pod-hh2gd 10.244.3.7 redis-master gurpartap/redis kubernetes-minion-4.c.lucid-walker-725.internal/104.154.49.134 name=redis,role=master Running 94 | redis-slave-controller-i7hvs 10.244.2.7 redis-slave gurpartap/redis kubernetes-minion-3.c.lucid-walker-725.internal/104.154.52.39 name=redis,role=slave Running 95 | redis-slave-controller-nyxxv 10.244.1.6 redis-slave gurpartap/redis kubernetes-minion-2.c.lucid-walker-725.internal/130.211.144.5 name=redis,role=slave Running 96 | ``` 97 | 98 | You will see a single redis master pod and two redis slave pods. 99 | 100 | ### Step Four: Create the redis slave service. 101 | 102 | Just like the master, we want to have a service to proxy connections to the read slaves. In this case, in addition to discovery, the slave service provides transparent load balancing to clients. The service specification for the slaves is in `examples/guestbook-go/redis-slave-service.json` 103 | 104 | This time the selector for the service is `name=redis,role=slave`, because that identifies the pods running redis slaves. It may also be helpful to set labels on your service itself--as we've done here--to make it easy to locate them later. 105 | 106 | Now that you have created the service specification, create it in your cluster with the `kubectl` CLI: 107 | 108 | ```shell 109 | $ cluster/kubectl.sh create -f examples/guestbook-go/redis-slave-service.json 110 | 111 | $ cluster/kubectl.sh get services 112 | NAME LABELS SELECTOR IP PORT 113 | redis-master name=redis,role=master 10.0.186.234 6379 114 | redis-slave name=redis,role=slave name=redis,role=slave 10.0.22.180 6379 115 | ``` 116 | 117 | ### Step Five: Create the guestbook pod. 118 | 119 | This is a simple Go net/http ([negroni](https://github.com/codegangsta/negroni) based) server that is configured to talk to either the slave or master services depending on whether the request is a read or a write. It exposes a simple JSON interface, and serves a jQuery-Ajax based UX. Like the redis read slaves it is a replicated service instantiated by a replication controller. 120 | 121 | The pod is described in the file `examples/guestbook-go/guestbook-controller.json`. Using this file, you can turn up your guestbook with: 122 | 123 | ```shell 124 | $ cluster/kubectl.sh create -f examples/guestbook-go/guestbook-controller.json 125 | 126 | $ cluster/kubectl.sh get replicationControllers 127 | CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS 128 | guestbook-controller guestbook kubernetes/guestbook name=guestbook 3 129 | redis-master-controller redis-master gurpartap/redis name=redis,role=master 1 130 | redis-slave-controller redis-slave gurpartap/redis name=redis,role=slave 2 131 | ``` 132 | 133 | Once that's up (it may take ten to thirty seconds to create the pods) you can list the pods in the cluster, to verify that the master, slaves and guestbook frontends are running: 134 | 135 | ```shell 136 | $ cluster/kubectl.sh get pods 137 | POD IP CONTAINER(S) IMAGE(S) HOST LABELS STATUS 138 | guestbook-controller-182tv 10.244.2.8 guestbook kubernetes/guestbook kubernetes-minion-3.c.lucid-walker-725.internal/104.154.52.39 name=guestbook Running 139 | guestbook-controller-jzjpe 10.244.0.7 guestbook kubernetes/guestbook kubernetes-minion-1.c.lucid-walker-725.internal/104.154.37.86 name=guestbook Running 140 | guestbook-controller-zwk1b 10.244.3.8 guestbook kubernetes/guestbook kubernetes-minion-4.c.lucid-walker-725.internal/104.154.49.134 name=guestbook Running 141 | redis-master-pod-hh2gd 10.244.3.7 redis-master gurpartap/redis kubernetes-minion-4.c.lucid-walker-725.internal/104.154.49.134 name=redis,role=master Running 142 | redis-slave-controller-i7hvs 10.244.2.7 redis-slave gurpartap/redis kubernetes-minion-3.c.lucid-walker-725.internal/104.154.52.39 name=redis,role=slave Running 143 | redis-slave-controller-nyxxv 10.244.1.6 redis-slave gurpartap/redis kubernetes-minion-2.c.lucid-walker-725.internal/130.211.144.5 name=redis,role=slave Running 144 | ``` 145 | 146 | You will see a single redis master pod, two redis slaves, and three guestbook pods. 147 | 148 | ### Step Six: Create the guestbook service. 149 | 150 | Just like the others, you want a service to group your guestbook pods. The service specification for the guestbook is in `examples/guestbook-go/guestbook-service.json`. There's a twist this time - because we want it to be externally visible, we set the `createExternalLoadBalancer` flag on the service. 151 | 152 | ```shell 153 | $ cluster/kubectl.sh create -f examples/guestbook-go/guestbook-service.json 154 | 155 | $ cluster/kubectl.sh get services 156 | NAME LABELS SELECTOR IP PORT 157 | guestbook name=guestbook 10.0.12.110 3000 158 | redis-master name=redis,role=master 10.0.186.234 6379 159 | redis-slave name=redis,role=slave name=redis,role=slave 10.0.22.180 6379 160 | ``` 161 | 162 | To play with the service itself, find the external IP of the load balancer: 163 | 164 | ```shell 165 | $ cluster/kubectl.sh get services guestbook -o template --template='{{index . "publicIPs"}}' 166 | current-context: "kubernetes-satnam_kubernetes" 167 | Running: cluster/../cluster/gce/../../_output/dockerized/bin/linux/amd64/kubectl get services guestbook -o template --template={{index . "publicIPs"}} 168 | [104.154.87.59]$ 169 | 170 | ``` 171 | and then visit port 3000 of that IP address e.g. `http://104.154.87.59:3000`. 172 | 173 | You may need to open the firewall for port 3000 using the [console][cloud-console] or the `gcloud` tool. The following command will allow traffic from any source to instances tagged `kubernetes-minion`: 174 | 175 | ```shell 176 | $ gcloud compute firewall-rules create --allow=tcp:3000 --target-tags=kubernetes-minion kubernetes-minion-3000 177 | ``` 178 | 179 | If you are running Kubernetes locally, you can just visit http://localhost:3000 180 | For details about limiting traffic to specific sources, see the [GCE firewall documentation][gce-firewall-docs]. 181 | 182 | [cloud-console]: https://console.developer.google.com 183 | [gce-firewall-docs]: https://cloud.google.com/compute/docs/networking#firewalls 184 | 185 | ### Step Seven: Cleanup 186 | 187 | You should delete the service which will remove any associated resources that were created e.g. load balancers, forwarding rules and target pools. All the resources (pods, replication controllers and service) can be deleted with a single command: 188 | ```shell 189 | $ cluster/kubectl.sh delete -f examples/guestbook-go 190 | current-context: "kubernetes-satnam_kubernetes" 191 | Running: cluster/../cluster/gce/../../_output/dockerized/bin/linux/amd64/kubectl delete -f examples/guestbook-go 192 | guestbook-controller 193 | guestbook 194 | redis-master-controller 195 | redis-master 196 | redis-slave-controller 197 | redis-slave 198 | ``` 199 | To turn down a Kubernetes cluster: 200 | 201 | ```shell 202 | $ cluster/kube-down.sh 203 | ``` 204 | -------------------------------------------------------------------------------- /test/guestbook-go/guestbook-controller.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1beta1", 3 | "kind": "ReplicationController", 4 | "id": "guestbook-controller", 5 | "desiredState": { 6 | "replicas": 3, 7 | "replicaSelector": { "name": "guestbook" }, 8 | "podTemplate": { 9 | "desiredState": { 10 | "manifest": { 11 | "version": "v1beta1", 12 | "id": "guestbook-controller", 13 | "containers": [{ 14 | "image": "kubernetes/guestbook", 15 | "name": "guestbook", 16 | "ports": [{ "name": "http-server", "containerPort": 3000 }] 17 | }] 18 | } 19 | }, 20 | "labels": { "name": "guestbook", "servicename": "guestbookwebapp"} 21 | } 22 | }, 23 | "labels": { "name": "guestbook", 24 | "servicename": "guestbookwebapp" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/guestbook-go/guestbook-service.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1beta1", 3 | "kind": "Service", 4 | "id": "guestbook", 5 | "port": 3000, 6 | "containerPort": "http-server", 7 | "publicIPs":[ 8 | "103.227.76.29" 9 | ], 10 | "labels": { "servicename": "guestbookwebapp" }, 11 | "selector": { "name": "guestbook" } 12 | } 13 | -------------------------------------------------------------------------------- /test/guestbook-go/redis-master-controller.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1beta1", 3 | "kind": "ReplicationController", 4 | "id": "redis-master-controller", 5 | "desiredState": { 6 | "replicas": 1, 7 | "replicaSelector": { "name": "redis", "role": "master" }, 8 | "podTemplate": { 9 | "desiredState": { 10 | "manifest": { 11 | "version": "v1beta1", 12 | "id": "redis-master-controller", 13 | "containers": [{ 14 | "name": "redis-master", 15 | "image": "gurpartap/redis", 16 | "ports": [{ "name": "redis-server", "containerPort": 6379 }] 17 | }] 18 | } 19 | }, 20 | "labels": { "name": "redis", "role": "master", 21 | "servicename": "guestbookwebapp" } 22 | } 23 | }, 24 | "labels": { "name": "redis", "role": "master", 25 | "servicename": "guestbookwebapp" } 26 | } 27 | -------------------------------------------------------------------------------- /test/guestbook-go/redis-master-service.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1beta1", 3 | "kind": "Service", 4 | "id": "redis-master", 5 | "port": 6379, 6 | "containerPort": "redis-server", 7 | "labels": { "servicename": "guestbookwebapp" }, 8 | "selector": { "name": "redis", "role": "master" } 9 | } 10 | -------------------------------------------------------------------------------- /test/guestbook-go/redis-slave-controller.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1beta1", 3 | "kind": "ReplicationController", 4 | "id": "redis-slave-controller", 5 | "desiredState": { 6 | "replicas": 2, 7 | "replicaSelector": { "name": "redis", "role": "slave" }, 8 | "podTemplate": { 9 | "desiredState": { 10 | "manifest": { 11 | "version": "v1beta1", 12 | "id": "redis-slave-controller", 13 | "containers": [{ 14 | "name": "redis-slave", 15 | "image": "gurpartap/redis", 16 | "command": ["sh", "-c", "redis-server /etc/redis/redis.conf --slaveof $REDIS_MASTER_SERVICE_HOST $REDIS_MASTER_SERVICE_PORT"], 17 | "ports": [{ "name": "redis-server", "containerPort": 6379 }] 18 | }] 19 | } 20 | }, 21 | "labels": { "name": "redis", "role": "slave","servicename": "guestbookwebapp" } 22 | } 23 | }, 24 | "labels": { "name": "redis", "role": "slave","servicename": "guestbookwebapp" } 25 | } 26 | -------------------------------------------------------------------------------- /test/guestbook-go/redis-slave-service.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1beta1", 3 | "kind": "Service", 4 | "id": "redis-slave", 5 | "port": 6379, 6 | "containerPort": "redis-server", 7 | "labels": { "name": "redis", "role": "slave","servicename": "guestbookwebapp" }, 8 | "selector": { "name": "redis", "role": "slave" } 9 | } 10 | -------------------------------------------------------------------------------- /test/guestbook_go_test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * test kubernetes replicationControllers 3 | * @author huangqg 4 | * @date 2051-03-18 5 | */ 6 | 7 | 8 | var assert = require('assert'); 9 | var fs = require('fs'); 10 | var Client = require('../'); 11 | var client; 12 | client = new Client(require('./config.json').k8s); 13 | console.log('creating redis-master-controller'); 14 | client.replicationControllers.create(require('./guestbook-go/redis-master-controller.json'), function (err, replicationControllersArr) { 15 | if (!err) { 16 | console.log('replicationControllers: ' + JSON.stringify(replicationControllersArr)); 17 | console.log("creating redis-master-service") 18 | client.services.create(require('./guestbook-go/redis-master-service.json'), function (err, service) { 19 | if (!err) { 20 | console.log('services ' + JSON.stringify(service)); 21 | // output results 22 | fs.writeFile("results/redis-master-service.json", JSON.stringify(service, null, 4)); 23 | console.log("creating redis-master-service") 24 | client.replicationControllers.create(require('./guestbook-go/redis-slave-controller.json'), function (err, replicationControllersArr) { 25 | if (!err) { 26 | client.services.create(require('./guestbook-go/redis-slave-service.json'), function (err, service) { 27 | if(!err) { 28 | client.replicationControllers.create(require('./guestbook-go/guestbook-controller.json'), function (err, replicationControllersArr) { 29 | if (!err) { 30 | client.services.create(require('./guestbook-go/guestbook-service.json'), function (err, service) { 31 | if(!err) { 32 | console.log('guest book created successfully'); 33 | console.log('please run the following cmd to remove the previous resources'); 34 | console.log(" kubectl delete pod,rc,service -l servicename=guestbookwebapp"); 35 | 36 | 37 | } else { 38 | console.log('err: ' + JSON.stringify(err)); 39 | throw err; 40 | } 41 | }); 42 | }else { 43 | throw err; 44 | } 45 | }); 46 | 47 | } else { 48 | throw err; 49 | } 50 | }); 51 | }else { 52 | throw err; 53 | } 54 | }); 55 | } else { 56 | console.log(err); 57 | 58 | } 59 | }); 60 | } else { 61 | console.log(err); 62 | 63 | } 64 | }); -------------------------------------------------------------------------------- /test/integrate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * integrate unit tests 3 | */ 4 | var Mocha = require('mocha'); 5 | var glob = require("glob"); 6 | var path = require('path'); 7 | var fs = require('fs'); 8 | var util = require('util'); 9 | 10 | 11 | var config = require('./config.json'); 12 | 13 | var pattern = config.pattern; 14 | var skip = config.skip; 15 | var test_suite = []; 16 | var skip_suite = []; 17 | var mocha = new Mocha(); 18 | 19 | glob(pattern, { 20 | sync : true 21 | }, function(er, matches) { 22 | matches.forEach(function(match) { 23 | console.log(skip); 24 | if (match.indexOf(skip) >= 0) { 25 | skip_suite.push(path.resolve(match)); 26 | } else { 27 | test_suite.push(path.resolve(match)); 28 | } 29 | }); 30 | console.log("test_suite:" + test_suite); 31 | console.log("skip_suite:" + skip_suite); 32 | test_suite.forEach(function(testcase) { 33 | mocha.addFile(testcase); 34 | }); 35 | mocha.reporter('spec').run(); 36 | //mocha.reporter('spec').ui('tdd').run(); 37 | }); 38 | 39 | /** 40 | patterns.forEach(function(pattern) { 41 | console.log('pattern: ' + pattern); 42 | glob(pattern, { 43 | sync : true 44 | }, function(err, matches) { 45 | console.log('err: ' + err); 46 | console.log('matches: ' + matches); 47 | matches.forEach(function(match) { 48 | if (skip.indexOf(match) > -1) { 49 | skip_suite.push(path.resolve(match)); 50 | } else { 51 | test_suite.push(path.resolve(match)); 52 | } 53 | }); 54 | }); 55 | }); 56 | console.log("test_suite:" + test_suite); 57 | console.log("skip_suite:" + skip_suite); 58 | test_suite.forEach(function(testcase) { 59 | mocha.addFile(testcase); 60 | }); 61 | mocha.reporter('spec').run(); 62 | **/ -------------------------------------------------------------------------------- /test/json/horizontalpodautoscaler.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "autoscaling/v1", 3 | "kind": "HorizontalPodAutoscaler", 4 | "metadata": { 5 | "name": "hpa" 6 | }, 7 | "spec": { 8 | "maxReplicas": 10, 9 | "minReplicas": 1, 10 | "scaleTargetRef": { 11 | "apiVersion": "extensions/v1beta1", 12 | "kind": "Deployment", 13 | "name": "mydeployment" 14 | }, 15 | "targetCPUUtilizationPercentage": 50 16 | } 17 | } -------------------------------------------------------------------------------- /test/json/namespace.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "metadata": { 4 | "name": "myspace2" 5 | }, 6 | "kind": "Namespace", 7 | "id": "myspace2" 8 | } -------------------------------------------------------------------------------- /test/json/pod.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Pod", 3 | "id": "busybox", 4 | "apiVersion": "v1", 5 | "namespace": "default", 6 | "metadata": { 7 | "name": "busybox" 8 | }, 9 | "spec": { 10 | "volumes": null, 11 | "containers": [ 12 | { 13 | "name": "busybox", 14 | "image": "busybox", 15 | "ports": [ 16 | { 17 | "containerPort": 22, 18 | "protocol": "TCP" 19 | } 20 | ] 21 | } 22 | ] 23 | } 24 | } -------------------------------------------------------------------------------- /test/json/rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "ReplicationController", 3 | "id": "guestbook-controller", 4 | "apiVersion": "v1", 5 | "namespace": "default", 6 | "desiredState": { 7 | "replicas": 1, 8 | "replicaSelector": { 9 | "name": "guestbook" 10 | }, 11 | "podTemplate": { 12 | "desiredState": { 13 | "manifest": { 14 | "version": "v1", 15 | "id": "", 16 | "volumes": null, 17 | "containers": [ 18 | { 19 | "name": "guestbook", 20 | "image": "kubernetes/guestbook:v2", 21 | "ports": [ 22 | { 23 | "name": "http-server", 24 | "containerPort": 3000, 25 | "protocol": "TCP" 26 | } 27 | ], 28 | "resources": {}, 29 | "terminationMessagePath": "/dev/termination-log", 30 | "imagePullPolicy": "PullIfNotPresent", 31 | "capabilities": {} 32 | } 33 | ], 34 | "restartPolicy": { 35 | "always": {} 36 | }, 37 | "dnsPolicy": "ClusterFirst" 38 | } 39 | }, 40 | "labels": { 41 | "name": "guestbook" 42 | } 43 | } 44 | }, 45 | "labels": { 46 | "name": "guestbook" 47 | } 48 | } -------------------------------------------------------------------------------- /test/json/service.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "kind": "Service", 4 | "metadata": { 5 | "labels": { 6 | "service": "redis" 7 | }, 8 | "name": "redis" 9 | }, 10 | "spec": { 11 | "ports": [ 12 | { 13 | "port": 80, 14 | "protocol": "TCP", 15 | "targetPort": 80 16 | } 17 | ], 18 | "selector": { 19 | "service": "redis" 20 | }, 21 | "type": "ClusterIP" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/json/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "mywordpress6", 3 | "apiVersion": "v1", 4 | "kind": "ReplicationController", 5 | "desiredState": { 6 | "replicas": 1, 7 | "replicaSelector": { 8 | "name": "mywordpress6" 9 | }, 10 | "podTemplate": { 11 | "desiredState": { 12 | "manifest": { 13 | "id": "mywordpress3", 14 | "version": "v1", 15 | "containers": [ 16 | { 17 | "name": "mywordpress6", 18 | "image": "wordpress", 19 | "ports": [ 20 | { 21 | "hostPort": 5001, 22 | "containerPort": 80 23 | } 24 | ], 25 | "env": [ 26 | { 27 | "name": "WORDPRESS_DB_HOST", 28 | "value": "10.80.197.181:3306" 29 | }, 30 | { 31 | "name": "WORDPRESS_DB_USER", 32 | "value": "root" 33 | }, 34 | { 35 | "name": "WORDPRESS_DB_PASSWORD", 36 | "value": "tenxcloud" 37 | } 38 | ] 39 | } 40 | ] 41 | } 42 | }, 43 | "labels": { 44 | "name": "mywordpress6" 45 | } 46 | } 47 | }, 48 | "labels": { 49 | "name": "mywordpress6" 50 | } 51 | } -------------------------------------------------------------------------------- /test/json/wordpress-controller.json: -------------------------------------------------------------------------------- 1 | { 2 | "id":"mywordpress-controller", 3 | "apiVersion":"v1", 4 | "kind":"ReplicationController", 5 | "metadata": { 6 | "name": "mywordpress" 7 | }, 8 | "spec":{ 9 | "replicas":1, 10 | "selector": { 11 | "name": "mywordpress" 12 | }, 13 | "template": { 14 | "metadata": { 15 | "labels": { 16 | "name": "mywordpress" 17 | }, 18 | "name": "mywordpress" 19 | }, 20 | "spec": { 21 | "containers": [ 22 | { 23 | "name": "wordpress", 24 | "image": "wordpress", 25 | "ports": [ 26 | { 27 | "name": "http-server", 28 | "containerPort": 80, 29 | "protocol": "TCP" 30 | } 31 | ], 32 | "imagePullPolicy": "IfNotPresent" 33 | } 34 | ] 35 | } 36 | } 37 | }, 38 | "labels":{ 39 | "name":"mywordpress" 40 | } 41 | } -------------------------------------------------------------------------------- /test/json/wordpress-service.json: -------------------------------------------------------------------------------- 1 | { 2 | "apiVersion": "v1", 3 | "kind": "Service", 4 | "metadata": { 5 | "labels": { 6 | "service": "mywordpress" 7 | }, 8 | "name": "mywordpress" 9 | }, 10 | "spec": { 11 | "ports": [ 12 | { 13 | "port": 3000, 14 | "protocol": "TCP", 15 | "targetPort": 80 16 | } 17 | ], 18 | "selector": { 19 | "name": "mywordpress" 20 | }, 21 | "type": "ClusterIP" 22 | } 23 | } -------------------------------------------------------------------------------- /test/results/pod.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "Pod", 3 | "id": "cassandra", 4 | "uid": "2afe32dd-cfa4-11e4-94ea-52546d69a10d", 5 | "creationTimestamp": "2015-03-21T16:27:53+08:00", 6 | "selfLink": "/api/v1beta1/pods/cassandra?namespace=default", 7 | "resourceVersion": 1323, 8 | "apiVersion": "v1beta1", 9 | "namespace": "default", 10 | "labels": { 11 | "name": "cassandra" 12 | }, 13 | "desiredState": { 14 | "manifest": { 15 | "version": "v1beta2", 16 | "id": "", 17 | "volumes": [ 18 | { 19 | "name": "data", 20 | "source": { 21 | "hostDir": null, 22 | "emptyDir": {}, 23 | "persistentDisk": null, 24 | "gitRepo": null, 25 | "secret": null 26 | } 27 | } 28 | ], 29 | "containers": [ 30 | { 31 | "name": "cassandra", 32 | "image": "kubernetes/cassandra:v1", 33 | "command": [ 34 | "/run.sh" 35 | ], 36 | "ports": [ 37 | { 38 | "name": "cql", 39 | "containerPort": 9042, 40 | "protocol": "TCP" 41 | }, 42 | { 43 | "name": "thrift", 44 | "containerPort": 9160, 45 | "protocol": "TCP" 46 | } 47 | ], 48 | "env": [ 49 | { 50 | "name": "MAX_HEAP_SIZE", 51 | "key": "MAX_HEAP_SIZE", 52 | "value": "512M" 53 | }, 54 | { 55 | "name": "HEAP_NEWSIZE", 56 | "key": "HEAP_NEWSIZE", 57 | "value": "100M" 58 | }, 59 | { 60 | "name": "KUBERNETES_API_PROTOCOL", 61 | "key": "KUBERNETES_API_PROTOCOL", 62 | "value": "http" 63 | } 64 | ], 65 | "resources": { 66 | "limits": { 67 | "cpu": "1" 68 | } 69 | }, 70 | "cpu": 1000, 71 | "volumeMounts": [ 72 | { 73 | "name": "data", 74 | "mountPath": "/cassandra_data", 75 | "path": "/cassandra_data" 76 | } 77 | ], 78 | "terminationMessagePath": "/dev/termination-log", 79 | "imagePullPolicy": "PullIfNotPresent", 80 | "capabilities": {} 81 | } 82 | ], 83 | "restartPolicy": { 84 | "always": {} 85 | }, 86 | "dnsPolicy": "ClusterFirst" 87 | } 88 | }, 89 | "currentState": { 90 | "manifest": { 91 | "version": "", 92 | "id": "", 93 | "volumes": null, 94 | "containers": null, 95 | "restartPolicy": {} 96 | }, 97 | "status": "Running", 98 | "Condition": [ 99 | { 100 | "kind": "Ready", 101 | "status": "Full" 102 | } 103 | ], 104 | "host": "10.80.188.27", 105 | "hostIP": "10.80.188.27", 106 | "podIP": "10.0.72.5", 107 | "info": { 108 | "cassandra": { 109 | "state": { 110 | "running": { 111 | "startedAt": "2015-03-24T03:33:10Z" 112 | } 113 | }, 114 | "ready": true, 115 | "restartCount": 4, 116 | "image": "kubernetes/cassandra:v1", 117 | "imageID": "docker://13d7bd47a96343941fdf99748e2ff0033a69b0baf85ce0f2222c0016f65bb0bb", 118 | "containerID": "docker://b8c5fa7025677b518dac9919dfa0feba44f9676c2b9fc095d840eb8184dc1ff9" 119 | } 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /test/results/pods.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "PodList", 3 | "creationTimestamp": null, 4 | "selfLink": "/api/v1beta1/pods?namespace=default", 5 | "resourceVersion": 38070, 6 | "apiVersion": "v1beta1", 7 | "items": [ 8 | { 9 | "id": "cassandra", 10 | "uid": "2afe32dd-cfa4-11e4-94ea-52546d69a10d", 11 | "creationTimestamp": "2015-03-21T16:27:53+08:00", 12 | "selfLink": "/api/v1beta1/pods/cassandra?namespace=default", 13 | "resourceVersion": 1323, 14 | "namespace": "default", 15 | "labels": { 16 | "name": "cassandra" 17 | }, 18 | "desiredState": { 19 | "manifest": { 20 | "version": "v1beta2", 21 | "id": "", 22 | "volumes": [ 23 | { 24 | "name": "data", 25 | "source": { 26 | "hostDir": null, 27 | "emptyDir": {}, 28 | "persistentDisk": null, 29 | "gitRepo": null, 30 | "secret": null 31 | } 32 | } 33 | ], 34 | "containers": [ 35 | { 36 | "name": "cassandra", 37 | "image": "kubernetes/cassandra:v1", 38 | "command": [ 39 | "/run.sh" 40 | ], 41 | "ports": [ 42 | { 43 | "name": "cql", 44 | "containerPort": 9042, 45 | "protocol": "TCP" 46 | }, 47 | { 48 | "name": "thrift", 49 | "containerPort": 9160, 50 | "protocol": "TCP" 51 | } 52 | ], 53 | "env": [ 54 | { 55 | "name": "MAX_HEAP_SIZE", 56 | "key": "MAX_HEAP_SIZE", 57 | "value": "512M" 58 | }, 59 | { 60 | "name": "HEAP_NEWSIZE", 61 | "key": "HEAP_NEWSIZE", 62 | "value": "100M" 63 | }, 64 | { 65 | "name": "KUBERNETES_API_PROTOCOL", 66 | "key": "KUBERNETES_API_PROTOCOL", 67 | "value": "http" 68 | } 69 | ], 70 | "resources": { 71 | "limits": { 72 | "cpu": "1" 73 | } 74 | }, 75 | "cpu": 1000, 76 | "volumeMounts": [ 77 | { 78 | "name": "data", 79 | "mountPath": "/cassandra_data", 80 | "path": "/cassandra_data" 81 | } 82 | ], 83 | "terminationMessagePath": "/dev/termination-log", 84 | "imagePullPolicy": "PullIfNotPresent", 85 | "capabilities": {} 86 | } 87 | ], 88 | "restartPolicy": { 89 | "always": {} 90 | }, 91 | "dnsPolicy": "ClusterFirst" 92 | } 93 | }, 94 | "currentState": { 95 | "manifest": { 96 | "version": "", 97 | "id": "", 98 | "volumes": null, 99 | "containers": null, 100 | "restartPolicy": {} 101 | }, 102 | "status": "Running", 103 | "Condition": [ 104 | { 105 | "kind": "Ready", 106 | "status": "Full" 107 | } 108 | ], 109 | "host": "10.80.188.27", 110 | "hostIP": "10.80.188.27", 111 | "podIP": "10.0.72.5", 112 | "info": { 113 | "cassandra": { 114 | "state": { 115 | "running": { 116 | "startedAt": "2015-03-24T03:33:10Z" 117 | } 118 | }, 119 | "ready": true, 120 | "restartCount": 4, 121 | "image": "kubernetes/cassandra:v1", 122 | "imageID": "docker://13d7bd47a96343941fdf99748e2ff0033a69b0baf85ce0f2222c0016f65bb0bb", 123 | "containerID": "docker://b8c5fa7025677b518dac9919dfa0feba44f9676c2b9fc095d840eb8184dc1ff9" 124 | } 125 | } 126 | } 127 | }, 128 | { 129 | "id": "cassandra-fplln", 130 | "uid": "695decfe-cfa9-11e4-94ea-52546d69a10d", 131 | "creationTimestamp": "2015-03-21T17:05:25+08:00", 132 | "selfLink": "/api/v1beta1/pods/cassandra-fplln?namespace=default", 133 | "resourceVersion": 1769, 134 | "namespace": "default", 135 | "generateName": "cassandra-", 136 | "labels": { 137 | "name": "cassandra" 138 | }, 139 | "desiredState": { 140 | "manifest": { 141 | "version": "v1beta2", 142 | "id": "", 143 | "volumes": [ 144 | { 145 | "name": "data", 146 | "source": { 147 | "hostDir": null, 148 | "emptyDir": {}, 149 | "persistentDisk": null, 150 | "gitRepo": null, 151 | "secret": null 152 | } 153 | } 154 | ], 155 | "containers": [ 156 | { 157 | "name": "cassandra", 158 | "image": "kubernetes/cassandra:v1", 159 | "command": [ 160 | "/run.sh" 161 | ], 162 | "ports": [ 163 | { 164 | "name": "cql", 165 | "containerPort": 9042, 166 | "protocol": "TCP" 167 | }, 168 | { 169 | "name": "thrift", 170 | "containerPort": 9160, 171 | "protocol": "TCP" 172 | } 173 | ], 174 | "env": [ 175 | { 176 | "name": "MAX_HEAP_SIZE", 177 | "key": "MAX_HEAP_SIZE", 178 | "value": "512M" 179 | }, 180 | { 181 | "name": "HEAP_NEWSIZE", 182 | "key": "HEAP_NEWSIZE", 183 | "value": "100M" 184 | } 185 | ], 186 | "resources": { 187 | "limits": { 188 | "cpu": "1" 189 | } 190 | }, 191 | "cpu": 1000, 192 | "volumeMounts": [ 193 | { 194 | "name": "data", 195 | "mountPath": "/cassandra_data", 196 | "path": "/cassandra_data" 197 | } 198 | ], 199 | "terminationMessagePath": "/dev/termination-log", 200 | "imagePullPolicy": "PullIfNotPresent", 201 | "capabilities": {} 202 | } 203 | ], 204 | "restartPolicy": { 205 | "always": {} 206 | }, 207 | "dnsPolicy": "ClusterFirst" 208 | } 209 | }, 210 | "currentState": { 211 | "manifest": { 212 | "version": "", 213 | "id": "", 214 | "volumes": null, 215 | "containers": null, 216 | "restartPolicy": {} 217 | }, 218 | "status": "Running", 219 | "Condition": [ 220 | { 221 | "kind": "Ready", 222 | "status": "Full" 223 | } 224 | ], 225 | "host": "10.80.197.181", 226 | "hostIP": "10.80.197.181", 227 | "podIP": "10.0.70.6", 228 | "info": { 229 | "cassandra": { 230 | "state": { 231 | "running": { 232 | "startedAt": "2015-03-24T03:32:39Z" 233 | } 234 | }, 235 | "ready": true, 236 | "restartCount": 4, 237 | "image": "kubernetes/cassandra:v1", 238 | "imageID": "docker://13d7bd47a96343941fdf99748e2ff0033a69b0baf85ce0f2222c0016f65bb0bb", 239 | "containerID": "docker://96117d28664005ca4cfb95547f11e71cf0166eb40a591b2bfc5311a5a5fa4a1a" 240 | } 241 | } 242 | } 243 | }, 244 | { 245 | "id": "guestbook-controller-hh2gd", 246 | "uid": "0dec89be-cfaf-11e4-94ea-52546d69a10d", 247 | "creationTimestamp": "2015-03-21T17:45:48+08:00", 248 | "selfLink": "/api/v1beta1/pods/guestbook-controller-hh2gd?namespace=default", 249 | "resourceVersion": 2288, 250 | "namespace": "default", 251 | "generateName": "guestbook-controller-", 252 | "labels": { 253 | "name": "guestbook" 254 | }, 255 | "desiredState": { 256 | "manifest": { 257 | "version": "v1beta2", 258 | "id": "", 259 | "volumes": null, 260 | "containers": [ 261 | { 262 | "name": "guestbook", 263 | "image": "kubernetes/guestbook:v2", 264 | "ports": [ 265 | { 266 | "name": "http-server", 267 | "containerPort": 3000, 268 | "protocol": "TCP" 269 | } 270 | ], 271 | "resources": {}, 272 | "terminationMessagePath": "/dev/termination-log", 273 | "imagePullPolicy": "PullIfNotPresent", 274 | "capabilities": {} 275 | } 276 | ], 277 | "restartPolicy": { 278 | "always": {} 279 | }, 280 | "dnsPolicy": "ClusterFirst" 281 | } 282 | }, 283 | "currentState": { 284 | "manifest": { 285 | "version": "", 286 | "id": "", 287 | "volumes": null, 288 | "containers": null, 289 | "restartPolicy": {} 290 | }, 291 | "status": "Running", 292 | "Condition": [ 293 | { 294 | "kind": "Ready", 295 | "status": "Full" 296 | } 297 | ], 298 | "host": "10.80.197.181", 299 | "hostIP": "10.80.197.181", 300 | "podIP": "10.0.70.3", 301 | "info": { 302 | "guestbook": { 303 | "state": { 304 | "running": { 305 | "startedAt": "2015-03-24T03:32:39Z" 306 | } 307 | }, 308 | "ready": true, 309 | "restartCount": 4, 310 | "image": "kubernetes/guestbook:v2", 311 | "imageID": "docker://6525c07bfd08eac8e77751b55f09ce88f90cf352c37b8a6e04ff4e360e5703a1", 312 | "containerID": "docker://ce68285f396071be765b4269ecdb1ca13dc00c192e7fad1cf69ec129a84b1652" 313 | } 314 | } 315 | } 316 | }, 317 | { 318 | "id": "guestbook-controller-ls6k1", 319 | "uid": "0debc86f-cfaf-11e4-94ea-52546d69a10d", 320 | "creationTimestamp": "2015-03-21T17:45:48+08:00", 321 | "selfLink": "/api/v1beta1/pods/guestbook-controller-ls6k1?namespace=default", 322 | "resourceVersion": 2286, 323 | "namespace": "default", 324 | "generateName": "guestbook-controller-", 325 | "labels": { 326 | "name": "guestbook" 327 | }, 328 | "desiredState": { 329 | "manifest": { 330 | "version": "v1beta2", 331 | "id": "", 332 | "volumes": null, 333 | "containers": [ 334 | { 335 | "name": "guestbook", 336 | "image": "kubernetes/guestbook:v2", 337 | "ports": [ 338 | { 339 | "name": "http-server", 340 | "containerPort": 3000, 341 | "protocol": "TCP" 342 | } 343 | ], 344 | "resources": {}, 345 | "terminationMessagePath": "/dev/termination-log", 346 | "imagePullPolicy": "PullIfNotPresent", 347 | "capabilities": {} 348 | } 349 | ], 350 | "restartPolicy": { 351 | "always": {} 352 | }, 353 | "dnsPolicy": "ClusterFirst" 354 | } 355 | }, 356 | "currentState": { 357 | "manifest": { 358 | "version": "", 359 | "id": "", 360 | "volumes": null, 361 | "containers": null, 362 | "restartPolicy": {} 363 | }, 364 | "status": "Running", 365 | "Condition": [ 366 | { 367 | "kind": "Ready", 368 | "status": "Full" 369 | } 370 | ], 371 | "host": "10.80.188.27", 372 | "hostIP": "10.80.188.27", 373 | "podIP": "10.0.72.6", 374 | "info": { 375 | "guestbook": { 376 | "state": { 377 | "running": { 378 | "startedAt": "2015-03-24T03:33:10Z" 379 | } 380 | }, 381 | "ready": true, 382 | "restartCount": 4, 383 | "image": "kubernetes/guestbook:v2", 384 | "imageID": "docker://6525c07bfd08eac8e77751b55f09ce88f90cf352c37b8a6e04ff4e360e5703a1", 385 | "containerID": "docker://8cd6d087a621b046dd25543b8e4457969832730cd85c5d62cc1cfc9550db1669" 386 | } 387 | } 388 | } 389 | }, 390 | { 391 | "id": "guestbook-controller-nyxxv", 392 | "uid": "0decfd2e-cfaf-11e4-94ea-52546d69a10d", 393 | "creationTimestamp": "2015-03-21T17:45:48+08:00", 394 | "selfLink": "/api/v1beta1/pods/guestbook-controller-nyxxv?namespace=default", 395 | "resourceVersion": 2291, 396 | "namespace": "default", 397 | "generateName": "guestbook-controller-", 398 | "labels": { 399 | "name": "guestbook" 400 | }, 401 | "desiredState": { 402 | "manifest": { 403 | "version": "v1beta2", 404 | "id": "", 405 | "volumes": null, 406 | "containers": [ 407 | { 408 | "name": "guestbook", 409 | "image": "kubernetes/guestbook:v2", 410 | "ports": [ 411 | { 412 | "name": "http-server", 413 | "containerPort": 3000, 414 | "protocol": "TCP" 415 | } 416 | ], 417 | "resources": {}, 418 | "terminationMessagePath": "/dev/termination-log", 419 | "imagePullPolicy": "PullIfNotPresent", 420 | "capabilities": {} 421 | } 422 | ], 423 | "restartPolicy": { 424 | "always": {} 425 | }, 426 | "dnsPolicy": "ClusterFirst" 427 | } 428 | }, 429 | "currentState": { 430 | "manifest": { 431 | "version": "", 432 | "id": "", 433 | "volumes": null, 434 | "containers": null, 435 | "restartPolicy": {} 436 | }, 437 | "status": "Running", 438 | "Condition": [ 439 | { 440 | "kind": "Ready", 441 | "status": "Full" 442 | } 443 | ], 444 | "host": "10.80.188.27", 445 | "hostIP": "10.80.188.27", 446 | "podIP": "10.0.72.4", 447 | "info": { 448 | "guestbook": { 449 | "state": { 450 | "running": { 451 | "startedAt": "2015-03-24T03:33:10Z" 452 | } 453 | }, 454 | "ready": true, 455 | "restartCount": 4, 456 | "image": "kubernetes/guestbook:v2", 457 | "imageID": "docker://6525c07bfd08eac8e77751b55f09ce88f90cf352c37b8a6e04ff4e360e5703a1", 458 | "containerID": "docker://688d78e6b1a73360745a192ce22b2ec9f441c1275ee61e6e5db61dc5675d883c" 459 | } 460 | } 461 | } 462 | }, 463 | { 464 | "id": "mysql", 465 | "uid": "37dc7bbb-d0a2-11e4-ab21-52546d69a10d", 466 | "creationTimestamp": "2015-03-22T22:46:26+08:00", 467 | "selfLink": "/api/v1beta1/pods/mysql?namespace=default", 468 | "resourceVersion": 18037, 469 | "namespace": "default", 470 | "labels": { 471 | "name": "mysql" 472 | }, 473 | "desiredState": { 474 | "manifest": { 475 | "version": "v1beta2", 476 | "id": "", 477 | "volumes": null, 478 | "containers": [ 479 | { 480 | "name": "mysql", 481 | "image": "mysql", 482 | "ports": [ 483 | { 484 | "containerPort": 3306, 485 | "protocol": "TCP" 486 | } 487 | ], 488 | "env": [ 489 | { 490 | "name": "MYSQL_ROOT_PASSWORD", 491 | "key": "MYSQL_ROOT_PASSWORD", 492 | "value": "tenxcloud" 493 | } 494 | ], 495 | "resources": {}, 496 | "terminationMessagePath": "/dev/termination-log", 497 | "imagePullPolicy": "PullIfNotPresent", 498 | "capabilities": {} 499 | } 500 | ], 501 | "restartPolicy": { 502 | "always": {} 503 | }, 504 | "dnsPolicy": "ClusterFirst" 505 | } 506 | }, 507 | "currentState": { 508 | "manifest": { 509 | "version": "", 510 | "id": "", 511 | "volumes": null, 512 | "containers": null, 513 | "restartPolicy": {} 514 | }, 515 | "status": "Running", 516 | "Condition": [ 517 | { 518 | "kind": "Ready", 519 | "status": "Full" 520 | } 521 | ], 522 | "host": "10.80.197.181", 523 | "hostIP": "10.80.197.181", 524 | "podIP": "10.0.70.4", 525 | "info": { 526 | "mysql": { 527 | "state": { 528 | "running": { 529 | "startedAt": "2015-03-24T03:32:39Z" 530 | } 531 | }, 532 | "ready": true, 533 | "restartCount": 2, 534 | "image": "mysql", 535 | "imageID": "docker://e93afb6a83e9df759e58f73804d2a7531ade4babeae6a3961e636885d97c2173", 536 | "containerID": "docker://48b8058dc949e65decead1d589cd43062d20deb86dd37843ccff07c8db820e5f" 537 | } 538 | } 539 | } 540 | }, 541 | { 542 | "id": "redis-master-controller-gziey", 543 | "uid": "a86457ec-cfaa-11e4-94ea-52546d69a10d", 544 | "creationTimestamp": "2015-03-21T17:14:20+08:00", 545 | "selfLink": "/api/v1beta1/pods/redis-master-controller-gziey?namespace=default", 546 | "resourceVersion": 1878, 547 | "namespace": "default", 548 | "generateName": "redis-master-controller-", 549 | "labels": { 550 | "name": "redis", 551 | "role": "master" 552 | }, 553 | "desiredState": { 554 | "manifest": { 555 | "version": "v1beta2", 556 | "id": "", 557 | "volumes": null, 558 | "containers": [ 559 | { 560 | "name": "redis-master", 561 | "image": "gurpartap/redis", 562 | "ports": [ 563 | { 564 | "name": "redis-server", 565 | "containerPort": 6379, 566 | "protocol": "TCP" 567 | } 568 | ], 569 | "resources": {}, 570 | "terminationMessagePath": "/dev/termination-log", 571 | "imagePullPolicy": "PullIfNotPresent", 572 | "capabilities": {} 573 | } 574 | ], 575 | "restartPolicy": { 576 | "always": {} 577 | }, 578 | "dnsPolicy": "ClusterFirst" 579 | } 580 | }, 581 | "currentState": { 582 | "manifest": { 583 | "version": "", 584 | "id": "", 585 | "volumes": null, 586 | "containers": null, 587 | "restartPolicy": {} 588 | }, 589 | "status": "Running", 590 | "Condition": [ 591 | { 592 | "kind": "Ready", 593 | "status": "Full" 594 | } 595 | ], 596 | "host": "10.80.188.27", 597 | "hostIP": "10.80.188.27", 598 | "podIP": "10.0.72.3", 599 | "info": { 600 | "redis-master": { 601 | "state": { 602 | "running": { 603 | "startedAt": "2015-03-24T03:33:10Z" 604 | } 605 | }, 606 | "ready": true, 607 | "restartCount": 4, 608 | "image": "gurpartap/redis", 609 | "imageID": "docker://8ed086d6282dcf392a0ae5f3d2c7b8f063681c9cd8a56322fa735735aa2f3aee", 610 | "containerID": "docker://a592759c227d27134330380ef241059158e389d7494a09321c68f13f089d4631" 611 | } 612 | } 613 | } 614 | }, 615 | { 616 | "id": "redis-slave-controller-0133o", 617 | "uid": "ff6e398a-cfad-11e4-94ea-52546d69a10d", 618 | "creationTimestamp": "2015-03-21T17:38:14+08:00", 619 | "selfLink": "/api/v1beta1/pods/redis-slave-controller-0133o?namespace=default", 620 | "resourceVersion": 2188, 621 | "namespace": "default", 622 | "generateName": "redis-slave-controller-", 623 | "labels": { 624 | "name": "redis", 625 | "role": "slave" 626 | }, 627 | "desiredState": { 628 | "manifest": { 629 | "version": "v1beta2", 630 | "id": "", 631 | "volumes": null, 632 | "containers": [ 633 | { 634 | "name": "redis-slave", 635 | "image": "gurpartap/redis", 636 | "command": [ 637 | "sh", 638 | "-c", 639 | "redis-server /etc/redis/redis.conf --slaveof redis-master 6379" 640 | ], 641 | "ports": [ 642 | { 643 | "name": "redis-server", 644 | "containerPort": 6379, 645 | "protocol": "TCP" 646 | } 647 | ], 648 | "resources": {}, 649 | "terminationMessagePath": "/dev/termination-log", 650 | "imagePullPolicy": "PullIfNotPresent", 651 | "capabilities": {} 652 | } 653 | ], 654 | "restartPolicy": { 655 | "always": {} 656 | }, 657 | "dnsPolicy": "ClusterFirst" 658 | } 659 | }, 660 | "currentState": { 661 | "manifest": { 662 | "version": "", 663 | "id": "", 664 | "volumes": null, 665 | "containers": null, 666 | "restartPolicy": {} 667 | }, 668 | "status": "Running", 669 | "Condition": [ 670 | { 671 | "kind": "Ready", 672 | "status": "Full" 673 | } 674 | ], 675 | "host": "10.80.188.27", 676 | "hostIP": "10.80.188.27", 677 | "podIP": "10.0.72.2", 678 | "info": { 679 | "redis-slave": { 680 | "state": { 681 | "running": { 682 | "startedAt": "2015-03-24T03:33:10Z" 683 | } 684 | }, 685 | "ready": true, 686 | "restartCount": 4, 687 | "image": "gurpartap/redis", 688 | "imageID": "docker://8ed086d6282dcf392a0ae5f3d2c7b8f063681c9cd8a56322fa735735aa2f3aee", 689 | "containerID": "docker://acb40aea29c149783088d8277dee9bd2e246ad7958cd65398adc6768269681f0" 690 | } 691 | } 692 | } 693 | }, 694 | { 695 | "id": "redis-slave-controller-oh43e", 696 | "uid": "ff6dc73c-cfad-11e4-94ea-52546d69a10d", 697 | "creationTimestamp": "2015-03-21T17:38:14+08:00", 698 | "selfLink": "/api/v1beta1/pods/redis-slave-controller-oh43e?namespace=default", 699 | "resourceVersion": 2186, 700 | "namespace": "default", 701 | "generateName": "redis-slave-controller-", 702 | "labels": { 703 | "name": "redis", 704 | "role": "slave" 705 | }, 706 | "desiredState": { 707 | "manifest": { 708 | "version": "v1beta2", 709 | "id": "", 710 | "volumes": null, 711 | "containers": [ 712 | { 713 | "name": "redis-slave", 714 | "image": "gurpartap/redis", 715 | "command": [ 716 | "sh", 717 | "-c", 718 | "redis-server /etc/redis/redis.conf --slaveof redis-master 6379" 719 | ], 720 | "ports": [ 721 | { 722 | "name": "redis-server", 723 | "containerPort": 6379, 724 | "protocol": "TCP" 725 | } 726 | ], 727 | "resources": {}, 728 | "terminationMessagePath": "/dev/termination-log", 729 | "imagePullPolicy": "PullIfNotPresent", 730 | "capabilities": {} 731 | } 732 | ], 733 | "restartPolicy": { 734 | "always": {} 735 | }, 736 | "dnsPolicy": "ClusterFirst" 737 | } 738 | }, 739 | "currentState": { 740 | "manifest": { 741 | "version": "", 742 | "id": "", 743 | "volumes": null, 744 | "containers": null, 745 | "restartPolicy": {} 746 | }, 747 | "status": "Running", 748 | "Condition": [ 749 | { 750 | "kind": "Ready", 751 | "status": "Full" 752 | } 753 | ], 754 | "host": "10.80.197.181", 755 | "hostIP": "10.80.197.181", 756 | "podIP": "10.0.70.2", 757 | "info": { 758 | "redis-slave": { 759 | "state": { 760 | "running": { 761 | "startedAt": "2015-03-24T03:32:39Z" 762 | } 763 | }, 764 | "ready": true, 765 | "restartCount": 4, 766 | "image": "gurpartap/redis", 767 | "imageID": "docker://8ed086d6282dcf392a0ae5f3d2c7b8f063681c9cd8a56322fa735735aa2f3aee", 768 | "containerID": "docker://bc03cfa02dd341d207c5dade28c64823f7a4fb9e6026f2f96d5b347b0967dc75" 769 | } 770 | } 771 | } 772 | }, 773 | { 774 | "id": "ubuntu1", 775 | "uid": "2a641cca-cfa5-11e4-94ea-52546d69a10d", 776 | "creationTimestamp": "2015-03-21T16:35:01+08:00", 777 | "selfLink": "/api/v1beta1/pods/ubuntu1?namespace=default", 778 | "resourceVersion": 1411, 779 | "namespace": "default", 780 | "labels": { 781 | "name": "ubuntu1" 782 | }, 783 | "desiredState": { 784 | "manifest": { 785 | "version": "v1beta2", 786 | "id": "", 787 | "volumes": null, 788 | "containers": [ 789 | { 790 | "name": "ubuntu1", 791 | "image": "tutum/ubuntu:trusty", 792 | "ports": [ 793 | { 794 | "hostPort": 34567, 795 | "containerPort": 22, 796 | "protocol": "TCP" 797 | } 798 | ], 799 | "env": [ 800 | { 801 | "name": "ROOT_PASS", 802 | "key": "ROOT_PASS", 803 | "value": "tenxcloud" 804 | } 805 | ], 806 | "resources": {}, 807 | "terminationMessagePath": "/dev/termination-log", 808 | "imagePullPolicy": "PullIfNotPresent", 809 | "capabilities": {} 810 | } 811 | ], 812 | "restartPolicy": { 813 | "onFailure": {} 814 | }, 815 | "dnsPolicy": "ClusterFirst" 816 | } 817 | }, 818 | "currentState": { 819 | "manifest": { 820 | "version": "", 821 | "id": "", 822 | "volumes": null, 823 | "containers": null, 824 | "restartPolicy": {} 825 | }, 826 | "status": "Succeeded", 827 | "Condition": [ 828 | { 829 | "kind": "Ready", 830 | "status": "None" 831 | } 832 | ], 833 | "host": "10.80.197.181", 834 | "hostIP": "10.80.197.181", 835 | "podIP": "10.0.70.5", 836 | "info": { 837 | "ubuntu1": { 838 | "state": { 839 | "termination": { 840 | "exitCode": 0, 841 | "startedAt": "2015-03-21T08:35:01Z", 842 | "finishedAt": "2015-03-21T13:39:05Z" 843 | } 844 | }, 845 | "ready": false, 846 | "restartCount": 0, 847 | "image": "tutum/ubuntu:trusty", 848 | "imageID": "docker://a1f9f1afcff779b7c1c0142fa70d3cc3205101ac9cb6f94c0e6f5c8d15ba6ea2", 849 | "containerID": "docker://1ad1a4dddef2976ef8472ef405792a91204d5f356b0b394868c39ffcf1fd5c10" 850 | } 851 | } 852 | } 853 | } 854 | ] 855 | } -------------------------------------------------------------------------------- /test/test-containerService.js: -------------------------------------------------------------------------------- 1 | /** 2 | * test kubernetes nodes 3 | * @author huangqg 4 | * @date 2015-03-18 5 | */ 6 | 7 | var should = require('should'); 8 | var assert = require('assert'); 9 | var Client = require('../'); 10 | var fs = require('fs'); 11 | 12 | describe('Test k8s nodes API', function() { 13 | this.timeout(5000); 14 | var client; 15 | var nodes = []; 16 | beforeEach(function() { 17 | client = new Client(require('./config.json').k8s); 18 | }); 19 | 20 | it('should create rc and service', function(done) { 21 | client.replicationControllers.create(require('./json/wordpress-controller.json'), function (err, rc) { 22 | if (!err) { 23 | console.log('rc: ' + JSON.stringify(rc)); 24 | client.services.create(require('./json/wordpress-service.json'), function(err, service) { 25 | if (!err) { 26 | console.log('service: ' + JSON.stringify(service)); 27 | done(); 28 | } else { 29 | console.log(err); 30 | assert(false); 31 | } 32 | }); 33 | } else { 34 | console.log(err); 35 | assert(false); 36 | } 37 | }); 38 | }); 39 | 40 | it('should update the replicationControllers', function(done) { 41 | client.replicationControllers.get(require('./json/wordpress-controller.json').metadata.name, function (err, rc1) { 42 | if (!err) { 43 | rc1.spec.replicas = 1; 44 | client.replicationControllers.update(require('./json/wordpress-controller.json').metadata.name, rc1, function (err, rc) { 45 | if (!err) { 46 | console.log('rc: ' + JSON.stringify(rc)); 47 | done(); 48 | } else { 49 | console.log(err); 50 | assert(false); 51 | } 52 | }); 53 | } else { 54 | console.log(err); 55 | assert(false); 56 | } 57 | }); 58 | }); 59 | 60 | it('should delete rc and service', function(done) { 61 | client.replicationControllers.delete(require('./json/wordpress-controller.json').metadata.name, function (err, rc) { 62 | if (!err) { 63 | console.log('rc: ' + JSON.stringify(rc)); 64 | client.services.delete(require('./json/wordpress-service.json').metadata.name, function(err, service) { 65 | if (!err) { 66 | console.log('service: ' + JSON.stringify(service)); 67 | done(); 68 | } else { 69 | console.log(err); 70 | assert(false); 71 | } 72 | }); 73 | } else { 74 | console.log(err); 75 | assert(false); 76 | } 77 | }); 78 | }); 79 | 80 | }); -------------------------------------------------------------------------------- /test/test-endpoints.js: -------------------------------------------------------------------------------- 1 | /** 2 | * test kubernetes minions 3 | * @author huangqg 4 | * @date 2015-03-18 5 | */ 6 | 7 | var should = require('should'); 8 | var assert = require('assert'); 9 | var Client = require('../'); 10 | var fs = require('fs'); 11 | 12 | describe('Test k8s endpoints API', function() { 13 | this.timeout(5000); 14 | var client; 15 | beforeEach(function() { 16 | client = new Client(require('./config.json').k8s); 17 | }); 18 | 19 | it('should return the endpoints list', function(done) { 20 | client.endpoints.get(function (err, endpoints) { 21 | if (!err) { 22 | console.log('endpoints: ' + JSON.stringify(endpoints)); 23 | assert(endpoints instanceof Array); 24 | // output results 25 | fs.writeFile("results/endpoints.json", JSON.stringify(endpoints, null, 4)); 26 | done(); 27 | } else { 28 | console.log(err); 29 | assert(false); 30 | } 31 | }); 32 | }); 33 | }); -------------------------------------------------------------------------------- /test/test-events.js: -------------------------------------------------------------------------------- 1 | /** 2 | * test kubernetes minions 3 | * @author huangqg 4 | * @date 2015-03-19 5 | */ 6 | 7 | var should = require('should'); 8 | var assert = require('assert'); 9 | var Client = require('../'); 10 | var fs = require('fs'); 11 | 12 | describe('Test k8s events API', function() { 13 | this.timeout(5000); 14 | var client; 15 | beforeEach(function() { 16 | client = new Client(require('./config.json').k8s); 17 | }); 18 | 19 | it('should return the events list', function(done) { 20 | client.events.get(function (err, events) { 21 | if (!err) { 22 | console.log('events: ' + JSON.stringify(events)); 23 | assert(events instanceof Array); 24 | // output results 25 | fs.writeFile("results/events.json", JSON.stringify(events, null, 4)); 26 | done(); 27 | } else { 28 | console.log(err); 29 | assert(false); 30 | } 31 | }); 32 | }); 33 | }); -------------------------------------------------------------------------------- /test/test-horizontalpodautoscaler.js: -------------------------------------------------------------------------------- 1 | var should = require('should'); 2 | var assert = require('assert'); 3 | var Client = require('../'); 4 | var fs = require('fs'); 5 | var testData = require('./json/horizontalpodautoscaler.json'); 6 | 7 | describe('Test k8s horizontalpodautoscaler API', function() { 8 | this.timeout(5000); 9 | var client, options; 10 | options = JSON.parse(JSON.stringify(require('./config.json').k8s)) 11 | options.version = "autoscaling/v1"; 12 | client = new Client(options); 13 | client.object = client.createCollection("horizontalpodautoscalers", null, null, { apiPrefix : "apis", namespaced: true }); 14 | 15 | it('should create an hpa', function(done) { 16 | client.object.create(testData, function (err, data) { 17 | assert(err == null, JSON.stringify(err)); 18 | data.should.be.an.instanceOf(Object).and.have.properties(['kind', 'apiVersion', 'metadata']); 19 | data.metadata.name.should.be.equal(testData.metadata.name); 20 | done(); 21 | }); 22 | }); 23 | 24 | it('should return the list of hpas', function(done) { 25 | client.object.get(function (err, data) { 26 | assert(err == null); 27 | data.should.be.an.instanceOf(Array) 28 | data[0].should.be.an.instanceOf(Object).and.have.properties(['kind', 'apiVersion', 'metadata', 'items']); 29 | data[0].items.should.be.an.instanceOf(Array) 30 | data[0].kind.should.be.equal('HorizontalPodAutoscalerList'); 31 | done(); 32 | }); 33 | }); 34 | 35 | it('should return the hpa with specified id', function(done) { 36 | var nsId = testData.metadata.name; 37 | client.object.get(nsId, function (err, data) { 38 | assert(err == null); 39 | data.should.be.an.instanceOf(Object).and.have.properties(['kind', 'apiVersion', 'metadata']); 40 | data.metadata.name.should.be.equal(testData.metadata.name); 41 | done(); 42 | }); 43 | }); 44 | 45 | it('should delete the hpa with specified id', function(done) { 46 | client.object.delete(testData.metadata.name, function (err, data) { 47 | assert(err == null); 48 | data.should.be.an.instanceOf(Object).and.have.properties(['kind', 'apiVersion', 'metadata', 'status']); 49 | data.status.should.be.equal('Success'); 50 | done(); 51 | }); 52 | }); 53 | 54 | }); -------------------------------------------------------------------------------- /test/test-namespaces.js: -------------------------------------------------------------------------------- 1 | /** 2 | * test kubernetes minions 3 | * @author huangqg 4 | * @date 2015-03-19 5 | */ 6 | 7 | var should = require('should'); 8 | var assert = require('assert'); 9 | var Client = require('../'); 10 | var fs = require('fs'); 11 | var testData = require('./json/namespace.json'); 12 | 13 | describe('Test k8s namespaces API', function() { 14 | this.timeout(5000); 15 | var client; 16 | beforeEach(function() { 17 | client = new Client(require('./config.json').k8s); 18 | }); 19 | 20 | it('should create a namespace', function(done) { 21 | client.namespaces.create(testData, function (err, data) { 22 | assert(err == null); 23 | data.should.be.an.instanceOf(Object).and.have.properties(['kind', 'apiVersion', 'metadata']); 24 | data.metadata.name.should.be.equal(testData.metadata.name); 25 | done(); 26 | }); 27 | }); 28 | 29 | it('should return the list of namespaces', function(done) { 30 | client.namespaces.get(function (err, data) { 31 | assert(err == null); 32 | data.should.be.an.instanceOf(Array) 33 | data[0].should.be.an.instanceOf(Object).and.have.properties(['kind', 'apiVersion', 'metadata', 'items']); 34 | data[0].items.should.be.an.instanceOf(Array) 35 | data[0].kind.should.be.equal('NamespaceList'); 36 | done(); 37 | }); 38 | }); 39 | 40 | it('should return the namespace with specified id', function(done) { 41 | var nsId = testData.metadata.name; 42 | client.namespaces.get(nsId, function (err, data) { 43 | assert(err == null); 44 | data.should.be.an.instanceOf(Object).and.have.properties(['kind', 'apiVersion', 'metadata']); 45 | data.metadata.name.should.be.equal(testData.metadata.name); 46 | done(); 47 | }); 48 | }); 49 | 50 | it('should delete the namespace with specified id', function(done) { 51 | client.namespaces.delete(testData.metadata.name, function (err, data) { 52 | assert(err == null); 53 | data.should.be.an.instanceOf(Object).and.have.properties(['kind', 'apiVersion', 'metadata']); 54 | data.metadata.name.should.be.equal(testData.metadata.name); 55 | done(); 56 | }); 57 | }); 58 | 59 | }); -------------------------------------------------------------------------------- /test/test-nodes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * test kubernetes minions 3 | * @author huangqg 4 | * @date 2015-03-19 5 | */ 6 | 7 | var should = require('should'); 8 | var assert = require('assert'); 9 | var Client = require('../'); 10 | var fs = require('fs'); 11 | 12 | describe('Test k8s nodes API', function() { 13 | this.timeout(5000); 14 | var client; 15 | var nodes = []; 16 | beforeEach(function() { 17 | client = new Client(require('./config.json').k8s); 18 | }); 19 | 20 | it('should return the list of nodes', function(done) { 21 | client.nodes.get(function (err, data) { 22 | assert(err == null); 23 | data.should.be.an.instanceOf(Array) 24 | data[0].should.be.an.instanceOf(Object).and.have.properties(['kind', 'apiVersion', 'metadata', 'items']); 25 | data[0].items.should.be.an.instanceOf(Array) 26 | data[0].kind.should.be.equal('NodeList'); 27 | nodes = data[0].items; 28 | done(); 29 | }); 30 | }); 31 | 32 | it('should return the nodes with specified id', function(done) { 33 | var nsId = nodes[0].metadata.name; 34 | client.nodes.get(nsId, function (err, data) { 35 | assert(err == null); 36 | data.should.be.an.instanceOf(Object).and.have.properties(['kind', 'apiVersion', 'metadata']); 37 | data.metadata.name.should.be.equal(nodes[0].metadata.name); 38 | done(); 39 | }); 40 | }); 41 | 42 | }); -------------------------------------------------------------------------------- /test/test-pods.js: -------------------------------------------------------------------------------- 1 | /** 2 | * test kubernetes minions 3 | * @author huangqg 4 | * @date 2015-03-19 5 | */ 6 | 7 | var should = require('should'); 8 | var assert = require('assert'); 9 | var Client = require('../'); 10 | var fs = require('fs'); 11 | var testData = require('./json/pod.json'); 12 | 13 | describe('Test k8s namespaces API', function() { 14 | this.timeout(5000); 15 | var client; 16 | beforeEach(function() { 17 | client = new Client(require('./config.json').k8s); 18 | }); 19 | 20 | it('should create a pod', function(done) { 21 | client.pods.create(testData, function (err, data) { 22 | assert(err == null, JSON.stringify(err)); 23 | data.should.be.an.instanceOf(Object).and.have.properties(['kind', 'apiVersion', 'metadata']); 24 | data.metadata.name.should.be.equal(testData.metadata.name); 25 | done(); 26 | }); 27 | }); 28 | 29 | it('should return the list of pods', function(done) { 30 | client.pods.get(function (err, data) { 31 | assert(err == null); 32 | data.should.be.an.instanceOf(Array) 33 | data[0].should.be.an.instanceOf(Object).and.have.properties(['kind', 'apiVersion', 'metadata', 'items']); 34 | data[0].items.should.be.an.instanceOf(Array) 35 | data[0].kind.should.be.equal('PodList'); 36 | done(); 37 | }); 38 | }); 39 | 40 | it('should return the pod with specified id', function(done) { 41 | var nsId = testData.metadata.name; 42 | client.pods.get(nsId, function (err, data) { 43 | assert(err == null); 44 | data.should.be.an.instanceOf(Object).and.have.properties(['kind', 'apiVersion', 'metadata']); 45 | data.metadata.name.should.be.equal(testData.metadata.name); 46 | done(); 47 | }); 48 | }); 49 | 50 | it('should delete the pod with specified id', function(done) { 51 | client.pods.delete(testData.metadata.name, function (err, data) { 52 | assert(err == null); 53 | data.should.be.an.instanceOf(Object).and.have.properties(['kind', 'apiVersion', 'metadata']); 54 | data.metadata.name.should.be.equal(testData.metadata.name); 55 | done(); 56 | }); 57 | }); 58 | 59 | }); -------------------------------------------------------------------------------- /test/test-services.js: -------------------------------------------------------------------------------- 1 | /** 2 | * test kubernetes minions 3 | * @author huangqg 4 | * @date 2015-03-19 5 | */ 6 | 7 | var should = require('should'); 8 | var assert = require('assert'); 9 | var Client = require('../'); 10 | var fs = require('fs'); 11 | var testData = require('./json/service.json'); 12 | 13 | describe('Test k8s services API', function() { 14 | this.timeout(5000); 15 | var client; 16 | beforeEach(function() { 17 | client = new Client(require('./config.json').k8s); 18 | }); 19 | 20 | it('should create a service', function(done) { 21 | client.services.create(testData, function (err, data) { 22 | assert(err == null, JSON.stringify(err)); 23 | data.should.be.an.instanceOf(Object).and.have.properties(['kind', 'apiVersion', 'metadata']); 24 | data.metadata.name.should.be.equal(testData.metadata.name); 25 | done(); 26 | }); 27 | }); 28 | 29 | it('should return the list of services', function(done) { 30 | client.services.get(function (err, data) { 31 | assert(err == null); 32 | data.should.be.an.instanceOf(Array) 33 | data[0].should.be.an.instanceOf(Object).and.have.properties(['kind', 'apiVersion', 'metadata', 'items']); 34 | data[0].items.should.be.an.instanceOf(Array) 35 | data[0].kind.should.be.equal('ServiceList'); 36 | done(); 37 | }); 38 | }); 39 | 40 | it('should return the service with specified id', function(done) { 41 | var nsId = testData.metadata.name; 42 | client.services.get(nsId, function (err, data) { 43 | assert(err == null); 44 | data.should.be.an.instanceOf(Object).and.have.properties(['kind', 'apiVersion', 'metadata']); 45 | data.metadata.name.should.be.equal(testData.metadata.name); 46 | done(); 47 | }); 48 | }); 49 | 50 | it('should delete the service with specified id', function(done) { 51 | client.services.delete(testData.metadata.name, function (err, data) { 52 | assert(err == null); 53 | data.should.be.an.instanceOf(Object).and.have.properties(['kind', 'apiVersion', 'metadata', 'status']); 54 | data.status.should.be.equal('Success'); 55 | done(); 56 | }); 57 | }); 58 | 59 | }); -------------------------------------------------------------------------------- /test/test-watch-pods.js: -------------------------------------------------------------------------------- 1 | /** 2 | * test kubernetes minions 3 | * @author huangqg 4 | * @date 2015-03-18 5 | */ 6 | 7 | //var should = require('should'); 8 | //var assert = require('assert'); 9 | //var Client = require('../'); 10 | //var fs = require('fs'); 11 | // 12 | //describe('Test k8s watch pods API', function() { 13 | // this.timeout(20000); 14 | // var client; 15 | // beforeEach(function() { 16 | // client = new Client(require('./config.json').k8s); 17 | // }); 18 | // 19 | // it('should return the watch pods list', function(done) { 20 | // client.watchPods.get(function (err, pods) { 21 | // if (!err) { 22 | // console.log('pods: ' + JSON.stringify(pods)); 23 | // // output results 24 | // fs.writeFile("results/watchPods.json", JSON.stringify(pods, null, 4)); 25 | // assert(pods instanceof Array); 26 | // done(); 27 | // } else { 28 | // console.log(err); 29 | // assert(false); 30 | // } 31 | // }); 32 | // }); 33 | //}); --------------------------------------------------------------------------------