[botanist][build] Move Image out of botanist

-Image is moved in to the build package, where it more naturally would
live. This also sidesteps a possible cyclic dependency with a desired
qemu package refactoring.
-A minor abstraction of `Images` is introduced, []Image with a Get()
helper, as this cleans things up a bit.

Change-Id: I5f1795c7b4b247218df40f6ab5c8c93b59772792
diff --git a/botanist/boot.go b/botanist/boot.go
index 50ecfda..3ba8676 100644
--- a/botanist/boot.go
+++ b/botanist/boot.go
@@ -16,6 +16,7 @@
 	"sort"
 	"time"
 
+	"fuchsia.googlesource.com/tools/build"
 	"fuchsia.googlesource.com/tools/netboot"
 	"fuchsia.googlesource.com/tools/retry"
 	"fuchsia.googlesource.com/tools/tftp"
@@ -76,13 +77,13 @@
 // Boot prepares and boots a device at the given IP address. Depending on bootMode, the
 // device will either be paved or netbooted with the provided images, command-line
 // arguments and a public SSH user key.
-func Boot(ctx context.Context, addr *net.UDPAddr, bootMode int, imgs []Image, cmdlineArgs []string, authorizedKey []byte) error {
-	var bootArgs func(Image) []string
+func Boot(ctx context.Context, addr *net.UDPAddr, bootMode int, imgs []build.Image, cmdlineArgs []string, authorizedKey []byte) error {
+	var bootArgs func(build.Image) []string
 	switch bootMode {
 	case ModePave:
-		bootArgs = func(img Image) []string { return img.PaveArgs }
+		bootArgs = func(img build.Image) []string { return img.PaveArgs }
 	case ModeNetboot:
-		bootArgs = func(img Image) []string { return img.NetbootArgs }
+		bootArgs = func(img build.Image) []string { return img.NetbootArgs }
 	default:
 		return fmt.Errorf("invalid boot mode: %d", bootMode)
 	}
diff --git a/botanist/images.go b/build/images.go
similarity index 80%
rename from botanist/images.go
rename to build/images.go
index ae2560d..94b55b5 100644
--- a/botanist/images.go
+++ b/build/images.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package botanist
+package build
 
 import (
 	"encoding/json"
@@ -34,10 +34,12 @@
 	NetbootArgs []string `json:"bootserver_netboot"`
 }
 
-// GetImage is a convenience function that returns the first image in a list with the
-// given name, or nil if no such image exists.
-func GetImage(images []Image, name string) *Image {
-	for _, img := range images {
+// Images is list of images produced by the build, as may be read in from an image manifest.
+type Images []Image
+
+// Get returns the first image in a list with the given name, or nil if no such image exists.
+func (imgs Images) Get(name string) *Image {
+	for _, img := range imgs {
 		if img.Name == name {
 			return &img
 		}
@@ -46,14 +48,14 @@
 }
 
 // LoadImages reads in the entries indexed in the given image manifests.
-func LoadImages(imageManifests ...string) ([]Image, error) {
-	decodeImages := func(manifest string) ([]Image, error) {
+func LoadImages(imageManifests ...string) (Images, error) {
+	decodeImages := func(manifest string) (Images, error) {
 		f, err := os.Open(manifest)
 		if err != nil {
 			return nil, fmt.Errorf("failed to open %s: %v", manifest, err)
 		}
 		defer f.Close()
-		imgs := []Image{}
+		var imgs Images
 		if err := json.NewDecoder(f).Decode(&imgs); err != nil {
 			return nil, fmt.Errorf("failed to decode %s: %v", manifest, err)
 		}
@@ -67,7 +69,7 @@
 		return imgs, nil
 	}
 
-	imgs := []Image{}
+	var imgs Images
 	for _, manifest := range imageManifests {
 		decoded, err := decodeImages(manifest)
 		if err != nil {
diff --git a/cmd/botanist/qemu.go b/cmd/botanist/qemu.go
index c97360f..2956864 100644
--- a/cmd/botanist/qemu.go
+++ b/cmd/botanist/qemu.go
@@ -14,7 +14,7 @@
 	"os/exec"
 	"path/filepath"
 
-	"fuchsia.googlesource.com/tools/botanist"
+	"fuchsia.googlesource.com/tools/build"
 	"fuchsia.googlesource.com/tools/qemu"
 	"fuchsia.googlesource.com/tools/secrets"
 	"github.com/google/subcommands"
@@ -78,7 +78,7 @@
 		return fmt.Errorf("-qemu-dir must be set")
 	}
 
-	imgs, err := botanist.LoadImages(cmd.imageManifest)
+	imgs, err := build.LoadImages(cmd.imageManifest)
 	if err != nil {
 		return err
 	}
diff --git a/cmd/botanist/run.go b/cmd/botanist/run.go
index 1d01591..ba6ffe1 100644
--- a/cmd/botanist/run.go
+++ b/cmd/botanist/run.go
@@ -17,6 +17,7 @@
 	"time"
 
 	"fuchsia.googlesource.com/tools/botanist"
+	"fuchsia.googlesource.com/tools/build"
 	"fuchsia.googlesource.com/tools/netboot"
 	"fuchsia.googlesource.com/tools/pdu"
 	"fuchsia.googlesource.com/tools/retry"
@@ -80,7 +81,7 @@
 	f.StringVar(&r.sshKey, "ssh", "", "file containing a private SSH user key; if not provided, a private key will be generated.")
 }
 
-func (r *RunCommand) runCmd(ctx context.Context, imgs []botanist.Image, nodename string, args []string, privKey []byte) error {
+func (r *RunCommand) runCmd(ctx context.Context, imgs build.Images, nodename string, args []string, privKey []byte) error {
 	// Find the node address UDP address.
 	n := netboot.NewClient(time.Second)
 	var addr *net.UDPAddr
@@ -186,7 +187,7 @@
 }
 
 func (r *RunCommand) execute(ctx context.Context, args []string) error {
-	imgs, err := botanist.LoadImages(r.imageManifests...)
+	imgs, err := build.LoadImages(r.imageManifests...)
 	if err != nil {
 		return fmt.Errorf("failed to load images: %v", err)
 	}
@@ -228,7 +229,7 @@
 	errs := make(chan error)
 	go func() {
 		if r.fastboot != "" {
-			zirconR := botanist.GetImage(imgs, "zircon-r")
+			zirconR := imgs.Get("zircon-r")
 			if zirconR == nil {
 				errs <- fmt.Errorf("zircon-r not provided")
 				return
diff --git a/cmd/botanist/zedboot.go b/cmd/botanist/zedboot.go
index f6588fd..b487b33 100644
--- a/cmd/botanist/zedboot.go
+++ b/cmd/botanist/zedboot.go
@@ -24,6 +24,7 @@
 	"time"
 
 	"fuchsia.googlesource.com/tools/botanist"
+	"fuchsia.googlesource.com/tools/build"
 	"fuchsia.googlesource.com/tools/netboot"
 	"fuchsia.googlesource.com/tools/pdu"
 	"fuchsia.googlesource.com/tools/retry"
@@ -207,7 +208,7 @@
 	return cmd.tarHostCmdArtifacts(summaryBuffer.Bytes(), stdoutBuf.Bytes(), tmpDir)
 }
 
-func (cmd *ZedbootCommand) runTests(ctx context.Context, imgs []botanist.Image, nodename string, cmdlineArgs []string) error {
+func (cmd *ZedbootCommand) runTests(ctx context.Context, imgs build.Images, nodename string, cmdlineArgs []string) error {
 	// Find the node address UDP address.
 	n := netboot.NewClient(time.Second)
 	var addr *net.UDPAddr
@@ -377,14 +378,14 @@
 	signals := make(chan os.Signal, 1)
 	signal.Notify(signals, syscall.SIGTERM)
 
-	imgs, err := botanist.LoadImages(cmd.imageManifests...)
+	imgs, err := build.LoadImages(cmd.imageManifests...)
 	if err != nil {
 		return err
 	}
 	errs := make(chan error)
 	go func() {
 		if cmd.fastboot != "" {
-			zirconR := botanist.GetImage(imgs, "zircon-r")
+			zirconR := imgs.Get("zircon-r")
 			if zirconR == nil {
 				errs <- fmt.Errorf("zircon-r not provided")
 				return
diff --git a/qemu/config.go b/qemu/config.go
index fb4d23f..a9583d8 100644
--- a/qemu/config.go
+++ b/qemu/config.go
@@ -13,7 +13,7 @@
 	"path/filepath"
 	"strings"
 
-	"fuchsia.googlesource.com/tools/botanist"
+	"fuchsia.googlesource.com/tools/build"
 )
 
 // Config gives a high-level configuration for QEMU on Fuchsia.
@@ -39,7 +39,7 @@
 
 // CreateInvocation creates a QEMU invocation given a particular configuration, a list of
 // images, and any specified command-line arguments.
-func CreateInvocation(cfg Config, imgs []botanist.Image, cmdlineArgs []string) ([]string, error) {
+func CreateInvocation(cfg Config, imgs build.Images, cmdlineArgs []string) ([]string, error) {
 	if _, err := os.Stat(cfg.QEMUBin); err != nil {
 		return nil, fmt.Errorf("QEMU binary not found: %v", err)
 	}
@@ -107,18 +107,18 @@
 		addArgs("-device", fmt.Sprintf("virtio-blk-pci,drive=testdisk,addr=%s", cfg.PCIAddr))
 	}
 
-	qemuKernel := botanist.GetImage(imgs, "qemu-kernel")
+	qemuKernel := imgs.Get("qemu-kernel")
 	if qemuKernel == nil {
 		return nil, fmt.Errorf("could not find qemu-kernel")
 	}
-	zirconA := botanist.GetImage(imgs, "zircon-a")
+	zirconA := imgs.Get("zircon-a")
 	if zirconA == nil {
 		return nil, fmt.Errorf("could not find zircon-a")
 	}
 	addArgs("-kernel", qemuKernel.Path)
 	addArgs("-initrd", zirconA.Path)
 
-	if storageFull := botanist.GetImage(imgs, "storage-full"); storageFull != nil {
+	if storageFull := imgs.Get("storage-full"); storageFull != nil {
 		addArgs("-drive", fmt.Sprintf("file=%s,format=raw,if=none,id=maindisk", storageFull.Path))
 		addArgs("-device", "virtio-blk-pci,drive=maindisk")
 	}