/* * Copyright © 2020 Atomist, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import * as gcp from "@pulumi/gcp"; import * as iam from "../iam"; import * as pubsub from "../pubsub"; interface BucketArgs { bucketName: string; environment: string; gcpProject: string; serviceName: string; workloadIdentityServiceAccount: gcp.serviceaccount.Account; } export function bucket({ bucketName, environment, gcpProject, serviceName, workloadIdentityServiceAccount, }: BucketArgs): gcp.storage.Bucket { const bucket = new gcp.storage.Bucket( `${serviceName}-${bucketName}-bucket`, { project: gcpProject, name: `atm-${environment}-${serviceName}-${bucketName}`, forceDestroy: false, labels: { managed_by: "pulumi", }, lifecycleRules: [ { action: { storageClass: "NEARLINE", type: "SetStorageClass", }, condition: { age: 180, matchesStorageClasses: ["STANDARD"], }, }, ], location: "US-EAST1", publicAccessPrevention: "enforced", storageClass: "STANDARD", uniformBucketLevelAccess: true, }, ); new gcp.storage.BucketIAMMember( `${serviceName}-${bucketName}-bucket-${serviceName}-iam-member`, { bucket: bucket.name, member: workloadIdentityServiceAccount.member, role: "roles/storage.objectUser", }, ); return bucket; } export interface createNotificationArgs { sourceProject: string; targetProject: string; topic: gcp.pubsub.Topic; sourceBucket: gcp.storage.Bucket; } export async function createNotification( notificationResourceId: string, { sourceProject, targetProject, topic, sourceBucket, }: createNotificationArgs, ): Promise { const projectStorageServiceAccount = await gcp.storage.getProjectServiceAccount({ project: sourceProject, }); const notificationTopicIAMMember = new gcp.pubsub.TopicIAMMember( `${notificationResourceId}-gcs-sa-publisher-iam-member`, { project: targetProject, topic: topic.id, role: "roles/pubsub.publisher", member: projectStorageServiceAccount.member, }, ); return new gcp.storage.Notification( notificationResourceId, { bucket: sourceBucket.name, payloadFormat: "JSON_API_V1", topic: topic.id, eventTypes: ["OBJECT_FINALIZE"], }, { dependsOn: [notificationTopicIAMMember], }, ); } export interface CreateNotificationSubscriptionArgs { gcpProject: string; notification: gcp.storage.Notification; prefix: string; serviceAccount: gcp.serviceaccount.Account; subscriberRole: string; } export function createNotificationSubscription({ gcpProject, notification, prefix, serviceAccount, subscriberRole, }: CreateNotificationSubscriptionArgs): gcp.pubsub.Subscription { const subscription = pubsub.createSubscription( gcpProject, prefix, // Pulumi is supposed to make inputs implicitly available as outputs, // but for some reason it adds this to the `topic`. Let's remove it // because the subscription doesn't like it. notification.topic.apply(t => t.replace(/\/\/pubsub\.googleapis\.com\//, ""), ), ); new gcp.pubsub.SubscriptionIAMMember( `${prefix}-subscription-sa-subscriber-iam-member`, { project: gcpProject, member: serviceAccount.member, role: "roles/pubsub.subscriber", subscription: subscription.id, }, ); new gcp.storage.BucketIAMMember( `${prefix}-source-bucket-${iam.simpleRoleName(subscriberRole)}-iam-member`, { bucket: notification.bucket, member: serviceAccount.member, role: subscriberRole, }, ); return subscription; }