UNPKG

11.7 kBJavaScriptView Raw
1/**
2 * Copyright 2018 F5 Networks, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17'use strict';
18
19/* This is a replacement for the cloud nodes worker. Once we started encrypting
20 credentials with private keys, that worker failed because resnoded has
21 no priveledges to read the private keys */
22
23const q = require('q');
24const options = require('commander');
25const path = require('path');
26const fs = require('fs');
27
28const LOG_ID = 'getNodes.js';
29
30const util = require('../lib/util');
31const localCryptoUtil = require('../lib/localCryptoUtil');
32const cloudProviderFactory = require('../lib/cloudProviderFactory');
33const Logger = require('../lib/logger');
34
35(function run() {
36 const runner = {
37 run(argv, testOpts, cb) {
38 const DEFAULT_LOG_FILE = '/var/log/cloudlibs/getNodes.log';
39 const REQUIRED_OPTIONS = ['cloud', 'memberAddressType'];
40 const REQUIRED_UNIQUE_OPTIONS = [['memberTag', 'uri']];
41 const KEYS_TO_MASK = ['--provider-options'];
42
43 const providerOptions = {};
44 const loggerOptions = {};
45 const optionsForTest = {};
46
47 let provider;
48 let loggableArgs;
49
50 Object.assign(optionsForTest, testOpts);
51
52 try {
53 /* eslint-disable max-len */
54 options
55 .version('4.23.0-beta.3')
56 .option(
57 '--cloud <cloud_provider>',
58 'Cloud provider (aws | azure | etc.)'
59 )
60 .option(
61 '--member-address-type <public | private>',
62 'Type of ip address to look for - public or private.'
63 )
64 .option(
65 '--member-tag <member_tag>',
66 'Tag that is on the members in the cloud provider. Ex: value, or key=value'
67 )
68 .option(
69 '--provider-options <cloud_options>',
70 'Options specific to cloud_provider. Ex: param1:value1,param2:value2',
71 util.map,
72 providerOptions
73 )
74 .option(
75 '--uri <uri>',
76 'Location of JSON data containing nodes.'
77 )
78 .option(
79 '--log-level <level>',
80 'Log level (none, error, warn, info, verbose, debug, silly). Default is info.', 'info'
81 )
82 .option(
83 '-o, --output <file>',
84 `Log to file. Default is ${DEFAULT_LOG_FILE}`, DEFAULT_LOG_FILE
85 )
86 .option(
87 'console',
88 'Log to console. Default false (log to file only).'
89 )
90 .parse(argv);
91 /* eslint-enable max-len */
92
93 loggerOptions.console = options.console;
94 loggerOptions.logLevel = options.logLevel;
95 loggerOptions.fileName = options.output;
96 loggerOptions.module = module;
97
98 if (loggerOptions.fileName) {
99 const dirName = path.dirname(loggerOptions.fileName);
100 if (!fs.existsSync(dirName)) {
101 fs.mkdirSync(dirName);
102 }
103 }
104
105 this.logger = Logger.getLogger(loggerOptions);
106 util.setLoggerOptions(loggerOptions);
107 localCryptoUtil.setLoggerOptions(loggerOptions);
108
109 // Log the input, but don't log passwords
110 loggableArgs = argv.slice();
111 for (let i = 0; i < loggableArgs.length; i++) {
112 if (KEYS_TO_MASK.indexOf(loggableArgs[i]) !== -1) {
113 loggableArgs[i + 1] = '*******';
114 }
115 }
116 this.logger.info(LOG_ID, `${loggableArgs[1]} called with`, loggableArgs.join(' '));
117
118 for (let i = 0; i < REQUIRED_OPTIONS.length; i++) {
119 if (!options[REQUIRED_OPTIONS[i]]) {
120 util.logAndExit(
121 `${REQUIRED_OPTIONS[i]} is a required command line option.`,
122 'error',
123 1
124 );
125 return;
126 }
127 }
128
129 for (let i = 0; i < REQUIRED_UNIQUE_OPTIONS.length; i++) {
130 const foundOpts = Object.keys(options).filter((opt) => {
131 return REQUIRED_UNIQUE_OPTIONS[i].indexOf(opt) > -1;
132 });
133 if (foundOpts.length !== 1) {
134 const opts = (foundOpts.length > 0 ? foundOpts : REQUIRED_UNIQUE_OPTIONS[i]);
135 util.logAndExit(
136 `Must include ${foundOpts.length > 1 ? 'only ' : ''}one of the `
137 + `following command line options: ${opts.join(', ')}`,
138 'error',
139 1
140 );
141 return;
142 }
143 }
144
145 provider = optionsForTest.cloudProvider;
146 if (!provider) {
147 provider = cloudProviderFactory.getCloudProvider(
148 options.cloud,
149 {
150 loggerOptions,
151 clOptions: options
152 }
153 );
154 }
155
156 let credentialsPromise;
157 if (providerOptions.secretData) {
158 credentialsPromise = localCryptoUtil.decryptData(
159 providerOptions.secretData,
160 providerOptions.secretPrivateKeyFolder,
161 providerOptions.secretPrivateKeyName,
162 {
163 encryptedKey: providerOptions.secretKey,
164 iv: providerOptions.secretIv
165 }
166 );
167 } else {
168 credentialsPromise = q();
169 }
170
171 credentialsPromise
172 .then((credentials) => {
173 if (credentials) {
174 providerOptions.secret = credentials;
175 }
176 this.logger.info(LOG_ID, 'Initializing cloud provider');
177 return provider.init(providerOptions);
178 })
179 .then(() => {
180 const promises = [];
181 if (options.memberTag) {
182 this.logger.debug(LOG_ID, 'Getting NICs');
183
184 const keyValue = options.memberTag.split('=');
185 let key;
186 let value;
187
188 if (keyValue.length > 1) {
189 key = keyValue[0];
190 value = keyValue[1];
191 } else {
192 value = keyValue[0];
193 }
194
195 this.logger.silly(LOG_ID, 'key', key);
196 this.logger.silly(LOG_ID, 'value', value);
197
198 promises.push(provider.getNicsByTag({ key, value }));
199 promises.push(provider.getVmsByTag({ key, value }));
200 } else if (options.uri) {
201 this.logger.debug(LOG_ID, 'Getting Nodes');
202
203 this.logger.silly(LOG_ID, 'URI', options.uri);
204
205 promises.push(provider.getNodesFromUri(options.uri));
206 }
207
208 return Promise.all(promises);
209 })
210 .then((responses) => {
211 let nodes = [];
212 const nics = responses[0] || [];
213 const vms = responses[1] || [];
214
215 if (options.memberTag) {
216 this.logger.silly(LOG_ID, 'nics', JSON.stringify(nics));
217 this.logger.silly(LOG_ID, 'vms', JSON.stringify(vms));
218 } else if (options.uri) {
219 this.logger.silly(LOG_ID, 'uri nodes', JSON.stringify(nics));
220 }
221
222 nodes = nics.reduce((result, nic) => {
223 const node = getNode(nic);
224 if (node) {
225 result.push(node);
226 }
227 return result;
228 }, []);
229
230 if (options.memberTag && nodes.length === 0) {
231 this.logger.debug(LOG_ID, 'no valid nics found, trying vms');
232 nodes = vms.reduce((result, vm) => {
233 const node = getNode(vm);
234 if (node) {
235 result.push(node);
236 }
237 return result;
238 }, []);
239 }
240
241 if (nodes.length === 0) {
242 this.logger.debug(LOG_ID, 'no valid pool nodes found');
243 }
244
245 console.log(JSON.stringify(nodes)); // eslint-disable-line no-console
246 })
247 .catch((err) => {
248 if (err && err.code && err.message) {
249 this.logger.error(LOG_ID, 'error code:', err.code, 'message:', err.message);
250 } else {
251 this.logger.error(LOG_ID, 'error:', err && err.message ? err.message : err);
252 }
253 return err;
254 })
255 .done((err) => {
256 if (cb) {
257 cb(err);
258 }
259
260 // Exit so that any listeners don't keep us alive
261 util.logAndExit('getNodes finished.');
262 });
263 } catch (err) {
264 if (this.logger) {
265 this.logger.error(LOG_ID, 'error:', err && err.message ? err.message : err);
266 }
267
268 if (cb) {
269 cb();
270 }
271 }
272 }
273 };
274
275 module.exports = runner;
276
277 // If we're called from the command line, run
278 // This allows for test code to call us as a module
279 if (!module.parent) {
280 runner.run(process.argv);
281 }
282}());
283
284
285function getNode(owner) {
286 const ip = options.memberAddressType.toLowerCase() === 'public' ? owner.ip.public : owner.ip.private;
287 if (ip) {
288 return {
289 ip,
290 id: `${owner.id}-${options.memberAddressType.toLowerCase()}`,
291 };
292 }
293 return '';
294}