| // 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) | |
| } |