Skip to content

Commit

Permalink
Issue #14: Handle pre-5.0 admin API auth (#18)
Browse files Browse the repository at this point in the history
Motivation
----------
Getting index statuses is not working on pre-5.0 clusters because it
requires authentication though other operations do not.

Modifications
-------------
Use a ClusterManager instead of BucketManager for cluster level admin
API requests.

Results
-------
Admin API calls now succeed on both 4.0 and 5.0 clusters.
  • Loading branch information
brantburnett authored Apr 11, 2018
1 parent f8c7bcf commit 8621f90
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 86 deletions.
3 changes: 2 additions & 1 deletion app/connection-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ export class ConnectionManager {
this.connectionInfo.bucketName,
this.connectionInfo.bucketPassword);

return new IndexManager(this.connectionInfo.bucketName, this.bucket);
return new IndexManager(this.connectionInfo.bucketName, this.bucket,
this.cluster);
}

/**
Expand Down
156 changes: 73 additions & 83 deletions app/index-manager.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {N1qlQuery} from 'couchbase';
import {extend} from 'lodash';

const WAIT_TICK_INTERVAL = 10000; // in milliseconds

Expand Down Expand Up @@ -42,18 +41,41 @@ function _respRead(callback) {
};
}

/** Extension methods injected into BucketManager */
const extensions = {
cbim_getIndexStatus: function(callback) {
this._mgmtRequest('indexStatus', 'GET', (err, httpReq) => {
if (err) {
return callback(err, null);
}
/**
* Manages Couchbase indexes
*
* @property {!string} bucketName
* @property {!boolean} is4XCluster
*/
export class IndexManager {
/**
* @param {string} bucketName
* @param {CouchbaseBucket} bucket
* @param {Cluster} cluster
*/
constructor(bucketName, bucket, cluster) {
this.bucketName = bucketName;
this.bucket = bucket;
this.manager = bucket.manager();
this.clusterManager = cluster.manager();
}

/**
* @private
* Gets index statuses for the bucket via the cluster manager
*
* @return {Promise.array}
*/
getIndexStatuses() {
return new Promise((resolve, reject) => {
let httpReq = this.clusterManager._mgmtRequest(
'indexStatus', 'GET');

httpReq.on('error', callback);
httpReq.on('error', reject);
httpReq.on('response', _respRead((err, resp, data) => {
if (err) {
return callback(err);
reject(err);
return;
}

if (resp.statusCode !== 200) {
Expand All @@ -65,45 +87,24 @@ const extensions = {
}

if (!errData) {
callback(new Error(
'operation failed (' + resp.statusCode +')'), null);
reject(new Error(
'operation failed (' + resp.statusCode +')'));
return;
}

callback(new Error(errData.reason), null);
reject(new Error(errData.reason));
return;
}

let indexStatusData = JSON.parse(data);
let indexStatuses = indexStatusData.indexes.filter((index) => {
return index.bucket === this._bucket._name;
return index.bucket === this.bucketName;
});

callback(null, indexStatuses);
resolve(indexStatuses);
}));
httpReq.end();
});
},
};

/**
* Manages Couchbase indexes
*
* @property {!string} bucketName
* @property {!boolean} is4XCluster
*/
export class IndexManager {
/**
* @param {string} bucketName
* @param {CouchbaseBucket} bucket
* @param {boolean} is4XCluster
*/
constructor(bucketName, bucket) {
this.bucketName = bucketName;
this.bucket = bucket;
this.manager = bucket.manager();

extend(this.manager, extensions);
}

/**
Expand All @@ -121,15 +122,7 @@ export class IndexManager {
});

// Get additional info from the index status API
let statuses = await new Promise((resolve, reject) => {
this.manager.cbim_getIndexStatus((err, statuses) => {
if (err) {
reject(err);
} else {
resolve(statuses);
}
});
});
let statuses = await this.getIndexStatuses();

// Apply hosts from index status API to index information
statuses.forEach((status) => {
Expand Down Expand Up @@ -273,53 +266,50 @@ export class IndexManager {
async getClusterVersion() {
// Get additional info from the index status API
let clusterCompatibility = await new Promise((resolve, reject) => {
this.manager._mgmtRequest('pools/default', 'GET',
(err, httpReq) => {
let httpReq = this.clusterManager._mgmtRequest(
'pools/default', 'GET');

httpReq.on('error', reject);
httpReq.on('response', _respRead((err, resp, data) => {
if (err) {
return reject(err);
reject(err);
return;
}

httpReq.on('error', reject);
httpReq.on('response', _respRead((err, resp, data) => {
if (err) {
return reject(err);
if (resp.statusCode !== 200) {
let errData = null;
try {
errData = JSON.parse(data);
} catch (e) {
// ignore
}

if (resp.statusCode !== 200) {
let errData = null;
try {
errData = JSON.parse(data);
} catch (e) {
// ignore
}
if (!errData) {
reject(new Error(
'operation failed (' + resp.statusCode +')'),
null);
return;
}

if (!errData) {
reject(new Error(
'operation failed (' + resp.statusCode +')'),
null);
return;
reject(new Error(errData.reason));
return;
}

let poolData = JSON.parse(data);
let minCompatibility = poolData.nodes.reduce(
(accum, value) => {
if (value.clusterCompatibility < accum) {
accum = value.clusterCompatibility;
}

reject(new Error(errData.reason));
return;
}
return accum;
}, 65535 * 65536);

let poolData = JSON.parse(data);
let minCompatibility = poolData.nodes.reduce(
(accum, value) => {
if (value.clusterCompatibility < accum) {
accum = value.clusterCompatibility;
}

return accum;
}, 65535 * 65536);

resolve(minCompatibility < 65535 * 65536 ?
minCompatibility :
0);
}));
httpReq.end();
});
resolve(minCompatibility < 65535 * 65536 ?
minCompatibility :
0);
}));
httpReq.end();
});

return {
Expand Down
2 changes: 0 additions & 2 deletions app/sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,6 @@ export class Sync {
clusterVersion: await this.manager.getClusterVersion(),
};

console.log(mutationContext.clusterVersion);

if (mutationContext.clusterVersion.major < 5) {
// Force all definitions to use manual replica management
definitions.forEach((def) => {
Expand Down

0 comments on commit 8621f90

Please sign in to comment.