-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathindex.js
More file actions
136 lines (116 loc) · 4.34 KB
/
index.js
File metadata and controls
136 lines (116 loc) · 4.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright 2025 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.
// [START functions_billing_stop]
const {CloudBillingClient} = require('@google-cloud/billing');
const functions = require('@google-cloud/functions-framework');
const gcpMetadata = require('gcp-metadata');
const billing = new CloudBillingClient();
const projectIdEnv = process.env.GOOGLE_CLOUD_PROJECT;
functions.cloudEvent('StopBillingCloudEvent', async cloudEvent => {
// TODO(developer): As stopping billing is a destructive action
// for your project, change the following constant to `false`
// after you validate with a test budget.
const simulateDeactivation = true;
let projectId = projectIdEnv;
if (projectId === undefined) {
console.log(
'Project ID not found in env variables. Getting GCP metadata...'
);
try {
projectId = await gcpMetadata.project('project-id');
} catch (error) {
console.error('project-id metadata not found:', error);
return;
}
}
const projectName = `projects/${projectId}`;
// Find more information about the notification format here:
// https://cloud.google.com/billing/docs/how-to/budgets-programmatic-notifications#notification-format
const messageData = cloudEvent.data?.message?.data;
if (!messageData) {
console.error('Invalid CloudEvent: missing data.message.data');
return;
}
const eventData = Buffer.from(messageData, 'base64').toString();
let eventObject;
try {
eventObject = JSON.parse(eventData);
} catch (e) {
console.error('Error parsing event data:', e);
return;
}
console.log(
`Project ID: ${projectId} ` +
`Current cost: ${eventObject.costAmount} ` +
`Budget: ${eventObject.budgetAmount}`
);
if (eventObject.costAmount <= eventObject.budgetAmount) {
console.log('No action required. Current cost is within budget.');
return;
}
console.log(`Disabling billing for project '${projectName}'...`);
const billingEnabled = _isBillingEnabled(projectName);
if (billingEnabled) {
_disableBillingForProject(projectName, simulateDeactivation);
} else {
console.log('Billing is already disabled.');
}
});
/**
* Determine whether billing is enabled for a project
* @param {string} projectName The name of the project to check
* @returns {boolean} Whether the project has billing enabled or not
*/
const _isBillingEnabled = async projectName => {
try {
console.log(`Getting billing info for project '${projectName}'...`);
const [res] = await billing.getProjectBillingInfo({name: projectName});
return res.billingEnabled;
} catch (e) {
console.log('Error getting billing info:', e);
console.log(
'Unable to determine if billing is enabled on specified project, ' +
'assuming billing is enabled'
);
return true;
}
};
/**
* Disable billing for a project by removing its billing account
* @param {string} projectName The name of the project to disable billing
* @param {boolean} simulateDeactivation
* If true, it won't actually disable billing.
* Useful to validate with test budgets.
* @returns {void}
*/
const _disableBillingForProject = async (projectName, simulateDeactivation) => {
if (simulateDeactivation) {
console.log('Billing disabled. (Simulated)');
return;
}
// Find more information about `projects/updateBillingInfo` API method here:
// https://cloud.google.com/billing/docs/reference/rest/v1/projects/updateBillingInfo
try {
// To disable billing set the `billingAccountName` field to empty
const requestBody = {billingAccountName: ''};
const [response] = await billing.updateProjectBillingInfo({
name: projectName,
resource: requestBody,
});
console.log(`Billing disabled: ${JSON.stringify(response)}`);
} catch (e) {
console.log('Failed to disable billing, check permissions.', e);
}
};
// [END functions_billing_stop]