diff --git a/config.go b/config.go index 16182907e..65d4a63fe 100644 --- a/config.go +++ b/config.go @@ -26,7 +26,10 @@ type Moby struct { Contents string } Outputs []struct { - Format string + Format string + Project string + Bucket string + Public bool } } diff --git a/gcp.go b/gcp.go new file mode 100644 index 000000000..ae93fffd2 --- /dev/null +++ b/gcp.go @@ -0,0 +1,82 @@ +package main + +import ( + "errors" + "fmt" + "io" + "os" + "os/exec" + + "cloud.google.com/go/storage" + "golang.org/x/net/context" +) + +func uploadGS(filename, project, bucket string, public bool) error { + if project != "" { + err := os.Setenv("GOOGLE_CLOUD_PROJECT", project) + if err != nil { + return err + } + } + if os.Getenv("GOOGLE_CLOUD_PROJECT") == "" { + return errors.New("GOOGLE_CLOUD_PROJECT environment variable must be set or project specified in config") + } + + ctx := context.Background() + client, err := storage.NewClient(ctx) + if err != nil { + return err + } + + f, err := os.Open(filename) + if err != nil { + return err + } + defer f.Close() + + wc := client.Bucket(bucket).Object(filename).NewWriter(ctx) + _, err = io.Copy(wc, f) + if err != nil { + return err + } + err = wc.Close() + if err != nil { + return err + } + + // TODO make public if requested + + fmt.Println("gs://" + bucket + "/" + filename) + + return nil +} + +func imageGS(filename, project, storage string) error { + if project != "" { + err := os.Setenv("GOOGLE_CLOUD_PROJECT", project) + if err != nil { + return err + } + } + if os.Getenv("GOOGLE_CLOUD_PROJECT") == "" { + return errors.New("GOOGLE_CLOUD_PROJECT environment variable must be set or project specified in config") + } + + // TODO do not shell out to gcloud tool, use the API + + gcloud, err := exec.LookPath("gcloud") + if err != nil { + return errors.New("Please install the gcloud binary") + } + args := []string{"compute", "images", "create", "--source-uri", storage, filename} + cmd := exec.Command(gcloud, args...) + + out, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("Image creation failed: %v - %s", err, string(out)) + } + + fmt.Println(filename) + + return nil +} diff --git a/output.go b/output.go index 719605fca..7f736f009 100644 --- a/output.go +++ b/output.go @@ -39,6 +39,34 @@ func outputs(m *Moby, base string, bzimage []byte, initrd []byte) error { if err != nil { return fmt.Errorf("Error writing %s output: %v", o.Format, err) } + case "gce-storage": + err := outputImg(gce, base+".img.tar.gz", bzimage, initrd, m.Kernel.Cmdline) + if err != nil { + return fmt.Errorf("Error writing %s output: %v", o.Format, err) + } + if o.Bucket == "" { + return fmt.Errorf("No bucket specified for GCE output") + } + err = uploadGS(base+".img.tar.gz", o.Project, o.Bucket, o.Public) + if err != nil { + return fmt.Errorf("Error copying to Google Storage: %v", err) + } + case "gce": + err := outputImg(gce, base+".img.tar.gz", bzimage, initrd, m.Kernel.Cmdline) + if err != nil { + return fmt.Errorf("Error writing %s output: %v", o.Format, err) + } + if o.Bucket == "" { + return fmt.Errorf("No bucket specified for GCE output") + } + err = uploadGS(base+".img.tar.gz", o.Project, o.Bucket, o.Public) + if err != nil { + return fmt.Errorf("Error copying to Google Storage: %v", err) + } + err = imageGS(base, o.Project, "https://storage.googleapis.com/"+o.Bucket+"/"+base+".img.tar.gz") + if err != nil { + return fmt.Errorf("Error creating Google Compute Image: %v", err) + } case "qcow", "qcow2": err := outputImg(qcow, base+".qcow2", bzimage, initrd, m.Kernel.Cmdline) if err != nil {