From d4c2aaf38ad066e742dad530535faade39dadd1a Mon Sep 17 00:00:00 2001 From: baude Date: Thu, 16 Jan 2020 11:23:16 -0600 Subject: Add service endpoint add service endpoint for the new API. Also supports the varlink implementation. Signed-off-by: baude Refactor to allow developer more control of API server * Add api.NewServerWithSettings() to create an API server with custom settings * Add api.ListenUnix() to create a UDS net.Listener and setup UDS Signed-off-by: Jhon Honce Signed-off-by: baude More service completion Add podman service command that allows users to run either a RESTful or varlink protocol API service. Addition of docs and RESTful listening. Signed-off-by: baude Signed-off-by: Brent Baude --- cmd/podman/cliconfig/config.go | 6 ++ cmd/podman/commands.go | 3 + cmd/podman/service.go | 154 +++++++++++++++++++++++++++++++++++++++++ cmd/podman/service_dummy.go | 11 +++ cmd/podman/varlink.go | 2 +- 5 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 cmd/podman/service.go create mode 100644 cmd/podman/service_dummy.go (limited to 'cmd/podman') diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index b261599e6..6bc8aa4a3 100644 --- a/cmd/podman/cliconfig/config.go +++ b/cmd/podman/cliconfig/config.go @@ -599,6 +599,12 @@ type VarlinkValues struct { Timeout int64 } +type ServiceValues struct { + PodmanCommand + Varlink bool + Timeout int64 +} + type SetTrustValues struct { PodmanCommand PolicyPath string diff --git a/cmd/podman/commands.go b/cmd/podman/commands.go index 31f1b3ba4..ebd7aeb0c 100644 --- a/cmd/podman/commands.go +++ b/cmd/podman/commands.go @@ -26,6 +26,9 @@ func getMainCommands() []*cobra.Command { if len(_varlinkCommand.Use) > 0 { rootCommands = append(rootCommands, _varlinkCommand) } + if len(_serviceCommand.Use) > 0 { + rootCommands = append(rootCommands, _serviceCommand) + } return rootCommands } diff --git a/cmd/podman/service.go b/cmd/podman/service.go new file mode 100644 index 000000000..6e2b4a366 --- /dev/null +++ b/cmd/podman/service.go @@ -0,0 +1,154 @@ +// +build varlink,!remoteclient + +package main + +import ( + "fmt" + "net" + "os" + "path/filepath" + "strings" + "time" + + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/cmd/podman/libpodruntime" + iopodman "github.com/containers/libpod/cmd/podman/varlink" + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/adapter" + api "github.com/containers/libpod/pkg/api/server" + "github.com/containers/libpod/pkg/rootless" + "github.com/containers/libpod/pkg/util" + "github.com/containers/libpod/pkg/varlinkapi" + "github.com/containers/libpod/version" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/varlink/go/varlink" +) + +var ( + serviceCommand cliconfig.ServiceValues + serviceDescription = `Run an API service + +Enable a listening service for API access to Podman commands. +` + + _serviceCommand = &cobra.Command{ + Use: "service [flags] [URI]", + Short: "Run API service", + Long: serviceDescription, + RunE: func(cmd *cobra.Command, args []string) error { + serviceCommand.InputArgs = args + serviceCommand.GlobalFlags = MainGlobalOpts + return serviceCmd(&serviceCommand) + }, + } +) + +func init() { + serviceCommand.Command = _serviceCommand + serviceCommand.SetHelpTemplate(HelpTemplate()) + serviceCommand.SetUsageTemplate(UsageTemplate()) + flags := serviceCommand.Flags() + flags.Int64VarP(&serviceCommand.Timeout, "timeout", "t", 1000, "Time until the service session expires in milliseconds. Use 0 to disable the timeout") + flags.BoolVar(&serviceCommand.Varlink, "varlink", false, "Use legacy varlink service instead of REST") +} + +func serviceCmd(c *cliconfig.ServiceValues) error { + // For V2, default to the REST socket + apiURI := adapter.DefaultAPIAddress + if c.Varlink { + apiURI = adapter.DefaultVarlinkAddress + } + + if rootless.IsRootless() { + xdg, err := util.GetRuntimeDir() + if err != nil { + return err + } + socketName := "podman.sock" + if c.Varlink { + socketName = "io.podman" + } + socketDir := filepath.Join(xdg, "podman", socketName) + if _, err := os.Stat(filepath.Dir(socketDir)); err != nil { + if os.IsNotExist(err) { + if err := os.Mkdir(filepath.Dir(socketDir), 0755); err != nil { + return err + } + } else { + return err + } + } + apiURI = fmt.Sprintf("unix:%s", socketDir) + } + + if len(c.InputArgs) > 0 { + apiURI = c.InputArgs[0] + } + + logrus.Infof("using API endpoint: %s", apiURI) + + // Create a single runtime api consumption + runtime, err := libpodruntime.GetRuntimeDisableFDs(getContext(), &c.PodmanCommand) + if err != nil { + return errors.Wrapf(err, "error creating libpod runtime") + } + defer runtime.DeferredShutdown(false) + + timeout := time.Duration(c.Timeout) * time.Millisecond + if c.Varlink { + return runVarlink(runtime, apiURI, timeout, c) + } + return runREST(runtime, apiURI, timeout) +} + +func runREST(r *libpod.Runtime, uri string, timeout time.Duration) error { + logrus.Warn("This function is EXPERIMENTAL") + fmt.Println("This function is EXPERIMENTAL.") + fields := strings.Split(uri, ":") + if len(fields) == 1 { + return errors.Errorf("%s is an invalid socket destination", uri) + } + address := strings.Join(fields[1:], ":") + l, err := net.Listen(fields[0], address) + if err != nil { + return errors.Wrapf(err, "unable to create socket %s", uri) + } + server, err := api.NewServerWithSettings(r, timeout, &l) + if err != nil { + return err + } + return server.Serve() +} + +func runVarlink(r *libpod.Runtime, uri string, timeout time.Duration, c *cliconfig.ServiceValues) error { + var varlinkInterfaces = []*iopodman.VarlinkInterface{varlinkapi.New(&c.PodmanCommand, r)} + service, err := varlink.NewService( + "Atomic", + "podman", + version.Version, + "https://github.com/containers/libpod", + ) + if err != nil { + return errors.Wrapf(err, "unable to create new varlink service") + } + + for _, i := range varlinkInterfaces { + if err := service.RegisterInterface(i); err != nil { + return errors.Errorf("unable to register varlink interface %v", i) + } + } + + // Run the varlink server at the given address + if err = service.Listen(uri, timeout); err != nil { + switch err.(type) { + case varlink.ServiceTimeoutError: + logrus.Infof("varlink service expired (use --timeout to increase session time beyond %d ms, 0 means never timeout)", timeout.String()) + return nil + default: + return errors.Wrapf(err, "unable to start varlink service") + } + } + return nil +} diff --git a/cmd/podman/service_dummy.go b/cmd/podman/service_dummy.go new file mode 100644 index 000000000..a774c34de --- /dev/null +++ b/cmd/podman/service_dummy.go @@ -0,0 +1,11 @@ +// +build !varlink + +package main + +import "github.com/spf13/cobra" + +var ( + _serviceCommand = &cobra.Command{ + Use: "", + } +) diff --git a/cmd/podman/varlink.go b/cmd/podman/varlink.go index cd21e3574..047d94fc2 100644 --- a/cmd/podman/varlink.go +++ b/cmd/podman/varlink.go @@ -51,7 +51,7 @@ func init() { } func varlinkCmd(c *cliconfig.VarlinkValues) error { - varlinkURI := adapter.DefaultAddress + varlinkURI := adapter.DefaultVarlinkAddress if rootless.IsRootless() { xdg, err := util.GetRuntimeDir() if err != nil { -- cgit v1.2.3-54-g00ecf