github.com/iron-io/functions@v0.0.0-20180820112432-d59d7d1c40b2/fn/commands/apps.go (about)

     1  package commands
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"os"
     9  	"strings"
    10  
    11  	"github.com/iron-io/functions/fn/common"
    12  	"github.com/iron-io/functions_go"
    13  	fnclient "github.com/iron-io/functions_go/client"
    14  	apiapps "github.com/iron-io/functions_go/client/apps"
    15  	"github.com/iron-io/functions_go/models"
    16  	"github.com/jmoiron/jsonq"
    17  	"github.com/urfave/cli"
    18  )
    19  
    20  type appsCmd struct {
    21  	client *fnclient.Functions
    22  }
    23  
    24  func Apps() cli.Command {
    25  	a := appsCmd{client: common.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  				Usage:  "delete an app",
    93  				Action: a.delete,
    94  			},
    95  		},
    96  	}
    97  }
    98  
    99  func (a *appsCmd) list(c *cli.Context) error {
   100  	resp, err := a.client.Apps.GetApps(&apiapps.GetAppsParams{
   101  		Context: context.Background(),
   102  	})
   103  
   104  	if err != nil {
   105  		switch err.(type) {
   106  		case *apiapps.GetAppsAppNotFound:
   107  			return fmt.Errorf("error: %v", err.(*apiapps.GetAppsAppNotFound).Payload.Error.Message)
   108  		case *apiapps.GetAppsAppDefault:
   109  			return fmt.Errorf("unexpected error: %v", err.(*apiapps.GetAppsAppDefault).Payload.Error.Message)
   110  		}
   111  		return fmt.Errorf("unexpected error: %v", err)
   112  	}
   113  
   114  	if len(resp.Payload.Apps) == 0 {
   115  		fmt.Println("no apps found")
   116  		return nil
   117  	}
   118  
   119  	for _, app := range resp.Payload.Apps {
   120  		fmt.Println(app.Name)
   121  	}
   122  
   123  	return nil
   124  }
   125  
   126  func (a *appsCmd) create(c *cli.Context) error {
   127  	body := &models.AppWrapper{App: &models.App{
   128  		Name:   c.Args().Get(0),
   129  		Config: common.ExtractEnvConfig(c.StringSlice("config")),
   130  	}}
   131  
   132  	resp, err := a.client.Apps.PostApps(&apiapps.PostAppsParams{
   133  		Context: context.Background(),
   134  		Body:    body,
   135  	})
   136  
   137  	if err != nil {
   138  		switch err.(type) {
   139  		case *apiapps.PostAppsBadRequest:
   140  			return fmt.Errorf("error: %v", err.(*apiapps.PostAppsBadRequest).Payload.Error.Message)
   141  		case *apiapps.PostAppsConflict:
   142  			return fmt.Errorf("error: %v", err.(*apiapps.PostAppsConflict).Payload.Error.Message)
   143  		case *apiapps.PostAppsDefault:
   144  			return fmt.Errorf("unexpected error: %v", err.(*apiapps.PostAppsDefault).Payload.Error.Message)
   145  		}
   146  		return fmt.Errorf("unexpected error: %v", err)
   147  	}
   148  
   149  	fmt.Println(resp.Payload.App.Name, "created")
   150  	return nil
   151  }
   152  
   153  func (a *appsCmd) update(c *cli.Context) error {
   154  	appName := c.Args().First()
   155  
   156  	patchedApp := &functions.App{
   157  		Config: common.ExtractEnvConfig(c.StringSlice("config")),
   158  	}
   159  
   160  	err := a.patchApp(appName, patchedApp)
   161  	if err != nil {
   162  		return err
   163  	}
   164  
   165  	fmt.Println("app", appName, "updated")
   166  	return nil
   167  }
   168  
   169  func (a *appsCmd) configSet(c *cli.Context) error {
   170  	appName := c.Args().Get(0)
   171  	key := c.Args().Get(1)
   172  	value := c.Args().Get(2)
   173  
   174  	app := &functions.App{
   175  		Config: make(map[string]string),
   176  	}
   177  
   178  	app.Config[key] = value
   179  
   180  	if err := a.patchApp(appName, app); err != nil {
   181  		return fmt.Errorf("error updating app configuration: %v", err)
   182  	}
   183  
   184  	fmt.Println(appName, "updated", key, "with", value)
   185  	return nil
   186  }
   187  
   188  func (a *appsCmd) configUnset(c *cli.Context) error {
   189  	appName := c.Args().Get(0)
   190  	key := c.Args().Get(1)
   191  
   192  	app := &functions.App{
   193  		Config: make(map[string]string),
   194  	}
   195  
   196  	app.Config["-"+key] = ""
   197  
   198  	if err := a.patchApp(appName, app); err != nil {
   199  		return fmt.Errorf("error updating app configuration: %v", err)
   200  	}
   201  
   202  	fmt.Printf("removed key '%s' from app '%s' \n", key, appName)
   203  	return nil
   204  }
   205  
   206  func (a *appsCmd) patchApp(appName string, app *functions.App) error {
   207  	resp, err := a.client.Apps.GetAppsApp(&apiapps.GetAppsAppParams{
   208  		Context: context.Background(),
   209  		App:     appName,
   210  	})
   211  
   212  	if err != nil {
   213  		switch err.(type) {
   214  		case *apiapps.GetAppsAppNotFound:
   215  			return fmt.Errorf("error: %v", err.(*apiapps.GetAppsAppNotFound).Payload.Error.Message)
   216  		case *apiapps.GetAppsAppDefault:
   217  			return fmt.Errorf("unexpected error: %v", err.(*apiapps.GetAppsAppDefault).Payload.Error.Message)
   218  		}
   219  		return fmt.Errorf("unexpected error: %v", err)
   220  	}
   221  
   222  	if resp.Payload.App.Config == nil {
   223  		resp.Payload.App.Config = map[string]string{}
   224  	}
   225  
   226  	resp.Payload.App.Name = ""
   227  	if app != nil {
   228  		if app.Config != nil {
   229  			for k, v := range app.Config {
   230  				if string(k[0]) == "-" {
   231  					delete(resp.Payload.App.Config, string(k[1:]))
   232  					continue
   233  				}
   234  				resp.Payload.App.Config[k] = v
   235  			}
   236  		}
   237  	}
   238  
   239  	body := &models.AppWrapper{App: resp.Payload.App}
   240  
   241  	_, err = a.client.Apps.PatchAppsApp(&apiapps.PatchAppsAppParams{
   242  		Context: context.Background(),
   243  		App:     appName,
   244  		Body:    body,
   245  	})
   246  
   247  	if err != nil {
   248  		switch err.(type) {
   249  		case *apiapps.PatchAppsAppBadRequest:
   250  			return errors.New(err.(*apiapps.PatchAppsAppBadRequest).Payload.Error.Message)
   251  		case *apiapps.PatchAppsAppNotFound:
   252  			return errors.New(err.(*apiapps.PatchAppsAppNotFound).Payload.Error.Message)
   253  		case *apiapps.PatchAppsAppDefault:
   254  			return errors.New(err.(*apiapps.PatchAppsAppDefault).Payload.Error.Message)
   255  		}
   256  		return fmt.Errorf("unexpected error: %v", err)
   257  	}
   258  
   259  	return nil
   260  }
   261  
   262  func (a *appsCmd) inspect(c *cli.Context) error {
   263  	if c.Args().Get(0) == "" {
   264  		return errors.New("error: missing app name after the inspect command")
   265  	}
   266  
   267  	appName := c.Args().First()
   268  	prop := c.Args().Get(1)
   269  
   270  	resp, err := a.client.Apps.GetAppsApp(&apiapps.GetAppsAppParams{
   271  		Context: context.Background(),
   272  		App:     appName,
   273  	})
   274  
   275  	if err != nil {
   276  		switch err.(type) {
   277  		case *apiapps.GetAppsAppNotFound:
   278  			return fmt.Errorf("error: %v", err.(*apiapps.GetAppsAppNotFound).Payload.Error.Message)
   279  		case *apiapps.GetAppsAppDefault:
   280  			return fmt.Errorf("unexpected error: %v", err.(*apiapps.GetAppsAppDefault).Payload.Error.Message)
   281  		}
   282  		return fmt.Errorf("unexpected error: %v", err)
   283  	}
   284  
   285  	enc := json.NewEncoder(os.Stdout)
   286  	enc.SetIndent("", "\t")
   287  
   288  	if prop == "" {
   289  		enc.Encode(resp.Payload.App)
   290  		return nil
   291  	}
   292  
   293  	// TODO: we really need to marshal it here just to
   294  	// unmarshal as map[string]interface{}?
   295  	data, err := json.Marshal(resp.Payload.App)
   296  	if err != nil {
   297  		return fmt.Errorf("error inspect app: %v", err)
   298  	}
   299  	var inspect map[string]interface{}
   300  	err = json.Unmarshal(data, &inspect)
   301  	if err != nil {
   302  		return fmt.Errorf("error inspect app: %v", err)
   303  	}
   304  
   305  	jq := jsonq.NewQuery(inspect)
   306  	field, err := jq.Interface(strings.Split(prop, ".")...)
   307  	if err != nil {
   308  		return errors.New("failed to inspect that apps's field")
   309  	}
   310  	enc.Encode(field)
   311  
   312  	return nil
   313  }
   314  
   315  func (a *appsCmd) delete(c *cli.Context) error {
   316  	appName := c.Args().First()
   317  	if appName == "" {
   318  		return errors.New("error: deleting an app takes one argument, an app name")
   319  	}
   320  
   321  	_, err := a.client.Apps.DeleteAppsApp(&apiapps.DeleteAppsAppParams{
   322  		Context: context.Background(),
   323  		App:     appName,
   324  	})
   325  
   326  	if err != nil {
   327  		switch err.(type) {
   328  		case *apiapps.DeleteAppsAppNotFound:
   329  			return errors.New(err.(*apiapps.DeleteAppsAppNotFound).Payload.Error.Message)
   330  		case *apiapps.DeleteAppsAppDefault:
   331  			return errors.New(err.(*apiapps.DeleteAppsAppDefault).Payload.Error.Message)
   332  		}
   333  		return fmt.Errorf("unexpected error: %v", err)
   334  	}
   335  
   336  	fmt.Println("app", appName, "deleted")
   337  	return nil
   338  }