Merge pull request #24843 from stijnbrouwers/bugfix/awsCodeCommit-getByUrl
Fix host for AWS Code Commit integration and add region
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/integration-react': patch
|
||||
'@backstage/backend-common': patch
|
||||
'@backstage/integration': patch
|
||||
---
|
||||
|
||||
Fix AWS CodeCommit integration by allowing to change the host
|
||||
@@ -20,7 +20,34 @@ To use this integration, add configuration to your `app-config.yaml`:
|
||||
```yaml
|
||||
integrations:
|
||||
awsCodeCommit:
|
||||
- accessKeyId: ${AWS_ACCESS_KEY_ID}
|
||||
- region: eu-west-1
|
||||
```
|
||||
|
||||
This most basic example can only be used if you are running Backstage on an instance that has an IAM identity with the following policies:
|
||||
|
||||
```
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Action": [
|
||||
"codecommit:GetFile",
|
||||
"codecommit:GetFolder"
|
||||
],
|
||||
"Resource": "*",
|
||||
"Effect": "Allow"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
If you are running Backstage outside AWS and want to use Access key authentication, you can use the following configuration:
|
||||
|
||||
```yaml
|
||||
integrations:
|
||||
awsCodeCommit:
|
||||
- region: eu-west-1
|
||||
accessKeyId: ${AWS_ACCESS_KEY_ID}
|
||||
secretAccessKey: ${AWS_SECRET_ACCESS_KEY}
|
||||
```
|
||||
|
||||
@@ -34,10 +61,27 @@ instruct the AWS CodeCommit reader to assume a role before accessing CodeCommit:
|
||||
```yaml
|
||||
integrations:
|
||||
awsCodeCommit:
|
||||
- accessKeyId: ${AWS_ACCESS_KEY_ID}
|
||||
secretAccessKey: ${AWS_SECRET_ACCESS_KEY}
|
||||
- region: eu-west-1
|
||||
roleArn: 'arn:aws:iam::xxxxxxxxxxxx:role/example-role'
|
||||
externalId: 'some-id' # optional
|
||||
```
|
||||
|
||||
When no entries are added, the AWS CodeCommit reader will add a default entry that uses the [standard credentials provider chain](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.html).
|
||||
Each entry is a structure with the following **required** elements:
|
||||
|
||||
- `region`: The AWS region to connect to, to communicate with the CodeCommit services
|
||||
|
||||
The configuration can also provide a `host` property like the other integrations. If it is not provided, it will be determined from the provided region.
|
||||
i.e. When you provide `eu-west-1` as the region, Backstage will use the host `eu-west-1.console.aws.amazon.com` to check whether a provided url matches the integration.
|
||||
|
||||
## Register an entity on AWS CodeCommit
|
||||
|
||||
To register an entity that is stored in an AWS CodeCommit repository, you need to fetch the URL from the AWS Console:
|
||||
|
||||
- Navigate to the Code Commit service in the AWS Console ([https://console.aws.amazon.com/codecommit/home](https://console.aws.amazon.com/codecommit/home))
|
||||
- _Optional: Make sure you select the correct region if you don't use the default one (us-east-1)_
|
||||
- Select the repository where the entity file is stored
|
||||
- Inside the repository navigate to the backstage entity file (`catalog-info.yaml`) and open the file
|
||||
- Now you can copy-paste the URL from your browser inside Backstage
|
||||
|
||||
The format would be something like:
|
||||
`https://{region}.console.aws.amazon.com/codesuite/codecommit/repositories/{reponame}/browse/refs/heads/{branch}/--/catalog-info.yaml`
|
||||
|
||||
@@ -212,7 +212,7 @@ describe('AwsCodeCommitUrlReader', () => {
|
||||
integrations: {},
|
||||
});
|
||||
|
||||
expect(entries).toHaveLength(1);
|
||||
expect(entries).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('creates a reader with credentials correctly configured', () => {
|
||||
@@ -221,6 +221,7 @@ describe('AwsCodeCommitUrlReader', () => {
|
||||
host: AMAZON_AWS_CODECOMMIT_HOST,
|
||||
accessKeyId: 'fakekey',
|
||||
secretAccessKey: 'fakekey',
|
||||
region: 'fakeregion',
|
||||
});
|
||||
|
||||
const entries = createReader({
|
||||
@@ -236,6 +237,7 @@ describe('AwsCodeCommitUrlReader', () => {
|
||||
const awsCodeCommitIntegrations = [];
|
||||
awsCodeCommitIntegrations.push({
|
||||
host: AMAZON_AWS_CODECOMMIT_HOST,
|
||||
region: 'fakeregion',
|
||||
});
|
||||
|
||||
const entries = createReader({
|
||||
@@ -247,10 +249,32 @@ describe('AwsCodeCommitUrlReader', () => {
|
||||
expect(entries).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('creates a reader without a region', () => {
|
||||
const awsCodeCommitIntegrations: any[] = [];
|
||||
awsCodeCommitIntegrations.push({
|
||||
accessKeyId: 'fakekey',
|
||||
secretAccessKey: 'fakekey',
|
||||
});
|
||||
|
||||
expect(() => {
|
||||
createReader({
|
||||
integrations: {
|
||||
awsCodeCommit: awsCodeCommitIntegrations,
|
||||
},
|
||||
});
|
||||
}).toThrow(
|
||||
"Missing required config value at 'integrations.awsCodeCommit[0].region' in 'mock-config'",
|
||||
);
|
||||
});
|
||||
|
||||
describe('predicates', () => {
|
||||
const readers = createReader({
|
||||
integrations: {
|
||||
awsCodeCommit: [{}],
|
||||
awsCodeCommit: [
|
||||
{
|
||||
region: 'fakeregion',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
const predicate = readers[0].predicate;
|
||||
@@ -259,7 +283,7 @@ describe('AwsCodeCommitUrlReader', () => {
|
||||
expect(
|
||||
predicate(
|
||||
new URL(
|
||||
'https://eu-west-1.console.aws.amazon.com/codesuite/codecommit/repositories/my-repo',
|
||||
'https://fakeregion.console.aws.amazon.com/codesuite/codecommit/repositories/my-repo',
|
||||
),
|
||||
),
|
||||
).toBe(true);
|
||||
@@ -269,12 +293,54 @@ describe('AwsCodeCommitUrlReader', () => {
|
||||
expect(
|
||||
predicate(
|
||||
new URL(
|
||||
'https://eu-west-1.console.aws.amazon.com/codesuite/codecommit/repositories/my-repo/browse/--/catalog-info.yaml?region=eu-west-1',
|
||||
'https://fakeregion.console.aws.amazon.com/codesuite/codecommit/repositories/my-repo/browse/--/catalog-info.yaml?region=eu-west-1',
|
||||
),
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true for a url with the full path and the correct host when the host overrules the region', () => {
|
||||
const predicateWithHost = createReader({
|
||||
integrations: {
|
||||
awsCodeCommit: [
|
||||
{
|
||||
host: 'fakehost',
|
||||
region: 'fakeregion',
|
||||
},
|
||||
],
|
||||
},
|
||||
})[0].predicate;
|
||||
|
||||
expect(
|
||||
predicateWithHost(
|
||||
new URL(
|
||||
'https://fakehost/codesuite/codecommit/repositories/my-repo/browse/--/catalog-info.yaml?region=eu-west-1',
|
||||
),
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false for a url with the full path and the wrong host when the host overrules the region', () => {
|
||||
const predicateWithHost = createReader({
|
||||
integrations: {
|
||||
awsCodeCommit: [
|
||||
{
|
||||
host: 'fakehost',
|
||||
region: 'fakeregion',
|
||||
},
|
||||
],
|
||||
},
|
||||
})[0].predicate;
|
||||
|
||||
expect(
|
||||
predicateWithHost(
|
||||
new URL(
|
||||
'https://fakeregion.console.aws.amazon.com/codesuite/codecommit/repositories/my-repo/browse/--/catalog-info.yaml?region=eu-west-1',
|
||||
),
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for an incorrect host', () => {
|
||||
expect(predicate(new URL('https://amazon.com'))).toBe(false);
|
||||
});
|
||||
@@ -304,6 +370,7 @@ describe('AwsCodeCommitUrlReader', () => {
|
||||
host: 'amazonaws.com',
|
||||
accessKeyId: 'fake-access-key',
|
||||
secretAccessKey: 'fake-secret-key',
|
||||
region: 'fakeregion',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -348,6 +415,7 @@ describe('AwsCodeCommitUrlReader', () => {
|
||||
host: AMAZON_AWS_CODECOMMIT_HOST,
|
||||
accessKeyId: 'fake-access-key',
|
||||
secretAccessKey: 'fake-secret-key',
|
||||
region: 'fakeregion',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -403,6 +471,7 @@ describe('AwsCodeCommitUrlReader', () => {
|
||||
host: AMAZON_AWS_CODECOMMIT_HOST,
|
||||
accessKeyId: 'fake-access-key',
|
||||
secretAccessKey: 'fake-secret-key',
|
||||
region: 'fakeregion',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -509,9 +578,9 @@ describe('AwsCodeCommitUrlReader', () => {
|
||||
});
|
||||
|
||||
const config = new ConfigReader({
|
||||
host: AMAZON_AWS_CODECOMMIT_HOST,
|
||||
accessKeyId: 'fake-access-key',
|
||||
secretAccessKey: 'fake-secret-key',
|
||||
region: 'fakeregion',
|
||||
});
|
||||
|
||||
const credsManager = DefaultAwsCredentialsManager.fromConfig(config);
|
||||
@@ -527,7 +596,7 @@ describe('AwsCodeCommitUrlReader', () => {
|
||||
|
||||
it('returns contents of a file in a repository', async () => {
|
||||
const response = await awsCodeCommitUrlReader.readTree(
|
||||
'https://eu-west-1.console.aws.amazon.com/codesuite/codecommit/repositories/my-test-techdocs',
|
||||
'https://fakeregion.console.aws.amazon.com/codesuite/codecommit/repositories/my-test-techdocs',
|
||||
);
|
||||
|
||||
const files = await response.files();
|
||||
|
||||
@@ -26,6 +26,6 @@ describe('scmIntegrationsApiRef', () => {
|
||||
|
||||
it('should be instantiated', () => {
|
||||
const i = ScmIntegrationsApi.fromConfig(new ConfigReader({}));
|
||||
expect(i.list().length).toBe(7); // The default ones
|
||||
expect(i.list().length).toBe(6); // The default ones
|
||||
});
|
||||
});
|
||||
|
||||
@@ -35,6 +35,7 @@ export type AwsCodeCommitIntegrationConfig = {
|
||||
secretAccessKey?: string;
|
||||
roleArn?: string;
|
||||
externalId?: string;
|
||||
region: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
|
||||
@@ -30,30 +30,29 @@ describe('AwsCodeCommitIntegration', () => {
|
||||
secretAccessKey: ' secret key ',
|
||||
roleArn: `role arn`,
|
||||
externalId: `external id`,
|
||||
region: `region`,
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
});
|
||||
expect(integrations.list().length).toBe(1); // including default
|
||||
expect(integrations.list()[0].config.host).toBe(AMAZON_AWS_CODECOMMIT_HOST);
|
||||
expect(integrations.list()[0].config.host).toBe(
|
||||
`region.${AMAZON_AWS_CODECOMMIT_HOST}`,
|
||||
);
|
||||
expect(integrations.list()[0].config.accessKeyId).toBe('access key');
|
||||
expect(integrations.list()[0].config.secretAccessKey).toBe('secret key');
|
||||
expect(integrations.list()[0].config.roleArn).toBe(`role arn`);
|
||||
expect(integrations.list()[0].config.externalId).toBe(`external id`);
|
||||
expect(integrations.list()[0].config.region).toBe(`region`);
|
||||
});
|
||||
it('has a working factory with default values when no data provided', () => {
|
||||
it('does not have a working factory with default values when no data provided', () => {
|
||||
const integrations = AwsCodeCommitIntegration.factory({
|
||||
config: new ConfigReader({
|
||||
integrations: {},
|
||||
}),
|
||||
});
|
||||
expect(integrations.list().length).toBe(1); // including default
|
||||
expect(integrations.list()[0].config.host).toBe(AMAZON_AWS_CODECOMMIT_HOST);
|
||||
expect(integrations.list()[0].config.accessKeyId).toBeUndefined();
|
||||
expect(integrations.list()[0].config.secretAccessKey).toBeUndefined();
|
||||
expect(integrations.list()[0].config.roleArn).toBeUndefined();
|
||||
expect(integrations.list()[0].config.externalId).toBeUndefined();
|
||||
expect(integrations.list().length).toBe(0); // including default
|
||||
});
|
||||
|
||||
it('returns the basics', () => {
|
||||
|
||||
@@ -29,9 +29,12 @@ describe('readAwsCodeCommitIntegrationConfig', () => {
|
||||
}
|
||||
|
||||
it('reads all values (default)', () => {
|
||||
const output = readAwsCodeCommitIntegrationConfig(buildConfig({}));
|
||||
const output = readAwsCodeCommitIntegrationConfig(
|
||||
buildConfig({ region: 'us-east-1' }),
|
||||
);
|
||||
expect(output).toEqual({
|
||||
host: AMAZON_AWS_CODECOMMIT_HOST,
|
||||
host: `us-east-1.${AMAZON_AWS_CODECOMMIT_HOST}`,
|
||||
region: `us-east-1`,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -42,14 +45,16 @@ describe('readAwsCodeCommitIntegrationConfig', () => {
|
||||
secretAccessKey: 'fake-secret-key',
|
||||
externalId: 'fake-external-id',
|
||||
roleArn: 'fake-role-arn',
|
||||
region: 'fake-region',
|
||||
}),
|
||||
);
|
||||
expect(output).toEqual({
|
||||
host: AMAZON_AWS_CODECOMMIT_HOST,
|
||||
host: `fake-region.${AMAZON_AWS_CODECOMMIT_HOST}`,
|
||||
accessKeyId: 'fake-key',
|
||||
secretAccessKey: 'fake-secret-key',
|
||||
externalId: 'fake-external-id',
|
||||
roleArn: 'fake-role-arn',
|
||||
region: 'fake-region',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -70,6 +75,7 @@ describe('readAwsCodeCommitIntegrationConfigs', () => {
|
||||
secretAccessKey: 'secret',
|
||||
externalId: 'external-id',
|
||||
roleArn: 'role-arn',
|
||||
region: 'region',
|
||||
},
|
||||
]),
|
||||
);
|
||||
@@ -79,15 +85,12 @@ describe('readAwsCodeCommitIntegrationConfigs', () => {
|
||||
secretAccessKey: 'secret',
|
||||
externalId: 'external-id',
|
||||
roleArn: 'role-arn',
|
||||
region: 'region',
|
||||
});
|
||||
});
|
||||
|
||||
it('adds a default entry when missing', () => {
|
||||
const output = readAwsCodeCommitIntegrationConfigs(buildConfig([]));
|
||||
expect(output).toEqual([
|
||||
{
|
||||
host: AMAZON_AWS_CODECOMMIT_HOST,
|
||||
},
|
||||
]);
|
||||
expect(output).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -48,6 +48,11 @@ export type AwsCodeCommitIntegrationConfig = {
|
||||
* (Optional) External ID to use when assuming role
|
||||
*/
|
||||
externalId?: string;
|
||||
|
||||
/**
|
||||
* region to use for AWS (default: us-east-1)
|
||||
*/
|
||||
region: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -64,7 +69,10 @@ export function readAwsCodeCommitIntegrationConfig(
|
||||
const secretAccessKey = config.getOptionalString('secretAccessKey')?.trim();
|
||||
const roleArn = config.getOptionalString('roleArn');
|
||||
const externalId = config.getOptionalString('externalId');
|
||||
const host = AMAZON_AWS_CODECOMMIT_HOST;
|
||||
const region = config.getString('region');
|
||||
const host =
|
||||
config.getOptionalString('host') ||
|
||||
`${region}.${AMAZON_AWS_CODECOMMIT_HOST}`;
|
||||
|
||||
return {
|
||||
host,
|
||||
@@ -72,6 +80,7 @@ export function readAwsCodeCommitIntegrationConfig(
|
||||
secretAccessKey,
|
||||
roleArn,
|
||||
externalId,
|
||||
region,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -85,16 +94,5 @@ export function readAwsCodeCommitIntegrationConfig(
|
||||
export function readAwsCodeCommitIntegrationConfigs(
|
||||
configs: Config[],
|
||||
): AwsCodeCommitIntegrationConfig[] {
|
||||
// First read all the explicit integrations
|
||||
const result = configs.map(readAwsCodeCommitIntegrationConfig);
|
||||
|
||||
// If no explicit console.aws.amazon.com integration was added, put one in the list as
|
||||
// a convenience
|
||||
if (!result.some(c => c.host === AMAZON_AWS_CODECOMMIT_HOST)) {
|
||||
result.push({
|
||||
host: AMAZON_AWS_CODECOMMIT_HOST,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
return configs.map(readAwsCodeCommitIntegrationConfig);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user