mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-07-20 09:39:08 +00:00
Merge pull request #3790 from giggsoff/support-gcp-vtpm
Support for vTPM on GCP
This commit is contained in:
commit
1073b2dd8d
@ -8,6 +8,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/term"
|
"github.com/docker/docker/pkg/term"
|
||||||
@ -20,8 +21,13 @@ import (
|
|||||||
"google.golang.org/api/storage/v1"
|
"google.golang.org/api/storage/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
const pollingInterval = 500 * time.Millisecond
|
const (
|
||||||
const timeout = 300
|
pollingInterval = 500 * time.Millisecond
|
||||||
|
timeout = 300
|
||||||
|
|
||||||
|
uefiCompatibleFeature = "UEFI_COMPATIBLE"
|
||||||
|
vmxImageLicence = "projects/vm-options/global/licenses/enable-vmx"
|
||||||
|
)
|
||||||
|
|
||||||
// GCPClient contains state required for communication with GCP
|
// GCPClient contains state required for communication with GCP
|
||||||
type GCPClient struct {
|
type GCPClient struct {
|
||||||
@ -125,8 +131,8 @@ func (g GCPClient) UploadFile(src, dst, bucketName string, public bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateImage creates a GCP image using the a source from Google Storage
|
// CreateImage creates a GCP image using the source from Google Storage
|
||||||
func (g GCPClient) CreateImage(name, storageURL, family string, nested, replace bool) error {
|
func (g GCPClient) CreateImage(name, storageURL, family string, nested, uefi, replace bool) error {
|
||||||
if replace {
|
if replace {
|
||||||
if err := g.DeleteImage(name); err != nil {
|
if err := g.DeleteImage(name); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -146,7 +152,13 @@ func (g GCPClient) CreateImage(name, storageURL, family string, nested, replace
|
|||||||
}
|
}
|
||||||
|
|
||||||
if nested {
|
if nested {
|
||||||
imgObj.Licenses = []string{"projects/vm-options/global/licenses/enable-vmx"}
|
imgObj.Licenses = []string{vmxImageLicence}
|
||||||
|
}
|
||||||
|
|
||||||
|
if uefi {
|
||||||
|
imgObj.GuestOsFeatures = []*compute.GuestOsFeature{
|
||||||
|
{Type: uefiCompatibleFeature},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
op, err := g.compute.Images.Insert(g.projectName, imgObj).Do()
|
op, err := g.compute.Images.Insert(g.projectName, imgObj).Do()
|
||||||
@ -185,7 +197,7 @@ func (g GCPClient) DeleteImage(name string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateInstance creates and starts an instance on GCP
|
// CreateInstance creates and starts an instance on GCP
|
||||||
func (g GCPClient) CreateInstance(name, image, zone, machineType string, disks Disks, data *string, nested, replace bool) error {
|
func (g GCPClient) CreateInstance(name, image, zone, machineType string, disks Disks, data *string, nested, vtpm, replace bool) error {
|
||||||
if replace {
|
if replace {
|
||||||
if err := g.DeleteInstance(name, zone, true); err != nil {
|
if err := g.DeleteInstance(name, zone, true); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -204,6 +216,34 @@ func (g GCPClient) CreateInstance(name, image, zone, machineType string, disks D
|
|||||||
sshKey := new(string)
|
sshKey := new(string)
|
||||||
*sshKey = fmt.Sprintf("moby:%s moby", string(ssh.MarshalAuthorizedKey(k)))
|
*sshKey = fmt.Sprintf("moby:%s moby", string(ssh.MarshalAuthorizedKey(k)))
|
||||||
|
|
||||||
|
// check provided image to be compatible with provided options
|
||||||
|
op, err := g.compute.Images.Get(g.projectName, image).Do()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
uefiCompatible := false
|
||||||
|
for _, feature := range op.GuestOsFeatures {
|
||||||
|
if feature != nil && feature.Type == uefiCompatibleFeature {
|
||||||
|
uefiCompatible = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if vtpm && !uefiCompatible {
|
||||||
|
return fmt.Errorf("cannot use vTPM without UEFI_COMPATIBLE image")
|
||||||
|
}
|
||||||
|
// we should check for nested
|
||||||
|
vmxLicense := false
|
||||||
|
for _, license := range op.Licenses {
|
||||||
|
// we omit hostname and version when define license
|
||||||
|
if strings.HasSuffix(license, vmxImageLicence) {
|
||||||
|
vmxLicense = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if nested && !vmxLicense {
|
||||||
|
return fmt.Errorf("cannot use nested virtualization without enable-vmx image")
|
||||||
|
}
|
||||||
|
|
||||||
instanceDisks := []*compute.AttachedDisk{
|
instanceDisks := []*compute.AttachedDisk{
|
||||||
{
|
{
|
||||||
AutoDelete: true,
|
AutoDelete: true,
|
||||||
@ -227,7 +267,13 @@ func (g GCPClient) CreateInstance(name, image, zone, machineType string, disks D
|
|||||||
} else {
|
} else {
|
||||||
diskSizeGb = int64(convertMBtoGB(disk.Size))
|
diskSizeGb = int64(convertMBtoGB(disk.Size))
|
||||||
}
|
}
|
||||||
diskOp, err := g.compute.Disks.Insert(g.projectName, zone, &compute.Disk{Name: diskName, SizeGb: diskSizeGb}).Do()
|
diskObj := &compute.Disk{Name: diskName, SizeGb: diskSizeGb}
|
||||||
|
if vtpm {
|
||||||
|
diskObj.GuestOsFeatures = []*compute.GuestOsFeature{
|
||||||
|
{Type: uefiCompatibleFeature},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
diskOp, err := g.compute.Disks.Insert(g.projectName, zone, diskObj).Do()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -274,9 +320,11 @@ func (g GCPClient) CreateInstance(name, image, zone, machineType string, disks D
|
|||||||
}
|
}
|
||||||
|
|
||||||
if nested {
|
if nested {
|
||||||
// TODO(rn): We could/should check here if the image has nested virt enabled
|
|
||||||
instanceObj.MinCpuPlatform = "Intel Haswell"
|
instanceObj.MinCpuPlatform = "Intel Haswell"
|
||||||
}
|
}
|
||||||
|
if vtpm {
|
||||||
|
instanceObj.ShieldedInstanceConfig = &compute.ShieldedInstanceConfig{EnableVtpm: true}
|
||||||
|
}
|
||||||
|
|
||||||
// Don't wait for operation to complete!
|
// Don't wait for operation to complete!
|
||||||
// A headstart is needed as by the time we've polled for this event to be
|
// A headstart is needed as by the time we've polled for this event to be
|
||||||
|
@ -26,6 +26,7 @@ func pushGcp(args []string) {
|
|||||||
familyFlag := flags.String("family", "", "GCP Image Family. A group of images where the family name points to the most recent image. *Optional*")
|
familyFlag := flags.String("family", "", "GCP Image Family. A group of images where the family name points to the most recent image. *Optional*")
|
||||||
nameFlag := flags.String("img-name", "", "Overrides the name used to identify the file in Google Storage and the VM image. Defaults to the base of 'path' with the '.img.tar.gz' suffix removed")
|
nameFlag := flags.String("img-name", "", "Overrides the name used to identify the file in Google Storage and the VM image. Defaults to the base of 'path' with the '.img.tar.gz' suffix removed")
|
||||||
nestedVirt := flags.Bool("nested-virt", false, "Enabled nested virtualization for the image")
|
nestedVirt := flags.Bool("nested-virt", false, "Enabled nested virtualization for the image")
|
||||||
|
uefiCompatible := flags.Bool("uefi-compatible", false, "Enable UEFI_COMPATIBLE feature for the image, required to enable vTPM.")
|
||||||
|
|
||||||
if err := flags.Parse(args); err != nil {
|
if err := flags.Parse(args); err != nil {
|
||||||
log.Fatal("Unable to parse args")
|
log.Fatal("Unable to parse args")
|
||||||
@ -65,7 +66,7 @@ func pushGcp(args []string) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error copying to Google Storage: %v", err)
|
log.Fatalf("Error copying to Google Storage: %v", err)
|
||||||
}
|
}
|
||||||
err = client.CreateImage(name, "https://storage.googleapis.com/"+bucket+"/"+name+suffix, family, *nestedVirt, true)
|
err = client.CreateImage(name, "https://storage.googleapis.com/"+bucket+"/"+name+suffix, family, *nestedVirt, *uefiCompatible, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error creating Google Compute Image: %v", err)
|
log.Fatalf("Error creating Google Compute Image: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,7 @@ func runGcp(args []string) {
|
|||||||
|
|
||||||
skipCleanup := flags.Bool("skip-cleanup", false, "Don't remove images or VMs")
|
skipCleanup := flags.Bool("skip-cleanup", false, "Don't remove images or VMs")
|
||||||
nestedVirt := flags.Bool("nested-virt", false, "Enabled nested virtualization")
|
nestedVirt := flags.Bool("nested-virt", false, "Enabled nested virtualization")
|
||||||
|
vTPM := flags.Bool("vtpm", false, "Enable vTPM device")
|
||||||
|
|
||||||
data := flags.String("data", "", "String of metadata to pass to VM; error to specify both -data and -data-file")
|
data := flags.String("data", "", "String of metadata to pass to VM; error to specify both -data and -data-file")
|
||||||
dataPath := flags.String("data-file", "", "Path to file containing metadata to pass to VM; error to specify both -data and -data-file")
|
dataPath := flags.String("data-file", "", "Path to file containing metadata to pass to VM; error to specify both -data and -data-file")
|
||||||
@ -87,7 +88,7 @@ func runGcp(args []string) {
|
|||||||
log.Fatalf("Unable to connect to GCP: %v", err)
|
log.Fatalf("Unable to connect to GCP: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = client.CreateInstance(*name, image, zone, machine, disks, data, *nestedVirt, true); err != nil {
|
if err = client.CreateInstance(*name, image, zone, machine, disks, data, *nestedVirt, *vTPM, true); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user