veyron/services/mgmt/profile: Add command-line tool.
Add a command-line tool to interface with the profile manager. At this time,
there is only very limited support for setting the profile specification. Every
profile is the same.
Change-Id: Iedbbdf54068dddc138eafc981196bd2ce204e5e6
diff --git a/services/mgmt/profile/profile/impl/impl.go b/services/mgmt/profile/profile/impl/impl.go
new file mode 100644
index 0000000..ccb9038
--- /dev/null
+++ b/services/mgmt/profile/profile/impl/impl.go
@@ -0,0 +1,148 @@
+package impl
+
+import (
+ "fmt"
+
+ "veyron/lib/cmdline"
+ "veyron/services/mgmt/profile"
+)
+
+var cmdLabel = &cmdline.Command{
+ Run: runLabel,
+ Name: "label",
+ Short: "Shows a human-readable profile key for the profile.",
+ Long: "Shows a human-readable profile key for the profile.",
+ ArgsName: "<profile>",
+ ArgsLong: "<profile> is the full name of the profile.",
+}
+
+func runLabel(cmd *cmdline.Command, args []string) error {
+ if expected, got := 1, len(args); expected != got {
+ return cmd.Errorf("label: incorrect number of arguments, expected %d, got %d", expected, got)
+ }
+ p, err := profile.BindProfile(args[0])
+ if err != nil {
+ return fmt.Errorf("bind error: %v", err)
+ }
+ label, err := p.Label()
+ if err != nil {
+ return err
+ }
+ fmt.Fprintln(cmd.Stdout(), label)
+ return nil
+}
+
+var cmdDescription = &cmdline.Command{
+ Run: runDescription,
+ Name: "description",
+ Short: "Shows a human-readable profile description for the profile.",
+ Long: "Shows a human-readable profile description for the profile.",
+ ArgsName: "<profile>",
+ ArgsLong: "<profile> is the full name of the profile.",
+}
+
+func runDescription(cmd *cmdline.Command, args []string) error {
+ if expected, got := 1, len(args); expected != got {
+ return cmd.Errorf("description: incorrect number of arguments, expected %d, got %d", expected, got)
+ }
+ p, err := profile.BindProfile(args[0])
+ if err != nil {
+ return fmt.Errorf("bind error: %v", err)
+ }
+ desc, err := p.Description()
+ if err != nil {
+ return err
+ }
+ fmt.Fprintln(cmd.Stdout(), desc)
+ return nil
+}
+
+var cmdSpec = &cmdline.Command{
+ Run: runSpec,
+ Name: "spec",
+ Short: "Shows the specification of the profile.",
+ Long: "Shows the specification of the profile.",
+ ArgsName: "<profile>",
+ ArgsLong: "<profile> is the full name of the profile.",
+}
+
+func runSpec(cmd *cmdline.Command, args []string) error {
+ if expected, got := 1, len(args); expected != got {
+ return cmd.Errorf("spec: incorrect number of arguments, expected %d, got %d", expected, got)
+ }
+ p, err := profile.BindProfile(args[0])
+ if err != nil {
+ return fmt.Errorf("bind error: %v", err)
+ }
+ spec, err := p.Specification()
+ if err != nil {
+ return err
+ }
+ fmt.Fprintf(cmd.Stdout(), "%#v\n", spec)
+ return nil
+}
+
+var cmdPut = &cmdline.Command{
+ Run: runPut,
+ Name: "put",
+ Short: "Sets a placeholder specification for the profile.",
+ Long: "Sets a placeholder specification for the profile.",
+ ArgsName: "<profile>",
+ ArgsLong: "<profile> is the full name of the profile.",
+}
+
+func runPut(cmd *cmdline.Command, args []string) error {
+ if expected, got := 1, len(args); expected != got {
+ return cmd.Errorf("put: incorrect number of arguments, expected %d, got %d", expected, got)
+ }
+ p, err := profile.BindProfile(args[0])
+ if err != nil {
+ return fmt.Errorf("bind error: %v", err)
+ }
+
+ // TODO(rthellend): Read an actual specification from a file.
+ spec := profile.Specification{
+ Format: profile.Format{Name: "elf", Attributes: map[string]string{"os": "linux", "arch": "amd64"}},
+ Libraries: map[profile.Library]struct{}{profile.Library{Name: "foo", MajorVersion: "1", MinorVersion: "0"}: struct{}{}},
+ Label: "example",
+ Description: "Example profile to test the profile manager implementation.",
+ }
+ if err := p.Put(spec); err != nil {
+ return err
+ }
+ fmt.Fprintln(cmd.Stdout(), "Specification updated successfully.")
+ return nil
+}
+
+var cmdRemove = &cmdline.Command{
+ Run: runRemove,
+ Name: "remove",
+ Short: "removes the profile specification for the profile.",
+ Long: "removes the profile specification for the profile.",
+ ArgsName: "<profile>",
+ ArgsLong: "<profile> is the full name of the profile.",
+}
+
+func runRemove(cmd *cmdline.Command, args []string) error {
+ if expected, got := 1, len(args); expected != got {
+ return cmd.Errorf("remove: incorrect number of arguments, expected %d, got %d", expected, got)
+ }
+ p, err := profile.BindProfile(args[0])
+ if err != nil {
+ return fmt.Errorf("bind error: %v", err)
+ }
+ if err = p.Remove(); err != nil {
+ return err
+ }
+ fmt.Fprintln(cmd.Stdout(), "Profile removed successfully.")
+ return nil
+}
+
+func Root() *cmdline.Command {
+ return &cmdline.Command{
+ Name: "profile",
+ Short: "Command-line tool for interacting with the Veyron profile manager",
+ Long: "Command-line tool for interacting with the Veyron profile manager",
+ Children: []*cmdline.Command{cmdLabel, cmdDescription, cmdSpec, cmdPut, cmdRemove},
+ }
+}
diff --git a/services/mgmt/profile/profile/impl/impl_test.go b/services/mgmt/profile/profile/impl/impl_test.go
new file mode 100644
index 0000000..c8784b7
--- /dev/null
+++ b/services/mgmt/profile/profile/impl/impl_test.go
@@ -0,0 +1,165 @@
+package impl_test
+
+import (
+ "bytes"
+ "fmt"
+ "strings"
+ "testing"
+
+ "veyron/services/mgmt/profile"
+ "veyron/services/mgmt/profile/profile/impl"
+
+ "veyron2"
+ "veyron2/ipc"
+ "veyron2/naming"
+ "veyron2/rt"
+ "veyron2/security"
+ "veyron2/vlog"
+)
+
+var (
+ // spec is an example profile specification used throughout the test.
+ spec = profile.Specification{
+ Format: profile.Format{Name: "elf", Attributes: map[string]string{"os": "linux"}},
+ Libraries: map[profile.Library]struct{}{profile.Library{Name: "foo", MajorVersion: "1", MinorVersion: "0"}: struct{}{}},
+ Label: "example",
+ Description: "Example profile to test the profile manager implementation.",
+ }
+)
+
+type server struct {
+ suffix string
+}
+
+func (s *server) Label(ipc.Context) (string, error) {
+ vlog.VI(2).Infof("%v.Label() was called", s.suffix)
+ if s.suffix != "exists" {
+ return "", fmt.Errorf("profile doesn't exist: %v", s.suffix)
+ }
+ return spec.Label, nil
+}
+
+func (s *server) Description(ipc.Context) (string, error) {
+ vlog.VI(2).Infof("%v.Description() was called", s.suffix)
+ if s.suffix != "exists" {
+ return "", fmt.Errorf("profile doesn't exist: %v", s.suffix)
+ }
+ return spec.Description, nil
+}
+
+func (s *server) Specification(ipc.Context) (profile.Specification, error) {
+ vlog.VI(2).Infof("%v.Specification() was called", s.suffix)
+ if s.suffix != "exists" {
+ return profile.Specification{}, fmt.Errorf("profile doesn't exist: %v", s.suffix)
+ }
+ return spec, nil
+}
+
+func (s *server) Put(_ ipc.Context, _ profile.Specification) error {
+ vlog.VI(2).Infof("%v.Put() was called", s.suffix)
+ return nil
+}
+
+func (s *server) Remove(ipc.Context) error {
+ vlog.VI(2).Infof("%v.Remove() was called", s.suffix)
+ if s.suffix != "exists" {
+ return fmt.Errorf("profile doesn't exist: %v", s.suffix)
+ }
+ return nil
+}
+
+type dispatcher struct {
+}
+
+func NewDispatcher() *dispatcher {
+ return &dispatcher{}
+}
+
+func (d *dispatcher) Lookup(suffix string) (ipc.Invoker, security.Authorizer, error) {
+ invoker := ipc.ReflectInvoker(profile.NewServerProfile(&server{suffix: suffix}))
+ return invoker, nil, nil
+}
+
+func startServer(t *testing.T, r veyron2.Runtime) (ipc.Server, naming.Endpoint, error) {
+ dispatcher := NewDispatcher()
+ server, err := r.NewServer()
+ if err != nil {
+ t.Errorf("NewServer failed: %v", err)
+ return nil, nil, err
+ }
+ if err := server.Register("", dispatcher); err != nil {
+ t.Errorf("Register failed: %v", err)
+ return nil, nil, err
+ }
+ endpoint, err := server.Listen("tcp", "localhost:0")
+ if err != nil {
+ t.Errorf("Listen failed: %v", err)
+ return nil, nil, err
+ }
+ return server, endpoint, nil
+}
+
+func stopServer(t *testing.T, server ipc.Server) {
+ if err := server.Stop(); err != nil {
+ t.Errorf("server.Stop failed: %v", err)
+ }
+}
+
+func TestProfileClient(t *testing.T) {
+ runtime := rt.Init()
+ server, endpoint, err := startServer(t, runtime)
+ if err != nil {
+ return
+ }
+ defer stopServer(t, server)
+ // Setup the command-line.
+ cmd := impl.Root()
+ var stdout, stderr bytes.Buffer
+ cmd.Init(nil, &stdout, &stderr)
+ exists := naming.JoinAddressName(endpoint.String(), "exists")
+
+ // Test the 'label' command.
+ if err := cmd.Execute([]string{"label", exists}); err != nil {
+ t.Fatalf("%v", err)
+ }
+ if expected, got := spec.Label, strings.TrimSpace(stdout.String()); got != expected {
+ t.Errorf("Got %q, expected %q", got, expected)
+ }
+ stdout.Reset()
+
+ // Test the 'description' command.
+ if err := cmd.Execute([]string{"description", exists}); err != nil {
+ t.Fatalf("%v", err)
+ }
+ if expected, got := spec.Description, strings.TrimSpace(stdout.String()); got != expected {
+ t.Errorf("Got %q, expected %q", got, expected)
+ }
+ stdout.Reset()
+
+ // Test the 'spec' command.
+ if err := cmd.Execute([]string{"spec", exists}); err != nil {
+ t.Fatalf("%v", err)
+ }
+ if expected, got := fmt.Sprintf("%#v", spec), strings.TrimSpace(stdout.String()); got != expected {
+ t.Errorf("Got %q, expected %q", got, expected)
+ }
+ stdout.Reset()
+
+ // Test the 'put' command.
+ if err := cmd.Execute([]string{"put", exists}); err != nil {
+ t.Fatalf("%v", err)
+ }
+ if expected, got := "Specification updated successfully.", strings.TrimSpace(stdout.String()); got != expected {
+ t.Errorf("Got %q, expected %q", got, expected)
+ }
+ stdout.Reset()
+
+ // Test the 'remove' command.
+ if err := cmd.Execute([]string{"remove", exists}); err != nil {
+ t.Fatalf("%v", err)
+ }
+ if expected, got := "Profile removed successfully.", strings.TrimSpace(stdout.String()); got != expected {
+ t.Errorf("Got %q, expected %q", got, expected)
+ }
+ stdout.Reset()
+}
diff --git a/services/mgmt/profile/profile/main.go b/services/mgmt/profile/profile/main.go
new file mode 100644
index 0000000..4b7f23f
--- /dev/null
+++ b/services/mgmt/profile/profile/main.go
@@ -0,0 +1,14 @@
+package main
+
+import (
+ "veyron/services/mgmt/profile/profile/impl"
+
+ "veyron2/rt"
+)
+
+func main() {
+ r := rt.Init()
+ defer r.Shutdown()
+
+ impl.Root().Main()
+}