forked from evergreen-ci/evergreen
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgce_client.go
146 lines (121 loc) · 4.12 KB
/
gce_client.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
// +build go1.7
package cloud
import (
"context"
"github.com/evergreen-ci/evergreen/model/host"
"github.com/mongodb/grip"
"github.com/mongodb/grip/message"
"github.com/pkg/errors"
"golang.org/x/oauth2"
"golang.org/x/oauth2/jwt"
compute "google.golang.org/api/compute/v1"
)
const (
computeScope = compute.ComputeScope
)
// The gceClient interface wraps the Google Compute gceClient interaction.
type gceClient interface {
Init(context.Context, *jwt.Config) error
CreateInstance(*host.Host, *GCESettings) (string, error)
GetInstance(*host.Host) (*compute.Instance, error)
DeleteInstance(*host.Host) error
}
type gceClientImpl struct {
InstancesService *compute.InstancesService
}
// Init establishes a connection to a Google Compute endpoint and creates a gceClient that
// can be used to manage instances.
func (c *gceClientImpl) Init(ctx context.Context, config *jwt.Config) error {
ts := config.TokenSource(ctx)
client := oauth2.NewClient(ctx, ts)
grip.Debug("created an OAuth HTTP client to Google services")
// Connect to Google Compute Engine.
service, err := compute.New(client)
if err != nil {
return errors.Wrap(err, "failed to connect to Google Compute Engine service")
}
grip.Debug("connected to Google Compute Engine service")
// Get a handle to a specific service for instance configuration.
c.InstancesService = compute.NewInstancesService(service)
return nil
}
// CreateInstance requests an instance to be provisioned.
//
// API calls to an instance refer to the instance by the user-provided name (which must be unique)
// and not the ID. If successful, CreateInstance returns the name of the provisioned instance.
func (c *gceClientImpl) CreateInstance(h *host.Host, s *GCESettings) (string, error) {
// Create instance options to spawn an instance
machineType := makeMachineType(h.Zone, s.MachineName, s.NumCPUs, s.MemoryMB)
instance := &compute.Instance{
Name: h.Id,
MachineType: machineType,
Labels: makeLabels(h),
Tags: &compute.Tags{Items: s.NetworkTags},
}
grip.Debug(message.Fields{
"message": "creating instance",
"host_id": h.Id,
"machine_type": machineType,
})
// Add the disk with the image URL
var imageURL string
if s.ImageFamily != "" {
imageURL = makeImageFromFamily(s.ImageFamily)
} else {
imageURL = makeImage(s.ImageName)
}
diskType := makeDiskType(h.Zone, s.DiskType)
instance.Disks = []*compute.AttachedDisk{&compute.AttachedDisk{
AutoDelete: true,
Boot: true,
DeviceName: instance.Name,
InitializeParams: &compute.AttachedDiskInitializeParams{
DiskSizeGb: s.DiskSizeGB,
DiskType: diskType,
SourceImage: imageURL,
},
}}
grip.Debug(message.Fields{
"message": "attaching boot disk",
"host_id": h.Id,
"disk_size": s.DiskSizeGB,
"disk_type": diskType,
"source_image": imageURL,
})
// Attach a network interface
instance.NetworkInterfaces = []*compute.NetworkInterface{&compute.NetworkInterface{
AccessConfigs: []*compute.AccessConfig{&compute.AccessConfig{}},
}}
// Add the ssh keys
keys := s.SSHKeys.String()
instance.Metadata = &compute.Metadata{
Items: []*compute.MetadataItems{
&compute.MetadataItems{Key: "ssh-keys", Value: &keys},
},
}
grip.Debug(message.Fields{
"message": "attaching metadata items",
"host_id": h.Id,
"ssh_keys": keys,
})
// Make the API call to insert the instance
if _, err := c.InstancesService.Insert(h.Project, h.Zone, instance).Do(); err != nil {
return "", errors.Wrap(err, "API call to insert instance failed")
}
return instance.Name, nil
}
// GetInstance requests details on a single instance.
func (c *gceClientImpl) GetInstance(h *host.Host) (*compute.Instance, error) {
instance, err := c.InstancesService.Get(h.Project, h.Zone, h.Id).Do()
if err != nil {
return nil, errors.Wrap(err, "API call to get instance failed")
}
return instance, nil
}
// DeleteInstance requests an instance previously provisioned to be removed.
func (c *gceClientImpl) DeleteInstance(h *host.Host) error {
if _, err := c.InstancesService.Delete(h.Project, h.Zone, h.Id).Do(); err != nil {
return errors.Wrap(err, "API call to delete instance failed")
}
return nil
}