/
table_gcp_cloudfunctions_function.go
339 lines (303 loc) · 10.7 KB
/
table_gcp_cloudfunctions_function.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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
package gcp
import (
"context"
"fmt"
"strings"
"github.com/turbot/go-kit/types"
"github.com/turbot/steampipe-plugin-sdk/v5/grpc/proto"
"github.com/turbot/steampipe-plugin-sdk/v5/plugin"
"github.com/turbot/steampipe-plugin-sdk/v5/plugin/transform"
"google.golang.org/api/cloudfunctions/v1"
)
func tableGcpCloudfunctionFunction(ctx context.Context) *plugin.Table {
return &plugin.Table{
Name: "gcp_cloudfunctions_function",
Description: "GCP Cloud Function",
Get: &plugin.GetConfig{
KeyColumns: plugin.AllColumns([]string{"name", "location"}),
Hydrate: getCloudFunction,
},
List: &plugin.ListConfig{
Hydrate: listCloudFunctions,
},
Columns: []*plugin.Column{
{
Name: "name",
Description: "The name of the function.",
Type: proto.ColumnType_STRING,
Transform: transform.FromCamel().Transform(lastPathElement),
},
{
Name: "status",
Description: "Status of the function deployment (ACTIVE, OFFLINE, CLOUD_FUNCTION_STATUS_UNSPECIFIED,DEPLOY_IN_PROGRESS, DELETE_IN_PROGRESS, UNKNOWN).",
Type: proto.ColumnType_STRING,
},
{
Name: "self_link",
Description: "Server-defined URL for the resource.",
Type: proto.ColumnType_STRING,
Transform: transform.From(cloudFunctionSelfLink),
},
{
Name: "description",
Description: "User-provided description of a function.",
Type: proto.ColumnType_STRING,
},
{
Name: "runtime",
Description: "The runtime in which to run the function.",
Type: proto.ColumnType_STRING,
},
// other columns
{
Name: "available_memory_mb",
Description: "The amount of memory in MB available for the function.",
Type: proto.ColumnType_INT,
},
{
Name: "build_environment_variables",
Description: "Environment variables that shall be available during build time",
Type: proto.ColumnType_JSON,
},
{
Name: "build_id",
Description: "The Cloud Build ID of the latest successful deployment of the function.",
Type: proto.ColumnType_STRING,
},
{
Name: "entry_point",
Description: "The name of the function (as defined in source code) that will be executed.",
Type: proto.ColumnType_STRING,
},
{
Name: "environment_variables",
Description: "Environment variables that shall be available during function execution.",
Type: proto.ColumnType_JSON,
},
{
Name: "event_trigger",
Description: "A source that fires events in response to a condition in another service.",
Type: proto.ColumnType_JSON,
},
{
Name: "https_trigger",
Description: "An HTTPS endpoint type of source that can be triggered via URL.",
Type: proto.ColumnType_JSON,
},
{
Name: "iam_policy",
Description: "The IAM policy for the function.", Transform: transform.FromValue(), Hydrate: getGcpCloudFunctionIamPolicy,
Type: proto.ColumnType_JSON,
},
{
Name: "ingress_settings",
Description: "The ingress settings for the function, controlling what traffic can reach it (INGRESS_SETTINGS_UNSPECIFIED, ALLOW_ALL, ALLOW_INTERNAL_ONLY, ALLOW_INTERNAL_AND_GCLB).",
Type: proto.ColumnType_STRING,
},
{
Name: "labels",
Description: "Labels that apply to this function.",
Type: proto.ColumnType_JSON,
},
{
Name: "max_instances",
Description: "The limit on the maximum number of function instances that may coexist at a given time. In some cases, such as rapid traffic surges, Cloud Functions may, for a short period of time, create more instances than the specified max instances limit.",
Type: proto.ColumnType_INT,
},
{
Name: "network",
Description: "The VPC Network that this cloud function can connect to.",
Type: proto.ColumnType_STRING,
},
{
Name: "service_account_email",
Description: "The email of the function's service account.",
Type: proto.ColumnType_STRING,
},
{
Name: "source_archive_url",
Description: "The Google Cloud Storage URL, starting with gs://, pointing to the zip archive which contains the function.",
Type: proto.ColumnType_STRING,
},
{
Name: "source_repository",
Description: "**Beta Feature** The source repository where a function is hosted.",
Type: proto.ColumnType_STRING,
},
{
Name: "source_upload_url",
Description: "The Google Cloud Storage signed URL used for source uploading, generated by google.cloud.functions.v1.GenerateUploadUrl",
Type: proto.ColumnType_STRING,
},
{
Name: "timeout",
Description: "The function execution timeout. Execution is consideredfailed and can be terminated if the function is not completed at the end of the timeout period. Defaults to 60 seconds.",
Type: proto.ColumnType_STRING,
},
{
Name: "update_time",
Description: "The last update timestamp of the Cloud Function.",
Type: proto.ColumnType_TIMESTAMP,
},
{
Name: "version_id",
Description: "The version identifier of the Cloud Function. Each deployment attempt results in a new version of a function being created.",
Type: proto.ColumnType_INT,
},
{
Name: "vpc_connector",
Description: "The VPC Network Connector that this cloud function can connect to. This field is mutually exclusive with `network` field and will eventually replace it.",
Type: proto.ColumnType_STRING,
},
{
Name: "vpc_connector_egress_settings",
Description: "The egress settings for the connector, controlling what traffic is diverted through it (VPC_CONNECTOR_EGRESS_SETTINGS_UNSPECIFIED, PRIVATE_RANGES_ONLY, ALL_TRAFFIC).",
Type: proto.ColumnType_STRING,
},
// standard steampipe columns
{
Name: "title",
Description: ColumnDescriptionTitle,
Type: proto.ColumnType_STRING,
Transform: transform.FromField("Name"),
},
{
Name: "tags",
Description: ColumnDescriptionTags,
Type: proto.ColumnType_JSON,
Transform: transform.FromField("Labels"),
},
{
Name: "akas",
Description: ColumnDescriptionAkas,
Type: proto.ColumnType_JSON,
Transform: transform.FromP(gcpCloudFunctionTurbotData, "Akas"),
},
// standard gcp columns
{
Name: "location",
Description: ColumnDescriptionLocation,
Type: proto.ColumnType_STRING,
Transform: transform.FromField("Name").Transform(locationFromFunctionName),
},
{
Name: "project",
Description: ColumnDescriptionProject,
Type: proto.ColumnType_STRING,
Transform: transform.FromP(gcpCloudFunctionTurbotData, "Project"),
},
},
}
}
//// HYDRATE FUNCTIONS
func listCloudFunctions(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
logger := plugin.Logger(ctx)
logger.Trace("listCloudFunctions")
// Create Service Connection
service, err := CloudFunctionsService(ctx, d)
if err != nil {
return nil, err
}
// Max limit isn't mentioned in the documentation
// Default limit is set as 1000
pageSize := types.Int64(1000)
limit := d.QueryContext.Limit
if d.QueryContext.Limit != nil {
if *limit < *pageSize {
pageSize = limit
}
}
// Get project details
getProjectCached := plugin.HydrateFunc(getProject).WithCache()
projectId, err := getProjectCached(ctx, d, h)
if err != nil {
return nil, err
}
project := projectId.(string)
data := "projects/" + project + "/locations/-" // '-' for all locations...
resp := service.Projects.Locations.Functions.List(data).PageSize(*pageSize)
if err := resp.Pages(
ctx,
func(page *cloudfunctions.ListFunctionsResponse) error {
for _, item := range page.Functions {
d.StreamListItem(ctx, item)
// Check if context has been cancelled or if the limit has been hit (if specified)
// if there is a limit, it will return the number of rows required to reach this limit
if d.RowsRemaining(ctx) == 0 {
page.NextPageToken = ""
return nil
}
}
return nil
},
); err != nil {
return nil, err
}
return nil, nil
}
func getCloudFunction(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
logger := plugin.Logger(ctx)
logger.Trace("GetCloudFunction")
// Create Service Connection
service, err := CloudFunctionsService(ctx, d)
if err != nil {
return nil, err
}
// Get project details
getProjectCached := plugin.HydrateFunc(getProject).WithCache()
projectId, err := getProjectCached(ctx, d, h)
if err != nil {
return nil, err
}
project := projectId.(string)
name := d.EqualsQuals["name"].GetStringValue()
location := d.EqualsQuals["location"].GetStringValue()
// API https://cloud.google.com/functions/docs/reference/rest/v1/projects.locations.functions/get
cloudFunction, err := service.Projects.Locations.Functions.Get("projects/" + project + "/locations/" + location + "/functions/" + name).Do()
if err != nil {
return nil, err
}
return cloudFunction, nil
}
func getGcpCloudFunctionIamPolicy(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
logger := plugin.Logger(ctx)
logger.Trace("getGcpCloudFunctionIamPolicy")
// Create Service Connection
service, err := CloudFunctionsService(ctx, d)
if err != nil {
return nil, err
}
function := h.Item.(*cloudfunctions.CloudFunction)
resp, err := service.Projects.Locations.Functions.GetIamPolicy(function.Name).Do()
if err != nil {
return nil, err
}
if resp != nil {
return resp, nil
}
return cloudfunctions.Policy{}, nil
}
//// TRANSFORM FUNCTIONS
func gcpCloudFunctionTurbotData(_ context.Context, d *transform.TransformData) (interface{}, error) {
function := d.HydrateItem.(*cloudfunctions.CloudFunction)
param := d.Param.(string)
project := strings.Split(function.Name, "/")[1]
turbotData := map[string]interface{}{
"Project": project,
"Akas": []string{"gcp://cloudfunctions.googleapis.com/" + function.Name},
}
return turbotData[param], nil
}
func locationFromFunctionName(_ context.Context, d *transform.TransformData) (interface{}, error) {
functionName := types.SafeString(d.Value)
parts := strings.Split(functionName, "/")
if len(parts) != 6 {
return nil, fmt.Errorf("transform locationFromFunctionName failed - unexpected name format: %s", functionName)
}
return parts[3], nil
}
func cloudFunctionSelfLink(_ context.Context, d *transform.TransformData) (interface{}, error) {
data := d.HydrateItem.(*cloudfunctions.CloudFunction)
selfLink := "https://cloudfunctions.googleapis.com/v1/" + data.Name
return selfLink, nil
}