github.com/pluralsh/plural-cli@v0.9.5/cmd/plural/clusters.go (about)

     1  package plural
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/urfave/cli"
     7  	"sigs.k8s.io/yaml"
     8  
     9  	"github.com/pluralsh/plural-cli/pkg/api"
    10  	"github.com/pluralsh/plural-cli/pkg/bootstrap"
    11  	"github.com/pluralsh/plural-cli/pkg/bootstrap/aws"
    12  	"github.com/pluralsh/plural-cli/pkg/bootstrap/validation"
    13  	"github.com/pluralsh/plural-cli/pkg/cluster"
    14  	"github.com/pluralsh/plural-cli/pkg/config"
    15  	"github.com/pluralsh/plural-cli/pkg/exp"
    16  	"github.com/pluralsh/plural-cli/pkg/kubernetes"
    17  	"github.com/pluralsh/plural-cli/pkg/machinepool"
    18  	"github.com/pluralsh/plural-cli/pkg/manifest"
    19  	"github.com/pluralsh/plural-cli/pkg/utils"
    20  )
    21  
    22  func (p *Plural) clusterCommands() []cli.Command {
    23  	return []cli.Command{
    24  		{
    25  			Name:   "list",
    26  			Usage:  "lists clusters accessible to your user",
    27  			Action: latestVersion(p.listClusters),
    28  		},
    29  		{
    30  			Name:   "transfer",
    31  			Usage:  "transfers ownership of the current cluster to another",
    32  			Action: latestVersion(rooted(p.transferOwnership)),
    33  			Flags: []cli.Flag{
    34  				cli.StringFlag{
    35  					Name:  "email",
    36  					Usage: "the email of the new owner",
    37  				},
    38  			},
    39  		},
    40  		{
    41  			Name:  "view",
    42  			Usage: "shows info for a cluster",
    43  			Flags: []cli.Flag{
    44  				cli.StringFlag{
    45  					Name:  "id",
    46  					Usage: "the id of the source cluster",
    47  				},
    48  			},
    49  			Action: latestVersion(p.showCluster),
    50  		},
    51  		{
    52  			Name:  "depend",
    53  			Usage: "have a cluster wait for promotion on another cluster",
    54  			Flags: []cli.Flag{
    55  				cli.StringFlag{
    56  					Name:  "source-id",
    57  					Usage: "the id of the source cluster",
    58  				},
    59  				cli.StringFlag{
    60  					Name:  "dest-id",
    61  					Usage: "the id of the cluster waiting for promotion",
    62  				},
    63  			},
    64  			Action: latestVersion(p.dependCluster),
    65  		},
    66  		{
    67  			Name:   "promote",
    68  			Usage:  "promote pending upgrades to your cluster",
    69  			Action: latestVersion(p.promoteCluster),
    70  		},
    71  		{
    72  			Name:      "wait",
    73  			Usage:     "waits on a cluster until it becomes ready",
    74  			ArgsUsage: "NAMESPACE NAME",
    75  			Action:    latestVersion(initKubeconfig(requireArgs(handleClusterWait, []string{"NAMESPACE", "NAME"}))),
    76  			Category:  "Debugging",
    77  		},
    78  		{
    79  			Name:      "mpwait",
    80  			Usage:     "waits on a machine pool until it becomes ready",
    81  			ArgsUsage: "NAMESPACE NAME",
    82  			Action:    latestVersion(initKubeconfig(requireArgs(handleMPWait, []string{"NAMESPACE", "NAME"}))),
    83  			Category:  "Debugging",
    84  		},
    85  		{
    86  			Name:     "migrate",
    87  			Usage:    "migrate to Cluster API",
    88  			Action:   latestVersion(rooted(initKubeconfig(p.handleMigration))),
    89  			Category: "Publishing",
    90  			Hidden:   !exp.IsFeatureEnabled(exp.EXP_PLURAL_CAPI),
    91  		},
    92  		{
    93  			Name:        "aws-auth",
    94  			Usage:       "fetches the current state of your aws auth config map",
    95  			Subcommands: awsAuthCommands(),
    96  		},
    97  	}
    98  }
    99  
   100  func (p *Plural) handleMigration(_ *cli.Context) error {
   101  	p.InitPluralClient()
   102  	if err := validation.ValidateMigration(p); err != nil {
   103  		return err
   104  	}
   105  
   106  	project, err := manifest.FetchProject()
   107  	if err != nil {
   108  		return err
   109  	}
   110  
   111  	if project.ClusterAPI {
   112  		utils.Success("Cluster already migrated.\n")
   113  		return nil
   114  	}
   115  
   116  	return bootstrap.MigrateCluster(RunPlural)
   117  }
   118  
   119  func awsAuthCommands() []cli.Command {
   120  	return []cli.Command{
   121  		{
   122  			Name:   "fetch",
   123  			Usage:  "gets the current state of your aws auth configmap",
   124  			Action: handleAwsAuth,
   125  		},
   126  		{
   127  			Name:  "update",
   128  			Usage: "adds a user or role to the aws auth configmap",
   129  			Flags: []cli.Flag{
   130  				cli.StringFlag{Name: "role-arn"},
   131  				cli.StringFlag{Name: "user-arn"},
   132  			},
   133  			Action: handleModifyAwsAuth,
   134  		},
   135  	}
   136  }
   137  
   138  func handleAwsAuth(c *cli.Context) error {
   139  	auth, err := aws.FetchAuth()
   140  	if err != nil {
   141  		return err
   142  	}
   143  
   144  	res, err := yaml.Marshal(auth)
   145  	if err != nil {
   146  		return err
   147  	}
   148  
   149  	fmt.Println(string(res))
   150  	return nil
   151  }
   152  
   153  func handleModifyAwsAuth(c *cli.Context) error {
   154  	role, user := c.String("role-arn"), c.String("user-arn")
   155  
   156  	if role != "" {
   157  		return aws.AddRole(role)
   158  	}
   159  
   160  	if user != "" {
   161  		return aws.AddUser(user)
   162  	}
   163  
   164  	return fmt.Errorf("you must specify at least one of role-arn or user-arn")
   165  }
   166  
   167  func handleClusterWait(c *cli.Context) error {
   168  	namespace := c.Args().Get(0)
   169  	name := c.Args().Get(1)
   170  	kubeConf, err := kubernetes.KubeConfig()
   171  	if err != nil {
   172  		return err
   173  	}
   174  
   175  	return cluster.Wait(kubeConf, namespace, name)
   176  }
   177  
   178  func handleMPWait(c *cli.Context) error {
   179  	namespace := c.Args().Get(0)
   180  	name := c.Args().Get(1)
   181  	kubeConf, err := kubernetes.KubeConfig()
   182  	if err != nil {
   183  		return err
   184  	}
   185  
   186  	return machinepool.WaitAll(kubeConf, namespace, name)
   187  }
   188  
   189  func (p *Plural) listClusters(c *cli.Context) error {
   190  	p.InitPluralClient()
   191  	clusters, err := p.Client.Clusters()
   192  	if err != nil {
   193  		return err
   194  	}
   195  
   196  	headers := []string{"ID", "Name", "Provider", "Git Url", "Owner"}
   197  	return utils.PrintTable(clusters, headers, func(c *api.Cluster) ([]string, error) {
   198  		return []string{c.Id, c.Name, c.Provider, c.GitUrl, c.Owner.Email}, nil
   199  	})
   200  }
   201  
   202  func (p *Plural) transferOwnership(c *cli.Context) error {
   203  	p.InitPluralClient()
   204  	email := c.String("email")
   205  	man, err := manifest.FetchProject()
   206  	if err != nil {
   207  		return err
   208  	}
   209  
   210  	if err := p.TransferOwnership(man.Cluster, email); err != nil {
   211  		return api.GetErrorResponse(err, "TransferOwnership")
   212  	}
   213  
   214  	man.Owner.Email = email
   215  	if err := man.Flush(); err != nil {
   216  		return err
   217  	}
   218  
   219  	if err := p.assumeServiceAccount(config.Read(), man); err != nil {
   220  		return err
   221  	}
   222  
   223  	utils.Highlight("rebuilding bootstrap and console to sync your cluster with the new owner:\n")
   224  
   225  	for _, app := range []string{"bootstrap", "console"} {
   226  		installation, err := p.GetInstallation(app)
   227  		if err != nil {
   228  			return api.GetErrorResponse(err, "GetInstallation")
   229  		} else if installation == nil {
   230  			continue
   231  		}
   232  
   233  		if err := p.doBuild(installation, false); err != nil {
   234  			return err
   235  		}
   236  	}
   237  
   238  	utils.Highlight("deploying rebuilt applications\n")
   239  	if err := p.deploy(c); err != nil {
   240  		return err
   241  	}
   242  
   243  	utils.Success("Ownership successfully transferred to %s", email)
   244  	return nil
   245  }
   246  
   247  func (p *Plural) showCluster(c *cli.Context) error {
   248  	p.InitPluralClient()
   249  	id := c.String("id")
   250  	if id == "" {
   251  		clusters, err := p.Client.Clusters()
   252  		if err != nil {
   253  			return err
   254  		}
   255  
   256  		project, err := manifest.FetchProject()
   257  		if err != nil {
   258  			return err
   259  		}
   260  		for _, cluster := range clusters {
   261  			if cluster.Name == project.Cluster && cluster.Owner.Email == project.Owner.Email {
   262  				id = cluster.Id
   263  				break
   264  			}
   265  		}
   266  	}
   267  	cluster, err := p.Client.Cluster(id)
   268  	if err != nil {
   269  		return err
   270  	}
   271  
   272  	fmt.Printf("Cluster %s:\n\n", cluster.Id)
   273  
   274  	utils.PrintAttributes(map[string]string{
   275  		"Id":       cluster.Id,
   276  		"Name":     cluster.Name,
   277  		"Provider": cluster.Provider,
   278  		"Git Url":  cluster.GitUrl,
   279  		"Owner":    cluster.Owner.Email,
   280  	})
   281  
   282  	fmt.Println("")
   283  	if len(cluster.UpgradeInfo) > 0 {
   284  		fmt.Printf("Pending Upgrades:\n\n")
   285  		headers := []string{"Repository", "Count"}
   286  		return utils.PrintTable(cluster.UpgradeInfo, headers, func(c *api.UpgradeInfo) ([]string, error) {
   287  			return []string{c.Installation.Repository.Name, fmt.Sprintf("%d", c.Count)}, nil
   288  		})
   289  	}
   290  
   291  	fmt.Println("No pending upgrades")
   292  	return nil
   293  }
   294  
   295  func (p *Plural) dependCluster(c *cli.Context) error {
   296  	p.InitPluralClient()
   297  	source, dest := c.String("source-id"), c.String("dest-id")
   298  	if err := p.Client.CreateDependency(source, dest); err != nil {
   299  		return err
   300  	}
   301  
   302  	utils.Highlight("Cluster %s will now delegate upgrades to %s", dest, source)
   303  	return nil
   304  }
   305  
   306  func (p *Plural) promoteCluster(c *cli.Context) error {
   307  	p.InitPluralClient()
   308  	if err := p.Client.PromoteCluster(); err != nil {
   309  		return err
   310  	}
   311  
   312  	utils.Success("Upgrades promoted!")
   313  	return nil
   314  }