github.com/section/sectionctl@v1.12.3/commands/apps.go (about)

     1  package commands
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  	"strconv"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/rs/zerolog/log"
    12  
    13  	"github.com/olekukonko/tablewriter"
    14  	"github.com/section/sectionctl/api"
    15  )
    16  
    17  // AppsCmd manages apps on Section
    18  type AppsCmd struct {
    19  	List   AppsListCmd   `cmd help:"List apps on Section." default:"1"`
    20  	Info   AppsInfoCmd   `cmd help:"Show detailed app information on Section."`
    21  	Create AppsCreateCmd `cmd help:"Create new app on Section."`
    22  	Delete AppsDeleteCmd `cmd help:"DANGER ZONE. This deletes an existing app on Section."`
    23  	Stacks AppsStacksCmd `cmd help:"See the available stacks to create new apps with."`
    24  }
    25  
    26  // AppsListCmd handles listing apps running on Section
    27  type AppsListCmd struct {
    28  	AccountID int `short:"a" help:"Account ID to find apps under"`
    29  }
    30  
    31  // NewTable returns a table with sectionctl standard formatting
    32  func NewTable(cli *CLI, out io.Writer) (t *tablewriter.Table) {
    33  	if cli.Quiet {
    34  		out = io.Discard
    35  	}
    36  	t = tablewriter.NewWriter(out)
    37  	t.SetBorders(tablewriter.Border{Left: true, Top: true, Right: true, Bottom: true})
    38  	t.SetCenterSeparator("|")
    39  	t.SetAlignment(tablewriter.ALIGN_LEFT)
    40  	return t
    41  }
    42  
    43  // Run executes the command
    44  func (c *AppsListCmd) Run(cli *CLI, logWriters *LogWriters) (err error){
    45  	s := NewSpinner("Looking up apps",logWriters)
    46  	s.Start()
    47  
    48  	accounts, err := api.Accounts()
    49  	if err != nil {
    50  		s. Stop()
    51  		log.Error().Err(err).Msg("Unable to look up accounts");
    52  		os.Exit(1)
    53  	}
    54  	s.Stop()
    55  	if c.AccountID != 0{
    56  		newAct := []api.Account{}
    57  		for _, a := range accounts {
    58  			if a.ID == c.AccountID{
    59  				newAct = append(newAct, a)
    60  			}
    61  			accounts = newAct
    62  		}
    63  		if(len(newAct) == 0){
    64  			log.Info().Int("Account ID",c.AccountID).Msg("Unable to find accounts where")
    65  			os.Exit(1)
    66  		}
    67  	}
    68  	fmt.Println()
    69  	fmt.Println()
    70  	for _, acc := range accounts {
    71  		log.Info().Msg(fmt.Sprint(HiWhite("Account #"),HiWhite(strconv.Itoa(acc.ID))," - ", HiYellow(acc.AccountName)))
    72  		table := NewTable(cli, os.Stdout)
    73  		table.SetHeader([]string{"App ID", "App Name"})
    74  		table.SetColumnColor(tablewriter.Colors{tablewriter.Normal,tablewriter.FgWhiteColor},
    75  		tablewriter.Colors{tablewriter.Normal, tablewriter.FgHiGreenColor})
    76  		table.SetAlignment(tablewriter.ALIGN_LEFT)
    77  		table.SetCenterSeparator("")
    78  		table.SetColumnSeparator("")
    79  		table.SetNoWhiteSpace(true)
    80  		table.SetAutoMergeCells(true)
    81  		table.SetRowLine(true)
    82  		for _, app := range acc.Applications {
    83  				r := []string{strconv.Itoa(app.ID), strings.Trim(app.ApplicationName,"\"")}
    84  				table.Append(r)
    85  		}
    86  		table.Render()
    87  		fmt.Println()
    88  		fmt.Println()
    89  	}
    90  	return err
    91  }
    92  
    93  // AppsInfoCmd shows detailed information on an app running on Section
    94  type AppsInfoCmd struct {
    95  	AccountID int `required short:"a"`
    96  	AppID     int `required short:"i"`
    97  }
    98  
    99  // Run executes the command
   100  func (c *AppsInfoCmd) Run(cli *CLI, logWriters *LogWriters) (err error) {
   101  	s := NewSpinner("Looking up app info", logWriters)
   102  	s.Start()
   103  
   104  	app, err := api.Application(c.AccountID, c.AppID)
   105  	s.Stop()
   106  	fmt.Println()
   107  	if err != nil {
   108  		return err
   109  	}
   110  
   111  	if !(cli.Quiet){
   112  		fmt.Printf("🌎🌏🌍\n")
   113  		fmt.Printf("App Name: %s\n", app.ApplicationName)
   114  		fmt.Printf("App ID: %d\n", app.ID)
   115  		fmt.Printf("Environment count: %d\n", len(app.Environments))
   116  
   117  		for i, env := range app.Environments {
   118  			fmt.Printf("\n-----------------\n\n")
   119  			fmt.Printf("Environment #%d: %s (ID:%d)\n\n", i+1, env.EnvironmentName, env.ID)
   120  			fmt.Printf("💬 Domains (%d total)\n", len(env.Domains))
   121  
   122  			for _, dom := range env.Domains {
   123  				fmt.Println()
   124  
   125  				table := NewTable(cli, os.Stdout)
   126  				table.SetHeader([]string{"Attribute", "Value"})
   127  				table.SetHeaderColor(tablewriter.Colors{tablewriter.Normal,tablewriter.FgWhiteColor},
   128  					tablewriter.Colors{tablewriter.Normal, tablewriter.FgWhiteColor})
   129  				table.SetColumnColor(tablewriter.Colors{tablewriter.Normal,tablewriter.FgWhiteColor},
   130  					tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiCyanColor})
   131  				table.SetAutoMergeCells(true)
   132  				r := [][]string{
   133  					{"Domain name", dom.Name},
   134  					{"Zone name", dom.ZoneName},
   135  					{"CNAME", dom.CNAME},
   136  					{"Mode", dom.Mode},
   137  				}
   138  				table.AppendBulk(r)
   139  				table.Render()
   140  			}
   141  
   142  			fmt.Println()
   143  			mod := "modules"
   144  			if len(env.Stack) == 1 {
   145  				mod = "module"
   146  			}
   147  			fmt.Printf("🥞 Stack (%d %s total)\n", len(env.Stack), mod)
   148  			fmt.Println()
   149  
   150  			table := NewTable(cli, os.Stdout)
   151  			table.SetHeader([]string{"Name", "Image"})
   152  			table.SetHeaderColor(tablewriter.Colors{tablewriter.Normal,tablewriter.FgWhiteColor},
   153  				tablewriter.Colors{tablewriter.Normal, tablewriter.FgWhiteColor})
   154  			table.SetColumnColor(tablewriter.Colors{tablewriter.Normal,tablewriter.FgWhiteColor},
   155  				tablewriter.Colors{tablewriter.Bold, tablewriter.FgHiCyanColor})
   156  			table.SetAutoMergeCells(true)
   157  			for _, p := range env.Stack {
   158  				r := []string{p.Name, p.Image}
   159  				table.Append(r)
   160  			}
   161  			table.Render()
   162  		}
   163  
   164  		fmt.Println()
   165  	}
   166  
   167  
   168  	return err
   169  }
   170  
   171  // AppsCreateCmd handles creating apps on Section
   172  type AppsCreateCmd struct {
   173  	AccountID int    `required short:"a" help:"ID of account to create the app under"`
   174  	Hostname  string `required short:"d" help:"FQDN the app can be accessed at"`
   175  	Origin    string `required short:"g" help:"URL to fetch the origin"`
   176  	StackName string `required short:"s" help:"Name of stack to deploy. Try, for example, nodejs-basic"`
   177  }
   178  
   179  // Run executes the command
   180  func (c *AppsCreateCmd) Run(logWriters *LogWriters) (err error) {	
   181  	s := NewSpinner(fmt.Sprintf("Creating new app %s", c.Hostname),logWriters)
   182  	s.Start()
   183  
   184  	api.Timeout = 120 * time.Second // this specific request can take a long time
   185  	r, err := api.ApplicationCreate(c.AccountID, c.Hostname, c.Origin, c.StackName)
   186  	s.Stop()
   187  	fmt.Println()
   188  	if err != nil {
   189  		if err == api.ErrStatusForbidden {
   190  			stacks, herr := api.Stacks()
   191  			if herr != nil {
   192  				return fmt.Errorf("unable to query stacks: %w", herr)
   193  			}
   194  			for _, s := range stacks {
   195  				if s.Name == c.StackName {
   196  					return err
   197  				}
   198  			}
   199  			return fmt.Errorf("bad request: unable to find stack %s", c.StackName)
   200  		}
   201  		return err
   202  	}
   203  
   204  	log.Info().Msg(fmt.Sprintf("\nSuccess: created app '%s' with id '%d'\n", r.ApplicationName, r.ID))
   205  
   206  	return err
   207  }
   208  
   209  // AppsDeleteCmd handles deleting apps on Section
   210  type AppsDeleteCmd struct {
   211  	AccountID int `required short:"a" help:"ID of account the app belongs to"`
   212  	AppID     int `required short:"i" help:"ID of the app to delete"`
   213  }
   214  
   215  // Run executes the command
   216  func (c *AppsDeleteCmd) Run(logWriters *LogWriters) (err error) {
   217  	s := NewSpinner(fmt.Sprintf("Deleting app with id '%d'", c.AppID),logWriters)
   218  	s.Start()
   219  
   220  	api.Timeout = 120 * time.Second // this specific request can take a long time
   221  	_, err = api.ApplicationDelete(c.AccountID, c.AppID)
   222  	s.Stop()
   223  	if err != nil {
   224  		return err
   225  	}
   226  	
   227  	log.Info().Msg(fmt.Sprintf("\nSuccess: deleted app with id '%d'\n", c.AppID))
   228  
   229  	return err
   230  }
   231  
   232  // AppsStacksCmd lists available stacks to create new apps with
   233  type AppsStacksCmd struct{}
   234  
   235  // Run executes the command
   236  func (c *AppsStacksCmd) Run(cli *CLI, logWriters *LogWriters) (err error) {
   237  	s := NewSpinner("Looking up stacks",logWriters)
   238  	s.Start()
   239  	k, err := api.Stacks()
   240  	s.Stop()
   241  	if err != nil {
   242  		return fmt.Errorf("unable to look up stacks: %w", err)
   243  	}
   244  
   245  	table := NewTable(cli, os.Stdout)
   246  	table.SetHeader([]string{"Name", "Label", "Description", "Type"})
   247  
   248  	for _, s := range k {
   249  		r := []string{s.Name, s.Label, s.Description, s.Type}
   250  		table.Append(r)
   251  	}
   252  
   253  	table.Render()
   254  	return err
   255  }