/
service.go
183 lines (149 loc) · 5.85 KB
/
service.go
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/*
Copyright 2020 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
https://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.
*/
package automation
import (
"context"
"net/http"
"time"
"golang.org/x/oauth2"
"google.golang.org/api/cloudresourcemanager/v1"
"google.golang.org/api/compute/v1"
"google.golang.org/api/googleapi"
"google.golang.org/api/option"
"google.golang.org/api/recommender/v1"
"google.golang.org/api/serviceusage/v1"
)
// GoogleService is the inferface that prodives methods required to list recommendations and apply them
type GoogleService interface {
// changes the machine type of an instance
ChangeMachineType(project, zone, instance, machineType string) error
// creates a snapshot of a disk
CreateSnapshot(project, zone, disk, name string) error
// deletes persistent disk
DeleteDisk(project, zone, disk string) error
// gets the specified instance resource
GetInstance(project string, zone string, instance string) (*compute.Instance, error)
// gets recommendation by name
GetRecommendation(name string) (*gcloudRecommendation, error)
// lists whether the requirements have been met for all APIs (APIs enabled).
ListAPIRequirements(project string, apis []string) ([]*Requirement, error)
// lists whether the requirements have been met for all required permissions.
ListPermissionRequirements(project string, permissions [][]string) ([]*Requirement, error)
// lists projects
ListProjects() ([]string, error)
// listing recommendations for specified project, zone and recommender
ListRecommendations(project, location, recommenderID string) ([]*gcloudRecommendation, error)
// listing every zone available for the project methods
ListZonesNames(project string) ([]string, error)
// listing every region available for the project methods
ListRegionsNames(project string) ([]string, error)
// marks recommendation for the project with given etag and name claimed
MarkRecommendationClaimed(name, etag string) (*gcloudRecommendation, error)
// marks recommendation for the project with given etag and name succeeded
MarkRecommendationSucceeded(name, etag string) (*gcloudRecommendation, error)
// marks recommendation for the project with given etag and name failed
MarkRecommendationFailed(name, etag string) (*gcloudRecommendation, error)
// stops the specified instance
StopInstance(project, zone, instance string) error
// starts the specified instance
StartInstance(project, zone, instance string) error
}
// googleService implements GoogleService interface for Recommender and Compute APIs.
type googleService struct {
ctx context.Context
computeService *compute.Service
recommenderService *recommender.Service
resourceManagerService *cloudresourcemanager.Service
serviceUsageService *serviceusage.Service
}
// NewGoogleService creates new googleServices.
// If creation failed the error will be non-nil.
func NewGoogleService(ctx context.Context, conf *oauth2.Config, tok *oauth2.Token) (GoogleService, error) {
client := conf.Client(ctx, tok)
clientOption := option.WithHTTPClient(client)
computeService, err := compute.NewService(ctx, clientOption)
if err != nil {
return nil, err
}
recommenderService, err := recommender.NewService(ctx, clientOption)
if err != nil {
return nil, err
}
resourceManagerService, err := cloudresourcemanager.NewService(ctx, clientOption)
if err != nil {
return nil, err
}
serviceUsageService, err := serviceusage.NewService(ctx, clientOption)
if err != nil {
return nil, err
}
return &googleService{
ctx: ctx,
computeService: computeService,
recommenderService: recommenderService,
resourceManagerService: resourceManagerService,
serviceUsageService: serviceUsageService,
}, nil
}
const (
sleepTimeCreatingSnapshots = 20 * time.Second
sleepTimeDeletingDisks = 5 * time.Second
sleepTimeChangingMachineType = time.Second
sleepTimeStoppingInstance = time.Second
sleepTimeStartingInstance = time.Second
)
// for anonymous functions passed to AwaitCompletion
type operationGenerator func() (*compute.Operation, error)
// AwaitCompletion takes a function that needs to be called repeatedly
// to check if a process (some Google Service request) has finished.
// Such a function is usually constructed by wrapping a requestId(x).Do() call.
func AwaitCompletion(gen operationGenerator, sleepTime time.Duration) error {
for {
oper, err := gen()
if err != nil {
return err
}
if oper.Status == "DONE" {
return nil
}
time.Sleep(sleepTime)
}
}
// anonymous function passed to DoRequestWithRetries
type apiCall func() error
var httpStatusesToRetry = []int{http.StatusTooManyRequests, http.StatusBadGateway, http.StatusServiceUnavailable}
// DoRequestWithRetries calls the specified function while it returns error with
// error code listed in httpStatusesToRetry, and tries again after some time.
// Stops, when maxSleepTime is reached or code not in in httpStatusesToRetry.
// Returns the last received result from apiCall.
func DoRequestWithRetries(call apiCall) error {
sleepTime := 1 * time.Second
maxSleepTime := 1 * time.Minute // maximum time we'll try to wait for
for {
err := call()
if gErr, ok := err.(*googleapi.Error); ok {
retry := false
for _, status := range httpStatusesToRetry {
if gErr.Code == status {
retry = true
}
}
if retry && sleepTime <= maxSleepTime {
time.Sleep(sleepTime)
sleepTime *= 2
continue
}
}
return err
}
}