github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/cli/cmd/organization/organization.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  This file is part of KubeBlocks project
     5  
     6  This program is free software: you can redistribute it and/or modify
     7  it under the terms of the GNU Affero General Public License as published by
     8  the Free Software Foundation, either version 3 of the License, or
     9  (at your option) any later version.
    10  
    11  This program is distributed in the hope that it will be useful
    12  but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  GNU Affero General Public License for more details.
    15  
    16  You should have received a copy of the GNU Affero General Public License
    17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18  */
    19  
    20  package organization
    21  
    22  import (
    23  	"encoding/json"
    24  	"fmt"
    25  	"strings"
    26  
    27  	"github.com/jedib0t/go-pretty/v6/table"
    28  	"github.com/pkg/errors"
    29  	"github.com/spf13/cobra"
    30  	"gopkg.in/yaml.v2"
    31  	"k8s.io/cli-runtime/pkg/genericiooptions"
    32  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    33  	"k8s.io/kubectl/pkg/util/templates"
    34  
    35  	"github.com/1aal/kubeblocks/pkg/cli/printer"
    36  )
    37  
    38  var organizationExample = templates.Examples(`
    39  	// Get the organization name currently used by the user.
    40  	kbcli org current 
    41  	// List all organizations the current user has joined.
    42  	kbcli org list
    43  	// Get the description information of organization org1.
    44  	kbcli org describe org1
    45  	// Switch to organization org2.
    46  	kbcli org switch org2
    47  `)
    48  
    49  const (
    50  	APIURL  = "http://a8ff89cbeec444c82b90c5f83a117b39-16361bbd933bde33.elb.cn-northwest-1.amazonaws.com.cn:8086"
    51  	APIPath = "api/v1"
    52  )
    53  
    54  type Organizations struct {
    55  	Items []OrgItem `json:"items"`
    56  }
    57  
    58  type OrgItem struct {
    59  	ID          string `json:"id"`
    60  	Name        string `json:"name"`
    61  	Role        string `json:"role"`
    62  	Description string `json:"description"`
    63  	DisplayName string `json:"displayName"`
    64  	CreatedAt   string `json:"createdAt"`
    65  	UpdatedAt   string `json:"updatedAt"`
    66  }
    67  
    68  type CurrentOrgAndContext struct {
    69  	CurrentOrganization string `json:"currentOrganization"`
    70  	CurrentContext      string `json:"currentContext"`
    71  }
    72  
    73  type Organization interface {
    74  	getOrganization(name string) (*OrgItem, error)
    75  	GetOrganizations() (*Organizations, error)
    76  	switchOrganization(name string) (string, error)
    77  	getCurrentOrganization() (string, error)
    78  	addOrganization(body []byte) error
    79  	deleteOrganization(name string) error
    80  	IsValidOrganization(name string) (bool, error)
    81  }
    82  
    83  type OrganizationOption struct {
    84  	Name         string
    85  	OutputFormat string
    86  	Organization Organization
    87  
    88  	genericiooptions.IOStreams
    89  }
    90  
    91  func newOrganizationOption(streams genericiooptions.IOStreams) *OrganizationOption {
    92  	return &OrganizationOption{
    93  		IOStreams: streams,
    94  	}
    95  }
    96  
    97  func NewOrganizationCmd(streams genericiooptions.IOStreams) *cobra.Command {
    98  	cmd := &cobra.Command{
    99  		Use:     "org",
   100  		Short:   "kbcli org is used to manage cloud organizations and is only suitable for interacting with cloud.",
   101  		Example: organizationExample,
   102  	}
   103  	cmd.AddCommand(
   104  		newOrgListCmd(streams),
   105  		newOrgSwitchCmd(streams),
   106  		newOrgCurrentCmd(streams),
   107  		newOrgDescribeCmd(streams),
   108  	)
   109  
   110  	return cmd
   111  }
   112  
   113  func newOrgListCmd(streams genericiooptions.IOStreams) *cobra.Command {
   114  	o := newOrganizationOption(streams)
   115  
   116  	cmd := &cobra.Command{
   117  		Use:   "list",
   118  		Short: "List all organizations you have joined.",
   119  		Run: func(cmd *cobra.Command, args []string) {
   120  			cmdutil.CheckErr(o.complete(args))
   121  			cmdutil.CheckErr(o.validate(cmd))
   122  			cmdutil.CheckErr(o.runList())
   123  		},
   124  	}
   125  
   126  	return cmd
   127  }
   128  
   129  func newOrgSwitchCmd(streams genericiooptions.IOStreams) *cobra.Command {
   130  	o := newOrganizationOption(streams)
   131  
   132  	cmd := &cobra.Command{
   133  		Use:   "switch",
   134  		Short: "Switch to another organization you are already a member of.",
   135  		Run: func(cmd *cobra.Command, args []string) {
   136  			cmdutil.CheckErr(o.complete(args))
   137  			cmdutil.CheckErr(o.validate(cmd))
   138  			cmdutil.CheckErr(o.runSwitch())
   139  		},
   140  	}
   141  
   142  	return cmd
   143  }
   144  
   145  func newOrgCurrentCmd(streams genericiooptions.IOStreams) *cobra.Command {
   146  	o := newOrganizationOption(streams)
   147  
   148  	cmd := &cobra.Command{
   149  		Use:   "current",
   150  		Short: "Get current organization.",
   151  		Run: func(cmd *cobra.Command, args []string) {
   152  			cmdutil.CheckErr(o.complete(args))
   153  			cmdutil.CheckErr(o.validate(cmd))
   154  			cmdutil.CheckErr(o.runCurrent())
   155  		},
   156  	}
   157  
   158  	return cmd
   159  }
   160  
   161  func newOrgDescribeCmd(streams genericiooptions.IOStreams) *cobra.Command {
   162  	o := newOrganizationOption(streams)
   163  
   164  	cmd := &cobra.Command{
   165  		Use:   "describe",
   166  		Short: "Get the description information of an organization.",
   167  		Run: func(cmd *cobra.Command, args []string) {
   168  			cmdutil.CheckErr(o.complete(args))
   169  			cmdutil.CheckErr(o.validate(cmd))
   170  			cmdutil.CheckErr(o.runDescribe())
   171  		},
   172  	}
   173  
   174  	cmd.Flags().StringVarP(&o.OutputFormat, "output", "o", "human", "Output format (human|yaml|json)")
   175  
   176  	return cmd
   177  }
   178  
   179  func (o *OrganizationOption) validate(cmd *cobra.Command) error {
   180  	if cmd.Name() == "switch" || cmd.Name() == "describe" {
   181  		if o.Name == "" {
   182  			return errors.New("Organization name is required.")
   183  		}
   184  	}
   185  	return nil
   186  }
   187  
   188  func (o *OrganizationOption) complete(args []string) error {
   189  	if len(args) > 0 {
   190  		o.Name = args[0]
   191  	}
   192  
   193  	token, err := GetToken()
   194  	if err != nil {
   195  		return err
   196  	}
   197  
   198  	if o.Organization == nil {
   199  		o.Organization = &CloudOrganization{
   200  			Token:   token,
   201  			APIURL:  APIURL,
   202  			APIPath: APIPath,
   203  		}
   204  	}
   205  
   206  	return nil
   207  }
   208  
   209  func (o *OrganizationOption) runList() error {
   210  	organizations, err := o.Organization.GetOrganizations()
   211  	if err != nil {
   212  		return err
   213  	}
   214  
   215  	if len(organizations.Items) == 0 {
   216  		fmt.Fprintln(o.Out, "you are currently not join in any organization")
   217  		return nil
   218  	}
   219  
   220  	tbl := printer.NewTablePrinter(o.Out)
   221  	tbl.Tbl.SetColumnConfigs([]table.ColumnConfig{
   222  		{Number: 5, WidthMax: 120},
   223  	})
   224  	tbl.SetHeader("NAME", "DISPLAYNAME", "DESCRIPTION", "ROLE", "ORGID")
   225  
   226  	for _, item := range organizations.Items {
   227  		tbl.AddRow(item.Name, item.DisplayName, item.Description, item.Role, item.ID)
   228  	}
   229  
   230  	tbl.Print()
   231  	return nil
   232  }
   233  
   234  func (o *OrganizationOption) runSwitch() error {
   235  	oldOrganizationName, err := o.Organization.switchOrganization(o.Name)
   236  	if err != nil {
   237  		return err
   238  	}
   239  	fmt.Fprintf(o.Out, "Successfully switched from %s to organization: %s\n", oldOrganizationName, o.Name)
   240  	return nil
   241  }
   242  
   243  func (o *OrganizationOption) runCurrent() error {
   244  	currentOrg, err := o.Organization.getCurrentOrganization()
   245  	if err != nil {
   246  		return err
   247  	}
   248  	fmt.Fprintf(o.Out, "Current organization: %s\n", currentOrg)
   249  	return nil
   250  }
   251  
   252  func (o *OrganizationOption) runDescribe() error {
   253  	orgItem, err := o.Organization.getOrganization(o.Name)
   254  	if err != nil {
   255  		return err
   256  	}
   257  
   258  	switch strings.ToLower(o.OutputFormat) {
   259  	case "yaml":
   260  		return o.printYAML(orgItem)
   261  	case "json":
   262  		return o.printJSON(orgItem)
   263  	case "human":
   264  		fallthrough
   265  	default:
   266  		return o.printTable(orgItem)
   267  	}
   268  }
   269  
   270  func (o *OrganizationOption) printYAML(orgItem *OrgItem) error {
   271  	body, err := yaml.Marshal(orgItem)
   272  	if err != nil {
   273  		return err
   274  	}
   275  	fmt.Fprintf(o.Out, "%s\n", body)
   276  	return nil
   277  }
   278  
   279  func (o *OrganizationOption) printJSON(orgItem *OrgItem) error {
   280  	body, err := json.MarshalIndent(orgItem, "", "  ")
   281  	if err != nil {
   282  		return err
   283  	}
   284  	fmt.Fprintf(o.Out, "%s\n", body)
   285  	return nil
   286  }
   287  
   288  func (o *OrganizationOption) printTable(orgItem *OrgItem) error {
   289  	tbl := printer.NewTablePrinter(o.Out)
   290  	tbl.Tbl.SetColumnConfigs([]table.ColumnConfig{
   291  		{Number: 5, WidthMax: 120},
   292  	})
   293  	tbl.SetHeader(
   294  		"NAME",
   295  		"DISPLAYNAME",
   296  		"CREATEDAT",
   297  		"UPDATEDAT",
   298  		"DESCRIPTION",
   299  		"ORGID",
   300  		"ROLE",
   301  	)
   302  
   303  	tbl.AddRow(
   304  		orgItem.Name,
   305  		orgItem.DisplayName,
   306  		orgItem.CreatedAt,
   307  		orgItem.UpdatedAt,
   308  		orgItem.Description,
   309  		orgItem.ID,
   310  		orgItem.Role,
   311  	)
   312  
   313  	tbl.Print()
   314  	return nil
   315  }