@naanlang/naan 1.4.3 → 1.5.0
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.
- package/LICENSE.md +2 -2
- package/README.md +4 -4
- package/bin/index.js +22 -3
- package/dist/env_web.js +2 -2
- package/dist/naan.min.js +6 -6
- package/frameworks/browser/browser.nlg +2 -2
- package/frameworks/browser/https_request.nlg +39 -10
- package/frameworks/browser/sworker.js +23 -23
- package/frameworks/browser/terminals.nlg +33 -24
- package/frameworks/client/apiclient.nlg +31 -18
- package/frameworks/client/client.nlg +22 -2
- package/frameworks/client/psm_client.nlg +12 -3
- package/frameworks/node/filesystem.nlg +9 -7
- package/frameworks/node/gitter.nlg +9 -7
- package/frameworks/node/http_request.nlg +103 -0
- package/frameworks/node/https_request.nlg +42 -9
- package/frameworks/node/node.nlg +1 -0
- package/frameworks/node/shell.nlg +4 -4
- package/frameworks/node/worker.nlg +1 -0
- package/frameworks/project/projects.nlg +2 -1
- package/frameworks/running/executors.nlg +26 -7
- package/frameworks/running/taskexec.nlg +48 -14
- package/frameworks/storage/dbt_pouch.nlg +26 -13
- package/frameworks/storage/psm.nlg +39 -5
- package/frameworks/storage/psm_dbtables.nlg +10 -2
- package/lib/browser/env_web.js +9 -9
- package/lib/browser/env_webworker.js +1 -2
- package/lib/core/naanlib.js +6 -6
- package/lib/env_node.js +9 -3
- package/lib/node_repl_init.nlg +2 -2
- package/package.json +1 -1
- package/plugins/nodeLib/node_util.nlg +14 -7
- package/plugins/openSearch/os_dynamo.nlg +5 -5
- package/plugins/serviceAws/aws_cloudwatchlogs.nlg +2 -1
- package/plugins/serviceAws/aws_dynamo.nlg +26 -8
- package/plugins/serviceAws/aws_s3.nlg +3 -3
- package/plugins/serviceAws/aws_sqs.nlg +3 -1
- package/plugins/serviceAws/aws_utils.nlg +3 -1
- package/plugins/serviceAws/aws_vm.nlg +198 -0
- package/plugins/serviceAws/dbt_aws.nlg +22 -20
- package/plugins/serviceAws/psm_aws.nlg +2 -1
- package/plugins/serviceGC/gc_api.nlg +1 -0
- package/plugins/serviceNexara/serviceNexara.nlg +32 -0
- package/plugins/serviceNexara/speechrec.nlg +160 -0
- package/plugins/serviceYC/serviceYC.nlg +2 -3
- package/plugins/serviceYC/yc_speechrec.nlg +4 -3
- package/plugins/serviceYC/yc_vm.nlg +44 -54
- package/test/test_01_core.nlg +42 -33
- package/test/test_03_jsinterop.nlg +35 -6
- package/plugins/serviceGC/russian_trusted_root_ca_pem.crt +0 -33
package/lib/env_node.js
CHANGED
|
@@ -110,11 +110,13 @@ exports.NaanNodeREPL = function NaanNodeREPL(options) {
|
|
|
110
110
|
{ // Node REPL does too much crap
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
if (process.stdin.isTTY
|
|
113
|
+
if (process.stdin.isTTY)
|
|
114
114
|
{ // must have TTY for local REPL
|
|
115
115
|
replServer = repl.start({ prompt: '', eval: myEval});
|
|
116
116
|
|
|
117
117
|
replServer.on('line', function(cmd) { // process raw command line
|
|
118
|
+
if (options.replDisable)
|
|
119
|
+
return;
|
|
118
120
|
if (cmd.charAt(0) != ".") {
|
|
119
121
|
textToRemoteTerm("\x1b[90m\x1b[3m".concat(cmd, "\x1b[0m\n"));
|
|
120
122
|
naanlib.textLine(cmd + "\n");
|
|
@@ -126,9 +128,13 @@ exports.NaanNodeREPL = function NaanNodeREPL(options) {
|
|
|
126
128
|
//
|
|
127
129
|
|
|
128
130
|
replServer._events.SIGINT = function () {
|
|
129
|
-
|
|
131
|
+
if (options.replDisable) {
|
|
132
|
+
console.log("^C");
|
|
133
|
+
process.exit();
|
|
134
|
+
} else
|
|
135
|
+
naanlib.escape();
|
|
130
136
|
};
|
|
131
|
-
|
|
137
|
+
|
|
132
138
|
//
|
|
133
139
|
// Hook an exit command
|
|
134
140
|
//
|
package/lib/node_repl_init.nlg
CHANGED
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* column positioning: // // !
|
|
7
7
|
*
|
|
8
|
-
* Copyright (c) 2021-
|
|
8
|
+
* Copyright (c) 2021-2025 by Richard C. Zulch
|
|
9
9
|
*
|
|
10
10
|
*/
|
|
11
11
|
|
|
@@ -13,12 +13,11 @@
|
|
|
13
13
|
/*
|
|
14
14
|
* MultipartBody
|
|
15
15
|
*
|
|
16
|
-
* column positioning: // // !
|
|
17
16
|
* Create a multipart Form body from the specified dictionary, returning a buffer. Any xobject in
|
|
18
17
|
* the dictionary is coded as an octet. If you want a different filename, include an options object
|
|
19
18
|
* that defines that filename.
|
|
20
19
|
*
|
|
21
|
-
*The
|
|
20
|
+
*The return value is a dictionary as follows:
|
|
22
21
|
* {
|
|
23
22
|
* body: <Buffer>
|
|
24
23
|
* header: {
|
|
@@ -29,11 +28,17 @@
|
|
|
29
28
|
*
|
|
30
29
|
*/
|
|
31
30
|
|
|
32
|
-
function MultipartBody(dict, options,
|
|
31
|
+
function MultipartBody(dict, options,
|
|
32
|
+
local boundary, parts, key, data, item, filename) {
|
|
33
33
|
boundary = "xx-xx-xx-xx-xx"
|
|
34
34
|
parts = []
|
|
35
35
|
for `(key, data) in dict {
|
|
36
|
-
if
|
|
36
|
+
if Array.isArray(data) { // PHP-style arrays
|
|
37
|
+
for item in data
|
|
38
|
+
parts.push(xnew(js.g.Buffer.from, "--".concat(boundary, "\r\n",
|
|
39
|
+
"Content-Disposition: form-data; name=\"", key, "[]",
|
|
40
|
+
"\"; \r\n\r\n", item, "\r\n"), "utf8"))
|
|
41
|
+
} else if xobject(data) { // binary file data
|
|
37
42
|
if options[key]
|
|
38
43
|
filename = options[key]
|
|
39
44
|
else
|
|
@@ -43,8 +48,7 @@ function MultipartBody(dict, options, local boundary, parts, key, data, filename
|
|
|
43
48
|
"Content-Type:application/octet-stream\r\n\r\n"), "utf8"))
|
|
44
49
|
parts.push(xnew(js.g.Buffer.from, data, "binary"))
|
|
45
50
|
parts.push(xnew(js.g.Buffer.from, "\r\n", "utf8"))
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
51
|
+
} else { // ordinary data
|
|
48
52
|
parts.push(xnew(js.g.Buffer.from, "--".concat(boundary, "\r\n",
|
|
49
53
|
"Content-Disposition: form-data; name=\"", key,
|
|
50
54
|
"\"; \r\n\r\n", data, "\r\n"), "utf8"))
|
|
@@ -165,8 +169,11 @@ function nodeUtilInit(local manifest) {
|
|
|
165
169
|
Naan.module.build(module.id, "node_util", function(modobj, compobj) {
|
|
166
170
|
require("nodeLib.nlg")
|
|
167
171
|
compobj.manifest = manifest
|
|
172
|
+
modobj.exports.MultipartBody = MultipartBody
|
|
173
|
+
modobj.exports.EncodeQuery = EncodeQuery
|
|
168
174
|
modobj.exports.JsonParse = JsonParse
|
|
169
175
|
modobj.exports.JsonStringify = JsonStringify
|
|
176
|
+
modobj.exports.RandomBytes = RandomBytes
|
|
170
177
|
modobj.exports.UUID = UUID
|
|
171
178
|
function postload() {
|
|
172
179
|
nodeFs = js.r("fs")
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2023 by Richard C. Zulch
|
|
9
|
+
* Copyright (c) 2023-2025 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -65,7 +65,7 @@ closure OSDynIndexer(index, table, options,
|
|
|
65
65
|
// Update ids into the database, returning a nonce signaled with error array.
|
|
66
66
|
//
|
|
67
67
|
closure updateRecordIDs(docs, ids, writeID) {
|
|
68
|
-
asyncArray(docs, 10,
|
|
68
|
+
asyncArray(docs, 10, closure(entry, index, cancel, local error) {
|
|
69
69
|
ignore(error)
|
|
70
70
|
`(error) = table.updateRecord(
|
|
71
71
|
entry[table.hashKey],
|
|
@@ -78,11 +78,11 @@ closure OSDynIndexer(index, table, options,
|
|
|
78
78
|
//
|
|
79
79
|
// Batch index documents into OpenSearch, returning a nonce signaled with ids array.
|
|
80
80
|
//
|
|
81
|
-
closure batchIndex(records, local pending
|
|
81
|
+
closure batchIndex(records, local pending) {
|
|
82
82
|
pending = new(nonce)
|
|
83
|
-
docs = records
|
|
83
|
+
docs = asyncArray(records, 10, closure(record) {
|
|
84
84
|
options.converter(record)
|
|
85
|
-
})
|
|
85
|
+
}).wait()
|
|
86
86
|
future(function(local error, ids) {
|
|
87
87
|
`(error, ids) = index.write(docs)
|
|
88
88
|
pending.signal(list(error, records, ids))
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2021-
|
|
9
|
+
* Copyright (c) 2021-2025 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
closure CloudWatchLogs(local cwlogs) {
|
|
22
|
+
global(awsSDK)
|
|
22
23
|
cwlogs = new(object, this)
|
|
23
24
|
|
|
24
25
|
//
|
|
@@ -946,7 +946,7 @@ closure DynaTable(dyna, tablename, options, local table) {
|
|
|
946
946
|
// range: <key-expression> // range of keys to retrieve
|
|
947
947
|
// filter: <key-expression> // conditions for items to return
|
|
948
948
|
// reverse: <boolean> // reverse order
|
|
949
|
-
// project: <array>
|
|
949
|
+
// project: <array> | `count // project only specified attributes
|
|
950
950
|
// paging: <exclusive start key> // for paging; updated each call
|
|
951
951
|
// consistentRead: <boolean> // true to use consistent read
|
|
952
952
|
// limit: <number> // maximum count of results
|
|
@@ -962,6 +962,7 @@ closure DynaTable(dyna, tablename, options, local table) {
|
|
|
962
962
|
TableName: tablename
|
|
963
963
|
}
|
|
964
964
|
hashKey = table.hashKey
|
|
965
|
+
output = []
|
|
965
966
|
if options.indexName {
|
|
966
967
|
params.IndexName = options.indexName
|
|
967
968
|
if options.indexHashKey
|
|
@@ -989,15 +990,23 @@ closure DynaTable(dyna, tablename, options, local table) {
|
|
|
989
990
|
}
|
|
990
991
|
if options.reverse
|
|
991
992
|
params.ScanIndexForward = false
|
|
992
|
-
if options.project
|
|
993
|
+
if options.project == `count {
|
|
994
|
+
params.Select = "COUNT"
|
|
995
|
+
output = {
|
|
996
|
+
Count: 0
|
|
997
|
+
ScannedCount: 0
|
|
998
|
+
}
|
|
999
|
+
} else if options.project
|
|
993
1000
|
params.ProjectionExpression = options.project.join(", ")
|
|
994
1001
|
if options.paging
|
|
995
1002
|
params.ExclusiveStartKey = options.paging
|
|
996
1003
|
if options.consistentRead
|
|
997
1004
|
params.ConsistentRead = options.consistentRead
|
|
998
|
-
output = []
|
|
999
1005
|
`(error) = dynQueryPager(`query, params, options.limit, function(data) {
|
|
1000
|
-
if
|
|
1006
|
+
if dictionary(output) {
|
|
1007
|
+
output.Count += data.Count
|
|
1008
|
+
output.ScannedCount += data.ScannedCount
|
|
1009
|
+
} else if data.Count > 0
|
|
1001
1010
|
output = output.concat(dyna.conv.recordDynToNaan(data.Items))
|
|
1002
1011
|
data.Count
|
|
1003
1012
|
})
|
|
@@ -1016,7 +1025,7 @@ closure DynaTable(dyna, tablename, options, local table) {
|
|
|
1016
1025
|
// {
|
|
1017
1026
|
// index: <string> // index name in table
|
|
1018
1027
|
// filter: <key-expression> // conditions for items to return
|
|
1019
|
-
// project: <array>
|
|
1028
|
+
// project: <array> | `count // project only specified attributes
|
|
1020
1029
|
// paging: <exclusive start key> // for paging; updated each call
|
|
1021
1030
|
// consistentRead: <boolean> // true to use consistent read
|
|
1022
1031
|
// limit: <number> // maximum count of results
|
|
@@ -1031,6 +1040,7 @@ closure DynaTable(dyna, tablename, options, local table) {
|
|
|
1031
1040
|
params = {
|
|
1032
1041
|
TableName: tablename
|
|
1033
1042
|
}
|
|
1043
|
+
output = []
|
|
1034
1044
|
if options.index
|
|
1035
1045
|
params.IndexName = options.index
|
|
1036
1046
|
if options.filter {
|
|
@@ -1040,15 +1050,23 @@ closure DynaTable(dyna, tablename, options, local table) {
|
|
|
1040
1050
|
if error
|
|
1041
1051
|
return (list(Error("table.scanRecords filter:", error)))
|
|
1042
1052
|
}
|
|
1043
|
-
if options.project
|
|
1053
|
+
if options.project == `count {
|
|
1054
|
+
params.Select = "COUNT"
|
|
1055
|
+
output = {
|
|
1056
|
+
Count: 0
|
|
1057
|
+
ScannedCount: 0
|
|
1058
|
+
}
|
|
1059
|
+
} else if options.project
|
|
1044
1060
|
params.ProjectionExpression = options.project.join(", ")
|
|
1045
1061
|
if options.paging
|
|
1046
1062
|
params.ExclusiveStartKey = options.paging
|
|
1047
1063
|
if options.consistentRead
|
|
1048
1064
|
params.ConsistentRead = options.consistentRead
|
|
1049
|
-
output = []
|
|
1050
1065
|
`(error) = dynQueryPager(`scan, params, options.limit, function(data) {
|
|
1051
|
-
if
|
|
1066
|
+
if dictionary(output) {
|
|
1067
|
+
output.Count += data.Count
|
|
1068
|
+
output.ScannedCount += data.ScannedCount
|
|
1069
|
+
} else if data.Count >= 0
|
|
1052
1070
|
output = output.concat(dyna.conv.recordDynToNaan(data.Items))
|
|
1053
1071
|
data.Count
|
|
1054
1072
|
})
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2020-
|
|
9
|
+
* Copyright (c) 2020-2025 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
closure S3bucket(s3, name, bucketOptions, local bucket) {
|
|
25
|
+
global()
|
|
25
26
|
bucket = new(object, this)
|
|
26
27
|
bucket.name = name
|
|
27
28
|
|
|
@@ -83,8 +84,6 @@ closure S3bucket(s3, name, bucketOptions, local bucket) {
|
|
|
83
84
|
// }
|
|
84
85
|
//
|
|
85
86
|
bucket.put = closure put(key, data, options, local params, pending) {
|
|
86
|
-
if !contentType
|
|
87
|
-
contentType = "application/octet-stream"
|
|
88
87
|
params = {
|
|
89
88
|
Bucket: bucket.name
|
|
90
89
|
Key: key
|
|
@@ -246,6 +245,7 @@ closure S3bucket(s3, name, bucketOptions, local bucket) {
|
|
|
246
245
|
*/
|
|
247
246
|
|
|
248
247
|
closure S3(local s3) {
|
|
248
|
+
global(awsSDK)
|
|
249
249
|
s3 = new(object, this)
|
|
250
250
|
|
|
251
251
|
//
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2023 by Richard C. Zulch
|
|
9
|
+
* Copyright (c) 2023-2025 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
closure SqsMQ(sqs, queueUrl, local sqsq) {
|
|
22
|
+
global()
|
|
22
23
|
sqsq = new(object, this)
|
|
23
24
|
|
|
24
25
|
//
|
|
@@ -55,6 +56,7 @@ closure SqsMQ(sqs, queueUrl, local sqsq) {
|
|
|
55
56
|
*/
|
|
56
57
|
|
|
57
58
|
closure SQS(local sqs) {
|
|
59
|
+
global(awsSDK)
|
|
58
60
|
sqs = new(object, this)
|
|
59
61
|
|
|
60
62
|
//
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2021 by Richard C. Zulch
|
|
9
|
+
* Copyright (c) 2021-2025 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
closure queryPager(apiobject, procname, params, xargs, doneCB, local pending, looping, result) {
|
|
25
|
+
global()
|
|
25
26
|
if !apiobject[procname]
|
|
26
27
|
return (list(Error("queryPager method undefined:", procname)))
|
|
27
28
|
pending = new(nonce)
|
|
@@ -55,6 +56,7 @@ closure queryPager(apiobject, procname, params, xargs, doneCB, local pending, lo
|
|
|
55
56
|
*/
|
|
56
57
|
|
|
57
58
|
function QueryPager args {
|
|
59
|
+
global()
|
|
58
60
|
closure(local apiobject, procname, params, xargs, doneCB) {
|
|
59
61
|
apiobject = pop(args)
|
|
60
62
|
procname = pop(args)
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* aws_vm.nlg
|
|
3
|
+
* serviceAws
|
|
4
|
+
*
|
|
5
|
+
* VM controller under AWS.
|
|
6
|
+
*
|
|
7
|
+
* column positioning: // // !
|
|
8
|
+
*
|
|
9
|
+
* Copyright (c) 2024-2025 by Richard C. Zulch
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
vmState = false;
|
|
14
|
+
|
|
15
|
+
/*
|
|
16
|
+
* VmControl
|
|
17
|
+
*
|
|
18
|
+
* VM control object for AWS.
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
closure VmControl(creds, local dokon) {
|
|
23
|
+
global(js, http, vmState)
|
|
24
|
+
if vmState.dokon
|
|
25
|
+
return (vmState.dokon) // singleton
|
|
26
|
+
dokon = new(object, this)
|
|
27
|
+
vmState = new(nonce) // don't save with state
|
|
28
|
+
vmState.dokon = dokon
|
|
29
|
+
|
|
30
|
+
// detachVM
|
|
31
|
+
//
|
|
32
|
+
// Tell AWS to detach a VM from its scalability group. Returns a tuple with:
|
|
33
|
+
// {
|
|
34
|
+
// Activities: [
|
|
35
|
+
// {
|
|
36
|
+
// ActivityId: "STRING_VALUE",
|
|
37
|
+
// AutoScalingGroupName: "STRING_VALUE",
|
|
38
|
+
// Description: "STRING_VALUE",
|
|
39
|
+
// Cause: "STRING_VALUE",
|
|
40
|
+
// StartTime: new Date("TIMESTAMP"),
|
|
41
|
+
// EndTime: new Date("TIMESTAMP"),
|
|
42
|
+
// StatusCode: "PendingSpotBidPlacement" || "WaitingForSpotInstanceRequestId"
|
|
43
|
+
// || "WaitingForSpotInstanceId" || "WaitingForInstanceId" || "PreInService"
|
|
44
|
+
// || "InProgress" || "WaitingForELBConnectionDraining" || "MidLifecycleAction"
|
|
45
|
+
// || "WaitingForInstanceWarmup" || "Successful" || "Failed" || "Cancelled"
|
|
46
|
+
// || "WaitingForConnectionDraining",
|
|
47
|
+
// StatusMessage: "STRING_VALUE",
|
|
48
|
+
// Progress: Number("int"),
|
|
49
|
+
// Details: "STRING_VALUE",
|
|
50
|
+
// AutoScalingGroupState: "STRING_VALUE",
|
|
51
|
+
// AutoScalingGroupARN: "STRING_VALUE",
|
|
52
|
+
// }
|
|
53
|
+
// ]
|
|
54
|
+
// }
|
|
55
|
+
closure detachVM(instanceID, local error, ascSdk, params, asclient, response) {
|
|
56
|
+
`(error, ascSdk) = await(js.i("@aws-sdk/client-auto-scaling"))
|
|
57
|
+
if error
|
|
58
|
+
return (list(error))
|
|
59
|
+
params = { }
|
|
60
|
+
params.region = creds.region
|
|
61
|
+
params.credentials = {
|
|
62
|
+
accessKeyId: creds.keyID
|
|
63
|
+
secretAccessKey: creds.keySecret
|
|
64
|
+
}
|
|
65
|
+
if creds.endpoint
|
|
66
|
+
params.endpoint = creds.endpoint
|
|
67
|
+
asclient = xnew(ascSdk.AutoScalingClient, params)
|
|
68
|
+
`(error, response) = await(asclient.send(xnew(ascSdk.DetachInstancesCommand, {
|
|
69
|
+
AutoScalingGroupName: creds.ec2AsgName
|
|
70
|
+
InstanceIds: [ instanceID ]
|
|
71
|
+
ShouldDecrementDesiredCapacity: false
|
|
72
|
+
})))
|
|
73
|
+
list(error, response)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// terminateVM
|
|
77
|
+
//
|
|
78
|
+
// Tell AWS to terminate a VM, ours in point of fact. Returns a tuple with:
|
|
79
|
+
// {
|
|
80
|
+
// TerminatingInstances: [
|
|
81
|
+
// {
|
|
82
|
+
// InstanceId: "STRING_VALUE"
|
|
83
|
+
// CurrentState: { // InstanceState
|
|
84
|
+
// Code: Number("int"),
|
|
85
|
+
// Name: "pending" || "running" || "shutting-down" || "terminated"
|
|
86
|
+
// || "stopping" || "stopped",
|
|
87
|
+
// }
|
|
88
|
+
// PreviousState: {
|
|
89
|
+
// Code: Number("int"),
|
|
90
|
+
// Name: "pending" || "running" || "shutting-down" || "terminated"
|
|
91
|
+
// || "stopping" || "stopped",
|
|
92
|
+
// }
|
|
93
|
+
// }
|
|
94
|
+
// ]
|
|
95
|
+
// }
|
|
96
|
+
//
|
|
97
|
+
closure terminateVM(instanceID, local error, ec2sdk, params, ec2client, response) {
|
|
98
|
+
`(error, ec2sdk) = await(js.i("@aws-sdk/client-ec2"))
|
|
99
|
+
if error
|
|
100
|
+
return (list(error))
|
|
101
|
+
params = { }
|
|
102
|
+
params.region = creds.region
|
|
103
|
+
params.credentials = {
|
|
104
|
+
accessKeyId: creds.keyID
|
|
105
|
+
secretAccessKey: creds.keySecret
|
|
106
|
+
}
|
|
107
|
+
if creds.endpoint
|
|
108
|
+
params.endpoint = creds.endpoint
|
|
109
|
+
ec2client = xnew(ec2sdk.EC2Client, params)
|
|
110
|
+
`(error, response) = await(ec2client.send(xnew(ec2sdk.TerminateInstancesCommand,
|
|
111
|
+
{ InstanceIds: [ instanceID ] })))
|
|
112
|
+
list(error, response)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// getHostname
|
|
116
|
+
//
|
|
117
|
+
// Ask the VM for its FQDN.
|
|
118
|
+
//
|
|
119
|
+
// TOKEN="`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`"
|
|
120
|
+
// curl -H "X-aws-ec2-metadata-token: $TOKEN" 'http://169.254.169.254/latest/meta-data/hostname'
|
|
121
|
+
// => ip-172-31-80-96.ec2.internal
|
|
122
|
+
//
|
|
123
|
+
dokon.getHostname = closure getHostname(local error, token, hostname) {
|
|
124
|
+
`(error, token) = http.HttpRequest("http://169.254.169.254/latest/api/token", {
|
|
125
|
+
headers: {
|
|
126
|
+
"X-aws-ec2-metadata-token-ttl-seconds": "2160"
|
|
127
|
+
}
|
|
128
|
+
method: "PUT"
|
|
129
|
+
})
|
|
130
|
+
if !error
|
|
131
|
+
`(error, hostname) = http.HttpRequest("http://169.254.169.254/latest/meta-data/hostname", {
|
|
132
|
+
headers: {
|
|
133
|
+
"X-aws-ec2-metadata-token": token
|
|
134
|
+
}
|
|
135
|
+
})
|
|
136
|
+
if error
|
|
137
|
+
list(error)
|
|
138
|
+
else
|
|
139
|
+
list(false, hostname)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// vmShutdown
|
|
143
|
+
//
|
|
144
|
+
// Get our AWS EC2 VM instance ID and then request that it be shut down.
|
|
145
|
+
//
|
|
146
|
+
// How to get auth token:
|
|
147
|
+
// curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"
|
|
148
|
+
// => AQAEAJHVrb_zRdju_A3tYBJLtKHsjej5GFVsDu1VuBCErP8MNVOr0Q==
|
|
149
|
+
//
|
|
150
|
+
// How to get instance-id:
|
|
151
|
+
// curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-id
|
|
152
|
+
// => i-008dc3846da316a93
|
|
153
|
+
//
|
|
154
|
+
|
|
155
|
+
dokon.shutdown = closure shutdown(local error, token, instanceID, response) {
|
|
156
|
+
`(error, token) = http.HttpRequest("http://169.254.169.254/latest/api/token", {
|
|
157
|
+
headers: {
|
|
158
|
+
"X-aws-ec2-metadata-token-ttl-seconds": "2160"
|
|
159
|
+
}
|
|
160
|
+
method: "PUT"
|
|
161
|
+
})
|
|
162
|
+
if !error
|
|
163
|
+
`(error, instanceID) = http.HttpRequest("http://169.254.169.254/latest/meta-data/instance-id", {
|
|
164
|
+
headers: {
|
|
165
|
+
"X-aws-ec2-metadata-token": token
|
|
166
|
+
}
|
|
167
|
+
})
|
|
168
|
+
if !error
|
|
169
|
+
`(error, response) = detachVM(instanceID)
|
|
170
|
+
if !error
|
|
171
|
+
`(error, response) = terminateVM(instanceID)
|
|
172
|
+
if error
|
|
173
|
+
list(error)
|
|
174
|
+
else
|
|
175
|
+
list(false, response)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// finis
|
|
179
|
+
dokon
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
/*
|
|
184
|
+
* vmConInit
|
|
185
|
+
*
|
|
186
|
+
* Initialize the component.
|
|
187
|
+
*
|
|
188
|
+
*/
|
|
189
|
+
|
|
190
|
+
function vmConInit(local manifest) {
|
|
191
|
+
manifest = `(VmControl, vmConInit)
|
|
192
|
+
Naan.module.build(module.id, "aws_vm", function(modobj, compobj) {
|
|
193
|
+
require("./serviceAws.nlg")
|
|
194
|
+
compobj.manifest = manifest
|
|
195
|
+
http = require("naanlib:frameworks/node/http_request.nlg")
|
|
196
|
+
module.exports.VmControl = VmControl
|
|
197
|
+
})
|
|
198
|
+
} ();
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2020-
|
|
9
|
+
* Copyright (c) 2020-2025 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
closure dawsBucket(s3b, local bucket, s3) {
|
|
25
|
+
global(JSpath)
|
|
25
26
|
bucket = new(object, this)
|
|
26
27
|
s3 = s3b.s3
|
|
27
28
|
bucket.name = s3b.bucketName
|
|
@@ -311,6 +312,7 @@ closure dawsBucket(s3b, local bucket, s3) {
|
|
|
311
312
|
*/
|
|
312
313
|
|
|
313
314
|
closure dawsTable(database, path, tableOptions, local table) {
|
|
315
|
+
global(js, TextDecoder)
|
|
314
316
|
table = new(object, this)
|
|
315
317
|
table.type = database.type
|
|
316
318
|
table.db = database
|
|
@@ -411,9 +413,10 @@ closure dawsTable(database, path, tableOptions, local table) {
|
|
|
411
413
|
|
|
412
414
|
// add
|
|
413
415
|
//
|
|
414
|
-
// Add a new record to the table with
|
|
415
|
-
//
|
|
416
|
-
//
|
|
416
|
+
// Add a new record to the table with options. This will fail if the key already exists. Optional
|
|
417
|
+
// metadata is stored with the record and cannot be changed without calling update. Metadata keys
|
|
418
|
+
// cannot begin with underscore ("_"). If optional cacheControl is not specified then the default
|
|
419
|
+
// tableOptions.cacheControl is used.
|
|
417
420
|
//
|
|
418
421
|
// The returned document, which can be used with update, has the following keys:
|
|
419
422
|
// key - the key name of the record
|
|
@@ -421,26 +424,25 @@ closure dawsTable(database, path, tableOptions, local table) {
|
|
|
421
424
|
// md5 - the MD5 hash of the content
|
|
422
425
|
// <...> - the various metadata keys
|
|
423
426
|
|
|
424
|
-
table.add = closure add(key, content,
|
|
427
|
+
table.add = closure add(key, content, options, callback, local id, doc) {
|
|
425
428
|
if !callback
|
|
426
|
-
return (syncAdapter(add, key, content,
|
|
429
|
+
return (syncAdapter(add, key, content, options))
|
|
427
430
|
id = makeID(key)
|
|
428
431
|
if !string(id)
|
|
429
|
-
return (asyncResult(callback, id))
|
|
430
|
-
if metadata && !dictionary(metadata)
|
|
432
|
+
return (asyncResult(callback, id)) // error from makeID
|
|
433
|
+
if options.metadata && !dictionary(options.metadata)
|
|
431
434
|
return (asyncResult(callback, Error("invalid arguments")))
|
|
432
|
-
if metadata
|
|
433
|
-
doc = new(metadata)
|
|
435
|
+
if options.metadata
|
|
436
|
+
doc = new(options.metadata) // start with metadata
|
|
434
437
|
else
|
|
435
438
|
doc = { }
|
|
436
439
|
doc.key = key
|
|
437
440
|
doc.id = id
|
|
438
441
|
if content
|
|
439
442
|
doc.content = content
|
|
440
|
-
metadata = metadata2s3(metadata) // filter out any invalid metadata
|
|
441
443
|
database.bucket.put(id, content, {
|
|
442
|
-
metadata: metadata
|
|
443
|
-
cacheControl: tableOptions.cacheControl
|
|
444
|
+
metadata: metadata2s3(options.metadata) // filter metadata for s3
|
|
445
|
+
cacheControl: options.cacheControl || tableOptions.cacheControl
|
|
444
446
|
}, function(error, resp) {
|
|
445
447
|
if error
|
|
446
448
|
error = Error("add failed", error, id, { status: error.status })
|
|
@@ -453,17 +455,17 @@ closure dawsTable(database, path, tableOptions, local table) {
|
|
|
453
455
|
|
|
454
456
|
// update
|
|
455
457
|
//
|
|
456
|
-
// Update a record in the table using the document previously returned by add or get.
|
|
458
|
+
// Update a record in the table using the document previously returned by add or get. Specify
|
|
459
|
+
// options to update those values, otherwise existing/default values are used.
|
|
457
460
|
|
|
458
|
-
table.update = closure update(doc,
|
|
461
|
+
table.update = closure update(doc, options, callback) {
|
|
459
462
|
if !callback
|
|
460
|
-
return (syncAdapter(update, doc))
|
|
463
|
+
return (syncAdapter(update, doc, options))
|
|
461
464
|
if !doc._id || !doc._id.startsWith(table.s3prefix) || !doc._md5
|
|
462
465
|
return (asyncResult(callback, Error("invalid argument")))
|
|
463
|
-
metadata = metadata2s3(doc)
|
|
464
466
|
database.bucket.put(doc._id, doc.content, {
|
|
465
|
-
metadata: metadata
|
|
466
|
-
cacheControl: tableOptions.cacheControl
|
|
467
|
+
metadata: metadata2s3(options.metadata || doc)
|
|
468
|
+
cacheControl: options.cacheControl || tableOptions.cacheControl
|
|
467
469
|
}, function(error, resp) {
|
|
468
470
|
if error
|
|
469
471
|
error = Error("update failed", error, doc._id, { status: error.status })
|
|
@@ -519,7 +521,7 @@ closure dawsTable(database, path, tableOptions, local table) {
|
|
|
519
521
|
resp.content = readableStreamToArrayBuffer(resp.content)
|
|
520
522
|
else if jsTypedArray(resp.content)
|
|
521
523
|
resp.content = xnew(TextDecoder, "utf-8").decode(resp.content)
|
|
522
|
-
else if jsInstanceOf(resp.content, js.w.ReadableStream)
|
|
524
|
+
else if jsInstanceOf(resp.content, js.w.ReadableStream) // for browsers only
|
|
523
525
|
resp.content = xnew(TextDecoder, "utf-8").decode(readableStreamToArrayBuffer(resp.content))
|
|
524
526
|
resp._id = id }
|
|
525
527
|
callback(error, resp)
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2020-
|
|
9
|
+
* Copyright (c) 2020-2025 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
closure psmaConnector(psm, local connClassID, connector, watch) {
|
|
22
|
+
global(awsSDK)
|
|
22
23
|
connClassID = "S3DB"
|
|
23
24
|
require("./dbt_aws.nlg")
|
|
24
25
|
connector = new(object, this)
|
|
@@ -83,6 +83,7 @@ closure GcApi(options, local gcapi, cacert, renew, access_token, expires_at) {
|
|
|
83
83
|
`(error, data, status) = https.HttpsApiRequest(url, {
|
|
84
84
|
putdata: put || undefined
|
|
85
85
|
cacert: cacert || undefined
|
|
86
|
+
debug: options.debug || undefined
|
|
86
87
|
headers: [
|
|
87
88
|
`(Accept, "application/json"),
|
|
88
89
|
list(`Authorization, "Bearer ".concat(access_token))
|