m/cli/metroctl: factor out some helper functions

We will need these to create metroctl-compatible configs from
//metropolis/cluster/launch.

Change-Id: I2705afefb62b7e1b35c87d9753c4ca9c7f534c26
Reviewed-on: https://review.monogon.dev/c/monogon/+/1324
Tested-by: Jenkins CI
Reviewed-by: Mateusz Zalega <mateusz@monogon.tech>
diff --git a/metropolis/cli/metroctl/takeownership.go b/metropolis/cli/metroctl/takeownership.go
index 6102cf1..e0b2f79 100644
--- a/metropolis/cli/metroctl/takeownership.go
+++ b/metropolis/cli/metroctl/takeownership.go
@@ -2,17 +2,12 @@
 
 import (
 	"context"
-	"encoding/pem"
 	"log"
 	"net"
 	"os"
 	"os/exec"
-	"path/filepath"
 
 	"github.com/spf13/cobra"
-	clientauthentication "k8s.io/client-go/pkg/apis/clientauthentication/v1"
-	"k8s.io/client-go/tools/clientcmd"
-	clientapi "k8s.io/client-go/tools/clientcmd/api"
 
 	"source.monogon.dev/metropolis/cli/metroctl/core"
 	clicontext "source.monogon.dev/metropolis/cli/pkg/context"
@@ -39,8 +34,8 @@
 
 	// Retrieve the cluster owner's private key, and use it to construct
 	// ephemeral credentials. Then, dial the cluster.
-	opk, err := getOwnerKey()
-	if err == noCredentialsError {
+	opk, err := core.GetOwnerKey(flags.configPath)
+	if err == core.NoCredentialsError {
 		log.Fatalf("Owner key does not exist. takeownership needs to be executed on the same system that has previously installed the cluster using metroctl install.")
 	}
 	if err != nil {
@@ -57,21 +52,12 @@
 	if err != nil {
 		log.Fatalf("Failed to retrive owner certificate from cluster: %v", err)
 	}
-	ownerCertPEM := pem.Block{
-		Type:  "CERTIFICATE",
-		Bytes: ownerCert.Certificate[0],
-	}
-	if err := os.WriteFile(filepath.Join(flags.configPath, "owner.pem"), pem.EncodeToMemory(&ownerCertPEM), 0644); err != nil {
+	if err := core.WriteOwnerCertificate(flags.configPath, ownerCert.Certificate[0]); err != nil {
 		log.Printf("Failed to store retrieved owner certificate: %v", err)
 		log.Fatalln("Sorry, the cluster has been lost as taking ownership cannot be repeated. Fix the reason the file couldn't be written and reinstall the node.")
 	}
 	log.Print("Successfully retrieved owner credentials! You now own this cluster. Setting up kubeconfig now...")
 
-	ca := clientcmd.NewDefaultPathOptions()
-	config, err := ca.GetStartingConfig()
-	if err != nil {
-		log.Fatalf("Failed to get initial kubeconfig to add Metropolis cluster: %v", err)
-	}
 	// 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.
@@ -82,44 +68,11 @@
 			log.Fatalf("Failed to create kubectl entry as metroctl is neither in PATH nor can its absolute path be determined: %v", err)
 		}
 	}
-
-	config.AuthInfos["metropolis"] = &clientapi.AuthInfo{
-		Exec: &clientapi.ExecConfig{
-			APIVersion: clientauthentication.SchemeGroupVersion.String(),
-			Command:    metroctlPath,
-			Args:       []string{k8scredpluginCmd.Use},
-			InstallHint: `Authenticating to Metropolis clusters requires metroctl to be present.
-Running metroctl takeownership creates this entry and either points to metroctl as a command in
-PATH if metroctl is in PATH at that time or to the absolute path to metroctl at that time.
-If you moved metroctl afterwards or want to switch to PATH resolution, edit $HOME/.kube/config and
-change users.metropolis.exec.command to the required path (or just metroctl if using PATH resolution).`,
-			InteractiveMode: clientapi.NeverExecInteractiveMode,
-		},
-	}
-
-	config.Clusters["metropolis"] = &clientapi.Cluster{
-		// MVP: This is insecure, but making this work would be wasted effort
-		// as all of it will be replaced by the identity system.
-		// TODO(issues/144): adjust cluster endpoints once have functioning roles
-		// implemented.
-		InsecureSkipTLSVerify: true,
-		Server:                "https://" + net.JoinHostPort(flags.clusterEndpoints[0], node.KubernetesAPIWrappedPort.PortString()),
-	}
-
-	config.Contexts["metropolis"] = &clientapi.Context{
-		AuthInfo:  "metropolis",
-		Cluster:   "metropolis",
-		Namespace: "default",
-	}
-
-	// Only set us as the current context if no other exists. Changing that
-	// unprompted would be kind of rude.
-	if config.CurrentContext == "" {
-		config.CurrentContext = "metropolis"
-	}
-
-	if err := clientcmd.ModifyConfig(ca, *config, true); err != nil {
-		log.Fatalf("Failed to modify kubeconfig to add Metropolis cluster: %v", err)
+	// TODO(issues/144): adjust cluster endpoints once have functioning roles
+	// implemented.
+	server := "https://" + net.JoinHostPort(flags.clusterEndpoints[0], node.KubernetesAPIWrappedPort.PortString())
+	if err := core.InstallK8SWrapper(metroctlPath, "metroctl", server, ""); err != nil {
+		log.Fatalf("Failed to install metroctl/k8s integration: %v", err)
 	}
 	log.Println("Success! kubeconfig is set up. You can now run kubectl --context=metropolis ... to access the Kubernetes cluster.")
 }