radlab-ui/automation/terraform/infrastructure/function/create_deployment/index.js (111 lines of code) (raw):
/**
* Copyright 2023 Google LLC
*
* 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.
*/
/**
* Exports a Cloud Function that creates a Build for a RAD Lab module and
* updates Firestore with the new Cloud Build ID.
*/
"use strict"
const admin = require("firebase-admin")
const {CloudBuildClient} = require("@google-cloud/cloudbuild")
const {getFirestore, Timestamp} = require("firebase-admin/firestore")
const {Storage} = require("@google-cloud/storage")
const envOrFail = (name) => {
const env = process.env[name]
if (!env) throw new Error(`${name} is not set`)
return env
}
const CLOUD_BUILD_PROJECT_ID = envOrFail("PROJECT_ID")
const CREATE_TRIGGER_ID = envOrFail("CREATE_TRIGGER_ID")
const UPDATE_TRIGGER_ID = envOrFail("UPDATE_TRIGGER_ID")
const DEPLOYMENTS_BUCKET_NAME = envOrFail("DEPLOYMENT_BUCKET_NAME")
const PARENT_FOLDER_ID = envOrFail("PARENT_FOLDER_ID")
admin.initializeApp()
const cloudBuild = new CloudBuildClient()
const storage = new Storage()
const db = getFirestore()
exports.createRadLabModule = (message, context) => {
const messageData = JSON.parse(Buffer.from(message.data, "base64").toString())
let action = messageData.action
let deploymentId = messageData.deploymentId
let moduleName = messageData.module
const user = messageData.user
messageData.variables.folder_id = `folders/${PARENT_FOLDER_ID}`
if (!messageData.variables.trusted_users.includes(user)) {
messageData.variables.trusted_users.push(user);
}
let triggerName = "";
if (action === "CREATE") {
console.log(
`Creating new module, ${moduleName} with deployment ID ${deploymentId}`,
)
triggerName = CREATE_TRIGGER_ID
let backendConfig = `terraform {
backend "gcs" {
bucket = "${DEPLOYMENTS_BUCKET_NAME}"
prefix = "deployments/${moduleName}_${deploymentId}/state/"
}
}`
storage
.bucket(DEPLOYMENTS_BUCKET_NAME)
.file(`deployments/${moduleName}_${deploymentId}/files/backend.tf`)
.save(backendConfig)
} else {
console.log(
`Updating module, ${moduleName} with deployment ID ${deploymentId}`,
)
triggerName = UPDATE_TRIGGER_ID
}
return storage
.bucket(DEPLOYMENTS_BUCKET_NAME)
.file(
`deployments/${moduleName}_${deploymentId}/files/terraform.tfvars.json`,
)
.save(JSON.stringify(messageData.variables, null, 2))
.then((result) => {
const request = {
projectId: CLOUD_BUILD_PROJECT_ID,
triggerId: triggerName,
source: {
substitutions: {
_MODULE_NAME: moduleName,
_DEPLOYMENT_ID: deploymentId,
},
},
}
return cloudBuild.runBuildTrigger(request).then((result) => {
let metadata = result[0].metadata
let buildId = metadata.build.id
return db
.collection("deployments")
.where("deploymentId", "==", deploymentId.toString())
.get()
.then((query) => {
const deployment = query.docs[0]
if (!deployment)
throw new Error(`Could not find deployment for ${deploymentId}`)
let documentData = deployment.data()
const newBuild = {
buildId,
action,
user,
status: "QUEUED",
createdAt: Timestamp.now(),
}
documentData.builds.push(newBuild)
return deployment.ref
.update(documentData)
.then((result) => {
console.log(
`Deployment with ID ${deploymentId} updated with build ID ${buildId}.`,
)
})
.catch((error) => {
console.error(
"Exception while storing Build ID in Firestore",
error,
)
})
})
.catch((error) => {
console.error(
`Exception while retrieving the document for deployment ID ${deploymentId}`,
error,
)
})
})
})
}