Skip to content

Commit 363eef7

Browse files
alanzanattadevGauthierPLM
authored andcommitted
Adds docker plugin (#460)
1 parent a0a788b commit 363eef7

File tree

11 files changed

+341
-2
lines changed

11 files changed

+341
-2
lines changed

images/plugins/docker.svg

Lines changed: 1 addition & 0 deletions
Loading

lib/ExecutionControlEpic/ConsoleFeature/Epics/ConsoleEpics.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import Rx from "rxjs";
55
import { addConsoleLogsForTask } from "../Actions/AddConsoleLog";
6+
import { addConsoleSource } from "../Actions/AddConsoleSource";
67
import { addBufferedLogsForTask } from "../Actions/AddBufferedLogs";
78
import {
89
ConsoleLogError,
@@ -39,6 +40,11 @@ const consoleEpic = () => (action$: any) => {
3940
}),
4041
),
4142

43+
action$
44+
.ofType("ADD_PLAN_CONFIGURATION")
45+
.filter(action => action.payload.ownSource)
46+
.map(action => addConsoleSource(action.payload.name, true)),
47+
4248
action$.ofType("FEATURE_LOAD").map(action =>
4349
addConsoleLogsForTask({
4450
...molecule,

lib/ExecutionControlEpic/ConsoleFeature/Reducers/ConsoleSources.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import { Map } from "immutable";
55

6-
const initialState = Map().setIn(["Molecule"], ["Molecule", true]);
6+
const initialState = Map().setIn(["Molecule"], ["Molecule", false]);
77

88
export default function(
99
state: ConsoleSourcesReducer = initialState,

lib/ExecutionControlEpic/LanguageServerProtocolFeature/Epics/languageClient.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export function languageClientGetActions(
4646
observer.next(
4747
// TODO - Remove hard-coded values
4848
bufferConsoleLogsForTask({
49-
source: "Molecule",
49+
source: planConfig.ownSource ? planConfig.name : "Molecule",
5050
color: "#592b71",
5151
version: "0.4.0",
5252
severity: data.type,
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
"use babel";
2+
// @flow
3+
4+
import type {
5+
GeneratedPlanObject,
6+
PlanConfig,
7+
} from "../../PlanConfigurationFeature/Types/types";
8+
import type { PackageTesterResult } from "../../../ProjectSystemEpic/PackageFeature/Types/types";
9+
import type { Plugin } from "../../DevtoolLoadingFeature/Types/types";
10+
import path from "path";
11+
import yaml from "js-yaml";
12+
import fs from "fs";
13+
import moment from "moment";
14+
15+
const plugin: Plugin = {
16+
info: {
17+
name: "docker",
18+
dominantColor: "#339fee",
19+
iconUri: "atom://molecule/images/plugins/docker.svg",
20+
},
21+
22+
configSchema: {
23+
type: "object",
24+
schemas: {
25+
command: {
26+
type: "enum",
27+
label: "command",
28+
default: "start",
29+
enum: [
30+
{ value: "build", description: "build" },
31+
{ value: "up", description: "up" },
32+
{ value: "push", description: "push" },
33+
],
34+
},
35+
serviceName: {
36+
type: "string",
37+
label: "service name",
38+
placeholder: "database,web... empty for all",
39+
default: "",
40+
},
41+
bin: {
42+
type: "string",
43+
label: "binary path",
44+
placeholder: "docker-compose",
45+
default: "docker-compose",
46+
},
47+
},
48+
},
49+
50+
getStrategyForPlan(plan: PlanConfig) {
51+
if (plan.config.command === "up") {
52+
return {
53+
strategy: {
54+
type: "node",
55+
path: path.join(__dirname, "lsp"),
56+
cwd: path.dirname(plan.packageInfo.path),
57+
lsp: true,
58+
env: {
59+
COMPOSE_COMMAND: JSON.stringify({
60+
bin: plan.config.bin,
61+
command: plan.config.command,
62+
service: plan.config.serviceName,
63+
configFile: plan.packageInfo.path,
64+
}),
65+
},
66+
},
67+
};
68+
} else {
69+
const command = `${plan.config.bin} -f ${plan.packageInfo.path} ${
70+
plan.config.command
71+
} ${plan.config.serviceName}`;
72+
return {
73+
strategy: {
74+
type: "terminal",
75+
cwd: path.dirname(plan.packageInfo.path),
76+
command: command,
77+
env: process.env,
78+
},
79+
controller: {
80+
onData(data: string, taskAPI: TaskAPI, helperAPI: HelperAPI) {},
81+
onError(err: any, taskAPI: TaskAPI, helperAPI: HelperAPI) {
82+
taskAPI.diagnostics.setForWorkspace({
83+
uri: "docker-compose",
84+
diagnostics: [
85+
{
86+
range: {
87+
start: { line: -1, character: -1 },
88+
end: { line: -1, character: -1 },
89+
},
90+
severity: helperAPI.severity.error,
91+
message: `Error: ${err.code | 1}`.toString(),
92+
date: moment().unix(),
93+
},
94+
],
95+
});
96+
},
97+
},
98+
};
99+
}
100+
},
101+
102+
async isPackage(packagePath: string): PackageTesterResult {
103+
if (!packagePath.endsWith(".yaml")) {
104+
return false;
105+
}
106+
107+
const rawData = await new Promise((resolve, reject) => {
108+
fs.readFile(packagePath, (err, data) => {
109+
if (err) {
110+
reject(err);
111+
} else {
112+
resolve(data);
113+
}
114+
});
115+
});
116+
const fileConfig = yaml.safeLoad(rawData);
117+
return "version" in fileConfig && "services" in fileConfig;
118+
},
119+
120+
async generatePlansForPackage(
121+
packagePath: string,
122+
): Promise<Array<GeneratedPlanObject>> {
123+
const rawData = await new Promise((resolve, reject) => {
124+
fs.readFile(packagePath, (err, data) => {
125+
if (err) {
126+
reject(err);
127+
} else {
128+
resolve(data);
129+
}
130+
});
131+
});
132+
const fileConfig = yaml.safeLoad(rawData);
133+
134+
return Promise.resolve([
135+
{
136+
name: `up`,
137+
value: {
138+
command: "up",
139+
serviceName: "",
140+
bin: "docker-compose",
141+
},
142+
autorun: false,
143+
ownSource: true,
144+
},
145+
{
146+
name: `build`,
147+
value: {
148+
command: "build",
149+
serviceName: "",
150+
bin: "docker-compose",
151+
},
152+
autorun: false,
153+
},
154+
{
155+
name: `push`,
156+
value: {
157+
command: "push",
158+
serviceName: "",
159+
bin: "docker-compose",
160+
},
161+
autorun: false,
162+
},
163+
...Object.keys(fileConfig.services || {})
164+
.map(serviceName => [
165+
{
166+
name: `up ${serviceName}`,
167+
value: {
168+
command: "up",
169+
serviceName: serviceName,
170+
bin: "docker-compose",
171+
},
172+
autorun: false,
173+
ownSource: true,
174+
},
175+
{
176+
name: `${
177+
fileConfig.services[serviceName].image ? "pull" : "build"
178+
} ${serviceName}`,
179+
value: {
180+
command: `${
181+
fileConfig.services[serviceName].image ? "pull" : "build"
182+
}`,
183+
serviceName: serviceName,
184+
bin: "docker-compose",
185+
},
186+
autorun: false,
187+
},
188+
{
189+
name: `push ${serviceName}`,
190+
value: {
191+
command: "push",
192+
serviceName: serviceName,
193+
bin: "docker-compose",
194+
},
195+
autorun: false,
196+
},
197+
])
198+
.reduce((red, arr) => [...red, ...arr], []),
199+
]);
200+
},
201+
};
202+
203+
export default plugin;
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/* eslint-disable */
2+
const {
3+
createMessageConnection,
4+
StreamMessageReader,
5+
StreamMessageWriter,
6+
} = require("vscode-jsonrpc");
7+
8+
const { spawn, exec } = require("child_process");
9+
const process = require("process");
10+
const StreamSplitter = require("stream-splitter");
11+
const stripAnsi = require("strip-ansi");
12+
13+
const connection = createMessageConnection(
14+
new StreamMessageReader(process.stdin),
15+
new StreamMessageWriter(process.stdout),
16+
);
17+
18+
connection.onRequest("initialize", () => {
19+
return new Promise((resolve, reject) => {
20+
const { bin, command, service, configFile } = JSON.parse(
21+
process.env.COMPOSE_COMMAND,
22+
);
23+
24+
const dockerProcess = spawn(bin, ["-f", configFile, command, service], {
25+
stderr: "pipe",
26+
stdout: "pipe",
27+
});
28+
dockerProcess.on("error", err =>
29+
connection.sendNotification("window/logMessage", {
30+
type: 3,
31+
message: `Error running Docker: ${err}`,
32+
}),
33+
);
34+
35+
const lines = dockerProcess.stdout.pipe(StreamSplitter("\n"));
36+
lines.on("token", line =>
37+
connection.sendNotification("window/logMessage", {
38+
type: 1,
39+
message: stripAnsi(line.toString()),
40+
}),
41+
);
42+
43+
connection.onRequest("shutdown", () => {
44+
return new Promise(resolve => {
45+
dockerProcess.kill();
46+
exec(
47+
`${bin} -f ${configFile} stop ${service}`,
48+
{},
49+
(err, stdout, stderr) => {
50+
resolve();
51+
},
52+
);
53+
});
54+
});
55+
resolve();
56+
});
57+
});
58+
59+
connection.listen();

lib/ProjectSystemEpic/PackageFeature/Epics/GeneratePlans.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ const generatePlansEpic = (action$: Observable, store: Store) => {
4343
autoGenerated: true,
4444
autoRun: planObject.autoRun,
4545
pinned: planObject.autoRun === true ? true : false,
46+
ownSource: planObject.ownSource || false,
4647
}),
4748
)
4849
.filter(

lib/molecule-dev-environment.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ export default {
5050
plugins: {
5151
type: "object",
5252
properties: {
53+
Docker: {
54+
type: "boolean",
55+
description: "Activate or not Docker in Molecule",
56+
default: true,
57+
},
5358
Eslint: {
5459
type: "boolean",
5560
description: "Activate or not Eslint in Molecule",
@@ -142,6 +147,7 @@ export default {
142147

143148
// Bind plugins. Path should be relative to /lib/ExecutionControlEpic/Plugins/
144149
[
150+
"Docker/index.js",
145151
"Eslint/index.js",
146152
"Nightwatch/index.js",
147153
"Flowtype.js",

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
"flow-language-server": "^0.5.0",
5656
"immutable": "^3.8.1",
5757
"jest-cli": "^23.1.0",
58+
"js-yaml": "^3.12.0",
5859
"less": "^3.0.4",
5960
"mocha": "^5.1.1",
6061
"moment": "^2.19.2",
@@ -76,6 +77,7 @@
7677
"remote-redux-devtools": "^0.5.7",
7778
"rxjs": "^5.4.3",
7879
"stream-buffers": "^3.0.1",
80+
"stream-splitter": "^0.3.2",
7981
"styled-components": "^3.2.6",
8082
"tree-kill": "^1.2.0",
8183
"vscode-jsonrpc": "^3.6.1",
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
version: "3"
2+
services:
3+
redis:
4+
image: redis:alpine
5+
ports:
6+
- "6379"
7+
networks:
8+
- frontend
9+
10+
db:
11+
image: postgres:9.4
12+
networks:
13+
- backend
14+
15+
vote:
16+
image: dockersamples/examplevotingapp_vote:before
17+
ports:
18+
- "5000:80"
19+
networks:
20+
- frontend
21+
depends_on:
22+
- redis
23+
24+
result:
25+
image: dockersamples/examplevotingapp_result:before
26+
ports:
27+
- "5001:80"
28+
networks:
29+
- backend
30+
depends_on:
31+
- db
32+
33+
worker:
34+
image: dockersamples/examplevotingapp_worker
35+
networks:
36+
- frontend
37+
- backend
38+
39+
visualizer:
40+
image: dockersamples/visualizer:stable
41+
ports:
42+
- "8080:8080"
43+
stop_grace_period: 1m30s
44+
volumes:
45+
- "/var/run/docker.sock:/var/run/docker.sock"
46+
47+
networks:
48+
frontend:
49+
backend:

0 commit comments

Comments
 (0)