Merge pull request #3981 from backstage/orkohunter/improve-gcs-credentials-handling

This commit is contained in:
Himanshu Mishra
2021-01-11 21:12:55 +01:00
committed by GitHub
9 changed files with 103 additions and 93 deletions
@@ -37,7 +37,7 @@ jest.spyOn(logger, 'info').mockReturnValue(logger);
let publisher: PublisherBase;
beforeEach(() => {
beforeEach(async () => {
const mockConfig = new ConfigReader({
techdocs: {
requestUrl: 'http://localhost:7000',
@@ -45,14 +45,13 @@ beforeEach(() => {
type: 'googleGcs',
googleGcs: {
credentials: '{}',
projectId: 'gcp-project-id',
bucketName: 'bucketName',
},
},
},
});
publisher = GoogleGCSPublish.fromConfig(mockConfig, logger);
publisher = await GoogleGCSPublish.fromConfig(mockConfig, logger);
});
describe('GoogleGCSPublish', () => {
@@ -27,57 +27,56 @@ import { getHeadersForFileExtension, getFileTreeRecursively } from './helpers';
import { PublisherBase, PublishRequest } from './types';
export class GoogleGCSPublish implements PublisherBase {
static fromConfig(config: Config, logger: Logger): PublisherBase {
let credentials = '';
let projectId = '';
static async fromConfig(
config: Config,
logger: Logger,
): Promise<PublisherBase> {
let bucketName = '';
try {
credentials = config.getString(
'techdocs.publisher.googleGcs.credentials',
);
projectId = config.getString('techdocs.publisher.googleGcs.projectId');
bucketName = config.getString('techdocs.publisher.googleGcs.bucketName');
} catch (error) {
throw new Error(
"Since techdocs.publisher.type is set to 'googleGcs' in your app config, " +
'credentials, projectId and bucketName are required in techdocs.publisher.googleGcs ' +
'required to authenticate with Google Cloud Storage.',
'techdocs.publisher.googleGcs.bucketName is required.',
);
}
// Credentials is an optional config. If missing, default GCS environment variables will be used.
// Read more here https://cloud.google.com/docs/authentication/production
const credentials = config.getOptionalString(
'techdocs.publisher.googleGcs.credentials',
);
let credentialsJson = {};
try {
credentialsJson = JSON.parse(credentials);
} catch (err) {
throw new Error(
'Error in parsing techdocs.publisher.googleGcs.credentials config to JSON.',
);
if (credentials) {
try {
credentialsJson = JSON.parse(credentials);
} catch (err) {
throw new Error(
'Error in parsing techdocs.publisher.googleGcs.credentials config to JSON.',
);
}
}
const storageClient = new Storage({
credentials: credentialsJson,
projectId: projectId,
...(credentials && {
credentials: credentialsJson,
}),
});
// Check if the defined bucket exists. Being able to connect means the configuration is good
// and the storage client will work.
storageClient
.bucket(bucketName)
.getMetadata()
.then(() => {
logger.info(
`Successfully connected to the GCS bucket ${bucketName} in the GCP project ${projectId}.`,
);
})
.catch(reason => {
logger.error(
`Could not retrieve metadata about the GCS bucket ${bucketName} in the GCP project ${projectId}. ` +
'Make sure the GCP project and the bucket exists and the access key located at the path ' +
"techdocs.publisher.googleGcs.credentials defined in app config has the role 'Storage Object Creator'. " +
'Refer to https://backstage.io/docs/features/techdocs/using-cloud-storage',
);
throw new Error(`from GCS client library: ${reason.message}`);
});
try {
await storageClient.bucket(bucketName).getMetadata();
logger.info(`Successfully connected to the GCS bucket ${bucketName}.`);
} catch (err) {
logger.error(
`Could not retrieve metadata about the GCS bucket ${bucketName}. ` +
'Make sure the bucket exists. Also make sure that authentication is setup either by explicitly defining ' +
'techdocs.publisher.googleGcs.credentials in app config or by using environment variables. ' +
'Refer to https://backstage.io/docs/features/techdocs/using-cloud-storage',
);
throw new Error(err.message);
}
return new GoogleGCSPublish(storageClient, bucketName, logger);
}
@@ -69,7 +69,6 @@ describe('Publisher', () => {
type: 'googleGcs',
googleGcs: {
credentials: '{}',
projectId: 'gcp-project-id',
bucketName: 'bucketName',
},
},
@@ -43,7 +43,7 @@ export class Publisher {
switch (publisherType) {
case 'googleGcs':
logger.info('Creating Google Storage Bucket publisher for TechDocs');
return GoogleGCSPublish.fromConfig(config, logger);
return await GoogleGCSPublish.fromConfig(config, logger);
case 'awsS3':
logger.info('Creating AWS S3 Bucket publisher for TechDocs');
return AwsS3Publish.fromConfig(config, logger);