mirror of
https://github.com/containers/podman.git
synced 2025-06-21 17:38:12 +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
|
- misspell
|
||||||
- prealloc
|
- prealloc
|
||||||
- unparam
|
- unparam
|
||||||
|
- nakedret
|
||||||
linters-settings:
|
linters-settings:
|
||||||
errcheck:
|
errcheck:
|
||||||
check-blank: false
|
check-blank: false
|
||||||
|
@ -122,7 +122,7 @@ func run(cmd *cobra.Command, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if runRmi {
|
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 {
|
if err != nil {
|
||||||
logrus.Errorf("%s", errors.Wrapf(err, "failed removing image"))
|
logrus.Errorf("%s", errors.Wrapf(err, "failed removing image"))
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package images
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/containers/libpod/cmd/podman/registry"
|
"github.com/containers/libpod/cmd/podman/registry"
|
||||||
"github.com/containers/libpod/pkg/domain/entities"
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
@ -23,7 +22,7 @@ var (
|
|||||||
podman image rm c4dfb1609ee2 93fd78260bd1 c0ed59d05ff7`,
|
podman image rm c4dfb1609ee2 93fd78260bd1 c0ed59d05ff7`,
|
||||||
}
|
}
|
||||||
|
|
||||||
imageOpts = entities.ImageDeleteOptions{}
|
imageOpts = entities.ImageRemoveOptions{}
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -40,32 +39,25 @@ func imageRemoveFlagSet(flags *pflag.FlagSet) {
|
|||||||
flags.BoolVarP(&imageOpts.All, "all", "a", false, "Remove all images")
|
flags.BoolVarP(&imageOpts.All, "all", "a", false, "Remove all images")
|
||||||
flags.BoolVarP(&imageOpts.Force, "force", "f", false, "Force Removal of the image")
|
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 {
|
if len(args) < 1 && !imageOpts.All {
|
||||||
return errors.Errorf("image name or ID must be specified")
|
return errors.Errorf("image name or ID must be specified")
|
||||||
}
|
}
|
||||||
if len(args) > 0 && imageOpts.All {
|
if len(args) > 0 && imageOpts.All {
|
||||||
return errors.Errorf("when using the --all switch, you may not pass any images names or IDs")
|
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 {
|
report, err := registry.ImageEngine().Remove(registry.GetContext(), args, imageOpts)
|
||||||
switch {
|
if report != nil {
|
||||||
case report != nil && report.ImageNotFound != nil:
|
for _, u := range report.Untagged {
|
||||||
fmt.Fprintln(os.Stderr, err.Error())
|
fmt.Println("Untagged: " + u)
|
||||||
registry.SetExitCode(2)
|
|
||||||
case report != nil && report.ImageInUse != nil:
|
|
||||||
fmt.Fprintln(os.Stderr, err.Error())
|
|
||||||
default:
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
for _, d := range report.Deleted {
|
||||||
|
fmt.Println("Deleted: " + d)
|
||||||
|
}
|
||||||
|
registry.SetExitCode(report.ExitCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, u := range report.Untagged {
|
return err
|
||||||
fmt.Println("Untagged: " + u)
|
|
||||||
}
|
|
||||||
for _, d := range report.Deleted {
|
|
||||||
fmt.Println("Deleted: " + d)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,8 @@ func service(cmd *cobra.Command, args []string) error {
|
|||||||
|
|
||||||
logrus.Warn("This function is EXPERIMENTAL")
|
logrus.Warn("This function is EXPERIMENTAL")
|
||||||
fmt.Fprintf(os.Stderr, "This function is EXPERIMENTAL.\n")
|
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) {
|
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
|
# Need to run linter twice to cover all the build tags code paths
|
||||||
|
|
||||||
declare -A BUILD_TAGS
|
declare -A BUILD_TAGS
|
||||||
|
# TODO: add systemd tag
|
||||||
BUILD_TAGS[default]="apparmor,seccomp,selinux"
|
BUILD_TAGS[default]="apparmor,seccomp,selinux"
|
||||||
BUILD_TAGS[abi]="${BUILD_TAGS[default]},ABISupport,varlink,!remoteclient"
|
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
|
[[ $1 == run ]] && shift
|
||||||
|
|
||||||
for i in tunnel abi; do
|
for i in tunnel abi; do
|
||||||
echo Build Tags: ${BUILD_TAGS[$i]}
|
echo ""
|
||||||
golangci-lint run --build-tags=${BUILD_TAGS[$i]} "$@"
|
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
|
done
|
||||||
|
@ -6,8 +6,8 @@ import (
|
|||||||
|
|
||||||
"github.com/containers/libpod/libpod"
|
"github.com/containers/libpod/libpod"
|
||||||
"github.com/containers/libpod/libpod/events"
|
"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/api/handlers/utils"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
"github.com/gorilla/schema"
|
"github.com/gorilla/schema"
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -70,7 +70,7 @@ func GetEvents(w http.ResponseWriter, r *http.Request) {
|
|||||||
coder.SetEscapeHTML(true)
|
coder.SetEscapeHTML(true)
|
||||||
|
|
||||||
for event := range eventChannel {
|
for event := range eventChannel {
|
||||||
e := handlers.EventToApiEvent(event)
|
e := entities.ConvertToEntitiesEvent(*event)
|
||||||
if err := coder.Encode(e); err != nil {
|
if err := coder.Encode(e); err != nil {
|
||||||
logrus.Errorf("unable to write json: %q", err)
|
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"
|
||||||
"github.com/containers/libpod/pkg/api/handlers/utils"
|
"github.com/containers/libpod/pkg/api/handlers/utils"
|
||||||
"github.com/containers/libpod/pkg/domain/entities"
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
|
"github.com/containers/libpod/pkg/domain/infra/abi"
|
||||||
"github.com/containers/libpod/pkg/util"
|
"github.com/containers/libpod/pkg/util"
|
||||||
utils2 "github.com/containers/libpod/utils"
|
utils2 "github.com/containers/libpod/utils"
|
||||||
"github.com/gorilla/schema"
|
"github.com/gorilla/schema"
|
||||||
@ -698,3 +699,30 @@ func SearchImages(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
utils.WriteResponse(w, http.StatusOK, reports)
|
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
|
Body handlers.LibpodImagesPullReport
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove response
|
||||||
|
// swagger:response DocsLibpodImagesRemoveResponse
|
||||||
|
type swagLibpodImagesRemoveResponse struct {
|
||||||
|
// in:body
|
||||||
|
Body handlers.LibpodImagesRemoveReport
|
||||||
|
}
|
||||||
|
|
||||||
// Delete response
|
// Delete response
|
||||||
// swagger:response DocsImageDeleteResponse
|
// swagger:response DocsImageDeleteResponse
|
||||||
type swagImageDeleteResponse struct {
|
type swagImageDeleteResponse struct {
|
||||||
|
@ -4,16 +4,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containers/image/v5/manifest"
|
"github.com/containers/image/v5/manifest"
|
||||||
"github.com/containers/libpod/libpod/events"
|
|
||||||
libpodImage "github.com/containers/libpod/libpod/image"
|
libpodImage "github.com/containers/libpod/libpod/image"
|
||||||
"github.com/containers/libpod/pkg/domain/entities"
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
docker "github.com/docker/docker/api/types"
|
docker "github.com/docker/docker/api/types"
|
||||||
dockerContainer "github.com/docker/docker/api/types/container"
|
dockerContainer "github.com/docker/docker/api/types/container"
|
||||||
dockerEvents "github.com/docker/docker/api/types/events"
|
|
||||||
dockerNetwork "github.com/docker/docker/api/types/network"
|
dockerNetwork "github.com/docker/docker/api/types/network"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -39,6 +36,14 @@ type LibpodImagesPullReport struct {
|
|||||||
ID string `json:"id"`
|
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 {
|
type ContainersPruneReport struct {
|
||||||
docker.ContainersPruneReport
|
docker.ContainersPruneReport
|
||||||
}
|
}
|
||||||
@ -143,10 +148,6 @@ type PodCreateConfig struct {
|
|||||||
Share string `json:"share"`
|
Share string `json:"share"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Event struct {
|
|
||||||
dockerEvents.Message
|
|
||||||
}
|
|
||||||
|
|
||||||
type HistoryResponse struct {
|
type HistoryResponse struct {
|
||||||
ID string `json:"Id"`
|
ID string `json:"Id"`
|
||||||
Created int64 `json:"Created"`
|
Created int64 `json:"Created"`
|
||||||
@ -173,49 +174,6 @@ type ExecCreateResponse struct {
|
|||||||
docker.IDResponse
|
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) {
|
func ImageToImageSummary(l *libpodImage.Image) (*entities.ImageSummary, error) {
|
||||||
containers, err := l.Containers()
|
containers, err := l.Containers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -822,6 +822,38 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
|
|||||||
// 500:
|
// 500:
|
||||||
// $ref: '#/responses/InternalError'
|
// $ref: '#/responses/InternalError'
|
||||||
r.Handle(VersionedPath("/libpod/images/import"), s.APIHandler(libpod.ImagesImport)).Methods(http.MethodPost)
|
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
|
// swagger:operation POST /libpod/images/pull libpod libpodImagesPull
|
||||||
// ---
|
// ---
|
||||||
// tags:
|
// 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"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containers/libpod/pkg/api/handlers"
|
"github.com/containers/libpod/pkg/api/types"
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -27,7 +27,7 @@ var (
|
|||||||
basePath = &url.URL{
|
basePath = &url.URL{
|
||||||
Scheme: "http",
|
Scheme: "http",
|
||||||
Host: "d",
|
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)
|
return &report, response.Process(&report)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove deletes an image from local storage. The optional force parameter will forcibly remove
|
// Remove deletes an image from local storage. The optional force parameter
|
||||||
// the image by removing all all containers, including those that are Running, first.
|
// will forcibly remove the image by removing all all containers, including
|
||||||
func Remove(ctx context.Context, nameOrID string, force *bool) ([]map[string]string, error) {
|
// those that are Running, first.
|
||||||
var deletes []map[string]string
|
func Remove(ctx context.Context, images []string, opts entities.ImageRemoveOptions) (*entities.ImageRemoveReport, error) {
|
||||||
|
var report handlers.LibpodImagesRemoveReport
|
||||||
conn, err := bindings.GetClient(ctx)
|
conn, err := bindings.GetClient(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
params := url.Values{}
|
params := url.Values{}
|
||||||
if force != nil {
|
params.Set("all", strconv.FormatBool(opts.All))
|
||||||
params.Set("force", strconv.FormatBool(*force))
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// Export saves an image from local storage as a tarball or image archive. The optional format
|
||||||
|
@ -7,8 +7,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/containers/libpod/pkg/api/handlers"
|
|
||||||
"github.com/containers/libpod/pkg/bindings"
|
"github.com/containers/libpod/pkg/bindings"
|
||||||
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@ -16,7 +16,7 @@ import (
|
|||||||
// Events allows you to monitor libdpod related events like container creation and
|
// 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
|
// 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.
|
// 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)
|
conn, err := bindings.GetClient(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -48,7 +48,7 @@ func Events(ctx context.Context, eventChan chan (handlers.Event), cancelChan cha
|
|||||||
}
|
}
|
||||||
dec := json.NewDecoder(response.Body)
|
dec := json.NewDecoder(response.Body)
|
||||||
for {
|
for {
|
||||||
e := handlers.Event{}
|
e := entities.Event{}
|
||||||
if err := dec.Decode(&e); err != nil {
|
if err := dec.Decode(&e); err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
break
|
break
|
||||||
|
@ -54,7 +54,6 @@ type ContainerEngine interface {
|
|||||||
PodStop(ctx context.Context, namesOrIds []string, options PodStopOptions) ([]*PodStopReport, error)
|
PodStop(ctx context.Context, namesOrIds []string, options PodStopOptions) ([]*PodStopReport, error)
|
||||||
PodTop(ctx context.Context, options PodTopOptions) (*StringSliceReport, error)
|
PodTop(ctx context.Context, options PodTopOptions) (*StringSliceReport, error)
|
||||||
PodUnpause(ctx context.Context, namesOrIds []string, options PodunpauseOptions) ([]*PodUnpauseReport, 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
|
SetupRootless(ctx context.Context, cmd *cobra.Command) error
|
||||||
VarlinkService(ctx context.Context, opts ServiceOptions) error
|
VarlinkService(ctx context.Context, opts ServiceOptions) error
|
||||||
VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IdOrNameResponse, error)
|
VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IdOrNameResponse, error)
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
type ImageEngine interface {
|
type ImageEngine interface {
|
||||||
Build(ctx context.Context, containerFiles []string, opts BuildOptions) (*BuildReport, error)
|
Build(ctx context.Context, containerFiles []string, opts BuildOptions) (*BuildReport, error)
|
||||||
Config(ctx context.Context) (*config.Config, 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)
|
Diff(ctx context.Context, nameOrId string, options DiffOptions) (*DiffReport, error)
|
||||||
Exists(ctx context.Context, nameOrId string) (*BoolReport, error)
|
Exists(ctx context.Context, nameOrId string) (*BoolReport, error)
|
||||||
History(ctx context.Context, nameOrId string, opts ImageHistoryOptions) (*ImageHistoryReport, 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)
|
Prune(ctx context.Context, opts ImagePruneOptions) (*ImagePruneReport, error)
|
||||||
Pull(ctx context.Context, rawImage string, opts ImagePullOptions) (*ImagePullReport, error)
|
Pull(ctx context.Context, rawImage string, opts ImagePullOptions) (*ImagePullReport, error)
|
||||||
Push(ctx context.Context, source string, destination string, opts ImagePushOptions) 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
|
Save(ctx context.Context, nameOrId string, tags []string, options ImageSaveOptions) error
|
||||||
Search(ctx context.Context, term string, opts ImageSearchOptions) ([]ImageSearchReport, error)
|
Search(ctx context.Context, term string, opts ImageSearchOptions) ([]ImageSearchReport, error)
|
||||||
Tag(ctx context.Context, nameOrId string, tags []string, options ImageTagOptions) 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
|
return i.Dangling
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImageDeleteOptions struct {
|
// ImageRemoveOptions can be used to alter image removal.
|
||||||
All bool
|
type ImageRemoveOptions struct {
|
||||||
|
// All will remove all images.
|
||||||
|
All bool
|
||||||
|
// Foce will force image removal including containers using the images.
|
||||||
Force bool
|
Force bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImageDeleteResponse is the response for removing one or more image(s) from storage
|
// ImageRemoveResponse is the response for removing one or more image(s) from storage
|
||||||
// and containers what was untagged vs actually removed
|
// and containers what was untagged vs actually removed.
|
||||||
type ImageDeleteReport struct {
|
type ImageRemoveReport struct {
|
||||||
Untagged []string `json:",omitempty"`
|
// Deleted images.
|
||||||
Deleted []string `json:",omitempty"`
|
Deleted []string `json:",omitempty"`
|
||||||
Errors []error
|
// Untagged images. Can be longer than Deleted.
|
||||||
ImageNotFound error
|
Untagged []string `json:",omitempty"`
|
||||||
ImageInUse error
|
// ExitCode describes the exit codes as described in the `podman rmi`
|
||||||
|
// man page.
|
||||||
|
ExitCode int
|
||||||
}
|
}
|
||||||
|
|
||||||
type ImageHistoryOptions struct{}
|
type ImageHistoryOptions struct{}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// +build ABISupport
|
|
||||||
|
|
||||||
package abi
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
//+build ABISupport
|
|
||||||
|
|
||||||
package abi
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// +build ABISupport
|
|
||||||
|
|
||||||
package abi
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// +build ABISupport
|
|
||||||
|
|
||||||
package abi
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -23,6 +21,7 @@ import (
|
|||||||
domainUtils "github.com/containers/libpod/pkg/domain/utils"
|
domainUtils "github.com/containers/libpod/pkg/domain/utils"
|
||||||
"github.com/containers/libpod/pkg/util"
|
"github.com/containers/libpod/pkg/util"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"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
|
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) {
|
func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) {
|
||||||
results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, opts.Filter)
|
results, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, opts.Filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -488,3 +417,135 @@ func (ir *ImageEngine) Tree(ctx context.Context, nameOrId string, opts entities.
|
|||||||
}
|
}
|
||||||
return &entities.ImageTreeReport{Tree: results}, nil
|
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
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// +build ABISupport
|
|
||||||
|
|
||||||
package abi
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// +build ABISupport
|
|
||||||
|
|
||||||
package abi
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,20 +1,15 @@
|
|||||||
// +build ABISupport
|
|
||||||
|
|
||||||
package abi
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/containers/common/pkg/config"
|
"github.com/containers/common/pkg/config"
|
||||||
"github.com/containers/libpod/libpod/define"
|
"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/cgroups"
|
||||||
"github.com/containers/libpod/pkg/domain/entities"
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
"github.com/containers/libpod/pkg/rootless"
|
"github.com/containers/libpod/pkg/rootless"
|
||||||
@ -33,42 +28,6 @@ func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) {
|
|||||||
return ic.Libpod.Info()
|
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 {
|
func (ic *ContainerEngine) VarlinkService(_ context.Context, opts entities.ServiceOptions) error {
|
||||||
var varlinkInterfaces = []*iopodman.VarlinkInterface{
|
var varlinkInterfaces = []*iopodman.VarlinkInterface{
|
||||||
iopodmanAPI.New(opts.Command, ic.Libpod),
|
iopodmanAPI.New(opts.Command, ic.Libpod),
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// +build ABISupport
|
|
||||||
|
|
||||||
package terminal
|
package terminal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// +build ABISupport
|
|
||||||
|
|
||||||
package terminal
|
package terminal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// +build ABISupport
|
|
||||||
|
|
||||||
package terminal
|
package terminal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containers/libpod/pkg/api/handlers"
|
|
||||||
"github.com/containers/libpod/pkg/bindings/system"
|
"github.com/containers/libpod/pkg/bindings/system"
|
||||||
"github.com/containers/libpod/pkg/domain/entities"
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
"github.com/pkg/errors"
|
"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:], "="))
|
filters[split[0]] = append(filters[split[0]], strings.Join(split[1:], "="))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
binChan := make(chan handlers.Event)
|
binChan := make(chan entities.Event)
|
||||||
go func() {
|
go func() {
|
||||||
for e := range binChan {
|
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)
|
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
|
return &entities.BoolReport{Value: found}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ir *ImageEngine) Delete(ctx context.Context, nameOrId []string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) {
|
func (ir *ImageEngine) Remove(ctx context.Context, imagesArg []string, opts entities.ImageRemoveOptions) (*entities.ImageRemoveReport, error) {
|
||||||
report := entities.ImageDeleteReport{}
|
return images.Remove(ir.ClientCxt, imagesArg, opts)
|
||||||
|
|
||||||
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) List(ctx context.Context, opts entities.ImageListOptions) ([]*entities.ImageSummary, error) {
|
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)
|
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 {
|
func (ic *ContainerEngine) VarlinkService(_ context.Context, _ entities.ServiceOptions) error {
|
||||||
panic(errors.New("varlink service is not supported when tunneling"))
|
panic(errors.New("varlink service is not supported when tunneling"))
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user