-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy paths3-lock.js
162 lines (141 loc) · 3.8 KB
/
s3-lock.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
'use strict'
import PATH from 'path'
import { fromString as uint8ArrayFromString } from 'uint8arrays'
/**
* Uses an object in an S3 bucket as a lock to signal that an IPFS repo is in use.
* When the object exists, the repo is in use. You would normally use this to make
* sure multiple IPFS nodes don’t use the same S3 bucket as a datastore at the same time.
*/
/**
* @typedef {import('ipfs-repo').LockCloser} LockCloser
*/
export class S3Lock {
/**
* @param {import('aws-sdk/clients/s3')} s3
*/
constructor (s3) {
this.s3 = s3
const {
config: {
params: {
Bucket
} = {}
} = {}
} = s3
if (typeof Bucket !== 'string') {
throw new Error('An S3 instance with a predefined Bucket must be supplied. See the datastore-s3 README for examples.')
}
this.bucket = Bucket
}
/**
* Returns the location of the lock file given the path it should be located at
*
* @private
* @param {string} dir
* @returns {string}
*/
getLockfilePath (dir) {
return PATH.join(dir, 'repo.lock')
}
/**
* Creates the lock. This can be overridden to customize where the lock should be created
*
* @param {string} dir
* @returns {Promise<LockCloser>}
*/
async lock (dir) {
const lockPath = this.getLockfilePath(dir)
let alreadyLocked, err
try {
alreadyLocked = await this.locked(dir)
} catch (e) {
err = e
}
if (err || alreadyLocked) {
throw new Error('The repo is already locked')
}
// There's no lock yet, create one
await this.s3.putObject({
Bucket: this.bucket,
Key: lockPath,
Body: Buffer.from('')
}).promise()
return this.getCloser(lockPath)
}
/**
* Returns a LockCloser, which has a `close` method for removing the lock located at `lockPath`
*
* @param {string} lockPath
* @returns {LockCloser}
*/
getCloser (lockPath) {
const closer = {
/**
* Removes the lock. This can be overridden to customize how the lock is removed. This
* is important for removing any created locks.
*/
close: async () => {
try {
await this.s3.deleteObject({
Bucket: this.bucket,
Key: lockPath
}).promise()
} catch (err) {
if (err.statusCode !== 404) {
throw err
}
}
}
}
/**
* @param {Error} [err]
*/
const cleanup = async (err) => {
if (err instanceof Error) {
console.log('\nAn Uncaught Exception Occurred:\n', err)
} else if (err) {
console.log('\nReceived a shutdown signal:', err)
}
console.log('\nAttempting to cleanup gracefully...')
try {
await closer.close()
} catch (e) {
console.log('Caught error cleaning up: %s', e.message)
}
console.log('Cleanup complete, exiting.')
process.exit()
}
// listen for graceful termination
process.on('SIGTERM', cleanup)
process.on('SIGINT', cleanup)
process.on('SIGHUP', cleanup)
process.on('uncaughtException', cleanup)
return closer
}
/**
* Calls back on whether or not a lock exists. Override this method to customize how the check is made.
*
* @param {string} dir
* @returns {Promise<boolean>}
*/
async locked (dir) {
console.log("buckedt is ", this.bucket)
console.log("directory to look for is ", this.getLockfilePath(dir));
try {
x = await this.s3.getObject({
Bucket: this.bucket,
Key: this.getLockfilePath(dir)
}).promise()
} catch (err) {
if (err.code === 'ERR_NOT_FOUND') {
return false
} else if (err.code === 'NoSuchKey') {
return false
}
console.log("CAUGHT ERROR ", err)
throw err
}
console.log("x is ", x)
return true
}
}