11/**
2- * Copyright 2018 , Google LLC.
2+ * Copyright 2019 , Google LLC.
33 * Licensed under the Apache License, Version 2.0 (the "License");
44 * you may not use this file except in compliance with the License.
55 * You may obtain a copy of the License at
1313 * limitations under the License.
1414 */
1515
16+ /* eslint-disable no-warning-comments */
17+
1618// [START functions_billing_limit]
1719// [START functions_billing_stop]
1820const { google} = require ( 'googleapis' ) ;
@@ -26,29 +28,33 @@ const PROJECT_NAME = `projects/${PROJECT_ID}`;
2628// [START functions_billing_slack]
2729const slack = require ( 'slack' ) ;
2830
29- const BOT_ACCESS_TOKEN = 'xxxx-111111111111-abcdefghidklmnopq' ;
30- const CHANNEL = 'general' ;
31+ // TODO(developer) replace these with your own values
32+ const BOT_ACCESS_TOKEN =
33+ process . env . BOT_ACCESS_TOKEN || 'xxxx-111111111111-abcdefghidklmnopq' ;
34+ const CHANNEL = process . env . SLACK_CHANNEL || 'general' ;
3135
32- exports . notifySlack = async ( data , context ) => {
33- const pubsubMessage = data ;
34- const pubsubAttrs = JSON . stringify ( pubsubMessage . attributes ) ;
35- const pubsubData = Buffer . from ( pubsubMessage . data , 'base64' ) . toString ( ) ;
36+ exports . notifySlack = async ( pubsubEvent , context ) => {
37+ const pubsubAttrs = pubsubEvent . attributes ;
38+ const pubsubData = Buffer . from ( pubsubEvent . data , 'base64' ) . toString ( ) ;
3639 const budgetNotificationText = `${ pubsubAttrs } , ${ pubsubData } ` ;
3740
38- const res = await slack . chat . postMessage ( {
41+ await slack . chat . postMessage ( {
3942 token : BOT_ACCESS_TOKEN ,
4043 channel : CHANNEL ,
4144 text : budgetNotificationText ,
4245 } ) ;
43- console . log ( res ) ;
46+
47+ return 'Slack notification sent successfully' ;
4448} ;
4549// [END functions_billing_slack]
4650
4751// [START functions_billing_stop]
4852const billing = google . cloudbilling ( 'v1' ) . projects ;
4953
50- exports . stopBilling = async ( data , context ) => {
51- const pubsubData = JSON . parse ( Buffer . from ( data . data , 'base64' ) . toString ( ) ) ;
54+ exports . stopBilling = async ( pubsubEvent , context ) => {
55+ const pubsubData = JSON . parse (
56+ Buffer . from ( pubsubEvent . data , 'base64' ) . toString ( )
57+ ) ;
5258 if ( pubsubData . costAmount <= pubsubData . budgetAmount ) {
5359 return `No action necessary. (Current cost: ${ pubsubData . costAmount } )` ;
5460 }
@@ -105,19 +111,50 @@ const _disableBillingForProject = async projectName => {
105111} ;
106112// [END functions_billing_stop]
107113
114+ // Helper function to restart billing (used in tests)
115+ exports . startBilling = async ( pubsubEvent , context ) => {
116+ const pubsubData = JSON . parse (
117+ Buffer . from ( pubsubEvent . data , 'base64' ) . toString ( )
118+ ) ;
119+
120+ await _setAuthCredential ( ) ;
121+ if ( ! ( await _isBillingEnabled ( PROJECT_NAME ) ) ) {
122+ // Enable billing
123+
124+ const res = await billing . updateBillingInfo ( {
125+ name : pubsubData . projectName ,
126+ resource : {
127+ billingAccountName : pubsubData . billingAccountName ,
128+ billingEnabled : true ,
129+ } ,
130+ } ) ;
131+ return `Billing enabled: ${ JSON . stringify ( res . data ) } ` ;
132+ } else {
133+ return 'Billing already enabled' ;
134+ }
135+ } ;
136+
108137// [START functions_billing_limit]
109138const compute = google . compute ( 'v1' ) ;
110- const ZONE = 'us-west1-b ' ;
139+ const ZONE = 'us-west1-a ' ;
111140
112- exports . limitUse = async ( data , context ) => {
113- const pubsubData = JSON . parse ( Buffer . from ( data . data , 'base64' ) . toString ( ) ) ;
141+ exports . limitUse = async ( pubsubEvent , context ) => {
142+ const pubsubData = JSON . parse (
143+ Buffer . from ( pubsubEvent . data , 'base64' ) . toString ( )
144+ ) ;
114145 if ( pubsubData . costAmount <= pubsubData . budgetAmount ) {
115146 return `No action necessary. (Current cost: ${ pubsubData . costAmount } )` ;
116147 }
117148
118149 await _setAuthCredential ( ) ;
150+
119151 const instanceNames = await _listRunningInstances ( PROJECT_ID , ZONE ) ;
152+ if ( ! instanceNames . length ) {
153+ return 'No running instances were found.' ;
154+ }
155+
120156 await _stopInstances ( PROJECT_ID , ZONE , instanceNames ) ;
157+ return `${ instanceNames . length } instance(s) stopped successfully.` ;
121158} ;
122159
123160/**
@@ -139,9 +176,6 @@ const _listRunningInstances = async (projectId, zone) => {
139176 * @return {Promise } Response from stopping instances
140177 */
141178const _stopInstances = async ( projectId , zone , instanceNames ) => {
142- if ( ! instanceNames . length ) {
143- return 'No running instances were found.' ;
144- }
145179 await Promise . all (
146180 instanceNames . map ( instanceName => {
147181 return compute . instances
@@ -158,3 +192,56 @@ const _stopInstances = async (projectId, zone, instanceNames) => {
158192 ) ;
159193} ;
160194// [END functions_billing_limit]
195+
196+ // Helper function to restart instances (used in tests)
197+ exports . startInstances = async ( pubsubEvent , context ) => {
198+ await _setAuthCredential ( ) ;
199+ const instanceNames = await _listStoppedInstances ( PROJECT_ID , ZONE ) ;
200+
201+ if ( ! instanceNames . length ) {
202+ return 'No stopped instances were found.' ;
203+ }
204+
205+ await _startInstances ( PROJECT_ID , ZONE , instanceNames ) ;
206+ return `${ instanceNames . length } instance(s) started successfully.` ;
207+ } ;
208+
209+ /**
210+ * @return {Promise } Array of names of running instances
211+ */
212+ const _listStoppedInstances = async ( projectId , zone ) => {
213+ const res = await compute . instances . list ( {
214+ project : projectId ,
215+ zone : zone ,
216+ } ) ;
217+
218+ const instances = res . data . items || [ ] ;
219+ const stoppedInstances = instances . filter ( item => item . status !== 'RUNNING' ) ;
220+ return stoppedInstances . map ( item => item . name ) ;
221+ } ;
222+
223+ /**
224+ * @param {Array } instanceNames Names of instance to stop
225+ * @return {Promise } Response from stopping instances
226+ */
227+ const _startInstances = async ( projectId , zone , instanceNames ) => {
228+ if ( ! instanceNames . length ) {
229+ return 'No stopped instances were found.' ;
230+ }
231+ await Promise . all (
232+ instanceNames . map ( instanceName => {
233+ return compute . instances . start ( {
234+ project : projectId ,
235+ zone : zone ,
236+ instance : instanceName ,
237+ } ) ;
238+ } )
239+ ) ;
240+ } ;
241+
242+ // Helper function used in tests
243+ exports . listRunningInstances = async ( pubsubEvent , context ) => {
244+ await _setAuthCredential ( ) ;
245+ console . log ( PROJECT_ID , ZONE ) ;
246+ return _listRunningInstances ( PROJECT_ID , ZONE ) ;
247+ } ;
0 commit comments