UNPKG

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