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 }