Skip to content

Commit

Permalink
feat: impl instance controller sevice;
Browse files Browse the repository at this point in the history
  • Loading branch information
maslow committed Apr 29, 2022
1 parent 3caa044 commit ddb03f0
Show file tree
Hide file tree
Showing 19 changed files with 278 additions and 846 deletions.
34 changes: 34 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,40 @@ services:
networks:
- laf_shared_network

instance-controller:
# image: node:16-alpine
build: ./packages/instance-controller
user: root
working_dir: /app
environment:
SCHEDULER_INTERVAL: 1000
SYS_DB_URI: mongodb://my_user:password123@mongo:27017/?authSource=laf-sys&replicaSet=laf&writeConcern=majority
APP_DB_URI: mongodb://root:password123@mongo:27017/?authSource=admin&replicaSet=laf&writeConcern=majority
SHARED_NETWORK: laf_shared_network
LOG_LEVEL: debug
SERVICE_DRIVER: docker
APP_SERVICE_DEPLOY_HOST: local-dev.host:8080 # `*.local-dev.host` always resolved to 127.0.0.1, used to local development
APP_SERVICE_DEPLOY_URL_SCHEMA: 'http'
APP_SERVICE_ENV_NPM_INSTALL_FLAGS: '--registry=https://registry.npm.taobao.org --no-audit --no-fund'
MINIO_ACCESS_KEY: minio-root-user
MINIO_ACCESS_SECRET: minio-root-password
MINIO_INTERNAL_ENDPOINT: http://oss:9000
MINIO_EXTERNAL_ENDPOINT: http://oss.local-dev.host:8080
MINIO_REGION_NAME: cn-default
DEBUG_BIND_HOST_APP_PATH: '${PWD}/packages/app-service'
SYSTEM_EXTENSION_APPID: '0000000000000000'
command: node ./dist/index.js
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./packages/instance-controller:/app
ports:
- "9000"
depends_on:
- mongo
restart: always
networks:
- laf_shared_network

volumes:
db-data:
oss-data:
Expand Down
29 changes: 1 addition & 28 deletions packages/instance-controller/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ dotenv.config()
export default class Config {

/**
* scheduler loop interval
* scheduler loop interval, in ms
*/
static get SCHEDULER_INTERVAL(): number {
const value = process.env.SCHEDULER_INTERVAL || '1000'
Expand Down Expand Up @@ -84,10 +84,6 @@ export default class Config {
return process.env.KUBE_NAMESPACE_OF_APP_SERVICES || 'laf'
}

static get KUBE_NAMESPACE_OF_SYS_SERVICES() {
return process.env.KUBE_NAMESPACE_OF_SYS_SERVICES || 'laf'
}

static get APP_SERVICE_ENV_NPM_INSTALL_FLAGS(): string {
return process.env.APP_SERVICE_ENV_NPM_INSTALL_FLAGS || ''
}
Expand All @@ -96,29 +92,6 @@ export default class Config {
return process.env.SYSTEM_EXTENSION_APPID || '0000000000000000'
}

/**
* The host to access the app service
* For example, if set this to `lafyun.com`, then you can access app service by format `[appid].lafyun.com`:
* - 7b0b318c-b96c-4cc5-b521-33d11bd16cde.lafyun.com
* - http://7b0b318c-b96c-4cc5-b521-33d11bd16cde.lafyun.com/file/public/33d11bd16cde.png
* - http://7b0b318c-b96c-4cc5-b521-33d11bd16cde.lafyun.com/FUNC_NAME
*
* You should resolve `*.lafyun.com` to your laf server ip, to support `[appid].lafyun.com` url.
* You can also provide the PORT, like `lafyun.com:8080`.
*/
static get APP_SERVICE_DEPLOY_HOST(): string {
return process.env.APP_SERVICE_DEPLOY_HOST ?? ''
}

/**
* The schema of app deployed url: `http` | `https`.
* Default value is `http`.
*/
static get APP_SERVICE_DEPLOY_URL_SCHEMA(): string {
return process.env.APP_SERVICE_DEPLOY_URL_SCHEMA ?? 'http'
}


/**
* Minio configuration
*/
Expand Down
3 changes: 3 additions & 0 deletions packages/instance-controller/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as express from 'express'
import Config from './config'
import { logger } from './support/logger'
import { DatabaseAgent } from './support/db'
import { start_schedular } from './scheduler'

DatabaseAgent.init(Config.SYS_DB_URI)

Expand All @@ -16,6 +17,8 @@ app.get('/healthz', (_req, res) => {
return res.status(200).send('ok')
})

start_schedular()

const server = app.listen(Config.PORT, () => logger.info(`listened on ${Config.PORT}`))

process.on('unhandledRejection', (reason, promise) => {
Expand Down
98 changes: 75 additions & 23 deletions packages/instance-controller/src/scheduler.ts
Original file line number Diff line number Diff line change
@@ -1,66 +1,118 @@
import Config from "./config"
import { getApplicationsInStatus, InstanceStatus, updateApplicationStatus } from "./support/application"
import { ApplicationInstanceOperator } from "./support/instance-operator"
import { InstanceOperator } from "./support/instance-operator"
import { logger } from "./support/logger"


export async function start_schedular() {
export function start_schedular() {
logger.info('start schedular loop')
setInterval(loop, Config.SCHEDULER_INTERVAL)
}

function loop() {
const tick = new Date()
logger.info('enter loop ' + tick)

handle_prepared_start(tick)
handle_starting(tick)
handle_prepared_stop(tick)
handle_prepared_stop(tick)
handle_stopping(tick)
handle_prepared_restart(tick)
handle_restarting_stopping(tick)
handle_restarting(tick)
}


async function handle_prepared_start(tick: Date) {
logger.info('processing `prepared_start` status with tick: ', tick)
async function handle_prepared_start(tick: any) {
const apps = await getApplicationsInStatus(InstanceStatus.PREPARED_START)
for (let app of apps) {
try {
const res = await ApplicationInstanceOperator.start(app)
if (res) await updateApplicationStatus(app.appid, app.status, InstanceStatus.STARTING)
const res = await InstanceOperator.start(app)
if (res) {
logger.info(tick, `update ${app.appid} status from 'PREPARED_START' to 'STARTING'`)
await updateApplicationStatus(app.appid, app.status, InstanceStatus.STARTING)
}
} catch (error) {
logger.error(`handle_prepared_start got error for app ${app.appid} with tick: ${tick}`)
logger.error(tick, `handle_prepared_start(${app.appid}) error: `, error)
}
}
}

async function handle_starting(tick: Date) {
logger.info('processing `starting` status with tick: ', tick)
async function handle_starting(tick: any) {
const apps = await getApplicationsInStatus(InstanceStatus.STARTING)
for (let app of apps) {
try {
// const res = await ApplicationInstanceOperator.start(app)
// if (res) await updateApplicationStatus(app.appid, app.status, InstanceStatus.STARTING)
const status = await InstanceOperator.status(app)
if (status === InstanceStatus.RUNNING) {
logger.info(tick, `update ${app.appid} status from 'STARTING' to 'RUNNING'`)
await updateApplicationStatus(app.appid, app.status, InstanceStatus.RUNNING)
}
} catch (error) {
logger.error(`handle_starting got error for app ${app.appid} with tick: ${tick}`)
logger.error(tick, `handle_starting(${app.appid}) error: `, error)
}
}
}

async function handle_prepared_stop(tick: Date) {
logger.info('processing `prepared_stop` status with tick: ', tick)
async function handle_prepared_stop(tick: any) {
const apps = await getApplicationsInStatus(InstanceStatus.PREPARED_STOP)
for (let app of apps) {
try {
const res = await InstanceOperator.stop(app)
if (res) {
logger.info(tick, `update ${app.appid} status from 'PREPARED_STOP' to 'STOPPING'`)
await updateApplicationStatus(app.appid, app.status, InstanceStatus.STOPPING)
}
} catch (error) {
logger.error(tick, `handle_prepared_stop(${app.appid}) error: `, error)
}
}
}


async function handle_stopping(tick: Date) {
logger.info('processing `stopping` status with tick: ', tick)
async function handle_stopping(tick: any) {
const apps = await getApplicationsInStatus(InstanceStatus.STOPPING)
for (let app of apps) {
try {
const status = await InstanceOperator.status(app)
if (status === InstanceStatus.STOPPED) {
logger.info(tick, `update ${app.appid} status from 'STOPPING' to 'STOPPED'`)
await updateApplicationStatus(app.appid, app.status, InstanceStatus.STOPPED)
}
} catch (error) {
logger.error(tick, `handle_stopping(${app.appid}) error: `, error)
}
}
}


async function handle_prepared_restart(tick: Date) {
logger.info('processing `prepared_restart` status with tick: ', tick)
async function handle_prepared_restart(tick: any) {
const apps = await getApplicationsInStatus(InstanceStatus.PREPARED_RESTART)
for (let app of apps) {
try {
const res = await InstanceOperator.stop(app)
if (res) {
logger.info(tick, `update ${app.appid} status from 'PREPARED_RESTART' to 'RESTARTING'`)
await updateApplicationStatus(app.appid, app.status, InstanceStatus.RESTARTING)
}
} catch (error) {
logger.error(tick, `handle_prepared_restart(${app.appid}) error: `, error)
}
}
}

async function handle_restarting_stopping(tick: Date) {
logger.info('processing `restarting:stopping` status with tick: ', tick)
async function handle_restarting(tick: any) {
const apps = await getApplicationsInStatus(InstanceStatus.RESTARTING)
for (let app of apps) {
try {
const status = await InstanceOperator.status(app)
if (status !== InstanceStatus.STOPPED) continue

logger.info(tick, `start stopped ${app.appid} for restarting`)
const res = await InstanceOperator.start(app)
if (res) {
logger.info(tick, `update ${app.appid} status from 'RESTARTING' to 'STARTING'`)
await updateApplicationStatus(app.appid, app.status, InstanceStatus.STARTING)
}
} catch (error) {
logger.error(tick, `handle_stopping(${app.appid}) error: `, error)
}
}
}
4 changes: 3 additions & 1 deletion packages/instance-controller/src/support/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,9 @@ export async function updateApplicationStatus(appid: string, from: InstanceStatu
appid: appid,
status: from,
}, {
status: to
$set: {
status: to
}
})

return r.modifiedCount
Expand Down
40 changes: 27 additions & 13 deletions packages/instance-controller/src/support/instance-docker-driver.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as Docker from 'dockerode'
import { IApplicationData, getApplicationDbUri } from './application'
import { IApplicationData, getApplicationDbUri, InstanceStatus } from './application'
import Config from '../config'
import { logger } from './logger'
import { InstanceDriverInterface } from './instance-operator'
Expand All @@ -19,7 +19,7 @@ export class DockerContainerDriver implements InstanceDriverInterface {
* Get name of service
* @param app
*/
getName(app: IApplicationData): string {
public getName(app: IApplicationData): string {
return `app-${app.appid}`
}

Expand All @@ -28,31 +28,31 @@ export class DockerContainerDriver implements InstanceDriverInterface {
* @param app
* @returns the container id
*/
async startService(app: IApplicationData) {
public async create(app: IApplicationData) {
let container = this.getContainer(app)
const info = await this.info(app)
const info = await this.inspect(app)
if (!info) {
container = await this.createService(app)
}

if (info?.State?.Running || info?.State?.Restarting) {
return container.id
if (info?.State?.Running) {
return true
}

await container.start()
logger.debug(`start container ${container.id} of app ${app.appid}`)

return container.id
return true
}

/**
* Remove application service
* @param app
*/
async removeService(app: IApplicationData) {
const info = await this.info(app)
public async remove(app: IApplicationData) {
const info = await this.inspect(app)
if (!info) {
return
return true
}

const container = this.getContainer(app)
Expand All @@ -67,15 +67,14 @@ export class DockerContainerDriver implements InstanceDriverInterface {
await container.remove()
logger.debug(`stop & remove container ${container.id} of app ${app.appid}`)

return container.id
return true
}

/**
* Get container info
* @param container
* @returns return null if container not exists
*/
async info(app: IApplicationData): Promise<Docker.ContainerInspectInfo> {
public async inspect(app: IApplicationData): Promise<Docker.ContainerInspectInfo> {
try {
const container = this.getContainer(app)
const info = await container.inspect()
Expand All @@ -88,6 +87,21 @@ export class DockerContainerDriver implements InstanceDriverInterface {
}
}

/**
* Get instance status
* @param app
* @returns
*/
public async status(app: IApplicationData): Promise<InstanceStatus> {
const res = await this.inspect(app)
if (!res) return InstanceStatus.STOPPED
const state = res?.State
if (state.Running)
return InstanceStatus.RUNNING

return InstanceStatus.STOPPING
}

/**
* Create application service
* @param app
Expand Down
Loading

0 comments on commit ddb03f0

Please sign in to comment.