diff options
38 files changed, 624 insertions, 404 deletions
@@ -95,6 +95,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in [func ImageSave(options: ImageSaveOptions) MoreResponse](#ImageSave) +[func ImageTree(name: string, whatRequires: bool) string](#ImageTree) + [func ImagesPrune(all: bool, filter: []string) []string](#ImagesPrune) [func ImportImage(source: string, reference: string, message: string, changes: []string, delete: bool) string](#ImportImage) @@ -775,6 +777,18 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.ImageExists '{"name": "im method ImageSave(options: [ImageSaveOptions](#ImageSaveOptions)) [MoreResponse](#MoreResponse)</div> ImageSave allows you to save an image from the local image storage to a tarball +### <a name="ImageTree"></a>func ImageTree +<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> + +method ImageTree(name: [string](https://godoc.org/builtin#string), whatRequires: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div> +ImageTree returns the image tree for the provided image name or ID +#### Example +~~~ +$ varlink call -m unix:/run/podman/io.podman/io.podman.ImageTree '{"name": "alpine"}' +{ + "tree": "Image ID: e7d92cdc71fe\nTags: [docker.io/library/alpine:latest]\nSize: 5.861MB\nImage Layers\n└── ID: 5216338b40a7 Size: 5.857MB Top Layer of: [docker.io/library/alpine:latest]\n" +} +~~~ ### <a name="ImagesPrune"></a>func ImagesPrune <div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;"> diff --git a/cmd/podman/commit.go b/cmd/podman/commit.go index b4d249c66..7c35a4832 100644 --- a/cmd/podman/commit.go +++ b/cmd/podman/commit.go @@ -15,7 +15,7 @@ var ( commitDescription = `Create an image from a container's changes. Optionally tag the image created, set the author with the --author flag, set the commit message with the --message flag, and make changes to the instructions with the --change flag.` _commitCommand = &cobra.Command{ - Use: "commit [flags] CONTAINER IMAGE", + Use: "commit [flags] CONTAINER [IMAGE]", Short: "Create new image based on the changed container", Long: commitDescription, RunE: func(cmd *cobra.Command, args []string) error { @@ -26,7 +26,8 @@ var ( }, Example: `podman commit -q --message "committing container to image" reverent_golick image-committed podman commit -q --author "firstName lastName" reverent_golick image-committed - podman commit -q --pause=false containerID image-committed`, + podman commit -q --pause=false containerID image-committed + podman commit containerID`, } // ChangeCmds is the list of valid Changes commands to passed to the Commit call diff --git a/cmd/podman/service.go b/cmd/podman/service.go index 4978b5d51..3e0ff927f 100644 --- a/cmd/podman/service.go +++ b/cmd/podman/service.go @@ -17,6 +17,7 @@ import ( "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/systemd" "github.com/containers/libpod/pkg/util" "github.com/containers/libpod/pkg/varlinkapi" "github.com/containers/libpod/version" @@ -50,21 +51,52 @@ func init() { 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.Int64VarP(&serviceCommand.Timeout, "timeout", "t", 5, "Time until the service session expires in seconds. 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 + apiURI, err := resolveApiURI(c) + if err != nil { + return err + } + + // 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 func() { + if err := runtime.Shutdown(false); err != nil { + fmt.Fprintf(os.Stderr, "Failed to shutdown libpod runtime: %v", err) + } + }() + + timeout := time.Duration(c.Timeout) * time.Second if c.Varlink { - apiURI = adapter.DefaultVarlinkAddress + return runVarlink(runtime, apiURI, timeout, c) } + return runREST(runtime, apiURI, timeout) +} + +func resolveApiURI(c *cliconfig.ServiceValues) (string, error) { + var apiURI string - if rootless.IsRootless() { + // When determining _*THE*_ listening endpoint -- + // 1) User input wins always + // 2) systemd socket activation + // 3) rootless honors XDG_RUNTIME_DIR + // 4) if varlink -- adapter.DefaultVarlinkAddress + // 5) lastly adapter.DefaultAPIAddress + + if len(c.InputArgs) > 0 { + apiURI = c.InputArgs[0] + } else if ok := systemd.SocketActivated(); ok { + apiURI = "" + } else if rootless.IsRootless() { xdg, err := util.GetRuntimeDir() if err != nil { - return err + return "", err } socketName := "podman.sock" if c.Varlink { @@ -74,53 +106,59 @@ func serviceCmd(c *cliconfig.ServiceValues) error { 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 + return "", err } } else { - return err + return "", err } } - apiURI = fmt.Sprintf("unix:%s", socketDir) - } - - if len(c.InputArgs) > 0 { - apiURI = c.InputArgs[0] + apiURI = "unix:" + socketDir + } else if c.Varlink { + apiURI = adapter.DefaultVarlinkAddress + } else { + // For V2, default to the REST socket + apiURI = adapter.DefaultAPIAddress } - 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") + if "" == apiURI { + logrus.Info("using systemd socket activation to determine API endpoint") + } else { + logrus.Infof("using API endpoint: %s", apiURI) } - 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) + return apiURI, nil } 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) + + var listener *net.Listener + if uri != "" { + 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) + } + defer l.Close() + listener = &l } - defer l.Close() - server, err := api.NewServerWithSettings(r, timeout, &l) + server, err := api.NewServerWithSettings(r, timeout, listener) if err != nil { return err } - return server.Serve() + defer func() { + if err := server.Shutdown(); err != nil { + fmt.Fprintf(os.Stderr, "Error when stopping service: %s", err) + } + }() + + err = server.Serve() + logrus.Debugf("%d/%d Active connections/Total connections\n", server.ActiveConnections, server.TotalConnections) + return err } func runVarlink(r *libpod.Runtime, uri string, timeout time.Duration, c *cliconfig.ServiceValues) error { diff --git a/cmd/podman/tree.go b/cmd/podman/tree.go index 69b42639d..28c770f0c 100644 --- a/cmd/podman/tree.go +++ b/cmd/podman/tree.go @@ -1,23 +1,14 @@ package main import ( - "context" "fmt" "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/adapter" - "github.com/docker/go-units" "github.com/pkg/errors" "github.com/spf13/cobra" ) -const ( - middleItem = "├── " - continueItem = "│ " - lastItem = "└── " -) - var ( treeCommand cliconfig.TreeValues @@ -56,95 +47,11 @@ func treeCmd(c *cliconfig.TreeValues) error { return errors.Wrapf(err, "error creating libpod runtime") } defer runtime.DeferredShutdown(false) - imageInfo, layerInfoMap, img, err := runtime.Tree(c.InputArgs[0]) - if err != nil { - return err - } - return printTree(imageInfo, layerInfoMap, img, c.WhatRequires) -} -func printTree(imageInfo *image.InfoImage, layerInfoMap map[string]*image.LayerInfo, img *adapter.ContainerImage, whatRequires bool) error { - size, err := img.Size(context.Background()) + tree, err := runtime.ImageTree(c.InputArgs[0], c.WhatRequires) if err != nil { return err } - - fmt.Printf("Image ID: %s\n", imageInfo.ID[:12]) - fmt.Printf("Tags:\t %s\n", imageInfo.Tags) - fmt.Printf("Size:\t %v\n", units.HumanSizeWithPrecision(float64(*size), 4)) - if img.TopLayer() != "" { - fmt.Printf("Image Layers\n") - } else { - fmt.Printf("No Image Layers\n") - } - - if !whatRequires { - // fill imageInfo with layers associated with image. - // the layers will be filled such that - // (Start)RootLayer->...intermediate Parent Layer(s)-> TopLayer(End) - // Build output from imageInfo into buffer - printImageHierarchy(imageInfo) - - } else { - // fill imageInfo with layers associated with image. - // the layers will be filled such that - // (Start)TopLayer->...intermediate Child Layer(s)-> Child TopLayer(End) - // (Forks)... intermediate Child Layer(s) -> Child Top Layer(End) - return printImageChildren(layerInfoMap, img.TopLayer(), "", true) - } - return nil -} - -// Stores all children layers which are created using given Image. -// Layers are stored as follows -// (Start)TopLayer->...intermediate Child Layer(s)-> Child TopLayer(End) -// (Forks)... intermediate Child Layer(s) -> Child Top Layer(End) -func printImageChildren(layerMap map[string]*image.LayerInfo, layerID string, prefix string, last bool) error { - if layerID == "" { - return nil - } - ll, ok := layerMap[layerID] - if !ok { - return fmt.Errorf("lookup error: layerid %s, not found", layerID) - } - fmt.Print(prefix) - - //initialize intend with middleItem to reduce middleItem checks. - intend := middleItem - if !last { - // add continueItem i.e. '|' for next iteration prefix - prefix += continueItem - } else if len(ll.ChildID) > 1 || len(ll.ChildID) == 0 { - // The above condition ensure, alignment happens for node, which has more then 1 children. - // If node is last in printing hierarchy, it should not be printed as middleItem i.e. ├── - intend = lastItem - prefix += " " - } - - var tags string - if len(ll.RepoTags) > 0 { - tags = fmt.Sprintf(" Top Layer of: %s", ll.RepoTags) - } - fmt.Printf("%sID: %s Size: %7v%s\n", intend, ll.ID[:12], units.HumanSizeWithPrecision(float64(ll.Size), 4), tags) - for count, childID := range ll.ChildID { - if err := printImageChildren(layerMap, childID, prefix, count == len(ll.ChildID)-1); err != nil { - return err - } - } + fmt.Print(tree) return nil } - -// prints the layers info of image -func printImageHierarchy(imageInfo *image.InfoImage) { - for count, l := range imageInfo.Layers { - var tags string - intend := middleItem - if len(l.RepoTags) > 0 { - tags = fmt.Sprintf(" Top Layer of: %s", l.RepoTags) - } - if count == len(imageInfo.Layers)-1 { - intend = lastItem - } - fmt.Printf("%s ID: %s Size: %7v%s\n", intend, l.ID[:12], units.HumanSizeWithPrecision(float64(l.Size), 4), tags) - } -} diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index a0227c48c..e9792fa8f 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -1188,6 +1188,16 @@ method GetPodsByStatus(statuses: []string) -> (pods: []string) # ~~~ method ImageExists(name: string) -> (exists: int) +# ImageTree returns the image tree for the provided image name or ID +# #### Example +# ~~~ +# $ varlink call -m unix:/run/podman/io.podman/io.podman.ImageTree '{"name": "alpine"}' +# { +# "tree": "Image ID: e7d92cdc71fe\nTags: [docker.io/library/alpine:latest]\nSize: 5.861MB\nImage Layers\n└── ID: 5216338b40a7 Size: 5.857MB Top Layer of: [docker.io/library/alpine:latest]\n" +# } +# ~~~ +method ImageTree(name: string, whatRequires: bool) -> (tree: string) + # ContainerExists takes a full or partial container ID or name and returns an int as to # whether the container exists in local storage. A result of 0 means the container does # exists; whereas a result of 1 means it could not be found. diff --git a/docs/source/markdown/podman-commit.1.md b/docs/source/markdown/podman-commit.1.md index 042ec7934..66d8811aa 100644 --- a/docs/source/markdown/podman-commit.1.md +++ b/docs/source/markdown/podman-commit.1.md @@ -4,7 +4,7 @@ podman\-commit - Create new image based on the changed container ## SYNOPSIS -**podman commit** [*options*] *container* *image* +**podman commit** [*options*] *container* [*image*] **podman container commit** [*options*] *container* [*image*] @@ -18,6 +18,7 @@ image. If this is not desired, the `--pause` flag can be set to false. When the is complete, Podman will print out the ID of the new image. If *image* does not begin with a registry name component, `localhost` will be added to the name. +If *image* is not provided, the values for the `REPOSITORY` and `TAG` values of the created image will each be set to `<none>`. ## OPTIONS @@ -86,6 +87,11 @@ $ podman commit -q --pause=false containerID image-committed e3ce4d93051ceea088d1c242624d659be32cf1667ef62f1d16d6b60193e2c7a8 ``` +``` +$ podman commit containerID +e3ce4d93051ceea088d1c242624d659be32cf1667ef62f1d16d6b60193e2c7a8 +``` + ## SEE ALSO podman(1), podman-run(1), podman-create(1) diff --git a/libpod/image/tree.go b/libpod/image/tree.go new file mode 100644 index 000000000..c7c69462f --- /dev/null +++ b/libpod/image/tree.go @@ -0,0 +1,138 @@ +package image + +import ( + "context" + "fmt" + "strings" + + "github.com/docker/go-units" + "github.com/pkg/errors" +) + +const ( + middleItem = "├── " + continueItem = "│ " + lastItem = "└── " +) + +type tree struct { + img *Image + imageInfo *InfoImage + layerInfo map[string]*LayerInfo + sb *strings.Builder +} + +// GenerateTree creates an image tree string representation for displaying it +// to the user. +func (i *Image) GenerateTree(whatRequires bool) (string, error) { + // Fetch map of image-layers, which is used for printing output. + layerInfo, err := GetLayersMapWithImageInfo(i.imageruntime) + if err != nil { + return "", errors.Wrapf(err, "error while retrieving layers of image %q", i.InputName) + } + + // Create an imageInfo and fill the image and layer info + imageInfo := &InfoImage{ + ID: i.ID(), + Tags: i.Names(), + } + + if err := BuildImageHierarchyMap(imageInfo, layerInfo, i.TopLayer()); err != nil { + return "", err + } + sb := &strings.Builder{} + tree := &tree{i, imageInfo, layerInfo, sb} + if err := tree.print(whatRequires); err != nil { + return "", err + } + return tree.string(), nil +} + +func (t *tree) string() string { + return t.sb.String() +} + +func (t *tree) print(whatRequires bool) error { + size, err := t.img.Size(context.Background()) + if err != nil { + return err + } + + fmt.Fprintf(t.sb, "Image ID: %s\n", t.imageInfo.ID[:12]) + fmt.Fprintf(t.sb, "Tags: %s\n", t.imageInfo.Tags) + fmt.Fprintf(t.sb, "Size: %v\n", units.HumanSizeWithPrecision(float64(*size), 4)) + if t.img.TopLayer() != "" { + fmt.Fprintf(t.sb, "Image Layers\n") + } else { + fmt.Fprintf(t.sb, "No Image Layers\n") + } + + if !whatRequires { + // fill imageInfo with layers associated with image. + // the layers will be filled such that + // (Start)RootLayer->...intermediate Parent Layer(s)-> TopLayer(End) + // Build output from imageInfo into buffer + t.printImageHierarchy(t.imageInfo) + } else { + // fill imageInfo with layers associated with image. + // the layers will be filled such that + // (Start)TopLayer->...intermediate Child Layer(s)-> Child TopLayer(End) + // (Forks)... intermediate Child Layer(s) -> Child Top Layer(End) + return t.printImageChildren(t.layerInfo, t.img.TopLayer(), "", true) + } + return nil +} + +// Stores all children layers which are created using given Image. +// Layers are stored as follows +// (Start)TopLayer->...intermediate Child Layer(s)-> Child TopLayer(End) +// (Forks)... intermediate Child Layer(s) -> Child Top Layer(End) +func (t *tree) printImageChildren(layerMap map[string]*LayerInfo, layerID string, prefix string, last bool) error { + if layerID == "" { + return nil + } + ll, ok := layerMap[layerID] + if !ok { + return fmt.Errorf("lookup error: layerid %s, not found", layerID) + } + fmt.Fprint(t.sb, prefix) + + //initialize intend with middleItem to reduce middleItem checks. + intend := middleItem + if !last { + // add continueItem i.e. '|' for next iteration prefix + prefix += continueItem + } else if len(ll.ChildID) > 1 || len(ll.ChildID) == 0 { + // The above condition ensure, alignment happens for node, which has more then 1 children. + // If node is last in printing hierarchy, it should not be printed as middleItem i.e. ├── + intend = lastItem + prefix += " " + } + + var tags string + if len(ll.RepoTags) > 0 { + tags = fmt.Sprintf(" Top Layer of: %s", ll.RepoTags) + } + fmt.Fprintf(t.sb, "%sID: %s Size: %7v%s\n", intend, ll.ID[:12], units.HumanSizeWithPrecision(float64(ll.Size), 4), tags) + for count, childID := range ll.ChildID { + if err := t.printImageChildren(layerMap, childID, prefix, count == len(ll.ChildID)-1); err != nil { + return err + } + } + return nil +} + +// prints the layers info of image +func (t *tree) printImageHierarchy(imageInfo *InfoImage) { + for count, l := range imageInfo.Layers { + var tags string + intend := middleItem + if len(l.RepoTags) > 0 { + tags = fmt.Sprintf(" Top Layer of: %s", l.RepoTags) + } + if count == len(imageInfo.Layers)-1 { + intend = lastItem + } + fmt.Fprintf(t.sb, "%s ID: %s Size: %7v%s\n", intend, l.ID[:12], units.HumanSizeWithPrecision(float64(l.Size), 4), tags) + } +} diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index cada93829..170b2e24e 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -26,7 +26,7 @@ import ( "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/libpod/logs" "github.com/containers/libpod/pkg/adapter/shortcuts" - "github.com/containers/libpod/pkg/systemdgen" + "github.com/containers/libpod/pkg/systemd/generate" "github.com/containers/storage" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -1154,7 +1154,7 @@ func generateServiceName(c *cliconfig.GenerateSystemdValues, ctr *libpod.Contain // generateSystemdgenContainerInfo is a helper to generate a // systemdgen.ContainerInfo for `GenerateSystemd`. -func (r *LocalRuntime) generateSystemdgenContainerInfo(c *cliconfig.GenerateSystemdValues, nameOrID string, pod *libpod.Pod) (*systemdgen.ContainerInfo, bool, error) { +func (r *LocalRuntime) generateSystemdgenContainerInfo(c *cliconfig.GenerateSystemdValues, nameOrID string, pod *libpod.Pod) (*generate.ContainerInfo, bool, error) { ctr, err := r.Runtime.LookupContainer(nameOrID) if err != nil { return nil, false, err @@ -1172,7 +1172,7 @@ func (r *LocalRuntime) generateSystemdgenContainerInfo(c *cliconfig.GenerateSyst } name, serviceName := generateServiceName(c, ctr, pod) - info := &systemdgen.ContainerInfo{ + info := &generate.ContainerInfo{ ServiceName: serviceName, ContainerName: name, RestartPolicy: c.RestartPolicy, @@ -1187,7 +1187,7 @@ func (r *LocalRuntime) generateSystemdgenContainerInfo(c *cliconfig.GenerateSyst // GenerateSystemd creates a unit file for a container or pod. func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (string, error) { - opts := systemdgen.Options{ + opts := generate.Options{ Files: c.Files, New: c.New, } @@ -1196,7 +1196,7 @@ func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (stri if info, found, err := r.generateSystemdgenContainerInfo(c, c.InputArgs[0], nil); found && err != nil { return "", err } else if found && err == nil { - return systemdgen.CreateContainerSystemdUnit(info, opts) + return generate.CreateContainerSystemdUnit(info, opts) } // --new does not support pods. @@ -1242,7 +1242,7 @@ func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (stri // Traverse the dependency graph and create systemdgen.ContainerInfo's for // each container. - containerInfos := []*systemdgen.ContainerInfo{podInfo} + containerInfos := []*generate.ContainerInfo{podInfo} for ctr, dependencies := range graph.DependencyMap() { // Skip the infra container as we already generated it. if ctr.ID() == infraID { @@ -1272,7 +1272,7 @@ func (r *LocalRuntime) GenerateSystemd(c *cliconfig.GenerateSystemdValues) (stri if i > 0 { builder.WriteByte('\n') } - out, err := systemdgen.CreateContainerSystemdUnit(info, opts) + out, err := generate.CreateContainerSystemdUnit(info, opts) if err != nil { return "", err } diff --git a/pkg/adapter/images.go b/pkg/adapter/images.go deleted file mode 100644 index 762f1a656..000000000 --- a/pkg/adapter/images.go +++ /dev/null @@ -1,33 +0,0 @@ -// +build !remoteclient - -package adapter - -import ( - "github.com/containers/libpod/libpod/image" - "github.com/pkg/errors" -) - -// Tree ... -func (r *LocalRuntime) Tree(imageOrID string) (*image.InfoImage, map[string]*image.LayerInfo, *ContainerImage, error) { - img, err := r.NewImageFromLocal(imageOrID) - if err != nil { - return nil, nil, nil, err - } - - // Fetch map of image-layers, which is used for printing output. - layerInfoMap, err := image.GetLayersMapWithImageInfo(r.Runtime.ImageRuntime()) - if err != nil { - return nil, nil, nil, errors.Wrapf(err, "error while retrieving layers of image %q", img.InputName) - } - - // Create an imageInfo and fill the image and layer info - imageInfo := &image.InfoImage{ - ID: img.ID(), - Tags: img.Names(), - } - - if err := image.BuildImageHierarchyMap(imageInfo, layerInfoMap, img.TopLayer()); err != nil { - return nil, nil, nil, err - } - return imageInfo, layerInfoMap, img, nil -} diff --git a/pkg/adapter/images_remote.go b/pkg/adapter/images_remote.go index 1d4997d9a..e7b38dccc 100644 --- a/pkg/adapter/images_remote.go +++ b/pkg/adapter/images_remote.go @@ -7,9 +7,7 @@ import ( "encoding/json" iopodman "github.com/containers/libpod/cmd/podman/varlink" - "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/inspect" - "github.com/pkg/errors" ) // Inspect returns returns an ImageData struct from over a varlink connection @@ -24,32 +22,3 @@ func (i *ContainerImage) Inspect(ctx context.Context) (*inspect.ImageData, error } return &data, nil } - -// Tree ... -func (r *LocalRuntime) Tree(imageOrID string) (*image.InfoImage, map[string]*image.LayerInfo, *ContainerImage, error) { - layerInfoMap := make(map[string]*image.LayerInfo) - imageInfo := &image.InfoImage{} - - img, err := r.NewImageFromLocal(imageOrID) - if err != nil { - return nil, nil, nil, err - } - - reply, err := iopodman.GetLayersMapWithImageInfo().Call(r.Conn) - if err != nil { - return nil, nil, nil, errors.Wrap(err, "failed to obtain image layers") - } - if err := json.Unmarshal([]byte(reply), &layerInfoMap); err != nil { - return nil, nil, nil, errors.Wrap(err, "failed to unmarshal image layers") - } - - reply, err = iopodman.BuildImageHierarchyMap().Call(r.Conn, imageOrID) - if err != nil { - return nil, nil, nil, errors.Wrap(err, "failed to get build image map") - } - if err := json.Unmarshal([]byte(reply), imageInfo); err != nil { - return nil, nil, nil, errors.Wrap(err, "failed to unmarshal build image map") - } - - return imageInfo, layerInfoMap, img, nil -} diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go index 40089797d..dfe6b7f07 100644 --- a/pkg/adapter/runtime.go +++ b/pkg/adapter/runtime.go @@ -133,6 +133,15 @@ func (r *LocalRuntime) NewImageFromLocal(name string) (*ContainerImage, error) { return &ContainerImage{img}, nil } +// ImageTree reutnrs an new image.Tree for the provided `imageOrID` and `whatrequires` flag +func (r *LocalRuntime) ImageTree(imageOrID string, whatRequires bool) (string, error) { + img, err := r.Runtime.ImageRuntime().NewFromLocal(imageOrID) + if err != nil { + return "", err + } + return img.GenerateTree(whatRequires) +} + // LoadFromArchiveReference calls into local storage to load an image from an archive func (r *LocalRuntime) LoadFromArchiveReference(ctx context.Context, srcRef types.ImageReference, signaturePolicyPath string, writer io.Writer) ([]*ContainerImage, error) { var containerImages []*ContainerImage diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go index c908358ff..220d4cf75 100644 --- a/pkg/adapter/runtime_remote.go +++ b/pkg/adapter/runtime_remote.go @@ -344,6 +344,10 @@ func (r *LocalRuntime) New(ctx context.Context, name, signaturePolicyPath, authf return newImage, nil } +func (r *LocalRuntime) ImageTree(imageOrID string, whatRequires bool) (string, error) { + return iopodman.ImageTree().Call(r.Conn, imageOrID, whatRequires) +} + // IsParent goes through the layers in the store and checks if i.TopLayer is // the parent of any other layer in store. Double check that image with that // layer exists as well. diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index bcbe4977e..eac0e4dad 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -46,17 +46,34 @@ func ImageExists(w http.ResponseWriter, r *http.Request) { } func ImageTree(w http.ResponseWriter, r *http.Request) { - // tree is a bit of a mess ... logic is in adapter and therefore not callable from here. needs rework - - // name := utils.GetName(r) - // _, layerInfoMap, _, err := s.Runtime.Tree(name) - // if err != nil { - // Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "Failed to find image information for %q", name)) - // return - // } - // it is not clear to me how to deal with this given all the processing of the image - // is in main. we need to discuss how that really should be and return something useful. - handlers.UnsupportedHandler(w, r) + runtime := r.Context().Value("runtime").(*libpod.Runtime) + name := utils.GetName(r) + + img, err := runtime.ImageRuntime().NewFromLocal(name) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusNotFound, errors.Wrapf(err, "Failed to find image %s", name)) + return + } + + decoder := r.Context().Value("decoder").(*schema.Decoder) + query := struct { + WhatRequires bool `schema:"whatrequires"` + }{ + WhatRequires: false, + } + if err := decoder.Decode(&query, r.URL.Query()); err != nil { + utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, + errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + return + } + + tree, err := img.GenerateTree(query.WhatRequires) + if err != nil { + utils.Error(w, "Server error", http.StatusInternalServerError, errors.Wrapf(err, "failed to generate image tree for %s", name)) + return + } + + utils.WriteResponse(w, http.StatusOK, tree) } func GetImage(w http.ResponseWriter, r *http.Request) { @@ -72,8 +89,8 @@ func GetImage(w http.ResponseWriter, r *http.Request) { return } utils.WriteResponse(w, http.StatusOK, inspect) - } + func GetImages(w http.ResponseWriter, r *http.Request) { images, err := utils.GetImages(w, r) if err != nil { diff --git a/pkg/api/handlers/swagger.go b/pkg/api/handlers/swagger.go index 10525bfc7..4ba123ba9 100644 --- a/pkg/api/handlers/swagger.go +++ b/pkg/api/handlers/swagger.go @@ -136,3 +136,12 @@ type swagInspectVolumeResponse struct { libpod.InspectVolumeData } } + +// Image tree response +// swagger:response LibpodImageTreeResponse +type swagImageTreeResponse struct { + // in:body + Body struct { + ImageTreeResponse + } +} diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go index f5d9f9ad5..a50f183f7 100644 --- a/pkg/api/handlers/types.go +++ b/pkg/api/handlers/types.go @@ -351,18 +351,21 @@ func ImageDataToImageInspect(ctx context.Context, l *libpodImage.Image) (*ImageI func LibpodToContainer(l *libpod.Container, infoData []define.InfoData) (*Container, error) { imageId, imageName := l.Image() - sizeRW, err := l.RWSize() - if err != nil { + + var ( + err error + sizeRootFs int64 + sizeRW int64 + state define.ContainerStatus + ) + + if state, err = l.State(); err != nil { return nil, err } - - SizeRootFs, err := l.RootFsSize() - if err != nil { + if sizeRW, err = l.RWSize(); err != nil { return nil, err } - - state, err := l.State() - if err != nil { + if sizeRootFs, err = l.RootFsSize(); err != nil { return nil, err } @@ -375,7 +378,7 @@ func LibpodToContainer(l *libpod.Container, infoData []define.InfoData) (*Contai Created: l.CreatedTime().Unix(), Ports: nil, SizeRw: sizeRW, - SizeRootFs: SizeRootFs, + SizeRootFs: sizeRootFs, Labels: l.Labels(), State: string(state), Status: "", diff --git a/pkg/api/server/handler_api.go b/pkg/api/server/handler_api.go index 4b93998ee..30a1680c9 100644 --- a/pkg/api/server/handler_api.go +++ b/pkg/api/server/handler_api.go @@ -2,32 +2,52 @@ package server import ( "context" + "fmt" "net/http" + "runtime" + "github.com/containers/libpod/pkg/api/handlers/utils" log "github.com/sirupsen/logrus" ) // APIHandler is a wrapper to enhance HandlerFunc's and remove redundant code -func APIHandler(ctx context.Context, h http.HandlerFunc) http.HandlerFunc { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - log.Debugf("APIHandler -- Method: %s URL: %s", r.Method, r.URL.String()) - if err := r.ParseForm(); err != nil { - log.Infof("Failed Request: unable to parse form: %q", err) - } +func (s *APIServer) APIHandler(h http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // http.Server hides panics, we want to see them and fix the cause. + defer func() { + err := recover() + if err != nil { + buf := make([]byte, 1<<20) + n := runtime.Stack(buf, true) + log.Warnf("Recovering from podman handler panic: %v, %s", err, buf[:n]) + // Try to inform client things went south... won't work if handler already started writing response body + utils.InternalServerError(w, fmt.Errorf("%v", err)) + } + }() + + // Wrapper to hide some boiler plate + fn := func(w http.ResponseWriter, r *http.Request) { + // Connection counting, ugh. Needed to support the sliding window for idle checking. + s.ConnectionCh <- EnterHandler + defer func() { s.ConnectionCh <- ExitHandler }() + + log.Debugf("APIHandler -- Method: %s URL: %s (conn %d/%d)", + r.Method, r.URL.String(), s.ActiveConnections, s.TotalConnections) - // TODO: Use ConnContext when ported to go 1.13 - c := context.WithValue(r.Context(), "decoder", ctx.Value("decoder")) - c = context.WithValue(c, "runtime", ctx.Value("runtime")) - c = context.WithValue(c, "shutdownFunc", ctx.Value("shutdownFunc")) - r = r.WithContext(c) + if err := r.ParseForm(); err != nil { + log.Infof("Failed Request: unable to parse form: %q", err) + } - h(w, r) + // TODO: Use r.ConnContext when ported to go 1.13 + c := context.WithValue(r.Context(), "decoder", s.Decoder) + c = context.WithValue(c, "runtime", s.Runtime) + c = context.WithValue(c, "shutdownFunc", s.Shutdown) + r = r.WithContext(c) - shutdownFunc := r.Context().Value("shutdownFunc").(func() error) - if err := shutdownFunc(); err != nil { - log.Errorf("Failed to shutdown Server in APIHandler(): %s", err.Error()) + h(w, r) } - }) + fn(w, r) + } } // VersionedPath prepends the version parsing code diff --git a/pkg/api/server/register_auth.go b/pkg/api/server/register_auth.go index 9f312683d..8db131153 100644 --- a/pkg/api/server/register_auth.go +++ b/pkg/api/server/register_auth.go @@ -5,7 +5,7 @@ import ( "github.com/gorilla/mux" ) -func (s *APIServer) RegisterAuthHandlers(r *mux.Router) error { - r.Handle(VersionedPath("/auth"), APIHandler(s.Context, handlers.UnsupportedHandler)) +func (s *APIServer) registerAuthHandlers(r *mux.Router) error { + r.Handle(VersionedPath("/auth"), s.APIHandler(handlers.UnsupportedHandler)) return nil } diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index b1facc27c..39e5dd10b 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -9,7 +9,7 @@ import ( "github.com/gorilla/mux" ) -func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { +func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // swagger:operation POST /containers/create compat containerCreate // --- // summary: Create a container @@ -33,7 +33,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/ConflictError" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/containers/create"), APIHandler(s.Context, handlers.CreateContainer)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/containers/create"), s.APIHandler(handlers.CreateContainer)).Methods(http.MethodPost) // swagger:operation GET /containers/json compat listContainers // --- // tags: @@ -83,7 +83,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/BadParamError" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/containers/json"), APIHandler(s.Context, generic.ListContainers)).Methods(http.MethodGet) + r.HandleFunc(VersionedPath("/containers/json"), s.APIHandler(generic.ListContainers)).Methods(http.MethodGet) // swagger:operation POST /containers/prune compat pruneContainers // --- // tags: @@ -105,7 +105,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/DocsContainerPruneReport" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/containers/prune"), APIHandler(s.Context, handlers.PruneContainers)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/containers/prune"), s.APIHandler(handlers.PruneContainers)).Methods(http.MethodPost) // swagger:operation DELETE /containers/{name} compat removeContainer // --- // tags: @@ -144,7 +144,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/ConflictError" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/containers/{name}"), APIHandler(s.Context, generic.RemoveContainer)).Methods(http.MethodDelete) + r.HandleFunc(VersionedPath("/containers/{name}"), s.APIHandler(generic.RemoveContainer)).Methods(http.MethodDelete) // swagger:operation GET /containers/{name}/json compat getContainer // --- // tags: @@ -171,7 +171,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/containers/{name}/json"), APIHandler(s.Context, generic.GetContainer)).Methods(http.MethodGet) + r.HandleFunc(VersionedPath("/containers/{name}/json"), s.APIHandler(generic.GetContainer)).Methods(http.MethodGet) // swagger:operation post /containers/{name}/kill compat killcontainer // --- // tags: @@ -201,7 +201,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/ConflictError" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/containers/{name}/kill"), APIHandler(s.Context, generic.KillContainer)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/containers/{name}/kill"), s.APIHandler(generic.KillContainer)).Methods(http.MethodPost) // swagger:operation GET /containers/{name}/logs compat LogsFromContainer // --- // tags: @@ -253,7 +253,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/containers/{name}/logs"), APIHandler(s.Context, generic.LogsFromContainer)).Methods(http.MethodGet) + r.HandleFunc(VersionedPath("/containers/{name}/logs"), s.APIHandler(generic.LogsFromContainer)).Methods(http.MethodGet) // swagger:operation POST /containers/{name}/pause compat pauseContainer // --- // tags: @@ -275,8 +275,8 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/containers/{name}/pause"), APIHandler(s.Context, handlers.PauseContainer)).Methods(http.MethodPost) - r.HandleFunc(VersionedPath("/containers/{name}/rename"), APIHandler(s.Context, handlers.UnsupportedHandler)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/containers/{name}/pause"), s.APIHandler(handlers.PauseContainer)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/containers/{name}/rename"), s.APIHandler(handlers.UnsupportedHandler)).Methods(http.MethodPost) // swagger:operation POST /containers/{name}/restart compat restartContainer // --- // tags: @@ -301,7 +301,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/containers/{name}/restart"), APIHandler(s.Context, handlers.RestartContainer)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/containers/{name}/restart"), s.APIHandler(handlers.RestartContainer)).Methods(http.MethodPost) // swagger:operation POST /containers/{name}/start compat startContainer // --- // tags: @@ -329,7 +329,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/containers/{name}/start"), APIHandler(s.Context, handlers.StartContainer)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/containers/{name}/start"), s.APIHandler(handlers.StartContainer)).Methods(http.MethodPost) // swagger:operation GET /containers/{name}/stats compat statsContainer // --- // tags: @@ -356,7 +356,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/containers/{name}/stats"), APIHandler(s.Context, generic.StatsContainer)).Methods(http.MethodGet) + r.HandleFunc(VersionedPath("/containers/{name}/stats"), s.APIHandler(generic.StatsContainer)).Methods(http.MethodGet) // swagger:operation POST /containers/{name}/stop compat stopContainer // --- // tags: @@ -384,7 +384,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/containers/{name}/stop"), APIHandler(s.Context, handlers.StopContainer)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/containers/{name}/stop"), s.APIHandler(handlers.StopContainer)).Methods(http.MethodPost) // swagger:operation GET /containers/{name}/top compat topContainer // --- // tags: @@ -409,7 +409,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/containers/{name}/top"), APIHandler(s.Context, handlers.TopContainer)).Methods(http.MethodGet) + r.HandleFunc(VersionedPath("/containers/{name}/top"), s.APIHandler(handlers.TopContainer)).Methods(http.MethodGet) // swagger:operation POST /containers/{name}/unpause compat unpauseContainer // --- // tags: @@ -431,7 +431,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/containers/{name}/unpause"), APIHandler(s.Context, handlers.UnpauseContainer)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/containers/{name}/unpause"), s.APIHandler(handlers.UnpauseContainer)).Methods(http.MethodPost) // swagger:operation POST /containers/{name}/wait compat waitContainer // --- // tags: @@ -457,7 +457,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/containers/{name}/wait"), APIHandler(s.Context, generic.WaitContainer)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/containers/{name}/wait"), s.APIHandler(generic.WaitContainer)).Methods(http.MethodPost) // swagger:operation POST /containers/{name}/attach compat attach // --- // tags: @@ -512,7 +512,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/containers/{name}/attach"), APIHandler(s.Context, handlers.AttachContainer)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/containers/{name}/attach"), s.APIHandler(handlers.AttachContainer)).Methods(http.MethodPost) // swagger:operation POST /containers/{name}/resize compat resize // --- // tags: @@ -544,13 +544,13 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/containers/{name}/resize"), APIHandler(s.Context, handlers.ResizeContainer)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/containers/{name}/resize"), s.APIHandler(handlers.ResizeContainer)).Methods(http.MethodPost) /* libpod endpoints */ - r.HandleFunc(VersionedPath("/libpod/containers/create"), APIHandler(s.Context, handlers.CreateContainer)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/libpod/containers/create"), s.APIHandler(handlers.CreateContainer)).Methods(http.MethodPost) // swagger:operation GET /libpod/containers/json libpod libpodListContainers // --- // tags: @@ -615,7 +615,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/BadParamError" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/containers/json"), APIHandler(s.Context, libpod.ListContainers)).Methods(http.MethodGet) + r.HandleFunc(VersionedPath("/libpod/containers/json"), s.APIHandler(libpod.ListContainers)).Methods(http.MethodGet) // swagger:operation POST /libpod/containers/prune libpod libpodPruneContainers // --- // tags: @@ -637,7 +637,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/DocsLibpodPruneResponse" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/containers/prune"), APIHandler(s.Context, handlers.PruneContainers)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/libpod/containers/prune"), s.APIHandler(handlers.PruneContainers)).Methods(http.MethodPost) // swagger:operation GET /libpod/containers/showmounted libpod showMounterContainers // --- // tags: @@ -655,7 +655,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // type: string // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/containers/showmounted"), APIHandler(s.Context, libpod.ShowMountedContainers)).Methods(http.MethodGet) + r.HandleFunc(VersionedPath("/libpod/containers/showmounted"), s.APIHandler(libpod.ShowMountedContainers)).Methods(http.MethodGet) // swagger:operation DELETE /libpod/containers/{name} libpod libpodRemoveContainer // --- // tags: @@ -689,7 +689,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/ConflictError" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/containers/{name}"), APIHandler(s.Context, libpod.RemoveContainer)).Methods(http.MethodDelete) + r.HandleFunc(VersionedPath("/libpod/containers/{name}"), s.APIHandler(libpod.RemoveContainer)).Methods(http.MethodDelete) // swagger:operation GET /libpod/containers/{name}/json libpod libpodGetContainer // --- // tags: @@ -715,7 +715,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/containers/{name}/json"), APIHandler(s.Context, libpod.GetContainer)).Methods(http.MethodGet) + r.HandleFunc(VersionedPath("/libpod/containers/{name}/json"), s.APIHandler(libpod.GetContainer)).Methods(http.MethodGet) // swagger:operation POST /libpod/containers/{name}/kill libpod libpodKillContainer // --- // tags: @@ -744,7 +744,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/ConflictError" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/containers/{name}/kill"), APIHandler(s.Context, libpod.KillContainer)).Methods(http.MethodGet) + r.HandleFunc(VersionedPath("/libpod/containers/{name}/kill"), s.APIHandler(libpod.KillContainer)).Methods(http.MethodGet) // swagger:operation GET /libpod/containers/{name}/mount libpod mountContainer // --- // tags: @@ -770,7 +770,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/containers/{name}/mount"), APIHandler(s.Context, libpod.MountContainer)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/libpod/containers/{name}/mount"), s.APIHandler(libpod.MountContainer)).Methods(http.MethodPost) // swagger:operation POST /libpod/containers/{name}/unmount libpod libpodUnmountContainer // --- // tags: @@ -792,7 +792,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/containers/{name}/unmount"), APIHandler(s.Context, libpod.UnmountContainer)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/libpod/containers/{name}/unmount"), s.APIHandler(libpod.UnmountContainer)).Methods(http.MethodPost) // swagger:operation GET /libpod/containers/{name}/logs libpod libpodLogsFromContainer // --- // tags: @@ -844,7 +844,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/containers/{name}/logs"), APIHandler(s.Context, generic.LogsFromContainer)).Methods(http.MethodGet) + r.HandleFunc(VersionedPath("/libpod/containers/{name}/logs"), s.APIHandler(generic.LogsFromContainer)).Methods(http.MethodGet) // swagger:operation POST /libpod/containers/{name}/pause libpod libpodPauseContainer // --- // tags: @@ -866,7 +866,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // "$ref": "#/responses/NoSuchContainer" // 500: // "$ref": "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/pause"), APIHandler(s.Context, handlers.PauseContainer)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/libpod/containers/{name:..*}/pause"), s.APIHandler(handlers.PauseContainer)).Methods(http.MethodPost) // swagger:operation POST /libpod/containers/{name}/restart libpod libpodRestartContainer // --- // tags: @@ -891,7 +891,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/containers/{name}/restart"), APIHandler(s.Context, handlers.RestartContainer)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/libpod/containers/{name}/restart"), s.APIHandler(handlers.RestartContainer)).Methods(http.MethodPost) // swagger:operation POST /libpod/containers/{name}/start libpod libpodStartContainer // --- // tags: @@ -919,7 +919,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/containers/{name}/start"), APIHandler(s.Context, handlers.StartContainer)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/libpod/containers/{name}/start"), s.APIHandler(handlers.StartContainer)).Methods(http.MethodPost) // swagger:operation GET /libpod/containers/{name}/stats libpod libpodStatsContainer // --- // tags: @@ -946,7 +946,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/containers/{name}/stats"), APIHandler(s.Context, generic.StatsContainer)).Methods(http.MethodGet) + r.HandleFunc(VersionedPath("/libpod/containers/{name}/stats"), s.APIHandler(generic.StatsContainer)).Methods(http.MethodGet) // swagger:operation GET /libpod/containers/{name}/top libpod libpodTopContainer // --- // tags: @@ -980,7 +980,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/containers/{name}/top"), APIHandler(s.Context, handlers.TopContainer)).Methods(http.MethodGet) + r.HandleFunc(VersionedPath("/libpod/containers/{name}/top"), s.APIHandler(handlers.TopContainer)).Methods(http.MethodGet) // swagger:operation POST /libpod/containers/{name}/unpause libpod libpodUnpauseContainer // --- // tags: @@ -1001,7 +1001,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/containers/{name}/unpause"), APIHandler(s.Context, handlers.UnpauseContainer)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/libpod/containers/{name}/unpause"), s.APIHandler(handlers.UnpauseContainer)).Methods(http.MethodPost) // swagger:operation POST /libpod/containers/{name}/wait libpod libpodWaitContainer // --- // tags: @@ -1022,7 +1022,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/containers/{name}/wait"), APIHandler(s.Context, libpod.WaitContainer)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/libpod/containers/{name}/wait"), s.APIHandler(libpod.WaitContainer)).Methods(http.MethodPost) // swagger:operation POST /libpod/containers/{name}/exists libpod containerExists // --- // tags: @@ -1044,7 +1044,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/containers/{name}/exists"), APIHandler(s.Context, libpod.ContainerExists)).Methods(http.MethodGet) + r.HandleFunc(VersionedPath("/libpod/containers/{name}/exists"), s.APIHandler(libpod.ContainerExists)).Methods(http.MethodGet) // swagger:operation POST /libpod/containers/{name}/stop libpod libpodStopContainer // --- // tags: @@ -1071,7 +1071,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/containers/{name}/stop"), APIHandler(s.Context, handlers.StopContainer)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/libpod/containers/{name}/stop"), s.APIHandler(handlers.StopContainer)).Methods(http.MethodPost) // swagger:operation POST /libpod/containers/{name}/attach libpod libpodAttach // --- // tags: @@ -1126,7 +1126,7 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/containers/{name}/attach"), APIHandler(s.Context, handlers.AttachContainer)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/libpod/containers/{name}/attach"), s.APIHandler(handlers.AttachContainer)).Methods(http.MethodPost) // swagger:operation POST /libpod/containers/{name}/resize libpod libpodResize // --- // tags: @@ -1158,6 +1158,6 @@ func (s *APIServer) RegisterContainersHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/containers/{name}/resize"), APIHandler(s.Context, handlers.ResizeContainer)).Methods(http.MethodPost) + r.HandleFunc(VersionedPath("/libpod/containers/{name}/resize"), s.APIHandler(handlers.ResizeContainer)).Methods(http.MethodPost) return nil } diff --git a/pkg/api/server/register_distribution.go b/pkg/api/server/register_distribution.go index b0ac61fb8..f03662224 100644 --- a/pkg/api/server/register_distribution.go +++ b/pkg/api/server/register_distribution.go @@ -5,7 +5,7 @@ import ( "github.com/gorilla/mux" ) -func (s *APIServer) RegisterDistributionHandlers(r *mux.Router) error { +func (s *APIServer) registerDistributionHandlers(r *mux.Router) error { r.HandleFunc(VersionedPath("/distribution/{name}/json"), handlers.UnsupportedHandler) return nil } diff --git a/pkg/api/server/register_events.go b/pkg/api/server/register_events.go index 090f66323..a511725b2 100644 --- a/pkg/api/server/register_events.go +++ b/pkg/api/server/register_events.go @@ -5,7 +5,7 @@ import ( "github.com/gorilla/mux" ) -func (s *APIServer) RegisterEventsHandlers(r *mux.Router) error { +func (s *APIServer) registerEventsHandlers(r *mux.Router) error { // swagger:operation GET /events system getEvents // --- // tags: @@ -32,6 +32,6 @@ func (s *APIServer) RegisterEventsHandlers(r *mux.Router) error { // description: returns a string of json data describing an event // 500: // "$ref": "#/responses/InternalError" - r.Handle(VersionedPath("/events"), APIHandler(s.Context, handlers.GetEvents)) + r.Handle(VersionedPath("/events"), s.APIHandler(handlers.GetEvents)) return nil } diff --git a/pkg/api/server/register_exec.go b/pkg/api/server/register_exec.go index dbf04dc19..581f6f79a 100644 --- a/pkg/api/server/register_exec.go +++ b/pkg/api/server/register_exec.go @@ -74,7 +74,7 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error { // description: container is paused // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/containers/{name}/create"), APIHandler(s.Context, handlers.CreateExec)).Methods(http.MethodPost) + r.Handle(VersionedPath("/containers/{name}/create"), s.APIHandler(handlers.CreateExec)).Methods(http.MethodPost) // swagger:operation POST /exec/{id}/start compat startExec // --- // tags: @@ -110,7 +110,7 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error { // description: container is stopped or paused // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/exec/{id}/start"), APIHandler(s.Context, handlers.StartExec)).Methods(http.MethodPost) + r.Handle(VersionedPath("/exec/{id}/start"), s.APIHandler(handlers.StartExec)).Methods(http.MethodPost) // swagger:operation POST /exec/{id}/resize compat resizeExec // --- // tags: @@ -141,7 +141,7 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchExecInstance" // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/exec/{id}/resize"), APIHandler(s.Context, handlers.ResizeExec)).Methods(http.MethodPost) + r.Handle(VersionedPath("/exec/{id}/resize"), s.APIHandler(handlers.ResizeExec)).Methods(http.MethodPost) // swagger:operation GET /exec/{id}/inspect compat inspectExec // --- // tags: @@ -163,7 +163,7 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchExecInstance" // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/exec/{id}/json"), APIHandler(s.Context, handlers.InspectExec)).Methods(http.MethodGet) + r.Handle(VersionedPath("/exec/{id}/json"), s.APIHandler(handlers.InspectExec)).Methods(http.MethodGet) /* libpod api follows @@ -235,7 +235,7 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error { // description: container is paused // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/libpod/containers/{name}/create"), APIHandler(s.Context, handlers.CreateExec)).Methods(http.MethodPost) + r.Handle(VersionedPath("/libpod/containers/{name}/create"), s.APIHandler(handlers.CreateExec)).Methods(http.MethodPost) // swagger:operation POST /libpod/exec/{id}/start libpod libpodStartExec // --- // tags: @@ -271,7 +271,7 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error { // description: container is stopped or paused // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/libpod/exec/{id}/start"), APIHandler(s.Context, handlers.StartExec)).Methods(http.MethodPost) + r.Handle(VersionedPath("/libpod/exec/{id}/start"), s.APIHandler(handlers.StartExec)).Methods(http.MethodPost) // swagger:operation POST /libpod/exec/{id}/resize libpod libpodResizeExec // --- // tags: @@ -302,7 +302,7 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchExecInstance" // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/libpod/exec/{id}/resize"), APIHandler(s.Context, handlers.ResizeExec)).Methods(http.MethodPost) + r.Handle(VersionedPath("/libpod/exec/{id}/resize"), s.APIHandler(handlers.ResizeExec)).Methods(http.MethodPost) // swagger:operation GET /libpod/exec/{id}/inspect libpod libpodInspectExec // --- // tags: @@ -324,6 +324,6 @@ func (s *APIServer) registerExecHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchExecInstance" // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/libpod/exec/{id}/json"), APIHandler(s.Context, handlers.InspectExec)).Methods(http.MethodGet) + r.Handle(VersionedPath("/libpod/exec/{id}/json"), s.APIHandler(handlers.InspectExec)).Methods(http.MethodGet) return nil } diff --git a/pkg/api/server/register_healthcheck.go b/pkg/api/server/register_healthcheck.go index 1286324f0..5466e2905 100644 --- a/pkg/api/server/register_healthcheck.go +++ b/pkg/api/server/register_healthcheck.go @@ -8,6 +8,6 @@ import ( ) func (s *APIServer) registerHealthCheckHandlers(r *mux.Router) error { - r.Handle(VersionedPath("/libpod/containers/{name}/runhealthcheck"), APIHandler(s.Context, libpod.RunHealthCheck)).Methods(http.MethodGet) + r.Handle(VersionedPath("/libpod/containers/{name}/runhealthcheck"), s.APIHandler(libpod.RunHealthCheck)).Methods(http.MethodGet) return nil } diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index f082c5fec..fc6c2571e 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -47,8 +47,8 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchImage" // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/images/create"), APIHandler(s.Context, generic.CreateImageFromImage)).Methods(http.MethodPost).Queries("fromImage", "{fromImage}") - r.Handle(VersionedPath("/images/create"), APIHandler(s.Context, generic.CreateImageFromSrc)).Methods(http.MethodPost).Queries("fromSrc", "{fromSrc}") + r.Handle(VersionedPath("/images/create"), s.APIHandler(generic.CreateImageFromImage)).Methods(http.MethodPost).Queries("fromImage", "{fromImage}") + r.Handle(VersionedPath("/images/create"), s.APIHandler(generic.CreateImageFromSrc)).Methods(http.MethodPost).Queries("fromSrc", "{fromSrc}") // swagger:operation GET /images/json compat listImages // --- // tags: @@ -83,7 +83,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: "#/responses/DockerImageSummary" // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/images/json"), APIHandler(s.Context, generic.GetImages)).Methods(http.MethodGet) + r.Handle(VersionedPath("/images/json"), s.APIHandler(generic.GetImages)).Methods(http.MethodGet) // swagger:operation POST /images/load compat importImage // --- // tags: @@ -107,7 +107,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // description: no error // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/images/load"), APIHandler(s.Context, generic.LoadImages)).Methods(http.MethodPost) + r.Handle(VersionedPath("/images/load"), s.APIHandler(generic.LoadImages)).Methods(http.MethodPost) // swagger:operation POST /images/prune compat pruneImages // --- // tags: @@ -132,7 +132,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: "#/responses/DocsImageDeleteResponse" // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/images/prune"), APIHandler(s.Context, generic.PruneImages)).Methods(http.MethodPost) + r.Handle(VersionedPath("/images/prune"), s.APIHandler(generic.PruneImages)).Methods(http.MethodPost) // swagger:operation GET /images/search compat searchImages // --- // tags: @@ -163,7 +163,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: "#/responses/DocsSearchResponse" // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/images/search"), APIHandler(s.Context, handlers.SearchImages)).Methods(http.MethodGet) + r.Handle(VersionedPath("/images/search"), s.APIHandler(handlers.SearchImages)).Methods(http.MethodGet) // swagger:operation DELETE /images/{name} compat removeImage // --- // tags: @@ -195,7 +195,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: '#/responses/ConflictError' // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/images/{name}"), APIHandler(s.Context, handlers.RemoveImage)).Methods(http.MethodDelete) + r.Handle(VersionedPath("/images/{name}"), s.APIHandler(handlers.RemoveImage)).Methods(http.MethodDelete) // swagger:operation GET /images/{name}/get compat exportImage // --- // tags: @@ -218,7 +218,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // format: binary // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/images/{name}/get"), APIHandler(s.Context, generic.ExportImage)).Methods(http.MethodGet) + r.Handle(VersionedPath("/images/{name}/get"), s.APIHandler(generic.ExportImage)).Methods(http.MethodGet) // swagger:operation GET /images/{name}/history compat imageHistory // --- // tags: @@ -240,7 +240,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchImage" // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/images/{name}/history"), APIHandler(s.Context, handlers.HistoryImage)).Methods(http.MethodGet) + r.Handle(VersionedPath("/images/{name}/history"), s.APIHandler(handlers.HistoryImage)).Methods(http.MethodGet) // swagger:operation GET /images/{name}/json compat inspectImage // --- // tags: @@ -262,7 +262,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchImage" // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/images/{name}/json"), APIHandler(s.Context, generic.GetImage)) + r.Handle(VersionedPath("/images/{name}/json"), s.APIHandler(generic.GetImage)) // swagger:operation POST /images/{name}/tag compat tagImage // --- // tags: @@ -296,7 +296,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: '#/responses/ConflictError' // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/images/{name}/tag"), APIHandler(s.Context, handlers.TagImage)).Methods(http.MethodPost) + r.Handle(VersionedPath("/images/{name}/tag"), s.APIHandler(handlers.TagImage)).Methods(http.MethodPost) // swagger:operation POST /commit/ compat commitContainer // --- // tags: @@ -341,7 +341,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: '#/responses/NoSuchImage' // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/commit"), APIHandler(s.Context, generic.CommitContainer)).Methods(http.MethodPost) + r.Handle(VersionedPath("/commit"), s.APIHandler(generic.CommitContainer)).Methods(http.MethodPost) // swagger:operation POST /build images buildImage // --- @@ -551,7 +551,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: "#/responses/BadParamError" // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/build"), APIHandler(s.Context, handlers.BuildImage)).Methods(http.MethodPost) + r.Handle(VersionedPath("/build"), s.APIHandler(handlers.BuildImage)).Methods(http.MethodPost) /* libpod endpoints */ @@ -577,8 +577,33 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: '#/responses/NoSuchImage' // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/libpod/images/{name}/exists"), APIHandler(s.Context, libpod.ImageExists)) - r.Handle(VersionedPath("/libpod/images/{name}/tree"), APIHandler(s.Context, libpod.ImageTree)) + r.Handle(VersionedPath("/libpod/images/{name}/exists"), s.APIHandler(libpod.ImageExists)) + // swagger:operation POST /libpod/images/{name}/tree libpod libpodImageTree + // --- + // tags: + // - images + // summary: Image tree + // description: Retrieve the image tree for the provided image name or ID + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: the name or ID of the container + // - in: query + // name: whatrequires + // type: boolean + // description: show all child images and layers of the specified image + // produces: + // - application/json + // responses: + // 200: + // $ref: '#/responses/LibpodImageTreeResponse' + // 401: + // $ref: '#/responses/NoSuchImage' + // 500: + // $ref: '#/responses/InternalError' + r.Handle(VersionedPath("/libpod/images/{name}/tree"), s.APIHandler(libpod.ImageTree)) // swagger:operation GET /libpod/images/{name}/history libpod libpodImageHistory // --- // tags: @@ -600,7 +625,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: '#/responses/NoSuchImage' // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/libpod/images/{name}/history"), APIHandler(s.Context, handlers.HistoryImage)).Methods(http.MethodGet) + r.Handle(VersionedPath("/libpod/images/{name}/history"), s.APIHandler(handlers.HistoryImage)).Methods(http.MethodGet) // swagger:operation GET /libpod/images/json libpod libpodListImages // --- // tags: @@ -630,7 +655,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: "#/responses/DockerImageSummary" // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/libpod/images/json"), APIHandler(s.Context, libpod.GetImages)).Methods(http.MethodGet) + r.Handle(VersionedPath("/libpod/images/json"), s.APIHandler(libpod.GetImages)).Methods(http.MethodGet) // swagger:operation POST /libpod/images/load libpod libpodImagesLoad // --- // tags: @@ -656,7 +681,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: "#/responses/BadParamError" // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/libpod/images/load"), APIHandler(s.Context, libpod.ImagesLoad)).Methods(http.MethodPost) + r.Handle(VersionedPath("/libpod/images/load"), s.APIHandler(libpod.ImagesLoad)).Methods(http.MethodPost) // swagger:operation POST /libpod/images/import libpod libpodImagesImport // --- // tags: @@ -696,7 +721,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: "#/responses/BadParamError" // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/libpod/images/import"), APIHandler(s.Context, libpod.ImagesImport)).Methods(http.MethodPost) + r.Handle(VersionedPath("/libpod/images/import"), s.APIHandler(libpod.ImagesImport)).Methods(http.MethodPost) // swagger:operation POST /libpod/images/pull libpod libpodImagesPull // --- // tags: @@ -738,7 +763,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: "#/responses/BadParamError" // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/libpod/images/pull"), APIHandler(s.Context, libpod.ImagesPull)).Methods(http.MethodPost) + r.Handle(VersionedPath("/libpod/images/pull"), s.APIHandler(libpod.ImagesPull)).Methods(http.MethodPost) // swagger:operation POST /libpod/images/prune libpod libpodPruneImages // --- // tags: @@ -763,7 +788,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: "#/responses/DocsImageDeleteResponse" // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/libpod/images/prune"), APIHandler(s.Context, libpod.PruneImages)).Methods(http.MethodPost) + r.Handle(VersionedPath("/libpod/images/prune"), s.APIHandler(libpod.PruneImages)).Methods(http.MethodPost) // swagger:operation GET /libpod/images/search libpod libpodSearchImages // --- // tags: @@ -794,7 +819,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: "#/responses/DocsSearchResponse" // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/libpod/images/search"), APIHandler(s.Context, handlers.SearchImages)).Methods(http.MethodGet) + r.Handle(VersionedPath("/libpod/images/search"), s.APIHandler(handlers.SearchImages)).Methods(http.MethodGet) // swagger:operation DELETE /libpod/images/{name} libpod libpodRemoveImage // --- // tags: @@ -824,7 +849,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: '#/responses/ConflictError' // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/libpod/images/{name}"), APIHandler(s.Context, handlers.RemoveImage)).Methods(http.MethodDelete) + r.Handle(VersionedPath("/libpod/images/{name}"), s.APIHandler(handlers.RemoveImage)).Methods(http.MethodDelete) // swagger:operation GET /libpod/images/{name}/get libpod libpoodExportImage // --- // tags: @@ -857,7 +882,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: '#/responses/NoSuchImage' // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/libpod/images/{name}/get"), APIHandler(s.Context, libpod.ExportImage)).Methods(http.MethodGet) + r.Handle(VersionedPath("/libpod/images/{name}/get"), s.APIHandler(libpod.ExportImage)).Methods(http.MethodGet) // swagger:operation GET /libpod/images/{name}/json libpod libpodInspectImage // --- // tags: @@ -879,7 +904,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: '#/responses/NoSuchImage' // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/libpod/images/{name}/json"), APIHandler(s.Context, libpod.GetImage)) + r.Handle(VersionedPath("/libpod/images/{name}/json"), s.APIHandler(libpod.GetImage)) // swagger:operation POST /libpod/images/{name}/tag libpod libpodTagImage // --- // tags: @@ -913,7 +938,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: '#/responses/ConflictError' // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/libpod/images/{name}/tag"), APIHandler(s.Context, handlers.TagImage)).Methods(http.MethodPost) + r.Handle(VersionedPath("/libpod/images/{name}/tag"), s.APIHandler(handlers.TagImage)).Methods(http.MethodPost) return nil } diff --git a/pkg/api/server/register_info.go b/pkg/api/server/register_info.go index 8c50fed7f..36c467cc3 100644 --- a/pkg/api/server/register_info.go +++ b/pkg/api/server/register_info.go @@ -21,6 +21,6 @@ func (s *APIServer) registerInfoHandlers(r *mux.Router) error { // description: to be determined // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/info"), APIHandler(s.Context, generic.GetInfo)).Methods(http.MethodGet) + r.Handle(VersionedPath("/info"), s.APIHandler(generic.GetInfo)).Methods(http.MethodGet) return nil } diff --git a/pkg/api/server/register_monitor.go b/pkg/api/server/register_monitor.go index e6c235419..dbe0d27ce 100644 --- a/pkg/api/server/register_monitor.go +++ b/pkg/api/server/register_monitor.go @@ -5,7 +5,7 @@ import ( "github.com/gorilla/mux" ) -func (s *APIServer) RegisterMonitorHandlers(r *mux.Router) error { - r.Handle(VersionedPath("/monitor"), APIHandler(s.Context, handlers.UnsupportedHandler)) +func (s *APIServer) registerMonitorHandlers(r *mux.Router) error { + r.Handle(VersionedPath("/monitor"), s.APIHandler(handlers.UnsupportedHandler)) return nil } diff --git a/pkg/api/server/register_ping.go b/pkg/api/server/register_ping.go index 086e674a1..349a8a71a 100644 --- a/pkg/api/server/register_ping.go +++ b/pkg/api/server/register_ping.go @@ -9,8 +9,8 @@ import ( func (s *APIServer) registerPingHandlers(r *mux.Router) error { - r.Handle("/_ping", APIHandler(s.Context, handlers.Ping)).Methods(http.MethodGet) - r.Handle("/_ping", APIHandler(s.Context, handlers.Ping)).Methods(http.MethodHead) + r.Handle("/_ping", s.APIHandler(handlers.Ping)).Methods(http.MethodGet) + r.Handle("/_ping", s.APIHandler(handlers.Ping)).Methods(http.MethodHead) // swagger:operation GET /libpod/_ping libpod libpodPingGet // --- @@ -61,7 +61,7 @@ func (s *APIServer) registerPingHandlers(r *mux.Router) error { // determine if talking to Podman engine or another engine // 500: // $ref: "#/responses/InternalError" - r.Handle("/libpod/_ping", APIHandler(s.Context, handlers.Ping)).Methods(http.MethodGet) - r.Handle("/libpod/_ping", APIHandler(s.Context, handlers.Ping)).Methods(http.MethodHead) + r.Handle("/libpod/_ping", s.APIHandler(handlers.Ping)).Methods(http.MethodGet) + r.Handle("/libpod/_ping", s.APIHandler(handlers.Ping)).Methods(http.MethodHead) return nil } diff --git a/pkg/api/server/register_plugins.go b/pkg/api/server/register_plugins.go index 7fd6b9c4c..479a79d1f 100644 --- a/pkg/api/server/register_plugins.go +++ b/pkg/api/server/register_plugins.go @@ -5,7 +5,7 @@ import ( "github.com/gorilla/mux" ) -func (s *APIServer) RegisterPluginsHandlers(r *mux.Router) error { - r.Handle(VersionedPath("/plugins"), APIHandler(s.Context, handlers.UnsupportedHandler)) +func (s *APIServer) registerPluginsHandlers(r *mux.Router) error { + r.Handle(VersionedPath("/plugins"), s.APIHandler(handlers.UnsupportedHandler)) return nil } diff --git a/pkg/api/server/register_pods.go b/pkg/api/server/register_pods.go index 974568d47..1c7486711 100644 --- a/pkg/api/server/register_pods.go +++ b/pkg/api/server/register_pods.go @@ -25,8 +25,8 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // $ref: "#/responses/BadParamError" // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/libpod/pods/json"), APIHandler(s.Context, libpod.Pods)).Methods(http.MethodGet) - r.Handle(VersionedPath("/libpod/pods/create"), APIHandler(s.Context, libpod.PodCreate)).Methods(http.MethodPost) + r.Handle(VersionedPath("/libpod/pods/json"), s.APIHandler(libpod.Pods)).Methods(http.MethodGet) + r.Handle(VersionedPath("/libpod/pods/create"), s.APIHandler(libpod.PodCreate)).Methods(http.MethodPost) // swagger:operation POST /libpod/pods/prune pods PrunePods // --- // summary: Prune unused pods @@ -45,7 +45,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // description: pod already exists // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/libpod/pods/prune"), APIHandler(s.Context, libpod.PodPrune)).Methods(http.MethodPost) + r.Handle(VersionedPath("/libpod/pods/prune"), s.APIHandler(libpod.PodPrune)).Methods(http.MethodPost) // swagger:operation DELETE /libpod/pods/{name} pods removePod // --- // summary: Remove pod @@ -70,7 +70,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchPod" // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/libpod/pods/{name}"), APIHandler(s.Context, libpod.PodDelete)).Methods(http.MethodDelete) + r.Handle(VersionedPath("/libpod/pods/{name}"), s.APIHandler(libpod.PodDelete)).Methods(http.MethodDelete) // swagger:operation GET /libpod/pods/{name}/json pods inspectPod // --- // summary: Inspect pod @@ -89,7 +89,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchPod" // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/libpod/pods/{name}/json"), APIHandler(s.Context, libpod.PodInspect)).Methods(http.MethodGet) + r.Handle(VersionedPath("/libpod/pods/{name}/json"), s.APIHandler(libpod.PodInspect)).Methods(http.MethodGet) // swagger:operation GET /libpod/pods/{name}/exists pods podExists // --- // summary: Pod exists @@ -109,7 +109,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchPod" // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/libpod/pods/{name}/exists"), APIHandler(s.Context, libpod.PodExists)).Methods(http.MethodGet) + r.Handle(VersionedPath("/libpod/pods/{name}/exists"), s.APIHandler(libpod.PodExists)).Methods(http.MethodGet) // swagger:operation POST /libpod/pods/{name}/kill pods killPod // --- // summary: Kill a pod @@ -137,7 +137,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // $ref: "#/responses/ConflictError" // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/libpod/pods/{name}/kill"), APIHandler(s.Context, libpod.PodKill)).Methods(http.MethodPost) + r.Handle(VersionedPath("/libpod/pods/{name}/kill"), s.APIHandler(libpod.PodKill)).Methods(http.MethodPost) // swagger:operation POST /libpod/pods/{name}/pause pods pausePod // --- // summary: Pause a pod @@ -157,7 +157,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchPod" // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/libpod/pods/{name}/pause"), APIHandler(s.Context, libpod.PodPause)).Methods(http.MethodPost) + r.Handle(VersionedPath("/libpod/pods/{name}/pause"), s.APIHandler(libpod.PodPause)).Methods(http.MethodPost) // swagger:operation POST /libpod/pods/{name}/restart pods restartPod // --- // summary: Restart a pod @@ -176,7 +176,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchPod" // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/libpod/pods/{name}/restart"), APIHandler(s.Context, libpod.PodRestart)).Methods(http.MethodPost) + r.Handle(VersionedPath("/libpod/pods/{name}/restart"), s.APIHandler(libpod.PodRestart)).Methods(http.MethodPost) // swagger:operation POST /libpod/pods/{name}/start pods startPod // --- // summary: Start a pod @@ -197,7 +197,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchPod" // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/libpod/pods/{name}/start"), APIHandler(s.Context, libpod.PodStart)).Methods(http.MethodPost) + r.Handle(VersionedPath("/libpod/pods/{name}/start"), s.APIHandler(libpod.PodStart)).Methods(http.MethodPost) // swagger:operation POST /libpod/pods/{name}/stop pods stopPod // --- // summary: Stop a pod @@ -224,7 +224,7 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchPod" // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/libpod/pods/{name}/stop"), APIHandler(s.Context, libpod.PodStop)).Methods(http.MethodPost) + r.Handle(VersionedPath("/libpod/pods/{name}/stop"), s.APIHandler(libpod.PodStop)).Methods(http.MethodPost) // swagger:operation POST /libpod/pods/{name}/unpause pods unpausePod // --- // summary: Unpause a pod @@ -243,6 +243,6 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchPod" // 500: // $ref: "#/responses/InternalError" - r.Handle(VersionedPath("/libpod/pods/{name}/unpause"), APIHandler(s.Context, libpod.PodUnpause)).Methods(http.MethodPost) + r.Handle(VersionedPath("/libpod/pods/{name}/unpause"), s.APIHandler(libpod.PodUnpause)).Methods(http.MethodPost) return nil } diff --git a/pkg/api/server/register_swarm.go b/pkg/api/server/register_swarm.go index 63d8acfde..e37ac4e41 100644 --- a/pkg/api/server/register_swarm.go +++ b/pkg/api/server/register_swarm.go @@ -9,7 +9,7 @@ import ( "github.com/sirupsen/logrus" ) -func (s *APIServer) RegisterSwarmHandlers(r *mux.Router) error { +func (s *APIServer) registerSwarmHandlers(r *mux.Router) error { r.PathPrefix("/v{version:[0-9.]+}/configs/").HandlerFunc(noSwarm) r.PathPrefix("/v{version:[0-9.]+}/nodes/").HandlerFunc(noSwarm) r.PathPrefix("/v{version:[0-9.]+}/secrets/").HandlerFunc(noSwarm) diff --git a/pkg/api/server/register_system.go b/pkg/api/server/register_system.go index f0eaeffd2..73f0fb470 100644 --- a/pkg/api/server/register_system.go +++ b/pkg/api/server/register_system.go @@ -6,6 +6,6 @@ import ( ) func (s *APIServer) registerSystemHandlers(r *mux.Router) error { - r.Handle(VersionedPath("/system/df"), APIHandler(s.Context, generic.GetDiskUsage)) + r.Handle(VersionedPath("/system/df"), s.APIHandler(generic.GetDiskUsage)) return nil } diff --git a/pkg/api/server/register_version.go b/pkg/api/server/register_version.go index d3b47c2a9..c8f9dcede 100644 --- a/pkg/api/server/register_version.go +++ b/pkg/api/server/register_version.go @@ -6,7 +6,7 @@ import ( ) func (s *APIServer) registerVersionHandlers(r *mux.Router) error { - r.Handle("/version", APIHandler(s.Context, handlers.VersionHandler)) - r.Handle(VersionedPath("/version"), APIHandler(s.Context, handlers.VersionHandler)) + r.Handle("/version", s.APIHandler(handlers.VersionHandler)) + r.Handle(VersionedPath("/version"), s.APIHandler(handlers.VersionHandler)) return nil } diff --git a/pkg/api/server/register_volumes.go b/pkg/api/server/register_volumes.go index d34c71238..1ccf92b46 100644 --- a/pkg/api/server/register_volumes.go +++ b/pkg/api/server/register_volumes.go @@ -18,8 +18,8 @@ func (s *APIServer) registerVolumeHandlers(r *mux.Router) error { // description: tbd // '500': // "$ref": "#/responses/InternalError" - r.Handle("/libpod/volumes/create", APIHandler(s.Context, libpod.CreateVolume)).Methods(http.MethodPost) - r.Handle("/libpod/volumes/json", APIHandler(s.Context, libpod.ListVolumes)).Methods(http.MethodGet) + r.Handle("/libpod/volumes/create", s.APIHandler(libpod.CreateVolume)).Methods(http.MethodPost) + r.Handle("/libpod/volumes/json", s.APIHandler(libpod.ListVolumes)).Methods(http.MethodGet) // swagger:operation POST /volumes/prune volumes pruneVolumes // --- // summary: Prune volumes @@ -30,7 +30,7 @@ func (s *APIServer) registerVolumeHandlers(r *mux.Router) error { // description: no error // '500': // "$ref": "#/responses/InternalError" - r.Handle("/libpod/volumes/prune", APIHandler(s.Context, libpod.PruneVolumes)).Methods(http.MethodPost) + r.Handle("/libpod/volumes/prune", s.APIHandler(libpod.PruneVolumes)).Methods(http.MethodPost) // swagger:operation GET /volumes/{name}/json volumes inspectVolume // --- // summary: Inspect volume @@ -49,7 +49,7 @@ func (s *APIServer) registerVolumeHandlers(r *mux.Router) error { // "$ref": "#/responses/NoSuchVolume" // '500': // "$ref": "#/responses/InternalError" - r.Handle("/libpod/volumes/{name}/json", APIHandler(s.Context, libpod.InspectVolume)).Methods(http.MethodGet) + r.Handle("/libpod/volumes/{name}/json", s.APIHandler(libpod.InspectVolume)).Methods(http.MethodGet) // swagger:operation DELETE /volumes/{name} volumes removeVolume // --- // summary: Remove volume @@ -74,6 +74,6 @@ func (s *APIServer) registerVolumeHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchVolume" // 500: // $ref: "#/responses/InternalError" - r.Handle("/libpod/volumes/{name}", APIHandler(s.Context, libpod.RemoveVolume)).Methods(http.MethodDelete) + r.Handle("/libpod/volumes/{name}", s.APIHandler(libpod.RemoveVolume)).Methods(http.MethodDelete) return nil } diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go index 2c709bc59..e7b2a5525 100644 --- a/pkg/api/server/server.go +++ b/pkg/api/server/server.go @@ -20,20 +20,26 @@ import ( ) type APIServer struct { - http.Server // The HTTP work happens here - *schema.Decoder // Decoder for Query parameters to structs - context.Context // Context to carry objects to handlers - *libpod.Runtime // Where the real work happens - net.Listener // mux for routing HTTP API calls to libpod routines - context.CancelFunc // Stop APIServer - *time.Timer // Hold timer for sliding window - time.Duration // Duration of client access sliding window + http.Server // The HTTP work happens here + *schema.Decoder // Decoder for Query parameters to structs + context.Context // Context to carry objects to handlers + *libpod.Runtime // Where the real work happens + net.Listener // mux for routing HTTP API calls to libpod routines + context.CancelFunc // Stop APIServer + *time.Timer // Hold timer for sliding window + time.Duration // Duration of client access sliding window + ActiveConnections uint64 // Number of handlers holding a connection + TotalConnections uint64 // Number of connections handled + ConnectionCh chan int // Channel for signalling handler enter/exit } // Number of seconds to wait for next request, if exceeded shutdown server const ( DefaultServiceDuration = 300 * time.Second UnlimitedServiceDuration = 0 * time.Second + EnterHandler = 1 + ExitHandler = -1 + NOOPHandler = 0 ) // NewServer will create and configure a new API server with all defaults @@ -68,31 +74,19 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li Server: http.Server{ Handler: router, ReadHeaderTimeout: 20 * time.Second, - ReadTimeout: 20 * time.Second, - WriteTimeout: 2 * time.Minute, + IdleTimeout: duration, }, - Decoder: handlers.NewAPIDecoder(), - Context: nil, - Runtime: runtime, - Listener: *listener, - CancelFunc: nil, - Duration: duration, + Decoder: handlers.NewAPIDecoder(), + Runtime: runtime, + Listener: *listener, + Duration: duration, + ConnectionCh: make(chan int), } + server.Timer = time.AfterFunc(server.Duration, func() { - if err := server.Shutdown(); err != nil { - logrus.Errorf("unable to shutdown server: %q", err) - } + server.ConnectionCh <- NOOPHandler }) - ctx, cancelFn := context.WithCancel(context.Background()) - server.CancelFunc = cancelFn - - // TODO: Use ConnContext when ported to go 1.13 - ctx = context.WithValue(ctx, "decoder", server.Decoder) - ctx = context.WithValue(ctx, "runtime", runtime) - ctx = context.WithValue(ctx, "shutdownFunc", server.Shutdown) - server.Context = ctx - router.NotFoundHandler = http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { // We can track user errors... @@ -102,20 +96,19 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li ) for _, fn := range []func(*mux.Router) error{ - server.RegisterAuthHandlers, - server.RegisterContainersHandlers, - server.RegisterDistributionHandlers, + server.registerAuthHandlers, + server.registerContainersHandlers, + server.registerDistributionHandlers, server.registerExecHandlers, - server.RegisterEventsHandlers, + server.registerEventsHandlers, server.registerHealthCheckHandlers, server.registerImagesHandlers, server.registerInfoHandlers, - server.RegisterMonitorHandlers, + server.registerMonitorHandlers, server.registerPingHandlers, - server.RegisterPluginsHandlers, + server.registerPluginsHandlers, server.registerPodsHandlers, - server.RegisterSwaggerHandlers, - server.RegisterSwarmHandlers, + server.registerSwarmHandlers, server.registerSystemHandlers, server.registerVersionHandlers, server.registerVolumeHandlers, @@ -145,7 +138,41 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li // Serve starts responding to HTTP requests func (s *APIServer) Serve() error { - defer s.CancelFunc() + // stalker to count the connections. Should the timer expire it will shutdown the service. + go func() { + for { + select { + case delta := <-s.ConnectionCh: + // Always stop the current timer, things will change... + s.Timer.Stop() + switch delta { + case EnterHandler: + s.ActiveConnections += 1 + s.TotalConnections += 1 + case ExitHandler: + s.ActiveConnections -= 1 + if s.ActiveConnections == 0 { + // Server will be shutdown iff the timer expires before being reset or stopped + s.Timer = time.AfterFunc(s.Duration, func() { + if err := s.Shutdown(); err != nil { + logrus.Errorf("Failed to shutdown APIServer: %v", err) + os.Exit(1) + } + }) + } else { + s.Timer.Reset(s.Duration) + } + case NOOPHandler: + // push the check out another duration... + s.Timer.Reset(s.Duration) + default: + logrus.Errorf("ConnectionCh received unsupported input %d", delta) + } + default: + time.Sleep(1 * time.Second) + } + } + }() sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) @@ -155,6 +182,7 @@ func (s *APIServer) Serve() error { err := s.Server.Serve(s.Listener) if err != nil && err != http.ErrServerClosed { errChan <- errors.Wrap(err, "Failed to start APIServer") + return } errChan <- nil }() @@ -171,27 +199,23 @@ func (s *APIServer) Serve() error { // Shutdown is a clean shutdown waiting on existing clients func (s *APIServer) Shutdown() error { - // Duration == 0 flags no auto-shutdown of server + // Duration == 0 flags no auto-shutdown of the server if s.Duration == 0 { + logrus.Debug("APIServer.Shutdown ignored as Duration == 0") return nil } + logrus.Debugf("APIServer.Shutdown called %v, conn %d/%d", time.Now(), s.ActiveConnections, s.TotalConnections) - // We're still in the sliding service window - if s.Timer.Stop() { - s.Timer.Reset(s.Duration) - return nil - } + // Gracefully shutdown server + ctx, cancel := context.WithTimeout(context.Background(), s.Duration) + defer cancel() - // We've been idle for the service window, really shutdown go func() { - err := s.Server.Shutdown(s.Context) + err := s.Server.Shutdown(ctx) if err != nil && err != context.Canceled { logrus.Errorf("Failed to cleanly shutdown APIServer: %s", err.Error()) } }() - - // Wait for graceful shutdown vs. just killing connections and dropping data - <-s.Context.Done() return nil } diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index 182a39f6b..f71d55776 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -452,6 +452,7 @@ func TryJoinFromFilePaths(pausePidPath string, needNewNamespace bool, paths []st var lastErr error var pausePid int + foundProcess := false for _, path := range paths { if !needNewNamespace { @@ -502,12 +503,16 @@ func TryJoinFromFilePaths(pausePidPath string, needNewNamespace bool, paths []st } pausePid, err = strconv.Atoi(string(b[:n])) - if err == nil { + if err == nil && unix.Kill(pausePid, 0) == nil { + foundProcess = true lastErr = nil break } } } + if !foundProcess { + return BecomeRootInUserNS(pausePidPath) + } if lastErr != nil { return false, 0, lastErr } diff --git a/pkg/systemd/activation.go b/pkg/systemd/activation.go new file mode 100644 index 000000000..c8b2389dc --- /dev/null +++ b/pkg/systemd/activation.go @@ -0,0 +1,40 @@ +package systemd + +import ( + "os" + "strconv" + "strings" +) + +// SocketActivated determine if podman is running under the socket activation protocol +func SocketActivated() bool { + pid, pid_found := os.LookupEnv("LISTEN_PID") + fds, fds_found := os.LookupEnv("LISTEN_FDS") + fdnames, fdnames_found := os.LookupEnv("LISTEN_FDNAMES") + + if !(pid_found && fds_found && fdnames_found) { + return false + } + + p, err := strconv.Atoi(pid) + if err != nil || p != os.Getpid() { + return false + } + + nfds, err := strconv.Atoi(fds) + if err != nil || nfds < 1 { + return false + } + + // First available file descriptor is always 3. + if nfds > 1 { + names := strings.Split(fdnames, ":") + for _, n := range names { + if strings.Contains(n, "podman") { + return true + } + } + } + + return true +} diff --git a/pkg/systemdgen/systemdgen.go b/pkg/systemd/generate/systemdgen.go index 26b3b3756..404347828 100644 --- a/pkg/systemdgen/systemdgen.go +++ b/pkg/systemd/generate/systemdgen.go @@ -1,4 +1,4 @@ -package systemdgen +package generate import ( "bytes" diff --git a/pkg/systemdgen/systemdgen_test.go b/pkg/systemd/generate/systemdgen_test.go index ee2429407..b74b75258 100644 --- a/pkg/systemdgen/systemdgen_test.go +++ b/pkg/systemd/generate/systemdgen_test.go @@ -1,4 +1,4 @@ -package systemdgen +package generate import ( "testing" diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go index b144bfa5e..c4809f16b 100644 --- a/pkg/varlinkapi/images.go +++ b/pkg/varlinkapi/images.go @@ -1016,3 +1016,17 @@ func (i *LibpodAPI) BuildImageHierarchyMap(call iopodman.VarlinkCall, name strin } return call.ReplyBuildImageHierarchyMap(string(b)) } + +// ImageTree returns the image tree string for the provided image name or ID +func (i *LibpodAPI) ImageTree(call iopodman.VarlinkCall, nameOrID string, whatRequires bool) error { + img, err := i.Runtime.ImageRuntime().NewFromLocal(nameOrID) + if err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + + tree, err := img.GenerateTree(whatRequires) + if err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + return call.ReplyImageTree(tree) +} |