metropolis/vm: delete vm package

This only contains the CRD right now and is not used/implemented
anywhere. This is also the only usage of kube-code-gen which breaks with
the newest version of rules_go. Since we don't plan to use this
in the near future this also gets deleted, because even if we need it
again, we can just revert this change.

Change-Id: I29ab75541957fce6a7dd8414c0df3cfdf90f8ec3
Reviewed-on: https://review.monogon.dev/c/monogon/+/3465
Tested-by: Jenkins CI
Reviewed-by: Serge Bazanski <serge@monogon.tech>
diff --git a/build/bazel/go.MODULE.bazel b/build/bazel/go.MODULE.bazel
index 969693a..35d2bfb 100644
--- a/build/bazel/go.MODULE.bazel
+++ b/build/bazel/go.MODULE.bazel
@@ -82,7 +82,6 @@
     "io_k8s_apiserver",
     "io_k8s_cli_runtime",
     "io_k8s_client_go",
-    "io_k8s_code_generator",
     "io_k8s_component_base",
     "io_k8s_klog_v2",
     "io_k8s_kubectl",
diff --git a/go.mod b/go.mod
index 7bda6d4..470152f 100644
--- a/go.mod
+++ b/go.mod
@@ -154,7 +154,6 @@
 	k8s.io/apiserver v0.31.0
 	k8s.io/cli-runtime v0.31.0
 	k8s.io/client-go v0.31.0
-	k8s.io/code-generator v0.31.0
 	k8s.io/component-base v0.31.0
 	k8s.io/klog/v2 v2.130.1
 	k8s.io/kubectl v0.0.0
@@ -440,7 +439,6 @@
 	k8s.io/csi-translation-lib v0.0.0 // indirect
 	k8s.io/dynamic-resource-allocation v0.0.0 // indirect
 	k8s.io/endpointslice v0.0.0 // indirect
-	k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 // indirect
 	k8s.io/klog v1.0.0 // indirect
 	k8s.io/kms v0.31.0 // indirect
 	k8s.io/kube-aggregator v0.0.0 // indirect
diff --git a/go.sum b/go.sum
index 9da31c3..b696a00 100644
--- a/go.sum
+++ b/go.sum
@@ -4940,7 +4940,6 @@
 k8s.io/cloud-provider v0.31.0/go.mod h1:QgUPqLoL6aXhLlrNg1U4IrJk/PvvxgeOnT2ixkgnqT0=
 k8s.io/cluster-bootstrap v0.31.0 h1:jj5t1PArBPddvDypdNpzqnZQ/+qnGxpJuTF7SX05h1Y=
 k8s.io/cluster-bootstrap v0.31.0/go.mod h1:6ujqWFrBV4amKe1ii/6BXgrd57bF/Q3gXebLJdmfSK4=
-k8s.io/code-generator v0.31.0 h1:w607nrMi1KeDKB3/F/J4lIoOgAwc+gV9ZKew4XRfMp8=
 k8s.io/code-generator v0.31.0/go.mod h1:84y4w3es8rOJOUUP1rLsIiGlO1JuEaPFXQPA9e/K6U0=
 k8s.io/component-base v0.31.0 h1:/KIzGM5EvPNQcYgwq5NwoQBaOlVFrghoVGr8lG6vNRs=
 k8s.io/component-base v0.31.0/go.mod h1:TYVuzI1QmN4L5ItVdMSXKvH7/DtvIuas5/mm8YT3rTo=
@@ -4960,7 +4959,6 @@
 k8s.io/endpointslice v0.31.0/go.mod h1:1yyB5w4UNm4b+IX6BtnvZ5XLtWbvFUfg90yWf+O1PVk=
 k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
 k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
-k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 h1:NGrVE502P0s0/1hudf8zjgwki1X/TByhmAoILTarmzo=
 k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70/go.mod h1:VH3AT8AaQOqiGjMF9p0/IM1Dj+82ZwjfxUP1IxaHE+8=
 k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
 k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
diff --git a/metropolis/build/kube-code-generator/BUILD.bazel b/metropolis/build/kube-code-generator/BUILD.bazel
deleted file mode 100644
index 52d6327..0000000
--- a/metropolis/build/kube-code-generator/BUILD.bazel
+++ /dev/null
@@ -1,21 +0,0 @@
-load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
-
-exports_files(["boilerplate.go.txt"])
-
-# Flag determining whether the current build graph traversal is happening for
-# preprocessing by kube-code-generator ('yes'), or not ('no'). Set by
-# preprocessing_transition.
-string_flag(
-    name = "preprocessing",
-    build_setting_default = "no",
-)
-
-# Config setting on which go_libraries embedding go_kubernetes_libraries
-# potentially forming a cycle (eg. deepcopy, which is embedded in the same
-# go_library from which it is generated) can rely on to break this cycle.
-config_setting(
-    name = "embed_deepcopy",
-    flag_values = {
-        ":preprocessing": "no",
-    },
-)
diff --git a/metropolis/build/kube-code-generator/README.md b/metropolis/build/kube-code-generator/README.md
deleted file mode 100644
index 7c18289..0000000
--- a/metropolis/build/kube-code-generator/README.md
+++ /dev/null
@@ -1,17 +0,0 @@
-kube-code-generator
-===================
-
-A small Bazel rule library for dealing with k8s.io/code-generators.
-
-See defs.bzl for documentation, and `//metropolis/vm/kube/apis` for an example of usage.
-
-Current Limitations
--------------------
-
- - Clientset-gen's `versioned/fake` is not generated.
- - Only the following generators are ran: deepcopy, clientset, informer, lister.
- - Bazel BUILDfiles for the generated structure must be crafted manually.
- - Go packages must follow upstream format (group/version). This influences
-   Bazel target structure, which can then look somewhat awkward in a
-   project-oriented monorepo (eg. //foo/bar/widget/kube/apis/widget/v1 has a
-   'widget' stutter.
diff --git a/metropolis/build/kube-code-generator/boilerplate.go.txt b/metropolis/build/kube-code-generator/boilerplate.go.txt
deleted file mode 100644
index c097ae1..0000000
--- a/metropolis/build/kube-code-generator/boilerplate.go.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Code generated by //osbase/build/kubernetes-code-generator. Do not commit to source control.
diff --git a/metropolis/build/kube-code-generator/defs.bzl b/metropolis/build/kube-code-generator/defs.bzl
deleted file mode 100644
index 2bc32bf..0000000
--- a/metropolis/build/kube-code-generator/defs.bzl
+++ /dev/null
@@ -1,561 +0,0 @@
-#  Copyright 2020 The Monogon Project Authors.
-#
-#  SPDX-License-Identifier: Apache-2.0
-#
-#  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.
-
-# Bazel rules for generating Kubernetes-style API types using
-# github.com/kubernetes/code-generator.
-#
-# k8s.io/code-gen generators target a peculiar filesystem/package structure for
-# generated code:
-#
-#    example.com/project/apis/goodbye/v1/doc.go          - hand written
-#                                       /types.go        - hand written
-#                                       /zz_generated.go - generated
-#    example.com/project/apis/hello/v1/doc.go            - hand written
-#                                     /types.go          - hand written
-#                                     /zz_generated.go   - generated
-#    example.com/project/generated/clientset/...         - generated
-#    example.com/project/generated/informers/...         - generated
-#    example.com/project/generated/listers/...           - generated
-#
-# This means, that usually the generated files are both colocated directly
-# with the package (for zz_generated.deepcopy.go, generated by deepcopy)
-# and have their own package (for files generated by clientset, informers,
-# listers).
-#
-# Most importantly, however, multiple Go packages (in the above example,
-# goodbye/v1 and hello/v1) are used to in turn generate multiple output Go
-# packages. This proves problematic when generating code for Bazel, as we have
-# to consume the result of code generation from multiple Bazel targets (each
-# representing a different generated Go package).
-#
-# To handle this, we split up the code generation into four main steps. These
-# need to be manually instantiated by any use who wants to consume these rules.
-#
-#  1. Create a rules_go go_path that contains all hand-written API packages.
-#  2. Parse hand written API packages using go_kubernetes_resource_bundle, via
-#     the generated go_path. This prepares outputs (ie., 'runs' generators),
-#     and creates an internal provider (KubeResourceBundle) that contains
-#     informations about generated Go packages.
-#  3. For every output package, create a go_kubernetes_library target,
-#     specifying the bundle from which it's supposed to be created, and which
-#     particular library from that bundle should be used.
-#  4. Next to every go_kubernetes_library, create a go_library with the same
-#     importpath which embeds the go_kubernetes_library. The split between
-#     go_kubernetes_library and go_library is required to let Gazelle know
-#     about the availability of given importpaths at given Bazel targets
-#     (unfortunately, it seems like Gazelle is unable to parse rules that emit
-#     the same providers as go_library does, instead requiring us to use a full
-#     go_library rule instead).
-#
-# Point 3. is somwhat different for the generated deepcopy code, which has to
-# live alongside (in the same importpath) as the hand-written API go_library,
-# and needs to be embedded into that. Special care has to be taken to not cause
-# a cycle (handwritten API -> go_path -> bundle -> kubernetes_library ->
-# go_library) in this case. This is done via a select() which selectively
-# enables the inclusion of the generated deepcopy code within the hand-written
-# API library, only enabling it for the target build, not the preprocessing
-# done by go_kubernetes_resource_bundle.
-#
-# Or, in graphical form:
-#
-#   .------------.   .------------.
-#   | go_library |   | go_library |
-#   |------------|   |------------|
-#   | goodbye/v1 |   | hello/v2   |
-#   '------------'   '------------'
-#        '------. .--------'
-#           .---------.
-#           | go_path |
-#           '---------'
-#                | (preprocessing transition)
-#  .-------------------------------.
-#  | go_kubernetes_resource_bundle |
-#  '-------------------------------'
-#              |   '--------------------------.
-#  .---------------------------.  .--------------------------.
-#  |   go_kubernetes_library   |  |   go_kubernetes_library  |
-#  |---------------------------|  |--------------------------| ... others ...
-#  | clientset/verioned/typed  |  | clientset/verioned/fake  |
-#  '---------------------------'  '--------------------------'
-#              |                               |
-#  .---------------------------.  .--------------------------.
-#  |       go_library          |  |       go_library         |
-#  |---------------------------|  |--------------------------| ... others ...
-#  | clientset/versioned/typed |  | clientset/versioned/fake |
-#  '---------------------------'  '--------------------------'
-#
-
-load("@io_bazel_rules_go//go:def.bzl", "GoLibrary", "GoPath", "go_context")
-
-def _preprocessing_transition_impl(settings, attr):
-    return {"//metropolis/build/kube-code-generator:preprocessing": "yes"}
-
-# preprocessing_transition is attached to the incoming go_path in
-# go_kubernetes_resource_bundle, unsets the
-# //metropolis/build/kube-code-generator:embed_deepcopy config setting.
-# This allows go_libraries that make up the handwritten API libraries to only
-# embed the generated deepcopy when they are pulled in for build reasons, not
-# when the graph is being traversed in order to generate the deepcopy itself.
-# This breaks up the cycle that would happen otherwise.
-preprocessing_transition = transition(
-    implementation = _preprocessing_transition_impl,
-    inputs = [],
-    outputs = ["//metropolis/build/kube-code-generator:preprocessing"],
-)
-
-# KubeResourceBundle is emitted by go_kubernetes_resource_bundle and contains
-# informations about libraries generated by the kubernetes code-generators.
-KubeResourceBundle = provider(
-    "Information about the generated Go sources of a k8s.io/code-generator-built library.",
-    fields = {
-        "libraries": "Map from Go importpath to list of Files that make up this importpath.",
-    },
-)
-
-def _go_kubernetes_library_impl(ctx):
-    go = go_context(ctx)
-    bundle = ctx.attr.bundle[KubeResourceBundle]
-    libraries = bundle.libraries
-
-    found_importpaths = [l.importpath for l in libraries]
-
-    libraries = [l for l in libraries if l.importpath == ctx.attr.importpath]
-    if len(libraries) < 1:
-        fail("importpath {} not found in bundle (have {})".format(ctx.attr.importpath, ", ".join(found_importpaths)))
-    if len(libraries) > 1:
-        fail("internal error: multiple libraries with importpath {} found in bundle".format(ctx.attr.importpath))
-    library = libraries[0]
-
-    source = go.library_to_source(go, ctx.attr, library, ctx.coverage_instrumented())
-    return [library, source, OutputGroupInfo(go_generated_srcs = depset(library.srcs))]
-
-# go_kubernetes_library picks a single Go library from a kube_resource_bundle
-# and prepares it for being embedded into a go_library.
-go_kubernetes_library = rule(
-    implementation = _go_kubernetes_library_impl,
-    attrs = {
-        "bundle": attr.label(
-            mandatory = True,
-            providers = [KubeResourceBundle],
-            doc = "A go_kubernetes_resource_bundle that contains the result of a kubernetes code-generation run.",
-        ),
-        "importpath": attr.string(
-            mandatory = True,
-            doc = "The importpath of the library picked from the bundle, same as the importpath of the go_library that embeds it.",
-        ),
-        "deps": attr.label_list(
-            providers = [GoLibrary],
-            doc = "All build dependencies of this library.",
-        ),
-        "_go_context_data": attr.label(
-            default = "@io_bazel_rules_go//:go_context_data",
-        ),
-    },
-    toolchains = ["@io_bazel_rules_go//go:toolchain"],
-)
-
-# _gotool_run is a helper function which runs an executable under
-# //osbase/build/gotoolwrap, effectively setting up everything required to
-# use standard Go tooling on the monogon workspace (ie. GOPATH/GOROOT). This is
-# required by generators to run 'go fmt'.
-#
-# The optional `copyout` argument configures which files to copy out from the
-# GOPATH into declared files. The contents of the list should be strings that
-# refer to a file within GOPATH/src, eg. source.monogon.dev/foo/bar/baz.go. The
-# files is then going to be copied into the path corresponding to a declared
-# file of the same basename.
-def _gotool_run(cg, executable, arguments, copyout=[], **kwargs):
-    ctx = cg.ctx
-    go = go_context(ctx)
-    gopath = ctx.attr.gopath[0][GoPath]
-
-    inputs = [
-        gopath.gopath_file,
-    ] + kwargs.get("inputs", [])
-
-    tools = [
-        executable,
-        go.sdk.go,
-    ] + go.sdk.srcs + go.sdk.tools + kwargs.get("tools", [])
-
-    copyout_env = []
-    for path in copyout:
-        to = cg.output_root + "/" + path
-        copyout_env.append("{}:{}".format(path, to))
-
-    env = {
-        "GOTOOLWRAP_GOPATH": gopath.gopath_file.path,
-        "GOTOOLWRAP_GOROOT": go.sdk.root_file.dirname,
-        "GOTOOLWRAP_COPYOUT": ';'.join(copyout_env),
-    }
-    env.update(kwargs.get("env", {}))
-
-    kwargs_ = dict([(k, v) for (k, v) in kwargs.items() if k not in [
-        "executable",
-        "arguments",
-        "inputs",
-        "env",
-        "tools",
-    ]])
-
-    ctx.actions.run(
-        executable = ctx.executable._gotoolwrap,
-        arguments = [executable.path] + arguments,
-        env = env,
-        inputs = inputs,
-        tools = tools,
-        **kwargs_
-    )
-
-# _output_directory returns the relative path into which
-# ctx.action.declare_file writes are rooted. This is used as code-generators
-# require a root path for all outputted files, instead of a list of files to
-# emit.
-def _output_directory(ctx):
-    # We combine bin_dir, the BUILDfile path and the target name. This seems
-    # wrong. Is there no simpler way to do this?
-    buildfile_path = ctx.build_file_path
-    parts = buildfile_path.split("/")
-    if not parts[-1].startswith("BUILD"):
-        fail("internal error: unexpected BUILD file path: {}", parts[-1])
-    package_path = "/".join(parts[:-1])
-    return "/".join([ctx.bin_dir.path, package_path, ctx.attr.name])
-
-# _cg returns a 'codegen context', a struct that's used to accumulate the
-# results of code generation. It assumes all output will be rooted in a
-# generated importpath (with more 'shortened' importpaths underneath the root),
-# and collects outputs to pass to the codegen execution action. It also
-# collects a map of importpaths to outputs that make it up.
-def _cg(ctx, importpath):
-    output_root = _output_directory(ctx)
-
-    return struct(
-        # The 'root' importpath, under which 'shortened' importpaths reside.
-        importpath = importpath,
-        # The prefix into which all files will be emitted. We use the target
-        # name for convenience.
-        output_prefix = ctx.attr.name,
-        # The full relative path visible to the codegen, pointing to the same
-        # directory as output_prefix (just from the point of view of the
-        # runtime filesystem, not the ctx.actions filepath declaration API).
-        output_root = output_root,
-        # The list of outputs that have to be generated by the codegen.
-        outputs = [],
-        # List of files compatible with the `copyout` argument of _gotool_run.
-        # Populated by calls to _declare_library. Should be used when the tool
-        # generates files within the GOPATH instead of directly at declared
-        # output file locations.
-        copyout = [],
-        # A map of importpath to list of outputs (from the above list) that
-        # make up a generated Go package/library.
-        libraries = {},
-        ctx = ctx,
-    )
-
-# _declare_library adds a single Go package/library at importpath to the
-# codegen context with the given file paths (rooted in the importpath).
-def _declare_library(cg, importpath, files):
-    importpath = cg.importpath + "/" + importpath
-    cg.libraries[importpath] = []
-    for f in files:
-        output = cg.ctx.actions.declare_file("{}/{}/{}".format(
-            cg.output_prefix,
-            importpath,
-            f,
-        ))
-        cg.outputs.append(output)
-        cg.libraries[importpath].append(output)
-        cg.copyout.append("{}/{}".format(importpath, f))
-
-# _declare_libraries declares multiple Go package/libraries to the codegen
-# context. The key of the dictionary is the importpath of the library, and the
-# value are the file names of generated outputs.
-def _declare_libraries(cg, libraries):
-    for k, v in libraries.items():
-        _declare_library(cg, k, v)
-
-# _codegen_clientset runs the clientset codegenerator.
-def _codegen_clientset(ctx):
-    cg = _cg(ctx, ctx.attr.importpath)
-
-    _declare_libraries(cg, {
-        "clientset/versioned": ["clientset.go"],
-        "clientset/versioned/fake": ["register.go", "clientset_generated.go"],
-        "clientset/versioned/scheme": ["register.go", "doc.go"],
-    })
-
-    for api, types in ctx.attr.apis.items():
-        client_name = api.split("/")[-2]
-        _declare_libraries(cg, {
-            "clientset/versioned/typed/{}".format(api): [
-                "doc.go",
-                "generated_expansion.go",
-                "{}_client.go".format(client_name),
-            ] + [
-                "{}.go".format(t)
-                for t in types
-            ],
-            "clientset/versioned/typed/{}/fake".format(api): [
-                "doc.go",
-                "fake_{}_client.go".format(client_name),
-            ],
-        })
-
-    _gotool_run(
-        cg,
-        mnemonic = "ClientsetGen",
-        executable = ctx.executable._client_gen,
-        arguments = [
-            "--clientset-name",
-            "versioned",
-            "--input-base",
-            ctx.attr.apipath,
-            "--input",
-            ",".join(ctx.attr.apis),
-            "--output-pkg",
-            cg.importpath + "/clientset",
-            "--output-dir",
-            cg.output_root + "/" + cg.importpath + "/clientset",
-            "--go-header-file",
-            ctx.file.boilerplate.path,
-        ],
-        inputs = [
-            ctx.file.boilerplate,
-        ],
-        outputs = cg.outputs,
-    )
-
-    return cg.libraries
-
-# _codegen_deepcopy runs the deepcopy codegenerator (outputting to the apipath,
-# not the importpath).
-def _codegen_deepcopy(ctx):
-    cg = _cg(ctx, ctx.attr.apipath)
-
-    for api, types in ctx.attr.apis.items():
-        _declare_libraries(cg, {
-            api: ["zz_generated.deepcopy.go"],
-        })
-
-    _gotool_run(
-        cg,
-        copyout = cg.copyout,
-        mnemonic = "DeepcopyGen",
-        executable = ctx.executable._deepcopy_gen,
-        arguments = [
-            "--go-header-file",
-            ctx.file.boilerplate.path,
-            "--stderrthreshold",
-            "0",
-            "--output-file",
-            "zz_generated.deepcopy.go",
-        ] + ["{}/{}".format(ctx.attr.apipath, api) for api in ctx.attr.apis],
-        inputs = [
-            ctx.file.boilerplate,
-        ],
-        outputs = cg.outputs,
-    )
-    return cg.libraries
-
-# _codegen_informer runs the informer codegenerator.
-def _codegen_informer(ctx):
-    cg = _cg(ctx, ctx.attr.importpath)
-
-    _declare_libraries(cg, {
-        "informers/externalversions": ["factory.go", "generic.go"],
-        "informers/externalversions/internalinterfaces": ["factory_interfaces.go"],
-    })
-
-    for api, types in ctx.attr.apis.items():
-        client_name = api.split("/")[-2]
-        _declare_libraries(cg, {
-            "informers/externalversions/{}".format(client_name): ["interface.go"],
-            "informers/externalversions/{}".format(api): [
-                "interface.go",
-            ] + [
-                "{}.go".format(t)
-                for t in types
-            ],
-        })
-
-    _gotool_run(
-        cg,
-        mnemonic = "InformerGen",
-        executable = ctx.executable._informer_gen,
-        arguments = [
-            "--versioned-clientset-package",
-            "{}/clientset/versioned".format(ctx.attr.importpath),
-            "--listers-package",
-            "{}/listers".format(ctx.attr.importpath),
-            "--output-pkg",
-            "{}/informers".format(ctx.attr.importpath),
-            "--output-dir",
-            cg.output_root + "/" + cg.importpath + "/informers",
-            "--go-header-file",
-            ctx.file.boilerplate.path,
-        ] + ["{}/{}".format(ctx.attr.apipath, api) for api in ctx.attr.apis],
-        inputs = [
-            ctx.file.boilerplate,
-        ],
-        outputs = cg.outputs,
-    )
-
-    return cg.libraries
-
-# _codegen_lister runs the lister codegenerator.
-def _codegen_lister(ctx):
-    cg = _cg(ctx, ctx.attr.importpath)
-
-    for api, types in ctx.attr.apis.items():
-        client_name = api.split("/")[-2]
-        _declare_libraries(cg, {
-            "listers/{}".format(api): [
-                "expansion_generated.go",
-            ] + [
-                "{}.go".format(t)
-                for t in types
-            ],
-        })
-
-    _gotool_run(
-        cg,
-        mnemonic = "ListerGen",
-        executable = ctx.executable._lister_gen,
-        arguments = [
-            "--output-pkg",
-            "{}/listers".format(ctx.attr.importpath),
-            "--output-dir",
-            cg.output_root + "/" + cg.importpath + "/listers",
-            "--go-header-file",
-            ctx.file.boilerplate.path,
-            "-v",
-            "10",
-        ] + ["{}/{}".format(ctx.attr.apipath, api) for api in ctx.attr.apis],
-        inputs = [
-            ctx.file.boilerplate,
-        ],
-        outputs = cg.outputs,
-    )
-
-    return cg.libraries
-
-# _update_dict_check is a helper function that updates dict a with dict b,
-# ensuring there's no overwritten keys.
-def _update_dict_check(a, b):
-    for k in b.keys():
-        if k in a:
-            fail("internal error: repeat importpath {}", k)
-    a.update(b)
-
-def _go_kubernetes_resource_bundle_impl(ctx):
-    go = go_context(ctx)
-
-    all_gens = {}
-    _update_dict_check(all_gens, _codegen_clientset(ctx))
-    _update_dict_check(all_gens, _codegen_deepcopy(ctx))
-    _update_dict_check(all_gens, _codegen_informer(ctx))
-    _update_dict_check(all_gens, _codegen_lister(ctx))
-
-    libraries = []
-    for importpath, srcs in all_gens.items():
-        library = go.new_library(
-            go,
-            srcs = srcs,
-            importpath = importpath,
-        )
-        libraries.append(library)
-
-    return [KubeResourceBundle(libraries = libraries)]
-
-# go_kubernetes_resource_bundle runs kubernetes code-generators on a codepath
-# for some requested APIs, and whose output can be made into Go library targets
-# via go_kubernetes_library. This bundle corresponds to a single Kubernetes API
-# resource group.
-go_kubernetes_resource_bundle = rule(
-    implementation = _go_kubernetes_resource_bundle_impl,
-    attrs = {
-        "gopath": attr.label(
-            mandatory = True,
-            providers = [GoPath],
-            cfg = preprocessing_transition,
-            doc = "A rules_go go_path that contains all the API libraries for which codegen should be run.",
-        ),
-        "importpath": attr.string(
-            mandatory = True,
-            doc = """
-                The root importpath of the generated code (apart from deepcopy
-                codegen). The Bazel target path corresponding to this
-                importpath needs to contain the go_kubernetes_library and
-                go_library targets that allow to actually build against the
-                generated code.
-            """,
-        ),
-        "apipath": attr.string(
-            mandatory = True,
-            doc = "The root importpath of the APIs for which to generate code.",
-        ),
-        "apis": attr.string_list_dict(
-            mandatory = True,
-            doc = """
-                The APIs underneath importpath for which to generated code,
-                eg. foo/v1, mapping into a list of lowercased types generated
-                from each (eg. widget for `type Widget struct`).
-            """,
-        ),
-        "boilerplate": attr.label(
-            default = "//metropolis/build/kube-code-generator:boilerplate.go.txt",
-            allow_single_file = True,
-            doc = "Header that will be used in the generated code.",
-        ),
-        "_go_context_data": attr.label(
-            default = "@io_bazel_rules_go//:go_context_data",
-        ),
-        "_gotoolwrap": attr.label(
-            default = Label("//build/gotoolwrap"),
-            allow_single_file = True,
-            executable = True,
-            cfg = "exec",
-        ),
-        "_deepcopy_gen": attr.label(
-            default = Label("@io_k8s_code_generator//cmd/deepcopy-gen"),
-            allow_single_file = True,
-            executable = True,
-            cfg = "exec",
-        ),
-        "_client_gen": attr.label(
-            default = Label("@io_k8s_code_generator//cmd/client-gen"),
-            allow_single_file = True,
-            executable = True,
-            cfg = "exec",
-        ),
-        "_informer_gen": attr.label(
-            default = Label("@io_k8s_code_generator//cmd/informer-gen"),
-            allow_single_file = True,
-            executable = True,
-            cfg = "exec",
-        ),
-        "_lister_gen": attr.label(
-            default = Label("@io_k8s_code_generator//cmd/lister-gen"),
-            allow_single_file = True,
-            executable = True,
-            cfg = "exec",
-        ),
-        "_allowlist_function_transition": attr.label(
-            default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
-        ),
-    },
-    toolchains = ["@io_bazel_rules_go//go:toolchain"],
-)
diff --git a/metropolis/build/kube-code-generator/tools.go b/metropolis/build/kube-code-generator/tools.go
deleted file mode 100644
index d688f57..0000000
--- a/metropolis/build/kube-code-generator/tools.go
+++ /dev/null
@@ -1,11 +0,0 @@
-//go:build tools
-// +build tools
-
-package kube_code_generator
-
-import (
-	_ "k8s.io/code-generator/cmd/client-gen"
-	_ "k8s.io/code-generator/cmd/deepcopy-gen"
-	_ "k8s.io/code-generator/cmd/informer-gen"
-	_ "k8s.io/code-generator/cmd/lister-gen"
-)
diff --git a/metropolis/test/e2e/BUILD.bazel b/metropolis/test/e2e/BUILD.bazel
index 565223e..ecbdc97 100644
--- a/metropolis/test/e2e/BUILD.bazel
+++ b/metropolis/test/e2e/BUILD.bazel
@@ -5,7 +5,6 @@
     images = [
         "//metropolis/test/e2e/selftest:selftest_image",
         "//metropolis/test/e2e/httpserver:httpserver_image",
-        "//metropolis/vm/smoketest:smoketest_image",
     ],
     visibility = [
         "//metropolis/test/e2e/suites:__subpackages__",
diff --git a/metropolis/vm/BUILD.bazel b/metropolis/vm/BUILD.bazel
deleted file mode 100644
index e69de29..0000000
--- a/metropolis/vm/BUILD.bazel
+++ /dev/null
diff --git a/metropolis/vm/kube/apis/vm/BUILD.bazel b/metropolis/vm/kube/apis/vm/BUILD.bazel
deleted file mode 100644
index 35fab05..0000000
--- a/metropolis/vm/kube/apis/vm/BUILD.bazel
+++ /dev/null
@@ -1,8 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-
-go_library(
-    name = "vm",
-    srcs = ["register.go"],
-    importpath = "source.monogon.dev/metropolis/vm/kube/apis/vm",
-    visibility = ["//visibility:public"],
-)
diff --git a/metropolis/vm/kube/apis/vm/register.go b/metropolis/vm/kube/apis/vm/register.go
deleted file mode 100644
index b2fe5f6..0000000
--- a/metropolis/vm/kube/apis/vm/register.go
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2020 The Monogon Project Authors.
-//
-// SPDX-License-Identifier: Apache-2.0
-//
-// 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 vm
-
-const (
-	GroupName = "vm.metropolis.monogon.dev"
-)
diff --git a/metropolis/vm/kube/apis/vm/v1alpha1/BUILD.bazel b/metropolis/vm/kube/apis/vm/v1alpha1/BUILD.bazel
deleted file mode 100644
index c520188..0000000
--- a/metropolis/vm/kube/apis/vm/v1alpha1/BUILD.bazel
+++ /dev/null
@@ -1,30 +0,0 @@
-load("//metropolis/build/kube-code-generator:defs.bzl", "go_kubernetes_library")
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-
-go_library(
-    name = "v1alpha1",
-    srcs = [
-        "doc.go",
-        "register.go",
-        "types.go",
-    ],
-    embed = select({
-        "//metropolis/build/kube-code-generator:embed_deepcopy": [":go_kubernetes_library"],
-        "//conditions:default": [],
-    }),  # keep
-    importpath = "source.monogon.dev/metropolis/vm/kube/apis/vm/v1alpha1",
-    visibility = ["//visibility:public"],
-    deps = [
-        "//metropolis/vm/kube/apis/vm",
-        "@io_k8s_api//core/v1:core",
-        "@io_k8s_apimachinery//pkg/apis/meta/v1:meta",
-        "@io_k8s_apimachinery//pkg/runtime",
-        "@io_k8s_apimachinery//pkg/runtime/schema",
-    ],
-)
-
-go_kubernetes_library(
-    name = "go_kubernetes_library",
-    bundle = "//metropolis/vm/kube/generated:bundle",
-    importpath = "source.monogon.dev/metropolis/vm/kube/apis/vm/v1alpha1",
-)
diff --git a/metropolis/vm/kube/apis/vm/v1alpha1/doc.go b/metropolis/vm/kube/apis/vm/v1alpha1/doc.go
deleted file mode 100644
index f0d6d3d..0000000
--- a/metropolis/vm/kube/apis/vm/v1alpha1/doc.go
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2020 The Monogon Project Authors.
-//
-// SPDX-License-Identifier: Apache-2.0
-//
-// 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.
-
-// +k8s:deepcopy-gen=package
-// +groupName=vm.metropolis.monogon.dev
-
-package v1alpha1
diff --git a/metropolis/vm/kube/apis/vm/v1alpha1/register.go b/metropolis/vm/kube/apis/vm/v1alpha1/register.go
deleted file mode 100644
index 4eebae5..0000000
--- a/metropolis/vm/kube/apis/vm/v1alpha1/register.go
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2020 The Monogon Project Authors.
-//
-// SPDX-License-Identifier: Apache-2.0
-//
-// 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 v1alpha1
-
-import (
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/runtime"
-	"k8s.io/apimachinery/pkg/runtime/schema"
-
-	"source.monogon.dev/metropolis/vm/kube/apis/vm"
-)
-
-// SchemeGroupVersion is group version used to register these objects
-var SchemeGroupVersion = schema.GroupVersion{Group: vm.GroupName, Version: "v1alpha1"}
-
-// Kind takes an unqualified kind and returns back a Group qualified GroupKind
-func Kind(kind string) schema.GroupKind {
-	return SchemeGroupVersion.WithKind(kind).GroupKind()
-}
-
-// Resource takes an unqualified resource and returns a Group qualified
-// GroupResource
-func Resource(resource string) schema.GroupResource {
-	return SchemeGroupVersion.WithResource(resource).GroupResource()
-}
-
-var (
-	// SchemeBuilder initializes a scheme builder
-	SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
-	// AddToScheme is a global function that registers this API group & version
-	// to a scheme
-	AddToScheme = SchemeBuilder.AddToScheme
-)
-
-// Adds the list of known types to Scheme.
-func addKnownTypes(scheme *runtime.Scheme) error {
-	scheme.AddKnownTypes(SchemeGroupVersion,
-		&VirtualMachine{},
-		&VirtualMachineList{},
-	)
-	metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
-	return nil
-}
diff --git a/metropolis/vm/kube/apis/vm/v1alpha1/types.go b/metropolis/vm/kube/apis/vm/v1alpha1/types.go
deleted file mode 100644
index 365dda2..0000000
--- a/metropolis/vm/kube/apis/vm/v1alpha1/types.go
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2020 The Monogon Project Authors.
-//
-// SPDX-License-Identifier: Apache-2.0
-//
-// 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 v1alpha1
-
-import (
-	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-)
-
-// +genclient
-// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
-
-type VirtualMachine struct {
-	metav1.TypeMeta   `json:",inline"`
-	metav1.ObjectMeta `json:"metadata,omitempty"`
-
-	Spec   VirtualMachineSpec   `json:"spec"`
-	Status VirtualMachineStatus `json:"status"`
-}
-
-type VirtualMachineSpec struct {
-	// TODO(lorenz): document
-	InitialImage VirtualMachineImage `json:"initialImage"`
-
-	// HypervsisorImage determines what OCI image will run within the pod. This
-	// image must communicate using the Metropolis VM hypervisor API and run
-	// the actual VM monitor (eg. qemu).
-	// Defaults to "" (default image for cluster).
-	// +optional
-	HypervisorImage string
-
-	// Resources are the resources assigned to the pod backing this virtual
-	// machine when running. Non-integer CPU requests and overcommit will
-	// result in reduced side-channel attack resistance as CPUs will not be
-	// statically assigned
-	// +optional
-	Resources corev1.ResourceRequirements `json:"resources,omitempty"`
-
-	// VolumeClaimTemplate is used to produce a PersistentVolumeClaim that will
-	// be attached to the pod backing this virtual machine when running.
-	VolumeClaimTemplate corev1.PersistentVolumeClaim `json:"volumeClaimTemplate,omitempty"`
-
-	// VirtualMachineIPs is a list of IP addresses (in CIDR prefix notation)
-	// that the virtual machine will receive traffic for when running. These
-	// can be either IPv4 or IPv6 addresses.
-	VirtualMachineIPs []string `json:"vmIPs"`
-
-	// MigrateStrategy determines what migration strategy is used when the
-	// virtual machine needs to be migrated across hosts.
-	// Defaults to "Live".
-	// +optional
-	// +default="Live"
-	MigrateStrategy VirtualMachineMigrateStrategy `json:"migrateStrategy"`
-
-	// ExecutionMode determines whether the machine is running or paused.
-	// Defaults to "Run".
-	// +optional
-	// +default="Run"
-	ExecutionMode VirtualMachineExecutionMode `json:"mode"`
-}
-
-type VirtualMachineImage struct {
-	// TODO(lorenz): document
-	// +optional
-	URL string `json:"url"`
-}
-
-type VirtualMachineMigrateStrategy string
-
-const (
-	VirtualMachineColdMigrate VirtualMachineMigrateStrategy = "Cold"
-	VirtualMachineLiveMigrate VirtualMachineMigrateStrategy = "Live"
-)
-
-type VirtualMachineExecutionMode string
-
-const (
-	VirtualMachineRun   VirtualMachineExecutionMode = "Run"
-	VirtualMachinePause VirtualMachineExecutionMode = "Pause"
-)
-
-type VirtualMachineStatus struct {
-	Phase           VirtualMachinePhase `json:"phase"`
-	ActivePodName   string              `json:"activePodName"`
-	VolumeClaimName string              `json:"volumeClaimName"`
-}
-
-type VirtualMachinePhase string
-
-const (
-	VirtualMachinePhaseCreating    VirtualMachinePhase = "Creating"
-	VirtualMachinePhaseRunning     VirtualMachinePhase = "Running"
-	VirtualMachinePhaseTerminating VirtualMachinePhase = "Terminating"
-	VirtualMachinePhaseStopped     VirtualMachinePhase = "Stopped"
-	VirtualMachinePhaseLost        VirtualMachinePhase = "Lost"
-)
-
-// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
-
-type VirtualMachineList struct {
-	metav1.TypeMeta   `json:",inline"`
-	metav1.ObjectMeta `json:"metadata,omitempty"`
-
-	Items []VirtualMachine `json:"items"`
-}
diff --git a/metropolis/vm/kube/generated/BUILD.bazel b/metropolis/vm/kube/generated/BUILD.bazel
deleted file mode 100644
index 7e4d8f3..0000000
--- a/metropolis/vm/kube/generated/BUILD.bazel
+++ /dev/null
@@ -1,20 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_path")
-load("//metropolis/build/kube-code-generator:defs.bzl", "go_kubernetes_resource_bundle")
-
-go_kubernetes_resource_bundle(
-    name = "bundle",
-    apipath = "source.monogon.dev/metropolis/vm/kube/apis",
-    apis = {
-        "vm/v1alpha1": ["virtualmachine"],
-    },
-    gopath = ":go_path",
-    importpath = "source.monogon.dev/metropolis/vm/kube/generated",
-    visibility = ["//metropolis/vm/kube:__subpackages__"],
-)
-
-go_path(
-    name = "go_path",
-    deps = [
-        "//metropolis/vm/kube/apis/vm/v1alpha1",
-    ],
-)
diff --git a/metropolis/vm/kube/generated/clientset/versioned/BUILD.bazel b/metropolis/vm/kube/generated/clientset/versioned/BUILD.bazel
deleted file mode 100644
index 6e21803..0000000
--- a/metropolis/vm/kube/generated/clientset/versioned/BUILD.bazel
+++ /dev/null
@@ -1,21 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-load("//metropolis/build/kube-code-generator:defs.bzl", "go_kubernetes_library")
-
-# keep
-go_library(
-    name = "go_default_library",
-    embed = [":go_kubernetes_library"],
-    visibility = ["//metropolis/vm:__subpackages__"],
-)
-
-go_kubernetes_library(
-    name = "go_kubernetes_library",
-    bundle = "//metropolis/vm/kube/generated:bundle",
-    importpath = "source.monogon.dev/metropolis/vm/kube/generated/clientset/versioned",
-    deps = [
-        "//metropolis/vm/kube/generated/clientset/versioned/typed/vm/v1alpha1:go_default_library",
-        "@io_k8s_client_go//discovery:go_default_library",
-        "@io_k8s_client_go//rest:go_default_library",
-        "@io_k8s_client_go//util/flowcontrol:go_default_library",
-    ],
-)
diff --git a/metropolis/vm/kube/generated/clientset/versioned/scheme/BUILD.bazel b/metropolis/vm/kube/generated/clientset/versioned/scheme/BUILD.bazel
deleted file mode 100644
index 1d2daa7..0000000
--- a/metropolis/vm/kube/generated/clientset/versioned/scheme/BUILD.bazel
+++ /dev/null
@@ -1,23 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-load("//metropolis/build/kube-code-generator:defs.bzl", "go_kubernetes_library")
-
-# keep
-go_library(
-    name = "go_default_library",
-    embed = [":go_kubernetes_library"],
-    visibility = ["//metropolis/vm:__subpackages__"],
-)
-
-go_kubernetes_library(
-    name = "go_kubernetes_library",
-    bundle = "//metropolis/vm/kube/generated:bundle",
-    importpath = "source.monogon.dev/metropolis/vm/kube/generated/clientset/versioned/scheme",
-    deps = [
-        "//metropolis/vm/kube/apis/vm/v1alpha1",
-        "@io_k8s_apimachinery//pkg/apis/meta/v1:meta",
-        "@io_k8s_apimachinery//pkg/runtime",
-        "@io_k8s_apimachinery//pkg/runtime/schema",
-        "@io_k8s_apimachinery//pkg/runtime/serializer",
-        "@io_k8s_apimachinery//pkg/util/runtime",
-    ],
-)
diff --git a/metropolis/vm/kube/generated/clientset/versioned/typed/vm/v1alpha1/BUILD.bazel b/metropolis/vm/kube/generated/clientset/versioned/typed/vm/v1alpha1/BUILD.bazel
deleted file mode 100644
index c69cec5..0000000
--- a/metropolis/vm/kube/generated/clientset/versioned/typed/vm/v1alpha1/BUILD.bazel
+++ /dev/null
@@ -1,24 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-load("//metropolis/build/kube-code-generator:defs.bzl", "go_kubernetes_library")
-
-# keep
-go_library(
-    name = "go_default_library",
-    embed = [":go_kubernetes_library"],
-    visibility = ["//metropolis/vm:__subpackages__"],
-)
-
-go_kubernetes_library(
-    name = "go_kubernetes_library",
-    bundle = "//metropolis/vm/kube/generated:bundle",
-    importpath = "source.monogon.dev/metropolis/vm/kube/generated/clientset/versioned/typed/vm/v1alpha1",
-    deps = [
-        "//metropolis/vm/kube/apis/vm/v1alpha1",
-        "//metropolis/vm/kube/generated/clientset/versioned/scheme:go_default_library",
-        "@io_k8s_apimachinery//pkg/apis/meta/v1:meta",
-        "@io_k8s_apimachinery//pkg/types",
-        "@io_k8s_apimachinery//pkg/watch",
-        "@io_k8s_client_go//gentype",
-        "@io_k8s_client_go//rest",
-    ],
-)
diff --git a/metropolis/vm/kube/generated/informers/externalversions/BUILD.bazel b/metropolis/vm/kube/generated/informers/externalversions/BUILD.bazel
deleted file mode 100644
index 78516e0..0000000
--- a/metropolis/vm/kube/generated/informers/externalversions/BUILD.bazel
+++ /dev/null
@@ -1,25 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-load("//metropolis/build/kube-code-generator:defs.bzl", "go_kubernetes_library")
-
-# keep
-go_library(
-    name = "go_default_library",
-    embed = [":go_kubernetes_library"],
-    visibility = ["//metropolis/vm:__subpackages__"],
-)
-
-go_kubernetes_library(
-    name = "go_kubernetes_library",
-    bundle = "//metropolis/vm/kube/generated:bundle",
-    importpath = "source.monogon.dev/metropolis/vm/kube/generated/informers/externalversions",
-    deps = [
-        "//metropolis/vm/kube/apis/vm/v1alpha1",
-        "//metropolis/vm/kube/generated/clientset/versioned:go_default_library",
-        "//metropolis/vm/kube/generated/informers/externalversions/internalinterfaces:go_default_library",
-        "//metropolis/vm/kube/generated/informers/externalversions/vm:go_default_library",
-        "@io_k8s_apimachinery//pkg/apis/meta/v1:meta",
-        "@io_k8s_apimachinery//pkg/runtime",
-        "@io_k8s_apimachinery//pkg/runtime/schema",
-        "@io_k8s_client_go//tools/cache",
-    ],
-)
diff --git a/metropolis/vm/kube/generated/informers/externalversions/internalinterfaces/BUILD.bazel b/metropolis/vm/kube/generated/informers/externalversions/internalinterfaces/BUILD.bazel
deleted file mode 100644
index da30152..0000000
--- a/metropolis/vm/kube/generated/informers/externalversions/internalinterfaces/BUILD.bazel
+++ /dev/null
@@ -1,21 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-load("//metropolis/build/kube-code-generator:defs.bzl", "go_kubernetes_library")
-
-# keep
-go_library(
-    name = "go_default_library",
-    embed = [":go_kubernetes_library"],
-    visibility = ["//metropolis/vm:__subpackages__"],
-)
-
-go_kubernetes_library(
-    name = "go_kubernetes_library",
-    bundle = "//metropolis/vm/kube/generated:bundle",
-    importpath = "source.monogon.dev/metropolis/vm/kube/generated/informers/externalversions/internalinterfaces",
-    deps = [
-        "//metropolis/vm/kube/generated/clientset/versioned:go_default_library",
-        "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
-        "@io_k8s_apimachinery//pkg/runtime:go_default_library",
-        "@io_k8s_client_go//tools/cache:go_default_library",
-    ],
-)
diff --git a/metropolis/vm/kube/generated/informers/externalversions/vm/BUILD.bazel b/metropolis/vm/kube/generated/informers/externalversions/vm/BUILD.bazel
deleted file mode 100644
index c336d96..0000000
--- a/metropolis/vm/kube/generated/informers/externalversions/vm/BUILD.bazel
+++ /dev/null
@@ -1,19 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-load("//metropolis/build/kube-code-generator:defs.bzl", "go_kubernetes_library")
-
-# keep
-go_library(
-    name = "go_default_library",
-    embed = [":go_kubernetes_library"],
-    visibility = ["//metropolis/vm:__subpackages__"],
-)
-
-go_kubernetes_library(
-    name = "go_kubernetes_library",
-    bundle = "//metropolis/vm/kube/generated:bundle",
-    importpath = "source.monogon.dev/metropolis/vm/kube/generated/informers/externalversions/vm",
-    deps = [
-        "//metropolis/vm/kube/generated/informers/externalversions/internalinterfaces:go_default_library",
-        "//metropolis/vm/kube/generated/informers/externalversions/vm/v1alpha1:go_default_library",
-    ],
-)
diff --git a/metropolis/vm/kube/generated/informers/externalversions/vm/v1alpha1/BUILD.bazel b/metropolis/vm/kube/generated/informers/externalversions/vm/v1alpha1/BUILD.bazel
deleted file mode 100644
index d51c170..0000000
--- a/metropolis/vm/kube/generated/informers/externalversions/vm/v1alpha1/BUILD.bazel
+++ /dev/null
@@ -1,25 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-load("//metropolis/build/kube-code-generator:defs.bzl", "go_kubernetes_library")
-
-# keep
-go_library(
-    name = "go_default_library",
-    embed = [":go_kubernetes_library"],
-    visibility = ["//metropolis/vm:__subpackages__"],
-)
-
-go_kubernetes_library(
-    name = "go_kubernetes_library",
-    bundle = "//metropolis/vm/kube/generated:bundle",
-    importpath = "source.monogon.dev/metropolis/vm/kube/generated/informers/externalversions/vm/v1alpha1",
-    deps = [
-        "//metropolis/vm/kube/apis/vm/v1alpha1",
-        "//metropolis/vm/kube/generated/clientset/versioned:go_default_library",
-        "//metropolis/vm/kube/generated/informers/externalversions/internalinterfaces:go_default_library",
-        "//metropolis/vm/kube/generated/listers/vm/v1alpha1:go_default_library",
-        "@io_k8s_apimachinery//pkg/apis/meta/v1:go_default_library",
-        "@io_k8s_apimachinery//pkg/runtime:go_default_library",
-        "@io_k8s_apimachinery//pkg/watch:go_default_library",
-        "@io_k8s_client_go//tools/cache:go_default_library",
-    ],
-)
diff --git a/metropolis/vm/kube/generated/listers/vm/v1alpha1/BUILD.bazel b/metropolis/vm/kube/generated/listers/vm/v1alpha1/BUILD.bazel
deleted file mode 100644
index 34b5aa6..0000000
--- a/metropolis/vm/kube/generated/listers/vm/v1alpha1/BUILD.bazel
+++ /dev/null
@@ -1,22 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-load("//metropolis/build/kube-code-generator:defs.bzl", "go_kubernetes_library")
-
-# keep
-go_library(
-    name = "go_default_library",
-    embed = [":go_kubernetes_library"],
-    visibility = ["//metropolis/vm:__subpackages__"],
-)
-
-go_kubernetes_library(
-    name = "go_kubernetes_library",
-    bundle = "//metropolis/vm/kube/generated:bundle",
-    importpath = "source.monogon.dev/metropolis/vm/kube/generated/listers/vm/v1alpha1",
-    deps = [
-        "//metropolis/vm/kube/apis/vm/v1alpha1",
-        "@io_k8s_apimachinery//pkg/api/errors",
-        "@io_k8s_apimachinery//pkg/labels",
-        "@io_k8s_client_go//listers",
-        "@io_k8s_client_go//tools/cache",
-    ],
-)
diff --git a/metropolis/vm/proto/BUILD.bazel b/metropolis/vm/proto/BUILD.bazel
deleted file mode 100644
index be0ceec..0000000
--- a/metropolis/vm/proto/BUILD.bazel
+++ /dev/null
@@ -1,24 +0,0 @@
-load("@rules_proto//proto:defs.bzl", "proto_library")
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
-
-proto_library(
-    name = "metropolis_vm_proto",
-    srcs = ["vm.proto"],
-    visibility = ["//visibility:public"],
-)
-
-go_proto_library(
-    name = "metropolis_vm_go_proto",
-    compilers = ["@io_bazel_rules_go//proto:go_grpc"],
-    importpath = "source.monogon.dev/metropolis/vm/proto",
-    proto = ":metropolis_vm_proto",
-    visibility = ["//visibility:public"],
-)
-
-go_library(
-    name = "proto",
-    embed = [":metropolis_vm_go_proto"],
-    importpath = "source.monogon.dev/metropolis/vm/proto",
-    visibility = ["//visibility:public"],
-)
diff --git a/metropolis/vm/proto/vm.proto b/metropolis/vm/proto/vm.proto
deleted file mode 100644
index 3cac9f1..0000000
--- a/metropolis/vm/proto/vm.proto
+++ /dev/null
@@ -1,299 +0,0 @@
-syntax = "proto3";
-
-package metropolis.proto.vm;
-
-// VMSpec fully defines all information about a VM and is consumed by the VM
-// hypervisor through a runtime environment variable.
-message VMSpec {
-  // Name field from Kubernetes VirtualMachine object.
-  string name = 1;
-  // Namespace of VM object
-  string namespace = 2;
-
-  enum StartMode {
-    SM_UNKNOWN = 0;
-    // Normal VM start
-    SM_RUN = 1;
-    // Initialize the disk of the new VM according to `initial_image` and start
-    // the VM
-    SM_PREPARE_IMAGE = 2;
-    // Wait for an incoming migration and start the migrated VM
-    SM_INCOMING_MIGRATION = 3;
-  }
-  StartMode mode = 3;
-  // Reference initial data which is copied to the root block device before
-  // starting the VM for the first time. Only used if starting with
-  // SM_PREPARE_IMAGE.
-  InitialImage initial_image = 4;
-  // Set of IP addresses assigned to the VM. Populated from vmIPs in the
-  // VirtualMachine object. Currently a maximum of one IP per IP protocol
-  // version is supported.
-  repeated string address = 5;
-  // gRPC endpoint of the controller for this VM
-  string controller_endpoint = 6;
-  // Lease mode used for the VM. See LeaseMode for additional info.
-  LeaseMode lease_mode = 7;
-}
-
-// InitialImage represents a source from which a new VM root block device can be
-// instantiated.
-message InitialImage {
-  // A URL to an image file. Populated from initialImage.url in the
-  // VirtualMachine object.
-  string url = 1;
-}
-
-// LeaseMode represents the different modes VM run authorizations can be
-// managed. The VM system has its own system for authorizing a given pod to run
-// a given VM because it requires different tradeoff as part of its distributed
-// systems design than Kubernetes. The core issue is that Kubernetes's design
-// does not guarantee that the control plane always has an accurate view
-// of running pods especially when nodes fail or get partitioned which they
-// trade for potentially better availability by keeping both sides of the
-// partition running. Kubernetes is also prone to bugs that result in running
-// pods no longer being accounted for (for example
-// https://github.com/kubernetes/kubernetes/issues/80968) or duplicated.This can
-// result in pods running which the controller cannot see which results in more
-// than one running pod for a VM indefinitely.
-// For stateful single-instance workloads like VMs this can cause
-// control issues (VMs no longer converging to the configured state because
-// the current pod is "out of control", continued unavailability because an
-// uncontrollable pod holds a lock on the backing storage or even data
-// corruption in case two VMs are concurrently writing to the same storage.
-//
-// To avoid these issues the VM system implements two different strategies
-// providing mutual exclusion itself: One for use exclusively with
-// local storage-backed VMs and one tailored for VMs with distributed storage.
-// They significantly differ in the tradeoffs they make and the guarantees they
-// deliver as documented below.
-// Both strategies rely (at least in part) on asking the VM controller directly
-// if a pod should keep running its VM. The statement of the VM controller
-// is called a "run authorization" in the context of the VM system. The exact
-// format of this run authorization depends on the strategy in use.
-enum LeaseMode {
-  LM_UNKNOWN = 0;
-  // In storage locking mode mutual exclusion and thus run authorization is
-  // provided through locks on the backing block storage system. Control plane
-  // convergence is only on a best-effort basis, under certain K8s failure modes
-  // the VM control plane might never converge. A Hypervisor that's partitioned
-  // from the control plane will continue to run its VM indefinitely and will
-  // not fence itself off from storage or networking. This mode is appropriate
-  // for local storage as the full leases mode would introduce more disruptions
-  // than it solves under these constraints. The run authorization for this
-  // strategy is a simple STATUS_OK/STATUS_TERMINATE status value with no
-  // explicit lease expiration as VMs should not stop executing if the control
-  // plane is unavailable. These authorizations are still useful as a way to
-  // ensure at least on a best-effort basis that leaked/out-of-control pods shut
-  // themselves down and locks held by the wrong pods are released.
-  LM_STORAGE_LOCKING = 1;
-  // In full leases mode all run authorizations come exclusively from the
-  // controller and are passed as leases to all external systems (like storage
-  // and network).  A Hypervisor that's partitioned from the control plane
-  // will after its lease expires kill its VM and fence itself from network and
-  // storage before terminating itself. This mode is appropriate for fully
-  // distributed storage as it allows higher availability in that scenario.
-  // The run authorization for this strategy is an expiring lease which also
-  // needs to be passed together with any IO operation for proper fencing.
-  // The hypervisor kills the VM if its lease expires.
-  // Not implemented currently.
-  LM_FULL_LEASES = 2;
-}
-
-// This is a structure exposing VM metadata to the VM via fw_cfg interface. It
-// currently only contains the name of the VM and its network configuration.
-// Exposed as vm.metropolis.monogon.dev/v1/metadata.pb to the VM.
-message VMMetadata {
-  // Name field from Kubernetes VirtualMachine object.
-  string name = 1;
-  NetworkConfig network_config = 2;
-}
-
-// PTPAddress contains the VM IP and the hypervisor IP for an IP point to
-// point interface. Both IPs need to be for the same IP protocol version (v4 or
-// v6).
-// For example on Linux this could be configured using
-// `ip addr add $ip peer $peer_ip dev eth0` for the PtP connection and
-// `ip route add default via $peer_ip` for the default route.
-message PTPAddress {
-  // IP address of the VM
-  string ip = 1;
-  // IP address of the hypervisor side, default gateway for the VM
-  string peer_ip = 2;
-}
-
-// NetworkConfig represents the network configuration the VM needs to configure
-// to communicate via its network interface.
-message NetworkConfig {
-  // IPv4 addresses of the PtP link between the VM and the hypervisor, if any.
-  PTPAddress v4 = 1;
-  // IPv6 addresses of the PtP link between the VM and the hypervisor, if any.
-  PTPAddress v6 = 2;
-}
-
-// HypervisorID identifies a running instance of a hypervisor uniquely.
-message HypervisorID {
-  // vm_name is the name of the VM object.
-  string vm_name = 1;
-  // namespace is the K8s namespace of the VM object.
-  string namespace = 2;
-  // pod_name is the pod name in which the hypervisor is running.
-  string pod_name = 3;
-  // run_id is selected by the hypervisor at the start of the process to
-  // uniquely identify that specific running process. A process which starts
-  // later with respect to other instances on the same node should have a higher
-  // run_id so that the controller can know that. In practice this should be
-  // derived from a precise timestamp like nanoseconds since the UNIX epoch.
-  uint64 run_id = 4;
-}
-
-message RunLeaseRequest {
-  HypervisorID us = 1;
-}
-
-message RunLeaseUpdate {
-  enum Status {
-    STATUS_UNKNOWN = 0;
-    // The pod should keep running its VM
-    STATUS_OK = 1;
-    // The pod should terminate the VM immediately and exit
-    STATUS_TERMINATE = 2;
-  }
-  Status status = 1;
-}
-
-message MigrationSwitchoverRequest {
-  HypervisorID us = 1;
-  HypervisorID them = 2;
-}
-message MigrationSwitchoverResponse {}
-
-message EnsureMigrationTargetRequest {
-  HypervisorID us = 1;
-}
-
-message EnsureMigrationTargetResponse {
-  enum Action {
-    ACTION_UNKNOWN = 0;
-    ACTION_LIVE_MIGRATE = 1;
-    ACTION_SOFT_SHUTDOWN = 2;
-  }
-  Action action = 1;
-  // Endpoint of the new Pod exposing a metropolis.vm.Hypervisor service if
-  // action == ACTION_LIVE_MIGRATE.
-  string target_endpoint = 2;
-}
-
-// The VMController service is exposed by the controller for the hypervisors to
-// interact with. It is responsible for (pseudo)-leases and and migrations.
-// A typical migration looks like this:
-// 1. Currently running pod with VM gets SIGTERM.
-// 2. Source pod runs EnsureMigrationTarget to inform the controller of its wish
-//    to migrate its VM away. The controller creates or reuses a target pod to
-//    migrate to and returns its endpoint to the source pod.
-// 3. Source pod runs Hypervisor.StartMigration on the target pod to negotiate a
-//    channel to migrate.
-// 4. Source pod bulk-migrates the vm in a hypervisor-specific way.
-// 5. After the bulk migration is done, the source pod stops executing the VM.
-//    The target pod calls MigrationSwitchover on the controller with `us` set
-//    to itself and `them` to the `us` parameter in the StartMigrationRequest it
-//    received in step 3.
-// 6. The controller performs the Compare-and-Swap and returns either Ok or
-//    PreconditionFailed depending on whether the authoritative pod has changed
-//    in the meantime. If the MigrationSwitchover RPC succeeded, the VM is now
-//    running on the target pod. If it doesn't succeed, the target pod will
-//    retry this step for a set period of time and then exit.
-// 7. After a set timeout, the source pod will regenerate is run id and attempt
-//    to call MigrationSwitchover with them set to its old identity and us to
-//    its new identity formed by updating its run id. This call is expected to
-//    fail with PreconditionFailed which will cause the source pod to shut
-//    itself down. If the call succeeds, the source pod will start running the
-//    VM again.
-service VMController {
-  // EnsureMigrationTarget returns either a request to soft-shutdown or a
-  // reference to a pod to which the caller should connect to migrate the VM.
-  // It waits for the pod to run and complete a gRPC health check, but clients
-  // should still retry a connection a few times before giving up and calling
-  // this endpoint again.
-  rpc EnsureMigrationTarget(EnsureMigrationTargetRequest) returns (EnsureMigrationTargetResponse);
-  // MigrationSwitchover attempts to atomically swap the authoritative Pod and
-  // PVC from the one in `them` to the one in `us`. If this request succeeds the
-  // pod in `us` (the caller) is now authoritative for a given VM. If the
-  // authoritative pod is not the one in `them`, this method will return
-  // PreconditionFailed and do nothing.
-  rpc MigrationSwitchover(MigrationSwitchoverRequest) returns (MigrationSwitchoverResponse);
-  // RunLease requests a pseudo-lease (or a full lease in LeaseMode
-  // LM_FULL_LEASES) and streams updates to the lease status or new leases (in
-  // LM_FULL_LEASES). Clients should always attempt to keep one RunLease
-  // connection open to ensure reliable control from the control plane.
-  rpc RunLease(RunLeaseRequest) returns (stream RunLeaseUpdate);
-}
-
-// The OOBManagement service is exposed by each VM pod to perform OOB
-// maintenance on the VM running inside of it.
-service OOBManagement {
-  // Reset resets the virtual CPU of the VM (essentially equivalent to a hard
-  // reboot). This has no effect on the hypervisor itself.
-  // TODO(lorenz): This API should have idempotency counters.
-  rpc Reset(ResetRequest) returns (ResetResponse);
-  // Console opens a bidirectional stream to the virtual serial port (for
-  // debugging or OOB data transfer).
-  // If multiple streams are open data from the VM is broadcast to all clients
-  // and data from all clients are sent to the VM. Ordering with multiple
-  // clients connected is best-effort and cannot be relied upon.
-  rpc Console(stream ConsoleIO) returns (stream ConsoleIO);
-}
-
-message ResetRequest {}
-message ResetResponse {}
-
-message ConsoleIO {
-  bytes data = 1;
-}
-
-// The Hypervisor service is exposed by each VM pod for migrations.
-service Hypervisor {
-  // StartMigration is called by the source pod when it wants to initiate a
-  // migration. It is used to negotiate parameters for migration and endpoints
-  // for the bulk transfer. If no common migration protocol is found,
-  // InvalidArgument is returned.
-  rpc StartMigration(StartMigrationRequest) returns (StartMigrationResponse);
-}
-
-// MigrationProtocol represents a protocol and some protocol-specific metadata
-// to allow for negotiating a connection using that protocol.
-// For each migration  protocol message, some fields will be set by the source
-// as constraints (constraint_*), and some will be populated by the target if
-// that migration protocol is picked (negotiated_*). The migration target will
-// keep all constraint_* fields that it was aware of, so that the source can
-// verify that all critical fields were considered by the target (thereby
-// allowing different versions of source/target to communicate).
-message MigrationProtocol {
-  // Qemu represents the native QEMU migration protocol.
-  message Qemu {
-    // If set, the root block device is migrated together with the VM. If the
-    // target doesn't have storage attached directly via QEMU (like RBD or
-    // iSCSI) this needs to be set otherwise this protocol cannot be picked as
-    // the VM would loose it storage during the migration. The opposite is
-    // allowed, it migrates a local-storage volume into QEMU-attached storage
-    // storage.
-    bool constraint_with_blockmigration = 1;
-    // Bulk endpoint on the migration target in QEMU native format
-    string negotiated_endpoint = 2;
-  }
-  oneof kind { Qemu qmeu_block = 1; }
-}
-
-message StartMigrationRequest {
-  // List of migration protocols supported by the source pod
-  repeated MigrationProtocol supported_migration_protocol = 1;
-
-  // Hypervisor ID of the hypervisor making the request (i.e. is currently
-  // running the VM)
-  HypervisorID us = 2;
-}
-
-message StartMigrationResponse {
-  // Migration protocol chosen from supported_migration_protocol by the target
-  // pod.
-  MigrationProtocol migration_protocol = 1;
-}
diff --git a/metropolis/vm/smoketest/BUILD.bazel b/metropolis/vm/smoketest/BUILD.bazel
deleted file mode 100644
index 6225e2d..0000000
--- a/metropolis/vm/smoketest/BUILD.bazel
+++ /dev/null
@@ -1,61 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
-load("//osbase/build:def.bzl", "node_initramfs")
-
-go_library(
-    name = "smoketest_lib",
-    srcs = ["main.go"],
-    data = [
-        "@qemu//:qemu-x86_64-softmmu",
-    ],
-    importpath = "source.monogon.dev/metropolis/vm/smoketest",
-    visibility = ["//visibility:private"],
-    x_defs = {
-        "xQemuPath": "$(rlocationpath @qemu//:qemu-x86_64-softmmu )",
-    },
-    deps = ["@io_bazel_rules_go//go/runfiles:go_default_library"],
-)
-
-node_initramfs(
-    name = "initramfs",
-    files = {
-        "//metropolis/vm/smoketest/payload": "/init",
-    },
-    fsspecs = [
-        "//osbase/build:earlydev.fsspec",
-    ],
-)
-
-go_binary(
-    name = "smoketest",
-    embed = [":smoketest_lib"],
-    pure = "on",
-    visibility = ["//visibility:private"],
-)
-
-load("@aspect_bazel_lib//lib:transitions.bzl", "platform_transition_binary")
-
-platform_transition_binary(
-    name = "smoketest_transitioned",
-    binary = ":smoketest",
-    target_platform = "//build/platforms:linux_amd64_static",
-    visibility = ["//visibility:private"],
-)
-
-load("@rules_pkg//pkg:tar.bzl", "pkg_tar")
-
-pkg_tar(
-    name = "smoketest_layer",
-    srcs = [":smoketest_transitioned"],
-    visibility = ["//visibility:private"],
-)
-
-load("@rules_oci//oci:defs.bzl", "oci_image")
-
-oci_image(
-    name = "smoketest_image",
-    base = "@distroless_base",
-    entrypoint = ["/smoketest"],
-    tars = [":smoketest_layer"],
-    visibility = ["//visibility:public"],
-    workdir = "/app",
-)
diff --git a/metropolis/vm/smoketest/main.go b/metropolis/vm/smoketest/main.go
deleted file mode 100644
index 58f1a22..0000000
--- a/metropolis/vm/smoketest/main.go
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2020 The Monogon Project Authors.
-//
-// SPDX-License-Identifier: Apache-2.0
-//
-// 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.
-
-// This is a small smoke test which will run in a container on top of Metropolis
-// Kubernetes. It exercises Metropolis' KVM device plugin,
-package main
-
-import (
-	"bytes"
-	"io"
-	"log"
-	"net"
-	"os"
-	"os/exec"
-	"path/filepath"
-
-	"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.
-	xQemuPath string
-)
-
-func init() {
-	var err error
-	for _, path := range []*string{
-		&xQemuPath,
-	} {
-		*path, err = runfiles.Rlocation(*path)
-		if err != nil {
-			panic(err)
-		}
-	}
-}
-
-func main() {
-	testSocket, err := net.Listen("unix", "@metropolis/vm/smoketest")
-	if err != nil {
-		panic(err)
-	}
-
-	testResultChan := make(chan bool)
-	go func() {
-		conn, err := testSocket.Accept()
-		if err != nil {
-			panic(err)
-		}
-		testValue, _ := io.ReadAll(conn)
-		if bytes.Equal(testValue, []byte("test123")) {
-			testResultChan <- true
-		} else {
-			testResultChan <- false
-		}
-	}()
-
-	// TODO(lorenz): This explicitly doesn't use our own qboot because it cannot be built in a musl configuration.
-	// This will be fixed once we have a proper multi-target toolchain.
-	biosPath := filepath.Join(filepath.Dir(xQemuPath), "pc-bios/qboot.rom")
-
-	baseArgs := []string{"-nodefaults", "-no-user-config", "-nographic", "-no-reboot",
-		"-accel", "kvm", "-cpu", "host",
-		"-bios", biosPath,
-		"-M", "microvm,x-option-roms=off,pic=off,pit=off,rtc=off,isa-serial=off",
-		"-kernel", "osbase/test/ktest/linux-testing.elf",
-		"-append", "reboot=t console=hvc0 quiet",
-		"-initrd", "metropolis/vm/smoketest/initramfs.cpio.lz4",
-		"-device", "virtio-rng-device,max-bytes=1024,period=1000",
-		"-device", "virtio-serial-device,max_ports=16",
-		"-chardev", "stdio,id=con0", "-device", "virtconsole,chardev=con0",
-		"-chardev", "socket,id=test,path=metropolis/vm/smoketest,abstract=on",
-		"-device", "virtserialport,chardev=test",
-	}
-	qemuCmd := exec.Command(xQemuPath, baseArgs...)
-	qemuCmd.Stdout = os.Stdout
-	qemuCmd.Stderr = os.Stderr
-	if err := qemuCmd.Run(); err != nil {
-		log.Fatalf("running QEMU failed: %v", err)
-	}
-	testResult := <-testResultChan
-	if testResult {
-		return
-	} else {
-		os.Exit(1)
-	}
-}
diff --git a/metropolis/vm/smoketest/payload/BUILD.bazel b/metropolis/vm/smoketest/payload/BUILD.bazel
deleted file mode 100644
index b2cb04b..0000000
--- a/metropolis/vm/smoketest/payload/BUILD.bazel
+++ /dev/null
@@ -1,15 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
-
-go_library(
-    name = "payload_lib",
-    srcs = ["main.go"],
-    importpath = "source.monogon.dev/metropolis/vm/smoketest/payload",
-    visibility = ["//visibility:private"],
-    deps = ["@org_golang_x_sys//unix"],
-)
-
-go_binary(
-    name = "payload",
-    embed = [":payload_lib"],
-    visibility = ["//visibility:public"],
-)
diff --git a/metropolis/vm/smoketest/payload/main.go b/metropolis/vm/smoketest/payload/main.go
deleted file mode 100644
index 2ea6485..0000000
--- a/metropolis/vm/smoketest/payload/main.go
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2020 The Monogon Project Authors.
-//
-// SPDX-License-Identifier: Apache-2.0
-//
-// 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 main
-
-import (
-	"os"
-
-	"golang.org/x/sys/unix"
-)
-
-func main() {
-	if err := unix.Mount("devtmpfs", "/dev", "devtmpfs", 0, ""); err != nil {
-		panic(err)
-	}
-	testPort, err := os.OpenFile("/dev/vport1p1", os.O_RDWR, 0)
-	if err != nil {
-		panic(err)
-	}
-	testPort.WriteString("test123")
-	testPort.Close()
-	unix.Reboot(unix.LINUX_REBOOT_CMD_POWER_OFF)
-}