Merge pull request #5863 from vrothberg/v2-fix-rmi

podman rmi: refactor logic
This commit is contained in:
OpenShift Merge Robot
2020-04-21 05:07:30 -04:00
committed by GitHub
34 changed files with 423 additions and 259 deletions

View File

@ -28,6 +28,7 @@ linters:
- misspell
- prealloc
- unparam
- nakedret
linters-settings:
errcheck:
check-blank: false

View File

@ -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"))
}

View File

@ -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
}

View File

@ -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) {

View File

@ -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
}

View File

@ -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")
}

View File

@ -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

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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:

9
pkg/api/types/types.go Normal file
View File

@ -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"
)

View File

@ -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",
}
)

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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(),
}}
}

View File

@ -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{}

View File

@ -1,5 +1,3 @@
// +build ABISupport
package abi
import (

View File

@ -1,5 +1,3 @@
//+build ABISupport
package abi
import (

View File

@ -1,5 +1,3 @@
// +build ABISupport
package abi
import (

View File

@ -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
}

View File

@ -1,5 +1,3 @@
// +build ABISupport
package abi
import (

View File

@ -1,5 +1,3 @@
// +build ABISupport
package abi
import (

View File

@ -1,5 +1,3 @@
// +build ABISupport
package abi
import (

View File

@ -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),

View File

@ -1,5 +1,3 @@
// +build ABISupport
package terminal
import (

View File

@ -1,5 +1,3 @@
// +build ABISupport
package terminal
import (

View File

@ -1,5 +1,3 @@
// +build ABISupport
package terminal
import (

View File

@ -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)

View File

@ -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) {

View File

@ -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"))
}