UNPKG

9.95 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const utils_1 = require("./utils");
4const k8s_1 = require("./k8s");
5const vdev_config_1 = require("./vdev-config");
6const renderer_1 = require("./renderer");
7const fs = require("fs-extra-plus");
8const Path = require("path");
9const hook_1 = require("./hook");
10// --------- /Public Types --------- //
11// --------- Public Realms APIs --------- //
12/**
13 * Set/Change the current (k8s context and eventual google project). Only change what is needed.
14 * @param {*} realm the realm object.
15 * @return {profileChanged?: true, contextChanged?: true} return a change object with what has changed.
16 */
17async function setRealm(realm) {
18 const currentRealm = await getCurrentRealm(false);
19 const change = {};
20 // NOTE: When realm.project is undefined, it will set the gclougProject to undefined,
21 // which is fine since we want to avoid accidental operation to the project.
22 // FIXME: Needs to handle the case where realm.project is not defined (probably remove the current google project to make sure no side effect)
23 const hookReturn = await hook_1.callHook(realm, 'realm_set_begin', currentRealm);
24 if (hookReturn != null) {
25 change.profileChanged = true;
26 }
27 if (realm.context === null) {
28 console.log(`INFO: realm ${realm.name} does not have a kubernetes context, skipping 'kubectl config use-context ...' (current kubectl context still active)`);
29 }
30 else if (!currentRealm || currentRealm.context !== realm.context) {
31 change.contextChanged = true;
32 await k8s_1.setCurrentContext(realm.context);
33 }
34 return change;
35}
36exports.setRealm = setRealm;
37/**
38 * Get the current Realm. Return undefined if not found
39 */
40async function getCurrentRealm(check = true) {
41 const realms = await loadRealms();
42 const context = await k8s_1.getCurrentContext();
43 let realm;
44 for (let realmName in realms) {
45 let realmItem = realms[realmName];
46 if (realmItem.context === context) {
47 realm = realmItem;
48 break;
49 }
50 }
51 if (check && realm) {
52 await hook_1.callHook(realm, 'realm_check');
53 }
54 // if no context matching, try get the first realm that has no context
55 if (!realm) {
56 realm = Object.values(realms).find(r => r.context === null);
57 }
58 return realm;
59}
60exports.getCurrentRealm = getCurrentRealm;
61/**
62 * Render realm yaml file.
63 * @param realm
64 * @param name name of the yaml file (without extension)
65 * @return The yaml file path
66 */
67async function renderRealmFile(realm, name) {
68 const realmOutDir = getRealmOutDir(realm);
69 const srcYamlFile = getKFile(realm, name);
70 const srcYamlFileName = Path.parse(srcYamlFile).base;
71 const srcYamlContent = await fs.readFile(srcYamlFile, 'utf8');
72 const outYamlFile = Path.join(realmOutDir, srcYamlFileName);
73 // render the content
74 var data = realm;
75 const outYamlContent = await renderer_1.render(srcYamlContent, data);
76 // for now, we do not generate do any template
77 await fs.ensureDir(realmOutDir);
78 await fs.writeFile(outYamlFile, outYamlContent);
79 return outYamlFile;
80}
81exports.renderRealmFile = renderRealmFile;
82function formatAsTable(realms, currentRealm) {
83 const txts = [];
84 const header = ' ' + 'REALM'.padEnd(20) + 'TYPE'.padEnd(12) + 'PROJECT/PROFILE'.padEnd(20) + 'CONTEXT';
85 txts.push(header);
86 const currentRealmName = (currentRealm) ? currentRealm.name : null;
87 const currentProject = (currentRealm) ? currentRealm.project : null;
88 for (let realm of Object.values(realms)) {
89 let row = (realm.name === currentRealmName) ? "* " : " ";
90 row += realm.name.padEnd(20);
91 row += realm.type.padEnd(12);
92 let profile = (realm.type === 'gcp') ? realm.project : realm.profile;
93 profile = (profile == null) ? '' : profile;
94 row += profile.padEnd(20);
95 row += (realm.context ? realm.context : 'NO CONTEXT FOUND');
96 txts.push(row);
97 }
98 return txts.join('\n');
99}
100exports.formatAsTable = formatAsTable;
101/**
102 * Templatize a set of yaml files
103 * @param resourceNames either a single name, comma deliminated set of names, or an array.
104 */
105async function templatize(realm, resourceNames) {
106 const names = await getConfigurationNames(realm, resourceNames);
107 const result = [];
108 for (let name of names) {
109 const path = await renderRealmFile(realm, name);
110 result.push({ name, path });
111 }
112 return result;
113}
114exports.templatize = templatize;
115// --------- /Resource Management --------- //
116/**
117 * Returns a list of k8s configuration file names for a given realm and optional configurations names delimited string or array.
118 * - If configurationNames is an array, then, just return as is.
119 * - If configurationNames is string (e.g., 'web-server, queue') then it will split on ',' and trim each item as returns.
120 * - If no resourceNames then returns all of the resourceNames for the realm.
121 */
122async function getConfigurationNames(realm, configurationNames) {
123 // Note: for now, we do the check if the realm has a context here, because this is called for each k*** commands
124 // TODO: Might want to make it more explicit, as validateRealmForKubectlCommand(realm)
125 if (realm.context === null) {
126 throw Error(`Realm '${realm.name}' does not have a Kubernetes context, cannot perform kubectly commands.`);
127 }
128 if (configurationNames) {
129 return utils_1.asNames(configurationNames);
130 }
131 else if (realm.defaultConfigurations) {
132 return realm.defaultConfigurations;
133 }
134 else {
135 return getAllConfigurationNames(realm);
136 }
137}
138exports.getConfigurationNames = getConfigurationNames;
139function getLocalImageName(realm, serviceName) {
140 return _getImageName(realm, 'localhost:5000/', serviceName);
141}
142exports.getLocalImageName = getLocalImageName;
143/**
144 * Note: right now assume remote is on gke cloud (gcr.io/)
145 * @param realm
146 * @param serviceName
147 */
148function getRemoteImageName(realm, serviceName) {
149 return _getImageName(realm, realm.registry, serviceName);
150}
151exports.getRemoteImageName = getRemoteImageName;
152function _getImageName(realm, basePath, serviceName) {
153 const tag = (realm.imageTag) ? realm.imageTag : 'latest';
154 const repoName = getRepositoryName(realm, serviceName);
155 return `${basePath}${repoName}:${tag}`;
156}
157function getRepositoryName(realm, serviceName) {
158 return `${realm.system}-${serviceName}`;
159}
160exports.getRepositoryName = getRepositoryName;
161function assertRealm(realm) {
162 if (!realm) {
163 throw new Error(`No realm found, do a 'npm run realm' to see the list of realm, and 'npm run realm realm_name' to set a realm`);
164 }
165 return realm;
166}
167exports.assertRealm = assertRealm;
168// --------- Loader --------- //
169async function loadRealms() {
170 const rawConfig = await vdev_config_1.loadVdevConfig();
171 const rawRealms = rawConfig.realms;
172 const realms = {};
173 const base = {
174 system: rawConfig.system,
175 k8sDir: rawConfig.k8sDir
176 };
177 // get the _common variables and delete it from the realm list
178 let _common = {};
179 if (rawRealms._common) {
180 _common = rawRealms._common;
181 delete rawRealms._common;
182 }
183 // Create the realm object
184 for (let name in rawRealms) {
185 const rawRealm = rawRealms[name];
186 // TODO: must do a deep merge
187 const realm = Object.assign(Object.assign(Object.assign({}, base), _common), rawRealm);
188 //// determine the type
189 let type = 'local';
190 const context = realm.context;
191 if (context) {
192 if (context.startsWith('arn:aws')) {
193 type = 'aws';
194 realm.profile = (realm.profile != null) ? realm.profile : 'default';
195 }
196 else if (context.startsWith('gke')) {
197 type = 'gcp';
198 }
199 else if (realm.registry && realm.registry.includes('azurecr')) {
200 type = 'azure';
201 }
202 }
203 else {
204 realm.context = null;
205 }
206 realm.type = type;
207 //// determine registry
208 if (!realm.registry) {
209 if (type === 'local' && realm.context) { // do not create localhost registry for local realm without context
210 realm.registry = 'localhost:5000/';
211 }
212 else if (type === 'gcp') {
213 realm.registry = `gcr.io/${realm.project}/`;
214 }
215 else if (type === 'aws') {
216 console.log(`WARNING - realm ${realm.name} of type 'aws' must have a registry property in the vdev.yaml`);
217 }
218 }
219 // set the name
220 realm.name = name;
221 // Call hook to finiish initializing the realm (i.e., realm type specific initialization)
222 await hook_1.callHook(realm, 'realm_init');
223 realms[name] = realm;
224 }
225 return realms;
226}
227exports.loadRealms = loadRealms;
228// --------- /Loader --------- //
229// --------- Private Helpers --------- //
230/** Get all of the resourceNames for a given realm */
231async function getAllConfigurationNames(realm) {
232 const dir = getRealmSrcDir(realm);
233 const yamlFiles = await fs.glob('*.yaml', dir);
234 // return the list of names only
235 if (yamlFiles) {
236 return yamlFiles.map((f) => { return Path.basename(f, '.yaml'); });
237 }
238 else {
239 return []; // return empty list if nothing found
240 }
241}
242function getRealmOutDir(realm) {
243 return Path.join(realm.k8sDir, '~out/', realm.name + '/');
244}
245function getRealmSrcDir(realm) {
246 return Path.join(realm.k8sDir, realm.yamlDir);
247}
248// get the Original Kubernets Yaml file (which could be a template)
249function getKFile(realm, kName) {
250 let k8sDir = getRealmSrcDir(realm);
251 return Path.join(k8sDir, `${kName.trim()}.yaml`);
252}
253async function cleanRealmOutDir(realm) {
254 await utils_1.saferRemove(getRealmOutDir(realm));
255}
256// --------- /Private Helpers --------- //