Skip to content

Commit 1a475fa

Browse files
authored
feat(storage): add action to create GCS buckets (#16)
chore(lint): Run Prettier docs: update README for GCS bucket creation chore: Add Changeset chore(template): remove unnecessary blank line in create-bucket template
1 parent 3a10c3d commit 1a475fa

File tree

9 files changed

+317
-1
lines changed

9 files changed

+317
-1
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@datolabs/plugin-scaffolder-backend-module-gcp': minor
3+
---
4+
5+
Add Action to create GCS Bucket
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
apiVersion: scaffolder.backstage.io/v1beta3
2+
kind: Template
3+
metadata:
4+
name: create-gcp-gcs-bucket
5+
title: Create a GCP GCS Bucket
6+
description: Creates a new GCS Bucket.
7+
tags:
8+
- gcp
9+
spec:
10+
type: template
11+
parameters:
12+
- title: Bucket Details
13+
required:
14+
- project
15+
- bucketName
16+
properties:
17+
bucketName:
18+
title: Name of GCS Bucket to create. Must be globally unique.
19+
type: string
20+
description: Bucket Name
21+
project:
22+
title: Project ID
23+
type: string
24+
description: Project ID
25+
autoclass:
26+
title: Auto Class
27+
type: boolean
28+
properties:
29+
enabled:
30+
type: boolean
31+
title: Enable Auto Class
32+
description: Enable Auto Class
33+
default: true
34+
default: true
35+
#ui:widget: CheckboxesWidget
36+
location:
37+
title: Location
38+
type: string
39+
description: Location
40+
default: us-central1
41+
42+
steps:
43+
- id: create-bucket
44+
name: Create GCP Bucket
45+
action: datolabs:gcp:bucket:create
46+
input:
47+
bucketName: ${{ parameters.bucketName }}
48+
project: ${{ parameters.project }}
49+
autoClass: ${{ parameters.autoclass }}
50+
location: ${{ parameters.location }}
51+
52+
output:
53+
links:
54+
- title: View the Bucket in GCP Console
55+
url: https://console.cloud.google.com/storage/browser?project=${{ parameters.project }}

workspaces/scaffolder-backend-module-gcp/examples/create-project.template.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ metadata:
99
spec:
1010
type: template
1111
parameters:
12-
- title: Secret Details
12+
- title: Project Details
1313
required:
1414
- displayName
1515
- parent

workspaces/scaffolder-backend-module-gcp/plugins/scaffolder-backend-module-gcp/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ Make sure the service account has the necessary IAM roles for the actions you pl
5555

5656
- For Secret Manager: `roles/secretmanager.admin` or more granular permissions
5757
- For Create projects: `roles/resourcemanager.projectCreator` or more [granular permissions](https://cloud.google.com/resource-manager/docs/creating-managing-projects#creating_a_project)
58+
- For Create GCS Buckets: `roles/storage.admin` or more [granular permissions](https://cloud.google.com/storage/docs/access-control/iam-roles)
59+
- For Create GCS Bucket IAM Policy: `roles/storage.admin` or more [granular permissions](https://cloud.google.com/storage/docs/access-control/iam-roles)
5860

5961
## Usage Examples
6062

@@ -89,4 +91,18 @@ steps:
8991
projectId: ${{ parameters.projectId }}
9092
```
9193

94+
### Creating a GCS Bucket
95+
96+
```yaml
97+
steps:
98+
- id: create-bucket
99+
name: Create GCP Bucket
100+
action: datolabs:gcp:bucket:create
101+
input:
102+
bucketName: ${{ parameters.bucketName }}
103+
project: ${{ parameters.project }}
104+
autoClass: ${{ parameters.autoclass }}
105+
location: ${{ parameters.location }}
106+
```
107+
92108
Full template examples can be found in the [examples](../../examples) directory.

workspaces/scaffolder-backend-module-gcp/plugins/scaffolder-backend-module-gcp/src/actions/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@
1515
*/
1616
export * from './secrets-manager';
1717
export * from './resource-manager';
18+
export * from './storage';
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright 2024 Datolabs, MB
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { getVoidLogger } from '@backstage/backend-common';
18+
import { createGcpGcsBucketCreateAction } from './create';
19+
20+
jest.mock('@google-cloud/storage');
21+
22+
describe('createGcpGcsBucketCreateAction', () => {
23+
const mockCreateBucket = jest.fn();
24+
const mockStorage = {
25+
createBucket: mockCreateBucket,
26+
};
27+
28+
beforeEach(() => {
29+
jest.resetAllMocks();
30+
const { Storage } = require('@google-cloud/storage');
31+
32+
Storage.mockImplementation(() => mockStorage);
33+
});
34+
35+
afterEach(() => {
36+
jest.resetAllMocks();
37+
});
38+
39+
it('should create a simple GCS bucket with the given parameters', async () => {
40+
const action = createGcpGcsBucketCreateAction();
41+
const logger = getVoidLogger();
42+
jest.spyOn(logger, 'info');
43+
const input = {
44+
input: {
45+
project: 'test-project',
46+
bucketName: 'test-bucket',
47+
autoClass: false,
48+
location: undefined,
49+
storageClass: 'standard',
50+
versioning: undefined,
51+
hierarchicalNamespace: undefined,
52+
},
53+
logger,
54+
};
55+
const context: any = input;
56+
57+
await action.handler(context);
58+
59+
expect(mockCreateBucket).toHaveBeenCalledWith('test-bucket', {
60+
projectId: 'test-project',
61+
autoclass: { enabled: false },
62+
location: undefined,
63+
storageClass: 'standard',
64+
versioning: { enabled: false },
65+
hierarchicalNamespace: { enabled: false },
66+
});
67+
expect(logger.info).toHaveBeenCalledWith(
68+
'Bucket test-bucket successfully created',
69+
);
70+
});
71+
72+
it('should create a GCS bucket in us-east4 with autoclass enabled', async () => {
73+
const action = createGcpGcsBucketCreateAction();
74+
const logger = getVoidLogger();
75+
jest.spyOn(logger, 'info');
76+
const input = {
77+
input: {
78+
project: 'test-project',
79+
bucketName: 'test-bucket-us-east4',
80+
autoClass: true,
81+
location: 'us-east4',
82+
storageClass: 'standard',
83+
versioning: undefined,
84+
hierarchicalNamespace: undefined,
85+
},
86+
logger,
87+
};
88+
const context: any = input;
89+
90+
await action.handler(context);
91+
92+
expect(mockCreateBucket).toHaveBeenCalledWith('test-bucket-us-east4', {
93+
projectId: 'test-project',
94+
autoclass: { enabled: true },
95+
location: 'us-east4',
96+
storageClass: 'standard',
97+
versioning: { enabled: false },
98+
hierarchicalNamespace: { enabled: false },
99+
});
100+
expect(logger.info).toHaveBeenCalledWith(
101+
'Bucket test-bucket-us-east4 successfully created',
102+
);
103+
});
104+
});
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Copyright 2024 Datolabs, MB
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
import {
17+
createTemplateAction,
18+
TemplateAction,
19+
} from '@backstage/plugin-scaffolder-node';
20+
import { Storage } from '@google-cloud/storage';
21+
22+
export function createGcpGcsBucketCreateAction(): TemplateAction<{
23+
project: string;
24+
autoClass: boolean;
25+
bucketName: string;
26+
hierarchicalNamespace: boolean;
27+
location: string;
28+
storageClass: string;
29+
versioning: boolean;
30+
}> {
31+
return createTemplateAction({
32+
id: 'datolabs:gcp:bucket:create',
33+
description: 'Creates a new GCS Bucket in GCP ',
34+
schema: {
35+
input: {
36+
required: ['project', 'bucketName'],
37+
type: 'object',
38+
properties: {
39+
project: {
40+
title: 'Project ID',
41+
description: 'The GCP project ID',
42+
type: 'string',
43+
},
44+
bucketName: {
45+
title: 'Bucket Name',
46+
description:
47+
'The name of the bucket to be created. Must be globally unique.',
48+
type: 'string',
49+
},
50+
autoClass: {
51+
title: 'Auto Class',
52+
description: 'Enable auto class.',
53+
type: 'boolean',
54+
required: false,
55+
default: false,
56+
},
57+
location: {
58+
title: 'Location',
59+
description:
60+
'The location of the bucket. https://cloud.google.com/storage/docs/locations',
61+
type: 'string',
62+
required: false,
63+
},
64+
storageClass: {
65+
title: 'Storage Class',
66+
description:
67+
'The storage class of the bucket. https://cloud.google.com/storage/docs/storage-classes. Must be: "standard, nearline, coldline, or archive"',
68+
type: 'string',
69+
default: 'standard',
70+
required: false,
71+
},
72+
versioning: {
73+
title: 'Versioning',
74+
description: 'Enable versioning.',
75+
type: 'boolean',
76+
required: false,
77+
default: false,
78+
},
79+
hierarchicalNamespace: {
80+
title: 'Hierarchical Namespace',
81+
description: 'Enable hierarchical namespace.',
82+
type: 'boolean',
83+
required: false,
84+
default: false,
85+
},
86+
},
87+
},
88+
},
89+
async handler(ctx) {
90+
const storageClient = new Storage();
91+
const {
92+
autoClass = false,
93+
bucketName,
94+
hierarchicalNamespace = false,
95+
location,
96+
project,
97+
storageClass = 'standard',
98+
versioning = false,
99+
} = ctx.input;
100+
101+
try {
102+
const request = {
103+
projectId: project,
104+
storageClass: storageClass,
105+
location: location,
106+
autoclass: { enabled: autoClass },
107+
versioning: { enabled: versioning },
108+
hierarchicalNamespace: { enabled: hierarchicalNamespace },
109+
};
110+
await storageClient.createBucket(bucketName, request);
111+
ctx.logger.info(`Bucket ${bucketName} successfully created`);
112+
} catch (e) {
113+
ctx.logger.error('Error creating Bucket:', e);
114+
throw e;
115+
}
116+
},
117+
});
118+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright 2024 Datolabs, MB
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
export * from './create';

workspaces/scaffolder-backend-module-gcp/plugins/scaffolder-backend-module-gcp/src/module.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export const scaffolderBackendModuleGCP = createBackendModule({
3232
scaffolder.addActions(
3333
backendModuleGCP.createGcpSecretsManagerCreateAction(),
3434
backendModuleGCP.createGcpProjectCreateAction(),
35+
backendModuleGCP.createGcpGcsBucketCreateAction(),
3536
);
3637
},
3738
});

0 commit comments

Comments
 (0)