commit c47137ec4aa7094338cb293a4f1c6a9c47845f56 Author: MiaoWoo Date: Wed Jul 3 15:50:59 2019 +0800 Init: Create & Init dayu Project... Signed-off-by: MiaoWoo diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..35ce849 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +package-lock.json +yarn.lock diff --git a/http/dayu.http b/http/dayu.http new file mode 100644 index 0000000..39cb7da --- /dev/null +++ b/http/dayu.http @@ -0,0 +1,38 @@ +@url=https://faas.n.yumc.pw + +### System +##### Info +GET {{url}}/info + +##### Version +GET {{url}}/version + +##### Events +GET {{url}}/events + +### Swarm +@swarm={{url}}/swarm + +##### Swarm Info +GET {{swarm}}/info + +### Swarm Init +POST {{swarm}}/init + +{} + +### Stack +@stack={{url}}/stack +##### Stack List +GET {{stack}}/list + +##### Stack Remove +POST {{stack}}/develop/remove + + +### Container +@container={{url}}/container + +##### Container logs +@id=6365041d2e52c5896dd14a5450920e482dddc33e6addaa07fab413bcda78d723 +GET {{container}}/{{id}}/logs \ No newline at end of file diff --git a/http/docker.http b/http/docker.http new file mode 100644 index 0000000..f2a4805 --- /dev/null +++ b/http/docker.http @@ -0,0 +1,36 @@ +@url=https://dcli.yumc.pw/v1.39 + +### Info +GET {{url}}/info + +### Version +GET {{url}}/version + +### Swarm +GET {{url}}/swarm + +### Swarm Init +POST {{url}}/swarm/init + +{} + +### Node +GET {{url}}/nodes + +### Container +@cid=87514be54f3b9a34ac8ea74097d2053d73c7c535f34d3446bcbe16d6f2ba0f0f +@container={{url}}/containers +##### List +GET {{container}}/json?filters={"label":{"com.docker.stack.namespace%3Dmonitor":false}} +##### In +GET {{container}}/{{cid}}/json + +### Services +GET {{url}}/services?filters={"mode":["global"]} + +### Create Services +POST {{url}}/services/create + +{ + +} \ No newline at end of file diff --git a/lerna.json b/lerna.json new file mode 100644 index 0000000..8ee7a25 --- /dev/null +++ b/lerna.json @@ -0,0 +1,27 @@ +{ + "npmClient": "yarn", + "useWorkspaces": true, + "version": "0.3.3", + "changelog": { + "labels": { + "tag: new feature": ":rocket: New Feature", + "tag: breaking change": ":boom: Breaking Change", + "tag: bug fix": ":bug: Bug Fix", + "tag: enhancement": ":nail_care: Enhancement", + "tag: documentation": ":memo: Documentation", + "tag: internal": ":house: Internal" + }, + "cacheDir": ".changelog" + }, + "packages": [ + "packages/*" + ], + "command": { + "run": { + "stream": true + }, + "publish": { + "registry": "https://repo.yumc.pw/repository/npm-hosted/" + } + } +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..a7f565f --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "private": true, + "name": "dayu", + "version": "1.0.0", + "main": "index.js", + "author": "MiaoWoo ", + "license": "MIT", + "scripts": { + "clean": "npx lerna run clean", + "watch": "npx lerna run watch --concurrency 10", + "build": "npx lerna run build", + "lp": "npx lerna publish" + }, + "workspaces": [ + "packages/*" + ], + "devDependencies": { + "lerna": "^3.14.1" + } +} \ No newline at end of file diff --git a/packages/common/.gitignore b/packages/common/.gitignore new file mode 100644 index 0000000..d856d44 --- /dev/null +++ b/packages/common/.gitignore @@ -0,0 +1,4 @@ +/node_modules +/dist +/package-lock.json +/yarn.lock diff --git a/packages/common/.npmignore b/packages/common/.npmignore new file mode 100644 index 0000000..b0eede3 --- /dev/null +++ b/packages/common/.npmignore @@ -0,0 +1,22 @@ +src +test +typings +bundled +build +coverage +docs +wiki +gulpfile.js +bower.json +karma.conf.js +tsconfig.json +typings.json +CONTRIBUTING.md +ISSUE_TEMPLATE.md +PULL_REQUEST_TEMPLATE.md +tslint.json +wallaby.js +.travis.yml +.gitignore +.vscode +type_definitions \ No newline at end of file diff --git a/packages/common/README.md b/packages/common/README.md new file mode 100644 index 0000000..74cc502 --- /dev/null +++ b/packages/common/README.md @@ -0,0 +1,11 @@ +# `@dayu/common` + +> TODO: description + +## Usage + +``` +const core = require('@dayu/common'); + +// TODO: DEMONSTRATE API +``` diff --git a/packages/common/package.json b/packages/common/package.json new file mode 100644 index 0000000..597b1da --- /dev/null +++ b/packages/common/package.json @@ -0,0 +1,27 @@ +{ + "name": "@dayu/common", + "version": "0.0.1", + "description": "> TODO: description", + "author": "MiaoWoo ", + "homepage": "https://github.com/circlecloud/dayu", + "license": "ISC", + "main": "dist/index.js", + "publishConfig": { + "registry": "https://repo.yumc.pw/repository/npm-hosted/" + }, + "scripts": { + "dev": "npx ts-node-dev src/index.ts", + "clean": "rimraf dist", + "watch": "npx tsc --watch", + "build": "yarn clean && npx tsc", + "test": "echo \"Error: run tests from root\" && exit 1" + }, + "dependencies": { + "axios": "^0.19.0" + }, + "devDependencies": { + "rimraf": "^2.6.3", + "ts-node-dev": "^1.0.0-pre.40", + "typescript": "^3.5.2" + } +} \ No newline at end of file diff --git a/packages/common/public/index.html b/packages/common/public/index.html new file mode 100644 index 0000000..3f9b35d --- /dev/null +++ b/packages/common/public/index.html @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/packages/common/public/js/index.js b/packages/common/public/js/index.js new file mode 100644 index 0000000..b2e56d6 --- /dev/null +++ b/packages/common/public/js/index.js @@ -0,0 +1,68 @@ +let command = ''; +Terminal.applyAddon(fit); +Terminal.applyAddon(attach); +Terminal.applyAddon(fullscreen); +var term = new Terminal({ + experimentalCharAtlas: 'dynamic', + cursorBlink: false, +}); +term.open(document.getElementById('terminal')); +term.toggleFullScreen(); +term.fit(); + +window.onresize = function() { + term.fit(); +} + +let query = {} +location.search.substring(1).split("&").forEach(q => { + let qy = q.split("=", 2); + query[qy[0]] = qy[1] +}) + +console.log(query); + +var socket = io('/container', { + path: '/ws', + transports: ['websocket'] +}); +socket.on('connect', () => { + term.writeln('connect') + if (query.action) { + term.writeln(`Recover Action: ${query.action} Data: ${query.data}`) + switch (query.action) { + case "container": + socket.emit('logs', { + id: query.data, + since: Date.now() / 1000 - 60 * 15, + until: Date.now() / 1000, + stderr: false, + tail: "all" + }) + break; + default: + } + } +}); +term.on('data', async data => { + if (data == '\t') { + return; + } + term.write(data); + if (data == '\r') { + term.write('\n'); + socket.emit('logs', { + id: command + }) + command = ''; + } else { + command += data; + } +}); +socket.on('message', data => { + term.write(data.toString() + '\r\n'); +}); +socket.on('disconnect', () => { + term.reset(); + term.writeln('disconnect'); +}); diff --git a/packages/common/src/api.ts b/packages/common/src/api.ts new file mode 100644 index 0000000..a589b7e --- /dev/null +++ b/packages/common/src/api.ts @@ -0,0 +1,68 @@ +import * as http from 'http' +import axios, { AxiosResponse, AxiosRequestConfig, Method, AxiosInstance } from 'axios' + +class HttpClient { + private api: AxiosInstance; + constructor() { + const instanceConfig: AxiosRequestConfig = { + headers: { + 'Content-Type': 'application/json' + }, + timeout: 5000 + } + if (process.env.DOCKER_HOST.startsWith("/")) { + instanceConfig.socketPath = process.env.DOCKER_HOST + } else { + instanceConfig.baseURL = process.env.DOCKER_HOST + } + this.api = axios.create(instanceConfig) + } + async get(path: string, data?: object): Promise { + return await this.handle("GET", path, { params: data }); + } + async post(path: string, data?: object): Promise { + return await this.handle("POST", path, { data }); + } + async stream(path: string, data?: object): Promise { + return await this.handle("GET", path, { params: data, responseType: "stream" }); + } + async handle(method: Method, path: string, reqConfig?: AxiosRequestConfig): Promise { + let config: AxiosRequestConfig = { + method, + url: path, + }; + let startTime = Date.now(); + Object.assign(config, reqConfig) + let response: AxiosResponse; + try { + response = await this.api.request(config); + return response.data as T + } catch (ex) { + if (!ex.response) { throw ex; } + response = ex.response; + if (this.isStream(response)) { + let stream = response.data; + response.data = await new Promise((resolve, reject) => { + let cache = ''; + stream.on('data', (chunk: ArrayBuffer) => { cache += chunk.toString() }) + stream.on('end', () => { resolve(JSON.parse(cache) as T); }) + }) + } + throw new Error(JSON.stringify(response.data)); + } finally { + if (response) { + console.log(`============== API Invoke ============== +REQUEST METHOD : ${method} +REQUEST PATH : ${axios.getUri(config)} +REQUEST PARAMS : ${JSON.stringify(config.params || {})} +REQUEST BODY : ${JSON.stringify(config.data || {})} +RESPONSE BODY : ${this.isStream(response) ? '' : JSON.stringify(response.data)} +HANDLE TIME : ${Date.now() - startTime}ms +========================================`); + } + } + } + isStream(response: AxiosResponse) { + return toString.call(response.data.pipe) === "[object Function]"; + } +} diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts new file mode 100644 index 0000000..3318fdb --- /dev/null +++ b/packages/common/src/index.ts @@ -0,0 +1 @@ +export * from './api' diff --git a/packages/common/tsconfig.json b/packages/common/tsconfig.json new file mode 100644 index 0000000..7aae5d2 --- /dev/null +++ b/packages/common/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": "src", + "outDir": "dist" + } +} \ No newline at end of file diff --git a/packages/core/.gitignore b/packages/core/.gitignore new file mode 100644 index 0000000..d856d44 --- /dev/null +++ b/packages/core/.gitignore @@ -0,0 +1,4 @@ +/node_modules +/dist +/package-lock.json +/yarn.lock diff --git a/packages/core/.npmignore b/packages/core/.npmignore new file mode 100644 index 0000000..b0eede3 --- /dev/null +++ b/packages/core/.npmignore @@ -0,0 +1,22 @@ +src +test +typings +bundled +build +coverage +docs +wiki +gulpfile.js +bower.json +karma.conf.js +tsconfig.json +typings.json +CONTRIBUTING.md +ISSUE_TEMPLATE.md +PULL_REQUEST_TEMPLATE.md +tslint.json +wallaby.js +.travis.yml +.gitignore +.vscode +type_definitions \ No newline at end of file diff --git a/packages/core/README.md b/packages/core/README.md new file mode 100644 index 0000000..9c056dd --- /dev/null +++ b/packages/core/README.md @@ -0,0 +1,11 @@ +# `@dayu/core` + +> TODO: description + +## Usage + +``` +const core = require('@dayu/core'); + +// TODO: DEMONSTRATE API +``` diff --git a/packages/core/package.json b/packages/core/package.json new file mode 100644 index 0000000..75fc2ab --- /dev/null +++ b/packages/core/package.json @@ -0,0 +1,33 @@ +{ + "name": "@dayu/core", + "version": "0.0.1", + "description": "> TODO: description", + "author": "MiaoWoo ", + "homepage": "https://github.com/circlecloud/dayu", + "license": "ISC", + "main": "dist/index.js", + "publishConfig": { + "registry": "https://repo.yumc.pw/repository/npm-hosted/" + }, + "scripts": { + "dev": "npx ts-node-dev --respawn --prefer-ts --debounce=1500 --ignore-watch=[] --project tsconfig.json", + "debug": "npx nodemon -V --watch ../../node_modules --delay 1500ms dist/index.js", + "clean": "rimraf dist", + "watch": "npx tsc --watch", + "build": "yarn clean && npx tsc", + "test": "echo \"Error: run tests from root\" && exit 1" + }, + "dependencies": { + "@cc-server/core": "^0.6.1", + "@cc-server/ws": "^0.6.1", + "@dayu/docker-api": "^0.0.1", + "axios": "^0.19.0" + }, + "devDependencies": { + "@types/express": "^4.17.0", + "@types/socket.io": "^2.1.2", + "rimraf": "^2.6.3", + "ts-node-dev": "^1.0.0-pre.40", + "typescript": "^3.5.2" + } +} \ No newline at end of file diff --git a/packages/core/public/index.html b/packages/core/public/index.html new file mode 100644 index 0000000..3f9b35d --- /dev/null +++ b/packages/core/public/index.html @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/packages/core/public/js/index.js b/packages/core/public/js/index.js new file mode 100644 index 0000000..b2e56d6 --- /dev/null +++ b/packages/core/public/js/index.js @@ -0,0 +1,68 @@ +let command = ''; +Terminal.applyAddon(fit); +Terminal.applyAddon(attach); +Terminal.applyAddon(fullscreen); +var term = new Terminal({ + experimentalCharAtlas: 'dynamic', + cursorBlink: false, +}); +term.open(document.getElementById('terminal')); +term.toggleFullScreen(); +term.fit(); + +window.onresize = function() { + term.fit(); +} + +let query = {} +location.search.substring(1).split("&").forEach(q => { + let qy = q.split("=", 2); + query[qy[0]] = qy[1] +}) + +console.log(query); + +var socket = io('/container', { + path: '/ws', + transports: ['websocket'] +}); +socket.on('connect', () => { + term.writeln('connect') + if (query.action) { + term.writeln(`Recover Action: ${query.action} Data: ${query.data}`) + switch (query.action) { + case "container": + socket.emit('logs', { + id: query.data, + since: Date.now() / 1000 - 60 * 15, + until: Date.now() / 1000, + stderr: false, + tail: "all" + }) + break; + default: + } + } +}); +term.on('data', async data => { + if (data == '\t') { + return; + } + term.write(data); + if (data == '\r') { + term.write('\n'); + socket.emit('logs', { + id: command + }) + command = ''; + } else { + command += data; + } +}); +socket.on('message', data => { + term.write(data.toString() + '\r\n'); +}); +socket.on('disconnect', () => { + term.reset(); + term.writeln('disconnect'); +}); diff --git a/packages/core/src/controller/container.ts b/packages/core/src/controller/container.ts new file mode 100644 index 0000000..69e08ce --- /dev/null +++ b/packages/core/src/controller/container.ts @@ -0,0 +1,36 @@ +import * as docker from '@dayu/docker-api' +import { io, interfaces, namespace, listener, Message } from '@cc-server/ws' +import { controller, httpGet, requestParam } from '@cc-server/binding' + +@controller('/container') +class ContainerController { + @httpGet('/list') + public async list() { + return await docker.container.list({ + filters: JSON.stringify({ + + }) + }); + } + @httpGet('/:id') + public async info(@requestParam('id') id: string) { + return await docker.container; + } +} + +@namespace("/container") +class ContainerNamespace extends interfaces.Namespace { + @listener() + async logs(socket: io.Socket, data: any) { + try { + let stream = await docker.container.logs(data.id, data); + this.defer(socket, () => stream.connection.destroy()); + stream.on('data', (chunk: ArrayBuffer) => { + let log = Buffer.from(chunk.slice(8, chunk.byteLength - 1)).toString(); + socket.send(log); + }) + } catch (ex) { + return new Message(ex.message); + } + } +} diff --git a/packages/core/src/controller/node.ts b/packages/core/src/controller/node.ts new file mode 100644 index 0000000..b311506 --- /dev/null +++ b/packages/core/src/controller/node.ts @@ -0,0 +1,10 @@ +import { controller, httpGet, httpPost } from 'inversify-express-utils'; +import * as docker from '@dayu/docker-api' + +@controller('/node') +class NodeController { + @httpGet('/list') + public async list() { + return await docker.node.list(); + } +} diff --git a/packages/core/src/controller/stack.ts b/packages/core/src/controller/stack.ts new file mode 100644 index 0000000..ab8aa38 --- /dev/null +++ b/packages/core/src/controller/stack.ts @@ -0,0 +1,41 @@ +import { controller, httpGet, httpPost, requestParam } from 'inversify-express-utils'; +import * as docker from '@dayu/docker-api' + +const STACK_LABEL = 'com.docker.stack.namespace'; + +@controller('/stack') +class StackController { + @httpGet('/list') + public async list() { + let stacks: { [key: string]: string[] } = {}; + let services = await docker.service.list(); + for (const service of services) { + let stackName = service.Spec.Labels[STACK_LABEL] + if (stackName) { + let stack = stacks[stackName] || []; + stack.push(service.Spec.Name); + stacks[stackName] = stack; + } + } + return stacks; + } + + @httpGet('/:stack') + public async details(@requestParam('stack') stack: string) { + let filter: any = {} + filter[`${STACK_LABEL}=${stack}`] = true + let filterOpt = { + filters: JSON.stringify({ + "label": filter + }) + } + let services = await docker.service.list(filterOpt) + let networks = await docker.network.list(filterOpt) + let containers = await docker.container.list(filterOpt); + return { + services: services.map(service => service.Spec.Name), + networks: networks.map(network => network.Name), + containers: containers.map(container => container.Names[0].substring(1)) + } + } +} diff --git a/packages/core/src/controller/swarm.ts b/packages/core/src/controller/swarm.ts new file mode 100644 index 0000000..7e95105 --- /dev/null +++ b/packages/core/src/controller/swarm.ts @@ -0,0 +1,24 @@ +import { controller, httpGet, httpPost } from 'inversify-express-utils'; +import * as docker from '@dayu/docker-api' + +@controller('/swarm') +class SwarmController { + @httpGet('/info') + public async version() { + return await docker.swarm.inspect(); + } + @httpPost('/init') + public async info() { + let info = await docker.swarm.init({ + ListenAddr: "0.0.0.0:2377", + AdvertiseAddr: "192.168.0.3:2377", + // DefaultAddrPool: [ + // "10.10.0.0/8", + // "20.20.0.0/8" + // ], + // ForceNewCluster: false, + // SubnetSize: 24 + }) + return info; + } +} \ No newline at end of file diff --git a/packages/core/src/controller/system.ts b/packages/core/src/controller/system.ts new file mode 100644 index 0000000..bcb22ec --- /dev/null +++ b/packages/core/src/controller/system.ts @@ -0,0 +1,22 @@ +import { controller, httpGet, httpPost } from 'inversify-express-utils'; +import * as docker from '@dayu/docker-api' + +@controller('') +class SystemController { + @httpGet('/version') + public async version() { + return await docker.system.version() + } + @httpGet('/info') + public async info() { + let info = await docker.system.info() + return info; + } + @httpGet('/events') + public async event() { + await docker.system.events((event) => { + if (!event) { return } + console.log(event) + }) + } +} \ No newline at end of file diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts new file mode 100644 index 0000000..0288df4 --- /dev/null +++ b/packages/core/src/index.ts @@ -0,0 +1,21 @@ +import { CcServerBoot } from '@cc-server/core' +import * as fs from 'fs' +import * as path from 'path' + +//process.env.DOCKER_HOST = 'https://ndcli.yumc.pw' +process.env.DOCKER_HOST = '/var/run/docker.sock' + +let server = new CcServerBoot(); + +let modulesDir = path.join(__dirname, 'controller') +let list = fs.readdirSync(modulesDir); + +for (let file of list) { + let moduleDir = path.join(modulesDir, file) + let stat = fs.statSync(moduleDir); + if (stat.isFile() && file.endsWith('.js')) { + require(moduleDir); + } +} + +server.static().build().start(); diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json new file mode 100644 index 0000000..7aae5d2 --- /dev/null +++ b/packages/core/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": "src", + "outDir": "dist" + } +} \ No newline at end of file diff --git a/packages/docker-api/.gitignore b/packages/docker-api/.gitignore new file mode 100644 index 0000000..d856d44 --- /dev/null +++ b/packages/docker-api/.gitignore @@ -0,0 +1,4 @@ +/node_modules +/dist +/package-lock.json +/yarn.lock diff --git a/packages/docker-api/.npmignore b/packages/docker-api/.npmignore new file mode 100644 index 0000000..b0eede3 --- /dev/null +++ b/packages/docker-api/.npmignore @@ -0,0 +1,22 @@ +src +test +typings +bundled +build +coverage +docs +wiki +gulpfile.js +bower.json +karma.conf.js +tsconfig.json +typings.json +CONTRIBUTING.md +ISSUE_TEMPLATE.md +PULL_REQUEST_TEMPLATE.md +tslint.json +wallaby.js +.travis.yml +.gitignore +.vscode +type_definitions \ No newline at end of file diff --git a/packages/docker-api/README.md b/packages/docker-api/README.md new file mode 100644 index 0000000..9c056dd --- /dev/null +++ b/packages/docker-api/README.md @@ -0,0 +1,11 @@ +# `@dayu/core` + +> TODO: description + +## Usage + +``` +const core = require('@dayu/core'); + +// TODO: DEMONSTRATE API +``` diff --git a/packages/docker-api/package.json b/packages/docker-api/package.json new file mode 100644 index 0000000..30c0946 --- /dev/null +++ b/packages/docker-api/package.json @@ -0,0 +1,29 @@ +{ + "name": "@dayu/docker-api", + "version": "0.0.1", + "description": "> TODO: description", + "author": "MiaoWoo ", + "homepage": "https://github.com/circlecloud/dayu", + "license": "ISC", + "main": "dist/index.js", + "publishConfig": { + "registry": "https://repo.yumc.pw/repository/npm-hosted/" + }, + "scripts": { + "dev": "npx ts-node-dev src/index.ts", + "clean": "rimraf dist", + "watch": "npx tsc --watch", + "build": "yarn clean && npx tsc", + "test": "echo \"Error: run tests from root\" && exit 1" + }, + "dependencies": { + "@cc-server/core": "^0.3.3", + "axios": "^0.19.0" + }, + "devDependencies": { + "@types/node": "^12.0.10", + "ts-node-dev": "^1.0.0-pre.40", + "typescript": "^3.5.2", + "rimraf": "^2.6.3" + } +} \ No newline at end of file diff --git a/packages/docker-api/src/api/common.ts b/packages/docker-api/src/api/common.ts new file mode 100644 index 0000000..357b490 --- /dev/null +++ b/packages/docker-api/src/api/common.ts @@ -0,0 +1,11 @@ +export interface ObjectMap { + [key: string]: {}; +} +export interface StringMap { + [key: string]: string; +} +export type PortSet = ObjectMap +export type Options = StringMap +export type Config = StringMap +export type Labels = StringMap +export type Ports = StringMap | ObjectMap diff --git a/packages/docker-api/src/api/opts/common.ts b/packages/docker-api/src/api/opts/common.ts new file mode 100644 index 0000000..3def63d --- /dev/null +++ b/packages/docker-api/src/api/opts/common.ts @@ -0,0 +1,10 @@ +export declare namespace query { + export interface FilterOpt { + filters?: string; + } + export interface LabelOpt { + [key: string]: { + [key: string]: boolean + } + } +} \ No newline at end of file diff --git a/packages/docker-api/src/api/opts/container.ts b/packages/docker-api/src/api/opts/container.ts new file mode 100644 index 0000000..2bee3ec --- /dev/null +++ b/packages/docker-api/src/api/opts/container.ts @@ -0,0 +1,18 @@ +import * as common from './common' + +export declare namespace container { + export interface ListOpts extends common.query.FilterOpt { + all?: boolean; + limit?: number; + size?: boolean; + } + export interface LogsOpts { + follow?: boolean; + stdout?: boolean; + stderr?: boolean; + since?: number; + until?: number; + timestamps?: boolean; + tail?: number | "all"; + } +} \ No newline at end of file diff --git a/packages/docker-api/src/api/opts/index.ts b/packages/docker-api/src/api/opts/index.ts new file mode 100644 index 0000000..2ec8b76 --- /dev/null +++ b/packages/docker-api/src/api/opts/index.ts @@ -0,0 +1,5 @@ +export * from './swarm' +export * from './common' +export * from './network' +export * from './service' +export * from './container' diff --git a/packages/docker-api/src/api/opts/network.ts b/packages/docker-api/src/api/opts/network.ts new file mode 100644 index 0000000..fa8eedc --- /dev/null +++ b/packages/docker-api/src/api/opts/network.ts @@ -0,0 +1,6 @@ +import * as common from './common' + +export declare namespace network { + export interface ListOpts extends common.query.FilterOpt { + } +} \ No newline at end of file diff --git a/packages/docker-api/src/api/opts/service.ts b/packages/docker-api/src/api/opts/service.ts new file mode 100644 index 0000000..ca19846 --- /dev/null +++ b/packages/docker-api/src/api/opts/service.ts @@ -0,0 +1,6 @@ +import * as common from './common' + +export declare namespace service { + export interface ListOpts extends common.query.FilterOpt { + } +} \ No newline at end of file diff --git a/packages/docker-api/src/api/opts/swarm.ts b/packages/docker-api/src/api/opts/swarm.ts new file mode 100644 index 0000000..e1f0250 --- /dev/null +++ b/packages/docker-api/src/api/opts/swarm.ts @@ -0,0 +1,59 @@ +import { Labels } from '../common' + +export declare namespace swarm { + type NodeAvailability = string; + + export interface InitOpts { + ListenAddr?: string; + AdvertiseAddr?: string; + DefaultAddrPool?: string[]; + DataPathAddr?: string; + DataPathPort?: number; + SubnetSize?: number; + ForceNewCluster?: boolean; + Availability?: NodeAvailability; + Spec?: Spec; + } + + export interface Spec { + Name?: string; + Labels?: Labels; + Orchestration?: Orchestration; + Raft?: Raft; + Dispatcher?: Dispatcher; + CAConfig?: CAConfig; + TaskDefaults?: TaskDefaults; + EncryptionConfig?: EncryptionConfig; + } + + export interface Version { + Index: number; + } + + export interface Orchestration { + TaskHistoryRetentionLimit?: number; + } + + export interface Raft { + SnapshotInterval?: number; + KeepOldSnapshots?: number; + LogEntriesForSlowFollowers?: number; + ElectionTick?: number; + HeartbeatTick?: number; + } + + export interface Dispatcher { + HeartbeatPeriod?: number; + } + + export interface CAConfig { + NodeCertExpiry?: number; + } + + export interface TaskDefaults { + } + + export interface EncryptionConfig { + AutoLockManagers?: boolean; + } +} \ No newline at end of file diff --git a/packages/docker-api/src/api/types/container.ts b/packages/docker-api/src/api/types/container.ts new file mode 100644 index 0000000..eea46dd --- /dev/null +++ b/packages/docker-api/src/api/types/container.ts @@ -0,0 +1,300 @@ +import { Labels, Config as CommonConfig, Ports, Options, StringMap, ObjectMap } from '../common' + +export declare namespace container { + export interface ContainerState { + Status: string; + Running: boolean; + Paused: boolean; + Restarting: boolean; + OOMKilled: boolean; + Dead: boolean; + Pid: number; + ExitCode: number; + Error: string; + StartedAt: string; + FinishedAt: Date; + } + + export interface LogConfig { + Type: string; + Config: CommonConfig; + } + + export interface PortBindings { + } + + export interface RestartPolicy { + Name: string; + MaximumRetryCount: number; + } + + // WeightDevice is a structure that holds device:weight pair + export interface WeightDevice { + Path: string; + Weight: number; + } + // ThrottleDevice is a structure that holds device:rate_per_second pair + export interface ThrottleDevice { + Path: string; + Rate: number; + } + // DeviceMapping represents the device mapping between the host and the container. + export interface DeviceMapping { + PathOnHost: string; + PathInContainer: string; + CgroupPermissions: string; + } + // DeviceRequest represents a request for devices from a device driver. + // Used by GPU device drivers. + export interface DeviceRequest { + Driver: string // Name of device driver + Count: number // Number of devices to request (-1 = All) + DeviceIDs: string[] // List of device IDs as recognizable by the device driver + Capabilities: string[][] // An OR list of AND lists of device capabilities (e.g. "gpu") + Options: StringMap // Options to pass onto the device driver + } + export interface Resources { + CpuShares?: number; + Memory?: number; + NanoCpus?: number; + CgroupParent?: string; + BlkioWeight?: number; + BlkioWeightDevice?: WeightDevice[]; + BlkioDeviceReadBps?: ThrottleDevice[]; + BlkioDeviceWriteBps?: ThrottleDevice[]; + BlkioDeviceReadIOps?: ThrottleDevice[]; + BlkioDeviceWriteIOps?: ThrottleDevice[]; + CpuPeriod?: number; + CpuQuota?: number; + CpuRealtimePeriod?: number; + CpuRealtimeRuntime?: number; + CpusetCpus?: string; + CpusetMems?: string; + Devices?: DeviceMapping[]; + DeviceCgroupRules?: string[]; + DeviceRequests: DeviceRequest[]; + DiskQuota?: number; + KernelMemory?: number; + MemoryReservation?: number; + MemorySwap?: number; + MemorySwappiness?: any; + OomKillDisable?: boolean; + PidsLimit?: number; + Ulimits?: any; + CpuCount?: number; + CpuPercent?: number; + IOMaximumIOps?: number; + IOMaximumBandwidth?: number; + } + + export interface HostConfig extends Resources { + Binds?: any; + ContainerIDFile: string; + LogConfig: LogConfig; + NetworkMode: string; + PortBindings: PortBindings; + RestartPolicy: RestartPolicy; + AutoRemove: boolean; + VolumeDriver: string; + VolumesFrom?: any; + CapAdd?: any; + CapDrop?: any; + Dns?: any; + DnsOptions?: any; + DnsSearch?: any; + ExtraHosts?: any; + GroupAdd?: any; + IpcMode: string; + Cgroup: string; + Links?: any; + OomScoreAdj: number; + PidMode: string; + Privileged: boolean; + PublishAllPorts: boolean; + ReadonlyRootfs: boolean; + SecurityOpt?: any; + UTSMode: string; + UsernsMode: string; + ShmSize: number; + Runtime: string; + ConsoleSize: number[]; + Isolation: string; + Mounts: Mount[]; + MaskedPaths: string[]; + ReadonlyPaths: string[]; + } + + export interface Data { + LowerDir: string; + MergedDir: string; + UpperDir: string; + WorkDir: string; + } + + export interface GraphDriver { + Data: Data; + Name: string; + } + + export type ExposedPorts = Ports + + // HealthConfig holds configuration settings for the HEALTHCHECK feature. + export interface HealthConfig { + // Test is the test to perform to check that the container is healthy. + // An empty slice means to inherit the default. + // The options are: + // {} : inherit healthcheck + // {"NONE"} : disable healthcheck + // {"CMD", args...} : exec arguments directly + // {"CMD-SHELL", command} : run command with system's default shell + Test: string[]; + + // Zero means to inherit. Durations are expressed as integer nanoseconds. + Interval: number; // Interval is the time to wait between checks. + Timeout: number; // Timeout is the time to wait before considering the check to have hung. + StartPeriod: number; // The start period for the container to initialize before the retries starts to count down. + + // Retries is the number of consecutive failures needed to consider a container as unhealthy. + // Zero means inherit. + Retries: number; + } + + export interface Config { + Hostname: string; + Domainname: string; + User: string; + AttachStdin: boolean; + AttachStdout: boolean; + AttachStderr: boolean; + ExposedPorts: ExposedPorts; + Tty: boolean; + OpenStdin: boolean; + StdinOnce: boolean; + Env: string[]; + Cmd?: any; + Healthcheck?: HealthConfig; + ArgsEscaped: boolean; + Image: string; + Volumes?: ObjectMap; + WorkingDir: string; + Entrypoint: string[]; + NetworkDisabled: boolean; + MacAddress: string; + OnBuild?: any; + Labels: Labels; + StopSignal: string; + StopTimeout: number; + Shell: string[]; + } + + export interface IPAMConfig { + IPv4Address: string; + } + + export interface Networks { + [key: string]: Network + } + + export interface NetworkSettings { + Bridge: string; + SandboxID: string; + HairpinMode: boolean; + LinkLocalIPv6Address: string; + LinkLocalIPv6PrefixLen: number; + Ports: Ports; + SandboxKey: string; + SecondaryIPAddresses?: any; + SecondaryIPv6Addresses?: any; + EndpointID: string; + Gateway: string; + GlobalIPv6Address: string; + GlobalIPv6PrefixLen: number; + IPAddress: string; + IPPrefixLen: number; + IPv6Gateway: string; + MacAddress: string; + Networks: Networks; + } + + export interface ContainerJSON { + Id: string; + Created: string; + Path: string; + Args: string[]; + State: ContainerState; + Image: string; + ResolvConfPath: string; + HostnamePath: string; + HostsPath: string; + LogPath: string; + Name: string; + RestartCount: number; + Driver: string; + Platform: string; + MountLabel: string; + ProcessLabel: string; + AppArmorProfile: string; + ExecIDs?: any; + HostConfig: HostConfig; + GraphDriver: GraphDriver; + Mounts: Mount[]; + Config: Config; + NetworkSettings: NetworkSettings; + } + + export interface Port { + IP: string; + PrivatePort: number; + PublicPort: number; + Type: string; + } + + export interface Network { + IPAMConfig?: IPAMConfig; + Links?: any; + Aliases?: any; + NetworkID: string; + EndpointID: string; + Gateway: string; + IPAddress: string; + IPPrefixLen: number; + IPv6Gateway: string; + GlobalIPv6Address: string; + GlobalIPv6PrefixLen: number; + MacAddress: string; + DriverOpts?: any; + } + + export interface SummaryNetworkSettings { + Networks: Networks; + } + + export interface Mount { + Type: string; + Name: string; + Source: string; + Destination: string; + Driver: string; + Mode: string; + RW: boolean; + Propagation: string; + } + + export interface Container { + Id: string; + Names: string[]; + Image: string; + ImageID: string; + Command: string; + Created: number; + Ports: Port[]; + Labels: Labels; + State: string; + Status: string; + HostConfig: { + NetworkMode: string; + }; + NetworkSettings: SummaryNetworkSettings; + Mounts: Mount[]; + } +} diff --git a/packages/docker-api/src/api/types/index.ts b/packages/docker-api/src/api/types/index.ts new file mode 100644 index 0000000..341ac03 --- /dev/null +++ b/packages/docker-api/src/api/types/index.ts @@ -0,0 +1,6 @@ +export * from './node' +export * from './swarm' +export * from './system' +export * from './network' +export * from './service' +export * from './container' diff --git a/packages/docker-api/src/api/types/network.ts b/packages/docker-api/src/api/types/network.ts new file mode 100644 index 0000000..97eec86 --- /dev/null +++ b/packages/docker-api/src/api/types/network.ts @@ -0,0 +1,70 @@ +import { Labels, Options, StringMap } from '../common' + +export declare namespace network { + export interface Config { + Subnet: string; + Gateway: string; + } + + export interface IPAM { + Driver: string; + Options?: any; + Config: Config[]; + } + + export interface ConfigFrom { + Network: string; + } + + export interface EndpointResource { + Name: string; + EndpointID: string; + MacAddress: string; + IPv4Address: string; + IPv6Address: string; + } + + export interface Containers { + [key: string]: EndpointResource; + } + + export interface PeerInfo { + Name: string; + IP: string; + } + + // Task carries the information about one backend task + export interface Task { + Name: string; + EndpointID: string; + EndpointIP: string; + Info: StringMap; + } + + export interface ServiceInfo { + VIP: string; + Ports: string; + LocalLBIndex: number; + Tasks: Task; + } + + export interface NetworkResource { + Name: string; + Id: string; + Created: string; + Scope: string; + Driver: string; + EnableIPv6: boolean; + IPAM: IPAM; + Internal: boolean; + Attachable: boolean; + Ingress: boolean; + ConfigFrom: ConfigFrom; + ConfigOnly: boolean; + Containers?: Containers; + Options: Options; + Labels: Labels; + Peers: network.PeerInfo[]; + Services: network.ServiceInfo[]; + } +} diff --git a/packages/docker-api/src/api/types/node.ts b/packages/docker-api/src/api/types/node.ts new file mode 100644 index 0000000..d8c8308 --- /dev/null +++ b/packages/docker-api/src/api/types/node.ts @@ -0,0 +1,71 @@ +import { Labels } from '../common' + +export declare namespace node { + + export interface Version { + Index: number; + } + + export interface Spec { + Labels: Labels; + Role: string; + Availability: string; + } + + export interface Platform { + Architecture: string; + OS: string; + } + + export interface Resources { + NanoCPUs: number; + MemoryBytes: number; + } + + export interface Plugin { + Type: string; + Name: string; + } + + export interface Engine { + EngineVersion: string; + Plugins: Plugin[]; + } + + export interface TLSInfo { + TrustRoot: string; + CertIssuerSubject: string; + CertIssuerPublicKey: string; + } + + export interface Description { + Hostname: string; + Platform: Platform; + Resources: Resources; + Engine: Engine; + TLSInfo: TLSInfo; + } + + export interface Status { + State: string; + Addr: string; + } + + export interface ManagerStatus { + Leader: boolean; + Reachability: string; + Addr: string; + } + + export interface Node { + ID: string; + Version: Version; + CreatedAt: string; + UpdatedAt: string; + Spec: Spec; + Description: Description; + Status: Status; + ManagerStatus: ManagerStatus; + } +} + diff --git a/packages/docker-api/src/api/types/service.ts b/packages/docker-api/src/api/types/service.ts new file mode 100644 index 0000000..aa72ccf --- /dev/null +++ b/packages/docker-api/src/api/types/service.ts @@ -0,0 +1,195 @@ +import { Labels, Options } from '../common' + +export declare namespace service { + export interface Version { + Index: number; + } + + export interface Privileges { + CredentialSpec?: any; + SELinuxContext?: any; + } + + export interface DriverConfig { + Options: Options; + } + + export interface VolumeOptions { + Labels: Labels; + DriverConfig: DriverConfig; + } + + export interface Mount { + Type: string; + Source: string; + Target: string; + VolumeOptions: VolumeOptions; + ReadOnly?: boolean; + } + + export interface File { + Name: string; + UID: string; + GID: string; + Mode: number; + } + + export interface Secret { + File: File; + SecretID: string; + SecretName: string; + } + + export interface DNSConfig { + } + + export interface ContainerSpec { + Image: string; + Labels: Labels; + Privileges: Privileges; + Mounts: Mount[]; + Isolation: string; + Env: string[]; + Secrets: Secret[]; + Args: string[]; + StopGracePeriod?: number; + DNSConfig: DNSConfig; + User: string; + } + + export interface Limits { + MemoryBytes: any; + NanoCPUs?: number; + } + + export interface Reservations { + MemoryBytes: any; + NanoCPUs?: number; + } + + export interface Resources { + Limits: Limits; + Reservations: Reservations; + } + + export interface Platform { + Architecture: string; + OS: string; + } + + export interface Placement { + Constraints: string[]; + Platforms: Platform[]; + } + + export interface Network { + Target: string; + Aliases: string[]; + } + + export interface RestartPolicy { + Condition: string; + Delay: any; + MaxAttempts: number; + Window: any; + } + + export interface LogDriver { + Name: string; + Options: Options; + } + + export interface TaskTemplate { + ContainerSpec: ContainerSpec; + Resources: Resources; + Placement: Placement; + Networks: Network[]; + ForceUpdate: number; + Runtime: string; + RestartPolicy: RestartPolicy; + LogDriver: LogDriver; + } + + export interface Replicated { + Replicas: number; + } + + export interface Global { + } + + export interface Mode { + Replicated: Replicated; + Global: Global; + } + + export interface Port { + Protocol: string; + TargetPort: number; + PublishedPort: number; + PublishMode: string; + } + + export interface EndpointSpec { + Mode: string; + Ports: Port[]; + } + + export interface UpdateConfig { + Parallelism: number; + FailureAction: string; + Monitor: number; + MaxFailureRatio: number; + Order: string; + } + + export interface RollbackConfig { + Parallelism: number; + FailureAction: string; + Monitor: number; + MaxFailureRatio: number; + Order: string; + } + + export interface Spec { + Name: string; + Labels: Labels; + TaskTemplate: TaskTemplate; + Mode: Mode; + EndpointSpec: EndpointSpec; + UpdateConfig: UpdateConfig; + RollbackConfig: RollbackConfig; + } + + export interface VirtualIP { + NetworkID: string; + Addr: string; + } + + export interface Endpoint { + Spec: Spec; + Ports: Port[]; + VirtualIPs: VirtualIP[]; + } + + export interface PreviousSpec { + Name: string; + Labels: Labels; + TaskTemplate: TaskTemplate; + Mode: Mode; + EndpointSpec: EndpointSpec; + UpdateConfig: UpdateConfig; + RollbackConfig: RollbackConfig; + } + + export interface Service { + ID: string; + Version: Version; + CreatedAt: string; + UpdatedAt: any; + Spec: Spec; + Endpoint: Endpoint; + PreviousSpec: PreviousSpec; + } + +} + diff --git a/packages/docker-api/src/api/types/swarm.ts b/packages/docker-api/src/api/types/swarm.ts new file mode 100644 index 0000000..6493415 --- /dev/null +++ b/packages/docker-api/src/api/types/swarm.ts @@ -0,0 +1,70 @@ +export declare namespace swarm { + export interface Version { + Index: number; + } + + export interface Labels { + } + + export interface Orchestration { + TaskHistoryRetentionLimit?: number; + } + + export interface Raft { + SnapshotInterval?: number; + KeepOldSnapshots?: number; + LogEntriesForSlowFollowers?: number; + ElectionTick?: number; + HeartbeatTick?: number; + } + + export interface Dispatcher { + HeartbeatPeriod?: number; + } + + export interface CAConfig { + NodeCertExpiry?: number; + } + + export interface TaskDefaults { + } + + export interface EncryptionConfig { + AutoLockManagers?: boolean; + } + + export interface Spec { + Name?: string; + Labels?: Labels; + Orchestration?: Orchestration; + Raft?: Raft; + Dispatcher?: Dispatcher; + CAConfig?: CAConfig; + TaskDefaults?: TaskDefaults; + EncryptionConfig?: EncryptionConfig; + } + + export interface TLSInfo { + TrustRoot: string; + CertIssuerSubject: string; + CertIssuerPublicKey: string; + } + + export interface JoinTokens { + Worker: string; + Manager: string; + } + + export interface Info { + ID: string; + Version: Version; + CreatedAt: string; + UpdatedAt: string; + Spec: Spec; + TLSInfo: TLSInfo; + RootRotationInProgress: boolean; + DefaultAddrPool: string[]; + SubnetSize: number; + JoinTokens: JoinTokens; + } +} \ No newline at end of file diff --git a/packages/docker-api/src/api/types/system.ts b/packages/docker-api/src/api/types/system.ts new file mode 100644 index 0000000..cd3bfa0 --- /dev/null +++ b/packages/docker-api/src/api/types/system.ts @@ -0,0 +1,224 @@ +import { Labels, Options } from '../common' + +export declare namespace system { + export class Plugins { + Volume: string[]; + Network: string[]; + Authorization?: any; + Log: string[]; + } + + export class DockerIo { + Name: string; + Mirrors: string[]; + Secure: boolean; + Official: boolean; + } + + export class IndexConfigs { + [key: string]: DockerIo; + } + + export class RegistryConfig { + AllowNondistributableArtifactsCIDRs: any[]; + AllowNondistributableArtifactsHostnames: any[]; + InsecureRegistryCIDRs: string[]; + IndexConfigs: IndexConfigs; + Mirrors: string[]; + } + + export class Runc { + path: string; + } + + export class Runtimes { + runc: Runc; + } + + export class RemoteManager { + NodeID: string; + Addr: string; + } + + export class ClusterVersion { + Index: number; + } + + export class Orchestration { + TaskHistoryRetentionLimit: number; + } + + export class Raft { + SnapshotInterval: number; + KeepOldSnapshots: number; + LogEntriesForSlowFollowers: number; + ElectionTick: number; + HeartbeatTick: number; + } + + export class Dispatcher { + HeartbeatPeriod: number; + } + + export class CAConfig { + NodeCertExpiry: number; + } + + export class TaskDefaults { + } + + export class EncryptionConfig { + AutoLockManagers: boolean; + } + + export class Spec { + Name: string; + Labels: Labels; + Orchestration: Orchestration; + Raft: Raft; + Dispatcher: Dispatcher; + CAConfig: CAConfig; + TaskDefaults: TaskDefaults; + EncryptionConfig: EncryptionConfig; + } + + export class TLSInfo { + TrustRoot: string; + CertIssuerSubject: string; + CertIssuerPublicKey: string; + } + + export class Cluster { + ID: string; + Version: ClusterVersion; + CreatedAt: string; + UpdatedAt: string; + Spec: Spec; + TLSInfo: TLSInfo; + RootRotationInProgress: boolean; + DefaultAddrPool: string[]; + SubnetSize: number; + } + + export class Swarm { + NodeID: string; + NodeAddr: string; + LocalNodeState: string; + ControlAvailable: boolean; + Error: string; + RemoteManagers: RemoteManager[]; + Nodes: number; + Managers: number; + Cluster: Cluster; + } + + export class ContainerdCommit { + ID: string; + Expected: string; + } + + export class RuncCommit { + ID: string; + Expected: string; + } + + export class InitCommit { + ID: string; + Expected: string; + } + + export class Info { + ID: string; + Containers: number; + ContainersRunning: number; + ContainersPaused: number; + ContainersStopped: number; + Images: number; + Driver: string; + DriverStatus: string[][]; + SystemStatus?: any; + Plugins: Plugins; + MemoryLimit: boolean; + SwapLimit: boolean; + KernelMemory: boolean; + CpuCfsPeriod: boolean; + CpuCfsQuota: boolean; + CPUShares: boolean; + CPUSet: boolean; + IPv4Forwarding: boolean; + BridgeNfIptables: boolean; + BridgeNfIp6tables: boolean; + Debug: boolean; + NFd: number; + OomKillDisable: boolean; + NGoroutines: number; + SystemTime: string; + LoggingDriver: string; + CgroupDriver: string; + NEventsListener: number; + KernelVersion: string; + OperatingSystem: string; + OSType: string; + Architecture: string; + IndexServerAddress: string; + RegistryConfig: RegistryConfig; + NCPU: number; + MemTotal: number; + GenericResources?: any; + DockerRootDir: string; + HttpProxy: string; + HttpsProxy: string; + NoProxy: string; + Name: string; + Labels: string[]; + ExperimentalBuild: boolean; + ServerVersion: string; + ClusterStore: string; + ClusterAdvertise: string; + Runtimes: Runtimes; + DefaultRuntime: string; + Swarm: Swarm; + LiveRestoreEnabled: boolean; + Isolation: string; + InitBinary: string; + ContainerdCommit: ContainerdCommit; + RuncCommit: RuncCommit; + InitCommit: InitCommit; + SecurityOptions: string[]; + ProductLicense: string; + Warnings?: any; + } + + export class Platform { + Name: string; + } + export class Details { + ApiVersion: string; + Arch: string; + BuildTime: string; + Experimental: string; + GitCommit: string; + GoVersion: string; + KernelVersion: string; + MinAPIVersion: string; + Os: string; + } + export class Component { + Name: string; + Version: string; + Details: Details; + } + export class Version { + Platform: Platform; + Components: Component[]; + Version: string; + ApiVersion: string; + MinAPIVersion: string; + GitCommit: string; + GoVersion: string; + Os: string; + Arch: string; + KernelVersion: string; + BuildTime: string; + } +} \ No newline at end of file diff --git a/packages/docker-api/src/client/container.ts b/packages/docker-api/src/client/container.ts new file mode 100644 index 0000000..94beb7d --- /dev/null +++ b/packages/docker-api/src/client/container.ts @@ -0,0 +1,23 @@ +import * as api from '../utils/api'; +import * as opts from '../api/opts' +import * as types from '../api/types' +import * as http from 'http' + +export namespace container { + export async function list(filters?: opts.container.ListOpts) { + return await api.get('/containers/json', filters) + } + + export async function info(id: string, query: { size: boolean } = { size: false }) { + return await api.get(`/containers/${id}/json`, query); + } + + export async function logs(id: string, opts: opts.container.LogsOpts = {}): Promise { + return await api.stream(`/containers/${id}/logs`, Object.assign({ + follow: true, + stdout: true, + stderr: true, + tail: 10 + }, opts)); + } +} diff --git a/packages/docker-api/src/client/index.ts b/packages/docker-api/src/client/index.ts new file mode 100644 index 0000000..341ac03 --- /dev/null +++ b/packages/docker-api/src/client/index.ts @@ -0,0 +1,6 @@ +export * from './node' +export * from './swarm' +export * from './system' +export * from './network' +export * from './service' +export * from './container' diff --git a/packages/docker-api/src/client/network.ts b/packages/docker-api/src/client/network.ts new file mode 100644 index 0000000..4a22a2a --- /dev/null +++ b/packages/docker-api/src/client/network.ts @@ -0,0 +1,9 @@ +import * as api from '../utils/api' +import * as opts from '../api/opts' +import * as types from '../api/types' + +export namespace network { + export async function list(opts?: opts.network.ListOpts) { + return await api.get('/networks', opts) + } +} diff --git a/packages/docker-api/src/client/node.ts b/packages/docker-api/src/client/node.ts new file mode 100644 index 0000000..b0057bb --- /dev/null +++ b/packages/docker-api/src/client/node.ts @@ -0,0 +1,8 @@ +import * as api from '../utils/api' +import * as types from '../api/types' + +export namespace node { + export async function list() { + return await api.get('/nodes'); + } +} diff --git a/packages/docker-api/src/client/service.ts b/packages/docker-api/src/client/service.ts new file mode 100644 index 0000000..b9a74a4 --- /dev/null +++ b/packages/docker-api/src/client/service.ts @@ -0,0 +1,9 @@ +import * as api from '../utils/api'; +import * as opts from '../api/opts'; +import * as types from '../api/types'; + +export namespace service { + export async function list(filters?: opts.service.ListOpts) { + return await api.get('/services', filters); + } +} \ No newline at end of file diff --git a/packages/docker-api/src/client/swarm.ts b/packages/docker-api/src/client/swarm.ts new file mode 100644 index 0000000..2684297 --- /dev/null +++ b/packages/docker-api/src/client/swarm.ts @@ -0,0 +1,13 @@ +import * as api from '../utils/api' +import * as opts from '../api/opts' +import * as types from '../api/types' + +export namespace swarm { + export async function inspect() { + return await api.get('/swarm'); + } + + export async function init(opts: opts.swarm.InitOpts) { + return await api.post('/swarm/init', opts); + } +} \ No newline at end of file diff --git a/packages/docker-api/src/client/system.ts b/packages/docker-api/src/client/system.ts new file mode 100644 index 0000000..27b6151 --- /dev/null +++ b/packages/docker-api/src/client/system.ts @@ -0,0 +1,22 @@ +import * as api from '../utils/api' +import * as types from '../api/types' + +export namespace system { + export async function info() { + return await api.get('/info'); + } + + export async function version() { + return await api.get('/version'); + } + + export async function events(cb: (events: object) => void) { + let stream = await api.stream('/events'); + stream.on('data', (chunk: ArrayBuffer) => { + cb(JSON.parse(Buffer.from(chunk).toString())) + }) + stream.on('end', () => { + cb(undefined); + }) + } +} diff --git a/packages/docker-api/src/index.ts b/packages/docker-api/src/index.ts new file mode 100644 index 0000000..7eec814 --- /dev/null +++ b/packages/docker-api/src/index.ts @@ -0,0 +1 @@ +export * from './client' \ No newline at end of file diff --git a/packages/docker-api/src/utils/api.ts b/packages/docker-api/src/utils/api.ts new file mode 100644 index 0000000..b0ade70 --- /dev/null +++ b/packages/docker-api/src/utils/api.ts @@ -0,0 +1,78 @@ +import * as http from 'http' +import axios, { AxiosResponse, AxiosRequestConfig, Method, AxiosInstance } from 'axios' + +let api: AxiosInstance; + +export async function get(path: string, data?: object): Promise { + return await handle("GET", path, { params: data }); +} + +export async function post(path: string, data?: object): Promise { + return await handle("POST", path, { data }); +} + +export async function stream(path: string, data?: object): Promise { + return await handle("GET", path, { params: data, responseType: "stream" }); +} + +async function handle(method: Method, path: string, reqConfig?: AxiosRequestConfig): Promise { + let config: AxiosRequestConfig = { + method, + url: path, + }; + let startTime = Date.now(); + Object.assign(config, reqConfig) + let response: AxiosResponse; + try { + response = await api.request(config); + return response.data as T + } catch (ex) { + if (!ex.response) { throw ex; } + response = ex.response; + if (response.status > 299 && config.responseType == "stream") { + let stream = response.data; + response.data = await new Promise((resolve, reject) => { + let cache = ''; + stream.on('data', (chunk: ArrayBuffer) => { + cache += chunk.toString() + }) + stream.on('end', () => { + resolve(JSON.parse(cache) as T); + }) + }) + } + throw new Error(JSON.stringify(response.data)); + } finally { + if (response) { + console.log(`========== Docker API Invoke ========== +REQUEST METHOD : ${method} +REQUEST PATH : ${response.request.path} +REQUEST PARAMS : ${config.params ? JSON.stringify(config.params) : ''} +REQUEST BODY : ${config.data ? JSON.stringify(config.data) : ''} +RESPONSE BODY : ${toString.call(response.data.pipe) === "[object Function]" ? '' : JSON.stringify(response.data)} +HANDLE TIME : ${Date.now() - startTime}ms +=======================================`); + } + // console.log(`${method} ${path} HTTP/1.1 + // ${config.data ? JSON.stringify(config.data, null, 2) + '\n' : ''} + // HTTP/1.1 ${response.status} ${response.statusText} + // ${config.responseType != 'stream' ? JSON.stringify(response.data, null, 2) : config.responseType}`); + } +} + +function init() { + const instanceConfig: AxiosRequestConfig = { + headers: { + 'Content-Type': 'application/json' + }, + timeout: 5000 + } + if (process.env.DOCKER_HOST.startsWith("/")) { + instanceConfig.socketPath = process.env.DOCKER_HOST + } else { + instanceConfig.baseURL = process.env.DOCKER_HOST + } + api = axios.create(instanceConfig) +} + +init(); diff --git a/packages/docker-api/src/utils/utils.ts b/packages/docker-api/src/utils/utils.ts new file mode 100644 index 0000000..e300a03 --- /dev/null +++ b/packages/docker-api/src/utils/utils.ts @@ -0,0 +1,3 @@ +export function Filters2String(filters: any) { + +} \ No newline at end of file diff --git a/packages/docker-api/tsconfig.json b/packages/docker-api/tsconfig.json new file mode 100644 index 0000000..7aae5d2 --- /dev/null +++ b/packages/docker-api/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": "src", + "outDir": "dist" + } +} \ No newline at end of file diff --git a/packages/faas/.gitignore b/packages/faas/.gitignore new file mode 100644 index 0000000..d856d44 --- /dev/null +++ b/packages/faas/.gitignore @@ -0,0 +1,4 @@ +/node_modules +/dist +/package-lock.json +/yarn.lock diff --git a/packages/faas/.npmignore b/packages/faas/.npmignore new file mode 100644 index 0000000..b0eede3 --- /dev/null +++ b/packages/faas/.npmignore @@ -0,0 +1,22 @@ +src +test +typings +bundled +build +coverage +docs +wiki +gulpfile.js +bower.json +karma.conf.js +tsconfig.json +typings.json +CONTRIBUTING.md +ISSUE_TEMPLATE.md +PULL_REQUEST_TEMPLATE.md +tslint.json +wallaby.js +.travis.yml +.gitignore +.vscode +type_definitions \ No newline at end of file diff --git a/packages/faas/README.md b/packages/faas/README.md new file mode 100644 index 0000000..fb468ce --- /dev/null +++ b/packages/faas/README.md @@ -0,0 +1,11 @@ +# `@dayu/faas` + +> TODO: description + +## Usage + +``` +const core = require('@dayu/faas'); + +// TODO: DEMONSTRATE API +``` diff --git a/packages/faas/package.json b/packages/faas/package.json new file mode 100644 index 0000000..af6abea --- /dev/null +++ b/packages/faas/package.json @@ -0,0 +1,27 @@ +{ + "name": "@dayu/faas", + "version": "0.0.1", + "description": "> TODO: description", + "author": "MiaoWoo ", + "homepage": "https://github.com/circlecloud/dayu", + "license": "ISC", + "main": "dist/index.js", + "publishConfig": { + "registry": "https://repo.yumc.pw/repository/npm-hosted/" + }, + "scripts": { + "dev": "npx ts-node-dev src/index.ts", + "clean": "rimraf dist", + "watch": "npx tsc --watch", + "build": "yarn clean && npx tsc", + "test": "echo \"Error: run tests from root\" && exit 1" + }, + "dependencies": { + "axios": "^0.19.0" + }, + "devDependencies": { + "rimraf": "^2.6.3", + "ts-node-dev": "^1.0.0-pre.40", + "typescript": "^3.5.2" + } +} \ No newline at end of file diff --git a/packages/faas/public/index.html b/packages/faas/public/index.html new file mode 100644 index 0000000..3f9b35d --- /dev/null +++ b/packages/faas/public/index.html @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/packages/faas/public/js/index.js b/packages/faas/public/js/index.js new file mode 100644 index 0000000..b2e56d6 --- /dev/null +++ b/packages/faas/public/js/index.js @@ -0,0 +1,68 @@ +let command = ''; +Terminal.applyAddon(fit); +Terminal.applyAddon(attach); +Terminal.applyAddon(fullscreen); +var term = new Terminal({ + experimentalCharAtlas: 'dynamic', + cursorBlink: false, +}); +term.open(document.getElementById('terminal')); +term.toggleFullScreen(); +term.fit(); + +window.onresize = function() { + term.fit(); +} + +let query = {} +location.search.substring(1).split("&").forEach(q => { + let qy = q.split("=", 2); + query[qy[0]] = qy[1] +}) + +console.log(query); + +var socket = io('/container', { + path: '/ws', + transports: ['websocket'] +}); +socket.on('connect', () => { + term.writeln('connect') + if (query.action) { + term.writeln(`Recover Action: ${query.action} Data: ${query.data}`) + switch (query.action) { + case "container": + socket.emit('logs', { + id: query.data, + since: Date.now() / 1000 - 60 * 15, + until: Date.now() / 1000, + stderr: false, + tail: "all" + }) + break; + default: + } + } +}); +term.on('data', async data => { + if (data == '\t') { + return; + } + term.write(data); + if (data == '\r') { + term.write('\n'); + socket.emit('logs', { + id: command + }) + command = ''; + } else { + command += data; + } +}); +socket.on('message', data => { + term.write(data.toString() + '\r\n'); +}); +socket.on('disconnect', () => { + term.reset(); + term.writeln('disconnect'); +}); diff --git a/packages/faas/src/api.ts b/packages/faas/src/api.ts new file mode 100644 index 0000000..a693fd0 --- /dev/null +++ b/packages/faas/src/api.ts @@ -0,0 +1,26 @@ +import * as faas from './interfaces' +import axios, { AxiosRequestConfig } from 'axios' + +const instanceConfig: AxiosRequestConfig = { + baseURL: 'https://faas.yumc.pw', + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Basic YWRtaW46MmQ2MzdiYzQ4MWQxYThhMjg2M2E2ZTIzOTY1ZWRlNDI0ZTRjMTk2OTQyMzU5N2M5MzRlNGQyY2FlZTNkMjk2OA==' + }, + timeout: 5000 +} + +let api = axios.create(instanceConfig) + +async function get(path: string) { + let result = await api.get(path); + return result.data as T; +} + +export async function getFunctions() { + return await get('/system/functions'); +} + +export async function getFunction(name: string) { + return await get(`/system/function/${name}`); +} diff --git a/packages/faas/src/index.ts b/packages/faas/src/index.ts new file mode 100644 index 0000000..3318fdb --- /dev/null +++ b/packages/faas/src/index.ts @@ -0,0 +1 @@ +export * from './api' diff --git a/packages/faas/src/interfaces.ts b/packages/faas/src/interfaces.ts new file mode 100644 index 0000000..05d885b --- /dev/null +++ b/packages/faas/src/interfaces.ts @@ -0,0 +1,13 @@ +export interface Labels { + [key: string]: string +} +export interface Function { + name: string; + image: string; + invocationCount: number; + replicas: number; + envProcess: string; + availableReplicas: number; + labels: Labels; + annotations?: any; +} \ No newline at end of file diff --git a/packages/faas/tsconfig.json b/packages/faas/tsconfig.json new file mode 100644 index 0000000..7aae5d2 --- /dev/null +++ b/packages/faas/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": "src", + "outDir": "dist" + } +} \ No newline at end of file diff --git a/packages/vscode/.gitignore b/packages/vscode/.gitignore new file mode 100644 index 0000000..5fe00fe --- /dev/null +++ b/packages/vscode/.gitignore @@ -0,0 +1,4 @@ +out +node_modules +.vscode-test/ +*.vsix diff --git a/packages/vscode/.vscode/extensions.json b/packages/vscode/.vscode/extensions.json new file mode 100644 index 0000000..0a18b9c --- /dev/null +++ b/packages/vscode/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "ms-vscode.vscode-typescript-tslint-plugin" + ] +} \ No newline at end of file diff --git a/packages/vscode/.vscode/launch.json b/packages/vscode/.vscode/launch.json new file mode 100644 index 0000000..6018202 --- /dev/null +++ b/packages/vscode/.vscode/launch.json @@ -0,0 +1,35 @@ +// A launch configuration that compiles the extension and then opens it inside a new window +// Use IntelliSense to learn about possible attributes. +// Hover to view descriptions of existing attributes. +// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 +{ + "version": "0.2.0", + "configurations": [{ + "name": "Run Extension", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}" + ], + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ], + "preLaunchTask": "npm: watch" + }, + { + "name": "Extension Tests", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/out/test" + ], + "outFiles": [ + "${workspaceFolder}/out/test/**/*.js" + ], + "preLaunchTask": "npm: watch" + } + ] +} diff --git a/packages/vscode/.vscode/settings.json b/packages/vscode/.vscode/settings.json new file mode 100644 index 0000000..f3bd701 --- /dev/null +++ b/packages/vscode/.vscode/settings.json @@ -0,0 +1,12 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "files.exclude": { + "out": false // set this to true to hide the "out" folder with the compiled JS files + }, + "search.exclude": { + "out": true // set this to false to include "out" folder in search results + }, + // Turn off tsc task auto detection since we have the necessary tasks as npm scripts + "typescript.tsc.autoDetect": "off", + "typescript.tsdk": "node_modules/typescript/lib" +} \ No newline at end of file diff --git a/packages/vscode/.vscode/tasks.json b/packages/vscode/.vscode/tasks.json new file mode 100644 index 0000000..3b17e53 --- /dev/null +++ b/packages/vscode/.vscode/tasks.json @@ -0,0 +1,20 @@ +// See https://go.microsoft.com/fwlink/?LinkId=733558 +// for the documentation about the tasks.json format +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "watch", + "problemMatcher": "$tsc-watch", + "isBackground": true, + "presentation": { + "reveal": "never" + }, + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} diff --git a/packages/vscode/.vscodeignore b/packages/vscode/.vscodeignore new file mode 100644 index 0000000..ed3f9d3 --- /dev/null +++ b/packages/vscode/.vscodeignore @@ -0,0 +1,10 @@ +.vscode/** +.vscode-test/** +out/test/** +src/** +.gitignore +vsc-extension-quickstart.md +**/tsconfig.json +**/tslint.json +**/*.map +**/*.ts \ No newline at end of file diff --git a/packages/vscode/CHANGELOG.md b/packages/vscode/CHANGELOG.md new file mode 100644 index 0000000..5d40c0e --- /dev/null +++ b/packages/vscode/CHANGELOG.md @@ -0,0 +1,9 @@ +# Change Log + +All notable changes to the "faas" extension will be documented in this file. + +Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. + +## [Unreleased] + +- Initial release \ No newline at end of file diff --git a/packages/vscode/README.md b/packages/vscode/README.md new file mode 100644 index 0000000..c76584b --- /dev/null +++ b/packages/vscode/README.md @@ -0,0 +1,65 @@ +# faas README + +This is the README for your extension "faas". After writing up a brief description, we recommend including the following sections. + +## Features + +Describe specific features of your extension including screenshots of your extension in action. Image paths are relative to this README file. + +For example if there is an image subfolder under your extension project workspace: + +\!\[feature X\]\(images/feature-x.png\) + +> Tip: Many popular extensions utilize animations. This is an excellent way to show off your extension! We recommend short, focused animations that are easy to follow. + +## Requirements + +If you have any requirements or dependencies, add a section describing those and how to install and configure them. + +## Extension Settings + +Include if your extension adds any VS Code settings through the `contributes.configuration` extension point. + +For example: + +This extension contributes the following settings: + +* `myExtension.enable`: enable/disable this extension +* `myExtension.thing`: set to `blah` to do something + +## Known Issues + +Calling out known issues can help limit users opening duplicate issues against your extension. + +## Release Notes + +Users appreciate release notes as you update your extension. + +### 1.0.0 + +Initial release of ... + +### 1.0.1 + +Fixed issue #. + +### 1.1.0 + +Added features X, Y, and Z. + +----------------------------------------------------------------------------------------------------------- + +## Working with Markdown + +**Note:** You can author your README using Visual Studio Code. Here are some useful editor keyboard shortcuts: + +* Split the editor (`Cmd+\` on macOS or `Ctrl+\` on Windows and Linux) +* Toggle preview (`Shift+CMD+V` on macOS or `Shift+Ctrl+V` on Windows and Linux) +* Press `Ctrl+Space` (Windows, Linux) or `Cmd+Space` (macOS) to see a list of Markdown snippets + +### For more information + +* [Visual Studio Code's Markdown Support](http://code.visualstudio.com/docs/languages/markdown) +* [Markdown Syntax Reference](https://help.github.com/articles/markdown-basics/) + +**Enjoy!** diff --git a/packages/vscode/package.json b/packages/vscode/package.json new file mode 100644 index 0000000..91426b7 --- /dev/null +++ b/packages/vscode/package.json @@ -0,0 +1,76 @@ +{ + "name": "@dayu/vscode", + "displayName": "vscode", + "description": "", + "version": "0.0.1", + "engines": { + "vscode": "^1.35.0" + }, + "categories": [ + "Other" + ], + "activationEvents": [ + "onCommand:extension.helloWorld" + ], + "main": "./dist/extension.js", + "contributes": { + "viewsContainers": { + "activitybar": [ + { + "id": "dayu-explorer", + "title": "Dayu Explorer", + "icon": "src/images/logo.svg" + } + ] + }, + "views": { + "dayu-explorer": [ + { + "id": "docker-explorer", + "name": "Docker" + }, + { + "id": "openfaas-explorer", + "name": "OpenFaaS" + } + ] + }, + "commands": [ + { + "command": "dayu.container.logs", + "title": "View Container Logs", + "icon": { + "dark": "src/images/browser.svg" + } + } + ], + "menus": { + "view/item/context": [ + { + "command": "dayu.container.logs", + "when": "view == docker-explorer && viewItem =~ /.*\"type\":\"CONTAINER\".*/", + "group": "inline" + } + ] + } + }, + "scripts": { + "vscode:prepublish": "yarn run compile", + "compile": "tsc -p ./", + "watch": "tsc -watch -p ./", + "postinstall": "node ../../node_modules/vscode/bin/install", + "test": "yarn run compile && node ./node_modules/vscode/bin/test" + }, + "dependencies": { + "@dayu/docker-api": "^0.0.1", + "@dayu/faas": "^0.0.1", + "axios": "^0.19.0" + }, + "devDependencies": { + "@types/mocha": "^2.2.42", + "@types/node": "^10.12.21", + "tslint": "^5.12.1", + "typescript": "^3.3.1", + "vscode": "^1.1.28" + } +} diff --git a/packages/vscode/src/extension.ts b/packages/vscode/src/extension.ts new file mode 100644 index 0000000..60b3c3e --- /dev/null +++ b/packages/vscode/src/extension.ts @@ -0,0 +1,15 @@ +// The module 'vscode' contains the VS Code extensibility API +// Import the module and reference it with the alias vscode in your code below +import * as vscode from 'vscode'; +process.env.DOCKER_HOST = '/var/run/docker.sock' +import { OpenFaasProvider, DockerProvider } from './provider' + +// this method is called when your extension is activated +// your extension is activated the very first time the command is executed +export function activate(context: vscode.ExtensionContext) { + new DockerProvider(context); + new OpenFaasProvider(context); +} + +// this method is called when your extension is deactivated +export function deactivate() { } diff --git a/packages/vscode/src/images/browser.svg b/packages/vscode/src/images/browser.svg new file mode 100644 index 0000000..8c9f6bd --- /dev/null +++ b/packages/vscode/src/images/browser.svg @@ -0,0 +1 @@ +Layer 1 \ No newline at end of file diff --git a/packages/vscode/src/images/centos.svg b/packages/vscode/src/images/centos.svg new file mode 100644 index 0000000..2960c85 --- /dev/null +++ b/packages/vscode/src/images/centos.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/vscode/src/images/docker-swarm.svg b/packages/vscode/src/images/docker-swarm.svg new file mode 100644 index 0000000..eb2143e --- /dev/null +++ b/packages/vscode/src/images/docker-swarm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/vscode/src/images/docker.svg b/packages/vscode/src/images/docker.svg new file mode 100644 index 0000000..745bea5 --- /dev/null +++ b/packages/vscode/src/images/docker.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/vscode/src/images/logo.svg b/packages/vscode/src/images/logo.svg new file mode 100644 index 0000000..d304bdb --- /dev/null +++ b/packages/vscode/src/images/logo.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/packages/vscode/src/images/refresh.svg b/packages/vscode/src/images/refresh.svg new file mode 100644 index 0000000..d79fdaa --- /dev/null +++ b/packages/vscode/src/images/refresh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/vscode/src/provider/base.ts b/packages/vscode/src/provider/base.ts new file mode 100644 index 0000000..33abc15 --- /dev/null +++ b/packages/vscode/src/provider/base.ts @@ -0,0 +1,28 @@ +import * as vscode from 'vscode' + +export class CteateItemOpt { + label: string; + context?: ItemContextValue; + state?: vscode.TreeItemCollapsibleState = vscode.TreeItemCollapsibleState.None; + icon?: string; + tooltip?: string; +} + +export interface ItemContextValue { + type?: string; + data?: any; +} + +export abstract class BaseProvider implements vscode.TreeDataProvider { + abstract onDidChangeTreeData?: vscode.Event | undefined; + abstract getTreeItem(element: T): vscode.TreeItem | Thenable; + abstract getChildren(element?: T | undefined): vscode.ProviderResult; + + createTreeItem(opts: CteateItemOpt) { + let item = new vscode.TreeItem(opts.label, opts.state); + item.contextValue = JSON.stringify(opts.context); + if (opts.icon) { item.iconPath = `src/images/${opts.icon}.svg` } + if (opts.tooltip) { item.tooltip = opts.tooltip; } + return item; + } +} diff --git a/packages/vscode/src/provider/docker.ts b/packages/vscode/src/provider/docker.ts new file mode 100644 index 0000000..1214096 --- /dev/null +++ b/packages/vscode/src/provider/docker.ts @@ -0,0 +1,127 @@ +import * as vscode from 'vscode' +import { BaseProvider, ItemContextValue } from './base' +import * as docker from '@dayu/docker-api' + +enum Type { + ROOT = "ROOT", + HOST = "HOST", + CONTAINERS = "CONTAINERS", + CONTAINER = "CONTAINER", + SERVICES = "SERVICES", + SERVICE = "SERVICE", + NETWORKS = "NETWORKS", + NETWORK = "NETWORK", + STACKS = "STACKS", + STACK = "STACK", + NODES = "NODES", + NODE = "NODE" +} + +let TREE_LIST = [Type.NODES, Type.CONTAINERS, Type.SERVICES, Type.NETWORKS] + +export class DockerProvider extends BaseProvider { + onDidChangeTreeData?: vscode.Event | undefined; + + constructor(context: vscode.ExtensionContext) { + super(); + context.subscriptions.push( + vscode.commands.registerCommand('dayu.container.logs', (item: vscode.TreeItem) => { + let value: ItemContextValue = JSON.parse(item.contextValue); + let url = `https://faas.n.yumc.pw?action=container&data=${value.data.id}`; + return vscode.commands.executeCommand("mini-browser.openUrl", url); + }), + vscode.window.registerTreeDataProvider('docker-explorer', this) + ) + } + + getTreeItem(element: vscode.TreeItem): vscode.TreeItem | Thenable { + return element; + } + + async getChildren(element?: vscode.TreeItem | undefined): Promise { + if (!element || !element.contextValue) { + return [this.createTreeItem({ + label: 'Docker', + context: { + type: Type.ROOT + }, + state: vscode.TreeItemCollapsibleState.Collapsed, + icon: "docker" + })] + } + let value: ItemContextValue = JSON.parse(element.contextValue); + switch (value.type) { + case Type.ROOT: + return TREE_LIST.map(i => { + return this.createTreeItem({ + label: i, + context: { + type: i + }, + state: vscode.TreeItemCollapsibleState.Collapsed + }) + }) + case Type.NODES: + let nodes = await docker.node.list(); + return nodes.map(n => { + return this.createTreeItem({ + label: n.ID, + context: { + type: Type.NODE, + data: { + id: n.ID + } + }, + tooltip: JSON.stringify(n, undefined, 2) + }) + }) + case Type.CONTAINERS: + let containers = await docker.container.list(); + return containers.map(c => { + return this.createTreeItem({ + label: c.Names[0], + context: { + type: Type.CONTAINER, + data: { + id: c.Id + } + }, + tooltip: JSON.stringify(c, undefined, 2) + }) + }) + case Type.SERVICES: + let services = await docker.service.list(); + return services.map(s => { + return this.createTreeItem({ + label: s.Spec.Name, + context: { + type: Type.SERVICE, + data: { + id: s.ID + } + }, + tooltip: JSON.stringify(s, undefined, 2) + }) + }) + case Type.NETWORKS: + let networks = await docker.network.list(); + return networks.map(n => { + return this.createTreeItem({ + label: n.Name, + context: { + type: Type.NETWORK, + data: { + id: n.Id + } + }, + tooltip: JSON.stringify(n, undefined, 2) + }) + }) + case Type.CONTAINER: + + break; + default: + } + return []; + } +} \ No newline at end of file diff --git a/packages/vscode/src/provider/faas.ts b/packages/vscode/src/provider/faas.ts new file mode 100644 index 0000000..e602adf --- /dev/null +++ b/packages/vscode/src/provider/faas.ts @@ -0,0 +1,48 @@ +import * as vscode from 'vscode' +import * as faas from '@dayu/faas' +import { BaseProvider, ItemContextValue } from './base' + +enum Type { + ROOT = "ROOT", +} + +export class OpenFaasProvider extends BaseProvider { + onDidChangeTreeData?: vscode.Event; + + constructor(context: vscode.ExtensionContext) { + super() + context.subscriptions.push( + vscode.window.registerTreeDataProvider('openfaas-explorer', this) + ) + } + + getTreeItem(element: vscode.TreeItem): vscode.TreeItem | Promise { + return element; + } + + async getChildren(element?: vscode.TreeItem): Promise { + if (!element || !element.contextValue) { + return [this.createTreeItem({ + label: "OpenFaaS", + state: vscode.TreeItemCollapsibleState.Collapsed, + context: { + type: Type.ROOT + }, + icon: "logo" + })] + } + let value: ItemContextValue = JSON.parse(element.contextValue); + switch (value.type) { + case Type.ROOT: + let funcs = await faas.getFunctions(); + return funcs.map(f => { + return this.createTreeItem({ + label: f.name, + tooltip: JSON.stringify(f, undefined, 2) + }) + }); + default: + return []; + } + } +} diff --git a/packages/vscode/src/provider/index.ts b/packages/vscode/src/provider/index.ts new file mode 100644 index 0000000..4c43cde --- /dev/null +++ b/packages/vscode/src/provider/index.ts @@ -0,0 +1,2 @@ +export * from './faas' +export * from './docker' diff --git a/packages/vscode/src/test/extension.test.ts b/packages/vscode/src/test/extension.test.ts new file mode 100644 index 0000000..a7a297f --- /dev/null +++ b/packages/vscode/src/test/extension.test.ts @@ -0,0 +1,22 @@ +// +// Note: This example test is leveraging the Mocha test framework. +// Please refer to their documentation on https://mochajs.org/ for help. +// + +// The module 'assert' provides assertion methods from node +import * as assert from 'assert'; + +// You can import and use all API from the 'vscode' module +// as well as import your extension to test it +// import * as vscode from 'vscode'; +// import * as myExtension from '../extension'; + +// Defines a Mocha test suite to group tests of similar kind together +suite("Extension Tests", function () { + + // Defines a Mocha unit test + test("Something 1", function() { + assert.equal(-1, [1, 2, 3].indexOf(5)); + assert.equal(-1, [1, 2, 3].indexOf(0)); + }); +}); \ No newline at end of file diff --git a/packages/vscode/src/test/index.ts b/packages/vscode/src/test/index.ts new file mode 100644 index 0000000..4cda15c --- /dev/null +++ b/packages/vscode/src/test/index.ts @@ -0,0 +1,23 @@ +// +// PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING +// +// This file is providing the test runner to use when running extension tests. +// By default the test runner in use is Mocha based. +// +// You can provide your own test runner if you want to override it by exporting +// a function run(testsRoot: string, clb: (error: Error, failures?: number) => void): void +// that the extension host can call to run the tests. The test runner is expected to use console.log +// to report the results back to the caller. When the tests are finished, return +// a possible error to the callback or null if none. + +import * as testRunner from 'vscode/lib/testrunner'; + +// You can directly control Mocha options by configuring the test runner below +// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options +// for more info +testRunner.configure({ + ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) + useColors: true // colored output from test results +}); + +module.exports = testRunner; \ No newline at end of file diff --git a/packages/vscode/tsconfig.json b/packages/vscode/tsconfig.json new file mode 100644 index 0000000..7aae5d2 --- /dev/null +++ b/packages/vscode/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": "src", + "outDir": "dist" + } +} \ No newline at end of file diff --git a/packages/vscode/vsc-extension-quickstart.md b/packages/vscode/vsc-extension-quickstart.md new file mode 100644 index 0000000..cde7334 --- /dev/null +++ b/packages/vscode/vsc-extension-quickstart.md @@ -0,0 +1,41 @@ +# Welcome to your VS Code Extension + +## What's in the folder + +* This folder contains all of the files necessary for your extension. +* `package.json` - this is the manifest file in which you declare your extension and command. + * The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesn’t yet need to load the plugin. +* `src/extension.ts` - this is the main file where you will provide the implementation of your command. + * The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`. + * We pass the function containing the implementation of the command as the second parameter to `registerCommand`. + +## Get up and running straight away + +* Press `F5` to open a new window with your extension loaded. +* Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`. +* Set breakpoints in your code inside `src/extension.ts` to debug your extension. +* Find output from your extension in the debug console. + +## Make changes + +* You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`. +* You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes. + +## Explore the API + +* You can open the full set of our API when you open the file `node_modules/vscode/vscode.d.ts`. + +## Run tests + +* Open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Extension Tests`. +* Press `F5` to run the tests in a new window with your extension loaded. +* See the output of the test result in the debug console. +* Make changes to `test/extension.test.ts` or create new test files inside the `test` folder. + * By convention, the test runner will only consider files matching the name pattern `**.test.ts`. + * You can create folders inside the `test` folder to structure your tests any way you want. + +## Go further + + * Reduce the extension size and improve the startup time by [bundling your extension](https://code.visualstudio.com/api/working-with-extensions/testing-extension). + * [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VSCode extension marketplace. + * Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration). diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..f754065 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "baseUrl": "src", + "outDir": "dist", + "target": "es6", + "module": "commonjs", + "sourceMap": true, + "declaration": true, + "noImplicitAny": true, + "allowUnreachableCode": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true + } +} \ No newline at end of file diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000..c791efd --- /dev/null +++ b/tslint.json @@ -0,0 +1,15 @@ +{ + "rules": { + "no-string-throw": true, + "no-unused-expression": true, + "no-duplicate-variable": true, + "curly": true, + "class-name": true, + "semicolon": [ + true, + "always" + ], + "triple-equals": true + }, + "defaultSeverity": "warning" +} \ No newline at end of file