use integration-aws-node credential provider

Signed-off-by: Andrew Ochsner <andrew.ochsner@cognizant.com>
This commit is contained in:
Andrew Ochsner
2023-05-03 16:34:33 -05:00
parent 91c6c7356c
commit b01684d280
4 changed files with 115 additions and 115 deletions
@@ -40,6 +40,7 @@
"@backstage/backend-common": "workspace:^",
"@backstage/backend-plugin-api": "workspace:^",
"@backstage/config": "workspace:^",
"@backstage/integration-aws-node": "workspace:^",
"@backstage/plugin-search-backend-node": "workspace:^",
"@backstage/plugin-search-common": "workspace:^",
"@elastic/elasticsearch": "^7.13.0",
@@ -14,17 +14,13 @@
* limitations under the License.
*/
import { ConfigReader } from '@backstage/config';
import { Client as ElasticSearchClient } from '@elastic/elasticsearch';
import { Client as OpenSearchClient } from '@opensearch-project/opensearch';
import Mock from '@short.io/opensearch-mock';
import { Readable } from 'stream';
import { ElasticSearchClientWrapper } from './ElasticSearchClientWrapper';
import {
createElasticSearchClientOptions,
ElasticSearchClientOptions,
} from './ElasticSearchSearchEngine';
import { ElasticSearchClientOptions } from './ElasticSearchSearchEngine';
jest.mock('@elastic/elasticsearch', () => ({
...jest.requireActual('@elastic/elasticsearch'),
@@ -103,11 +99,9 @@ describe('ElasticSearchClientWrapper', () => {
let esOptions: ElasticSearchClientOptions;
beforeEach(async () => {
esOptions = await createElasticSearchClientOptions(
new ConfigReader({
node: 'http://localhost:9200',
}),
);
esOptions = {
node: 'http://localhost:9200',
};
jest.clearAllMocks();
});
@@ -23,7 +23,6 @@ import {
} from '@backstage/plugin-search-common';
import { isEmpty, isNumber, isNaN as nan } from 'lodash';
import { defaultProvider } from '@aws-sdk/credential-provider-node'; // V3 SDK.
import { AwsSigv4Signer } from '@opensearch-project/opensearch/aws';
import { RequestSigner } from 'aws4';
import { Config } from '@backstage/config';
@@ -35,6 +34,10 @@ import { Logger } from 'winston';
import { MissingIndexError } from '@backstage/plugin-search-backend-node';
import esb from 'elastic-builder';
import { v4 as uuid } from 'uuid';
import {
AwsCredentialsManager,
DefaultAwsCredentialsManager,
} from '@backstage/integration-aws-node';
export type { ElasticSearchClientOptions };
@@ -118,6 +121,7 @@ const DEFAULT_INDEXER_BATCH_SIZE = 1000;
export class ElasticSearchSearchEngine implements SearchEngine {
private readonly elasticSearchClientWrapper: ElasticSearchClientWrapper;
private readonly highlightOptions: ElasticSearchHighlightConfig;
private static credentialProvider: AwsCredentialsManager;
constructor(
private readonly elasticSearchClientOptions: ElasticSearchClientOptions,
@@ -147,8 +151,8 @@ export class ElasticSearchSearchEngine implements SearchEngine {
aliasPostfix = `search`,
indexPrefix = ``,
} = options;
const clientOptions = await createElasticSearchClientOptions(
this.credentialProvider = DefaultAwsCredentialsManager.fromConfig(config);
const clientOptions = await this.createElasticSearchClientOptions(
config.getConfig('search.elasticsearch'),
);
if (clientOptions.provider === 'elastic') {
@@ -414,6 +418,108 @@ export class ElasticSearchSearchEngine implements SearchEngine {
const postFix = this.aliasPostfix ? `__${this.aliasPostfix}` : '';
return `${this.indexPrefix}${type}${postFix}`;
}
private static async createElasticSearchClientOptions(
config?: Config,
): Promise<ElasticSearchClientOptions> {
if (!config) {
throw new Error('No elastic search config found');
}
const clientOptionsConfig = config.getOptionalConfig('clientOptions');
const sslConfig = clientOptionsConfig?.getOptionalConfig('ssl');
if (config.getOptionalString('provider') === 'elastic') {
const authConfig = config.getConfig('auth');
return {
provider: 'elastic',
cloud: {
id: config.getString('cloudId'),
},
auth: {
username: authConfig.getString('username'),
password: authConfig.getString('password'),
},
...(sslConfig
? {
ssl: {
rejectUnauthorized:
sslConfig?.getOptionalBoolean('rejectUnauthorized'),
},
}
: {}),
};
}
if (config.getOptionalString('provider') === 'aws') {
const requestSigner = new RequestSigner(config.getString('node'));
const service =
config.getOptionalString('service') ?? requestSigner.service;
if (service !== 'es' && service !== 'aoss')
throw new Error(`Unrecognized serivce type: ${service}`);
return {
provider: 'aws',
node: config.getString('node'),
...(sslConfig
? {
ssl: {
rejectUnauthorized:
sslConfig?.getOptionalBoolean('rejectUnauthorized'),
},
}
: {}),
...AwsSigv4Signer({
region: config.getOptionalString('region') ?? requestSigner.region, // for backwards compatibility
service: service,
getCredentials: async () => {
const provider =
await this.credentialProvider.getCredentialProvider();
return await provider.sdkCredentialProvider();
},
}),
};
}
if (config.getOptionalString('provider') === 'opensearch') {
const authConfig = config.getConfig('auth');
return {
provider: 'opensearch',
node: config.getString('node'),
auth: {
username: authConfig.getString('username'),
password: authConfig.getString('password'),
},
...(sslConfig
? {
ssl: {
rejectUnauthorized:
sslConfig?.getOptionalBoolean('rejectUnauthorized'),
},
}
: {}),
};
}
const authConfig = config.getOptionalConfig('auth');
const auth =
authConfig &&
(authConfig.has('apiKey')
? {
apiKey: authConfig.getString('apiKey'),
}
: {
username: authConfig.getString('username'),
password: authConfig.getString('password'),
});
return {
node: config.getString('node'),
auth,
...(sslConfig
? {
ssl: {
rejectUnauthorized:
sslConfig?.getOptionalBoolean('rejectUnauthorized'),
},
}
: {}),
};
}
}
/**
@@ -432,105 +538,3 @@ export function decodePageCursor(pageCursor?: string): { page: number } {
export function encodePageCursor({ page }: { page: number }): string {
return Buffer.from(`${page}`, 'utf-8').toString('base64');
}
export async function createElasticSearchClientOptions(
config?: Config,
): Promise<ElasticSearchClientOptions> {
if (!config) {
throw new Error('No elastic search config found');
}
const clientOptionsConfig = config.getOptionalConfig('clientOptions');
const sslConfig = clientOptionsConfig?.getOptionalConfig('ssl');
if (config.getOptionalString('provider') === 'elastic') {
const authConfig = config.getConfig('auth');
return {
provider: 'elastic',
cloud: {
id: config.getString('cloudId'),
},
auth: {
username: authConfig.getString('username'),
password: authConfig.getString('password'),
},
...(sslConfig
? {
ssl: {
rejectUnauthorized:
sslConfig?.getOptionalBoolean('rejectUnauthorized'),
},
}
: {}),
};
}
if (config.getOptionalString('provider') === 'aws') {
const requestSigner = new RequestSigner(config.getString('node'));
const service =
config.getOptionalString('service') ?? requestSigner.service;
if (service !== 'es' && service !== 'aoss')
throw new Error(`Unrecognized serivce type: ${service}`);
return {
provider: 'aws',
node: config.getString('node'),
...(sslConfig
? {
ssl: {
rejectUnauthorized:
sslConfig?.getOptionalBoolean('rejectUnauthorized'),
},
}
: {}),
...AwsSigv4Signer({
region: config.getOptionalString('region') ?? requestSigner.region, // for backwards compatibility
service: service,
getCredentials: () => {
// Any other method to acquire a new Credentials object can be used.
const credentialsProvider = defaultProvider();
return credentialsProvider();
},
}),
};
}
if (config.getOptionalString('provider') === 'opensearch') {
const authConfig = config.getConfig('auth');
return {
provider: 'opensearch',
node: config.getString('node'),
auth: {
username: authConfig.getString('username'),
password: authConfig.getString('password'),
},
...(sslConfig
? {
ssl: {
rejectUnauthorized:
sslConfig?.getOptionalBoolean('rejectUnauthorized'),
},
}
: {}),
};
}
const authConfig = config.getOptionalConfig('auth');
const auth =
authConfig &&
(authConfig.has('apiKey')
? {
apiKey: authConfig.getString('apiKey'),
}
: {
username: authConfig.getString('username'),
password: authConfig.getString('password'),
});
return {
node: config.getString('node'),
auth,
...(sslConfig
? {
ssl: {
rejectUnauthorized:
sslConfig?.getOptionalBoolean('rejectUnauthorized'),
},
}
: {}),
};
}
+1
View File
@@ -8622,6 +8622,7 @@ __metadata:
"@backstage/backend-plugin-api": "workspace:^"
"@backstage/cli": "workspace:^"
"@backstage/config": "workspace:^"
"@backstage/integration-aws-node": "workspace:^"
"@backstage/plugin-search-backend-node": "workspace:^"
"@backstage/plugin-search-common": "workspace:^"
"@elastic/elasticsearch": ^7.13.0