blob: 92af977f19096689fa2fd82a963e562a4b8d4bd6 [file] [log] [blame]
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package provisioner
import (
"context"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"testing"
"cos.googlesource.com/cos/tools.git/src/pkg/fakes"
"golang.org/x/sys/unix"
)
const trueExecutable = "#!/bin/bash\ntrue"
func testDataDir(t *testing.T) string {
t.Helper()
path, err := filepath.Abs("testdata")
if err != nil {
t.Fatal(err)
}
return path
}
func stubMount() {
mountFunc = func(a1, a2, a3 string, a4 uintptr, a5 string) error {
return nil
}
unmountFunc = func(a1 string, a2 int) error {
return nil
}
}
func restoreMount() {
mountFunc = unix.Mount
unmountFunc = unix.Unmount
}
func stubMountInfo(filePath, mountPoint string) error {
if err := os.MkdirAll(filepath.Dir(filePath), 0755); err != nil {
return err
}
return ioutil.WriteFile(filePath, []byte(fmt.Sprintf("0 0 0 / %s ro\n", mountPoint)), 0644)
}
func TestStateExists(t *testing.T) {
ctx := context.Background()
dir, err := ioutil.TempDir("", "provisioner-test-")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { os.RemoveAll(dir) })
if err := ioutil.WriteFile(filepath.Join(dir, "state.json"), []byte("{}"), 0660); err != nil {
t.Fatal(err)
}
deps := Deps{
GCSClient: nil,
TarCmd: "",
SystemctlCmd: "",
DockerCredentialGCR: []byte(trueExecutable),
VeritySetupImage: []byte(trueExecutable),
HandleDiskLayoutBin: []byte(trueExecutable),
}
config := Config{}
if err := Run(ctx, deps, dir, config); err != errStateAlreadyExists {
t.Fatalf("Run(ctx, %+v, %q, %+v) = %v; want %v", deps, dir, config, err, errStateAlreadyExists)
}
}
func TestRunInvalidArgs(t *testing.T) {
stubMount()
t.Cleanup(restoreMount)
tests := []struct {
name string
config Config
}{
{
name: "RunScript",
config: Config{
Steps: []StepConfig{
{
Type: "RunScript",
Args: []byte("{}"),
},
},
},
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
ctx := context.Background()
tempDir, err := ioutil.TempDir("", "provisioner-test-")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tempDir)
gcs := fakes.GCSForTest(t)
deps := Deps{
GCSClient: gcs.Client,
TarCmd: "tar",
SystemctlCmd: "/bin/true",
RootDir: tempDir,
DockerCredentialGCR: []byte(trueExecutable),
VeritySetupImage: []byte(trueExecutable),
HandleDiskLayoutBin: []byte(trueExecutable),
}
stateDir := filepath.Join(tempDir, "var", "lib", ".cos-customizer")
if err := stubMountInfo(filepath.Join(tempDir, "proc", "self", "mountinfo"), filepath.Join(stateDir, "bin")); err != nil {
t.Fatal(err)
}
funcCall := fmt.Sprintf("Run(ctx, %+v, %q, %+v)", deps, stateDir, test.config)
if err := Run(ctx, deps, stateDir, test.config); err == nil {
t.Fatalf("%s = nil; want invalid args", funcCall)
}
})
}
}
func TestRunFailure(t *testing.T) {
stubMount()
t.Cleanup(restoreMount)
testData := testDataDir(t)
buildCtxDir, err := ioutil.TempDir("", "provisioner-test-")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { os.RemoveAll(buildCtxDir) })
buildCtx := filepath.Join(buildCtxDir, "test.tar")
if err := exec.Command("tar", "cf", buildCtx, "-C", filepath.Join(testData, "test_ctx"), ".").Run(); err != nil {
t.Fatal(err)
}
tests := []struct {
name string
gcsObjects map[string]string
config Config
}{
{
name: "RunScript",
gcsObjects: map[string]string{
"/test/test.tar": buildCtx,
},
config: Config{
BuildContexts: map[string]string{
"bc": "gs://test/test.tar",
},
Steps: []StepConfig{
{
Type: "RunScript",
Args: []byte(`{"BuildContext": "bc", "Path": "run_env.sh"}`),
},
},
},
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
ctx := context.Background()
tempDir, err := ioutil.TempDir("", "provisioner-test-")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tempDir)
gcs := fakes.GCSForTest(t)
for name, path := range test.gcsObjects {
data, err := ioutil.ReadFile(path)
if err != nil {
t.Fatal(err)
}
gcs.Objects[name] = data
}
deps := Deps{
GCSClient: gcs.Client,
TarCmd: "tar",
SystemctlCmd: "/bin/true",
RootDir: tempDir,
DockerCredentialGCR: []byte(trueExecutable),
VeritySetupImage: []byte(trueExecutable),
HandleDiskLayoutBin: []byte(trueExecutable),
}
stateDir := filepath.Join(tempDir, "var", "lib", ".cos-customizer")
if err := stubMountInfo(filepath.Join(tempDir, "proc", "self", "mountinfo"), filepath.Join(stateDir, "bin")); err != nil {
t.Fatal(err)
}
funcCall := fmt.Sprintf("Run(ctx, %+v, %q, %+v)", deps, stateDir, test.config)
if err := Run(ctx, deps, stateDir, test.config); err == nil {
t.Fatalf("%s = nil; want err", funcCall)
}
})
}
}
func TestRunSuccess(t *testing.T) {
stubMount()
t.Cleanup(restoreMount)
testData := testDataDir(t)
buildCtxDir, err := ioutil.TempDir("", "provisioner-test-")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { os.RemoveAll(buildCtxDir) })
buildCtx := filepath.Join(buildCtxDir, "test.tar")
if err := exec.Command("tar", "cf", buildCtx, "-C", filepath.Join(testData, "test_ctx"), ".").Run(); err != nil {
t.Fatal(err)
}
tests := []struct {
name string
gcsObjects map[string]string
config Config
}{
{
name: "EmptyConfig",
config: Config{},
},
{
name: "RunScript",
gcsObjects: map[string]string{
"/test/test.tar": buildCtx,
},
config: Config{
BuildContexts: map[string]string{
"bc": "gs://test/test.tar",
},
Steps: []StepConfig{
{
Type: "RunScript",
Args: []byte(`{"BuildContext": "bc", "Path": "run.sh"}`),
},
{
Type: "RunScript",
Args: []byte(`{"BuildContext": "bc", "Path": "run_env.sh", "Env": "TEST=t"}`),
},
},
},
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
ctx := context.Background()
tempDir, err := ioutil.TempDir("", "provisioner-test-")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tempDir)
gcs := fakes.GCSForTest(t)
for name, path := range test.gcsObjects {
data, err := ioutil.ReadFile(path)
if err != nil {
t.Fatal(err)
}
gcs.Objects[name] = data
}
deps := Deps{
GCSClient: gcs.Client,
TarCmd: "tar",
SystemctlCmd: "/bin/true",
RootDir: tempDir,
DockerCredentialGCR: []byte(trueExecutable),
VeritySetupImage: []byte(trueExecutable),
HandleDiskLayoutBin: []byte(trueExecutable),
}
stateDir := filepath.Join(tempDir, "var", "lib", ".cos-customizer")
if err := stubMountInfo(filepath.Join(tempDir, "proc", "self", "mountinfo"), filepath.Join(stateDir, "bin")); err != nil {
t.Fatal(err)
}
funcCall := fmt.Sprintf("Run(ctx, %+v, %q, %+v)", deps, stateDir, test.config)
if err := Run(ctx, deps, stateDir, test.config); err != nil {
t.Fatalf("%s = %v; want nil", funcCall, err)
}
})
}
}