blob: adad3cfd4832c9a813d1581625578f72a0d71a19 [file] [log] [blame]
Lorenz Bruna9b455f2021-12-07 03:53:22 +01001package main
2
3import (
4 "context"
Tim Windelschmidtd5f851b2024-04-23 14:59:37 +02005 "errors"
Lorenz Bruna9b455f2021-12-07 03:53:22 +01006 "log"
Lorenz Bruna9b455f2021-12-07 03:53:22 +01007 "os"
Lorenz Brun20d1dd12022-07-01 12:21:42 +00008 "os/exec"
Lorenz Bruna9b455f2021-12-07 03:53:22 +01009
Lorenz Bruna9b455f2021-12-07 03:53:22 +010010 "github.com/spf13/cobra"
Serge Bazanski925ec3d2024-02-05 14:38:20 +010011 "google.golang.org/grpc"
Lorenz Bruna9b455f2021-12-07 03:53:22 +010012
Mateusz Zalega18a67b02022-08-02 13:37:50 +020013 "source.monogon.dev/metropolis/cli/metroctl/core"
Lorenz Bruna9b455f2021-12-07 03:53:22 +010014 clicontext "source.monogon.dev/metropolis/cli/pkg/context"
Lorenz Bruna9b455f2021-12-07 03:53:22 +010015 "source.monogon.dev/metropolis/node/core/rpc"
Serge Bazanski925ec3d2024-02-05 14:38:20 +010016 "source.monogon.dev/metropolis/node/core/rpc/resolver"
Serge Bazanskidc1bec42021-12-16 17:38:53 +010017 apb "source.monogon.dev/metropolis/proto/api"
Lorenz Bruna9b455f2021-12-07 03:53:22 +010018)
19
20var takeownershipCommand = &cobra.Command{
Mateusz Zalegab1e7ee42022-07-08 12:19:02 +020021 Use: "takeownership",
Lorenz Bruna9b455f2021-12-07 03:53:22 +010022 Short: "Takes ownership of a new Metropolis cluster",
23 Long: `This takes ownership of a new Metropolis cluster by asking the new
24cluster to issue an owner certificate to for the owner key generated by a
Mateusz Zalegab1e7ee42022-07-08 12:19:02 +020025previous invocation of metroctl install on this machine. A single cluster
26endpoint must be provided with the --endpoints parameter.`,
27 Args: cobra.ExactArgs(0),
28 Run: doTakeOwnership,
Lorenz Bruna9b455f2021-12-07 03:53:22 +010029}
30
Mateusz Zalegab1e7ee42022-07-08 12:19:02 +020031func doTakeOwnership(cmd *cobra.Command, _ []string) {
Tim Windelschmidt0a8797d2024-03-04 18:58:47 +010032 ctx := clicontext.WithInterrupt(context.Background())
Mateusz Zalegab1e7ee42022-07-08 12:19:02 +020033 if len(flags.clusterEndpoints) != 1 {
34 log.Fatalf("takeownership requires a single cluster endpoint to be provided with the --endpoints parameter.")
35 }
Tim Windelschmidt0a8797d2024-03-04 18:58:47 +010036
37 contextName, err := cmd.Flags().GetString("context")
38 if err != nil || contextName == "" {
39 log.Fatalf("takeownership requires a valid context name to be provided with the --context parameter.")
40 }
Serge Bazanski7eeef0f2024-02-05 14:40:15 +010041
42 ca, err := core.GetClusterCAWithTOFU(ctx, connectOptions())
43 if err != nil {
44 log.Fatalf("Could not retrieve cluster CA: %v", err)
45 }
Mateusz Zalegab1e7ee42022-07-08 12:19:02 +020046
Mateusz Zalega18464502022-07-14 16:18:26 +020047 // Retrieve the cluster owner's private key, and use it to construct
48 // ephemeral credentials. Then, dial the cluster.
Serge Bazanskicf23ebc2023-03-14 17:02:04 +010049 opk, err := core.GetOwnerKey(flags.configPath)
Tim Windelschmidtd5f851b2024-04-23 14:59:37 +020050 if errors.Is(err, core.NoCredentialsError) {
Lorenz Bruna9b455f2021-12-07 03:53:22 +010051 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.")
Lorenz Bruna9b455f2021-12-07 03:53:22 +010052 }
Mateusz Zalega18464502022-07-14 16:18:26 +020053 if err != nil {
54 log.Fatalf("Couldn't get owner's key: %v", err)
Lorenz Bruna9b455f2021-12-07 03:53:22 +010055 }
Serge Bazanski925ec3d2024-02-05 14:38:20 +010056 opts, err := core.DialOpts(ctx, connectOptions())
57 if err != nil {
58 log.Fatalf("While configuring cluster dial opts: %v", err)
59 }
Serge Bazanski7eeef0f2024-02-05 14:40:15 +010060 creds, err := rpc.NewEphemeralCredentials(opk, rpc.WantRemoteCluster(ca))
Serge Bazanski925ec3d2024-02-05 14:38:20 +010061 if err != nil {
62 log.Fatalf("While generating ephemeral credentials: %v", err)
63 }
64 opts = append(opts, grpc.WithTransportCredentials(creds))
65
66 cc, err := grpc.Dial(resolver.MetropolisControlAddress, opts...)
Mateusz Zalega18464502022-07-14 16:18:26 +020067 if err != nil {
68 log.Fatalf("While dialing the cluster: %v", err)
Lorenz Bruna9b455f2021-12-07 03:53:22 +010069 }
Mateusz Zalega18464502022-07-14 16:18:26 +020070 aaa := apb.NewAAAClient(cc)
Lorenz Bruna9b455f2021-12-07 03:53:22 +010071
Mateusz Zalega18464502022-07-14 16:18:26 +020072 ownerCert, err := rpc.RetrieveOwnerCertificate(ctx, aaa, opk)
Lorenz Bruna9b455f2021-12-07 03:53:22 +010073 if err != nil {
74 log.Fatalf("Failed to retrive owner certificate from cluster: %v", err)
75 }
Serge Bazanski925ec3d2024-02-05 14:38:20 +010076
Serge Bazanskicf23ebc2023-03-14 17:02:04 +010077 if err := core.WriteOwnerCertificate(flags.configPath, ownerCert.Certificate[0]); err != nil {
Lorenz Bruna9b455f2021-12-07 03:53:22 +010078 log.Printf("Failed to store retrieved owner certificate: %v", err)
79 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.")
80 }
Lorenz Brun20d1dd12022-07-01 12:21:42 +000081 log.Print("Successfully retrieved owner credentials! You now own this cluster. Setting up kubeconfig now...")
82
Lorenz Brun20d1dd12022-07-01 12:21:42 +000083 // If the user has metroctl in their path, use the metroctl from path as
84 // a credential plugin. Otherwise use the path to the currently-running
85 // metroctl.
86 metroctlPath := "metroctl"
87 if _, err := exec.LookPath("metroctl"); err != nil {
88 metroctlPath, err = os.Executable()
89 if err != nil {
90 log.Fatalf("Failed to create kubectl entry as metroctl is neither in PATH nor can its absolute path be determined: %v", err)
91 }
92 }
Serge Bazanski1f8cad72023-03-20 16:58:10 +010093 // TODO(q3k, issues/144): this only works as long as all nodes are kubernetes controller
94 // nodes. This won't be the case for too long. Figure this out.
Tim Windelschmidtb37d7d82023-06-14 19:06:44 +020095 configName := "metroctl"
Serge Bazanski568c38c2024-02-05 14:40:39 +010096 if err := core.InstallKubeletConfig(ctx, metroctlPath, connectOptions(), configName, flags.clusterEndpoints[0]); err != nil {
Serge Bazanskicf23ebc2023-03-14 17:02:04 +010097 log.Fatalf("Failed to install metroctl/k8s integration: %v", err)
Lorenz Brun20d1dd12022-07-01 12:21:42 +000098 }
Tim Windelschmidtb37d7d82023-06-14 19:06:44 +020099 log.Printf("Success! kubeconfig is set up. You can now run kubectl --context=%s ... to access the Kubernetes cluster.", configName)
Lorenz Bruna9b455f2021-12-07 03:53:22 +0100100}
101
102func init() {
Tim Windelschmidt0a8797d2024-03-04 18:58:47 +0100103 takeownershipCommand.Flags().String("context", "metroctl", "The name for the kubernetes context to configure")
Lorenz Bruna9b455f2021-12-07 03:53:22 +0100104 rootCmd.AddCommand(takeownershipCommand)
105}