@@ -2,7 +2,6 @@ package main
22
33import (
44 "context"
5- "encoding/json"
65 "fmt"
76 "log/slog"
87 "net/http"
@@ -14,6 +13,10 @@ import (
1413 "sync"
1514 "syscall"
1615 "time"
16+
17+ "golang.org/x/oauth2/google"
18+ compute "google.golang.org/api/compute/v1"
19+ "google.golang.org/api/option"
1720)
1821
1922type Config struct {
@@ -26,16 +29,6 @@ type Config struct {
2629 GCEInstance string
2730}
2831
29- type AccessToken struct {
30- AccessToken string `json:"access_token"`
31- ExpiresIn int `json:"expires_in"`
32- TokenType string `json:"token_type"`
33- }
34-
35- type ComputeInstance struct {
36- Status string `json:"status"`
37- }
38-
3932type ActivityTracker struct {
4033 mu sync.RWMutex
4134 requestCount int64
@@ -162,115 +155,57 @@ func getLastGitHubActionsActivity() (time.Time, error) {
162155 return time.Time {}, fmt .Errorf ("could not parse github-actions timestamp" )
163156}
164157
165- func getAccessToken () (AccessToken , error ) {
166- t := AccessToken {}
167-
168- req , err := http .NewRequest ("GET" , "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token" , nil )
158+ func createComputeService (ctx context.Context ) (* compute.Service , error ) {
159+ // Use Application Default Credentials (ADC)
160+ // This will automatically use:
161+ // 1. GOOGLE_APPLICATION_CREDENTIALS environment variable
162+ // 2. GCE metadata server (when running on GCE)
163+ // 3. gcloud CLI credentials
164+ creds , err := google .FindDefaultCredentials (ctx , compute .ComputeScope )
169165 if err != nil {
170- return t , err
166+ return nil , fmt . Errorf ( "failed to find default credentials: %w" , err )
171167 }
172- req .Header .Set ("Metadata-Flavor" , "Google" )
173168
174- client := & http.Client {Timeout : 10 * time .Second }
175- resp , err := client .Do (req )
169+ service , err := compute .NewService (ctx , option .WithCredentials (creds ))
176170 if err != nil {
177- return t , err
178- }
179- defer resp .Body .Close ()
180-
181- if resp .StatusCode != http .StatusOK {
182- return t , fmt .Errorf ("fetching token non-200: %d" , resp .StatusCode )
171+ return nil , fmt .Errorf ("failed to create compute service: %w" , err )
183172 }
184173
185- if err := json .NewDecoder (resp .Body ).Decode (& t ); err != nil {
186- return t , fmt .Errorf ("failed to decode token response: %w" , err )
187- }
188- return t , nil
189- }
190-
191- func getMachineMetadata (token AccessToken , targetURL string ) (ComputeInstance , error ) {
192- vm := ComputeInstance {}
193-
194- req , err := http .NewRequest ("GET" , targetURL , nil )
195- if err != nil {
196- return vm , err
197- }
198- req .Header .Set ("Authorization" , "Bearer " + token .AccessToken )
199-
200- client := & http.Client {Timeout : 30 * time .Second }
201- resp , err := client .Do (req )
202- if err != nil {
203- return vm , err
204- }
205- defer resp .Body .Close ()
206-
207- if resp .StatusCode != http .StatusOK {
208- return vm , fmt .Errorf ("getMachineMetadata non-200: %d" , resp .StatusCode )
209- }
210-
211- if err := json .NewDecoder (resp .Body ).Decode (& vm ); err != nil {
212- return vm , fmt .Errorf ("failed to decode instance metadata: %w" , err )
213- }
214- return vm , nil
215- }
216-
217- func performInstanceAction (token AccessToken , targetURL , action string ) error {
218- actionURL := targetURL + "/" + action
219-
220- req , err := http .NewRequest ("POST" , actionURL , nil )
221- if err != nil {
222- return err
223- }
224- req .Header .Set ("Authorization" , "Bearer " + token .AccessToken )
225-
226- client := & http.Client {Timeout : 30 * time .Second }
227- resp , err := client .Do (req )
228- if err != nil {
229- return err
230- }
231- defer resp .Body .Close ()
232-
233- if resp .StatusCode != http .StatusOK {
234- return fmt .Errorf ("performInstanceAction non-200: %d" , resp .StatusCode )
235- }
236-
237- return nil
174+ return service , nil
238175}
239176
240- func suspendMachine () (ComputeInstance , error ) {
241- targetURL := fmt .Sprintf ("https://compute.googleapis.com/compute/v1/projects/%s/zones/%s/instances/%s" ,
242- config .GoogleProjectID , config .GCEZone , config .GCEInstance )
177+ func suspendMachine () (* compute.Instance , error ) {
178+ ctx := context .Background ()
243179
244180 slog .Info ("Checking if machine is suspended" ,
245181 "project" , config .GoogleProjectID ,
246182 "zone" , config .GCEZone ,
247183 "instance" , config .GCEInstance )
248184
249- // get access token
250- t , err := getAccessToken ( )
185+ // Create compute service with default credentials
186+ service , err := createComputeService ( ctx )
251187 if err != nil {
252- return ComputeInstance {} , fmt .Errorf ("getAccessToken : %v" , err )
188+ return nil , fmt .Errorf ("createComputeService : %v" , err )
253189 }
254190
255- // get machine metadata
256- vm , err := getMachineMetadata ( t , targetURL )
191+ // Get instance details
192+ instance , err := service . Instances . Get ( config . GoogleProjectID , config . GCEZone , config . GCEInstance ). Context ( ctx ). Do ( )
257193 if err != nil {
258- return vm , fmt .Errorf ("getMachineMetadata : %v" , err )
194+ return nil , fmt .Errorf ("failed to get instance : %v" , err )
259195 }
260196
261- // if the machine is running, suspend it
262- if vm .Status == "RUNNING" {
263- action := "suspend"
197+ // If the machine is running, suspend it
198+ if instance .Status == "RUNNING" {
264199 slog .Info ("Instance is RUNNING, suspending instance" )
265- err := performInstanceAction ( t , targetURL , action )
200+ _ , err := service . Instances . Suspend ( config . GoogleProjectID , config . GCEZone , config . GCEInstance ). Context ( ctx ). Do ( )
266201 if err != nil {
267- return vm , fmt .Errorf ("performInstanceAction : %v" , err )
202+ return instance , fmt .Errorf ("failed to suspend instance : %v" , err )
268203 }
269204 } else {
270- slog .Info ("Instance is not RUNNING, skipping suspension" , "status" , vm .Status )
205+ slog .Info ("Instance is not RUNNING, skipping suspension" , "status" , instance .Status )
271206 }
272207
273- return vm , nil
208+ return instance , nil
274209}
275210
276211func suspendInstance () error {
0 commit comments