func (i *GCEImage) String() string {
	return "GCEImage[" + i.name + "]"
}
String returns a string representation of the image
package imagebuilder
import (
	"fmt"
	"time"
	"golang.org/x/crypto/ssh"
	compute "google.golang.org/api/compute/v1"
	"google.golang.org/api/googleapi"
	"k8s.io/klog"
	"sigs.k8s.io/image-builder/images/kube-deploy/imagebuilder/pkg/imagebuilder/executor"
)
type GCEInstance struct {
	cloud    *GCECloud
	name     string
	instance *compute.Instance
}
func (i *GCEInstance) Shutdown() error {
	klog.Infof("Terminating instance %q", i.name)
	return i.cloud.deleteInstance(i.name)
}
func (i *GCEInstance) DialSSH(config *ssh.ClientConfig) (executor.Executor, error) {
	publicIP, err := i.WaitPublicIP()
	if err != nil {
		return nil, err
	}
	for {
		sshClient, err := ssh.Dial("tcp", publicIP+":22", config)
		if err != nil {
			klog.Warningf("error connecting to SSH on server %q: %v", publicIP, err)
			time.Sleep(5 * time.Second)
			continue
		}
		return executor.NewSSH(sshClient), nil
	}
}
func (i *GCEInstance) WaitPublicIP() (string, error) {
	for {
		instance, err := i.cloud.describeInstance(i.name)
		if err != nil {
			return "", err
		}
		for _, ni := range instance.NetworkInterfaces {
			for _, ac := range ni.AccessConfigs {
				if ac.NatIP != "" {
					klog.Infof("Instance public IP is %q", ac.NatIP)
					return ac.NatIP, nil
				}
			}
		}
		klog.V(2).Infof("Sleeping before requerying instance for public IP: %q", i.name)
		time.Sleep(5 * time.Second)
	}
}
type GCECloud struct {
	config *GCEConfig
	computeClient *compute.Service
}
var _ Cloud = &GCECloud{}
func NewGCECloud(computeClient *compute.Service, config *GCEConfig) *GCECloud {
	return &GCECloud{
		computeClient: computeClient,
		config:        config,
	}
}
func (a *GCECloud) GetExtraEnv() (map[string]string, error) {
	env := make(map[string]string)
	return env, nil
}
func IsGCENotFound(err error) bool {
	apiErr, ok := err.(*googleapi.Error)
	if !ok {
		return false
	}
	return apiErr.Code == 404
}
func (c *GCECloud) describeInstance(name string) (*compute.Instance, error) {
	klog.V(2).Infof("GCE Instances List Name=%q", name)
	instances, err := c.computeClient.Instances.List(c.config.Project, c.config.Zone).Filter("name eq " + name).Do()
	if err != nil {
		return nil, fmt.Errorf("error making GCE Instances List call: %v", err)
	}
	if len(instances.Items) == 0 {
		return nil, nil
	}
	if len(instances.Items) != 1 {
		return nil, fmt.Errorf("found multiple instances with name %q", name)
	}
	return instances.Items[0], nil
}
func (c *GCECloud) deleteInstance(name string) error {
	klog.V(2).Infof("GCE Delete Instances name=%q", name)
	_, err := c.computeClient.Instances.Delete(c.config.Project, c.config.Zone, name).Do()
	if err != nil {
		return fmt.Errorf("error terminating instance %q: %v", name, err)
	}
	return nil
}
func (c *GCECloud) GetInstance() (Instance, error) {
	name := c.config.MachineName
	instance, err := c.describeInstance(name)
	if err != nil {
		return nil, err
	}
	if instance != nil {
		klog.Infof("Found existing instance: %q", instance.Name)
		return &GCEInstance{
			cloud:    c,
			instance: instance,
			name:     instance.Name,
		}, nil
	}
	return nil, nil
}
func (c *GCECloud) CreateInstance() (Instance, error) {
	name := c.config.MachineName
	zone := c.config.Zone
	machineType := "zones/" + zone + "/machineTypes/" + c.config.MachineType
	klog.Infof("creating instance with machinetype %s", machineType)
	var disks []*compute.AttachedDisk
	disks = append(disks, &compute.AttachedDisk{
		InitializeParams: &compute.AttachedDiskInitializeParams{
			SourceImage: c.config.Image,
			DiskType:    "zones/" + zone + "/diskTypes/pd-ssd",
		},
		Boot:       true,
		DeviceName: "disk-0",
		Index:      0,
		AutoDelete: true,
		Mode:       "READ_WRITE",
		Type:       "PERSISTENT",
	})
	metadata := &compute.Metadata{}
	if c.config.SSHPublicKey != "" {
		publicKey, err := ReadFile(c.config.SSHPublicKey)
		if err != nil {
			return nil, err
		}
		sshKey := "admin:" + string(publicKey)
		metadata.Items = append(metadata.Items, &compute.MetadataItems{
			Key:   "ssh-keys",
			Value: &sshKey,
		})
	}
	scopes := []string{
		"https://www.googleapis.com/auth/devstorage.read_write",
		"https://www.googleapis.com/auth/compute",
	}
	instance := &compute.Instance{
		Name: name,
		NetworkInterfaces: []*compute.NetworkInterface{
			{
				AccessConfigs: []*compute.AccessConfig{
					{
						Name: "nat",
						Type: "ONE_TO_ONE_NAT",
					},
				},
			},
		},
		MachineType: machineType,
		Disks:       disks,
		Metadata:    metadata,
		ServiceAccounts: []*compute.ServiceAccount{
			{
				Email:  "default",
				Scopes: scopes,
			},
		},
	}
	_, err := c.computeClient.Instances.Insert(c.config.Project, c.config.Zone, instance).Do()
	if err != nil {
		return nil, fmt.Errorf("error running instance: %v", err)
	}
	return &GCEInstance{
		cloud: c,
		name:  name,
	}, nil
}
func (c *GCECloud) FindImage(imageName string) (Image, error) {
	image, err := findGCEImage(c.computeClient, c.config.Project, imageName)
	if err != nil {
		return nil, err
	}
	if image == nil {
		return nil, nil
	}
	return &GCEImage{
		computeClient: c.computeClient,
		name:          imageName,
	}, nil
}
func findGCEImage(computeClient *compute.Service, project string, imageName string) (*compute.Image, error) {
	images, err := computeClient.Images.List(project).Filter("name eq " + imageName).Do()
	if err != nil {
		return nil, fmt.Errorf("error listing images: %v", err)
	}
	klog.V(2).Infof("GCE Images List Filter:Name=%q", imageName)
	if len(images.Items) == 0 {
		return nil, nil
	}
	if len(images.Items) != 1 {
		return nil, fmt.Errorf("found multiple matching images for name: %q", imageName)
	}
	return images.Items[0], nil
}
type GCEImage struct {
	computeClient *compute.Service
	name          string
}
var _ Image = &GCEImage{}