cloudcms-server 4.0.0-beta.11 → 4.0.0-beta.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +0 -5
  2. package/index.js +58 -32
  3. package/middleware/authentication/authentication.js +40 -12
  4. package/middleware/authentication/providers/saml.js +7 -3
  5. package/middleware/config/adapter.js +0 -44
  6. package/middleware/deployment/deployment.js +22 -24
  7. package/middleware/driver/driver.js +24 -1
  8. package/middleware/registration/registration.js +0 -5
  9. package/middleware/stores/engines/empty.js +0 -4
  10. package/middleware/stores/engines/fs-caching-adapter.js +0 -5
  11. package/middleware/stores/engines/fs.js +0 -9
  12. package/middleware/stores/engines/s3.js +0 -5
  13. package/middleware/stores/engines/s3fs.js +0 -5
  14. package/middleware/stores/multistore.js +0 -29
  15. package/middleware/stores/store.js +0 -9
  16. package/middleware/virtual-config/virtual-config.js +253 -203
  17. package/package.json +33 -48
  18. package/server/index.js +166 -111
  19. package/server/standalone.js +1 -6
  20. package/util/cloudcms.js +34 -7
  21. package/util/loaders.js +113 -0
  22. package/util/workqueue.js +100 -0
  23. package/duster/helpers/core/cloudcms/associations.js +0 -34
  24. package/duster/helpers/core/cloudcms/beta/markdown.js +0 -46
  25. package/duster/helpers/core/cloudcms/beta/nodeAttachmentText.js +0 -46
  26. package/duster/helpers/core/cloudcms/beta/params.js +0 -33
  27. package/duster/helpers/core/cloudcms/beta/processTemplate.js +0 -82
  28. package/duster/helpers/core/cloudcms/content.js +0 -34
  29. package/duster/helpers/core/cloudcms/expand.js +0 -38
  30. package/duster/helpers/core/cloudcms/form.js +0 -34
  31. package/duster/helpers/core/cloudcms/query.js +0 -34
  32. package/duster/helpers/core/cloudcms/queryOne.js +0 -34
  33. package/duster/helpers/core/cloudcms/relatives.js +0 -34
  34. package/duster/helpers/core/cloudcms/search.js +0 -34
  35. package/duster/helpers/core/cloudcms/searchOne.js +0 -34
  36. package/duster/helpers/core/cloudcms/wcm/dependency.js +0 -83
  37. package/duster/helpers/core/cloudcms/wcm/fragment.js +0 -34
  38. package/duster/helpers/core/dev/debug.js +0 -42
  39. package/duster/helpers/core/dom/block.js +0 -49
  40. package/duster/helpers/core/dom/include.js +0 -38
  41. package/duster/helpers/core/dom/layout.js +0 -49
  42. package/duster/helpers/core/dom/link.js +0 -81
  43. package/duster/helpers/core/dom/resource.js +0 -77
  44. package/duster/helpers/core/engine.js +0 -1580
  45. package/duster/helpers/core/ice/value.js +0 -65
  46. package/duster/helpers/core/index.js +0 -49
  47. package/duster/helpers/core/operators/if.js +0 -64
  48. package/duster/helpers/core/operators/iter.js +0 -45
  49. package/duster/helpers/core/operators/iterate.js +0 -129
  50. package/duster/helpers/sample/nyt.js +0 -114
  51. package/duster/index.js +0 -319
  52. package/duster/support.js +0 -436
  53. package/duster/tracker.js +0 -262
  54. package/middleware/authentication/providers/cas.js +0 -73
  55. package/middleware/authentication/providers/facebook.js +0 -120
  56. package/middleware/authentication/providers/github.js +0 -88
  57. package/middleware/authentication/providers/linkedin.js +0 -112
  58. package/middleware/authentication/providers/twitter.js +0 -120
  59. package/middleware/server-tags/server-tags.js +0 -113
  60. package/middleware/wcm/wcm.js +0 -1437
@@ -1,5 +1,9 @@
1
1
  var util = require("../../util/util");
2
2
 
3
+ var workQueueFactory = require("../../util/workqueue");
4
+
5
+ //var debugLog = process.debugLog;
6
+
3
7
  /**
4
8
  * Retrieves virtual driver configuration for hosts from Cloud CMS.
5
9
  *
@@ -7,7 +11,12 @@ var util = require("../../util/util");
7
11
  */
8
12
  exports = module.exports = function()
9
13
  {
14
+ // ensures that we only load 2 virtual config at a time
15
+ var enqueueLoadVirtualConfig = workQueueFactory(2);
16
+
10
17
  var SENTINEL_NOT_FOUND_VALUE = "null";
18
+ var BLACKLIST_TTL_SECONDS = 60 * 60 * 24 * 30; // 30 days
19
+ var DISABLED_TTL_SECONDS = 60 * 10; // 10 minutes
11
20
 
12
21
  var VIRTUAL_DRIVER_CACHE_KEY = "virtualdriver";
13
22
 
@@ -30,12 +39,9 @@ exports = module.exports = function()
30
39
  // force key to "virtualdriver"
31
40
  configuration.virtualDriver.key = VIRTUAL_DRIVER_CACHE_KEY;
32
41
 
33
- //console.log("a1: " + JSON.stringify(configuration.virtualDriver, null, 2));
34
-
35
42
  // either connect anew or re-use an existing connection to Cloud CMS for this application
36
43
  Gitana.connect(configuration.virtualDriver, function(err) {
37
-
38
- //console.log("a2:" + err);
44
+
39
45
  if (err)
40
46
  {
41
47
  return callback(err);
@@ -56,59 +62,66 @@ exports = module.exports = function()
56
62
  {
57
63
  var configuration = process.configuration;
58
64
 
59
- if (configuration.virtualDriver && configuration.virtualDriver.enabled)
65
+ if (!configuration.virtualDriver || !configuration.virtualDriver.enabled)
60
66
  {
61
- connectAsVirtualDriver(function(err, gitana) {
67
+ return callback();
68
+ }
62
69
 
63
- if (err)
64
- {
65
- return callback(err);
66
- }
70
+ // no appkey, cannot load
71
+ if (!configuration.virtualDriver.appKey)
72
+ {
73
+ return callback();
74
+ }
67
75
 
68
- // no appkey, cannot load
69
- if (!configuration.virtualDriver.appKey)
70
- {
71
- return callback();
72
- }
76
+ connectAsVirtualDriver(function(err, gitana) {
73
77
 
74
- // Basic Authentication request back to server
75
- var qs = {};
76
- qs.h = host;
77
- qs.a = configuration.virtualDriver.appKey;
78
+ if (err)
79
+ {
80
+ return callback(err);
81
+ }
78
82
 
79
- if (configuration.virtualDriver && configuration.virtualDriver.webhost)
80
- {
81
- qs.w = configuration.virtualDriver.webhost;
82
- }
83
+ // Basic Authentication request back to server
84
+ var qs = {};
85
+ qs.h = host;
86
+ qs.a = configuration.virtualDriver.appKey;
83
87
 
84
- var URL = configuration.virtualDriver.baseURL;
85
- if (!URL) {
86
- URL = util.asURL(process.env.GITANA_PROXY_SCHEME, process.env.GITANA_PROXY_HOST, process.env.GITANA_PROXY_PORT, process.env.GITANA_PROXY_PATH);
87
- }
88
- URL += "/virtual/driver/config";
89
- var requestConfig = {
90
- "url": URL,
91
- "qs": qs
92
- };
88
+ if (configuration.virtualDriver && configuration.virtualDriver.webhost)
89
+ {
90
+ qs.w = configuration.virtualDriver.webhost;
91
+ }
93
92
 
94
- util.retryGitanaRequest(logMethod, gitana, requestConfig, 2, function(err, response, json) {
93
+ var URL = configuration.virtualDriver.baseURL;
94
+ if (!URL) {
95
+ URL = util.asURL(process.env.GITANA_PROXY_SCHEME, process.env.GITANA_PROXY_HOST, process.env.GITANA_PROXY_PORT, process.env.GITANA_PROXY_PATH);
96
+ }
97
+ URL += "/virtual/driver/config";
98
+ var requestConfig = {
99
+ "url": URL,
100
+ "qs": qs
101
+ };
95
102
 
96
- if (response && response.status === 200 && json)
97
- {
98
- var config = json.config;
99
- if (!config)
100
- {
101
- // nothing found
102
- return callback();
103
- }
103
+ util.retryGitanaRequest(logMethod, gitana, requestConfig, 2, function(err, response, body) {
104
104
 
105
+ if (response && response.status === 200 && body)
106
+ {
107
+ var config = body.config;
108
+ if (!config)
109
+ {
110
+ // nothing found
111
+ callback();
112
+ }
113
+ else
114
+ {
105
115
  // make sure we update baseURL
106
116
  config.baseURL = configuration.virtualDriver.baseURL;
107
117
 
108
118
  // hand back
109
- return callback(null, config);
119
+ var disabled = body.disabled ? true : false;
120
+ callback(null, config, disabled);
110
121
  }
111
-
122
+ }
123
+ else
124
+ {
112
125
  logMethod("Load virtual driver config failed");
113
126
  if (response && response.status)
114
127
  {
@@ -117,10 +130,13 @@ exports = module.exports = function()
117
130
  if (err) {
118
131
  logMethod("Err: " + JSON.stringify(err));
119
132
  }
120
- if (json) {
121
- logMethod("Body: " + json);
133
+ if (body) {
134
+ logMethod("Body: " + body);
135
+ }
136
+ var message = null;
137
+ if (body) {
138
+ message = JSON.stringify(body);
122
139
  }
123
- var message = json;
124
140
  if (!message) {
125
141
  message = "Unable to load virtual driver configuration";
126
142
  }
@@ -130,24 +146,15 @@ exports = module.exports = function()
130
146
  disconnectVirtualDriver();
131
147
 
132
148
  // fire callback
133
- return callback({
149
+ callback({
134
150
  "message": message,
135
151
  "err": err
136
152
  });
137
- });
153
+ }
138
154
  });
139
- }
140
- else
141
- {
142
- callback();
143
- }
155
+ });
144
156
  };
145
157
 
146
- // var _LOCK = function(lockKey, workFunction)
147
- // {
148
- // process.locks.lock(lockKey, workFunction);
149
- // };
150
-
151
158
  var r = {};
152
159
 
153
160
  /**
@@ -159,166 +166,188 @@ exports = module.exports = function()
159
166
 
160
167
  var VCSENTINEL_CACHE_KEY = "vcSentinelFailed-" + host;
161
168
 
162
- rootStore.existsFile("gitana.json", function(exists) {
169
+ // so that only N number of virtual configs are loaded at a time
170
+ var workFn = function(host, rootStore, logMethod) {
163
171
 
164
- var loadFromRemote = function(finishedLoading) {
172
+ return function(done)
173
+ {
174
+ rootStore.existsFile("gitana.json", function(exists) {
165
175
 
166
- // check cache to see if we already tried to load this in the past few minutes and were sorely disappointed
167
- process.cache.read(VCSENTINEL_CACHE_KEY, function (err, failedRecently) {
176
+ var loadFromRemote = function(finishedLoading) {
168
177
 
169
- if (failedRecently) {
170
- return finishedLoading({
171
- "message": "No virtual config found for host (from previous attempt)"
172
- });
173
- }
178
+ // check cache to see if we already tried to load this in the past few minutes and were sorely disappointed
179
+ process.cache.read(VCSENTINEL_CACHE_KEY, function (err, doesNotExist) {
174
180
 
175
- // load the gitana.json file from Cloud CMS
176
- loadConfigForVirtualHost(host, logMethod, function (err, virtualConfig) {
181
+ if (doesNotExist) {
182
+ return finishedLoading({
183
+ "message": "No virtual config found for host (from previous attempt)"
184
+ }, null, true);
185
+ }
177
186
 
178
- if (err)
179
- {
180
- // something failed, perhaps a network issue
181
- // don't store anything
182
- return finishedLoading(err);
183
- }
187
+ // load the gitana.json file from Cloud CMS
188
+ loadConfigForVirtualHost(host, logMethod, function (err, virtualConfig, disabled) {
184
189
 
185
- if (!virtualConfig)
186
- {
187
- // mark that it failed (5 seconds TTL)
188
- return process.cache.write(VCSENTINEL_CACHE_KEY, SENTINEL_NOT_FOUND_VALUE, 5, function() {
189
- finishedLoading({
190
- "message": "No virtual config found for host: " + host
191
- });
192
- });
193
- }
190
+ if (err)
191
+ {
192
+ // something failed, perhaps a network issue
193
+ // don't store anything
194
+ return finishedLoading(err);
195
+ }
194
196
 
195
- // populate gitana.json
196
- var gitanaJson = {
197
- "clientKey": virtualConfig.clientKey
198
- };
199
- if (virtualConfig.clientSecret) {
200
- gitanaJson.clientSecret = virtualConfig.clientSecret;
201
- }
202
- if (virtualConfig.username) {
203
- gitanaJson.username = virtualConfig.username;
204
- }
205
- if (virtualConfig.password) {
206
- gitanaJson.password = virtualConfig.password;
207
- }
208
- if (virtualConfig.application) {
209
- gitanaJson.application = virtualConfig.application;
210
- }
211
- if (virtualConfig.baseURL) {
212
- gitanaJson.baseURL = virtualConfig.baseURL;
213
- }
214
- if (!gitanaJson.baseURL)
215
- {
216
- gitanaJson.baseURL = util.cleanupURL(util.asURL(process.env.GITANA_PROXY_SCHEME, process.env.GITANA_PROXY_HOST, process.env.GITANA_PROXY_PORT, process.env.GITANA_PROXY_PATH));
217
- }
197
+ if (!virtualConfig)
198
+ {
199
+ // mark that it failed (30 day blacklist TTL)
200
+ return process.cache.write(VCSENTINEL_CACHE_KEY, true, BLACKLIST_TTL_SECONDS, function() {
201
+ finishedLoading({
202
+ "message": "No virtual config found for host: " + host
203
+ });
204
+ });
205
+ }
218
206
 
219
- // mark as retrieved from virtual driver
220
- gitanaJson._virtual = true;
207
+ if (disabled)
208
+ {
209
+ // mark that the virtual config is disabled (10 minutes TLL)
210
+ return process.cache.write(VCSENTINEL_CACHE_KEY, true, DISABLED_TTL_SECONDS, function() {
211
+ finishedLoading({
212
+ "message": "The virtual config was found for host: " + host + " but it has been marked as disabled"
213
+ });
214
+ });
215
+ }
221
216
 
222
- // write the gitana.json file
223
- rootStore.writeFile("gitana.json", JSON.stringify(gitanaJson, null, " "), function (err) {
217
+ // populate gitana.json
218
+ var gitanaJson = {
219
+ "clientKey": virtualConfig.clientKey
220
+ };
221
+ if (virtualConfig.clientSecret) {
222
+ gitanaJson.clientSecret = virtualConfig.clientSecret;
223
+ }
224
+ if (virtualConfig.username) {
225
+ gitanaJson.username = virtualConfig.username;
226
+ }
227
+ if (virtualConfig.password) {
228
+ gitanaJson.password = virtualConfig.password;
229
+ }
230
+ if (virtualConfig.application) {
231
+ gitanaJson.application = virtualConfig.application;
232
+ }
233
+ if (virtualConfig.baseURL) {
234
+ gitanaJson.baseURL = virtualConfig.baseURL;
235
+ }
236
+ if (!gitanaJson.baseURL)
237
+ {
238
+ gitanaJson.baseURL = util.cleanupURL(util.asURL(process.env.GITANA_PROXY_SCHEME, process.env.GITANA_PROXY_HOST, process.env.GITANA_PROXY_PORT, process.env.GITANA_PROXY_PATH));
239
+ }
240
+
241
+ // mark as retrieved from virtual driver
242
+ gitanaJson._virtual = true;
243
+
244
+ // write the gitana.json file
245
+ rootStore.writeFile("gitana.json", JSON.stringify(gitanaJson, null, " "), function (err) {
246
+
247
+ // if we failed to write the file, then delete and call back with error
248
+ if (err)
249
+ {
250
+ return rootStore.deleteFile("gitana.json", function() {
251
+ finishedLoading(err);
252
+ });
253
+ }
254
+
255
+ // make sure the file wrote successfully
256
+ // check stats, ensure non-error and file size > 0
257
+ rootStore.fileStats("gitana.json", function(err, stats) {
258
+
259
+ // if we failed to read stats, then delete and call back with error
260
+ if (err || stats.size === 0)
261
+ {
262
+ return rootStore.deleteFile("gitana.json", function() {
263
+ finishedLoading({
264
+ "message": "There was a problem writing the driver configuration file. Please reload."
265
+ });
266
+ });
267
+ }
268
+
269
+ finishedLoading(null, gitanaJson);
270
+ });
271
+ });
272
+ });
273
+ });
274
+ };
275
+
276
+ if (exists)
277
+ {
278
+ // read gitana json and send back
279
+ rootStore.readFile("gitana.json", function(err, data) {
224
280
 
225
- // if we failed to write the file, then delete and call back with error
226
281
  if (err)
227
282
  {
228
- return rootStore.deleteFile("gitana.json", function() {
229
- finishedLoading(err);
230
- });
283
+ return done(err);
284
+ }
285
+
286
+ if (!data)
287
+ {
288
+ return done({
289
+ "message": "The gitana.json data read from disk was null or empty"
290
+ })
231
291
  }
232
292
 
233
- // make sure the file wrote successfully
234
- // check stats, ensure non-error and file size > 0
293
+ // make sure not size 0
235
294
  rootStore.fileStats("gitana.json", function(err, stats) {
236
295
 
237
- // if we failed to read stats, then delete and call back with error
296
+ if (err)
297
+ {
298
+ return done(err);
299
+ }
300
+
301
+ // if we failed to read stats or file size 0, then delete and call back with error
238
302
  if (err || stats.size === 0)
239
303
  {
240
304
  return rootStore.deleteFile("gitana.json", function() {
241
- finishedLoading({
305
+ done({
242
306
  "message": "There was a problem writing the driver configuration file. Please reload."
243
307
  });
244
308
  });
245
309
  }
246
310
 
247
- finishedLoading(null, gitanaJson);
248
- });
249
- });
250
- });
251
- });
252
- };
311
+ // remove vcSentinel if it exists
312
+ process.cache.remove(VCSENTINEL_CACHE_KEY);
253
313
 
254
- if (exists)
255
- {
256
- // read gitana json and send back
257
- rootStore.readFile("gitana.json", function(err, data) {
314
+ var gitanaJson = JSON.parse("" + data);
258
315
 
259
- if (err)
260
- {
261
- return callback(err);
262
- }
263
-
264
- if (!data)
265
- {
266
- return callback({
267
- "message": "The gitana.json data read from disk was null or empty"
268
- })
269
- }
316
+ // auto-upgrade the host?
317
+ if (gitanaJson.baseURL)
318
+ {
319
+ var newBaseURL = util.cleanupURL(gitanaJson.baseURL);
320
+ if (newBaseURL !== gitanaJson.baseURL)
321
+ {
322
+ console.log("Auto-upgrade gitana.json from: " + gitanaJson.baseURL + ", to: " + newBaseURL);
270
323
 
271
- // make sure not size 0
272
- rootStore.fileStats("gitana.json", function(err, stats) {
324
+ gitanaJson.baseURL = newBaseURL;
273
325
 
274
- if (err)
275
- {
276
- return callback(err);
277
- }
326
+ // write the gitana.json file
327
+ rootStore.writeFile("gitana.json", JSON.stringify(gitanaJson, null, " "), function (err) {
328
+ // nada
329
+ });
330
+ }
331
+ }
278
332
 
279
- // if we failed to read stats or file size 0, then delete and call back with error
280
- if (err || stats.size === 0)
281
- {
282
- return rootStore.deleteFile("gitana.json", function() {
283
- callback({
284
- "message": "There was a problem writing the driver configuration file. Please reload."
285
- });
333
+ // otherwise, fine!
334
+ done(null, gitanaJson);
286
335
  });
287
- }
288
-
289
- // remove vcSentinel if it exists
290
- process.cache.remove(VCSENTINEL_CACHE_KEY);
291
-
292
- var gitanaJson = JSON.parse("" + data);
293
-
294
- // auto-upgrade the host?
295
- if (gitanaJson.baseURL)
296
- {
297
- var newBaseURL = util.cleanupURL(gitanaJson.baseURL);
298
- if (newBaseURL !== gitanaJson.baseURL)
299
- {
300
- console.log("Auto-upgrade gitana.json from: " + gitanaJson.baseURL + ", to: " + newBaseURL);
301
-
302
- gitanaJson.baseURL = newBaseURL;
303
-
304
- // write the gitana.json file
305
- rootStore.writeFile("gitana.json", JSON.stringify(gitanaJson, null, " "), function (err) {
306
- // nada
307
- });
308
- }
309
- }
310
-
311
- // otherwise, fine!
312
- callback(null, gitanaJson);
313
- });
314
- });
315
- }
316
- else
317
- {
318
- loadFromRemote(function(err, gitanaJson) {
319
- callback(err, gitanaJson);
336
+ });
337
+ }
338
+ else
339
+ {
340
+ loadFromRemote(function(err, gitanaJson, doesNotExist) {
341
+ done(err, gitanaJson, doesNotExist);
342
+ });
343
+ }
320
344
  });
321
345
  }
346
+
347
+ }(host, rootStore, logMethod);
348
+
349
+ enqueueLoadVirtualConfig(workFn, function(err, gitanaJson, doesNotExist) {
350
+ callback(err, gitanaJson, doesNotExist);
322
351
  });
323
352
  };
324
353
 
@@ -342,15 +371,38 @@ exports = module.exports = function()
342
371
  configuration.key = "virtual";
343
372
  }
344
373
 
345
- var completionFunction = function (err, gitanaConfig) {
346
- if (err) {
347
- if (err.message) {
374
+ var completionFunction = function (err, gitanaConfig, doesNotExist)
375
+ {
376
+ if (doesNotExist)
377
+ {
378
+ // console.log("BLOCK, method: " + req.method + ", url: " + req.url);
379
+ // if (req.headers)
380
+ // {
381
+ // console.log(" -> headers: " + JSON.stringify(req.headers, null, 2));
382
+ // }
383
+ // if (req.query)
384
+ // {
385
+ // console.log(" -> query: " + JSON.stringify(req.query, null, 2));
386
+ // }
387
+ //
388
+ // are we being spoofed? kill the connection
389
+ res.blocked = true;
390
+ res.writeHead(503, { 'Content-Type': 'application/json' });
391
+ return res.end(JSON.stringify({"error": true, "message": "Bad Request."}));
392
+ }
393
+
394
+ if (err)
395
+ {
396
+ if (err.message)
397
+ {
348
398
  req.log(err.message);
349
399
  }
400
+
350
401
  return next();
351
402
  }
352
403
 
353
- if (gitanaConfig) {
404
+ if (gitanaConfig)
405
+ {
354
406
  // store config
355
407
  req.gitanaConfig = gitanaConfig;
356
408
 
@@ -372,7 +424,7 @@ exports = module.exports = function()
372
424
  if (cachedValue === SENTINEL_NOT_FOUND_VALUE)
373
425
  {
374
426
  // null means there verifiably isn't anything on disk (null used as sentinel marker)
375
- completionFunction();
427
+ completionFunction(null, null, true);
376
428
  }
377
429
  else
378
430
  {
@@ -383,29 +435,27 @@ exports = module.exports = function()
383
435
  else
384
436
  {
385
437
  // try to load from disk
386
- acquireGitanaJson(req.virtualHost, req.rootStore, req.log, function (err, gitanaConfig)
438
+ acquireGitanaJson(req.virtualHost, req.rootStore, req.log, function (err, gitanaConfig, doesNotExist)
387
439
  {
388
- if (err)
440
+ if (err && !doesNotExist)
389
441
  {
390
442
  return completionFunction(err);
391
443
  }
392
444
 
393
445
  if (gitanaConfig)
394
446
  {
395
- process.driverConfigCache.write(req.virtualHost, {
447
+ return process.driverConfigCache.write(req.virtualHost, {
396
448
  "config": gitanaConfig
397
449
  }, function (err) {
398
- completionFunction(null, gitanaConfig);
399
- });
400
- }
401
- else
402
- {
403
- // mark with sentinel
404
- process.driverConfigCache.write(req.virtualHost, SENTINEL_NOT_FOUND_VALUE, 5, function (err)
405
- {
406
- completionFunction();
450
+ completionFunction(err, gitanaConfig);
407
451
  });
408
452
  }
453
+
454
+ // mark with sentinel (30 minutes)
455
+ req.log("[BLACKLIST] Adding: " + req.virtualHost);
456
+ process.driverConfigCache.write(req.virtualHost, SENTINEL_NOT_FOUND_VALUE, BLACKLIST_TTL_SECONDS, function (err) {
457
+ completionFunction(null, null, true);
458
+ });
409
459
  });
410
460
  }
411
461
  });