blob: 033775b8b495f81b61440f7a400ba068ead115ab [file] [log] [blame]
Robin Thellend18205cf2014-10-21 13:53:59 -07001package main
Robin Thellend32c438b2014-05-22 17:28:00 -07002
3import (
4 "bytes"
5 "encoding/json"
6 "errors"
7 "fmt"
8 "io/ioutil"
9 "os"
10 "os/exec"
11 "strings"
Matt Rosencrantz137b8d22014-08-18 09:56:15 -070012 "time"
Robin Thellend32c438b2014-05-22 17:28:00 -070013
Jiri Simsabc26d692014-11-19 18:30:55 -080014 "veyron.io/lib/cmdline"
Jiri Simsa519c5072014-09-17 21:37:57 -070015 "veyron.io/veyron/veyron/services/mgmt/repository"
Jiri Simsa519c5072014-09-17 21:37:57 -070016 "veyron.io/veyron/veyron2/context"
Jiri Simsa519c5072014-09-17 21:37:57 -070017 "veyron.io/veyron/veyron2/services/mgmt/application"
Robin Thellend32c438b2014-05-22 17:28:00 -070018)
19
Todd Wang702385a2014-11-07 01:54:08 -080020func getEnvelopeJSON(ctx context.T, app repository.ApplicationClientMethods, profiles string) ([]byte, error) {
Matt Rosencrantz137b8d22014-08-18 09:56:15 -070021 env, err := app.Match(ctx, strings.Split(profiles, ","))
Robin Thellend32c438b2014-05-22 17:28:00 -070022 if err != nil {
Jiri Simsa1023a342014-08-20 18:01:22 -070023 return nil, err
Robin Thellend32c438b2014-05-22 17:28:00 -070024 }
25 j, err := json.MarshalIndent(env, "", " ")
26 if err != nil {
Jiri Simsa1023a342014-08-20 18:01:22 -070027 return nil, fmt.Errorf("MarshalIndent(%v) failed: %v", env, err)
Robin Thellend32c438b2014-05-22 17:28:00 -070028 }
29 return j, nil
30}
31
Todd Wang702385a2014-11-07 01:54:08 -080032func putEnvelopeJSON(ctx context.T, app repository.ApplicationClientMethods, profiles string, j []byte) error {
Robin Thellend32c438b2014-05-22 17:28:00 -070033 var env application.Envelope
34 if err := json.Unmarshal(j, &env); err != nil {
Jiri Simsa1023a342014-08-20 18:01:22 -070035 return fmt.Errorf("Unmarshal(%v) failed: %v", string(j), err)
Robin Thellend32c438b2014-05-22 17:28:00 -070036 }
Matt Rosencrantz137b8d22014-08-18 09:56:15 -070037 if err := app.Put(ctx, strings.Split(profiles, ","), env); err != nil {
Robin Thellend32c438b2014-05-22 17:28:00 -070038 return err
39 }
40 return nil
41}
42
43func 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
52var 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
63func runMatch(cmd *cmdline.Command, args []string) error {
64 if expected, got := 2, len(args); expected != got {
Todd Wanga615e4d2014-09-29 16:56:05 -070065 return cmd.UsageErrorf("match: incorrect number of arguments, expected %d, got %d", expected, got)
Robin Thellend32c438b2014-05-22 17:28:00 -070066 }
Jiri Simsa1023a342014-08-20 18:01:22 -070067 name, profiles := args[0], args[1]
Todd Wang702385a2014-11-07 01:54:08 -080068 app := repository.ApplicationClient(name)
Matt Rosencrantzc2ed03e2014-11-25 15:40:48 -080069 ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
Matt Rosencrantz137b8d22014-08-18 09:56:15 -070070 defer cancel()
Jiri Simsa1023a342014-08-20 18:01:22 -070071 j, err := getEnvelopeJSON(ctx, app, profiles)
Robin Thellend32c438b2014-05-22 17:28:00 -070072 if err != nil {
73 return err
74 }
75 fmt.Fprintln(cmd.Stdout(), string(j))
76 return nil
77}
78
79var 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
91func runPut(cmd *cmdline.Command, args []string) error {
92 if expected, got := 3, len(args); expected != got {
Todd Wanga615e4d2014-09-29 16:56:05 -070093 return cmd.UsageErrorf("put: incorrect number of arguments, expected %d, got %d", expected, got)
Robin Thellend32c438b2014-05-22 17:28:00 -070094 }
Jiri Simsa1023a342014-08-20 18:01:22 -070095 name, profiles, envelope := args[0], args[1], args[2]
Todd Wang702385a2014-11-07 01:54:08 -080096 app := repository.ApplicationClient(name)
Jiri Simsa1023a342014-08-20 18:01:22 -070097 j, err := ioutil.ReadFile(envelope)
Robin Thellend32c438b2014-05-22 17:28:00 -070098 if err != nil {
Jiri Simsa1023a342014-08-20 18:01:22 -070099 return fmt.Errorf("ReadFile(%v): %v", envelope, err)
Robin Thellend32c438b2014-05-22 17:28:00 -0700100 }
Matt Rosencrantzc2ed03e2014-11-25 15:40:48 -0800101 ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
Matt Rosencrantz137b8d22014-08-18 09:56:15 -0700102 defer cancel()
Jiri Simsa1023a342014-08-20 18:01:22 -0700103 if err = putEnvelopeJSON(ctx, app, profiles, j); err != nil {
Robin Thellend32c438b2014-05-22 17:28:00 -0700104 return err
105 }
Jiri Simsa1a15c162014-08-19 11:36:24 -0700106 fmt.Fprintln(cmd.Stdout(), "Application envelope added successfully.")
Robin Thellend32c438b2014-05-22 17:28:00 -0700107 return nil
108}
109
110var 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
121func runRemove(cmd *cmdline.Command, args []string) error {
122 if expected, got := 2, len(args); expected != got {
Todd Wanga615e4d2014-09-29 16:56:05 -0700123 return cmd.UsageErrorf("remove: incorrect number of arguments, expected %d, got %d", expected, got)
Robin Thellend32c438b2014-05-22 17:28:00 -0700124 }
Jiri Simsa1023a342014-08-20 18:01:22 -0700125 name, profile := args[0], args[1]
Todd Wang702385a2014-11-07 01:54:08 -0800126 app := repository.ApplicationClient(name)
Matt Rosencrantzc2ed03e2014-11-25 15:40:48 -0800127 ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
Matt Rosencrantz137b8d22014-08-18 09:56:15 -0700128 defer cancel()
Todd Wang702385a2014-11-07 01:54:08 -0800129 if err := app.Remove(ctx, profile); err != nil {
Robin Thellend32c438b2014-05-22 17:28:00 -0700130 return err
131 }
132 fmt.Fprintln(cmd.Stdout(), "Application envelope removed successfully.")
133 return nil
134}
135
136var 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
147func runEdit(cmd *cmdline.Command, args []string) error {
148 if expected, got := 2, len(args); expected != got {
Todd Wanga615e4d2014-09-29 16:56:05 -0700149 return cmd.UsageErrorf("edit: incorrect number of arguments, expected %d, got %d", expected, got)
Robin Thellend32c438b2014-05-22 17:28:00 -0700150 }
Jiri Simsa1023a342014-08-20 18:01:22 -0700151 name, profile := args[0], args[1]
Todd Wang702385a2014-11-07 01:54:08 -0800152 app := repository.ApplicationClient(name)
Robin Thellend32c438b2014-05-22 17:28:00 -0700153 f, err := ioutil.TempFile("", "application-edit-")
154 if err != nil {
Jiri Simsa1023a342014-08-20 18:01:22 -0700155 return fmt.Errorf("TempFile() failed: %v", err)
Robin Thellend32c438b2014-05-22 17:28:00 -0700156 }
157 fileName := f.Name()
158 f.Close()
159 defer os.Remove(fileName)
160
Matt Rosencrantzc2ed03e2014-11-25 15:40:48 -0800161 ctx, cancel := runtime.NewContext().WithTimeout(time.Minute)
Matt Rosencrantz137b8d22014-08-18 09:56:15 -0700162 defer cancel()
Jiri Simsa1023a342014-08-20 18:01:22 -0700163 envData, err := getEnvelopeJSON(ctx, app, profile)
Robin Thellend32c438b2014-05-22 17:28:00 -0700164 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 Simsa1023a342014-08-20 18:01:22 -0700194 if err = putEnvelopeJSON(ctx, app, profile, newData); err != nil {
Robin Thellend32c438b2014-05-22 17:28:00 -0700195 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 Thellend18205cf2014-10-21 13:53:59 -0700207func root() *cmdline.Command {
Robin Thellend32c438b2014-05-22 17:28:00 -0700208 return &cmdline.Command{
Todd Wangfcb72a52014-10-01 09:53:56 -0700209 Name: "application",
210 Short: "Tool for interacting with the veyron application repository",
211 Long: `
212The application tool facilitates interaction with the veyron application
213repository.
214`,
Robin Thellend32c438b2014-05-22 17:28:00 -0700215 Children: []*cmdline.Command{cmdMatch, cmdPut, cmdRemove, cmdEdit},
216 }
217}