UNPKG

scylla-migration

Version:
336 lines (309 loc) 12.9 kB
// Generated by CoffeeScript 1.12.7 (function() { var FS, Q, _, applyMigration, cassandra, createVersionTable, debugMode, durations, getCassandraClient, getSchemaVersion, listMigrations, logDebug, logError, logInfo, migrate, moduleVersion, moment, parseCassandraHosts, program, quietMode, readConfig, runQuery, runScript; _ = require('lodash'); Q = require('q'); FS = require('fs'); moment = require('moment'); program = require('commander'); moduleVersion = require('../package.json').version; cassandra = require('cassandra-driver'); durations = require('durations'); quietMode = false; debugMode = false; logError = function(message, error) { var errorMessage, stack; errorMessage = error != null ? ": " + error : ''; stack = (error != null) && debugMode ? "\n" + error.stack : ''; return console.error("" + message + errorMessage + stack); }; logInfo = function(message) { if (!quietMode) { return console.log(message); } }; logDebug = function(message) { if (!quietMode && debugMode === true) { return console.log(message); } }; readConfig = function(configFile) { return Q.nfcall(FS.readFile, configFile, 'utf-8').then(function(rawConfig) { var config, d, error; d = Q.defer(); try { config = JSON.parse(rawConfig); d.resolve(config); } catch (error1) { error = error1; d.reject(error); } return d.promise; }).then(function(config) { var d; d = Q.defer(); if (config.cassandra != null) { d.resolve(config); } else { d.reject(new Error("Cassandra configuration not supplied.")); } return d.promise; }); }; listMigrations = function(config) { var d, migrationsDir; d = Q.defer(); migrationsDir = config.migrationsDir; if (migrationsDir == null) { d.reject(new Error("The config did not contain a migrationsDir property.")); } else if (!FS.existsSync(migrationsDir)) { d.reject(new Error("Migrations directory does not exist.")); } else { FS.readdir(migrationsDir, function(error, files) { var migrationFiles; if (error != null) { return d.reject(new Error("Error listing migrations directory contents: " + error, error)); } else { migrationFiles = _(files).filter(function(fileName) { return _.endsWith(fileName.toLowerCase(), '.cql'); }).filter(function(fileName) { var filePath; filePath = migrationsDir + "/" + fileName; return FS.statSync(filePath).isFile(); }).map(function(fileName) { var file, version; version = fileName.split('__')[0]; file = migrationsDir + "/" + fileName; return [file, version]; }).filter(function(arg) { var file, version; file = arg[0], version = arg[1]; return !isNaN(version); }).map(function(arg) { var file, version; file = arg[0], version = arg[1]; return [file, parseInt(version)]; }).value(); if (_(migrationFiles).size() > 0) { return d.resolve(migrationFiles); } else { return d.reject(new Error("No migration files found")); } } }); } return d.promise.then(function(files) { return files; }); }; getCassandraClient = function(config) { var client, d, error; d = Q.defer(); try { client = new cassandra.Client(config.cassandra); client.connect(function(error) { if (error != null) { return d.reject(error); } else { logDebug("Connected to Cassandra."); return d.resolve(client); } }); } catch (error1) { error = error1; d.reject(new Error("Error creating Cassandra client: " + error, error)); } return d.promise; }; createVersionTable = function(config, client, keyspace) { var d, versionQuery; d = Q.defer(); versionQuery = "SELECT release_version FROM system.local"; client.execute(versionQuery, function(error, results) { var cassandraVersion, isVersion3, schemaKeyspace, tableNameColumn, tableQuery, tablesTable; cassandraVersion = _(results.rows).map(function(row) { return row.release_version; }).first(); if (error != null) { d.reject(error); } if (cassandraVersion == null) { return d.reject(new Error("Could not determine the version of Cassandra!")); } else { logDebug("Cassandra version: " + cassandraVersion); isVersion3 = _.startsWith(cassandraVersion, "3."); schemaKeyspace = isVersion3 ? "system_schema" : "system"; tablesTable = isVersion3 ? "tables" : "schema_columnfamilies"; tableNameColumn = isVersion3 ? "table_name" : "columnfamily_name"; logDebug("schemaKeyspace: " + schemaKeyspace); logDebug("tablesTable: " + tablesTable); logDebug("tableNameColumn: " + tableNameColumn); tableQuery = "SELECT " + tableNameColumn + " \nFROM " + schemaKeyspace + "." + tablesTable + " \nWHERE keyspace_name='" + keyspace + "'"; return client.execute(tableQuery, function(error, results) { var createQuery, tableNames; tableNames = _(results.rows).map(function(row) { return row[tableNameColumn]; }).value(); if (error != null) { return d.reject(error); } else if (_(tableNames).filter(function(tableName) { return tableName === 'schema_version'; }).size() > 0) { logDebug("Table 'schema_version' already exists."); return d.resolve(client); } else { logDebug(""); createQuery = "CREATE TABLE " + keyspace + ".schema_version (\n zero INT,\n version INT,\n migration_timestamp TIMESTAMP, \n\n PRIMARY KEY (zero, version)\n) WITH CLUSTERING ORDER BY (version DESC)"; logDebug("creating the schema_version table..."); return client.execute(createQuery, function(error, results) { if (error != null) { return d.reject(new Error("Error creating the schema_version table: " + error, error)); } else { return d.resolve(client); } }); } }); } }); return d.promise; }; getSchemaVersion = function(config, client, keyspace) { return createVersionTable(config, client, keyspace).then(function() { var d, versionQuery; d = Q.defer(); logDebug("Fetching version info..."); versionQuery = "SELECT version FROM " + keyspace + ".schema_version LIMIT 1"; client.execute(versionQuery, function(error, results) { var ref, ref1, ref2, version; if (error != null) { logError("Error reading version information from the version table", error); return d.reject(new Error("Error reading version information from the version table: " + error, error)); } else if (_(results.rows).size() > 0) { version = (ref = (ref1 = _(results.rows)) != null ? (ref2 = ref1.first()) != null ? ref2.version : void 0 : void 0) != null ? ref : 0; version = parseInt(version); logDebug("Current version is " + version); return d.resolve(version); } else { logDebug("Current version is 0"); return d.resolve(0); } }); return d.promise; }); }; runQuery = function(config, client, query, version) { var d; d = Q.defer(); client.execute(query, function(error, results) { logDebug("running query: " + query); if (error != null) { return d.reject(new Error("Error applying migration " + version + ": " + error, error)); } else { return d.resolve(version); } }); return d.promise; }; applyMigration = function(config, client, keyspace, file, version) { var cql, queries, queryStrings; logInfo("Applying migration: " + file); queryStrings = _.trim(FS.readFileSync(file, 'utf-8')).split('---'); cql = ("INSERT INTO " + keyspace + ".schema_version") + " (zero, version, migration_timestamp)" + (" VALUES (0, " + version + ", '" + (moment().unix() * 1000) + "');"); queryStrings.push(cql); queries = _(queryStrings).map(function(cql) { return function() { return runQuery(config, client, cql, version); }; }).value(); return queries.reduce(Q.when, Q(version)); }; migrate = function(config, client, keyspace, migrationFiles, schemaVersion) { var migrationFunctions, migrations, versionString, versions; migrations = _(migrationFiles).filter(function(arg) { var file, version; file = arg[0], version = arg[1]; return version > schemaVersion && version <= config.targetVersion; }).sortBy(function(arg) { var file, version; file = arg[0], version = arg[1]; return version; }).value(); versionString = config.targetVersion === Number.MAX_VALUE ? "unlimited" : config.targetVersion; logDebug("Migrations to be applied: " + migrations + " (target version is " + versionString + ")"); if (_(migrations).size() > 0) { versions = _(migrations).map(function(arg) { var file, version; file = arg[0], version = arg[1]; return version; }).value(); versions.unshift(schemaVersion); logInfo("Migrating database " + (_(versions).join(" -> ")) + " ..."); migrationFunctions = _(migrations).map(function(arg) { var file, version; file = arg[0], version = arg[1]; return function() { return applyMigration(config, client, keyspace, file, version); }; }).value(); return migrationFunctions.reduce(Q.when, Q(schemaVersion)).then(function(version) { console.log("All migrations complete. Schema is now at version " + version + "."); return version; }); } else { console.log("No new migrations. Schema version is " + schemaVersion); return Q(schemaVersion); } }; parseCassandraHosts = function(val) { return val.split(','); }; runScript = function() { var cassandraClient, code, configFile; program.version(moduleVersion).usage('[options] <config_file>').option('-d, --debug', 'Increase verbosity and error detail').option('-h, --hosts <hosts>', 'A comma separated list of cassandra hosts', parseCassandraHosts).option('-k, --keyspace <keyspace>', 'Cassandra keyspace used for migration and schema_version table').option('-q, --quiet', 'Silence non-error output (default is false)').option('-t, --target-version <version>', 'Maximum migration version to apply (default runs all migrations)').parse(process.argv); configFile = _(program.args).last(); code = 1; cassandraClient = void 0; return readConfig(configFile).then(function(config) { var keyspace, ref, ref1, ref2, ref3, ref4; config.quiet = (ref = program.quiet) != null ? ref : config.quiet; config.debug = (ref1 = program.debug) != null ? ref1 : config.debug; config.cassandra.keyspace = (ref2 = program.keyspace) != null ? ref2 : config.cassandra.keyspace; config.targetVersion = (ref3 = program.targetVersion) != null ? ref3 : Number.MAX_VALUE; config.cassandra.contactPoints = (ref4 = program.hosts) != null ? ref4 : config.cassandra.contactPoints; quietMode = config.quiet; debugMode = config.debug; keyspace = config.cassandra.keyspace; if (config.auth != null) { logDebug("Connecting with simple user authentication."); config.cassandra.authProvider = new cassandra.auth.PlainTextAuthProvider(config.auth.username, config.auth.password); } else { logDebug("Connecting without authentication."); } return Q.all([listMigrations(config), getCassandraClient(config)]).spread(function(migrationFiles, client) { cassandraClient = client; return getSchemaVersion(config, client, keyspace).then(function(schemaVersion) { return migrate(config, client, keyspace, migrationFiles, schemaVersion); }).then(function(version) { return code = 0; })["catch"](function(error) { return logError("Migration Error", error); }); }); })["catch"](function(error) { return logError("Error reading configuration file", error); })["finally"](function() { if (cassandraClient != null) { cassandraClient.shutdown; } return process.exit(code); }); }; module.exports = { run: runScript, listMigrations: listMigrations }; if (require.main === module) { runScript(); } }).call(this);