m/cli/metroctl: implement `k8s configure` command

Change-Id: I3da7a627b1ada462e62b739bde2073743262e23e
Reviewed-on: https://review.monogon.dev/c/monogon/+/1905
Reviewed-by: Leopold Schabel <leo@monogon.tech>
Tested-by: Jenkins CI
diff --git a/metropolis/cli/metroctl/BUILD.bazel b/metropolis/cli/metroctl/BUILD.bazel
index 984bd98..de7b759 100644
--- a/metropolis/cli/metroctl/BUILD.bazel
+++ b/metropolis/cli/metroctl/BUILD.bazel
@@ -4,6 +4,7 @@
     name = "metroctl_lib",
     srcs = [
         "cmd_install.go",
+        "cmd_k8s_configure.go",
         "cmd_k8scredplugin.go",
         "cmd_node.go",
         "cmd_node_approve.go",
diff --git a/metropolis/cli/metroctl/cmd_k8s_configure.go b/metropolis/cli/metroctl/cmd_k8s_configure.go
new file mode 100644
index 0000000..0e24767
--- /dev/null
+++ b/metropolis/cli/metroctl/cmd_k8s_configure.go
@@ -0,0 +1,55 @@
+package main
+
+import (
+	"log"
+	"os"
+	"os/exec"
+
+	"github.com/spf13/cobra"
+
+	"source.monogon.dev/metropolis/cli/metroctl/core"
+)
+
+var k8sCommand = &cobra.Command{
+	Short: "Manages kubernetes-specific functionality in Metropolis.",
+	Use:   "k8s",
+}
+
+var k8sConfigureCommand = &cobra.Command{
+	Use:   "configure",
+	Short: "Configures local `kubectl` for use with a Metropolis cluster.",
+	Long: `Configures a local kubectl instance (or any other Kubernetes application)
+to connect to a Metropolis cluster. A cluster endpoint must be provided with the
+--endpoints parameter.`,
+	Args: cobra.ExactArgs(0),
+	Run:  doK8sConfigure,
+}
+
+func doK8sConfigure(cmd *cobra.Command, _ []string) {
+	if len(flags.clusterEndpoints) < 1 {
+		log.Fatalf("k8s configure requires at least one cluster endpoint to be provided with the --endpoints parameter.")
+	}
+
+	// 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)
+		}
+	}
+	// TODO(q3k, issues/144): this only works as long as all nodes are kubernetes controller
+	// nodes. This won't be the case for too long. Figure this out.
+	configName := "metroctl"
+	if err := core.InstallKubeletConfig(metroctlPath, connectOptions(), configName, flags.clusterEndpoints[0]); err != nil {
+		log.Fatalf("Failed to install metroctl/k8s integration: %v", err)
+	}
+	log.Printf("Success! kubeconfig is set up. You can now run kubectl --context=%s ... to access the Kubernetes cluster.", configName)
+}
+
+func init() {
+	k8sCommand.AddCommand(k8sConfigureCommand)
+	rootCmd.AddCommand(k8sCommand)
+}