summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.golangci.yml1
-rw-r--r--cmd/podman/containers/run.go2
-rw-r--r--cmd/podman/images/rm.go32
-rw-r--r--cmd/podman/system/service.go3
-rw-r--r--cmd/podman/system/service_abi.go57
-rw-r--r--cmd/podman/system/service_unsupported.go14
-rwxr-xr-xhack/golangci-lint.sh15
-rw-r--r--pkg/api/handlers/compat/events.go4
-rw-r--r--pkg/api/handlers/libpod/images.go28
-rw-r--r--pkg/api/handlers/swagger/swagger.go7
-rw-r--r--pkg/api/handlers/types.go58
-rw-r--r--pkg/api/server/register_images.go32
-rw-r--r--pkg/api/types/types.go9
-rw-r--r--pkg/bindings/connection.go4
-rw-r--r--pkg/bindings/images/images.go27
-rw-r--r--pkg/bindings/system/system.go6
-rw-r--r--pkg/domain/entities/engine_container.go1
-rw-r--r--pkg/domain/entities/engine_image.go2
-rw-r--r--pkg/domain/entities/events.go61
-rw-r--r--pkg/domain/entities/images.go25
-rw-r--r--pkg/domain/infra/abi/containers.go2
-rw-r--r--pkg/domain/infra/abi/events.go2
-rw-r--r--pkg/domain/infra/abi/healthcheck.go2
-rw-r--r--pkg/domain/infra/abi/images.go205
-rw-r--r--pkg/domain/infra/abi/images_list.go2
-rw-r--r--pkg/domain/infra/abi/pods.go2
-rw-r--r--pkg/domain/infra/abi/runtime.go2
-rw-r--r--pkg/domain/infra/abi/system.go41
-rw-r--r--pkg/domain/infra/abi/terminal/sigproxy_linux.go2
-rw-r--r--pkg/domain/infra/abi/terminal/terminal.go2
-rw-r--r--pkg/domain/infra/abi/terminal/terminal_linux.go2
-rw-r--r--pkg/domain/infra/tunnel/events.go5
-rw-r--r--pkg/domain/infra/tunnel/images.go21
-rw-r--r--pkg/domain/infra/tunnel/system.go4
34 files changed, 423 insertions, 259 deletions
diff --git a/.golangci.yml b/.golangci.yml
index b04683b7b..5480b02bb 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -28,6 +28,7 @@ linters:
- misspell
- prealloc
- unparam
+ - nakedret
linters-settings:
errcheck:
check-blank: false
diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go
index 9d222e44d..16a54e578 100644
--- a/cmd/podman/containers/run.go
+++ b/cmd/podman/containers/run.go
@@ -122,7 +122,7 @@ func run(cmd *cobra.Command, args []string) error {
return nil
}
if runRmi {
- _, err := registry.ImageEngine().Delete(registry.GetContext(), []string{args[0]}, entities.ImageDeleteOptions{})
+ _, err := registry.ImageEngine().Remove(registry.GetContext(), []string{args[0]}, entities.ImageRemoveOptions{})
if err != nil {
logrus.Errorf("%s", errors.Wrapf(err, "failed removing image"))
}
diff --git a/cmd/podman/images/rm.go b/cmd/podman/images/rm.go
index 135fda387..da6a90d2b 100644
--- a/cmd/podman/images/rm.go
+++ b/cmd/podman/images/rm.go
@@ -2,7 +2,6 @@ package images
import (
"fmt"
- "os"
"github.com/containers/libpod/cmd/podman/registry"
"github.com/containers/libpod/pkg/domain/entities"
@@ -23,7 +22,7 @@ var (
podman image rm c4dfb1609ee2 93fd78260bd1 c0ed59d05ff7`,
}
- imageOpts = entities.ImageDeleteOptions{}
+ imageOpts = entities.ImageRemoveOptions{}
)
func init() {
@@ -40,32 +39,25 @@ func imageRemoveFlagSet(flags *pflag.FlagSet) {
flags.BoolVarP(&imageOpts.All, "all", "a", false, "Remove all images")
flags.BoolVarP(&imageOpts.Force, "force", "f", false, "Force Removal of the image")
}
-func rm(cmd *cobra.Command, args []string) error {
+func rm(cmd *cobra.Command, args []string) error {
if len(args) < 1 && !imageOpts.All {
return errors.Errorf("image name or ID must be specified")
}
if len(args) > 0 && imageOpts.All {
return errors.Errorf("when using the --all switch, you may not pass any images names or IDs")
}
- report, err := registry.ImageEngine().Delete(registry.GetContext(), args, imageOpts)
- if err != nil {
- switch {
- case report != nil && report.ImageNotFound != nil:
- fmt.Fprintln(os.Stderr, err.Error())
- registry.SetExitCode(2)
- case report != nil && report.ImageInUse != nil:
- fmt.Fprintln(os.Stderr, err.Error())
- default:
- return err
+
+ report, err := registry.ImageEngine().Remove(registry.GetContext(), args, imageOpts)
+ if report != nil {
+ for _, u := range report.Untagged {
+ fmt.Println("Untagged: " + u)
}
+ for _, d := range report.Deleted {
+ fmt.Println("Deleted: " + d)
+ }
+ registry.SetExitCode(report.ExitCode)
}
- for _, u := range report.Untagged {
- fmt.Println("Untagged: " + u)
- }
- for _, d := range report.Deleted {
- fmt.Println("Deleted: " + d)
- }
- return nil
+ return err
}
diff --git a/cmd/podman/system/service.go b/cmd/podman/system/service.go
index e09107b53..f4b91dd78 100644
--- a/cmd/podman/system/service.go
+++ b/cmd/podman/system/service.go
@@ -90,7 +90,8 @@ func service(cmd *cobra.Command, args []string) error {
logrus.Warn("This function is EXPERIMENTAL")
fmt.Fprintf(os.Stderr, "This function is EXPERIMENTAL.\n")
- return registry.ContainerEngine().RestService(registry.GetContext(), opts)
+
+ return restService(opts, cmd.Flags(), registry.PodmanConfig())
}
func resolveApiURI(_url []string) (string, error) {
diff --git a/cmd/podman/system/service_abi.go b/cmd/podman/system/service_abi.go
new file mode 100644
index 000000000..3da6ccfc7
--- /dev/null
+++ b/cmd/podman/system/service_abi.go
@@ -0,0 +1,57 @@
+// +build ABISupport
+
+package system
+
+import (
+ "context"
+ "net"
+ "strings"
+
+ api "github.com/containers/libpod/pkg/api/server"
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/domain/infra"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "github.com/spf13/pflag"
+)
+
+func restService(opts entities.ServiceOptions, flags *pflag.FlagSet, cfg *entities.PodmanConfig) error {
+ var (
+ listener *net.Listener
+ err error
+ )
+
+ if opts.URI != "" {
+ fields := strings.Split(opts.URI, ":")
+ if len(fields) == 1 {
+ return errors.Errorf("%s is an invalid socket destination", opts.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", opts.URI)
+ }
+ listener = &l
+ }
+
+ rt, err := infra.GetRuntime(context.Background(), flags, cfg)
+ if err != nil {
+ return err
+ }
+
+ server, err := api.NewServerWithSettings(rt, opts.Timeout, listener)
+ if err != nil {
+ return err
+ }
+ defer func() {
+ if err := server.Shutdown(); err != nil {
+ logrus.Warnf("Error when stopping API service: %s", err)
+ }
+ }()
+
+ err = server.Serve()
+ if listener != nil {
+ _ = (*listener).Close()
+ }
+ return err
+}
diff --git a/cmd/podman/system/service_unsupported.go b/cmd/podman/system/service_unsupported.go
new file mode 100644
index 000000000..95f8189f6
--- /dev/null
+++ b/cmd/podman/system/service_unsupported.go
@@ -0,0 +1,14 @@
+// +build !ABISupport
+
+package system
+
+import (
+ "errors"
+
+ "github.com/containers/libpod/pkg/domain/entities"
+ "github.com/spf13/pflag"
+)
+
+func restService(opts entities.ServiceOptions, flags *pflag.FlagSet, cfg *entities.PodmanConfig) error {
+ return errors.New("not supported")
+}
diff --git a/hack/golangci-lint.sh b/hack/golangci-lint.sh
index 385b21f39..f4e60d8f5 100755
--- a/hack/golangci-lint.sh
+++ b/hack/golangci-lint.sh
@@ -3,13 +3,22 @@
# Need to run linter twice to cover all the build tags code paths
declare -A BUILD_TAGS
+# TODO: add systemd tag
BUILD_TAGS[default]="apparmor,seccomp,selinux"
BUILD_TAGS[abi]="${BUILD_TAGS[default]},ABISupport,varlink,!remoteclient"
-BUILD_TAGS[tunnel]="${BUILD_TAGS[default]},!ABISupport,!varlink,remoteclient"
+BUILD_TAGS[tunnel]="${BUILD_TAGS[default]},!ABISupport,varlink,remoteclient"
+
+declare -A SKIP_DIRS
+SKIP_DIRS[abi]=""
+# TODO: add "ABISupport" build tag to pkg/api
+SKIP_DIRS[tunnel]="pkg/api"
[[ $1 == run ]] && shift
for i in tunnel abi; do
- echo Build Tags: ${BUILD_TAGS[$i]}
- golangci-lint run --build-tags=${BUILD_TAGS[$i]} "$@"
+ echo ""
+ echo Running golangci-lint for "$i"
+ echo Build Tags "$i": ${BUILD_TAGS[$i]}
+ echo Skipped directories "$i": ${SKIP_DIRS[$i]}
+ golangci-lint run --build-tags=${BUILD_TAGS[$i]} --skip-dirs=${SKIP_DIRS[$i]} "$@"
done
diff --git a/pkg/api/handlers/compat/events.go b/pkg/api/handlers/compat/events.go
index 8ef32716d..7ebfb0d1e 100644
--- a/pkg/api/handlers/compat/events.go
+++ b/pkg/api/handlers/compat/events.go
@@ -6,8 +6,8 @@ import (
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/events"
- "github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/utils"
+ "github.com/containers/libpod/pkg/domain/entities"
"github.com/gorilla/schema"
jsoniter "github.com/json-iterator/go"
"github.com/pkg/errors"
@@ -70,7 +70,7 @@ func GetEvents(w http.ResponseWriter, r *http.Request) {
coder.SetEscapeHTML(true)
for event := range eventChannel {
- e := handlers.EventToApiEvent(event)
+ e := entities.ConvertToEntitiesEvent(*event)
if err := coder.Encode(e); err != nil {
logrus.Errorf("unable to write json: %q", err)
}
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index 284b33637..46401e4f2 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -22,6 +22,7 @@ import (
"github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/api/handlers/utils"
"github.com/containers/libpod/pkg/domain/entities"
+ "github.com/containers/libpod/pkg/domain/infra/abi"
"github.com/containers/libpod/pkg/util"
utils2 "github.com/containers/libpod/utils"
"github.com/gorilla/schema"
@@ -698,3 +699,30 @@ func SearchImages(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusOK, reports)
}
+
+// ImagesRemove is the endpoint for image removal.
+func ImagesRemove(w http.ResponseWriter, r *http.Request) {
+ runtime := r.Context().Value("runtime").(*libpod.Runtime)
+ decoder := r.Context().Value("decoder").(*schema.Decoder)
+ query := struct {
+ All bool `schema:"all"`
+ Force bool `schema:"force"`
+ Images []string `schema:"images"`
+ }{
+ All: false,
+ Force: 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
+ }
+
+ opts := entities.ImageRemoveOptions{All: query.All, Force: query.Force}
+
+ imageEngine := abi.ImageEngine{Libpod: runtime}
+ rmReport, rmError := imageEngine.Remove(r.Context(), query.Images, opts)
+ report := handlers.LibpodImagesRemoveReport{ImageRemoveReport: *rmReport, Error: rmError.Error()}
+ utils.WriteResponse(w, http.StatusOK, report)
+}
diff --git a/pkg/api/handlers/swagger/swagger.go b/pkg/api/handlers/swagger/swagger.go
index ba97a4755..87891d4a8 100644
--- a/pkg/api/handlers/swagger/swagger.go
+++ b/pkg/api/handlers/swagger/swagger.go
@@ -49,6 +49,13 @@ type swagLibpodImagesPullResponse struct {
Body handlers.LibpodImagesPullReport
}
+// Remove response
+// swagger:response DocsLibpodImagesRemoveResponse
+type swagLibpodImagesRemoveResponse struct {
+ // in:body
+ Body handlers.LibpodImagesRemoveReport
+}
+
// Delete response
// swagger:response DocsImageDeleteResponse
type swagImageDeleteResponse struct {
diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go
index 4c5e38437..58a12ea6a 100644
--- a/pkg/api/handlers/types.go
+++ b/pkg/api/handlers/types.go
@@ -4,16 +4,13 @@ import (
"context"
"encoding/json"
"fmt"
- "strconv"
"time"
"github.com/containers/image/v5/manifest"
- "github.com/containers/libpod/libpod/events"
libpodImage "github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/domain/entities"
docker "github.com/docker/docker/api/types"
dockerContainer "github.com/docker/docker/api/types/container"
- dockerEvents "github.com/docker/docker/api/types/events"
dockerNetwork "github.com/docker/docker/api/types/network"
"github.com/docker/go-connections/nat"
"github.com/pkg/errors"
@@ -39,6 +36,14 @@ type LibpodImagesPullReport struct {
ID string `json:"id"`
}
+// LibpodImagesRemoveReport is the return type for image removal via the rest
+// api.
+type LibpodImagesRemoveReport struct {
+ entities.ImageRemoveReport
+ // Image removal requires is to return data and an error.
+ Error string
+}
+
type ContainersPruneReport struct {
docker.ContainersPruneReport
}
@@ -143,10 +148,6 @@ type PodCreateConfig struct {
Share string `json:"share"`
}
-type Event struct {
- dockerEvents.Message
-}
-
type HistoryResponse struct {
ID string `json:"Id"`
Created int64 `json:"Created"`
@@ -173,49 +174,6 @@ type ExecCreateResponse struct {
docker.IDResponse
}
-func (e *Event) ToLibpodEvent() *events.Event {
- exitCode, err := strconv.Atoi(e.Actor.Attributes["containerExitCode"])
- if err != nil {
- return nil
- }
- status, err := events.StringToStatus(e.Action)
- if err != nil {
- return nil
- }
- t, err := events.StringToType(e.Type)
- if err != nil {
- return nil
- }
- lp := events.Event{
- ContainerExitCode: exitCode,
- ID: e.Actor.ID,
- Image: e.Actor.Attributes["image"],
- Name: e.Actor.Attributes["name"],
- Status: status,
- Time: time.Unix(e.Time, e.TimeNano),
- Type: t,
- }
- return &lp
-}
-
-func EventToApiEvent(e *events.Event) *Event {
- return &Event{dockerEvents.Message{
- Type: e.Type.String(),
- Action: e.Status.String(),
- Actor: dockerEvents.Actor{
- ID: e.ID,
- Attributes: map[string]string{
- "image": e.Image,
- "name": e.Name,
- "containerExitCode": strconv.Itoa(e.ContainerExitCode),
- },
- },
- Scope: "local",
- Time: e.Time.Unix(),
- TimeNano: e.Time.UnixNano(),
- }}
-}
-
func ImageToImageSummary(l *libpodImage.Image) (*entities.ImageSummary, error) {
containers, err := l.Containers()
if err != nil {
diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go
index 6cc6f0cfa..f59dca6f5 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -822,6 +822,38 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/libpod/images/import"), s.APIHandler(libpod.ImagesImport)).Methods(http.MethodPost)
+ // swagger:operation GET /libpod/images/remove libpod libpodImagesRemove
+ // ---
+ // tags:
+ // - images
+ // summary: Remove one or more images from the storage.
+ // description: Remove one or more images from the storage.
+ // parameters:
+ // - in: query
+ // name: images
+ // description: Images IDs or names to remove.
+ // type: array
+ // items:
+ // type: string
+ // - in: query
+ // name: all
+ // description: Remove all images.
+ // type: boolean
+ // default: true
+ // - in: query
+ // name: force
+ // description: Force image removal (including containers using the images).
+ // type: boolean
+ // produces:
+ // - application/json
+ // responses:
+ // 200:
+ // $ref: "#/responses/DocsLibpodImagesRemoveResponse"
+ // 400:
+ // $ref: "#/responses/BadParamError"
+ // 500:
+ // $ref: '#/responses/InternalError'
+ r.Handle(VersionedPath("/libpod/images/remove"), s.APIHandler(libpod.ImagesRemove)).Methods(http.MethodGet)
// swagger:operation POST /libpod/images/pull libpod libpodImagesPull
// ---
// tags:
diff --git a/pkg/api/types/types.go b/pkg/api/types/types.go
new file mode 100644
index 000000000..1b91364e3
--- /dev/null
+++ b/pkg/api/types/types.go
@@ -0,0 +1,9 @@
+package types
+
+const (
+ // DefaultAPIVersion is the version of the API the server defaults to.
+ DefaultAPIVersion = "1.40" // See https://docs.docker.com/engine/api/v1.40/
+
+ // DefaultAPIVersion is the minimal required version of the API.
+ MinimalAPIVersion = "1.24"
+)
diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go
index 29b6f04ec..da3755fc8 100644
--- a/pkg/bindings/connection.go
+++ b/pkg/bindings/connection.go
@@ -15,7 +15,7 @@ import (
"strings"
"time"
- "github.com/containers/libpod/pkg/api/handlers"
+ "github.com/containers/libpod/pkg/api/types"
jsoniter "github.com/json-iterator/go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -27,7 +27,7 @@ var (
basePath = &url.URL{
Scheme: "http",
Host: "d",
- Path: "/v" + handlers.MinimalApiVersion + "/libpod",
+ Path: "/v" + types.MinimalAPIVersion + "/libpod",
}
)
diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go
index e0f523ebd..06f01c7a0 100644
--- a/pkg/bindings/images/images.go
+++ b/pkg/bindings/images/images.go
@@ -109,23 +109,34 @@ func Load(ctx context.Context, r io.Reader, name *string) (*entities.ImageLoadRe
return &report, response.Process(&report)
}
-// Remove deletes an image from local storage. The optional force parameter will forcibly remove
-// the image by removing all all containers, including those that are Running, first.
-func Remove(ctx context.Context, nameOrID string, force *bool) ([]map[string]string, error) {
- var deletes []map[string]string
+// Remove deletes an image from local storage. The optional force parameter
+// will forcibly remove the image by removing all all containers, including
+// those that are Running, first.
+func Remove(ctx context.Context, images []string, opts entities.ImageRemoveOptions) (*entities.ImageRemoveReport, error) {
+ var report handlers.LibpodImagesRemoveReport
conn, err := bindings.GetClient(ctx)
if err != nil {
return nil, err
}
params := url.Values{}
- if force != nil {
- params.Set("force", strconv.FormatBool(*force))
+ params.Set("all", strconv.FormatBool(opts.All))
+ params.Set("force", strconv.FormatBool(opts.Force))
+ for _, i := range images {
+ params.Add("images", i)
}
- response, err := conn.DoRequest(nil, http.MethodDelete, "/images/%s", params, nameOrID)
+
+ response, err := conn.DoRequest(nil, http.MethodGet, "/images/remove", params)
if err != nil {
return nil, err
}
- return deletes, response.Process(&deletes)
+ if err := response.Process(&report); err != nil {
+ return nil, err
+ }
+ var rmError error
+ if report.Error != "" {
+ rmError = errors.New(report.Error)
+ }
+ return &report.ImageRemoveReport, rmError
}
// Export saves an image from local storage as a tarball or image archive. The optional format
diff --git a/pkg/bindings/system/system.go b/pkg/bindings/system/system.go
index fce8bbb8e..e2f264139 100644
--- a/pkg/bindings/system/system.go
+++ b/pkg/bindings/system/system.go
@@ -7,8 +7,8 @@ import (
"net/http"
"net/url"
- "github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/bindings"
+ "github.com/containers/libpod/pkg/domain/entities"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -16,7 +16,7 @@ import (
// Events allows you to monitor libdpod related events like container creation and
// removal. The events are then passed to the eventChan provided. The optional cancelChan
// can be used to cancel the read of events and close down the HTTP connection.
-func Events(ctx context.Context, eventChan chan (handlers.Event), cancelChan chan bool, since, until *string, filters map[string][]string) error {
+func Events(ctx context.Context, eventChan chan (entities.Event), cancelChan chan bool, since, until *string, filters map[string][]string) error {
conn, err := bindings.GetClient(ctx)
if err != nil {
return err
@@ -48,7 +48,7 @@ func Events(ctx context.Context, eventChan chan (handlers.Event), cancelChan cha
}
dec := json.NewDecoder(response.Body)
for {
- e := handlers.Event{}
+ e := entities.Event{}
if err := dec.Decode(&e); err != nil {
if err == io.EOF {
break
diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go
index b730f8743..506d1c317 100644
--- a/pkg/domain/entities/engine_container.go
+++ b/pkg/domain/entities/engine_container.go
@@ -54,7 +54,6 @@ type ContainerEngine interface {
PodStop(ctx context.Context, namesOrIds []string, options PodStopOptions) ([]*PodStopReport, error)
PodTop(ctx context.Context, options PodTopOptions) (*StringSliceReport, error)
PodUnpause(ctx context.Context, namesOrIds []string, options PodunpauseOptions) ([]*PodUnpauseReport, error)
- RestService(ctx context.Context, opts ServiceOptions) error
SetupRootless(ctx context.Context, cmd *cobra.Command) error
VarlinkService(ctx context.Context, opts ServiceOptions) error
VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IdOrNameResponse, error)
diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go
index b6283b6ad..84680ab1b 100644
--- a/pkg/domain/entities/engine_image.go
+++ b/pkg/domain/entities/engine_image.go
@@ -9,7 +9,6 @@ import (
type ImageEngine interface {
Build(ctx context.Context, containerFiles []string, opts BuildOptions) (*BuildReport, error)
Config(ctx context.Context) (*config.Config, error)
- Delete(ctx context.Context, nameOrId []string, opts ImageDeleteOptions) (*ImageDeleteReport, error)
Diff(ctx context.Context, nameOrId string, options DiffOptions) (*DiffReport, error)
Exists(ctx context.Context, nameOrId string) (*BoolReport, error)
History(ctx context.Context, nameOrId string, opts ImageHistoryOptions) (*ImageHistoryReport, error)
@@ -20,6 +19,7 @@ type ImageEngine interface {
Prune(ctx context.Context, opts ImagePruneOptions) (*ImagePruneReport, error)
Pull(ctx context.Context, rawImage string, opts ImagePullOptions) (*ImagePullReport, error)
Push(ctx context.Context, source string, destination string, opts ImagePushOptions) error
+ Remove(ctx context.Context, images []string, opts ImageRemoveOptions) (*ImageRemoveReport, error)
Save(ctx context.Context, nameOrId string, tags []string, options ImageSaveOptions) error
Search(ctx context.Context, term string, opts ImageSearchOptions) ([]ImageSearchReport, error)
Tag(ctx context.Context, nameOrId string, tags []string, options ImageTagOptions) error
diff --git a/pkg/domain/entities/events.go b/pkg/domain/entities/events.go
new file mode 100644
index 000000000..8861be158
--- /dev/null
+++ b/pkg/domain/entities/events.go
@@ -0,0 +1,61 @@
+package entities
+
+import (
+ "strconv"
+ "time"
+
+ libpodEvents "github.com/containers/libpod/libpod/events"
+ dockerEvents "github.com/docker/docker/api/types/events"
+)
+
+// Event combines various event-related data such as time, event type, status
+// and more.
+type Event struct {
+ // TODO: it would be nice to have full control over the types at some
+ // point and fork such Docker types.
+ dockerEvents.Message
+}
+
+// ConvertToLibpodEvent converts an entities event to a libpod one.
+func ConvertToLibpodEvent(e Event) *libpodEvents.Event {
+ exitCode, err := strconv.Atoi(e.Actor.Attributes["containerExitCode"])
+ if err != nil {
+ return nil
+ }
+ status, err := libpodEvents.StringToStatus(e.Action)
+ if err != nil {
+ return nil
+ }
+ t, err := libpodEvents.StringToType(e.Type)
+ if err != nil {
+ return nil
+ }
+ return &libpodEvents.Event{
+ ContainerExitCode: exitCode,
+ ID: e.Actor.ID,
+ Image: e.Actor.Attributes["image"],
+ Name: e.Actor.Attributes["name"],
+ Status: status,
+ Time: time.Unix(e.Time, e.TimeNano),
+ Type: t,
+ }
+}
+
+// ConvertToEntitiesEvent converts a libpod event to an entities one.
+func ConvertToEntitiesEvent(e libpodEvents.Event) *Event {
+ return &Event{dockerEvents.Message{
+ Type: e.Type.String(),
+ Action: e.Status.String(),
+ Actor: dockerEvents.Actor{
+ ID: e.ID,
+ Attributes: map[string]string{
+ "image": e.Image,
+ "name": e.Name,
+ "containerExitCode": strconv.Itoa(e.ContainerExitCode),
+ },
+ },
+ Scope: "local",
+ Time: e.Time.Unix(),
+ TimeNano: e.Time.UnixNano(),
+ }}
+}
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
index 56c4c0ac5..773cd90b4 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -82,19 +82,24 @@ func (i *ImageSummary) IsDangling() bool {
return i.Dangling
}
-type ImageDeleteOptions struct {
- All bool
+// ImageRemoveOptions can be used to alter image removal.
+type ImageRemoveOptions struct {
+ // All will remove all images.
+ All bool
+ // Foce will force image removal including containers using the images.
Force bool
}
-// ImageDeleteResponse is the response for removing one or more image(s) from storage
-// and containers what was untagged vs actually removed
-type ImageDeleteReport struct {
- Untagged []string `json:",omitempty"`
- Deleted []string `json:",omitempty"`
- Errors []error
- ImageNotFound error
- ImageInUse error
+// ImageRemoveResponse is the response for removing one or more image(s) from storage
+// and containers what was untagged vs actually removed.
+type ImageRemoveReport struct {
+ // Deleted images.
+ Deleted []string `json:",omitempty"`
+ // Untagged images. Can be longer than Deleted.
+ Untagged []string `json:",omitempty"`
+ // ExitCode describes the exit codes as described in the `podman rmi`
+ // man page.
+ ExitCode int
}
type ImageHistoryOptions struct{}
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index 50003dbe2..e71ceb536 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -1,5 +1,3 @@
-// +build ABISupport
-
package abi
import (
diff --git a/pkg/domain/infra/abi/events.go b/pkg/domain/infra/abi/events.go
index 9540a5b96..20773cdce 100644
--- a/pkg/domain/infra/abi/events.go
+++ b/pkg/domain/infra/abi/events.go
@@ -1,5 +1,3 @@
-//+build ABISupport
-
package abi
import (
diff --git a/pkg/domain/infra/abi/healthcheck.go b/pkg/domain/infra/abi/healthcheck.go
index 699483243..351bf4f7e 100644
--- a/pkg/domain/infra/abi/healthcheck.go
+++ b/pkg/domain/infra/abi/healthcheck.go
@@ -1,5 +1,3 @@
-// +build ABISupport
-
package abi
import (
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index 4353e0798..32f7d75e5 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -1,5 +1,3 @@
-// +build ABISupport
-
package abi
import (
@@ -23,6 +21,7 @@ import (
domainUtils "github.com/containers/libpod/pkg/domain/utils"
"github.com/containers/libpod/pkg/util"
"github.com/containers/storage"
+ "github.com/hashicorp/go-multierror"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -36,76 +35,6 @@ func (ir *ImageEngine) Exists(_ context.Context, nameOrId string) (*entities.Boo
return &entities.BoolReport{Value: err == nil}, nil
}
-func (ir *ImageEngine) Delete(ctx context.Context, nameOrId []string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) {
- report := entities.ImageDeleteReport{}
-
- if opts.All {
- var previousTargets []*libpodImage.Image
- repeatRun:
- targets, err := ir.Libpod.ImageRuntime().GetRWImages()
- if err != nil {
- return &report, errors.Wrapf(err, "unable to query local images")
- }
- if len(targets) == 0 {
- return &report, nil
- }
- if len(targets) > 0 && len(targets) == len(previousTargets) {
- return &report, errors.New("unable to delete all images; re-run the rmi command again.")
- }
- previousTargets = targets
-
- for _, img := range targets {
- isParent, err := img.IsParent(ctx)
- if err != nil {
- return &report, err
- }
- if isParent {
- continue
- }
- err = ir.deleteImage(ctx, img, opts, report)
- report.Errors = append(report.Errors, err)
- }
- if len(previousTargets) != 1 {
- goto repeatRun
- }
- return &report, nil
- }
-
- for _, id := range nameOrId {
- image, err := ir.Libpod.ImageRuntime().NewFromLocal(id)
- if err != nil {
- return nil, err
- }
-
- err = ir.deleteImage(ctx, image, opts, report)
- if err != nil {
- return &report, err
- }
- }
- return &report, nil
-}
-
-func (ir *ImageEngine) deleteImage(ctx context.Context, img *libpodImage.Image, opts entities.ImageDeleteOptions, report entities.ImageDeleteReport) error {
- results, err := ir.Libpod.RemoveImage(ctx, img, opts.Force)
- switch errors.Cause(err) {
- case nil:
- break
- case storage.ErrImageUsedByContainer:
- report.ImageInUse = errors.New(
- fmt.Sprintf("A container associated with containers/storage, i.e. via Buildah, CRI-O, etc., may be associated with this image: %-12.12s\n", img.ID()))
- return nil
- case libpodImage.ErrNoSuchImage:
- report.ImageNotFound = err
- return nil
- default:
- return err
- }
-
- report.Deleted = append(report.Deleted, results.Deleted)
- report.Untagged = append(report.Untagged, results.Untagged...)
- return nil
-}
-
func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) {
results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, opts.Filter)
if err != nil {
@@ -488,3 +417,135 @@ func (ir *ImageEngine) Tree(ctx context.Context, nameOrId string, opts entities.
}
return &entities.ImageTreeReport{Tree: results}, nil
}
+
+// Remove removes one or more images from local storage.
+func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entities.ImageRemoveOptions) (report *entities.ImageRemoveReport, finalError error) {
+ var (
+ // noSuchImageErrors indicates that at least one image was not found.
+ noSuchImageErrors bool
+ // inUseErrors indicates that at least one image is being used by a
+ // container.
+ inUseErrors bool
+ // otherErrors indicates that at least one error other than the two
+ // above occured.
+ otherErrors bool
+ // deleteError is a multierror to conveniently collect errors during
+ // removal. We really want to delete as many images as possible and not
+ // error out immediately.
+ deleteError *multierror.Error
+ )
+
+ report = &entities.ImageRemoveReport{}
+
+ // Set the removalCode and the error after all work is done.
+ defer func() {
+ switch {
+ // 2
+ case inUseErrors:
+ // One of the specified images has child images or is
+ // being used by a container.
+ report.ExitCode = 2
+ // 1
+ case noSuchImageErrors && !(otherErrors || inUseErrors):
+ // One of the specified images did not exist, and no other
+ // failures.
+ report.ExitCode = 1
+ // 0
+ default:
+ // Nothing to do.
+ }
+ if deleteError != nil {
+ // go-multierror has a trailing new line which we need to remove to normalize the string.
+ finalError = deleteError.ErrorOrNil()
+ finalError = errors.New(strings.TrimSpace(finalError.Error()))
+ }
+ }()
+
+ // deleteImage is an anonymous function to conveniently delete an image
+ // withouth having to pass all local data around.
+ deleteImage := func(img *image.Image) error {
+ results, err := ir.Libpod.RemoveImage(ctx, img, opts.Force)
+ switch errors.Cause(err) {
+ case nil:
+ break
+ case storage.ErrImageUsedByContainer:
+ inUseErrors = true // Important for exit codes in Podman.
+ return errors.New(
+ fmt.Sprintf("A container associated with containers/storage, i.e. via Buildah, CRI-O, etc., may be associated with this image: %-12.12s\n", img.ID()))
+ default:
+ otherErrors = true // Important for exit codes in Podman.
+ return err
+ }
+
+ report.Deleted = append(report.Deleted, results.Deleted)
+ report.Untagged = append(report.Untagged, results.Untagged...)
+ return nil
+ }
+
+ // Delete all images from the local storage.
+ if opts.All {
+ previousImages := 0
+ // Remove all images one-by-one.
+ for {
+ storageImages, err := ir.Libpod.ImageRuntime().GetRWImages()
+ if err != nil {
+ deleteError = multierror.Append(deleteError,
+ errors.Wrapf(err, "unable to query local images"))
+ otherErrors = true // Important for exit codes in Podman.
+ return
+ }
+ // No images (left) to remove, so we're done.
+ if len(storageImages) == 0 {
+ return
+ }
+ // Prevent infinity loops by making a delete-progress check.
+ if previousImages == len(storageImages) {
+ otherErrors = true // Important for exit codes in Podman.
+ deleteError = multierror.Append(deleteError,
+ errors.New("unable to delete all images, check errors and re-run image removal if needed"))
+ break
+ }
+ previousImages = len(storageImages)
+ // Delete all "leaves" (i.e., images without child images).
+ for _, img := range storageImages {
+ isParent, err := img.IsParent(ctx)
+ if err != nil {
+ otherErrors = true // Important for exit codes in Podman.
+ deleteError = multierror.Append(deleteError, err)
+ }
+ // Skip parent images.
+ if isParent {
+ continue
+ }
+ if err := deleteImage(img); err != nil {
+ deleteError = multierror.Append(deleteError, err)
+ }
+ }
+ }
+
+ return
+ }
+
+ // Delete only the specified images.
+ for _, id := range images {
+ img, err := ir.Libpod.ImageRuntime().NewFromLocal(id)
+ switch errors.Cause(err) {
+ case nil:
+ break
+ case image.ErrNoSuchImage:
+ noSuchImageErrors = true // Important for exit codes in Podman.
+ fallthrough
+ default:
+ deleteError = multierror.Append(deleteError, err)
+ continue
+ }
+
+ err = deleteImage(img)
+ if err != nil {
+ otherErrors = true // Important for exit codes in Podman.
+ deleteError = multierror.Append(deleteError, err)
+ }
+ }
+
+ return
+}
diff --git a/pkg/domain/infra/abi/images_list.go b/pkg/domain/infra/abi/images_list.go
index 68b961cb6..9add915ea 100644
--- a/pkg/domain/infra/abi/images_list.go
+++ b/pkg/domain/infra/abi/images_list.go
@@ -1,5 +1,3 @@
-// +build ABISupport
-
package abi
import (
diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go
index 6b6e13e24..c4ae9efbf 100644
--- a/pkg/domain/infra/abi/pods.go
+++ b/pkg/domain/infra/abi/pods.go
@@ -1,5 +1,3 @@
-// +build ABISupport
-
package abi
import (
diff --git a/pkg/domain/infra/abi/runtime.go b/pkg/domain/infra/abi/runtime.go
index b53fb6d3a..7394cadfc 100644
--- a/pkg/domain/infra/abi/runtime.go
+++ b/pkg/domain/infra/abi/runtime.go
@@ -1,5 +1,3 @@
-// +build ABISupport
-
package abi
import (
diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go
index 078f5404d..e5c109ee6 100644
--- a/pkg/domain/infra/abi/system.go
+++ b/pkg/domain/infra/abi/system.go
@@ -1,20 +1,15 @@
-// +build ABISupport
-
package abi
import (
"context"
"fmt"
"io/ioutil"
- "net"
"os"
"strconv"
- "strings"
"syscall"
"github.com/containers/common/pkg/config"
"github.com/containers/libpod/libpod/define"
- api "github.com/containers/libpod/pkg/api/server"
"github.com/containers/libpod/pkg/cgroups"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/rootless"
@@ -33,42 +28,6 @@ func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) {
return ic.Libpod.Info()
}
-func (ic *ContainerEngine) RestService(_ context.Context, opts entities.ServiceOptions) error {
- var (
- listener *net.Listener
- err error
- )
-
- if opts.URI != "" {
- fields := strings.Split(opts.URI, ":")
- if len(fields) == 1 {
- return errors.Errorf("%s is an invalid socket destination", opts.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", opts.URI)
- }
- listener = &l
- }
-
- server, err := api.NewServerWithSettings(ic.Libpod, opts.Timeout, listener)
- if err != nil {
- return err
- }
- defer func() {
- if err := server.Shutdown(); err != nil {
- logrus.Warnf("Error when stopping API service: %s", err)
- }
- }()
-
- err = server.Serve()
- if listener != nil {
- _ = (*listener).Close()
- }
- return err
-}
-
func (ic *ContainerEngine) VarlinkService(_ context.Context, opts entities.ServiceOptions) error {
var varlinkInterfaces = []*iopodman.VarlinkInterface{
iopodmanAPI.New(opts.Command, ic.Libpod),
diff --git a/pkg/domain/infra/abi/terminal/sigproxy_linux.go b/pkg/domain/infra/abi/terminal/sigproxy_linux.go
index d7f5853d8..b422e549e 100644
--- a/pkg/domain/infra/abi/terminal/sigproxy_linux.go
+++ b/pkg/domain/infra/abi/terminal/sigproxy_linux.go
@@ -1,5 +1,3 @@
-// +build ABISupport
-
package terminal
import (
diff --git a/pkg/domain/infra/abi/terminal/terminal.go b/pkg/domain/infra/abi/terminal/terminal.go
index f187bdd6b..0fc3af511 100644
--- a/pkg/domain/infra/abi/terminal/terminal.go
+++ b/pkg/domain/infra/abi/terminal/terminal.go
@@ -1,5 +1,3 @@
-// +build ABISupport
-
package terminal
import (
diff --git a/pkg/domain/infra/abi/terminal/terminal_linux.go b/pkg/domain/infra/abi/terminal/terminal_linux.go
index 664205df1..15701342f 100644
--- a/pkg/domain/infra/abi/terminal/terminal_linux.go
+++ b/pkg/domain/infra/abi/terminal/terminal_linux.go
@@ -1,5 +1,3 @@
-// +build ABISupport
-
package terminal
import (
diff --git a/pkg/domain/infra/tunnel/events.go b/pkg/domain/infra/tunnel/events.go
index 46d88341a..93da3aeb4 100644
--- a/pkg/domain/infra/tunnel/events.go
+++ b/pkg/domain/infra/tunnel/events.go
@@ -4,7 +4,6 @@ import (
"context"
"strings"
- "github.com/containers/libpod/pkg/api/handlers"
"github.com/containers/libpod/pkg/bindings/system"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/pkg/errors"
@@ -21,10 +20,10 @@ func (ic *ContainerEngine) Events(ctx context.Context, opts entities.EventsOptio
filters[split[0]] = append(filters[split[0]], strings.Join(split[1:], "="))
}
}
- binChan := make(chan handlers.Event)
+ binChan := make(chan entities.Event)
go func() {
for e := range binChan {
- opts.EventChan <- e.ToLibpodEvent()
+ opts.EventChan <- entities.ConvertToLibpodEvent(e)
}
}()
return system.Events(ic.ClientCxt, binChan, nil, &opts.Since, &opts.Until, filters)
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index 2decd605d..822842936 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -19,25 +19,8 @@ func (ir *ImageEngine) Exists(_ context.Context, nameOrId string) (*entities.Boo
return &entities.BoolReport{Value: found}, err
}
-func (ir *ImageEngine) Delete(ctx context.Context, nameOrId []string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) {
- report := entities.ImageDeleteReport{}
-
- for _, id := range nameOrId {
- results, err := images.Remove(ir.ClientCxt, id, &opts.Force)
- if err != nil {
- return nil, err
- }
- for _, e := range results {
- if a, ok := e["Deleted"]; ok {
- report.Deleted = append(report.Deleted, a)
- }
-
- if a, ok := e["Untagged"]; ok {
- report.Untagged = append(report.Untagged, a)
- }
- }
- }
- return &report, nil
+func (ir *ImageEngine) Remove(ctx context.Context, imagesArg []string, opts entities.ImageRemoveOptions) (*entities.ImageRemoveReport, error) {
+ return images.Remove(ir.ClientCxt, imagesArg, opts)
}
func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) ([]*entities.ImageSummary, error) {
diff --git a/pkg/domain/infra/tunnel/system.go b/pkg/domain/infra/tunnel/system.go
index f373525c5..97bf885e7 100644
--- a/pkg/domain/infra/tunnel/system.go
+++ b/pkg/domain/infra/tunnel/system.go
@@ -14,10 +14,6 @@ func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) {
return system.Info(ic.ClientCxt)
}
-func (ic *ContainerEngine) RestService(_ context.Context, _ entities.ServiceOptions) error {
- panic(errors.New("rest service is not supported when tunneling"))
-}
-
func (ic *ContainerEngine) VarlinkService(_ context.Context, _ entities.ServiceOptions) error {
panic(errors.New("varlink service is not supported when tunneling"))
}