UNPKG

9.66 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const realm_1 = require("./realm");
4const utils_1 = require("./utils");
5const p_spawn_1 = require("p-spawn");
6// --------- Public create/delete/logs/restart --------- //
7async function kcreate(realm, resourceNames) {
8 const names = await realm_1.getConfigurationNames(realm, resourceNames);
9 for (let name of names) {
10 const fileName = await realm_1.renderRealmFile(realm, name);
11 try {
12 const args = ['create', '-f', fileName];
13 await p_spawn_1.spawn('kubectl', args);
14 }
15 catch (ex) {
16 console.log(`Can't kcreate ${fileName}, skipping`);
17 }
18 console.log();
19 }
20}
21exports.kcreate = kcreate;
22async function kapply(realm, resourceNames) {
23 const names = await realm_1.getConfigurationNames(realm, resourceNames);
24 for (let name of names) {
25 const fileName = await realm_1.renderRealmFile(realm, name);
26 try {
27 const args = ['apply', '-f', fileName];
28 addNamespaceIfDefined(realm, args);
29 await p_spawn_1.spawn('kubectl', args);
30 }
31 catch (ex) {
32 console.log(`Can't kapply ${fileName}, skipping`);
33 }
34 console.log();
35 }
36}
37exports.kapply = kapply;
38// TODO: need to have a way to force the YES when use as API outside of cmd.
39async function kdel(realm, resourceNames) {
40 const names = await realm_1.getConfigurationNames(realm, resourceNames);
41 // If flagged as prod, we do a
42 if (realm.confirmOnDelete) {
43 const answer = await utils_1.prompt("Do you really want to delete? (YES to continue)");
44 if (answer !== "YES") {
45 console.log(`Operation not confirmed. Exiting (nothing done).`);
46 return;
47 }
48 }
49 for (let kName of names) {
50 const fileName = await realm_1.renderRealmFile(realm, kName);
51 const args = ['delete', '-f', fileName];
52 addNamespaceIfDefined(realm, args);
53 try {
54 await p_spawn_1.spawn('kubectl', args);
55 }
56 catch (ex) {
57 console.log(`Can't kdelete ${kName}, skipping`);
58 }
59 console.log();
60 }
61}
62exports.kdel = kdel;
63let currentLogPodName = null;
64async function klogs(realm, resourceNames) {
65 const names = await realm_1.getConfigurationNames(realm, resourceNames);
66 const pods = await fetchK8sObjectsByType(realm, 'pods');
67 for (let serviceName of names) {
68 const podNames = await getPodNames(pods, { labels: { run: `${realm.system}-${serviceName}` } });
69 for (let podName of podNames) {
70 console.log(`Will show log for pod: ${podName}`);
71 const args = ['logs', '-f', podName];
72 addNamespaceIfDefined(realm, args);
73 // Note: here we do not await, because, we want to be able to launch multiple at the same time, and not be blocking.
74 p_spawn_1.spawn('kubectl', args, {
75 detached: true,
76 onStdout: function (data) {
77 // If we had a log block before, and not the same as this one, we end it.
78 if (currentLogPodName != null && currentLogPodName !== podName) {
79 console.log('-----------\n');
80 }
81 // if the current log block is not this one, we put a new start
82 if (currentLogPodName !== podName) {
83 console.log(`----- LOG for: ${serviceName} / ${podName} `);
84 currentLogPodName = podName;
85 }
86 // print the info
87 process.stdout.write(data);
88 }
89 });
90 }
91 }
92}
93exports.klogs = klogs;
94/**
95 * Will kubectl exec /service/restart.sh (will assume the pods/containers has one).
96 * Assumptions:
97 * - One container per pod.
98 * - Does not check if /service/restart.sh exists, so, will crash if not.
99 * @param runLabelPrefix
100 * @param serviceNamesStr
101 */
102async function kshRestart(realm, serviceNamesStr) {
103 const serviceNames = utils_1.asNames(serviceNamesStr);
104 const pods = await fetchK8sObjectsByType(realm, 'pods');
105 for (let serviceName of serviceNames) {
106 const podNames = await getPodNames(pods, { labels: { run: `${realm.system}-${serviceName}` } });
107 for (let podName of podNames) {
108 // TODO need to check if there is a /service/restart.sh
109 try {
110 const args = ['exec', podName];
111 addNamespaceIfDefined(realm, args);
112 args.push('--', 'test', '-e', '/service/restart.sh');
113 await p_spawn_1.spawn('kubectl', args, { toConsole: false });
114 }
115 catch (ex) {
116 console.log(`Skipping service ${serviceName} - '/service/restart.sh' not found.`);
117 continue;
118 }
119 console.log(`\n--- Restarting: ${serviceName} (pod: ${podName})`);
120 const args = ['exec', podName];
121 addNamespaceIfDefined(realm, args);
122 args.push('--', '/service/restart.sh');
123 await p_spawn_1.spawn('kubectl', args);
124 console.log(`--- DONE: ${serviceName} : ${podName}`);
125 }
126 }
127 // TODO: Run: kubectl exec $(kubectl get pods -l run=halo-web-server --no-headers=true -o custom-columns=:metadata.name) -- /service/restart.sh
128 // TODO: needs to check if the service has restart.sh in the source tree, otherwise warn and skip the service
129}
130exports.kshRestart = kshRestart;
131async function kexec(realm, serviceNamesStr, commandAndArgs) {
132 const serviceNames = utils_1.asNames(serviceNamesStr);
133 const pods = await fetchK8sObjectsByType(realm, 'pods');
134 for (let serviceName of serviceNames) {
135 const podNames = await getPodNames(pods, { labels: { run: `${realm.system}-${serviceName}` } });
136 for (let podName of podNames) {
137 try {
138 let args = ['exec', podName];
139 addNamespaceIfDefined(realm, args);
140 args.push('--'); // base arguments
141 args = args.concat(commandAndArgs); // we add the sub command and arguments
142 await p_spawn_1.spawn('kubectl', args); // for now, we have it in the toConsole, but should put it configurable
143 }
144 catch (ex) {
145 console.log(`Cannot run ${commandAndArgs} on pod ${podName} because ${ex}`);
146 continue;
147 }
148 }
149 }
150}
151exports.kexec = kexec;
152// --------- /Public create/delete/logs/restart --------- //
153// --------- Public get/set context --------- //
154// fetch the current context
155async function getCurrentContext() {
156 // kubectl config current-context
157 const psResult = await p_spawn_1.spawn('kubectl', ['config', 'current-context'], { capture: ['stdout', 'stderr'], ignoreFail: true });
158 if (psResult.stderr) {
159 // console.log(`INFO: ${psResult.stderr}`);
160 return null;
161 }
162 else {
163 return psResult.stdout.toString().trim();
164 }
165}
166exports.getCurrentContext = getCurrentContext;
167async function setCurrentContext(name) {
168 // TODO: perhaps when null, should unset it: 'kubectl config unset current-context'
169 // downside is that it can create some unwanted side effect, if the user has another context set
170 await p_spawn_1.spawn('kubectl', ['config', 'use-context', name]);
171}
172exports.setCurrentContext = setCurrentContext;
173// --------- /Public get/set context --------- //
174// --------- Private Utils --------- //
175function addNamespaceIfDefined(realm, args) {
176 const namespace = realm.namespace;
177 if (namespace) {
178 args.push('--namespace', namespace);
179 }
180}
181// Fetch the pod for a part
182async function fetchK8sObjectsByType(realm, type) {
183 const args = ['get', type, '-o', 'json'];
184 addNamespaceIfDefined(realm, args);
185 const psResult = await p_spawn_1.spawn('kubectl', args, { capture: 'stdout' });
186 const podsJsonStr = psResult.stdout.toString();
187 const result = JSON.parse(podsJsonStr);
188 return result.items || [];
189}
190// return the pod names
191// Today filter: {imageName: string, labels: {[string]: value} (All properties of the filter must match (i.e. AND))
192function getPodNames(items, filter) {
193 const names = [];
194 if (items) {
195 for (let item of items) {
196 let pass = false;
197 const itemName = item.metadata.name;
198 // test the filter.imageName
199 if (filter && filter.imageName) {
200 // Note: for now, just assume one container per pod, which should be the way to go anyway.
201 const itemImageName = (item.spec && item.spec.containers) ? item.spec.containers[0].image : null;
202 if (itemImageName != null && itemImageName.startsWith(filter.imageName)) {
203 pass = true;
204 }
205 else {
206 pass = false;
207 continue; // if false, we can stop this item now.
208 }
209 }
210 // test the filter.labels
211 if (filter && filter.labels) {
212 for (let labelName in filter.labels) {
213 if (item.metadata.labels[labelName] === filter.labels[labelName]) {
214 pass = true;
215 }
216 else {
217 pass = false;
218 continue; // if false, we can stop now
219 }
220 }
221 }
222 // if pass, adde it.
223 if (pass) {
224 names.push(itemName);
225 }
226 }
227 }
228 return names;
229}
230// --------- /Private Utils --------- //