/
check_command.go
132 lines (109 loc) · 3.24 KB
/
check_command.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
package check
import (
"context"
"fmt"
"sort"
"strings"
"time"
gceimgresource "github.com/GoogleCloudPlatform/guest-test-infra/container_images/gce-img-resource"
"google.golang.org/api/compute/v1"
)
/*
{
"source": {
"project": "some-project",
"family": "some-family",
"regexp": "rhel-8-v([0-9]+).*",
"readyOnly": true,
},
"version": { "name": "rhel-8-v20220322" }
}
*/
// Request is the input of a resource check.
type Request struct {
Source gceimgresource.Source `json:"source"`
Version gceimgresource.Version `json:"version"`
}
// Response is the output of a resource check.
type Response []gceimgresource.Version
// Run performs a check for image versions.
func Run(request Request) (Response, error) {
ctx := context.Background()
computeService, err := compute.NewService(ctx)
if err != nil {
return Response{}, err
}
call := computeService.Images.List(request.Source.Project)
var filter []string
if request.Source.ReadyOnly {
filter = append(filter, "(status = READY)")
}
if request.Source.Family != "" {
filter = append(filter, fmt.Sprintf("(family = %s)", request.Source.Family))
}
if len(filter) > 0 {
call = call.Filter(strings.Join(filter, " "))
}
var images []*compute.Image
var token string
for il, err := call.PageToken(token).Do(); ; il, err = call.PageToken(token).Do() {
if err != nil {
return Response{}, err
}
images = append(images, il.Items...)
if il.NextPageToken == "" {
break
}
token = il.NextPageToken
}
// "By default, results are returned in alphanumerical order based on the resource name."
// - https://cloud.google.com/compute/docs/reference/rest/v1/images/list
// "[the] check script...must print the array of new versions, in chronological order (oldest first)"
// - https://concourse-ci.org/implementing-resource-types.html
sort.Slice(images, func(i, j int) bool {
// image.CreationTimestamp is a string in rfc3339 format.
itime, _ := time.Parse(time.RFC3339, images[i].CreationTimestamp)
jtime, _ := time.Parse(time.RFC3339, images[j].CreationTimestamp)
return itime.Unix() < jtime.Unix()
})
// No version specified, return only the latest image.
if request.Version.Name == "" && len(images) > 0 {
image := images[len(images)-1]
version, err := mkVersion(image.Name, image.CreationTimestamp)
if err != nil {
return Response{}, err
}
return Response{version}, nil
}
// Requested version must at least be included in the response.
response := Response{request.Version}
var start bool
for _, image := range images {
if image.Name == request.Version.Name {
// Start appending from the image after the matching version, aka 'newer'.
start = true
continue
}
if image.Deprecated != nil && image.Deprecated.State == "DEPRECATED" {
continue
}
if start {
version, err := mkVersion(image.Name, image.CreationTimestamp)
if err != nil {
return Response{}, err
}
response = append(response, version)
}
}
return response, nil
}
func mkVersion(name, timestring string) (gceimgresource.Version, error) {
creationTime, err := time.Parse(time.RFC3339, timestring)
if err != nil {
return gceimgresource.Version{}, err
}
return gceimgresource.Version{
Name: name,
Version: fmt.Sprintf("%d", creationTime.Unix()),
}, nil
}