blob: a0c1792525abde8683d4276f3f8dff8717b5f8f7 [file] [log] [blame]
// Copyright 2018 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 cipd
import (
"bytes"
"encoding/hex"
"io/ioutil"
"os"
"path"
"reflect"
"sort"
"strings"
"testing"
"go.fuchsia.dev/jiri/jiritest/xtest"
)
const (
// Some random valid cipd version tags from infra/tools/cipd
cipdVersionForTestA = "git_revision:00e2d8b49a4e7505d1c71f19d15c9e7c5b9245a5"
cipdVersionForTestB = "git_revision:8fac632847b1ce0de3b57d16d0f2193625f4a4f0"
// package path and versions for ACL tests
cipdPkgPathA = "gn/gn/${platform}"
cipdPkgVersionA = "git_revision:bdb0fd02324b120cacde634a9235405061c8ea06"
cipdPkgPathB = "notexist/notexist"
cipdPkgVersionB = "git_revision:bdb0fd02324b120cacde634a9235405061c8ea06"
)
var (
// Digests generated by cipd selfupdate-roll ...
digestMapA = map[string]string{
"linux-amd64": "df37ffc2588e345a31ca790d773b6136fedbd2efbf9a34cb735dd34b6891c16c",
"linux-arm64": "650f2a045f8587062a16299a650aa24ba5c5c0652585a2d9bd56594369d5f99e",
"linux-armv6l": "61b657c860ddc39d3286ced073c843852b1dafc0222af0bdc22ad988b289d733",
"mac-amd64": "4d015791ed6f03f305cf6a5a673a447e5c47ff5fdb701f43f99fba9ca73e61f8",
}
digestMapB = map[string]string{
"linux-amd64": "bdc971fd2895c3771e0709d2a3ec5fcace69c59a3a9f9dc33ab76fbc2f777d40",
"linux-arm64": "e1d6aadc9bfc155e9088aa3de39b9d3311c7359f398f372b5ad1c308e25edfeb",
"linux-armv6l": "3ad97b47ecc1b358c8ebd1b0307087d354433d88f24bf8ece096fb05452837f9",
"mac-amd64": "167edadf7c7c019a40b9f7869a4c05b2d9834427dad68e295442ef9ebce88dba",
}
instanceIDMap = map[string]string{
"gn/gn/linux-amd64": "0uGjKAZkJXPZjtYktgEwHiNbwsut_qRsk7ZCGGxi82IC",
"gn/gn/mac-amd64": "rN2F641yR4Bj-H1q8OwC_RiqRpUYxy3hryzRfPER9wcC",
}
)
// TestFetchBinary tests fetchiBinary method by fetching a set of
// cipd binaries. This test requires network access
func TestFetchBinary(t *testing.T) {
fakex, cleanup := xtest.NewX(t)
defer cleanup()
tmpDir, err := ioutil.TempDir("", "jiri-test")
if err != nil {
t.Error("failed to create temp dir for testing")
}
defer os.RemoveAll(tmpDir)
tests := []struct {
version string
digest map[string]string
}{
{cipdVersionForTestA, digestMapA},
{cipdVersionForTestB, digestMapB},
}
for i, test := range tests {
for platform, digest := range test.digest {
cipdPath := path.Join(tmpDir, "cipd"+platform+test.version)
if err := fetchBinary(fakex, cipdPath, platform, test.version, digest); err != nil {
t.Errorf("test %d failed while retrieving cipd binary for platform %q on version %q with digest %q: %v", i, platform, test.version, digest, err)
}
}
}
}
func TestCipdVersion(t *testing.T) {
t.Parallel()
// Assume cipd version is always a git commit hash for now
versionStr := string(cipdVersion)
if len(versionStr) != len("git_revision:00e2d8b49a4e7505d1c71f19d15c9e7c5b9245a5") ||
!strings.HasPrefix(versionStr, "git_revision:") {
t.Errorf("unsupported cipd version tag: %q", versionStr)
}
versionHash := versionStr[len("git_revision:"):]
if _, err := hex.DecodeString(versionHash); err != nil {
t.Errorf("unsupported cipd version tag: %q", versionStr)
}
}
func TestFetchDigest(t *testing.T) {
t.Parallel()
tests := []string{
"linux-amd64",
"linux-arm64",
"linux-armv6l",
"mac-amd64",
}
for _, platform := range tests {
digest, _, err := fetchDigest(platform)
if err != nil {
t.Errorf("failed to retrieve cipd digest for platform %q due to error: %v", platform, err)
}
if _, err := hex.DecodeString(digest); err != nil {
t.Errorf("digest %q is not a valid hex string for platform %q", digest, platform)
}
}
}
func TestSelfUpdate(t *testing.T) {
fakex, cleanup := xtest.NewX(t)
defer cleanup()
tmpDir, err := ioutil.TempDir("", "jiri-test")
if err != nil {
t.Error("failed to create temp dir for testing")
}
defer os.RemoveAll(tmpDir)
// Bootstrap cipd to version A
cipdPath := path.Join(tmpDir, "cipd")
if err := fetchBinary(fakex, cipdPath, CipdPlatform.String(), cipdVersionForTestA, digestMapA[CipdPlatform.String()]); err != nil {
t.Errorf("failed to bootstrap cipd with version %q: %v", cipdVersionForTestA, err)
}
// Perform cipd self update to version B
if err := selfUpdate(cipdPath, cipdVersionForTestB); err != nil {
t.Errorf("failed to perform cipd self update: %v", err)
}
// Verify self updated cipd
cipdData, err := ioutil.ReadFile(cipdPath)
if err != nil {
t.Errorf("failed to read self-updated cipd binary: %v", err)
}
verified, err := verifyDigest(cipdData, digestMapB[CipdPlatform.String()])
if err != nil {
t.Errorf("digest failed verification for platform %q on version %q", CipdPlatform.String(), cipdVersionForTestB)
}
if !verified {
t.Errorf("self-updated cipd failed integrity test")
}
}
func TestBootsrap(t *testing.T) {
fakex, cleanup := xtest.NewX(t)
defer cleanup()
cipdPath, err := Bootstrap(fakex, fakex.CIPDPath())
if cipdPath == "" {
t.Errorf("bootstrap returned an empty path")
}
fileInfo, err := os.Stat(cipdPath)
if err != nil {
if os.IsNotExist(err) {
t.Errorf("bootstrap failed, cipd binary was not found at %q", cipdPath)
}
t.Errorf("bootstrap failed, could not access cipd binary at %q due to error %v", cipdPath, err)
}
if fileInfo.Mode()&0111 == 0 {
t.Errorf("bootstrap failed, cipd binary at %q is not executable", cipdBinary)
}
}
func TestEnsure(t *testing.T) {
fakex, cleanup := xtest.NewX(t)
defer cleanup()
cipdPath, err := Bootstrap(fakex, fakex.CIPDPath())
if err != nil {
t.Errorf("bootstrap failed due to error: %v", err)
}
defer os.Remove(cipdPath)
// Write test ensure file
testEnsureFile, err := ioutil.TempFile("", "test_jiri*.ensure")
if err != nil {
t.Errorf("failed to create test ensure file: %v", err)
}
defer testEnsureFile.Close()
defer os.Remove(testEnsureFile.Name())
_, err = testEnsureFile.Write([]byte(`
$ParanoidMode CheckPresence
# GN
gn/gn/${platform} git_revision:bdb0fd02324b120cacde634a9235405061c8ea06
`))
if err != nil {
t.Errorf("failed to write test ensure file: %v", err)
}
testEnsureFile.Sync()
tmpDir, err := ioutil.TempDir("", "jiri-test")
if err != nil {
t.Error("failed to creat temp dir for testing")
}
defer os.RemoveAll(tmpDir)
// Invoke Ensure on test ensure file
if err := Ensure(fakex, testEnsureFile.Name(), tmpDir, 30); err != nil {
t.Errorf("ensure failed due to error: %v", err)
}
// Check the existence downloaded package
gnPath := path.Join(tmpDir, "gn")
if _, err := os.Stat(gnPath); err != nil {
if os.IsNotExist(err) {
t.Errorf("fetched cipd package is not found at %q", gnPath)
}
t.Errorf("failed to execute os.Stat() on fetched cipd package due to error: %v", err)
}
}
func TestEnsureFileVerify(t *testing.T) {
fakex, cleanup := xtest.NewX(t)
defer cleanup()
cipdPath, err := Bootstrap(fakex, fakex.CIPDPath())
if err != nil {
t.Errorf("bootstrap failed due to error: %v", err)
}
defer os.Remove(cipdPath)
// Write test ensure file
testEnsureFile, err := ioutil.TempFile("", "test_jiri*.ensure")
if err != nil {
t.Errorf("failed to create test ensure file: %v", err)
}
defer testEnsureFile.Close()
defer os.Remove(testEnsureFile.Name())
_, err = testEnsureFile.Write([]byte(`
$ParanoidMode CheckPresence
$VerifiedPlatform linux-amd64
$VerifiedPlatform mac-amd64
# GN
gn/gn/${platform} git_revision:bdb0fd02324b120cacde634a9235405061c8ea06
`))
if err != nil {
t.Errorf("failed to write test ensure file: %v", err)
}
testEnsureFile.Sync()
if err := EnsureFileVerify(fakex, testEnsureFile.Name()); err != nil {
t.Error("ensure file failed verification")
}
}
func TestEnsureFileVerifyInvalid(t *testing.T) {
fakex, cleanup := xtest.NewX(t)
defer cleanup()
cipdPath, err := Bootstrap(fakex, fakex.CIPDPath())
if err != nil {
t.Errorf("bootstrap failed due to error: %v", err)
}
defer os.Remove(cipdPath)
// Write test ensure file
testEnsureFile, err := ioutil.TempFile("", "test_jiri*.ensure")
if err != nil {
t.Errorf("failed to create test ensure file: %v", err)
}
defer testEnsureFile.Close()
defer os.Remove(testEnsureFile.Name())
_, err = testEnsureFile.Write([]byte(`
$ParanoidMode CheckPresence
$VerifiedPlatform linux-amd64
$VerifiedPlatform mac-amd64
# GN
gn/gn/${platform} git_revision:not_a_real_version
`))
if err != nil {
t.Errorf("failed to write test ensure file: %v", err)
}
testEnsureFile.Sync()
if err := EnsureFileVerify(fakex, testEnsureFile.Name()); err == nil {
t.Error("ensure file passed verification, should have failed")
}
}
func TestCheckACL(t *testing.T) {
fakex, cleanup := xtest.NewX(t)
defer cleanup()
cipdPath, err := Bootstrap(fakex, fakex.CIPDPath())
if err != nil {
t.Errorf("bootstrap failed due to error: %v", err)
}
defer os.Remove(cipdPath)
pkgMap := make(map[string]bool)
pkgMap[cipdPkgPathA] = false
pkgMap[cipdPkgPathB] = false
if err := CheckPackageACL(fakex, pkgMap); err != nil {
t.Errorf("CheckPackageACL failed due to error: %v", err)
}
if !pkgMap[cipdPkgPathA] {
t.Errorf("pkg %q should be accessible, but it is not accessible by cipd", cipdPkgPathA)
}
if pkgMap[cipdPkgPathB] {
t.Errorf("pkg %q should not be accessible, but it is accessible by cipd", cipdPkgPathB)
}
}
func TestResolve(t *testing.T) {
fakex, cleanup := xtest.NewX(t)
defer cleanup()
cipdPath, err := Bootstrap(fakex, fakex.CIPDPath())
if err != nil {
t.Errorf("bootstrap failed due to error: %v", err)
}
defer os.Remove(cipdPath)
// Write test ensure file
testEnsureFile, err := ioutil.TempFile("", "test_jiri*.ensure")
if err != nil {
t.Errorf("failed to create test ensure file: %v", err)
}
defer testEnsureFile.Close()
ensureFileName := testEnsureFile.Name()
defer os.Remove(ensureFileName)
versionFileName := ensureFileName[:len(ensureFileName)-len(".ensure")] + ".version"
var ensureBuf bytes.Buffer
ensureBuf.WriteString("$ResolvedVersions " + versionFileName + "\n")
ensureBuf.WriteString(`
$ParanoidMode CheckPresence
$VerifiedPlatform linux-amd64
$VerifiedPlatform mac-amd64
# GN
gn/gn/${platform} git_revision:bdb0fd02324b120cacde634a9235405061c8ea06
`)
_, err = testEnsureFile.Write(ensureBuf.Bytes())
if err != nil {
t.Errorf("failed to write test ensure file: %v", err)
}
testEnsureFile.Sync()
instances, err := Resolve(fakex, testEnsureFile.Name())
if err != nil {
t.Errorf("resolve failed due to error: %v", err)
}
for _, instance := range instances {
if val, ok := instanceIDMap[instance.PackageName]; ok {
if val != instance.InstanceID {
t.Errorf("instance id %q for package %q does not match the record %q",
instance.InstanceID, instance.PackageName, val)
}
} else {
t.Errorf("package %q is not found in record", instance.PackageName)
}
}
}
func TestExpand(t *testing.T) {
t.Parallel()
platforms := []Platform{
{"linux", "amd64"},
{"linux", "arm64"},
{"mac", "amd64"},
}
tests := map[string][]string{
"gn/gn/${platform}": {"gn/gn/linux-amd64", "gn/gn/linux-arm64", "gn/gn/mac-amd64"},
"fuchsia/sysroot/${os=linux}-${arch}": {"fuchsia/sysroot/linux-amd64", "fuchsia/sysroot/linux-arm64"},
"infra/ninja/linux-amd64": {"infra/ninja/linux-amd64"},
}
for k, p := range tests {
pkgs, err := Expand(k, platforms)
if err != nil {
t.Errorf("Expand faild on path %q due to error: %v", p, err)
}
sort.Strings(p)
sort.Strings(pkgs)
if !reflect.DeepEqual(p, pkgs) {
t.Errorf("test on %q failed: expecting %v, got %v", k, p, pkgs)
}
}
}
func TestMustExpand(t *testing.T) {
t.Parallel()
tests := map[string]bool{
"fuchsia/clang/${platform}": true,
"fuchsia/clang/${os}-${arch}": true,
"fuchsia/clang/${os=linux}-${arch=amd64}": true,
"fuchsia/clang/linux-amd64": false,
}
for k, v := range tests {
if MustExpand(k) != v {
t.Errorf("MustExpand failed on package %q, expecting %v got %v", k, v, MustExpand(k))
}
}
}
func TestDecl(t *testing.T) {
t.Parallel()
platforms := []Platform{
{"linux", "amd64"},
{"linux", "arm64"},
{"mac", "amd64"},
}
tests := map[string]string{
"fuchsia/clang/${platform}": "fuchsia/clang/${platform=linux-amd64,linux-arm64,mac-amd64}",
"fuchsia/clang/${os}-${arch}": "fuchsia/clang/${os=linux,mac}-${arch=amd64,arm64}",
"fuchsia/clang/${os=linux}-${arch}": "fuchsia/clang/${os=linux}-${arch=amd64,arm64}",
"fuchsia/clang/${os=linux}-${arch=amd64}": "fuchsia/clang/${os=linux}-${arch=amd64}",
"fuchsia/clang/linux-amd64": "fuchsia/clang/linux-amd64",
}
for k, v := range tests {
cipdPath, err := Decl(k, platforms)
if err != nil {
t.Errorf("Decl failed on cipdPath %q due to error: %v", k, err)
}
if cipdPath != v {
t.Errorf("test on %q failed: expecting %q, got %q", k, v, cipdPath)
}
}
}
func TestFloatingRefs(t *testing.T) {
fakex, cleanup := xtest.NewX(t)
defer cleanup()
cipdPath, err := Bootstrap(fakex, fakex.CIPDPath())
if err != nil {
t.Errorf("bootstrap failed due to error: %v", err)
}
defer os.Remove(cipdPath)
testExpects := map[PackageInstance]bool{
{
PackageName: "gn/gn/${platform}",
VersionTag: "latest",
}: true,
{
PackageName: "gn/gn/${platform}",
VersionTag: "git_revision:bdb0fd02324b120cacde634a9235405061c8ea06",
}: false,
}
platformMap := make(map[PackageInstance][]Platform)
for k := range testExpects {
platformMap[k] = DefaultPlatforms()
}
tests := make(map[PackageInstance]bool)
for k, v := range testExpects {
tests[k] = v
}
if err := CheckFloatingRefs(fakex, tests, platformMap); err != nil {
t.Errorf("CheckFloatingRefs failed due to error: %v", err)
return
}
for k, v := range tests {
if v != testExpects[k] {
t.Errorf("expecting %v, got %v for test %q", testExpects[k], v, k.PackageName)
}
}
}