mirror of
https://github.com/containers/podman.git
synced 2025-06-20 09:03:43 +08:00
Merge pull request #5863 from vrothberg/v2-fix-rmi
podman rmi: refactor logic
This commit is contained in:
@ -28,6 +28,7 @@ linters:
|
||||
- misspell
|
||||
- prealloc
|
||||
- unparam
|
||||
- nakedret
|
||||
linters-settings:
|
||||
errcheck:
|
||||
check-blank: false
|
||||
|
@ -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"))
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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) {
|
||||
|
57
cmd/podman/system/service_abi.go
Normal file
57
cmd/podman/system/service_abi.go
Normal 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
|
||||
}
|
14
cmd/podman/system/service_unsupported.go
Normal file
14
cmd/podman/system/service_unsupported.go
Normal 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")
|
||||
}
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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
9
pkg/api/types/types.go
Normal 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"
|
||||
)
|
@ -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",
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
61
pkg/domain/entities/events.go
Normal file
61
pkg/domain/entities/events.go
Normal 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(),
|
||||
}}
|
||||
}
|
@ -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{}
|
||||
|
@ -1,5 +1,3 @@
|
||||
// +build ABISupport
|
||||
|
||||
package abi
|
||||
|
||||
import (
|
||||
|
@ -1,5 +1,3 @@
|
||||
//+build ABISupport
|
||||
|
||||
package abi
|
||||
|
||||
import (
|
||||
|
@ -1,5 +1,3 @@
|
||||
// +build ABISupport
|
||||
|
||||
package abi
|
||||
|
||||
import (
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
// +build ABISupport
|
||||
|
||||
package abi
|
||||
|
||||
import (
|
||||
|
@ -1,5 +1,3 @@
|
||||
// +build ABISupport
|
||||
|
||||
package abi
|
||||
|
||||
import (
|
||||
|
@ -1,5 +1,3 @@
|
||||
// +build ABISupport
|
||||
|
||||
package abi
|
||||
|
||||
import (
|
||||
|
@ -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),
|
||||
|
@ -1,5 +1,3 @@
|
||||
// +build ABISupport
|
||||
|
||||
package terminal
|
||||
|
||||
import (
|
||||
|
@ -1,5 +1,3 @@
|
||||
// +build ABISupport
|
||||
|
||||
package terminal
|
||||
|
||||
import (
|
||||
|
@ -1,5 +1,3 @@
|
||||
// +build ABISupport
|
||||
|
||||
package terminal
|
||||
|
||||
import (
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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"))
|
||||
}
|
||||
|
Reference in New Issue
Block a user