treewide: replace hardcoded runfiles paths

We hardcoded some of the runfiles paths to find specific files. This replaces the hardcoded paths by a call to rlocationpath. This prevents running a target without the correct dependencies at build time instead of at runtime

Change-Id: I7ce56935ac80be6b28b824ccb0781ab401bd6521
Reviewed-on: https://review.monogon.dev/c/monogon/+/3301
Reviewed-by: Serge Bazanski <serge@monogon.tech>
Tested-by: Jenkins CI
diff --git a/metropolis/test/launch/BUILD.bazel b/metropolis/test/launch/BUILD.bazel
index 1b7ee4d..d494a6b 100644
--- a/metropolis/test/launch/BUILD.bazel
+++ b/metropolis/test/launch/BUILD.bazel
@@ -5,24 +5,39 @@
     srcs = [
         "cluster.go",
         "insecure_key.go",
+        "launch.go",
         "metroctl.go",
         "prefixed_stdio.go",
         "swtpm.go",
     ],
     data = [
+        "//metropolis/cli/metroctl",
         "//metropolis/node:image",
         "//metropolis/test/nanoswitch:initramfs",
         "//metropolis/test/swtpm/certtool",
         "//metropolis/test/swtpm/swtpm_cert",
         "//osbase/test/ktest:linux-testing",
-        "//third_party/edk2:firmware",
-        "@com_github_bonzini_qboot//:qboot-bin",
+        "//third_party/edk2:OVMF_CODE.fd",
+        "//third_party/edk2:OVMF_VARS.fd",
         "@swtpm",
         "@swtpm//:swtpm_localca",
         "@swtpm//:swtpm_setup",
     ],
     importpath = "source.monogon.dev/metropolis/test/launch",
     visibility = ["//visibility:public"],
+    x_defs = {
+        "xSwtpmPath": "$(rlocationpath @swtpm )",
+        "xSwtpmSetupPath": "$(rlocationpath @swtpm//:swtpm_setup )",
+        "xSwtpmLocalCAPath": "$(rlocationpath @swtpm//:swtpm_localca )",
+        "xSwtpmCertPath": "$(rlocationpath //metropolis/test/swtpm/swtpm_cert )",
+        "xCerttoolPath": "$(rlocationpath //metropolis/test/swtpm/certtool )",
+        "xMetroctlPath": "$(rlocationpath //metropolis/cli/metroctl )",
+        "xOvmfVarsPath": "$(rlocationpath //third_party/edk2:OVMF_VARS.fd )",
+        "xOvmfCodePath": "$(rlocationpath //third_party/edk2:OVMF_CODE.fd )",
+        "xKernelPath": "$(rlocationpath //osbase/test/ktest:linux-testing )",
+        "xInitramfsPath": "$(rlocationpath //metropolis/test/nanoswitch:initramfs )",
+        "xNodeImagePath": "$(rlocationpath //metropolis/node:image )",
+    },
     deps = [
         "//go/qcow2",
         "//metropolis/cli/metroctl/core",
diff --git a/metropolis/test/launch/cli/launch-cluster/BUILD.bazel b/metropolis/test/launch/cli/launch-cluster/BUILD.bazel
index 9e48795..a952abd 100644
--- a/metropolis/test/launch/cli/launch-cluster/BUILD.bazel
+++ b/metropolis/test/launch/cli/launch-cluster/BUILD.bazel
@@ -14,9 +14,6 @@
 
 go_binary(
     name = "launch-cluster",
-    data = [
-        "//metropolis/cli/metroctl",
-    ],
     embed = [":launch-cluster_lib"],
     visibility = ["//visibility:public"],
 )
diff --git a/metropolis/test/launch/cli/launch-cluster/main.go b/metropolis/test/launch/cli/launch-cluster/main.go
index 1529396..695fcc7 100644
--- a/metropolis/test/launch/cli/launch-cluster/main.go
+++ b/metropolis/test/launch/cli/launch-cluster/main.go
@@ -20,6 +20,7 @@
 	"context"
 	"log"
 	"os"
+	"os/exec"
 	"os/signal"
 
 	metroctl "source.monogon.dev/metropolis/cli/metroctl/core"
@@ -36,10 +37,6 @@
 		log.Fatalf("LaunchCluster: %v", err)
 	}
 
-	mpath, err := mlaunch.MetroctlRunfilePath()
-	if err != nil {
-		log.Fatalf("MetroctlRunfilePath: %v", err)
-	}
 	wpath, err := cl.MakeMetroctlWrapper()
 	if err != nil {
 		log.Fatalf("MakeWrapper: %v", err)
@@ -53,8 +50,19 @@
 		log.Fatalf("Cluster has no Kubernetes controller nodes")
 	}
 
+	// If the user has metroctl in their path, use the metroctl from path as
+	// a credential plugin. Otherwise use the path to the currently-running
+	// metroctl.
+	metroctlPath := "metroctl"
+	if _, err := exec.LookPath("metroctl"); err != nil {
+		metroctlPath, err = os.Executable()
+		if err != nil {
+			log.Fatalf("Failed to create kubectl entry as metroctl is neither in PATH nor can its absolute path be determined: %v", err)
+		}
+	}
+
 	configName := "launch-cluster"
-	if err := metroctl.InstallKubeletConfig(ctx, mpath, cl.ConnectOptions(), configName, apiservers[0]); err != nil {
+	if err := metroctl.InstallKubeletConfig(ctx, metroctlPath, cl.ConnectOptions(), configName, apiservers[0]); err != nil {
 		log.Fatalf("InstallKubeletConfig: %v", err)
 	}
 
diff --git a/metropolis/test/launch/cluster.go b/metropolis/test/launch/cluster.go
index ac8a017..98ab760 100644
--- a/metropolis/test/launch/cluster.go
+++ b/metropolis/test/launch/cluster.go
@@ -26,7 +26,6 @@
 	"syscall"
 	"time"
 
-	"github.com/bazelbuild/rules_go/go/runfiles"
 	"github.com/cenkalti/backoff/v4"
 	"go.uber.org/multierr"
 	"golang.org/x/net/proxy"
@@ -140,30 +139,21 @@
 	}
 
 	// Initialize the node's storage with a prebuilt image.
-	si, err := runfiles.Rlocation("_main/metropolis/node/image.img")
-	if err != nil {
-		return nil, fmt.Errorf("while resolving a path: %w", err)
-	}
-
 	di := filepath.Join(stdp, "image.qcow2")
-	launch.Log("Cluster: generating node QCOW2 snapshot image: %s -> %s", si, di)
+	launch.Log("Cluster: generating node QCOW2 snapshot image: %s -> %s", xNodeImagePath, di)
 
 	df, err := os.Create(di)
 	if err != nil {
 		return nil, fmt.Errorf("while opening image for writing: %w", err)
 	}
 	defer df.Close()
-	if err := qcow2.Generate(df, qcow2.GenerateWithBackingFile(si)); err != nil {
+	if err := qcow2.Generate(df, qcow2.GenerateWithBackingFile(xNodeImagePath)); err != nil {
 		return nil, fmt.Errorf("while creating copy-on-write node image: %w", err)
 	}
 
 	// Initialize the OVMF firmware variables file.
-	sv, err := runfiles.Rlocation("edk2/OVMF_VARS.fd")
-	if err != nil {
-		return nil, fmt.Errorf("while resolving a path: %w", err)
-	}
-	dv := filepath.Join(stdp, filepath.Base(sv))
-	if err := copyFile(sv, dv); err != nil {
+	dv := filepath.Join(stdp, filepath.Base(xOvmfVarsPath))
+	if err := copyFile(xOvmfVarsPath, dv); err != nil {
 		return nil, fmt.Errorf("while copying firmware variables: %w", err)
 	}
 
@@ -268,18 +258,13 @@
 		options.Mac = mac
 	}
 
-	ovmfCodePath, err := runfiles.Rlocation("edk2/OVMF_CODE.fd")
-	if err != nil {
-		return err
-	}
-
 	tpmSocketPath := filepath.Join(r.sd, "tpm-socket")
 	fwVarPath := filepath.Join(r.ld, "OVMF_VARS.fd")
 	storagePath := filepath.Join(r.ld, "image.qcow2")
 	qemuArgs := []string{
 		"-machine", "q35", "-accel", "kvm", "-nographic", "-nodefaults", "-m", "2048",
 		"-cpu", "host", "-smp", "sockets=1,cpus=1,cores=2,threads=2,maxcpus=4",
-		"-drive", "if=pflash,format=raw,readonly=on,file=" + ovmfCodePath,
+		"-drive", "if=pflash,format=raw,readonly=on,file=" + xOvmfCodePath,
 		"-drive", "if=pflash,format=raw,file=" + fwVarPath,
 		"-drive", "if=virtio,format=qcow2,cache=unsafe,file=" + storagePath,
 		"-netdev", qemuNetConfig.ToOption(qemuNetType),
@@ -318,7 +303,7 @@
 
 	// Manufacture TPM if needed.
 	tpmd := filepath.Join(r.ld, "tpm")
-	err = tpmFactory.Manufacture(ctx, tpmd, &TPMPlatform{
+	err := tpmFactory.Manufacture(ctx, tpmd, &TPMPlatform{
 		Manufacturer: "Monogon",
 		Version:      "1.0",
 		Model:        "TestCluster",
@@ -328,14 +313,9 @@
 	}
 
 	// Start TPM emulator as a subprocess
-	swtpm, err := runfiles.Rlocation("swtpm/swtpm")
-	if err != nil {
-		return fmt.Errorf("could not find swtpm: %w", err)
-	}
-
 	tpmCtx, tpmCancel := context.WithCancel(options.Runtime.ctxT)
 
-	tpmEmuCmd := exec.CommandContext(tpmCtx, swtpm, "socket", "--tpm2", "--tpmstate", "dir="+tpmd, "--ctrl", "type=unixio,path="+tpmSocketPath)
+	tpmEmuCmd := exec.CommandContext(tpmCtx, xSwtpmPath, "socket", "--tpm2", "--tpmstate", "dir="+tpmd, "--ctrl", "type=unixio,path="+tpmSocketPath)
 	// Silence warnings from unsafe libtpms build (uses non-constant-time
 	// cryptographic operations).
 	tpmEmuCmd.Env = append(tpmEmuCmd.Env, "MONOGON_LIBTPMS_ACKNOWLEDGE_UNSAFE=yes")
@@ -854,18 +834,10 @@
 		} else {
 			serialPort = newPrefixedStdio(99)
 		}
-		kernelPath, err := runfiles.Rlocation("_main/osbase/test/ktest/vmlinux")
-		if err != nil {
-			launch.Fatal("Failed to resolved nanoswitch kernel: %v", err)
-		}
-		initramfsPath, err := runfiles.Rlocation("_main/metropolis/test/nanoswitch/initramfs.cpio.zst")
-		if err != nil {
-			launch.Fatal("Failed to resolved nanoswitch initramfs: %v", err)
-		}
 		if err := launch.RunMicroVM(ctxT, &launch.MicroVMOptions{
 			Name:                   "nanoswitch",
-			KernelPath:             kernelPath,
-			InitramfsPath:          initramfsPath,
+			KernelPath:             xKernelPath,
+			InitramfsPath:          xInitramfsPath,
 			ExtraNetworkInterfaces: switchPorts,
 			PortMap:                portMap,
 			GuestServiceMap:        guestSvcMap,
diff --git a/metropolis/test/launch/launch.go b/metropolis/test/launch/launch.go
new file mode 100644
index 0000000..d6c29fe
--- /dev/null
+++ b/metropolis/test/launch/launch.go
@@ -0,0 +1,37 @@
+package launch
+
+import (
+	"github.com/bazelbuild/rules_go/go/runfiles"
+)
+
+var (
+	// These are filled by bazel at linking time with the canonical path of
+	// their corresponding file. Inside the init function we resolve it
+	// with the rules_go runfiles package to the real path.
+	xSwtpmPath        string
+	xSwtpmSetupPath   string
+	xSwtpmLocalCAPath string
+	xSwtpmCertPath    string
+	xCerttoolPath     string
+	xMetroctlPath     string
+	xOvmfCodePath     string
+	xOvmfVarsPath     string
+	xKernelPath       string
+	xInitramfsPath    string
+	xNodeImagePath    string
+)
+
+func init() {
+	var err error
+	for _, path := range []*string{
+		&xSwtpmPath, &xSwtpmSetupPath, &xSwtpmLocalCAPath,
+		&xSwtpmCertPath, &xCerttoolPath, &xMetroctlPath,
+		&xOvmfCodePath, &xOvmfVarsPath, &xKernelPath,
+		&xInitramfsPath, &xNodeImagePath,
+	} {
+		*path, err = runfiles.Rlocation(*path)
+		if err != nil {
+			panic(err)
+		}
+	}
+}
diff --git a/metropolis/test/launch/metroctl.go b/metropolis/test/launch/metroctl.go
index e3196a6..711855b 100644
--- a/metropolis/test/launch/metroctl.go
+++ b/metropolis/test/launch/metroctl.go
@@ -9,25 +9,11 @@
 	"path"
 	"sort"
 
-	"github.com/bazelbuild/rules_go/go/runfiles"
 	"github.com/kballard/go-shellquote"
 
 	metroctl "source.monogon.dev/metropolis/cli/metroctl/core"
 )
 
-const metroctlRunfile = "_main/metropolis/cli/metroctl/metroctl_/metroctl"
-
-// MetroctlRunfilePath returns the absolute path to the metroctl binary available
-// if the built target depends on //metropolis/cli/metroctl. Otherwise, an error
-// is returned.
-func MetroctlRunfilePath() (string, error) {
-	path, err := runfiles.Rlocation(metroctlRunfile)
-	if err != nil {
-		return "", fmt.Errorf("//metropolis/cli/metroctl not found in runfiles, did you include it as a data dependency? error: %w", err)
-	}
-	return path, nil
-}
-
 type acceptall struct{}
 
 func (a *acceptall) Ask(ctx context.Context, _ *metroctl.ConnectOptions, _ *x509.Certificate) (bool, error) {
@@ -63,10 +49,6 @@
 // dependency of the built target) with all the required flags to connect to the
 // launched cluster.
 func (c *Cluster) MakeMetroctlWrapper() (string, error) {
-	mpath, err := MetroctlRunfilePath()
-	if err != nil {
-		return "", err
-	}
 	wpath := path.Join(c.metroctlDir, "metroctl.sh")
 
 	// Don't create wrapper if it already exists.
@@ -74,7 +56,7 @@
 		return wpath, nil
 	}
 
-	wrapper := fmt.Sprintf("#!/usr/bin/env bash\nexec %s %s \"$@\"", mpath, c.MetroctlFlags())
+	wrapper := fmt.Sprintf("#!/usr/bin/env bash\nexec %s %s \"$@\"", xMetroctlPath, c.MetroctlFlags())
 	if err := os.WriteFile(wpath, []byte(wrapper), 0555); err != nil {
 		return "", fmt.Errorf("could not write wrapper: %w", err)
 	}
diff --git a/metropolis/test/launch/swtpm.go b/metropolis/test/launch/swtpm.go
index fa5cb78..5691c86 100644
--- a/metropolis/test/launch/swtpm.go
+++ b/metropolis/test/launch/swtpm.go
@@ -10,8 +10,6 @@
 	"sort"
 	"strings"
 
-	"github.com/bazelbuild/rules_go/go/runfiles"
-
 	"source.monogon.dev/osbase/test/launch"
 )
 
@@ -111,29 +109,6 @@
 		launch.Log("Skipping manufacturing TPM for %s, already exists", path)
 		return nil
 	}
-
-	// Find all tools.
-	swtpm, err := runfiles.Rlocation("swtpm/swtpm")
-	if err != nil {
-		return fmt.Errorf("could not find swtpm: %w", err)
-	}
-	swtpmSetup, err := runfiles.Rlocation("swtpm/swtpm_setup")
-	if err != nil {
-		return fmt.Errorf("could not find swtpm_setup: %w", err)
-	}
-	swtpmLocalca, err := runfiles.Rlocation("swtpm/swtpm_localca")
-	if err != nil {
-		return fmt.Errorf("could not find swtpm_localca: %w", err)
-	}
-	swtpmCert, err := runfiles.Rlocation("_main/metropolis/test/swtpm/swtpm_cert/swtpm_cert_/swtpm_cert")
-	if err != nil {
-		return fmt.Errorf("could not find swtpm_cert: %w", err)
-	}
-	certtool, err := runfiles.Rlocation("_main/metropolis/test/swtpm/certtool/certtool_/certtool")
-	if err != nil {
-		return fmt.Errorf("could not find certtool: %w", err)
-	}
-
 	// Prepare swtpm-localca.options.
 	options := []string{
 		"--platform-manufacturer " + platform.Manufacturer,
@@ -141,14 +116,14 @@
 		"--platform-model " + platform.Model,
 		"",
 	}
-	err = os.WriteFile(f.localCAOptionsPath(), []byte(strings.Join(options, "\n")), 0600)
+	err := os.WriteFile(f.localCAOptionsPath(), []byte(strings.Join(options, "\n")), 0600)
 	if err != nil {
 		return fmt.Errorf("could not write local options: %w", err)
 	}
 
 	// Prepare swptm.conf.
 	err = writeSWTPMConfig(f.swtpmConfPath(), map[string]string{
-		"create_certs_tool":         swtpmLocalca,
+		"create_certs_tool":         xSwtpmLocalCAPath,
 		"create_certs_tool_config":  f.localCAConfPath(),
 		"create_certs_tool_options": f.localCAOptionsPath(),
 	})
@@ -159,8 +134,8 @@
 	if err := os.MkdirAll(path, 0700); err != nil {
 		return fmt.Errorf("could not make output path: %w", err)
 	}
-	cmd := exec.CommandContext(ctx, swtpmSetup,
-		"--tpm", fmt.Sprintf("%s socket", swtpm),
+	cmd := exec.CommandContext(ctx, xSwtpmSetupPath,
+		"--tpm", fmt.Sprintf("%s socket", xSwtpmPath),
 		"--tpmstate", path,
 		"--create-ek-cert",
 		"--create-platform-cert",
@@ -169,7 +144,7 @@
 		"--display",
 		"--pcr-banks", "sha1,sha256,sha384,sha512",
 		"--config", f.swtpmConfPath())
-	cmd.Env = append(cmd.Env, fmt.Sprintf("PATH=%s:%s", filepath.Dir(swtpmCert), filepath.Dir(certtool)))
+	cmd.Env = append(cmd.Env, fmt.Sprintf("PATH=%s:%s", filepath.Dir(xSwtpmCertPath), filepath.Dir(xCerttoolPath)))
 	cmd.Env = append(cmd.Env, "MONOGON_LIBTPMS_ACKNOWLEDGE_UNSAFE=yes")
 	if out, err := cmd.CombinedOutput(); err != nil {
 		log.Printf("Manufacturing TPM for %s failed: swtm_setup: %s", path, out)