w-orm-mongodb 1.1.42 → 1.1.44

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.
@@ -45,6 +45,13 @@ let genUrl = (port) => {
45
45
  return `mongodb://${ctUser}:${ctPassword}@127.0.0.1:${port}`
46
46
  }
47
47
 
48
+ //genUrlRs, replica set容器之連線字串
49
+ //directConnection=true係因容器內之replica set成員位址為127.0.0.1:27017, 由宿主機依該位址連不到,
50
+ //故不可令驅動走成員探索, 須直連本次映射之對外埠
51
+ let genUrlRs = (port) => {
52
+ return `mongodb://127.0.0.1:${port}/?directConnection=true`
53
+ }
54
+
48
55
  //probeReady, 以mongodb驅動直連容器對外埠試跑ping
49
56
  //不使用容器內mongosh, 因mongo映像初始化期間會先啟動僅監聽unix socket之臨時服務,
50
57
  //容器內探測會提早回報就緒, 直連TCP方為測試實際所走路徑
@@ -64,10 +71,28 @@ let probeReady = async (port) => {
64
71
  return ok
65
72
  }
66
73
 
67
- //startContainer, 創建容器並等待服務就緒, 回傳容器對外埠
74
+ //probeReadyRs, replica set容器須等到本節點成為PRIMARY方可寫入
75
+ let probeReadyRs = async (port) => {
76
+ let client = new mongodb.MongoClient(genUrlRs(port), { serverSelectionTimeoutMS: 2000 })
77
+ let ok = false
78
+ try {
79
+ let hello = await client.db('admin').command({ hello: 1 })
80
+ ok = hello.isWritablePrimary === true
81
+ }
82
+ catch (err) {
83
+ ok = false
84
+ }
85
+ finally {
86
+ await client.close().catch(() => {})
87
+ }
88
+ return ok
89
+ }
90
+
91
+ //runContainer, 創建容器並回傳對外埠
68
92
  //對外埠由getFreePort動態取得, 避免與既有MongoDB服務搶27017, 亦避免多測試檔並行時互搶同一埠
69
- //mongo映像以MONGO_INITDB_ROOT_USERNAME、MONGO_INITDB_ROOT_PASSWORD即建妥帳號, 無須另跑建置語句
70
- let startContainer = async (ctName) => {
93
+ //getFreePort取得之埠於關閉監聽至docker實際綁定之間可能已被他者佔用, 多測試檔並行時尤其容易發生,
94
+ //故綁定失敗即改取新埠重試
95
+ let runContainer = async (ctName, genArgs) => {
71
96
 
72
97
  //check docker
73
98
  await docker(['version', '--format', '{{.Server.Version}}'])
@@ -78,8 +103,7 @@ let startContainer = async (ctName) => {
78
103
  //清除前次殘留容器
79
104
  await docker(['rm', '-f', ctName]).catch(() => {})
80
105
 
81
- //run, getFreePort取得之埠於關閉監聽至docker實際綁定之間可能已被他者佔用,
82
- //多測試檔並行時尤其容易發生, 故綁定失敗即改取新埠重試
106
+ //run
83
107
  let ctPort = null
84
108
  let nTry = 10
85
109
  for (let i = 0; i < nTry; i++) {
@@ -89,7 +113,7 @@ let startContainer = async (ctName) => {
89
113
 
90
114
  //run
91
115
  let err = null
92
- await docker(['run', '-d', '--rm', '--name', ctName, '-e', `MONGO_INITDB_ROOT_USERNAME=${ctUser}`, '-e', `MONGO_INITDB_ROOT_PASSWORD=${ctPassword}`, '-p', `${ctPort}:27017`, ctImage])
116
+ await docker(genArgs(ctPort))
93
117
  .catch((e) => {
94
118
  err = e
95
119
  })
@@ -111,20 +135,75 @@ let startContainer = async (ctName) => {
111
135
 
112
136
  }
113
137
 
114
- //等待服務就緒, 首次需拉取映像故給予較長時間
115
- let ready = false
138
+ return ctPort
139
+ }
140
+
141
+ //waitReady, 輪詢至就緒, 首次需拉取映像故給予較長時間
142
+ let waitReady = async (fnProbe, port, msgErr) => {
116
143
  for (let i = 0; i < 90; i++) {
117
- let ok = await probeReady(ctPort)
144
+ let ok = await fnProbe(port)
118
145
  if (ok) {
119
- ready = true
146
+ return
147
+ }
148
+ await delay(2000)
149
+ }
150
+ throw new Error(msgErr)
151
+ }
152
+
153
+ //startContainer, 創建standalone容器並等待服務就緒, 回傳容器對外埠
154
+ //mongo映像以MONGO_INITDB_ROOT_USERNAME、MONGO_INITDB_ROOT_PASSWORD即建妥帳號, 無須另跑建置語句
155
+ let startContainer = async (ctName) => {
156
+
157
+ //run
158
+ let ctPort = await runContainer(ctName, (port) => {
159
+ return ['run', '-d', '--rm', '--name', ctName, '-e', `MONGO_INITDB_ROOT_USERNAME=${ctUser}`, '-e', `MONGO_INITDB_ROOT_PASSWORD=${ctPassword}`, '-p', `${port}:27017`, ctImage]
160
+ })
161
+
162
+ //waitReady
163
+ await waitReady(probeReady, ctPort, 'mongodb容器啟動逾時')
164
+
165
+ return ctPort
166
+ }
167
+
168
+ //startContainerRs, 創建replica set容器並等待PRIMARY就緒, 回傳容器對外埠
169
+ //交易須replica set或分片叢集方可用, standalone不支援, 故驗證交易路徑須另起本容器
170
+ //本容器不開啟認證: --replSet配合MONGO_INITDB_ROOT_*須另備keyFile以供成員間內部認證, 於測試無必要
171
+ let startContainerRs = async (ctName) => {
172
+
173
+ //run
174
+ let ctPort = await runContainer(ctName, (port) => {
175
+ return ['run', '-d', '--rm', '--name', ctName, '-p', `${port}:27017`, ctImage, '--replSet', 'rs0', '--bind_ip_all']
176
+ })
177
+
178
+ //等待服務可回應, 此時尚未initiate故非PRIMARY
179
+ let up = false
180
+ for (let i = 0; i < 90; i++) {
181
+ let ok = await docker(['exec', ctName, 'mongosh', '--quiet', '--eval', 'db.adminCommand({ping:1}).ok'])
182
+ .then(() => true)
183
+ .catch(() => false)
184
+ if (ok) {
185
+ up = true
120
186
  break
121
187
  }
122
188
  await delay(2000)
123
189
  }
124
- if (!ready) {
125
- throw new Error('mongodb容器啟動逾時')
190
+ if (!up) {
191
+ throw new Error('mongodb replica set容器啟動逾時')
126
192
  }
127
193
 
194
+ //initiate, 單成員之replica set即足以啟用交易
195
+ await docker(['exec', ctName, 'mongosh', '--quiet', '--eval', 'rs.initiate({_id:"rs0",members:[{_id:0,host:"127.0.0.1:27017"}]})'])
196
+ .catch((err) => {
197
+ //已initiate者再次呼叫會報AlreadyInitialized, 屬正常
198
+ let msg = `${err.stderr || ''}${err.message || ''}`
199
+ if (msg.indexOf('AlreadyInitialized') < 0) {
200
+ throw err
201
+ }
202
+ })
203
+
204
+ //waitReady, 等待本節點成為PRIMARY
205
+ await waitReady(probeReadyRs, ctPort, 'mongodb replica set選舉逾時')
206
+
128
207
  return ctPort
129
208
  }
130
209
 
@@ -137,6 +216,8 @@ let stopContainer = async (ctName) => {
137
216
  export {
138
217
  delay,
139
218
  genUrl,
219
+ genUrlRs,
140
220
  startContainer,
221
+ startContainerRs,
141
222
  stopContainer
142
223
  }
@@ -7,7 +7,7 @@ let fdTar = './dist'
7
7
 
8
8
 
9
9
  rollupFiles({
10
- fns: getFiles(fdSrc),
10
+ fns: 'WOrmMongodb.mjs',
11
11
  fdSrc,
12
12
  fdTar,
13
13
  nameDistType: 'kebabCase',