Robin Thellend | 18205cf | 2014-10-21 13:53:59 -0700 | [diff] [blame] | 1 | package main |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 2 | |
| 3 | import ( |
| 4 | "bytes" |
| 5 | "encoding/json" |
| 6 | "errors" |
| 7 | "fmt" |
| 8 | "io/ioutil" |
| 9 | "os" |
| 10 | "os/exec" |
| 11 | "strings" |
Matt Rosencrantz | 137b8d2 | 2014-08-18 09:56:15 -0700 | [diff] [blame] | 12 | "time" |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 13 | |
Jiri Simsa | bc26d69 | 2014-11-19 18:30:55 -0800 | [diff] [blame] | 14 | "veyron.io/lib/cmdline" |
Jiri Simsa | 519c507 | 2014-09-17 21:37:57 -0700 | [diff] [blame] | 15 | "veyron.io/veyron/veyron/services/mgmt/repository" |
Jiri Simsa | 519c507 | 2014-09-17 21:37:57 -0700 | [diff] [blame] | 16 | "veyron.io/veyron/veyron2/context" |
Jiri Simsa | 519c507 | 2014-09-17 21:37:57 -0700 | [diff] [blame] | 17 | "veyron.io/veyron/veyron2/services/mgmt/application" |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 18 | ) |
| 19 | |
Todd Wang | 702385a | 2014-11-07 01:54:08 -0800 | [diff] [blame] | 20 | func getEnvelopeJSON(ctx context.T, app repository.ApplicationClientMethods, profiles string) ([]byte, error) { |
Matt Rosencrantz | 137b8d2 | 2014-08-18 09:56:15 -0700 | [diff] [blame] | 21 | env, err := app.Match(ctx, strings.Split(profiles, ",")) |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 22 | if err != nil { |
Jiri Simsa | 1023a34 | 2014-08-20 18:01:22 -0700 | [diff] [blame] | 23 | return nil, err |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 24 | } |
| 25 | j, err := json.MarshalIndent(env, "", " ") |
| 26 | if err != nil { |
Jiri Simsa | 1023a34 | 2014-08-20 18:01:22 -0700 | [diff] [blame] | 27 | return nil, fmt.Errorf("MarshalIndent(%v) failed: %v", env, err) |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 28 | } |
| 29 | return j, nil |
| 30 | } |
| 31 | |
Todd Wang | 702385a | 2014-11-07 01:54:08 -0800 | [diff] [blame] | 32 | func putEnvelopeJSON(ctx context.T, app repository.ApplicationClientMethods, profiles string, j []byte) error { |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 33 | var env application.Envelope |
| 34 | if err := json.Unmarshal(j, &env); err != nil { |
Jiri Simsa | 1023a34 | 2014-08-20 18:01:22 -0700 | [diff] [blame] | 35 | return fmt.Errorf("Unmarshal(%v) failed: %v", string(j), err) |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 36 | } |
Matt Rosencrantz | 137b8d2 | 2014-08-18 09:56:15 -0700 | [diff] [blame] | 37 | if err := app.Put(ctx, strings.Split(profiles, ","), env); err != nil { |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 38 | return err |
| 39 | } |
| 40 | return nil |
| 41 | } |
| 42 | |
| 43 | func promptUser(cmd *cmdline.Command, msg string) string { |
| 44 | fmt.Fprint(cmd.Stdout(), msg) |
| 45 | var answer string |
| 46 | if _, err := fmt.Scanf("%s", &answer); err != nil { |
| 47 | return "" |
| 48 | } |
| 49 | return answer |
| 50 | } |
| 51 | |
| 52 | var cmdMatch = &cmdline.Command{ |
| 53 | Run: runMatch, |
| 54 | Name: "match", |
| 55 | Short: "Shows the first matching envelope that matches the given profiles.", |
| 56 | Long: "Shows the first matching envelope that matches the given profiles.", |
| 57 | ArgsName: "<application> <profiles>", |
| 58 | ArgsLong: ` |
| 59 | <application> is the full name of the application. |
| 60 | <profiles> is a comma-separated list of profiles.`, |
| 61 | } |
| 62 | |
| 63 | func runMatch(cmd *cmdline.Command, args []string) error { |
| 64 | if expected, got := 2, len(args); expected != got { |
Todd Wang | a615e4d | 2014-09-29 16:56:05 -0700 | [diff] [blame] | 65 | return cmd.UsageErrorf("match: incorrect number of arguments, expected %d, got %d", expected, got) |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 66 | } |
Jiri Simsa | 1023a34 | 2014-08-20 18:01:22 -0700 | [diff] [blame] | 67 | name, profiles := args[0], args[1] |
Todd Wang | 702385a | 2014-11-07 01:54:08 -0800 | [diff] [blame] | 68 | app := repository.ApplicationClient(name) |
Matt Rosencrantz | c2ed03e | 2014-11-25 15:40:48 -0800 | [diff] [blame] | 69 | ctx, cancel := runtime.NewContext().WithTimeout(time.Minute) |
Matt Rosencrantz | 137b8d2 | 2014-08-18 09:56:15 -0700 | [diff] [blame] | 70 | defer cancel() |
Jiri Simsa | 1023a34 | 2014-08-20 18:01:22 -0700 | [diff] [blame] | 71 | j, err := getEnvelopeJSON(ctx, app, profiles) |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 72 | if err != nil { |
| 73 | return err |
| 74 | } |
| 75 | fmt.Fprintln(cmd.Stdout(), string(j)) |
| 76 | return nil |
| 77 | } |
| 78 | |
| 79 | var cmdPut = &cmdline.Command{ |
| 80 | Run: runPut, |
| 81 | Name: "put", |
| 82 | Short: "Add the given envelope to the application for the given profiles.", |
| 83 | Long: "Add the given envelope to the application for the given profiles.", |
| 84 | ArgsName: "<application> <profiles> <envelope>", |
| 85 | ArgsLong: ` |
| 86 | <application> is the full name of the application. |
| 87 | <profiles> is a comma-separated list of profiles. |
| 88 | <envelope> is the file that contains a JSON-encoded envelope.`, |
| 89 | } |
| 90 | |
| 91 | func runPut(cmd *cmdline.Command, args []string) error { |
| 92 | if expected, got := 3, len(args); expected != got { |
Todd Wang | a615e4d | 2014-09-29 16:56:05 -0700 | [diff] [blame] | 93 | return cmd.UsageErrorf("put: incorrect number of arguments, expected %d, got %d", expected, got) |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 94 | } |
Jiri Simsa | 1023a34 | 2014-08-20 18:01:22 -0700 | [diff] [blame] | 95 | name, profiles, envelope := args[0], args[1], args[2] |
Todd Wang | 702385a | 2014-11-07 01:54:08 -0800 | [diff] [blame] | 96 | app := repository.ApplicationClient(name) |
Jiri Simsa | 1023a34 | 2014-08-20 18:01:22 -0700 | [diff] [blame] | 97 | j, err := ioutil.ReadFile(envelope) |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 98 | if err != nil { |
Jiri Simsa | 1023a34 | 2014-08-20 18:01:22 -0700 | [diff] [blame] | 99 | return fmt.Errorf("ReadFile(%v): %v", envelope, err) |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 100 | } |
Matt Rosencrantz | c2ed03e | 2014-11-25 15:40:48 -0800 | [diff] [blame] | 101 | ctx, cancel := runtime.NewContext().WithTimeout(time.Minute) |
Matt Rosencrantz | 137b8d2 | 2014-08-18 09:56:15 -0700 | [diff] [blame] | 102 | defer cancel() |
Jiri Simsa | 1023a34 | 2014-08-20 18:01:22 -0700 | [diff] [blame] | 103 | if err = putEnvelopeJSON(ctx, app, profiles, j); err != nil { |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 104 | return err |
| 105 | } |
Jiri Simsa | 1a15c16 | 2014-08-19 11:36:24 -0700 | [diff] [blame] | 106 | fmt.Fprintln(cmd.Stdout(), "Application envelope added successfully.") |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 107 | return nil |
| 108 | } |
| 109 | |
| 110 | var cmdRemove = &cmdline.Command{ |
| 111 | Run: runRemove, |
| 112 | Name: "remove", |
| 113 | Short: "removes the application envelope for the given profile.", |
| 114 | Long: "removes the application envelope for the given profile.", |
| 115 | ArgsName: "<application> <profile>", |
| 116 | ArgsLong: ` |
| 117 | <application> is the full name of the application. |
| 118 | <profile> is a profile.`, |
| 119 | } |
| 120 | |
| 121 | func runRemove(cmd *cmdline.Command, args []string) error { |
| 122 | if expected, got := 2, len(args); expected != got { |
Todd Wang | a615e4d | 2014-09-29 16:56:05 -0700 | [diff] [blame] | 123 | return cmd.UsageErrorf("remove: incorrect number of arguments, expected %d, got %d", expected, got) |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 124 | } |
Jiri Simsa | 1023a34 | 2014-08-20 18:01:22 -0700 | [diff] [blame] | 125 | name, profile := args[0], args[1] |
Todd Wang | 702385a | 2014-11-07 01:54:08 -0800 | [diff] [blame] | 126 | app := repository.ApplicationClient(name) |
Matt Rosencrantz | c2ed03e | 2014-11-25 15:40:48 -0800 | [diff] [blame] | 127 | ctx, cancel := runtime.NewContext().WithTimeout(time.Minute) |
Matt Rosencrantz | 137b8d2 | 2014-08-18 09:56:15 -0700 | [diff] [blame] | 128 | defer cancel() |
Todd Wang | 702385a | 2014-11-07 01:54:08 -0800 | [diff] [blame] | 129 | if err := app.Remove(ctx, profile); err != nil { |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 130 | return err |
| 131 | } |
| 132 | fmt.Fprintln(cmd.Stdout(), "Application envelope removed successfully.") |
| 133 | return nil |
| 134 | } |
| 135 | |
| 136 | var cmdEdit = &cmdline.Command{ |
| 137 | Run: runEdit, |
| 138 | Name: "edit", |
| 139 | Short: "edits the application envelope for the given profile.", |
| 140 | Long: "edits the application envelope for the given profile.", |
| 141 | ArgsName: "<application> <profile>", |
| 142 | ArgsLong: ` |
| 143 | <application> is the full name of the application. |
| 144 | <profile> is a profile.`, |
| 145 | } |
| 146 | |
| 147 | func runEdit(cmd *cmdline.Command, args []string) error { |
| 148 | if expected, got := 2, len(args); expected != got { |
Todd Wang | a615e4d | 2014-09-29 16:56:05 -0700 | [diff] [blame] | 149 | return cmd.UsageErrorf("edit: incorrect number of arguments, expected %d, got %d", expected, got) |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 150 | } |
Jiri Simsa | 1023a34 | 2014-08-20 18:01:22 -0700 | [diff] [blame] | 151 | name, profile := args[0], args[1] |
Todd Wang | 702385a | 2014-11-07 01:54:08 -0800 | [diff] [blame] | 152 | app := repository.ApplicationClient(name) |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 153 | f, err := ioutil.TempFile("", "application-edit-") |
| 154 | if err != nil { |
Jiri Simsa | 1023a34 | 2014-08-20 18:01:22 -0700 | [diff] [blame] | 155 | return fmt.Errorf("TempFile() failed: %v", err) |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 156 | } |
| 157 | fileName := f.Name() |
| 158 | f.Close() |
| 159 | defer os.Remove(fileName) |
| 160 | |
Matt Rosencrantz | c2ed03e | 2014-11-25 15:40:48 -0800 | [diff] [blame] | 161 | ctx, cancel := runtime.NewContext().WithTimeout(time.Minute) |
Matt Rosencrantz | 137b8d2 | 2014-08-18 09:56:15 -0700 | [diff] [blame] | 162 | defer cancel() |
Jiri Simsa | 1023a34 | 2014-08-20 18:01:22 -0700 | [diff] [blame] | 163 | envData, err := getEnvelopeJSON(ctx, app, profile) |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 164 | if err != nil { |
| 165 | return err |
| 166 | } |
| 167 | if err = ioutil.WriteFile(fileName, envData, os.FileMode(0644)); err != nil { |
| 168 | return err |
| 169 | } |
| 170 | editor := os.Getenv("EDITOR") |
| 171 | if len(editor) == 0 { |
| 172 | editor = "nano" |
| 173 | } |
| 174 | for { |
| 175 | c := exec.Command("sh", "-c", fmt.Sprintf("%s %s", editor, fileName)) |
| 176 | c.Stdin = os.Stdin |
| 177 | c.Stdout = os.Stdout |
| 178 | c.Stderr = os.Stderr |
| 179 | if err := c.Run(); err != nil { |
| 180 | return fmt.Errorf("failed to run %s %s", editor, fileName) |
| 181 | } |
| 182 | newData, err := ioutil.ReadFile(fileName) |
| 183 | if err != nil { |
| 184 | fmt.Fprintf(cmd.Stdout(), "Error: %v\n", err) |
| 185 | if ans := promptUser(cmd, "Try again? [y/N] "); strings.ToUpper(ans) == "Y" { |
| 186 | continue |
| 187 | } |
| 188 | return errors.New("aborted") |
| 189 | } |
| 190 | if bytes.Compare(envData, newData) == 0 { |
| 191 | fmt.Fprintln(cmd.Stdout(), "Nothing changed") |
| 192 | return nil |
| 193 | } |
Jiri Simsa | 1023a34 | 2014-08-20 18:01:22 -0700 | [diff] [blame] | 194 | if err = putEnvelopeJSON(ctx, app, profile, newData); err != nil { |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 195 | fmt.Fprintf(cmd.Stdout(), "Error: %v\n", err) |
| 196 | if ans := promptUser(cmd, "Try again? [y/N] "); strings.ToUpper(ans) == "Y" { |
| 197 | continue |
| 198 | } |
| 199 | return errors.New("aborted") |
| 200 | } |
| 201 | break |
| 202 | } |
| 203 | fmt.Fprintln(cmd.Stdout(), "Application envelope updated successfully.") |
| 204 | return nil |
| 205 | } |
| 206 | |
Robin Thellend | 18205cf | 2014-10-21 13:53:59 -0700 | [diff] [blame] | 207 | func root() *cmdline.Command { |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 208 | return &cmdline.Command{ |
Todd Wang | fcb72a5 | 2014-10-01 09:53:56 -0700 | [diff] [blame] | 209 | Name: "application", |
| 210 | Short: "Tool for interacting with the veyron application repository", |
| 211 | Long: ` |
| 212 | The application tool facilitates interaction with the veyron application |
| 213 | repository. |
| 214 | `, |
Robin Thellend | 32c438b | 2014-05-22 17:28:00 -0700 | [diff] [blame] | 215 | Children: []*cmdline.Command{cmdMatch, cmdPut, cmdRemove, cmdEdit}, |
| 216 | } |
| 217 | } |