blob: 8af0a9f6bfffe6f01bd296df22b72e35cdf4d53f [file] [log] [blame]
Robin Thellend32c438b2014-05-22 17:28:00 -07001package impl
2
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 Simsa519c5072014-09-17 21:37:57 -070014 "veyron.io/veyron/veyron/lib/cmdline"
15 "veyron.io/veyron/veyron/services/mgmt/repository"
Robin Thellend32c438b2014-05-22 17:28:00 -070016
Jiri Simsa519c5072014-09-17 21:37:57 -070017 "veyron.io/veyron/veyron2/context"
18 "veyron.io/veyron/veyron2/rt"
19 "veyron.io/veyron/veyron2/services/mgmt/application"
Robin Thellend32c438b2014-05-22 17:28:00 -070020)
21
Matt Rosencrantz137b8d22014-08-18 09:56:15 -070022func getEnvelopeJSON(ctx context.T, app repository.Application, profiles string) ([]byte, error) {
23 env, err := app.Match(ctx, strings.Split(profiles, ","))
Robin Thellend32c438b2014-05-22 17:28:00 -070024 if err != nil {
Jiri Simsa1023a342014-08-20 18:01:22 -070025 return nil, err
Robin Thellend32c438b2014-05-22 17:28:00 -070026 }
27 j, err := json.MarshalIndent(env, "", " ")
28 if err != nil {
Jiri Simsa1023a342014-08-20 18:01:22 -070029 return nil, fmt.Errorf("MarshalIndent(%v) failed: %v", env, err)
Robin Thellend32c438b2014-05-22 17:28:00 -070030 }
31 return j, nil
32}
33
Matt Rosencrantz137b8d22014-08-18 09:56:15 -070034func putEnvelopeJSON(ctx context.T, app repository.Application, profiles string, j []byte) error {
Robin Thellend32c438b2014-05-22 17:28:00 -070035 var env application.Envelope
36 if err := json.Unmarshal(j, &env); err != nil {
Jiri Simsa1023a342014-08-20 18:01:22 -070037 return fmt.Errorf("Unmarshal(%v) failed: %v", string(j), err)
Robin Thellend32c438b2014-05-22 17:28:00 -070038 }
Matt Rosencrantz137b8d22014-08-18 09:56:15 -070039 if err := app.Put(ctx, strings.Split(profiles, ","), env); err != nil {
Robin Thellend32c438b2014-05-22 17:28:00 -070040 return err
41 }
42 return nil
43}
44
45func promptUser(cmd *cmdline.Command, msg string) string {
46 fmt.Fprint(cmd.Stdout(), msg)
47 var answer string
48 if _, err := fmt.Scanf("%s", &answer); err != nil {
49 return ""
50 }
51 return answer
52}
53
54var cmdMatch = &cmdline.Command{
55 Run: runMatch,
56 Name: "match",
57 Short: "Shows the first matching envelope that matches the given profiles.",
58 Long: "Shows the first matching envelope that matches the given profiles.",
59 ArgsName: "<application> <profiles>",
60 ArgsLong: `
61<application> is the full name of the application.
62<profiles> is a comma-separated list of profiles.`,
63}
64
65func runMatch(cmd *cmdline.Command, args []string) error {
66 if expected, got := 2, len(args); expected != got {
Todd Wanga615e4d2014-09-29 16:56:05 -070067 return cmd.UsageErrorf("match: incorrect number of arguments, expected %d, got %d", expected, got)
Robin Thellend32c438b2014-05-22 17:28:00 -070068 }
Jiri Simsa1023a342014-08-20 18:01:22 -070069 name, profiles := args[0], args[1]
70 app, err := repository.BindApplication(name)
Robin Thellend32c438b2014-05-22 17:28:00 -070071 if err != nil {
Jiri Simsa1023a342014-08-20 18:01:22 -070072 return fmt.Errorf("BindApplication(%v) failed: %v", name, err)
Robin Thellend32c438b2014-05-22 17:28:00 -070073 }
Matt Rosencrantz137b8d22014-08-18 09:56:15 -070074 ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
75 defer cancel()
Jiri Simsa1023a342014-08-20 18:01:22 -070076 j, err := getEnvelopeJSON(ctx, app, profiles)
Robin Thellend32c438b2014-05-22 17:28:00 -070077 if err != nil {
78 return err
79 }
80 fmt.Fprintln(cmd.Stdout(), string(j))
81 return nil
82}
83
84var cmdPut = &cmdline.Command{
85 Run: runPut,
86 Name: "put",
87 Short: "Add the given envelope to the application for the given profiles.",
88 Long: "Add the given envelope to the application for the given profiles.",
89 ArgsName: "<application> <profiles> <envelope>",
90 ArgsLong: `
91<application> is the full name of the application.
92<profiles> is a comma-separated list of profiles.
93<envelope> is the file that contains a JSON-encoded envelope.`,
94}
95
96func runPut(cmd *cmdline.Command, args []string) error {
97 if expected, got := 3, len(args); expected != got {
Todd Wanga615e4d2014-09-29 16:56:05 -070098 return cmd.UsageErrorf("put: incorrect number of arguments, expected %d, got %d", expected, got)
Robin Thellend32c438b2014-05-22 17:28:00 -070099 }
Jiri Simsa1023a342014-08-20 18:01:22 -0700100 name, profiles, envelope := args[0], args[1], args[2]
101 app, err := repository.BindApplication(name)
Robin Thellend32c438b2014-05-22 17:28:00 -0700102 if err != nil {
Jiri Simsa1023a342014-08-20 18:01:22 -0700103 return fmt.Errorf("BindApplication(%v) failed: %v", name, err)
Robin Thellend32c438b2014-05-22 17:28:00 -0700104 }
Jiri Simsa1023a342014-08-20 18:01:22 -0700105 j, err := ioutil.ReadFile(envelope)
Robin Thellend32c438b2014-05-22 17:28:00 -0700106 if err != nil {
Jiri Simsa1023a342014-08-20 18:01:22 -0700107 return fmt.Errorf("ReadFile(%v): %v", envelope, err)
Robin Thellend32c438b2014-05-22 17:28:00 -0700108 }
Matt Rosencrantz137b8d22014-08-18 09:56:15 -0700109 ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
110 defer cancel()
Jiri Simsa1023a342014-08-20 18:01:22 -0700111 if err = putEnvelopeJSON(ctx, app, profiles, j); err != nil {
Robin Thellend32c438b2014-05-22 17:28:00 -0700112 return err
113 }
Jiri Simsa1a15c162014-08-19 11:36:24 -0700114 fmt.Fprintln(cmd.Stdout(), "Application envelope added successfully.")
Robin Thellend32c438b2014-05-22 17:28:00 -0700115 return nil
116}
117
118var cmdRemove = &cmdline.Command{
119 Run: runRemove,
120 Name: "remove",
121 Short: "removes the application envelope for the given profile.",
122 Long: "removes the application envelope for the given profile.",
123 ArgsName: "<application> <profile>",
124 ArgsLong: `
125<application> is the full name of the application.
126<profile> is a profile.`,
127}
128
129func runRemove(cmd *cmdline.Command, args []string) error {
130 if expected, got := 2, len(args); expected != got {
Todd Wanga615e4d2014-09-29 16:56:05 -0700131 return cmd.UsageErrorf("remove: incorrect number of arguments, expected %d, got %d", expected, got)
Robin Thellend32c438b2014-05-22 17:28:00 -0700132 }
Jiri Simsa1023a342014-08-20 18:01:22 -0700133 name, profile := args[0], args[1]
134 app, err := repository.BindApplication(name)
Robin Thellend32c438b2014-05-22 17:28:00 -0700135 if err != nil {
Jiri Simsa1023a342014-08-20 18:01:22 -0700136 return fmt.Errorf("BindApplication(%v) failed: %v", name, err)
Robin Thellend32c438b2014-05-22 17:28:00 -0700137 }
Matt Rosencrantz137b8d22014-08-18 09:56:15 -0700138 ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
139 defer cancel()
Jiri Simsa1023a342014-08-20 18:01:22 -0700140 if err = app.Remove(ctx, profile); err != nil {
Robin Thellend32c438b2014-05-22 17:28:00 -0700141 return err
142 }
143 fmt.Fprintln(cmd.Stdout(), "Application envelope removed successfully.")
144 return nil
145}
146
147var cmdEdit = &cmdline.Command{
148 Run: runEdit,
149 Name: "edit",
150 Short: "edits the application envelope for the given profile.",
151 Long: "edits the application envelope for the given profile.",
152 ArgsName: "<application> <profile>",
153 ArgsLong: `
154<application> is the full name of the application.
155<profile> is a profile.`,
156}
157
158func runEdit(cmd *cmdline.Command, args []string) error {
159 if expected, got := 2, len(args); expected != got {
Todd Wanga615e4d2014-09-29 16:56:05 -0700160 return cmd.UsageErrorf("edit: incorrect number of arguments, expected %d, got %d", expected, got)
Robin Thellend32c438b2014-05-22 17:28:00 -0700161 }
Jiri Simsa1023a342014-08-20 18:01:22 -0700162 name, profile := args[0], args[1]
163 app, err := repository.BindApplication(name)
Robin Thellend32c438b2014-05-22 17:28:00 -0700164 if err != nil {
Jiri Simsa1023a342014-08-20 18:01:22 -0700165 return fmt.Errorf("BindApplication(%v) failed: %v", name, err)
Robin Thellend32c438b2014-05-22 17:28:00 -0700166 }
167 f, err := ioutil.TempFile("", "application-edit-")
168 if err != nil {
Jiri Simsa1023a342014-08-20 18:01:22 -0700169 return fmt.Errorf("TempFile() failed: %v", err)
Robin Thellend32c438b2014-05-22 17:28:00 -0700170 }
171 fileName := f.Name()
172 f.Close()
173 defer os.Remove(fileName)
174
Matt Rosencrantz137b8d22014-08-18 09:56:15 -0700175 ctx, cancel := rt.R().NewContext().WithTimeout(time.Minute)
176 defer cancel()
Jiri Simsa1023a342014-08-20 18:01:22 -0700177 envData, err := getEnvelopeJSON(ctx, app, profile)
Robin Thellend32c438b2014-05-22 17:28:00 -0700178 if err != nil {
179 return err
180 }
181 if err = ioutil.WriteFile(fileName, envData, os.FileMode(0644)); err != nil {
182 return err
183 }
184 editor := os.Getenv("EDITOR")
185 if len(editor) == 0 {
186 editor = "nano"
187 }
188 for {
189 c := exec.Command("sh", "-c", fmt.Sprintf("%s %s", editor, fileName))
190 c.Stdin = os.Stdin
191 c.Stdout = os.Stdout
192 c.Stderr = os.Stderr
193 if err := c.Run(); err != nil {
194 return fmt.Errorf("failed to run %s %s", editor, fileName)
195 }
196 newData, err := ioutil.ReadFile(fileName)
197 if err != nil {
198 fmt.Fprintf(cmd.Stdout(), "Error: %v\n", err)
199 if ans := promptUser(cmd, "Try again? [y/N] "); strings.ToUpper(ans) == "Y" {
200 continue
201 }
202 return errors.New("aborted")
203 }
204 if bytes.Compare(envData, newData) == 0 {
205 fmt.Fprintln(cmd.Stdout(), "Nothing changed")
206 return nil
207 }
Jiri Simsa1023a342014-08-20 18:01:22 -0700208 if err = putEnvelopeJSON(ctx, app, profile, newData); err != nil {
Robin Thellend32c438b2014-05-22 17:28:00 -0700209 fmt.Fprintf(cmd.Stdout(), "Error: %v\n", err)
210 if ans := promptUser(cmd, "Try again? [y/N] "); strings.ToUpper(ans) == "Y" {
211 continue
212 }
213 return errors.New("aborted")
214 }
215 break
216 }
217 fmt.Fprintln(cmd.Stdout(), "Application envelope updated successfully.")
218 return nil
219}
220
221func Root() *cmdline.Command {
222 return &cmdline.Command{
223 Name: "application",
Jiri Simsaddbfebb2014-06-20 15:56:06 -0700224 Short: "Command-line tool for interacting with the veyron application repository",
225 Long: "Command-line tool for interacting with the veyron application repository",
Robin Thellend32c438b2014-05-22 17:28:00 -0700226 Children: []*cmdline.Command{cmdMatch, cmdPut, cmdRemove, cmdEdit},
227 }
228}