blob: 00effeba26bddd3550874ce171c415f9ac8cf6dd [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package artifacts
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"golang.org/x/crypto/ssh"
"go.fuchsia.dev/fuchsia/src/sys/pkg/testing/host-target-testing/avb"
"go.fuchsia.dev/fuchsia/src/sys/pkg/testing/host-target-testing/packages"
"go.fuchsia.dev/fuchsia/src/sys/pkg/testing/host-target-testing/paver"
"go.fuchsia.dev/fuchsia/src/sys/pkg/testing/host-target-testing/util"
"go.fuchsia.dev/fuchsia/src/sys/pkg/testing/host-target-testing/zbi"
)
type Build interface {
// GetPackageRepository returns a Repository for this build.
GetPackageRepository(ctx context.Context) (*packages.Repository, error)
// GetPaverDir downloads and returns the directory containing the paver scripts.
GetPaverDir(ctx context.Context) (string, error)
// GetPaver downloads and returns a paver for the build.
GetPaver(ctx context.Context) (paver.Paver, error)
// GetSshPublicKey returns the SSH public key used by this build's paver.
GetSshPublicKey() ssh.PublicKey
// GetVbmetaPath downloads and returns a path to the zircon-a vbmeta image.
GetVbmetaPath(ctx context.Context) (string, error)
}
type ArchiveBuild struct {
id string
archive *Archive
dir string
packages *packages.Repository
buildArchiveDir string
sshPublicKey ssh.PublicKey
}
// GetPackageRepository returns a Repository for this build.
func (b *ArchiveBuild) GetPackageRepository(ctx context.Context) (*packages.Repository, error) {
if b.packages != nil {
return b.packages, nil
}
path, err := b.archive.download(ctx, b.dir, b.id, "packages.tar.gz")
if err != nil {
return nil, fmt.Errorf("failed to download packages.tar.gz: %w", err)
}
packagesDir := filepath.Join(b.dir, b.id, "packages")
if err := os.MkdirAll(packagesDir, 0755); err != nil {
return nil, err
}
p, err := packages.NewRepositoryFromTar(ctx, packagesDir, path)
if err != nil {
return nil, err
}
b.packages = p
return b.packages, nil
}
// GetBuildArchive downloads and extracts the build-artifacts.tgz from the
// build id `buildId`. Returns a path to the directory of the extracted files,
// or an error if it fails to download or extract.
func (b *ArchiveBuild) GetBuildArchive(ctx context.Context) (string, error) {
if b.buildArchiveDir != "" {
return b.buildArchiveDir, nil
}
path, err := b.archive.download(ctx, b.dir, b.id, "build-archive.tgz")
if err != nil {
return "", fmt.Errorf("failed to download build-archive.tar.gz: %w", err)
}
buildArchiveDir := filepath.Join(b.dir, b.id, "build-archive")
if err := os.MkdirAll(buildArchiveDir, 0755); err != nil {
return "", err
}
if err := util.Untar(ctx, buildArchiveDir, path); err != nil {
return "", fmt.Errorf("failed to extract packages: %w", err)
}
b.buildArchiveDir = buildArchiveDir
return b.buildArchiveDir, nil
}
func (b *ArchiveBuild) GetPaverDir(ctx context.Context) (string, error) {
return b.GetBuildArchive(ctx)
}
// GetPaver downloads and returns a paver for the build.
func (b *ArchiveBuild) GetPaver(ctx context.Context) (paver.Paver, error) {
buildArchiveDir, err := b.GetBuildArchive(ctx)
if err != nil {
return nil, err
}
paveScript := filepath.Join(buildArchiveDir, "pave.sh")
paveZedbootScript := filepath.Join(buildArchiveDir, "pave-zedboot.sh")
return paver.NewBuildPaver(paveZedbootScript, paveScript, paver.SSHPublicKey(b.sshPublicKey))
}
func (b *ArchiveBuild) GetSshPublicKey() ssh.PublicKey {
return b.sshPublicKey
}
func (b *ArchiveBuild) GetVbmetaPath(ctx context.Context) (string, error) {
buildArchiveDir, err := b.GetBuildArchive(ctx)
if err != nil {
return "", err
}
return filepath.Join(buildArchiveDir, "zircon-a.vbmeta"), nil
}
func (b *ArchiveBuild) Pave(ctx context.Context, deviceName string) error {
paver, err := b.GetPaver(ctx)
if err != nil {
return err
}
return paver.Pave(ctx, deviceName)
}
func (b *ArchiveBuild) String() string {
return b.id
}
type FuchsiaDirBuild struct {
dir string
sshPublicKey ssh.PublicKey
}
func NewFuchsiaDirBuild(dir string, publicKey ssh.PublicKey) *FuchsiaDirBuild {
return &FuchsiaDirBuild{dir: dir, sshPublicKey: publicKey}
}
func (b *FuchsiaDirBuild) String() string {
return b.dir
}
func (b *FuchsiaDirBuild) GetPackageRepository(ctx context.Context) (*packages.Repository, error) {
return packages.NewRepository(ctx, filepath.Join(b.dir, "amber-files"))
}
func (b *FuchsiaDirBuild) GetPaverDir(ctx context.Context) (string, error) {
return b.dir, nil
}
func (b *FuchsiaDirBuild) GetPaver(ctx context.Context) (paver.Paver, error) {
return paver.NewBuildPaver(
filepath.Join(b.dir, "pave-zedboot.sh"),
filepath.Join(b.dir, "pave.sh"),
paver.SSHPublicKey(b.sshPublicKey),
)
}
func (b *FuchsiaDirBuild) GetSshPublicKey() ssh.PublicKey {
return b.sshPublicKey
}
func (b *FuchsiaDirBuild) GetVbmetaPath(ctx context.Context) (string, error) {
imagesJSON := filepath.Join(b.dir, "images.json")
f, err := os.Open(imagesJSON)
if err != nil {
return "", fmt.Errorf("failed to open %q: %w", imagesJSON, err)
}
defer f.Close()
var items []struct {
Name string `json:"name"`
Path string `json:"path"`
Type string `json:"type"`
}
if err := json.NewDecoder(f).Decode(&items); err != nil {
return "", fmt.Errorf("failed to parse %q: %w", imagesJSON, err)
}
for _, item := range items {
if item.Name == "zircon-a" && item.Type == "vbmeta" {
return filepath.Join(b.dir, item.Path), nil
}
}
return "", fmt.Errorf("failed to file zircon-a vbmeta in %q", imagesJSON)
}
type OmahaBuild struct {
build Build
omahaUrl string
avbtool *avb.AVBTool
zbitool *zbi.ZBITool
}
func NewOmahaBuild(build Build, omahaUrl string, avbtool *avb.AVBTool, zbitool *zbi.ZBITool) *OmahaBuild {
return &OmahaBuild{build: build, omahaUrl: omahaUrl, avbtool: avbtool, zbitool: zbitool}
}
// GetPackageRepository returns a Repository for this build.
func (b *OmahaBuild) GetPackageRepository(ctx context.Context) (*packages.Repository, error) {
return b.build.GetPackageRepository(ctx)
}
func (b *OmahaBuild) GetPaverDir(ctx context.Context) (string, error) {
return b.build.GetPaverDir(ctx)
}
// GetPaver downloads and returns a paver for the build.
func (b *OmahaBuild) GetPaver(ctx context.Context) (paver.Paver, error) {
paverDir, err := b.GetPaverDir(ctx)
if err != nil {
return nil, err
}
// Create a ZBI with the omaha_url argument.
tempDir, err := ioutil.TempDir("", "")
if err != nil {
return nil, fmt.Errorf("failed to create temp directory: %w", err)
}
defer os.RemoveAll(tempDir)
// Create a ZBI with the omaha_url argument.
destZbiPath := path.Join(tempDir, "omaha_argument.zbi")
imageArguments := map[string]string{
"omaha_url": b.omahaUrl,
}
if err := b.zbitool.MakeImageArgsZbi(ctx, destZbiPath, imageArguments); err != nil {
return nil, fmt.Errorf("Failed to create ZBI: %w", err)
}
// Create a vbmeta that includes the ZBI we just created.
propFiles := map[string]string{
"zbi": destZbiPath,
}
destVbmetaPath := filepath.Join(paverDir, "zircon-a-omaha-test.vbmeta")
srcVbmetaPath, err := b.GetVbmetaPath(ctx)
if err != nil {
return nil, fmt.Errorf("failed to find zircon-a vbmeta: %w", err)
}
err = b.avbtool.MakeVBMetaImage(ctx, destVbmetaPath, srcVbmetaPath, propFiles)
if err != nil {
return nil, fmt.Errorf("failed to create vbmeta: %w", err)
}
paveScript := filepath.Join(paverDir, "pave.sh")
paveZedbootScript := filepath.Join(paverDir, "pave-zedboot.sh")
return paver.NewBuildPaver(
paveZedbootScript,
paveScript,
paver.SSHPublicKey(b.GetSshPublicKey()),
paver.OverrideVBMetaA(destVbmetaPath),
)
}
func (b *OmahaBuild) GetSshPublicKey() ssh.PublicKey {
return b.build.GetSshPublicKey()
}
func (b *OmahaBuild) GetVbmetaPath(ctx context.Context) (string, error) {
return b.build.GetVbmetaPath(ctx)
}