github.com/tonto/cli@v0.0.0-20180104210444-aec958fa47db/apps.go (about)

     1  package main
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  
     9  	"context"
    10  	"strings"
    11  
    12  	client "github.com/fnproject/cli/client"
    13  	fnclient "github.com/fnproject/fn_go/client"
    14  	apiapps "github.com/fnproject/fn_go/client/apps"
    15  	"github.com/fnproject/fn_go/models"
    16  	"github.com/jmoiron/jsonq"
    17  	"github.com/urfave/cli"
    18  )
    19  
    20  type appsCmd struct {
    21  	client *fnclient.Fn
    22  }
    23  
    24  func apps() cli.Command {
    25  	a := appsCmd{client: client.APIClient()}
    26  
    27  	return cli.Command{
    28  		Name:  "apps",
    29  		Usage: "manage applications",
    30  		Subcommands: []cli.Command{
    31  			{
    32  				Name:      "create",
    33  				Aliases:   []string{"c"},
    34  				Usage:     "create a new app",
    35  				ArgsUsage: "<app>",
    36  				Action:    a.create,
    37  				Flags: []cli.Flag{
    38  					cli.StringSliceFlag{
    39  						Name:  "config",
    40  						Usage: "application configuration",
    41  					},
    42  				},
    43  			},
    44  			{
    45  				Name:      "inspect",
    46  				Aliases:   []string{"i"},
    47  				Usage:     "retrieve one or all apps properties",
    48  				ArgsUsage: "<app> [property.[key]]",
    49  				Action:    a.inspect,
    50  			},
    51  			{
    52  				Name:      "update",
    53  				Aliases:   []string{"u"},
    54  				Usage:     "update an `app`",
    55  				ArgsUsage: "<app>",
    56  				Action:    a.update,
    57  				Flags: []cli.Flag{
    58  					cli.StringSliceFlag{
    59  						Name:  "config,c",
    60  						Usage: "route configuration",
    61  					},
    62  				},
    63  			},
    64  			{
    65  				Name:  "config",
    66  				Usage: "manage your apps's function configs",
    67  				Subcommands: []cli.Command{
    68  					{
    69  						Name:      "set",
    70  						Aliases:   []string{"s"},
    71  						Usage:     "store a configuration key for this application",
    72  						ArgsUsage: "<app> <key> <value>",
    73  						Action:    a.configSet,
    74  					},
    75  					{
    76  						Name:      "unset",
    77  						Aliases:   []string{"u"},
    78  						Usage:     "remove a configuration key for this application",
    79  						ArgsUsage: "<app> <key>",
    80  						Action:    a.configUnset,
    81  					},
    82  				},
    83  			},
    84  			{
    85  				Name:    "list",
    86  				Aliases: []string{"l"},
    87  				Usage:   "list all apps",
    88  				Action:  a.list,
    89  			},
    90  			{
    91  				Name:   "delete",
    92  				Aliases:   []string{"d"},
    93  				Usage:  "delete an app",
    94  				Action: a.delete,
    95  			},
    96  		},
    97  	}
    98  }
    99  
   100  func (a *appsCmd) list(c *cli.Context) error {
   101  	resp, err := a.client.Apps.GetApps(&apiapps.GetAppsParams{
   102  		Context: context.Background(),
   103  	})
   104  
   105  	if err != nil {
   106  		switch e := err.(type) {
   107  		case *apiapps.GetAppsAppNotFound:
   108  			return fmt.Errorf("%v", e.Payload.Error.Message)
   109  		case *apiapps.GetAppsAppDefault:
   110  			return fmt.Errorf("%v", e.Payload.Error.Message)
   111  		case *apiapps.GetAppsDefault:
   112  			// this is the one getting called, not sure what the one above is?
   113  			return fmt.Errorf("%v", e.Payload.Error.Message)
   114  		default:
   115  			return fmt.Errorf("%v", err)
   116  		}
   117  	}
   118  
   119  	if len(resp.Payload.Apps) == 0 {
   120  		fmt.Println("no apps found")
   121  		return nil
   122  	}
   123  
   124  	for _, app := range resp.Payload.Apps {
   125  		fmt.Println(app.Name)
   126  	}
   127  
   128  	return nil
   129  }
   130  
   131  func (a *appsCmd) create(c *cli.Context) error {
   132  	body := &models.AppWrapper{App: &models.App{
   133  		Name:   c.Args().Get(0),
   134  		Config: extractEnvConfig(c.StringSlice("config")),
   135  	}}
   136  
   137  	resp, err := a.client.Apps.PostApps(&apiapps.PostAppsParams{
   138  		Context: context.Background(),
   139  		Body:    body,
   140  	})
   141  
   142  	if err != nil {
   143  		switch e := err.(type) {
   144  		case *apiapps.PostAppsBadRequest:
   145  			return fmt.Errorf("%v", e.Payload.Error.Message)
   146  		case *apiapps.PostAppsConflict:
   147  			return fmt.Errorf("%v", e.Payload.Error.Message)
   148  		case *apiapps.PostAppsDefault:
   149  			return fmt.Errorf("%v", e.Payload.Error.Message)
   150  		default:
   151  			return fmt.Errorf("%v", err)
   152  		}
   153  	}
   154  
   155  	fmt.Println("Successfully created app: ", resp.Payload.App.Name)
   156  	return nil
   157  }
   158  
   159  func (a *appsCmd) update(c *cli.Context) error {
   160  	appName := c.Args().First()
   161  
   162  	patchedApp := &models.App{
   163  		Config: extractEnvConfig(c.StringSlice("config")),
   164  	}
   165  
   166  	err := a.patchApp(appName, patchedApp)
   167  	if err != nil {
   168  		return err
   169  	}
   170  
   171  	fmt.Println("app", appName, "updated")
   172  	return nil
   173  }
   174  
   175  func (a *appsCmd) configSet(c *cli.Context) error {
   176  	appName := c.Args().Get(0)
   177  	key := c.Args().Get(1)
   178  	value := c.Args().Get(2)
   179  
   180  	app := &models.App{
   181  		Config: make(map[string]string),
   182  	}
   183  
   184  	app.Config[key] = value
   185  
   186  	if err := a.patchApp(appName, app); err != nil {
   187  		return fmt.Errorf("error updating app configuration: %v", err)
   188  	}
   189  
   190  	fmt.Println(appName, "updated", key, "with", value)
   191  	return nil
   192  }
   193  
   194  func (a *appsCmd) configUnset(c *cli.Context) error {
   195  	appName := c.Args().Get(0)
   196  	key := c.Args().Get(1)
   197  
   198  	app := &models.App{
   199  		Config: make(map[string]string),
   200  	}
   201  
   202  	app.Config[key] = ""
   203  
   204  	if err := a.patchApp(appName, app); err != nil {
   205  		return fmt.Errorf("error updating app configuration: %v", err)
   206  	}
   207  
   208  	fmt.Printf("removed key '%s' from app '%s' \n", key, appName)
   209  	return nil
   210  }
   211  
   212  func (a *appsCmd) patchApp(appName string, app *models.App) error {
   213  	_, err := a.client.Apps.PatchAppsApp(&apiapps.PatchAppsAppParams{
   214  		Context: context.Background(),
   215  		App:     appName,
   216  		Body:    &models.AppWrapper{App: app},
   217  	})
   218  
   219  	if err != nil {
   220  		switch e := err.(type) {
   221  		case *apiapps.PatchAppsAppBadRequest:
   222  			return errors.New(e.Payload.Error.Message)
   223  		case *apiapps.PatchAppsAppNotFound:
   224  			return errors.New(e.Payload.Error.Message)
   225  		case *apiapps.PatchAppsAppDefault:
   226  			return errors.New(e.Payload.Error.Message)
   227  		default:
   228  			return fmt.Errorf("%v", err)
   229  		}
   230  	}
   231  
   232  	return nil
   233  }
   234  
   235  func (a *appsCmd) inspect(c *cli.Context) error {
   236  	if c.Args().Get(0) == "" {
   237  		return errors.New("missing app name after the inspect command")
   238  	}
   239  
   240  	appName := c.Args().First()
   241  	prop := c.Args().Get(1)
   242  
   243  	resp, err := a.client.Apps.GetAppsApp(&apiapps.GetAppsAppParams{
   244  		Context: context.Background(),
   245  		App:     appName,
   246  	})
   247  
   248  	if err != nil {
   249  		switch e := err.(type) {
   250  		case *apiapps.GetAppsAppNotFound:
   251  			return fmt.Errorf("%v", e.Payload.Error.Message)
   252  		case *apiapps.GetAppsAppDefault:
   253  			return fmt.Errorf("%v", e.Payload.Error.Message)
   254  		default:
   255  			return fmt.Errorf("%v", err)
   256  		}
   257  	}
   258  
   259  	enc := json.NewEncoder(os.Stdout)
   260  	enc.SetIndent("", "\t")
   261  
   262  	if prop == "" {
   263  		enc.Encode(resp.Payload.App)
   264  		return nil
   265  	}
   266  
   267  	// TODO: we really need to marshal it here just to
   268  	// unmarshal as map[string]interface{}?
   269  	data, err := json.Marshal(resp.Payload.App)
   270  	if err != nil {
   271  		return fmt.Errorf("could not marshal app: %v", err)
   272  	}
   273  	var inspect map[string]interface{}
   274  	err = json.Unmarshal(data, &inspect)
   275  	if err != nil {
   276  		return fmt.Errorf("could not unmarshal data: %v", err)
   277  	}
   278  
   279  	jq := jsonq.NewQuery(inspect)
   280  	field, err := jq.Interface(strings.Split(prop, ".")...)
   281  	if err != nil {
   282  		return fmt.Errorf("failed to inspect field %v", prop)
   283  	}
   284  	enc.Encode(field)
   285  
   286  	return nil
   287  }
   288  
   289  func (a *appsCmd) delete(c *cli.Context) error {
   290  	appName := c.Args().First()
   291  	if appName == "" {
   292  		return errors.New("app name required to delete")
   293  	}
   294  
   295  	_, err := a.client.Apps.DeleteAppsApp(&apiapps.DeleteAppsAppParams{
   296  		Context: context.Background(),
   297  		App:     appName,
   298  	})
   299  
   300  	if err != nil {
   301  		switch e := err.(type) {
   302  		case *apiapps.DeleteAppsAppNotFound:
   303  			return errors.New(e.Payload.Error.Message)
   304  		case *apiapps.DeleteAppsAppDefault:
   305  			return errors.New(e.Payload.Error.Message)
   306  		}
   307  		return fmt.Errorf("%v", err)
   308  	}
   309  
   310  	fmt.Println("App", appName, "deleted")
   311  	return nil
   312  }