github.com/devcamcar/cli@v0.0.0-20181107134215-706a05759d18/objects/app/apps.go (about)

     1  package app
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  	"text/tabwriter"
     9  
    10  	"context"
    11  	"strings"
    12  
    13  	"github.com/fnproject/cli/common"
    14  	fnclient "github.com/fnproject/fn_go/clientv2"
    15  	apiapps "github.com/fnproject/fn_go/clientv2/apps"
    16  	"github.com/fnproject/fn_go/modelsv2"
    17  	"github.com/fnproject/fn_go/provider"
    18  	"github.com/jmoiron/jsonq"
    19  	"github.com/urfave/cli"
    20  )
    21  
    22  type appsCmd struct {
    23  	provider provider.Provider
    24  	client   *fnclient.Fn
    25  }
    26  
    27  func printApps(c *cli.Context, apps []*modelsv2.App) error {
    28  	outputFormat := strings.ToLower(c.String("output"))
    29  	if outputFormat == "json" {
    30  		var allApps []interface{}
    31  		for _, app := range apps {
    32  			a := struct {
    33  				Name string `json:"name"`
    34  				ID   string `json:"id"`
    35  			}{app.Name,
    36  				app.ID,
    37  			}
    38  			allApps = append(allApps, a)
    39  		}
    40  		b, err := json.MarshalIndent(allApps, "", "    ")
    41  		if err != nil {
    42  			return err
    43  		}
    44  		fmt.Fprint(os.Stdout, string(b))
    45  	} else {
    46  		w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
    47  		fmt.Fprint(w, "NAME", "\t", "ID", "\t", "\n")
    48  		for _, app := range apps {
    49  			fmt.Fprint(w, app.Name, "\t", app.ID, "\t", "\n")
    50  
    51  		}
    52  		if err := w.Flush(); err != nil {
    53  			return err
    54  		}
    55  	}
    56  	return nil
    57  }
    58  
    59  func (a *appsCmd) list(c *cli.Context) error {
    60  	params := &apiapps.ListAppsParams{Context: context.Background()}
    61  	var resApps []*modelsv2.App
    62  	for {
    63  		resp, err := a.client.Apps.ListApps(params)
    64  		if err != nil {
    65  			return err
    66  		}
    67  
    68  		resApps = append(resApps, resp.Payload.Items...)
    69  
    70  		n := c.Int64("n")
    71  		if n < 0 {
    72  			return errors.New("Number of calls: negative value not allowed")
    73  		}
    74  
    75  		howManyMore := n - int64(len(resApps)+len(resp.Payload.Items))
    76  		if howManyMore <= 0 || resp.Payload.NextCursor == "" {
    77  			break
    78  		}
    79  
    80  		params.Cursor = &resp.Payload.NextCursor
    81  	}
    82  
    83  	if len(resApps) == 0 {
    84  		fmt.Fprint(os.Stderr, "No apps found\n")
    85  		return nil
    86  	}
    87  
    88  	if err := printApps(c, resApps); err != nil {
    89  		return err
    90  	}
    91  	return nil
    92  }
    93  
    94  func appWithFlags(c *cli.Context, app *modelsv2.App) {
    95  	if len(c.String("syslog-url")) > 0 {
    96  		app.SyslogURL = c.String("syslog-url")
    97  	}
    98  	if len(c.StringSlice("config")) > 0 {
    99  		app.Config = common.ExtractConfig(c.StringSlice("config"))
   100  	}
   101  	if len(c.StringSlice("annotation")) > 0 {
   102  		app.Annotations = common.ExtractAnnotations(c)
   103  	}
   104  }
   105  
   106  func (a *appsCmd) create(c *cli.Context) error {
   107  	app := &modelsv2.App{
   108  		Name: c.Args().Get(0),
   109  	}
   110  
   111  	appWithFlags(c, app)
   112  
   113  	return CreateApp(a.client, app)
   114  }
   115  
   116  // CreateApp creates a new app using the given client
   117  func CreateApp(a *fnclient.Fn, app *modelsv2.App) error {
   118  	resp, err := a.Apps.CreateApp(&apiapps.CreateAppParams{
   119  		Context: context.Background(),
   120  		Body:    app,
   121  	})
   122  
   123  	if err != nil {
   124  		switch e := err.(type) {
   125  		case *apiapps.CreateAppBadRequest:
   126  			return fmt.Errorf("%v", e.Payload.Message)
   127  		case *apiapps.CreateAppConflict:
   128  			return fmt.Errorf("%v", e.Payload.Message)
   129  		default:
   130  			return err
   131  		}
   132  	}
   133  
   134  	fmt.Println("Successfully created app: ", resp.Payload.Name)
   135  	return nil
   136  }
   137  
   138  func (a *appsCmd) update(c *cli.Context) error {
   139  	appName := c.Args().First()
   140  
   141  	app, err := GetAppByName(a.client, appName)
   142  	if err != nil {
   143  		return err
   144  	}
   145  
   146  	appWithFlags(c, app)
   147  
   148  	if err = PutApp(a.client, app.ID, app); err != nil {
   149  		return err
   150  	}
   151  
   152  	fmt.Println("app", appName, "updated")
   153  	return nil
   154  }
   155  
   156  func (a *appsCmd) setConfig(c *cli.Context) error {
   157  	appName := c.Args().Get(0)
   158  	key := c.Args().Get(1)
   159  	value := c.Args().Get(2)
   160  
   161  	app, err := GetAppByName(a.client, appName)
   162  	if err != nil {
   163  		return err
   164  	}
   165  
   166  	app.Config = make(map[string]string)
   167  	app.Config[key] = value
   168  
   169  	if err = PutApp(a.client, app.ID, app); err != nil {
   170  		return fmt.Errorf("Error updating app configuration: %v", err)
   171  	}
   172  
   173  	fmt.Println(appName, "updated", key, "with", value)
   174  	return nil
   175  }
   176  
   177  func (a *appsCmd) getConfig(c *cli.Context) error {
   178  	appName := c.Args().Get(0)
   179  	key := c.Args().Get(1)
   180  
   181  	app, err := GetAppByName(a.client, appName)
   182  	if err != nil {
   183  		return err
   184  	}
   185  
   186  	val, ok := app.Config[key]
   187  	if !ok {
   188  		return fmt.Errorf("Config key does not exist")
   189  	}
   190  
   191  	fmt.Println(val)
   192  
   193  	return nil
   194  }
   195  
   196  func (a *appsCmd) listConfig(c *cli.Context) error {
   197  	appName := c.Args().Get(0)
   198  
   199  	app, err := GetAppByName(a.client, appName)
   200  	if err != nil {
   201  		return err
   202  	}
   203  
   204  	if len(app.Config) == 0 {
   205  		fmt.Fprintf(os.Stderr, "No config found for app: %s\n", appName)
   206  		return nil
   207  	}
   208  
   209  	w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
   210  	fmt.Fprint(w, "KEY", "\t", "VALUE", "\n")
   211  	for key, val := range app.Config {
   212  		fmt.Fprint(w, key, "\t", val, "\n")
   213  	}
   214  	w.Flush()
   215  
   216  	return nil
   217  }
   218  
   219  func (a *appsCmd) unsetConfig(c *cli.Context) error {
   220  	appName := c.Args().Get(0)
   221  	key := c.Args().Get(1)
   222  
   223  	app, err := GetAppByName(a.client, appName)
   224  	if err != nil {
   225  		return err
   226  	}
   227  	app.Config[key] = ""
   228  
   229  	err = PutApp(a.client, app.ID, app)
   230  	if err != nil {
   231  		return err
   232  	}
   233  
   234  	fmt.Printf("Removed key '%s' from app '%s' \n", key, appName)
   235  	return nil
   236  }
   237  
   238  func (a *appsCmd) inspect(c *cli.Context) error {
   239  	if c.Args().Get(0) == "" {
   240  		return errors.New("Missing app name after the inspect command")
   241  	}
   242  
   243  	appName := c.Args().First()
   244  	prop := c.Args().Get(1)
   245  
   246  	app, err := GetAppByName(a.client, appName)
   247  	if err != nil {
   248  		return err
   249  	}
   250  
   251  	enc := json.NewEncoder(os.Stdout)
   252  	enc.SetIndent("", "\t")
   253  
   254  	if prop == "" {
   255  		enc.Encode(app)
   256  		return nil
   257  	}
   258  
   259  	// TODO: we really need to marshal it here just to
   260  	// unmarshal as map[string]interface{}?
   261  	data, err := json.Marshal(app)
   262  	if err != nil {
   263  		return fmt.Errorf("Could not marshal app: %v", err)
   264  	}
   265  	var inspect map[string]interface{}
   266  	err = json.Unmarshal(data, &inspect)
   267  	if err != nil {
   268  		return fmt.Errorf("Could not unmarshal data: %v", err)
   269  	}
   270  
   271  	jq := jsonq.NewQuery(inspect)
   272  	field, err := jq.Interface(strings.Split(prop, ".")...)
   273  	if err != nil {
   274  		return fmt.Errorf("Failed to inspect field %v", prop)
   275  	}
   276  	enc.Encode(field)
   277  
   278  	return nil
   279  }
   280  
   281  func (a *appsCmd) delete(c *cli.Context) error {
   282  	appName := c.Args().First()
   283  	if appName == "" {
   284  		//return errors.New("App name required to delete")
   285  	}
   286  
   287  	app, err := GetAppByName(a.client, appName)
   288  	if err != nil {
   289  		return err
   290  	}
   291  
   292  	_, err = a.client.Apps.DeleteApp(&apiapps.DeleteAppParams{
   293  		Context: context.Background(),
   294  		AppID:   app.ID,
   295  	})
   296  
   297  	if err != nil {
   298  		switch e := err.(type) {
   299  		case *apiapps.DeleteAppNotFound:
   300  			return errors.New(e.Payload.Message)
   301  		}
   302  		return err
   303  	}
   304  
   305  	fmt.Println("App", appName, "deleted")
   306  	return nil
   307  }
   308  
   309  // PutApp updates the app with the given ID using the content of the provided app
   310  func PutApp(a *fnclient.Fn, appID string, app *modelsv2.App) error {
   311  	_, err := a.Apps.UpdateApp(&apiapps.UpdateAppParams{
   312  		Context: context.Background(),
   313  		AppID:   appID,
   314  		Body:    app,
   315  	})
   316  
   317  	if err != nil {
   318  		switch e := err.(type) {
   319  		case *apiapps.UpdateAppBadRequest:
   320  			return fmt.Errorf("%s", e.Payload.Message)
   321  
   322  		default:
   323  			return err
   324  		}
   325  	}
   326  
   327  	return nil
   328  }
   329  
   330  // NameNotFoundError error for app not found when looked up by name
   331  type NameNotFoundError struct {
   332  	Name string
   333  }
   334  
   335  func (n NameNotFoundError) Error() string {
   336  	return fmt.Sprintf("app %s not found", n.Name)
   337  }
   338  
   339  // GetAppByName looks up an app by name using the given client
   340  func GetAppByName(client *fnclient.Fn, appName string) (*modelsv2.App, error) {
   341  	appsResp, err := client.Apps.ListApps(&apiapps.ListAppsParams{
   342  		Context: context.Background(),
   343  		Name:    &appName,
   344  	})
   345  	if err != nil {
   346  		return nil, err
   347  	}
   348  
   349  	var app *modelsv2.App
   350  	if len(appsResp.Payload.Items) > 0 {
   351  		app = appsResp.Payload.Items[0]
   352  	} else {
   353  		return nil, NameNotFoundError{appName}
   354  	}
   355  
   356  	return app, nil
   357  }