Skip to content
This repository was archived by the owner on Oct 1, 2021. It is now read-only.

Commit 2148c6b

Browse files
committed
fix: require passing repo options to migrator
In order to successfully open a datastore we should accept config that tells us how to open the datastore. Refs: ipld/explore.ipld.io#65
1 parent c1e6403 commit 2148c6b

File tree

13 files changed

+315
-122
lines changed

13 files changed

+315
-122
lines changed

README.md

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Migration tool for JS IPFS Repo
1+
# Migration tool for JS IPFS Repo <!-- omit in toc -->
22

33
[![Travis CI](https://flat.badgen.net/travis/ipfs/js-ipfs-repo-migrations)](https://travis-ci.com/ipfs/js-ipfs-repo-migrations)
44
[![codecov](https://codecov.io/gh/ipfs/js-ipfs-repo-migrations/branch/master/graph/badge.svg)](https://codecov.io/gh/ipfs/js-ipfs-repo-migrations)
@@ -15,36 +15,41 @@
1515
1616
This package is inspired by the [go-ipfs repo migration tool](https://github.com/ipfs/fs-repo-migrations/)
1717

18-
## Lead Maintainer
18+
## Lead Maintainer <!-- omit in toc -->
1919

2020
[Adam Uhlíř](https://github.com/auhau/)
2121

22-
## Table of Contents
22+
## Table of Contents <!-- omit in toc -->
2323

2424
- [Background](#background)
2525
- [Install](#install)
2626
- [npm](#npm)
2727
- [Use in Node.js](#use-in-nodejs)
2828
- [Use in a browser with browserify, webpack or any other bundler](#use-in-a-browser-with-browserify-webpack-or-any-other-bundler)
29-
- [Use in a browser Using a script tag](#use-in-a-browser-using-a-script-tag)
3029
- [Usage](#usage)
3130
- [API](#api)
31+
- [`.migrate(path, toVersion, {ignoreLock, repoOptions, onProgress, isDryRun}) -> Promise<void>`](#migratepath-toversion-ignorelock-repooptions-onprogress-isdryrun---promisevoid)
32+
- [`onProgress(migration, counter, totalMigrations)`](#onprogressmigration-counter-totalmigrations)
33+
- [`.revert(path, toVersion, {ignoreLock, repoOptions, onProgress, isDryRun}) -> Promise<void>`](#revertpath-toversion-ignorelock-repooptions-onprogress-isdryrun---promisevoid)
34+
- [`getLatestMigrationVersion() -> int`](#getlatestmigrationversion---int)
3235
- [CLI](#cli)
3336
- [Creating a new migration](#creating-a-new-migration)
3437
- [Architecture of a migration](#architecture-of-a-migration)
38+
- [`.migrate(repoPath, repoOptions)`](#migraterepopath-repooptions)
39+
- [`.revert(repoPath, repoOptions)`](#revertrepopath-repooptions)
3540
- [Browser vs. NodeJS environments](#browser-vs-nodejs-environments)
3641
- [Guidelines](#guidelines)
3742
- [Integration with js-ipfs](#integration-with-js-ipfs)
43+
- [Tests](#tests)
3844
- [Empty migrations](#empty-migrations)
3945
- [Migrations matrix](#migrations-matrix)
4046
- [Developer](#developer)
41-
- [Module versioning notes](#module-versioning-notes)
47+
- [Module versioning notes](#module-versioning-notes)
4248
- [Contribute](#contribute)
4349
- [License](#license)
4450

4551
## Background
4652

47-
4853
As js-ipfs evolves and new technologies, algorithms and data structures are incorporated it is necessary to
4954
enable users to transition between versions. Different versions of js-ipfs may expect a different IPFS repo structure or content (see: [IPFS repo spec](https://github.com/ipfs/specs/tree/master/repo), [JS implementation](https://github.com/ipfs/js-ipfs-repo) ).
5055
So the IPFS repo is versioned, and this package provides a framework to create migrations to transition
@@ -87,10 +92,15 @@ const migrations = require('ipfs-repo-migrations')
8792
const repoPath = 'some/repo/path'
8893
const currentRepoVersion = 7
8994
const latestVersion = migrations.getLatestMigrationVersion()
95+
const repoOptions = {
96+
... // the same storage backend/storage options passed to `ipfs-repo`
97+
}
9098

9199
if(currentRepoVersion < latestVersion){
92100
// Old repo! Lets migrate to latest version!
93-
await migrations.migrate(repoPath, latestVersion)
101+
await migrations.migrate(repoPath, latestVersion, {
102+
repoOptions
103+
})
94104
}
95105
```
96106

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"main": "src/index.js",
2121
"browser": {
2222
"./src/repo/lock.js": "./src/repo/lock-memory.js",
23+
"./src/repo/default-root-options.js": "./src/repo/ldefault-root-options.browser.js",
2324
"datastore-fs": "datastore-level"
2425
},
2526
"bin": {

src/commands.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,15 @@ function reportingClosure (action) {
4343
process.stdout.write(`${chalk.green(`[${currentlyMigrated}/${totalToMigrate}]`)} Successfully ${action} ${chalk.bold(migration.version)}: ${migration.description}\n`)
4444
}
4545

46-
async function migrate ({ repoPath, migrations, to, dry, revertOk }) {
46+
async function migrate ({ repoPath, repoOptions, migrations, to, dry, revertOk }) {
4747
repoPath = repoPath || process.env.IPFS_PATH || path.join(os.homedir(), '.jsipfs')
4848
migrations = migrations === undefined ? require('../migrations') : require(migrations)
4949

5050
if (!to) {
5151
to = migrator.getLatestMigrationVersion(migrations)
5252
}
5353

54-
const version = await repoVersion.getVersion(repoPath)
54+
const version = await repoVersion.getVersion(repoPath, repoOptions)
5555

5656
let action
5757
if (dry) {

src/errors.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,18 @@ class InvalidValueError extends Error {
6060

6161
InvalidValueError.code = 'ERR_INVALID_VALUE'
6262
exports.InvalidValueError = InvalidValueError
63+
64+
/**
65+
* Exception raised when config is not passed.
66+
*/
67+
class MissingRepoOptionsError extends Error {
68+
constructor (message) {
69+
super(message)
70+
this.name = 'MissingRepoOptionsError'
71+
this.code = 'ERR_MISSING_REPO_OPTIONS'
72+
this.message = message
73+
}
74+
}
75+
76+
MissingRepoOptionsError.code = 'ERR_MISSING_REPO_OPTIONS'
77+
exports.MissingRepoOptionsError = MissingRepoOptionsError

src/index.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ async function migrate (path, toVersion, { ignoreLock = false, repoOptions, onPr
6161
throw new errors.InvalidValueError('Version has to be positive integer!')
6262
}
6363

64-
const currentVersion = await repoVersion.getVersion(path)
64+
const currentVersion = await repoVersion.getVersion(path, repoOptions)
6565

6666
if (currentVersion === toVersion) {
6767
log('Nothing to migrate.')
@@ -105,7 +105,7 @@ async function migrate (path, toVersion, { ignoreLock = false, repoOptions, onPr
105105
log(`Migrating to version ${migration.version} finished`)
106106
}
107107

108-
if (!isDryRun) await repoVersion.setVersion(path, toVersion || getLatestMigrationVersion(migrations))
108+
if (!isDryRun) await repoVersion.setVersion(path, toVersion || getLatestMigrationVersion(migrations), repoOptions)
109109
log('Repo successfully migrated ', toVersion !== undefined ? `to version ${toVersion}!` : 'to latest version!')
110110
} finally {
111111
if (!isDryRun && !ignoreLock) await lock.close()
@@ -146,7 +146,7 @@ async function revert (path, toVersion, { ignoreLock = false, repoOptions, onPro
146146
throw new errors.InvalidValueError('Version has to be positive integer!')
147147
}
148148

149-
const currentVersion = await repoVersion.getVersion(path)
149+
const currentVersion = await repoVersion.getVersion(path, repoOptions)
150150
if (currentVersion === toVersion) {
151151
log('Nothing to revert.')
152152
return
@@ -182,7 +182,7 @@ async function revert (path, toVersion, { ignoreLock = false, repoOptions, onPro
182182
} catch (e) {
183183
const lastSuccessfullyRevertedVersion = migration.version
184184
log(`An exception was raised during execution of migration. Setting the repo's version to last successfully reverted version: ${lastSuccessfullyRevertedVersion}`)
185-
await repoVersion.setVersion(path, lastSuccessfullyRevertedVersion)
185+
await repoVersion.setVersion(path, lastSuccessfullyRevertedVersion, repoOptions)
186186

187187
e.message = `During reversion to version ${migration.version} exception was raised: ${e.message}`
188188
throw e
@@ -192,7 +192,7 @@ async function revert (path, toVersion, { ignoreLock = false, repoOptions, onPro
192192
log(`Reverting to version ${migration.version} finished`)
193193
}
194194

195-
if (!isDryRun) await repoVersion.setVersion(path, toVersion)
195+
if (!isDryRun) await repoVersion.setVersion(path, toVersion, repoOptions)
196196
log(`All migrations successfully reverted to version ${toVersion}!`)
197197
} finally {
198198
if (!isDryRun && !ignoreLock) await lock.close()

src/repo/init.js

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,25 @@
11
'use strict'
22

3-
const Datastore = require('datastore-fs')
43
const log = require('debug')('repo-migrations:repo:init')
4+
const { CONFIG_KEY, VERSION_KEY, getDatastoreAndOptions } = require('../utils')
5+
const { MissingRepoOptionsError } = require('../errors')
56

6-
const Key = require('interface-datastore').Key
7-
8-
const versionKey = new Key('/version')
9-
const configKey = new Key('/config')
7+
exports.isRepoInitialized = async function isRepoInitialized (path, repoOptions) {
8+
if (!repoOptions) {
9+
throw new MissingRepoOptionsError('Please pass repo options when trying to open a repo')
10+
}
1011

11-
exports.isRepoInitialized = async function isRepoInitialized (path) {
1212
let root
1313
try {
14-
root = new Datastore(path, { extension: '', createIfMissing: false })
14+
const {
15+
StorageBackend,
16+
storageOptions
17+
} = getDatastoreAndOptions(repoOptions, 'root')
18+
19+
root = new StorageBackend(path, storageOptions)
1520
await root.open()
16-
const versionCheck = await root.has(versionKey)
17-
const configCheck = await root.has(configKey)
21+
const versionCheck = await root.has(VERSION_KEY)
22+
const configCheck = await root.has(CONFIG_KEY)
1823
if (!versionCheck || !configCheck) {
1924
log(`Version entry present: ${versionCheck}`)
2025
log(`Config entry present: ${configCheck}`)
@@ -26,6 +31,8 @@ exports.isRepoInitialized = async function isRepoInitialized (path) {
2631
log('While checking if repo is initialized error was thrown: ' + e.message)
2732
return false
2833
} finally {
29-
if (root !== undefined) await root.close()
34+
if (root !== undefined) {
35+
await root.close()
36+
}
3037
}
3138
}

src/repo/version.js

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
'use strict'
22

33
const { Buffer } = require('buffer')
4-
const errors = require('../errors')
54
const repoInit = require('./init')
6-
const Datastore = require('datastore-fs')
7-
8-
const Key = require('interface-datastore').Key
9-
10-
const versionKey = new Key('version')
5+
const { MissingRepoOptionsError, NotInitializedRepoError } = require('../errors')
6+
const { VERSION_KEY, getDatastoreAndOptions } = require('../utils')
117

128
exports.getVersion = getVersion
139

@@ -17,17 +13,27 @@ exports.getVersion = getVersion
1713
* even in case of change of repo's versioning.
1814
*
1915
* @param {string} path
16+
* @param {Object} repoOptions Options used to create a repo, the same as pased to ipfs-repo
2017
* @returns {Promise<int>}
2118
*/
22-
async function getVersion (path) {
23-
if (!(await repoInit.isRepoInitialized(path))) {
24-
throw new errors.NotInitializedRepoError(`Repo in path ${path} is not initialized!`)
19+
async function getVersion (path, repoOptions) {
20+
if (!(await repoInit.isRepoInitialized(path, repoOptions))) {
21+
throw new NotInitializedRepoError(`Repo in path ${path} is not initialized!`)
2522
}
2623

27-
const store = new Datastore(path, { extension: '', createIfMissing: false })
24+
if (!repoOptions) {
25+
throw new MissingRepoOptionsError('Please pass repo options when trying to open a repo')
26+
}
27+
28+
const {
29+
StorageBackend,
30+
storageOptions
31+
} = getDatastoreAndOptions(repoOptions, 'root')
32+
33+
const store = new StorageBackend(path, storageOptions)
2834
await store.open()
2935

30-
const version = parseInt(await store.get(versionKey))
36+
const version = parseInt(await store.get(VERSION_KEY))
3137
await store.close()
3238

3339
return version
@@ -38,12 +44,23 @@ async function getVersion (path) {
3844
*
3945
* @param {string} path
4046
* @param {int} version
47+
* @param {Object} repoOptions Options used to create a repo, the same as pased to ipfs-repo
4148
* @returns {Promise<void>}
4249
*/
43-
async function setVersion (path, version) {
44-
const store = new Datastore(path, { extension: '', createIfMissing: false })
50+
async function setVersion (path, version, repoOptions) {
51+
if (!repoOptions) {
52+
throw new MissingRepoOptionsError('Please pass repo options when trying to open a repo')
53+
}
54+
55+
const {
56+
StorageBackend,
57+
storageOptions
58+
} = getDatastoreAndOptions(repoOptions, 'root')
59+
60+
const store = new StorageBackend(path, storageOptions)
4561
await store.open()
46-
await store.put(versionKey, Buffer.from(String(version)))
62+
63+
await store.put(VERSION_KEY, Buffer.from(String(version)))
4764
await store.close()
4865
}
4966

src/utils.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
'use strict'
22

33
const Datastore = require('datastore-fs')
4+
const Key = require('interface-datastore').Key
5+
6+
exports.CONFIG_KEY = new Key('/config')
7+
exports.VERSION_KEY = new Key('/version')
48

59
exports.getDatastoreAndOptions = function getDatastoreAndOptions (options, key, defaultDatastore = Datastore) {
610
let StorageBackend, storageBackendOptions

0 commit comments

Comments
 (0)