Commit 010abfc8 by chalermpong

add pull image and fix start-stop

parent 0e5d6fa0
{
"services": [
{
"image": "testcontainers/helloworld",
"restart": "always",
"name": "my_hello_world_container",
"ports": [
{ "containerPort": "8080", "hostPort": "9080" }
],
"volumes": [
{
"source": "/home/bakukan2008/npm",
"target": "/usr/src/app"
},
{
"source": "/var/run/docker.sock",
"target": "/var/run/docker.sock"
}
],
"environment": [
{
"key": "ENV_KEY1",
"value": "ENV_VALUE1"
},
{
"key": "ENV_KEY2",
"value": "ENV_VALUE2"
}
],
"network": "bridge",
"logging": {
"Config": {
"max-size": "10m",
"max-file": "3"
},
"Type": "json-file"
}
},
{
"image": "2e8d6130b913",
"restart": "always",
"name": "myflowengine",
"ports": [
{ "containerPort": "80", "hostPort": "41880" },
{ "containerPort": "1883", "hostPort": "4080" }
],
"volumes": [
{
"source": "/mongo/data",
"target": "/data/db"
}
],
"environment": [
{
"key": "MONGO_INITDB_ROOT_USERNAME",
"value": "admin"
},
{
"key": "MONGO_INITDB_ROOT_PASSWORD",
"value": "admin_password"
}
],
"network": "flowstack",
"logging": {
"Config": {
"max-size": "10m",
"max-file": "3"
},
"Type": "json-file"
}
}
]
}
{
"serviceNames": ["2a2a514409d6", "my_hello_world_container"]
}
\ No newline at end of file
{
"services": [
{
"image": "testcontainers/helloworld",
"name": "my_hello_world_container",
"port": "8080",
"hostPort": "9080",
"volumes": [
{
"source": "/home/bakukan2008/npm",
"target": "/usr/src/app"
},
{
"source": "/var/run/docker.sock",
"target": "/var/run/docker.sock"
}
],
"envs": [
{
"key": "ENV_KEY1",
"value": "ENV_VALUE1"
},
{
"key": "ENV_KEY2",
"value": "ENV_VALUE2"
}
],
"network": "flowstack",
"logging": {
"Config": {
"max-size": "10m",
"max-file": "3"
},
"Type": "json-file"
}
},
{
"image": "2e8d6130b913",
"name": "flowengine",
"port": "80",
"hostPort": "41880",
"volumes": [
{
"source": "/flowtest/data",
"target": "/data/db"
}
],
"envs": [
{
"key": "ROOT_USERNAME",
"value": "admin"
},
{
"key": "ROOT_PASSWORD",
"value": "admin_password"
}
],
"network": "flowstack",
"logging": {
"Config": {
"max-size": "10m",
"max-file": "3"
},
"Type": "json-file"
}
}
]
}
const express = require('express');
const Docker = require('dockerode');
const DockerodeCompose = require('dockerode-compose');
const app = express();
var docker = new Docker({socketPath: '/var/run/docker.sock'});
const yaml = require('js-yaml');
const fs = require('fs');
const bodyParser = require('body-parser');
const dockerCLI = require('docker-cli-js');
const DockerOptions = dockerCLI.Options;
const DockerCli = dockerCLI.Docker;
const app = express();
const docker = new Docker({socketPath: '/var/run/docker.sock'});
const dockerCli = new DockerCli();
// Middleware เพื่อตั้งค่า HTTP headers เพื่อรับ JSON
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.post('/api/docker/login', (req, res) => {
const { username, password } = req.body;
// กำหนดคำสั่ง login
const loginCommand = `login -u ${username} -p ${password}`;
// ทำการ login
dockerCli.command(loginCommand).then(data => {
console.log('Login สำเร็จ:', data);
res.status(200).json({ message: 'Login สำเร็จ' });
}).catch(err => {
console.error('เกิดข้อผิดพลาดในการ login:', err);
res.status(500).json({ error: 'เกิดข้อผิดพลาดในการ login' });
});
});
app.post('/api/docker/pull', async (req, res) => {
try {
const { imageName } = req.body;
if (!imageName) {
return res.status(400).json({ error: 'กรุณาระบุชื่อของ Docker image' });
}
const stream = await docker.pull(imageName);
stream.on('data', (chunk) => {
const data = JSON.parse(chunk.toString());
console.log(data); // Log progress data (optional)
});
stream.on('end', () => {
console.log(`เสร็จสิ้นการดึง Docker image: ${imageName}`);
res.status(200).json({ message: `เสร็จสิ้นการดึง Docker image: ${imageName}` });
});
} catch (error) {
console.error('เกิดข้อผิดพลาดในการดึง Docker image:', error);
res.status(500).json({ error: 'เกิดข้อผิดพลาดในการดึง Docker image' });
}
});
app.post('/api/docker/pull-image', async (req, res) => {
const { imageName } = req.body;
// ตรวจสอบว่าชื่อ Docker image ถูกส่งมาหรือไม่
if (!imageName) {
return res.status(400).json({ error: 'กรุณาระบุชื่อ Docker image' });
}
// ตรวจสอบว่าชื่อ Docker image เป็น remote หรือไม่
docker.getImage(imageName).inspect((err, data) => {
if (err) {
console.error('เกิดข้อผิดพลาดในการตรวจสอบ Docker image:', err);
return res.status(500).json({ error: 'เกิดข้อผิดพลาดในการตรวจสอบ Docker image' });
}
// ถ้า Docker image เป็น remote image
if (data && data.RepoTags) {
console.log('Docker image เป็น remote image');
// ดึง Docker image จาก remote repository
docker.pull(imageName, (err, stream) => {
if (err) {
console.error('เกิดข้อผิดพลาดในการดึง Docker image:', err);
return res.status(500).json({ error: 'เกิดข้อผิดพลาดในการดึง Docker image' });
}
// ติดตามความคืบหน้าของการดึง Docker image
docker.modem.followProgress(stream, (err, output) => {
if (err) {
console.error('เกิดข้อผิดพลาดในการติดตามความคืบหน้า:', err);
return res.status(500).json({ error: 'เกิดข้อผิดพลาดในการติดตามความคืบหน้า' });
}
console.log('การดึง Docker image เสร็จสมบูรณ์');
return res.status(200).json({ message: 'การดึง Docker image เสร็จสมบูรณ์' });
});
});
} else {
console.log('Docker image เป็น local image');
return res.status(400).json({ error: 'Docker image เป็น local image' });
}
});
});
app.get('/api/docker/images', async (req, res) => {
......@@ -27,78 +125,102 @@ app.get('/api/docker/images', async (req, res) => {
app.use(express.json());
app.post('/api/docker/start-compose', (req, res) => {
app.post('/api/docker/start-compose', async (req, res) => {
const { services } = req.body;
const containerNames = [];
const errors = [];
// ตรวจสอบว่ามีข้อมูลใน services หรือไม่
try {
if (!services || services.length === 0) {
return res.status(400).json({ error: 'กรุณาระบุข้อมูล service ให้ถูกต้อง' });
throw new Error('กรุณาระบุข้อมูล service ให้ถูกต้อง');
}
// สร้าง container สำหรับแต่ละ service
services.forEach((service) => {
const { image, name, port, hostPort, volumes, envs, network, logging } = service;
for (const service of services) {
const { image, name, ports, volumes, environment, network, logging, restart } = service;
// ตรวจสอบข้อมูลที่จำเป็นสำหรับการสร้าง container
if (!image || !name || !port || !hostPort || !volumes || !envs || !network || !logging) {
return res.status(400).json({ error: 'กรุณาระบุข้อมูลที่จำเป็นให้ครบถ้วน' });
if (!image || !name || !ports || !volumes || !environment || !network || !logging || !restart) {
errors.push(`กรุณาระบุข้อมูล service ${name} ให้ถูกต้องทั้งหมด`);
}
// ตรวจสอบว่ามี Docker container ที่มีชื่อซ้ำกันอยู่แล้วหรือไม่
docker.getContainer(name).inspect((err, data) => {
if (!err) {
return res.status(400).json({ error: `มี Docker container ที่มีชื่อ ${name} ซ้ำกันอยู่แล้ว` });
const existingContainer = await docker.getContainer(name).inspect().catch(() => null);
if (existingContainer) {
errors.push(`มี Docker container ที่มีชื่อ ${name} ซ้ำกันอยู่แล้ว`);
}
}
if (errors.length > 0) {
return res.status(400).json({ errors });
}
for (const service of services) {
const { image, name, ports, volumes, environment, network, logging, restart } = service;
const portBindings = ports.reduce((acc, port) => {
acc[`${port.containerPort}/tcp`] = [{ 'HostPort': port.hostPort }];
return acc;
}, {});
const exposePorts = ports.reduce((acc, port) => {
acc[`${port.containerPort}/tcp`] = {};
return acc;
}, {});
const binds = volumes.map(volume => `${volume.source}:${volume.target}`);
const envVariables = envs.map(env => `${env.key}=${env.value}`);
const envVariables = environment.map(env => `${env.key}=${env.value}`);
// สร้าง Docker container options
const options = {
Image: image,
name: name,
Env: envVariables,
ExposedPorts: { [`${port}/tcp`]: {} },
ExposedPorts: exposePorts,
Hostconfig: {
PortBindings: { [`${port}/tcp`]: [{ 'HostPort': hostPort }] },
PortBindings: portBindings,
Binds: binds,
NetworkMode: network || 'bridge',
LogConfig: logging || { 'Driver': 'json-file' },
// LogConfig: {
// Config: {
// "max-file": logging.options.max-file,
// "max-size": logging.options.max-size
// },
// Type: logging.driver
// },
LogConfig: logging,
NetworkMode: network,
restartPolicy: {Name: restart}
}
};
// สร้าง Docker container
docker.createContainer(options, (err, container) => {
if (err) {
console.error('Error:', err);
return res.status(500).json({ error: `เกิดข้อผิดพลาดในการสร้าง Docker container สำหรับ ${name}` });
}
// เริ่มต้น container
container.start((err) => {
if (err) {
console.error('Error:', err);
return res.status(500).json({ error: `เกิดข้อผิดพลาดในการเริ่มต้น Docker container สำหรับ ${name}` });
}
const container = await docker.createContainer(options);
await container.start();
console.log(`Container ${name} started successfully`);
containerNames.push(name);
// หากเป็นการสร้าง container สำเร็จให้ส่งค่า status 200 พร้อมกับชื่อ container กลับไป
if (containerNames.length === services.length) {
}
res.status(200).json({ message: 'การสร้าง Docker container เสร็จสิ้น', containers: containerNames });
} catch (error) {
console.error('Error:', error);
res.status(500).json({ error: error.message });
}
});
app.post('/api/docker/stop-compose', async (req, res) => {
const { serviceNames } = req.body;
// ตรวจสอบว่ามีข้อมูล serviceNames หรือไม่
if (!serviceNames || serviceNames.length === 0) {
return res.status(400).json({ error: 'กรุณาระบุชื่อ service ให้ถูกต้อง' });
}
try {
// สำหรับแต่ละ service ใน serviceNames
for (const serviceName of serviceNames) {
// หยุด container
await docker.getContainer(serviceName).stop();
console.log(`Container ${serviceName} stopped successfully`);
// ลบ container
await docker.getContainer(serviceName).remove();
console.log(`Container ${serviceName} removed successfully`);
}
res.status(200).json({ message: 'หยุดและลบ container เสร็จสิ้น' });
} catch (error) {
console.error('Error:', error);
res.status(500).json({ error: 'เกิดข้อผิดพลาดในการหยุดและลบ container' });
}
});
});
});
});
});
......
const Docker = require('dockerode');
// var dockerCLI = require('docker-cli-js');
// var DockerOptions = dockerCLI.Options;
// var DockerCli = dockerCLI.Docker;
// สร้าง instance ของ Dockerode
const docker = new Docker();
// เรียกใช้งาน Docker API เพื่อดึงข้อมูลสถิติของ container ทั้งหมด
docker.listContainers((err, containers) => {
// var dockerCli = new DockerCli();
// const username = 'myusername';
// const password = 'mypassword';
// const loginCommand = `login -u ${username} -p ${password}`;
// // ทำการ login
// dockerCli.command(loginCommand).then(data => {
// console.log('Login สำเร็จ:', data);
// }).catch(err => {
// console.error('เกิดข้อผิดพลาดในการ login:', err);
// });
const Docker = require('dockerode');
const docker = new Docker();
docker.listImages((err, images) => {
if (err) {
console.error('Error:', err);
console.error('เกิดข้อผิดพลาดในการดึงรายการ Docker images:', err);
} else {
// วนลูปผ่าน container ทั้งหมด
containers.forEach((containerInfo) => {
const containerId = containerInfo.Id;
// ดึงข้อมูลสถิติของ container นั้น
docker.getContainer(containerId).stats((err, stream) => {
if (err) {
console.error('Error:', err);
console.log('รายการ Docker images ทั้งหมด:', images);
// ตรวจสอบว่า Docker image ที่ต้องการอยู่ใน Repository หรือไม่
const imageName = 'harbor.nexpie.com/flowstack/mongo-gui:1.0.0-4';
const imageExists = images.some(image => image.RepoTags.includes(imageName));
if (imageExists) {
console.log(`Docker image '${imageName}' อยู่ใน Repository`);
} else {
stream.on('data', (chunk) => {
const stats = JSON.parse(chunk.toString('utf8'));
// แสดงข้อมูล CPU และ Memory
console.log('Container:', containerInfo.Names[0]);
console.log('CPU Usage:', stats.cpu_stats.cpu_usage.cpu_percent);
console.log('Memory Usage:', stats.memory_stats.usage);
});
stream.on('error', (err) => {
console.error('Stream error:', err);
});
console.log(`Docker image '${imageName}' ไม่อยู่ใน Repository`);
}
});
});
}
});
});
\ No newline at end of file
......@@ -5,6 +5,9 @@
"packages": {
"": {
"dependencies": {
"body-parser": "^1.20.2",
"docker-cli-js": "^2.10.0",
"docker-modem": "^5.0.3",
"dockerode": "^4.0.2",
"dockerode-compose": "^1.4.0",
"express": "^4.19.2",
......@@ -18,6 +21,45 @@
"resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz",
"integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q=="
},
"node_modules/@types/blue-tape": {
"version": "0.1.36",
"resolved": "https://registry.npmjs.org/@types/blue-tape/-/blue-tape-0.1.36.tgz",
"integrity": "sha512-t2nyn0z6Q5PM/SA8suQEVfV1QsJ37XFlR7zEayviC/EdrqWq8JiKzE7BtwBcO28wFZ4bKQeD7MeJAj1B35KYAA==",
"dependencies": {
"@types/node": "*",
"@types/tape": "*"
}
},
"node_modules/@types/lodash": {
"version": "4.14.119",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.119.tgz",
"integrity": "sha512-Z3TNyBL8Vd/M9D9Ms2S3LmFq2sSMzahodD6rCS9V2N44HUMINb75jNkSuwAx7eo2ufqTdfOdtGQpNbieUjPQmw=="
},
"node_modules/@types/node": {
"version": "20.12.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz",
"integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==",
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@types/tape": {
"version": "5.6.4",
"resolved": "https://registry.npmjs.org/@types/tape/-/tape-5.6.4.tgz",
"integrity": "sha512-EmL4fJpZyByNCkupLLcJhneqcnT+rQUG5fWKNCsZyBK1x7nUuDTwwEerc4biEMZgvSK2+FXr775aLeXhKXK4Yw==",
"dependencies": {
"@types/node": "*",
"@types/through": "*"
}
},
"node_modules/@types/through": {
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.33.tgz",
"integrity": "sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
......@@ -222,6 +264,16 @@
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
},
"node_modules/cli-table-2-json": {
"version": "1.0.13",
"resolved": "https://registry.npmjs.org/cli-table-2-json/-/cli-table-2-json-1.0.13.tgz",
"integrity": "sha512-CpUj9dubfuIZSEezwUPycAJqM2dlATyyRUyBkfGeK2KNfrqK3XrdaBohMt0XlkEvsZyDfUEmPWCNvUO+a/7Wsw==",
"dependencies": {
"@types/blue-tape": "^0.1.30",
"@types/lodash": "4.14.119",
"lodash": "^4.17.15"
}
},
"node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
......@@ -317,6 +369,17 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/docker-cli-js": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/docker-cli-js/-/docker-cli-js-2.10.0.tgz",
"integrity": "sha512-p/Hf3igU69bIZ0WvHu0M0/VrKE3LojVWiKyh/4hfPkvCGaad3mWE51q0OyxYP+P26cEISAx0mImtsO6VJdKiZA==",
"dependencies": {
"cli-table-2-json": "1.0.13",
"dockermachine-cli-js": "3.0.5",
"lodash.snakecase": "^4.1.1",
"nodeify-ts": "1.0.6"
}
},
"node_modules/docker-modem": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.3.tgz",
......@@ -331,6 +394,15 @@
"node": ">= 8.0"
}
},
"node_modules/dockermachine-cli-js": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/dockermachine-cli-js/-/dockermachine-cli-js-3.0.5.tgz",
"integrity": "sha512-oV9RRKGvWrvsGl8JW9TWKpjBJVGxn/1qMvhqwPJiOPfRES0+lrq/Q8Wzixb6qinuXPVBhlWqhXb/Oxrh6Vuf/g==",
"dependencies": {
"cli-table-2-json": "1.0.13",
"nodeify-ts": "1.0.6"
}
},
"node_modules/dockerode": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.2.tgz",
......@@ -694,6 +766,16 @@
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash.snakecase": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
"integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
......@@ -769,6 +851,11 @@
"node": ">= 0.6"
}
},
"node_modules/nodeify-ts": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/nodeify-ts/-/nodeify-ts-1.0.6.tgz",
"integrity": "sha512-jq+8sqVG1aLqy5maMTceL8NUJ1CvarWztlxvrYh3G0aao9BsVeoVmVedUnrUSBLetP7oLIAJrPrw4+iIo7v3GA=="
},
"node_modules/object-inspect": {
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
......@@ -1114,6 +1201,11 @@
"node": ">= 0.6"
}
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
......
{
"dependencies": {
"body-parser": "^1.20.2",
"docker-cli-js": "^2.10.0",
"docker-modem": "^5.0.3",
"dockerode": "^4.0.2",
"dockerode-compose": "^1.4.0",
"express": "^4.19.2",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment