blob: 2c373407b92a9424dd9ceb48a961563b6b14fa84 [file] [log] [blame]
package main
import (
"context"
"log"
"net"
"os"
"os/exec"
"github.com/spf13/cobra"
"source.monogon.dev/metropolis/cli/metroctl/core"
clicontext "source.monogon.dev/metropolis/cli/pkg/context"
"source.monogon.dev/metropolis/node"
"source.monogon.dev/metropolis/node/core/rpc"
apb "source.monogon.dev/metropolis/proto/api"
)
var takeownershipCommand = &cobra.Command{
Use: "takeownership",
Short: "Takes ownership of a new Metropolis cluster",
Long: `This takes ownership of a new Metropolis cluster by asking the new
cluster to issue an owner certificate to for the owner key generated by a
previous invocation of metroctl install on this machine. A single cluster
endpoint must be provided with the --endpoints parameter.`,
Args: cobra.ExactArgs(0),
Run: doTakeOwnership,
}
func doTakeOwnership(cmd *cobra.Command, _ []string) {
if len(flags.clusterEndpoints) != 1 {
log.Fatalf("takeownership requires a single cluster endpoint to be provided with the --endpoints parameter.")
}
// Retrieve the cluster owner's private key, and use it to construct
// ephemeral credentials. Then, dial the cluster.
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 {
log.Fatalf("Couldn't get owner's key: %v", err)
}
ctx := clicontext.WithInterrupt(context.Background())
cc, err := core.DialCluster(ctx, opk, nil, flags.proxyAddr, flags.clusterEndpoints, rpcLogger)
if err != nil {
log.Fatalf("While dialing the cluster: %v", err)
}
aaa := apb.NewAAAClient(cc)
ownerCert, err := rpc.RetrieveOwnerCertificate(ctx, aaa, opk)
if err != nil {
log.Fatalf("Failed to retrive owner certificate from cluster: %v", err)
}
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...")
// 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.
apiserver := "https://" + net.JoinHostPort(flags.clusterEndpoints[0], node.KubernetesAPIWrappedPort.PortString())
if err := core.InstallKubeletConfig(metroctlPath, connectOptions(), "metroctl", apiserver); 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.")
}
func init() {
rootCmd.AddCommand(takeownershipCommand)
}