11package manager
22
33import (
4+ "bufio"
45 "fmt"
56 "os"
7+ "strconv"
8+ "strings"
69
710 "github.com/docker/infrakit/cmd/cli/base"
811 "github.com/docker/infrakit/pkg/cli"
@@ -11,10 +14,13 @@ import (
1114 "github.com/docker/infrakit/pkg/manager"
1215 "github.com/docker/infrakit/pkg/plugin"
1316 "github.com/docker/infrakit/pkg/rpc/client"
14- group_plugin "github.com/docker/infrakit/pkg/rpc/group"
17+ group_rpc "github.com/docker/infrakit/pkg/rpc/group"
1518 manager_rpc "github.com/docker/infrakit/pkg/rpc/manager"
19+ metadata_rpc "github.com/docker/infrakit/pkg/rpc/metadata"
1620 "github.com/docker/infrakit/pkg/spi/group"
21+ "github.com/docker/infrakit/pkg/spi/metadata"
1722 "github.com/docker/infrakit/pkg/types"
23+ "github.com/sergi/go-diff/diffmatchpatch"
1824 "github.com/spf13/cobra"
1925)
2026
@@ -30,6 +36,9 @@ func Command(plugins func() discovery.Plugins) *cobra.Command {
3036 var groupPlugin group.Plugin
3137 var groupPluginName string
3238
39+ var updatablePlugin metadata.Updatable
40+ var updatablePluginName string
41+
3342 cmd := & cobra.Command {
3443 Use : "manager" ,
3544 Short : "Access the manager" ,
@@ -59,11 +68,15 @@ func Command(plugins func() discovery.Plugins) *cobra.Command {
5968 log .Debug ("Found manager" , "name" , name , "leader" , isLeader )
6069 if isLeader {
6170
62- groupPlugin = group_plugin .Adapt (rpcClient )
71+ groupPlugin = group_rpc .Adapt (rpcClient )
6372 groupPluginName = name
6473
6574 log .Debug ("Found manager" , "name" , name , "addr" , endpoint .Address )
6675
76+ updatablePlugin = metadata_rpc .AdaptUpdatable (rpcClient )
77+ updatablePluginName = name
78+
79+ log .Debug ("Found updatable" , "name" , name , "addr" , endpoint .Address )
6780 break
6881 }
6982 }
@@ -132,7 +145,7 @@ func Command(plugins func() discovery.Plugins) *cobra.Command {
132145
133146 // TODO(chungers) -- we need to enforce and confirm the type of this.
134147 // Right now we assume the RPC endpoint is indeed a group.
135- target , err := group_plugin .NewClient (endpoint .Address )
148+ target , err := group_rpc .NewClient (endpoint .Address )
136149
137150 log .Debug ("commit" , "plugin" , gp .Plugin , "address" , endpoint .Address , "err" , err , "spec" , spec )
138151
@@ -165,26 +178,11 @@ func Command(plugins func() discovery.Plugins) *cobra.Command {
165178 os .Exit (1 )
166179 }
167180
168- specs , err := groupPlugin . InspectGroups ( )
181+ out , err := getGlobalConfig ( groupPlugin , groupPluginName )
169182 if err != nil {
170183 return err
171184 }
172185
173- // the format is plugin.Spec
174- out := []plugin.Spec {}
175- for _ , spec := range specs {
176-
177- any , err := types .AnyValue (spec )
178- if err != nil {
179- return err
180- }
181-
182- out = append (out , plugin.Spec {
183- Plugin : plugin .Name (groupPluginName ),
184- Properties : any ,
185- })
186- }
187-
188186 view , err := types .AnyValue (out )
189187 if err != nil {
190188 return err
@@ -202,7 +200,181 @@ func Command(plugins func() discovery.Plugins) *cobra.Command {
202200 }
203201 inspect .Flags ().AddFlagSet (templateFlags )
204202
205- cmd .AddCommand (commit , inspect )
203+ ///////////////////////////////////////////////////////////////////////////////////
204+ // change
205+ change := & cobra.Command {
206+ Use : "change" ,
207+ Short : "Change returns the plugin configurations known by the manager" ,
208+ }
209+ vars := change .Flags ().StringSliceP ("var" , "v" , []string {}, "key=value pairs" )
210+ commitChange := change .Flags ().BoolP ("commit" , "c" , false , "Commit changes" )
211+
212+ // This is the only interactive command. We want to show the user the proposal, with the diff
213+ // and when the user accepts the change, call a commit.
214+ change .RunE = func (cmd * cobra.Command , args []string ) error {
215+
216+ if len (args ) != 0 {
217+ cmd .Usage ()
218+ os .Exit (1 )
219+ }
220+
221+ log .Info ("applying changes" )
222+
223+ // get the changes
224+ changes , err := changeSet (* vars )
225+ if err != nil {
226+ return err
227+ }
228+ current , proposed , cas , err := updatablePlugin .Changes (changes )
229+ if err != nil {
230+ return err
231+ }
232+ currentBuff , err := current .MarshalYAML ()
233+ if err != nil {
234+ return err
235+ }
236+
237+ proposedBuff , err := proposed .MarshalYAML ()
238+ if err != nil {
239+ return err
240+ }
241+
242+ // render the proposal
243+ fmt .Printf ("Proposed changes, hash=%s\n " , cas )
244+ dmp := diffmatchpatch .New ()
245+ diffs := dmp .DiffMain (string (currentBuff ), string (proposedBuff ), false )
246+ fmt .Println (dmp .DiffPrettyText (diffs ))
247+
248+ if * commitChange {
249+ // ask for final approval
250+ input := bufio .NewReader (os .Stdin )
251+ fmt .Fprintf (os .Stderr , "\n \n Commit? [y/n] " )
252+ text , _ := input .ReadString ('\n' )
253+ text = strings .Trim (text , " \t \n " )
254+
255+ agree , err := parseBool (text )
256+ if err != nil {
257+ return fmt .Errorf ("not boolean %v" , text )
258+ }
259+ if ! agree {
260+ fmt .Fprintln (os .Stderr , "Not committing. Bye." )
261+ os .Exit (0 )
262+ }
263+
264+ return updatablePlugin .Commit (proposed , cas )
265+ }
266+
267+ return nil
268+ }
269+ change .Flags ().AddFlagSet (templateFlags )
270+
271+ ///////////////////////////////////////////////////////////////////////////////////
272+ // change-list
273+ changeList := & cobra.Command {
274+ Use : "ls" ,
275+ Short : "Lists all the changeable paths" ,
276+ RunE : func (cmd * cobra.Command , args []string ) error {
277+
278+ if len (args ) != 0 {
279+ cmd .Usage ()
280+ os .Exit (1 )
281+ }
282+
283+ all , err := types .ListAll (updatablePlugin , types .PathFromString ("." ))
284+ if err != nil {
285+ return err
286+ }
287+
288+ types .Sort (all )
289+ for _ , p := range all {
290+ fmt .Println (p .String ())
291+ }
292+ return nil
293+ },
294+ }
295+ ///////////////////////////////////////////////////////////////////////////////////
296+ // change-cat
297+ changeGet := & cobra.Command {
298+ Use : "cat" ,
299+ Short : "Cat returns the current value at given path" ,
300+ RunE : func (cmd * cobra.Command , args []string ) error {
301+
302+ if len (args ) != 1 {
303+ cmd .Usage ()
304+ os .Exit (1 )
305+ }
306+
307+ path := types .PathFromString (args [0 ])
308+ any , err := updatablePlugin .Get (path )
309+ if err != nil {
310+ return err
311+ }
312+
313+ fmt .Println (any .String ())
314+
315+ return nil
316+ },
317+ }
318+ change .AddCommand (changeList , changeGet )
319+
320+ cmd .AddCommand (commit , inspect , change )
206321
207322 return cmd
208323}
324+
325+ func parseBool (text string ) (bool , error ) {
326+ agree , err := strconv .ParseBool (text )
327+ if err == nil {
328+ return agree , nil
329+ }
330+ switch strings .ToLower (text ) {
331+ case "yes" , "ok" , "y" :
332+ return true , nil
333+ case "no" , "nope" , "n" :
334+ return false , nil
335+ }
336+ return false , err
337+ }
338+
339+ func getGlobalConfig (groupPlugin group.Plugin , groupPluginName string ) ([]plugin.Spec , error ) {
340+ specs , err := groupPlugin .InspectGroups ()
341+ if err != nil {
342+ return nil , err
343+ }
344+
345+ // the format is plugin.Spec
346+ out := []plugin.Spec {}
347+ for _ , spec := range specs {
348+
349+ any , err := types .AnyValue (spec )
350+ if err != nil {
351+ return nil , err
352+ }
353+
354+ out = append (out , plugin.Spec {
355+ Plugin : plugin .Name (groupPluginName ),
356+ Properties : any ,
357+ })
358+ }
359+ return out , nil
360+ }
361+
362+ // changeSet returns a set of changes from the input pairs of path / value
363+ func changeSet (kvPairs []string ) ([]metadata.Change , error ) {
364+ changes := []metadata.Change {}
365+
366+ for _ , kv := range kvPairs {
367+
368+ parts := strings .SplitN (kv , "=" , 2 )
369+ key := strings .Trim (parts [0 ], " \t \n " )
370+ value := strings .Trim (parts [1 ], " \t \n " )
371+
372+ change := metadata.Change {
373+ Path : types .PathFromString (key ),
374+ Value : types .AnyYAMLMust ([]byte (value )),
375+ }
376+
377+ changes = append (changes , change )
378+ }
379+ return changes , nil
380+ }
0 commit comments