blob: 802d1ecbd7af883eafa8e4e440aeec01a0244399 [file] [log] [blame]
// Copyright 2020 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 sdkcommon
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"testing"
)
const resolvedAddr = "fe80::c0ff:eee:fe00:4444%en0"
// See exec_test.go for details, but effectively this runs the function called TestHelperProcess passing
// the args.
func helperCommandForSDKCommon(command string, s ...string) (cmd *exec.Cmd) {
//testenv.MustHaveExec(t)
cs := []string{"-test.run=TestFakeSDKCommon", "--"}
cs = append(cs, command)
cs = append(cs, s...)
cmd = exec.Command(os.Args[0], cs...)
// Set this in the enviroment, so we can control the result.
cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
return cmd
}
func TestGetAvailableImages(t *testing.T) {
ExecCommand = helperCommandForSDKCommon
ExecLookPath = func(cmd string) (string, error) { return filepath.Join("mocked", cmd), nil }
defer func() {
ExecCommand = exec.Command
ExecLookPath = exec.LookPath
}()
testSDK := SDKProperties{
DataPath: t.TempDir(),
}
version := "test-version"
bucket := ""
images, err := testSDK.GetAvailableImages(version, bucket)
if err != nil {
t.Fatal(err)
}
if len(images) != 2 {
t.Fatalf("Expected 2 images, got %v: %v", len(images), images)
}
bucket = "private-bucket"
images, err = testSDK.GetAvailableImages(version, bucket)
if err != nil {
t.Fatal(err)
}
if len(images) != 4 {
t.Fatalf("Expected 4 images, got %v: %v", len(images), images)
}
bucket = ""
version = "unknown"
images, err = testSDK.GetAvailableImages(version, bucket)
if err != nil {
expected := "CommandException: One or more URLs matched no objects.: exit status 2"
actual := fmt.Sprintf("%v", err)
if actual != expected {
t.Fatalf("Expected exception [%v] got [%v]", expected, actual)
}
} else {
t.Fatal("Expected exception, but did not get one")
}
}
func TestGetAddressByName(t *testing.T) {
ExecCommand = helperCommandForSDKCommon
defer func() {
ExecCommand = exec.Command
}()
testSDK := SDKProperties{
DataPath: t.TempDir(),
}
deviceName := "test-device"
_, err := testSDK.GetAddressByName(deviceName)
if err != nil {
t.Fatal(err)
}
deviceName = "unknown-device"
_, err = testSDK.GetAddressByName(deviceName)
if err != nil {
expected := "resolve.go:76: no devices found for domains: [unknown-device]: exit status 2"
actual := fmt.Sprintf("%v", err)
if actual != expected {
t.Fatalf("Expected exception [%v] got [%v]", expected, actual)
}
} else {
t.Fatal("Expected exception, but did not get one")
}
}
func TestRunSSHCommand(t *testing.T) {
tempDir := t.TempDir()
homeDir := filepath.Join(tempDir, "_TEMP_HOME")
if err := os.MkdirAll(homeDir, 0755); err != nil {
t.Fatal(err)
}
ExecCommand = helperCommandForSDKCommon
GetUserHomeDir = mockedUserProperty(homeDir)
GetUsername = mockedUserProperty("testuser")
GetHostname = mockedUserProperty("test-host")
defer func() {
ExecCommand = exec.Command
GetUserHomeDir = DefaultGetUserHomeDir
GetUsername = DefaultGetUsername
GetHostname = DefaultGetHostname
}()
testSDK := SDKProperties{
DataPath: t.TempDir(),
}
targetAddress := resolvedAddr
customSSHConfig := ""
privateKey := ""
args := []string{"echo", "$SSH_CONNECTION"}
if _, err := testSDK.RunSSHCommand(targetAddress, customSSHConfig, privateKey, args); err != nil {
t.Fatal(err)
}
if _, err := testSDK.RunSSHCommand(targetAddress, customSSHConfig, privateKey, args); err != nil {
t.Fatal(err)
}
customSSHConfig = "custom-sshconfig"
os.Setenv("FSERVE_TEST_USE_CUSTOM_SSH_CONFIG", "1")
if _, err := testSDK.RunSSHCommand(targetAddress, customSSHConfig, privateKey, args); err != nil {
t.Fatal(err)
}
customSSHConfig = ""
privateKey = "private-key"
os.Setenv("FSERVE_TEST_USE_CUSTOM_SSH_CONFIG", "")
os.Setenv("FSERVE_TEST_USE_PRIVATE_KEY", "1")
if _, err := testSDK.RunSSHCommand(targetAddress, customSSHConfig, privateKey, args); err != nil {
t.Fatal(err)
}
}
func TestCheckSSHConfig(t *testing.T) {
tempDir := t.TempDir()
homeDir := filepath.Join(tempDir, "_TEMP_HOME")
if err := os.MkdirAll(homeDir, 0755); err != nil {
t.Fatal(err)
}
ExecCommand = helperCommandForSDKCommon
GetUserHomeDir = mockedUserProperty(homeDir)
GetUsername = mockedUserProperty("testuser")
GetHostname = mockedUserProperty("test-host")
defer func() {
ExecCommand = exec.Command
GetUserHomeDir = DefaultGetUserHomeDir
GetUsername = DefaultGetUsername
GetHostname = DefaultGetHostname
}()
testSDK := SDKProperties{
DataPath: t.TempDir(),
}
if err := checkSSHConfig(testSDK); err != nil {
t.Fatal(err)
}
}
func TestCheckSSHConfigExistingFiles(t *testing.T) {
tempDir := t.TempDir()
homeDir := filepath.Join(tempDir, "_TEMP_HOME")
if err := os.MkdirAll(homeDir, 0755); err != nil {
t.Fatal(err)
}
ExecCommand = helperCommandForSDKCommon
GetUserHomeDir = mockedUserProperty(homeDir)
GetUsername = mockedUserProperty("testuser")
GetHostname = mockedUserProperty("test-host")
defer func() {
ExecCommand = exec.Command
GetUserHomeDir = DefaultGetUserHomeDir
GetUsername = DefaultGetUsername
GetHostname = DefaultGetHostname
}()
testSDK := SDKProperties{
DataPath: t.TempDir(),
}
// Write out SSH keys and config
data := []byte("Test SSH Key\n")
sshDir := filepath.Join(homeDir, ".ssh")
authFile := filepath.Join(sshDir, "fuchsia_authorized_keys")
keyFile := filepath.Join(sshDir, "fuchsia_ed25519")
sshConfigFile := getFuchsiaSSHConfigFile(testSDK)
if err := os.MkdirAll(sshDir, 0755); err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile(authFile, data, 0644); err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile(keyFile, data, 0644); err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile(sshConfigFile, []byte(sshConfigTag), 0644); err != nil {
t.Fatal(err)
}
if err := checkSSHConfig(testSDK); err != nil {
t.Fatal(err)
}
// Make sure they have not changed
content, err := ioutil.ReadFile(authFile)
if err != nil {
t.Fatal(err)
}
if string(content) != string(data) {
t.Fatalf("Expected test auth file to contain [%v], but contains [%v]", string(data), string(content))
}
content, err = ioutil.ReadFile(keyFile)
if err != nil {
t.Fatal(err)
}
if string(content) != string(data) {
t.Fatalf("Expected test key file to contain [%v], but contains [%v]", string(data), string(content))
}
content, err = ioutil.ReadFile(sshConfigFile)
if err != nil {
t.Fatal(err)
}
if string(content) != sshConfigTag {
t.Fatalf("Expected sshConfig file to contain [%v], but contains [%v]", string(data), string(content))
}
}
func mockedUserProperty(value string) func() (string, error) {
return func() (string, error) {
return value, nil
}
}
func TestFakeSDKCommon(t *testing.T) {
t.Helper()
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
return
}
defer os.Exit(0)
args := os.Args
for len(args) > 0 {
if args[0] == "--" {
args = args[1:]
break
}
args = args[1:]
}
if len(args) == 0 {
fmt.Fprintf(os.Stderr, "No command\n")
os.Exit(2)
}
// Check the command line
cmd, args := args[0], args[1:]
switch filepath.Base(cmd) {
case "gsutil":
fakeGSUtil(args)
case "device-finder":
fakeDeviceFinder(args)
case "ssh":
fakeSSH(args)
case "ssh-keygen":
fakeSSHKeygen(args)
default:
fmt.Fprintf(os.Stderr, "Unexpected command %v", cmd)
os.Exit(1)
}
}
func fakeGSUtil(args []string) {
expected := []string{}
if len(args) == 0 {
fmt.Fprintf(os.Stderr, "Expected arguments to gsutil\n")
os.Exit(1)
}
switch args[0] {
case "ls":
switch args[1] {
case "gs://fuchsia/development/test-version/images":
expected = []string{args[0], args[1]}
fmt.Printf("%v/image1.tgz\n", args[1])
fmt.Printf("%v/image2.tgz\n", args[1])
case "gs://private-bucket/development/test-version/images":
expected = []string{args[0], args[1]}
fmt.Printf("%v/priv-image3.tgz\n", args[1])
fmt.Printf("%v/priv-image4.tgz\n", args[1])
case "gs://fuchsia/development/unknown/images":
expected = []string{args[0], args[1]}
fmt.Fprintf(os.Stderr, "CommandException: One or more URLs matched no objects.")
os.Exit(2)
default:
expected = []string{"ls", "gs://fuchsia/development/test-version/images"}
}
}
ok := len(args) == len(expected)
if ok {
for i := range args {
if strings.Contains(expected[i], "*") {
expectedPattern := regexp.MustCompile(expected[i])
ok = ok && expectedPattern.MatchString(args[i])
} else {
ok = ok && args[i] == expected[i]
}
}
}
if !ok {
fmt.Fprintf(os.Stderr, "unexpected gsutil args %v. Expected %v", args, expected)
os.Exit(1)
}
}
func fakeDeviceFinder(args []string) {
expected := []string{}
expectedResolveArgs := []string{"resolve", "-device-limit", "1", "-ipv4=false", "test-device"}
if args[0] == "resolve" {
expected = expectedResolveArgs
if args[len(args)-1] == "test-device" {
fmt.Println(resolvedAddr)
} else {
fmt.Fprintf(os.Stderr, "resolve.go:76: no devices found for domains: [%v]", args[len(args)-1])
os.Exit(2)
}
}
ok := len(expected) == len(args)
for i := range args {
if strings.Contains(expected[i], "*") {
expectedPattern := regexp.MustCompile(expected[i])
ok = ok && expectedPattern.MatchString(args[i])
} else {
ok = ok && args[i] == expected[i]
}
}
if !ok {
fmt.Fprintf(os.Stderr, "unexpected ssh args %v exepected %v", args, expected)
os.Exit(1)
}
}
func fakeSSH(args []string) {
expected := []string{}
expectedHostConnection := []string{}
expectedSetSource := []string{}
privateKeyArgs := []string{"-i", "private-key"}
sshConfigMatch := "/.*/sshconfig"
if os.Getenv("FSERVE_TEST_USE_CUSTOM_SSH_CONFIG") != "" {
sshConfigMatch = "custom-sshconfig"
}
sshConfigArgs := []string{"-F", sshConfigMatch}
hostaddr := "fe80::c0ff:eeee:fefe:c000%eth1"
targetaddr := resolvedAddr
targetIndex := 2
expectedHostConnection = append(expectedHostConnection, sshConfigArgs...)
expectedSetSource = append(expectedSetSource, sshConfigArgs...)
if os.Getenv("FSERVE_TEST_USE_PRIVATE_KEY") != "" {
targetIndex = 4
expectedHostConnection = append(expectedHostConnection, privateKeyArgs...)
expectedSetSource = append(expectedSetSource, privateKeyArgs...)
}
if args[targetIndex] == resolvedAddr {
hostaddr = "fe80::c0ff:eeee:fefe:c000%eth1"
} else {
targetaddr = args[targetIndex]
hostaddr = "10.10.1.12"
}
expectedHostConnection = append(expectedHostConnection, targetaddr, "echo", "$SSH_CONNECTION")
if args[len(args)-1] == "$SSH_CONNECTION" {
expected = expectedHostConnection
fmt.Printf("%v 54545 fe80::c00f:f0f0:eeee:cccc 22\n", hostaddr)
}
ok := len(args) == len(expected)
if ok {
for i := range args {
if strings.Contains(expected[i], "*") {
expectedPattern := regexp.MustCompile(expected[i])
ok = ok && expectedPattern.MatchString(args[i])
} else {
ok = ok && args[i] == expected[i]
}
}
}
if !ok {
fmt.Fprintf(os.Stderr, "unexpected ssh args %v expected %v", args, expected)
os.Exit(1)
}
}
func fakeSSHKeygen(args []string) {
expectedPrivate := []string{"-P", "", "-t", "ed25519", "-f", "/.*/_TEMP_HOME/.ssh/fuchsia_ed25519", "-C", "testuser@test-host generated by Fuchsia GN SDK"}
expectedPublic := []string{"-y", "-f", "/.*/_TEMP_HOME/.ssh/fuchsia_ed25519"}
expected := []string{}
if len(args) == 0 {
fmt.Fprintf(os.Stderr, "expected ssh-keygen args")
os.Exit(1)
}
if args[0] == "-P" {
expected = expectedPrivate
} else if args[0] == "-y" {
expected = expectedPublic
fmt.Println("ssh-ed25519 AAAAC3NzaC1lTESTNTE5AAAAILxVYY7Q++kWUCmlfK1B6JQ9FPRaee05Te/PSHWVTeST testuser@test-host generated by Fuchsia GN SDK")
}
ok := len(args) == len(expected)
if ok {
for i := range args {
if strings.Contains(expected[i], "*") {
expectedPattern := regexp.MustCompile(expected[i])
ok = ok && expectedPattern.MatchString(args[i])
} else {
ok = ok && args[i] == expected[i]
}
}
}
if !ok {
fmt.Fprintf(os.Stderr, "unexpected ssh-keygen args %v exepected %v", args, expected)
os.Exit(1)
}
}