Files
node-problem-detector/test/e2e/lib/gce/instance.go
2019-12-06 14:49:17 -08:00

181 lines
5.2 KiB
Go

/*
Copyright 2018 The Kubernetes Authors.
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
http://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 gce
import (
"fmt"
"os/exec"
"time"
"k8s.io/node-problem-detector/test/e2e/lib/ssh"
. "github.com/onsi/gomega"
compute "google.golang.org/api/compute/v1"
)
// Instance represents a GCE instance.
type Instance struct {
Name string
Zone string
Project string
MachineType string
ExternalIP string
SshKey string
SshUser string
ComputeService *compute.Service
}
// CreateInstance creates a GCE instance with provided spec.
func CreateInstance(instance Instance, imageName string, imageProject string) (Instance, error) {
if instance.MachineType == "" {
instance.MachineType = "n1-standard-1"
}
p, err := instance.ComputeService.Projects.Get(instance.Project).Do()
if err != nil {
return instance, fmt.Errorf("failed to get project info %q: %v", instance.Project, err)
}
i := &compute.Instance{
Name: instance.Name,
MachineType: fmt.Sprintf("zones/%s/machineTypes/%s", instance.Zone, instance.MachineType),
NetworkInterfaces: []*compute.NetworkInterface{
{
AccessConfigs: []*compute.AccessConfig{
{
Type: "ONE_TO_ONE_NAT",
Name: "External NAT",
},
}},
},
Disks: []*compute.AttachedDisk{
{
AutoDelete: true,
Boot: true,
Type: "PERSISTENT",
InitializeParams: &compute.AttachedDiskInitializeParams{
SourceImage: fmt.Sprintf("projects/%s/global/images/%s", imageProject, imageName),
DiskSizeGb: 20,
},
},
},
ServiceAccounts: []*compute.ServiceAccount{
{
Email: p.DefaultServiceAccount,
Scopes: []string{
"https://www.googleapis.com/auth/cloud-platform",
},
},
},
}
if _, err := instance.ComputeService.Instances.Get(instance.Project, instance.Zone, instance.Name).Do(); err != nil {
op, err := instance.ComputeService.Instances.Insert(instance.Project, instance.Zone, i).Do()
if err != nil {
ret := fmt.Sprintf("could not create instance %s: API error: %v", instance.Name, err)
if op != nil {
ret = fmt.Sprintf("%s: %v", ret, op.Error)
}
return instance, fmt.Errorf(ret)
} else if op.Error != nil {
return instance, fmt.Errorf("could not create instance %s: %+v", instance.Name, op.Error)
}
}
instanceRunning := false
// Waiting for the instance to be SSH-able by retrying SSH with timeout of 5 min (15*20sec)
for i := 0; i < 15 && !instanceRunning; i++ {
if i > 0 {
time.Sleep(time.Second * 20)
}
if instance.ExternalIP == "" {
instance.populateExternalIP()
}
result := ssh.Run("pwd", instance.ExternalIP, instance.SshUser, instance.SshKey)
if result.SSHError != nil {
err = fmt.Errorf("SSH to instance %s failed: %s", instance.Name, result.SSHError)
continue
}
instanceRunning = true
}
if !instanceRunning {
return instance, err
}
return instance, nil
}
func (ins *Instance) populateExternalIP() {
gceInstance, err := ins.ComputeService.Instances.Get(ins.Project, ins.Zone, ins.Name).Do()
if err != nil {
return
}
for i := range gceInstance.NetworkInterfaces {
ni := gceInstance.NetworkInterfaces[i]
for j := range ni.AccessConfigs {
ac := ni.AccessConfigs[j]
if len(ac.NatIP) > 0 {
ins.ExternalIP = ac.NatIP
return
}
}
}
}
// RunCommand runs a command on the GCE instance and returns the command result.
func (ins *Instance) RunCommand(cmd string) ssh.Result {
if ins.ExternalIP == "" {
ins.populateExternalIP()
}
return ssh.Run(cmd, ins.ExternalIP, ins.SshUser, ins.SshKey)
}
// RunCommand runs a command on the GCE instance and returns the command result, and fails the test when the command failed.
func (ins *Instance) RunCommandOrFail(cmd string) ssh.Result {
result := ins.RunCommand(cmd)
Expect(result.SSHError).ToNot(HaveOccurred(), "SSH-ing to the instance failed: %v\n", result)
Expect(result.Code).To(Equal(0), "Running command failed: %v\n", result)
return result
}
// PushFile pushes a local file to a GCE instance.
func (ins *Instance) PushFile(srcPath, destPath string) error {
if ins.ExternalIP == "" {
ins.populateExternalIP()
}
output, err := exec.Command("scp", "-o", "StrictHostKeyChecking no",
"-i", ins.SshKey,
srcPath, fmt.Sprintf("%s@%s:%s", ins.SshUser, ins.ExternalIP, destPath)).CombinedOutput()
if err != nil {
return fmt.Errorf("Error running scp: %v.\nHere is the output for the command: %v", err, string(output))
}
return nil
}
// DeleteInstance deletes a GCE instance.
func (ins *Instance) DeleteInstance() error {
if _, err := ins.ComputeService.Instances.Get(ins.Project, ins.Zone, ins.Name).Do(); err != nil {
return err
}
if _, err := ins.ComputeService.Instances.Delete(ins.Project, ins.Zone, ins.Name).Do(); err != nil {
return err
}
return nil
}