diff --git a/packages/docker-api/src/api/opts/common.ts b/packages/docker-api/src/api/opts/common.ts index c36a55a..2f45f84 100644 --- a/packages/docker-api/src/api/opts/common.ts +++ b/packages/docker-api/src/api/opts/common.ts @@ -7,4 +7,13 @@ export declare namespace query { [key: string]: 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/container.ts b/packages/docker-api/src/api/opts/container.ts index 7c651bb..0307c04 100644 --- a/packages/docker-api/src/api/opts/container.ts +++ b/packages/docker-api/src/api/opts/container.ts @@ -6,14 +6,7 @@ export declare namespace container { limit?: number; size?: boolean; } - export interface LogsOpts { - follow?: boolean; - stdout?: boolean; - stderr?: boolean; - since?: number; - until?: number; - timestamps?: boolean; - tail?: number | "all"; + export interface LogsOpts extends common.LogsOpts { } export namespace exec { export interface Create { diff --git a/packages/docker-api/src/api/opts/index.ts b/packages/docker-api/src/api/opts/index.ts index 91a174d..3aabf67 100644 --- a/packages/docker-api/src/api/opts/index.ts +++ b/packages/docker-api/src/api/opts/index.ts @@ -1,3 +1,4 @@ +export * from './task' export * from './swarm' export * from './common' export * from './config' diff --git a/packages/docker-api/src/api/opts/service.ts b/packages/docker-api/src/api/opts/service.ts index 4bfef3e..f1a5832 100644 --- a/packages/docker-api/src/api/opts/service.ts +++ b/packages/docker-api/src/api/opts/service.ts @@ -2,16 +2,8 @@ import * as common from './common' export declare namespace service { export interface FilterOpt { - + } - export interface LogsOpts { - details?: boolean; - follow?: boolean; - stdout?: boolean; - stderr?: boolean; - since?: number; - until?: number; - timestamps?: boolean; - tail?: number | "all"; + export interface LogsOpts extends common.LogsOpts { } } \ No newline at end of file diff --git a/packages/docker-api/src/api/opts/task.ts b/packages/docker-api/src/api/opts/task.ts new file mode 100644 index 0000000..50813bb --- /dev/null +++ b/packages/docker-api/src/api/opts/task.ts @@ -0,0 +1,9 @@ +import * as common from './common' + +export declare namespace task { + export interface FilterOpt { + + } + export interface LogsOpts extends common.LogsOpts { + } +} \ No newline at end of file diff --git a/packages/docker-api/src/client/api.ts b/packages/docker-api/src/client/api.ts new file mode 100644 index 0000000..d4164af --- /dev/null +++ b/packages/docker-api/src/client/api.ts @@ -0,0 +1,92 @@ +import { ServerResponse } from 'http' +import axios, { AxiosInstance, AxiosRequestConfig, Method, AxiosResponse } from 'axios'; +export interface DockerApiClient { + get(path: string, data?: object): Promise; + post(path: string, data?: object): Promise; + del(path: string, data?: object): Promise; + stream(path: string, data?: object): Promise; + getUri(path: string, data?: object): string; +} + +class DefaultDockerApiClient implements DockerApiClient { + private api: AxiosInstance; + constructor(host: string = process.env.DOCKER_HOST) { + const instanceConfig: AxiosRequestConfig = { + headers: { + 'Content-Type': 'application/json' + } + } + if (host.startsWith("/")) { + instanceConfig.socketPath = host + } else { + instanceConfig.baseURL = 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 del(path: string, data?: object): Promise { + return await this.handle("DELETE", path, { params: data }); + } + + async stream(path: string, data?: object): Promise { + return await this.handle("GET", path, { params: data, responseType: "stream" }); + } + + getUri(path: string, data?: object): string { + return this.api.getUri({ + url: path, + params: data + }) + } + + async handle(method: Method, path: string, reqConfig?: AxiosRequestConfig): Promise { + let config: AxiosRequestConfig = { + method, + url: path, + ...reqConfig + }; + let startTime = Date.now(); + 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 (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 +=======================================`); + } + } + } + +} + +export default new DefaultDockerApiClient(); diff --git a/packages/docker-api/src/client/config.ts b/packages/docker-api/src/client/config.ts index 34656af..fc7932d 100644 --- a/packages/docker-api/src/client/config.ts +++ b/packages/docker-api/src/client/config.ts @@ -1,18 +1,20 @@ -import * as api from '../utils/api' import * as opts from '../api/opts' import * as types from '../api/types' import * as filterUtil from '../api/opts/filter' +import { DockerApiClient } from './api'; -export namespace config { - export async function list(filter?: opts.config.FilterOpt) { - return await api.get('/configs', { +export class Config { + constructor(public api: DockerApiClient) { + } + list(filter?: opts.config.FilterOpt) { + return this.api.get('/configs', { filters: filterUtil.toJSON(filter) }); } - export async function inspect(id: string) { - return await api.get(`/configs/${id}`) + inspect(id: string) { + return this.api.get(`/configs/${id}`) } - export async function create() { - return await api.post<{}>('/configs/create') + create() { + return this.api.post<{}>('/configs/create') } } diff --git a/packages/docker-api/src/client/container.ts b/packages/docker-api/src/client/container.ts index 79f66c2..d9d0443 100644 --- a/packages/docker-api/src/client/container.ts +++ b/packages/docker-api/src/client/container.ts @@ -1,22 +1,31 @@ -import * as api from '../utils/api'; import * as opts from '../api/opts' import * as types from '../api/types' import * as http from 'http' +import { DockerApiClient } from './api'; -export namespace container { - export async function list(filters?: opts.container.ListOpts) { - return await api.get('/containers/json', filters) +export class Container { + private execClient: Exec; + constructor(public api: DockerApiClient) { + this.execClient = new Exec(api); } - export async function inspect(id: string, query: { size: boolean } = { size: false }) { - return await api.get(`/containers/${id}/json`, query); + get exec() { + return this.execClient; } - export function prune() { - return api.post('/containers/prune'); + async list(filters?: opts.container.ListOpts) { + return await this.api.get('/containers/json', filters) } - export async function logs(id: string, opts: opts.container.LogsOpts = {}): Promise { + async inspect(id: string, query: { size: boolean } = { size: false }) { + return await this.api.get(`/containers/${id}/json`, query); + } + + prune() { + return this.api.post('/containers/prune'); + } + + async logs(id: string, opts: opts.container.LogsOpts = {}): Promise { let data = { follow: true, stdout: true, @@ -24,31 +33,32 @@ export namespace container { tail: 10, ...opts } - return await api.stream(`/containers/${id}/logs`, data); - } - - export namespace exec { - export function create(id: string, opts: opts.container.exec.Create = {}): Promise { - let request = { - AttachStdin: true, - AttachStdout: true, - AttachStderr: true, - DetachKeys: 'ctrl-d', - Tty: true, - Cmd: '/bin/sh', - ...opts - } - request.AttachStderr = true - return api.post(`/containers/${id}/exec`, request) - } - export function start(id: string, opts: opts.container.exec.Start = {}) { - return api.post(`/exec/${id}/start`, opts) - } - export function resize(id: string, opts: opts.container.exec.Resize = {}) { - return api.post(`/exec/${id}/resize`, opts) - } - export function inspect(id: string) { - return api.get(`/exec/${id}/json`) - } + return await this.api.stream(`/containers/${id}/logs`, data); } } + +class Exec { + constructor(public api: DockerApiClient) { } + create(id: string, opts: opts.container.exec.Create = {}): Promise { + let request = { + AttachStdin: true, + AttachStdout: true, + AttachStderr: true, + DetachKeys: 'ctrl-d', + Tty: true, + Cmd: '/bin/sh', + ...opts + } + request.AttachStderr = true + return this.api.post(`/containers/${id}/exec`, request) + } + start(id: string, opts: opts.container.exec.Start = {}) { + return this.api.post(`/exec/${id}/start`, opts) + } + resize(id: string, opts: opts.container.exec.Resize = {}) { + return this.api.post(`/exec/${id}/resize`, opts) + } + inspect(id: string) { + return this.api.get(`/exec/${id}/json`) + } +} \ No newline at end of file diff --git a/packages/docker-api/src/client/image.ts b/packages/docker-api/src/client/image.ts index 4489527..037d5e5 100644 --- a/packages/docker-api/src/client/image.ts +++ b/packages/docker-api/src/client/image.ts @@ -1,8 +1,10 @@ -import * as api from '../utils/api' import * as types from '../api/types' +import { DockerApiClient } from './api'; -export namespace image { - export async function list() { - return await api.get('/images/json'); +export class Image { + constructor(public api: DockerApiClient) { + } + list() { + return this.api.get('/images/json'); } } diff --git a/packages/docker-api/src/client/index.ts b/packages/docker-api/src/client/index.ts index 633854c..16f504b 100644 --- a/packages/docker-api/src/client/index.ts +++ b/packages/docker-api/src/client/index.ts @@ -1,10 +1,67 @@ -export * from './node' -export * from './task' -export * from './image' -export * from './swarm' -export * from './config' -export * from './volume' -export * from './system' -export * from './network' -export * from './service' -export * from './container' +import api, { DockerApiClient } from './api' +import { Container } from './container' +import { Service } from './service' +import { Node } from './node' +import { Task } from './task' +import { Image } from './image' +import { Swarm } from './swarm' +import { Config } from './config' +import { Volume } from './volume' +import { System } from './system' +import { Network } from './network' +export class DockerClient { + private containerInstance: Container; + private serviceInstance: Service; + private nodeInstance: Node; + private taskInstance: Task; + private imageInstance: Image; + private swarmInstance: Swarm; + private configInstance: Config; + private volumeInstance: Volume; + private systemInstance: System; + private networkInstance: Network; + constructor(apiClient: DockerApiClient = api) { + this.containerInstance = new Container(apiClient) + this.serviceInstance = new Service(apiClient) + this.nodeInstance = new Node(apiClient) + this.taskInstance = new Task(apiClient) + this.imageInstance = new Image(apiClient) + this.swarmInstance = new Swarm(apiClient) + this.configInstance = new Config(apiClient) + this.volumeInstance = new Volume(apiClient) + this.systemInstance = new System(apiClient) + this.networkInstance = new Network(apiClient) + } + get container() { + return this.containerInstance; + } + get service() { + return this.serviceInstance; + } + get node() { + return this.nodeInstance; + } + get task() { + return this.taskInstance; + } + get image() { + return this.imageInstance; + } + get swarm() { + return this.swarmInstance; + } + get config() { + return this.configInstance; + } + get volume() { + return this.volumeInstance; + } + get system() { + return this.systemInstance; + } + get network() { + return this.networkInstance; + } +} + +export default new DockerClient(); \ No newline at end of file diff --git a/packages/docker-api/src/client/network.ts b/packages/docker-api/src/client/network.ts index 4a22a2a..f12cae1 100644 --- a/packages/docker-api/src/client/network.ts +++ b/packages/docker-api/src/client/network.ts @@ -1,9 +1,11 @@ -import * as api from '../utils/api' import * as opts from '../api/opts' import * as types from '../api/types' +import { DockerApiClient } from './api' -export namespace network { - export async function list(opts?: opts.network.ListOpts) { - return await api.get('/networks', opts) +export class Network { + constructor(public api: DockerApiClient) { + } + list(opts?: opts.network.ListOpts) { + return this.api.get('/networks', opts) } } diff --git a/packages/docker-api/src/client/node.ts b/packages/docker-api/src/client/node.ts index b0057bb..2a72890 100644 --- a/packages/docker-api/src/client/node.ts +++ b/packages/docker-api/src/client/node.ts @@ -1,8 +1,10 @@ -import * as api from '../utils/api' import * as types from '../api/types' +import { DockerApiClient } from './api'; -export namespace node { - export async function list() { - return await api.get('/nodes'); +export class Node { + constructor(public api: DockerApiClient) { + } + list() { + return this.api.get('/nodes'); } } diff --git a/packages/docker-api/src/client/service.ts b/packages/docker-api/src/client/service.ts index 68a7ccd..53268df 100644 --- a/packages/docker-api/src/client/service.ts +++ b/packages/docker-api/src/client/service.ts @@ -1,25 +1,27 @@ -import * as api from '../utils/api'; import * as opts from '../api/opts'; import * as types from '../api/types'; import * as filterUtil from '../api/opts/filter' import * as http from 'http' +import { DockerApiClient } from './api'; -export namespace service { - export async function list(filter?: opts.service.FilterOpt) { - return await api.get('/services', { +export class Service { + constructor(public api: DockerApiClient) { + } + async list(filter?: opts.service.FilterOpt) { + return await this.api.get('/services', { filters: filterUtil.toJSON(filter) }); } - export async function create() { + async create() { } - export async function inspect(id: string, query: { insertDefaults: boolean } = { insertDefaults: false }) { - return await api.get(`/services/${id}`, query); + async inspect(id: string, query: { insertDefaults: boolean } = { insertDefaults: false }) { + return await this.api.get(`/services/${id}`, query); } - export async function update(id: string, query: { version: number, registryAuthFrom?: string, rollback?: string }, data: any) { - return await api.post(api.getUri(`/services/${id}/update`, query), data) + async update(id: string, query: { version: number, registryAuthFrom?: string, rollback?: string }, data: any) { + return await this.api.post(this.api.getUri(`/services/${id}/update`, query), data) } - export async function logs(id: string, opts: opts.service.LogsOpts = {}): Promise { + async logs(id: string, opts: opts.service.LogsOpts = {}): Promise { let data = { follow: true, stdout: true, @@ -27,6 +29,6 @@ export namespace service { tail: 10, ...opts } - return await api.stream(`/services/${id}/logs`, data); + return await this.api.stream(`/services/${id}/logs`, data); } } diff --git a/packages/docker-api/src/client/swarm.ts b/packages/docker-api/src/client/swarm.ts index 6ad3d8a..5fe948d 100644 --- a/packages/docker-api/src/client/swarm.ts +++ b/packages/docker-api/src/client/swarm.ts @@ -1,29 +1,26 @@ -import * as api from '../utils/api' import * as opts from '../api/opts' import * as types from '../api/types' +import { DockerApiClient } from './api'; -export namespace swarm { - export async function inspect() { - return await api.get('/swarm'); +export class Swarm { + constructor(public client: DockerApiClient) { } - - export async function init(opts: opts.swarm.InitOpts) { - return await api.post('/swarm/init', opts); + inspect() { + return this.client.get('/swarm'); } - - export async function join(opts: opts.swarm.JoinOpts) { - return await api.post('/swarm/join', opts); + init(opts: opts.swarm.InitOpts) { + return this.client.post('/swarm/init', opts); } - - export async function leave(force: boolean = false) { - return await api.post(`/swarm/leave?force=${force}`); + join(opts: opts.swarm.JoinOpts) { + return this.client.post('/swarm/join', opts); } - - export async function unlockkey() { - return await api.get(`/swarm/unlockkey`); + leave(force: boolean = false) { + return this.client.post(`/swarm/leave?force=${force}`); } - - export async function unlock(opts: opts.swarm.UnlockOpts) { - return await api.post(`/swarm/unlockkey`, opts); + unlockkey() { + return this.client.get(`/swarm/unlockkey`); + } + unlock(opts: opts.swarm.UnlockOpts) { + return this.client.post(`/swarm/unlockkey`, 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 index 4eae7a6..48de6f4 100644 --- a/packages/docker-api/src/client/system.ts +++ b/packages/docker-api/src/client/system.ts @@ -1,16 +1,18 @@ -import * as api from '../utils/api' import * as types from '../api/types' +import { DockerApiClient } from './api'; -export namespace system { - export async function info() { - return await api.get('/info'); +export class System { + constructor(public api: DockerApiClient) { + } + info() { + return this.api.get('/info'); } - export async function version() { - return await api.get('/version'); + version() { + return this.api.get('/version'); } - export async function events() { - return await api.stream('/events'); + events() { + return this.api.stream('/events'); } } diff --git a/packages/docker-api/src/client/task.ts b/packages/docker-api/src/client/task.ts index 69a9358..d0a120b 100644 --- a/packages/docker-api/src/client/task.ts +++ b/packages/docker-api/src/client/task.ts @@ -1,8 +1,22 @@ -import * as api from '../utils/api' +import * as opts from '../api/opts' import * as types from '../api/types' +import * as http from 'http' +import { DockerApiClient } from './api'; -export namespace task { - export async function list() { - return await api.get('/tasks'); +export class Task { + constructor(public api: DockerApiClient) { + } + list() { + return this.api.get('/tasks'); + } + logs(id: string, opts: opts.task.LogsOpts = {}): Promise { + let data = { + follow: true, + stdout: true, + stderr: true, + tail: 10, + ...opts + } + return this.api.stream(`/services/${id}/logs`, data); } } \ No newline at end of file diff --git a/packages/docker-api/src/client/volume.ts b/packages/docker-api/src/client/volume.ts index 8f37f83..1ac6823 100644 --- a/packages/docker-api/src/client/volume.ts +++ b/packages/docker-api/src/client/volume.ts @@ -1,8 +1,10 @@ -import * as api from '../utils/api' import * as types from '../api/types' +import { DockerApiClient } from './api'; -export namespace volume { - export async function list() { - return await api.get('/volumes'); +export class Volume { + constructor(public api: DockerApiClient) { + } + list() { + return this.api.get('/volumes'); } } \ No newline at end of file diff --git a/packages/docker-api/src/index.ts b/packages/docker-api/src/index.ts index 7eec814..aa28bd6 100644 --- a/packages/docker-api/src/index.ts +++ b/packages/docker-api/src/index.ts @@ -1 +1,2 @@ -export * from './client' \ No newline at end of file +import client from './client' +export default 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 index ee3870d..5a05307 100644 --- a/packages/docker-api/src/utils/api.ts +++ b/packages/docker-api/src/utils/api.ts @@ -1,5 +1,5 @@ import * as http from 'http' -import axios, { AxiosResponse, AxiosRequestConfig, Method, AxiosInstance } from 'axios' +import { AxiosResponse, AxiosRequestConfig, Method, AxiosInstance } from 'axios' let api: AxiosInstance; @@ -66,19 +66,3 @@ HANDLE TIME : ${Date.now() - startTime}ms } } } - -function init() { - const instanceConfig: AxiosRequestConfig = { - headers: { - 'Content-Type': 'application/json' - } - } - if (process.env.DOCKER_HOST.startsWith("/")) { - instanceConfig.socketPath = process.env.DOCKER_HOST - } else { - instanceConfig.baseURL = process.env.DOCKER_HOST - } - api = axios.create(instanceConfig) -} - -init();