diff options
Diffstat (limited to 'pkg')
167 files changed, 1808 insertions, 988 deletions
diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index 0f89c859e..6e1945db1 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -465,3 +465,34 @@ func formatCapabilities(slice []string) { slice[i] = strings.TrimPrefix(slice[i], "CAP_") } } + +func RenameContainer(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value("decoder").(*schema.Decoder) + + name := utils.GetName(r) + query := struct { + Name string `schema:"name"` + }{} + if err := decoder.Decode(&query, r.URL.Query()); err != nil { + utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + return + } + + ctr, err := runtime.LookupContainer(name) + if err != nil { + utils.ContainerNotFound(w, name, err) + return + } + + if _, err := runtime.RenameContainer(r.Context(), ctr, query.Name); err != nil { + if errors.Cause(err) == define.ErrPodExists || errors.Cause(err) == define.ErrCtrExists { + utils.Error(w, "Something went wrong.", http.StatusConflict, err) + return + } + utils.InternalServerError(w, err) + return + } + + utils.WriteResponse(w, http.StatusNoContent, nil) +} diff --git a/pkg/api/handlers/compat/containers_prune.go b/pkg/api/handlers/compat/containers_prune.go index a1e35dd97..7bba38475 100644 --- a/pkg/api/handlers/compat/containers_prune.go +++ b/pkg/api/handlers/compat/containers_prune.go @@ -1,22 +1,19 @@ package compat import ( + "bytes" "net/http" "github.com/containers/podman/v2/libpod" + "github.com/containers/podman/v2/pkg/api/handlers" "github.com/containers/podman/v2/pkg/api/handlers/utils" - "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/containers/podman/v2/pkg/domain/filters" - "github.com/docker/docker/api/types" "github.com/gorilla/schema" "github.com/pkg/errors" ) func PruneContainers(w http.ResponseWriter, r *http.Request) { - var ( - delContainers []string - space int64 - ) runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) @@ -37,48 +34,45 @@ func PruneContainers(w http.ResponseWriter, r *http.Request) { filterFuncs = append(filterFuncs, generatedFunc) } + report, err := PruneContainersHelper(r, filterFuncs) + if err != nil { + utils.InternalServerError(w, err) + return + } + // Libpod response differs if utils.IsLibpodRequest(r) { - report, err := PruneContainersHelper(w, r, filterFuncs) - if err != nil { - utils.InternalServerError(w, err) - return - } - utils.WriteResponse(w, http.StatusOK, report) return } - prunedContainers, pruneErrors, err := runtime.PruneContainers(filterFuncs) - if err != nil { - utils.InternalServerError(w, err) - return - } - for ctrID, size := range prunedContainers { - if pruneErrors[ctrID] == nil { - space += size - delContainers = append(delContainers, ctrID) + var payload handlers.ContainersPruneReport + var errorMsg bytes.Buffer + for _, pr := range report { + if pr.Err != nil { + // Docker stops on first error vs. libpod which keeps going. Given API constraints, concatenate all errors + // and return that string. + errorMsg.WriteString(pr.Err.Error()) + errorMsg.WriteString("; ") + continue } + payload.ContainersDeleted = append(payload.ContainersDeleted, pr.Id) + payload.SpaceReclaimed += pr.Size } - report := types.ContainersPruneReport{ - ContainersDeleted: delContainers, - SpaceReclaimed: uint64(space), + if errorMsg.Len() > 0 { + utils.InternalServerError(w, errors.New(errorMsg.String())) + return } - utils.WriteResponse(w, http.StatusOK, report) + + utils.WriteResponse(w, http.StatusOK, payload) } -func PruneContainersHelper(w http.ResponseWriter, r *http.Request, filterFuncs []libpod.ContainerFilter) ( - *entities.ContainerPruneReport, error) { +func PruneContainersHelper(r *http.Request, filterFuncs []libpod.ContainerFilter) ([]*reports.PruneReport, error) { runtime := r.Context().Value("runtime").(*libpod.Runtime) - prunedContainers, pruneErrors, err := runtime.PruneContainers(filterFuncs) + + report, err := runtime.PruneContainers(filterFuncs) if err != nil { - utils.InternalServerError(w, err) return nil, err } - - report := &entities.ContainerPruneReport{ - Err: pruneErrors, - ID: prunedContainers, - } return report, nil } diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go index dc72500e4..0ae0f3bcf 100644 --- a/pkg/api/handlers/compat/images.go +++ b/pkg/api/handlers/compat/images.go @@ -18,7 +18,6 @@ import ( "github.com/containers/podman/v2/pkg/api/handlers/utils" "github.com/containers/podman/v2/pkg/auth" "github.com/containers/podman/v2/pkg/domain/entities" - "github.com/docker/docker/api/types" "github.com/gorilla/schema" "github.com/opencontainers/go-digest" "github.com/pkg/errors" @@ -74,50 +73,6 @@ func ExportImage(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, rdr) } -func PruneImages(w http.ResponseWriter, r *http.Request) { - var ( - filters []string - ) - decoder := r.Context().Value("decoder").(*schema.Decoder) - runtime := r.Context().Value("runtime").(*libpod.Runtime) - - query := struct { - All bool - Filters map[string][]string `schema:"filters"` - }{ - // This is where you can override the golang default value for one of fields - } - - if err := decoder.Decode(&query, r.URL.Query()); err != nil { - utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) - return - } - - idr := []types.ImageDeleteResponseItem{} - for k, v := range query.Filters { - for _, val := range v { - filters = append(filters, fmt.Sprintf("%s=%s", k, val)) - } - } - pruneCids, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, filters) - if err != nil { - utils.InternalServerError(w, err) - return - } - for _, p := range pruneCids { - idr = append(idr, types.ImageDeleteResponseItem{ - Deleted: p, - }) - } - - // FIXME/TODO to do this exactly correct, pruneimages needs to return idrs and space-reclaimed, then we are golden - ipr := types.ImagesPruneReport{ - ImagesDeleted: idr, - SpaceReclaimed: 1, // TODO we cannot supply this right now - } - utils.WriteResponse(w, http.StatusOK, handlers.ImagesPruneReport{ImagesPruneReport: ipr}) -} - func CommitContainer(w http.ResponseWriter, r *http.Request) { var ( destImage string diff --git a/pkg/api/handlers/compat/images_prune.go b/pkg/api/handlers/compat/images_prune.go new file mode 100644 index 000000000..c7e84804b --- /dev/null +++ b/pkg/api/handlers/compat/images_prune.go @@ -0,0 +1,75 @@ +package compat + +import ( + "bytes" + "fmt" + "net/http" + + "github.com/containers/podman/v2/libpod" + "github.com/containers/podman/v2/pkg/api/handlers" + "github.com/containers/podman/v2/pkg/api/handlers/utils" + "github.com/docker/docker/api/types" + "github.com/gorilla/schema" + "github.com/pkg/errors" +) + +func PruneImages(w http.ResponseWriter, r *http.Request) { + var ( + filters []string + ) + decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value("runtime").(*libpod.Runtime) + + query := struct { + All bool + Filters map[string][]string `schema:"filters"` + }{ + // This is where you can override the golang default value for one of fields + } + + if err := decoder.Decode(&query, r.URL.Query()); err != nil { + utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + return + } + + for k, v := range query.Filters { + for _, val := range v { + filters = append(filters, fmt.Sprintf("%s=%s", k, val)) + } + } + imagePruneReports, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, filters) + if err != nil { + utils.InternalServerError(w, err) + return + } + + idr := make([]types.ImageDeleteResponseItem, len(imagePruneReports)) + var reclaimedSpace uint64 + var errorMsg bytes.Buffer + for _, p := range imagePruneReports { + if p.Err != nil { + // Docker stops on first error vs. libpod which keeps going. Given API constraints, concatenate all errors + // and return that string. + errorMsg.WriteString(p.Err.Error()) + errorMsg.WriteString("; ") + continue + } + + idr = append(idr, types.ImageDeleteResponseItem{ + Deleted: p.Id, + }) + reclaimedSpace = reclaimedSpace + p.Size + } + if errorMsg.Len() > 0 { + utils.InternalServerError(w, errors.New(errorMsg.String())) + return + } + + payload := handlers.ImagesPruneReport{ + ImagesPruneReport: types.ImagesPruneReport{ + ImagesDeleted: idr, + SpaceReclaimed: reclaimedSpace, + }, + } + utils.WriteResponse(w, http.StatusOK, payload) +} diff --git a/pkg/api/handlers/compat/unsupported.go b/pkg/api/handlers/compat/unsupported.go index e5ff266f9..1c518690f 100644 --- a/pkg/api/handlers/compat/unsupported.go +++ b/pkg/api/handlers/compat/unsupported.go @@ -4,9 +4,8 @@ import ( "fmt" "net/http" - "github.com/containers/podman/v2/pkg/domain/entities" - "github.com/containers/podman/v2/pkg/api/handlers/utils" + "github.com/containers/podman/v2/pkg/errorhandling" log "github.com/sirupsen/logrus" ) @@ -14,5 +13,5 @@ func UnsupportedHandler(w http.ResponseWriter, r *http.Request) { msg := fmt.Sprintf("Path %s is not supported", r.URL.Path) log.Infof("Request Failed: %s", msg) - utils.WriteJSON(w, http.StatusNotFound, entities.ErrorModel{Message: msg}) + utils.WriteJSON(w, http.StatusNotFound, errorhandling.ErrorModel{Message: msg}) } diff --git a/pkg/api/handlers/compat/volumes.go b/pkg/api/handlers/compat/volumes.go index f49f06b17..82e70eb90 100644 --- a/pkg/api/handlers/compat/volumes.go +++ b/pkg/api/handlers/compat/volumes.go @@ -1,6 +1,7 @@ package compat import ( + "bytes" "encoding/json" "net/http" "net/url" @@ -8,6 +9,7 @@ import ( "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" + "github.com/containers/podman/v2/pkg/api/handlers" "github.com/containers/podman/v2/pkg/api/handlers/utils" "github.com/containers/podman/v2/pkg/domain/filters" "github.com/containers/podman/v2/pkg/domain/infra/abi/parse" @@ -56,10 +58,15 @@ func ListVolumes(w http.ResponseWriter, r *http.Request) { } volumeConfigs := make([]*docker_api_types.Volume, 0, len(vols)) for _, v := range vols { + mp, err := v.MountPoint() + if err != nil { + utils.InternalServerError(w, err) + return + } config := docker_api_types.Volume{ Name: v.Name(), Driver: v.Driver(), - Mountpoint: v.MountPoint(), + Mountpoint: mp, CreatedAt: v.CreatedTime().Format(time.RFC3339), Labels: v.Labels(), Scope: v.Scope(), @@ -104,11 +111,16 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) { // if using the compat layer and the volume already exists, we // must return a 201 with the same information as create if existingVolume != nil && !utils.IsLibpodRequest(r) { + mp, err := existingVolume.MountPoint() + if err != nil { + utils.InternalServerError(w, err) + return + } response := docker_api_types.Volume{ CreatedAt: existingVolume.CreatedTime().Format(time.RFC3339), Driver: existingVolume.Driver(), Labels: existingVolume.Labels(), - Mountpoint: existingVolume.MountPoint(), + Mountpoint: mp, Name: existingVolume.Name(), Options: existingVolume.Options(), Scope: existingVolume.Scope(), @@ -144,10 +156,15 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } + mp, err := vol.MountPoint() + if err != nil { + utils.InternalServerError(w, err) + return + } volResponse := docker_api_types.Volume{ Name: config.Name, Driver: config.Driver, - Mountpoint: config.MountPoint, + Mountpoint: mp, CreatedAt: config.CreatedTime.Format(time.RFC3339), Labels: config.Labels, Options: config.Options, @@ -171,10 +188,15 @@ func InspectVolume(w http.ResponseWriter, r *http.Request) { utils.VolumeNotFound(w, name, err) return } + mp, err := vol.MountPoint() + if err != nil { + utils.InternalServerError(w, err) + return + } volResponse := docker_api_types.Volume{ Name: vol.Name(), Driver: vol.Driver(), - Mountpoint: vol.MountPoint(), + Mountpoint: mp, CreatedAt: vol.CreatedTime().Format(time.RFC3339), Labels: vol.Labels(), Options: vol.Options(), @@ -268,17 +290,29 @@ func PruneVolumes(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } + + var errorMsg bytes.Buffer + var reclaimedSpace uint64 prunedIds := make([]string, 0, len(pruned)) - for k := range pruned { - // XXX: This drops any pruning per-volume error messages on the floor - prunedIds = append(prunedIds, k) + for _, v := range pruned { + if v.Err != nil { + errorMsg.WriteString(v.Err.Error()) + errorMsg.WriteString("; ") + continue + } + prunedIds = append(prunedIds, v.Id) + reclaimedSpace += v.Size } - pruneResponse := docker_api_types.VolumesPruneReport{ - VolumesDeleted: prunedIds, - // TODO: We don't have any insight into how much space was reclaimed - // from `PruneVolumes()` but it's not nullable - SpaceReclaimed: 0, + if errorMsg.Len() > 0 { + utils.InternalServerError(w, errors.New(errorMsg.String())) + return } - utils.WriteResponse(w, http.StatusOK, pruneResponse) + payload := handlers.VolumesPruneReport{ + VolumesPruneReport: docker_api_types.VolumesPruneReport{ + VolumesDeleted: prunedIds, + SpaceReclaimed: reclaimedSpace, + }, + } + utils.WriteResponse(w, http.StatusOK, payload) } diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go index 14eb44831..6b07b1cc5 100644 --- a/pkg/api/handlers/libpod/containers.go +++ b/pkg/api/handlers/libpod/containers.go @@ -275,6 +275,7 @@ func Restore(w http.ResponseWriter, r *http.Request) { Import bool `schema:"import"` Name string `schema:"name"` IgnoreRootFS bool `schema:"ignoreRootFS"` + IgnoreVolumes bool `schema:"ignoreVolumes"` IgnoreStaticIP bool `schema:"ignoreStaticIP"` IgnoreStaticMAC bool `schema:"ignoreStaticMAC"` }{ diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index 0b8712f16..5b15527b7 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -16,7 +16,6 @@ import ( "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/image" - image2 "github.com/containers/podman/v2/libpod/image" "github.com/containers/podman/v2/pkg/api/handlers" "github.com/containers/podman/v2/pkg/api/handlers/utils" "github.com/containers/podman/v2/pkg/auth" @@ -156,12 +155,12 @@ func PruneImages(w http.ResponseWriter, r *http.Request) { } } - cids, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, libpodFilters) + imagePruneReports, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, libpodFilters) if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) return } - utils.WriteResponse(w, http.StatusOK, cids) + utils.WriteResponse(w, http.StatusOK, imagePruneReports) } func ExportImage(w http.ResponseWriter, r *http.Request) { @@ -354,20 +353,7 @@ func ImagesLoad(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to load image")) return } - split := strings.Split(loadedImage, ",") - newImage, err := runtime.ImageRuntime().NewFromLocal(split[0]) - if err != nil { - utils.InternalServerError(w, err) - return - } - // TODO this should go into libpod proper at some point. - if len(query.Reference) > 0 { - if err := newImage.TagImage(query.Reference); err != nil { - utils.InternalServerError(w, err) - return - } - } - utils.WriteResponse(w, http.StatusOK, entities.ImageLoadReport{Names: split}) + utils.WriteResponse(w, http.StatusOK, entities.ImageLoadReport{Names: strings.Split(loadedImage, ",")}) } func ImagesImport(w http.ResponseWriter, r *http.Request) { @@ -524,7 +510,7 @@ func CommitContainer(w http.ResponseWriter, r *http.Request) { utils.Error(w, "failed to get runtime config", http.StatusInternalServerError, errors.Wrap(err, "failed to get runtime config")) return } - sc := image2.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false) + sc := image.GetSystemContext(rtc.Engine.SignaturePolicyPath, "", false) tag := "latest" options := libpod.ContainerCommitOptions{ Pause: true, diff --git a/pkg/api/handlers/libpod/images_pull.go b/pkg/api/handlers/libpod/images_pull.go index 5e2727e95..bacba006d 100644 --- a/pkg/api/handlers/libpod/images_pull.go +++ b/pkg/api/handlers/libpod/images_pull.go @@ -115,10 +115,10 @@ func ImagesPull(w http.ResponseWriter, r *http.Request) { } } - writer := channel.NewWriter(make(chan []byte, 1)) + writer := channel.NewWriter(make(chan []byte)) defer writer.Close() - stderr := channel.NewWriter(make(chan []byte, 1)) + stderr := channel.NewWriter(make(chan []byte)) defer stderr.Close() images := make([]string, 0, len(imagesToPull)) diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go index 5422411cf..2409d3a20 100644 --- a/pkg/api/handlers/libpod/pods.go +++ b/pkg/api/handlers/libpod/pods.go @@ -43,6 +43,7 @@ func PodCreate(w http.ResponseWriter, r *http.Request) { } func Pods(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { Filters map[string][]string `schema:"filters"` @@ -55,7 +56,11 @@ func Pods(w http.ResponseWriter, r *http.Request) { return } - pods, err := utils.GetPods(w, r) + containerEngine := abi.ContainerEngine{Libpod: runtime} + podPSOptions := entities.PodPSOptions{ + Filters: query.Filters, + } + pods, err := containerEngine.PodPs(r.Context(), podPSOptions) if err != nil { utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return @@ -235,7 +240,7 @@ func PodRestart(w http.ResponseWriter, r *http.Request) { } func PodPrune(w http.ResponseWriter, r *http.Request) { - reports, err := PodPruneHelper(w, r) + reports, err := PodPruneHelper(r) if err != nil { utils.InternalServerError(w, err) return @@ -243,7 +248,7 @@ func PodPrune(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, reports) } -func PodPruneHelper(w http.ResponseWriter, r *http.Request) ([]*entities.PodPruneReport, error) { +func PodPruneHelper(r *http.Request) ([]*entities.PodPruneReport, error) { var ( runtime = r.Context().Value("runtime").(*libpod.Runtime) ) diff --git a/pkg/api/handlers/libpod/system.go b/pkg/api/handlers/libpod/system.go index b157dfc7b..c48c186ed 100644 --- a/pkg/api/handlers/libpod/system.go +++ b/pkg/api/handlers/libpod/system.go @@ -30,7 +30,7 @@ func SystemPrune(w http.ResponseWriter, r *http.Request) { return } - podPruneReport, err := PodPruneHelper(w, r) + podPruneReport, err := PodPruneHelper(r) if err != nil { utils.InternalServerError(w, err) return @@ -38,35 +38,28 @@ func SystemPrune(w http.ResponseWriter, r *http.Request) { systemPruneReport.PodPruneReport = podPruneReport // We could parallelize this, should we? - containerPruneReport, err := compat.PruneContainersHelper(w, r, nil) + containerPruneReports, err := compat.PruneContainersHelper(r, nil) if err != nil { utils.InternalServerError(w, err) return } - systemPruneReport.ContainerPruneReport = containerPruneReport + systemPruneReport.ContainerPruneReports = containerPruneReports - results, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, nil) + imagePruneReports, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, nil) if err != nil { utils.InternalServerError(w, err) return } - report := entities.ImagePruneReport{ - Report: entities.Report{ - Id: results, - Err: nil, - }, - } - - systemPruneReport.ImagePruneReport = &report + systemPruneReport.ImagePruneReports = imagePruneReports if query.Volumes { - volumePruneReport, err := pruneVolumesHelper(r) + volumePruneReports, err := pruneVolumesHelper(r) if err != nil { utils.InternalServerError(w, err) return } - systemPruneReport.VolumePruneReport = volumePruneReport + systemPruneReport.VolumePruneReports = volumePruneReports } utils.WriteResponse(w, http.StatusOK, systemPruneReport) } diff --git a/pkg/api/handlers/libpod/volumes.go b/pkg/api/handlers/libpod/volumes.go index b02a6a8ce..38fdf1b4d 100644 --- a/pkg/api/handlers/libpod/volumes.go +++ b/pkg/api/handlers/libpod/volumes.go @@ -9,6 +9,7 @@ import ( "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/api/handlers/utils" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/containers/podman/v2/pkg/domain/filters" "github.com/containers/podman/v2/pkg/domain/infra/abi/parse" "github.com/gorilla/schema" @@ -59,20 +60,13 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) { utils.InternalServerError(w, err) return } - config, err := vol.Config() + inspectOut, err := vol.Inspect() if err != nil { utils.InternalServerError(w, err) return } volResponse := entities.VolumeConfigResponse{ - Name: config.Name, - Driver: config.Driver, - Mountpoint: config.MountPoint, - CreatedAt: config.CreatedTime, - Labels: config.Labels, - Options: config.Options, - UID: config.UID, - GID: config.GID, + InspectVolumeData: *inspectOut, } utils.WriteResponse(w, http.StatusCreated, volResponse) } @@ -87,27 +81,13 @@ func InspectVolume(w http.ResponseWriter, r *http.Request) { utils.VolumeNotFound(w, name, err) return } - var uid, gid int - uid, err = vol.UID() + inspectOut, err := vol.Inspect() if err != nil { - utils.Error(w, "Error fetching volume UID", http.StatusInternalServerError, err) - return - } - gid, err = vol.GID() - if err != nil { - utils.Error(w, "Error fetching volume GID", http.StatusInternalServerError, err) + utils.InternalServerError(w, err) return } volResponse := entities.VolumeConfigResponse{ - Name: vol.Name(), - Driver: vol.Driver(), - Mountpoint: vol.MountPoint(), - CreatedAt: vol.CreatedTime(), - Labels: vol.Labels(), - Scope: vol.Scope(), - Options: vol.Options(), - UID: uid, - GID: gid, + InspectVolumeData: *inspectOut, } utils.WriteResponse(w, http.StatusOK, volResponse) } @@ -142,27 +122,13 @@ func ListVolumes(w http.ResponseWriter, r *http.Request) { } volumeConfigs := make([]*entities.VolumeListReport, 0, len(vols)) for _, v := range vols { - var uid, gid int - uid, err = v.UID() + inspectOut, err := v.Inspect() if err != nil { - utils.Error(w, "Error fetching volume UID", http.StatusInternalServerError, err) - return - } - gid, err = v.GID() - if err != nil { - utils.Error(w, "Error fetching volume GID", http.StatusInternalServerError, err) + utils.InternalServerError(w, err) return } config := entities.VolumeConfigResponse{ - Name: v.Name(), - Driver: v.Driver(), - Mountpoint: v.MountPoint(), - CreatedAt: v.CreatedTime(), - Labels: v.Labels(), - Scope: v.Scope(), - Options: v.Options(), - UID: uid, - GID: gid, + InspectVolumeData: *inspectOut, } volumeConfigs = append(volumeConfigs, &entities.VolumeListReport{VolumeConfigResponse: config}) } @@ -178,7 +144,7 @@ func PruneVolumes(w http.ResponseWriter, r *http.Request) { utils.WriteResponse(w, http.StatusOK, reports) } -func pruneVolumesHelper(r *http.Request) ([]*entities.VolumePruneReport, error) { +func pruneVolumesHelper(r *http.Request) ([]*reports.PruneReport, error) { var ( runtime = r.Context().Value("runtime").(*libpod.Runtime) decoder = r.Context().Value("decoder").(*schema.Decoder) @@ -199,17 +165,10 @@ func pruneVolumesHelper(r *http.Request) ([]*entities.VolumePruneReport, error) return nil, err } - pruned, err := runtime.PruneVolumes(r.Context(), filterFuncs) + reports, err := runtime.PruneVolumes(r.Context(), filterFuncs) if err != nil { return nil, err } - reports := make([]*entities.VolumePruneReport, 0, len(pruned)) - for k, v := range pruned { - reports = append(reports, &entities.VolumePruneReport{ - Err: v, - Id: k, - }) - } return reports, nil } func RemoveVolume(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/api/handlers/utils/errors.go b/pkg/api/handlers/utils/errors.go index fc77b8ec0..e2c287c45 100644 --- a/pkg/api/handlers/utils/errors.go +++ b/pkg/api/handlers/utils/errors.go @@ -5,7 +5,7 @@ import ( "net/http" "github.com/containers/podman/v2/libpod/define" - "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/errorhandling" "github.com/pkg/errors" log "github.com/sirupsen/logrus" ) @@ -24,7 +24,7 @@ var ( func Error(w http.ResponseWriter, apiMessage string, code int, err error) { // Log detailed message of what happened to machine running podman service log.Infof("Request Failed(%s): %s", http.StatusText(code), err.Error()) - em := entities.ErrorModel{ + em := errorhandling.ErrorModel{ Because: (errors.Cause(err)).Error(), Message: err.Error(), ResponseCode: code, diff --git a/pkg/api/handlers/utils/pods.go b/pkg/api/handlers/utils/pods.go deleted file mode 100644 index 0fe3a308b..000000000 --- a/pkg/api/handlers/utils/pods.go +++ /dev/null @@ -1,87 +0,0 @@ -package utils - -import ( - "net/http" - - "github.com/containers/podman/v2/libpod" - "github.com/containers/podman/v2/pkg/domain/entities" - dfilters "github.com/containers/podman/v2/pkg/domain/filters" - "github.com/gorilla/schema" -) - -func GetPods(w http.ResponseWriter, r *http.Request) ([]*entities.ListPodsReport, error) { - var ( - pods []*libpod.Pod - ) - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) - - query := struct { - All bool - Filters map[string][]string `schema:"filters"` - Digests bool - }{} - - if err := decoder.Decode(&query, r.URL.Query()); err != nil { - return nil, err - } - if _, found := r.URL.Query()["digests"]; found && query.Digests { - UnSupportedParameter("digests") - } - - filters := make([]libpod.PodFilter, 0, len(query.Filters)) - for k, v := range query.Filters { - f, err := dfilters.GeneratePodFilterFunc(k, v) - if err != nil { - return nil, err - } - filters = append(filters, f) - } - pods, err := runtime.Pods(filters...) - if err != nil { - return nil, err - } - - if len(pods) == 0 { - return []*entities.ListPodsReport{}, nil - } - - lps := make([]*entities.ListPodsReport, 0, len(pods)) - for _, pod := range pods { - status, err := pod.GetPodStatus() - if err != nil { - return nil, err - } - ctrs, err := pod.AllContainers() - if err != nil { - return nil, err - } - infraID, err := pod.InfraContainerID() - if err != nil { - return nil, err - } - lp := entities.ListPodsReport{ - Cgroup: pod.CgroupParent(), - Created: pod.CreatedTime(), - Id: pod.ID(), - Name: pod.Name(), - Namespace: pod.Namespace(), - Status: status, - InfraId: infraID, - Labels: pod.Labels(), - } - for _, ctr := range ctrs { - state, err := ctr.State() - if err != nil { - return nil, err - } - lp.Containers = append(lp.Containers, &entities.ListPodContainer{ - Id: ctr.ID(), - Names: ctr.Name(), - Status: state.String(), - }) - } - lps = append(lps, &lp) - } - return lps, nil -} diff --git a/pkg/api/server/docs.go b/pkg/api/server/docs.go index 1aaf31117..a99fefd7b 100644 --- a/pkg/api/server/docs.go +++ b/pkg/api/server/docs.go @@ -13,7 +13,7 @@ // You can then use cURL on the socket using requests documented below. // // NOTE: if you install the package podman-docker, it will create a symbolic -// link for /var/run/docker.sock to /run/podman/podman.sock +// link for /run/docker.sock to /run/podman/podman.sock // // See podman-service(1) for more information. // diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index b80dea545..74a04b2e6 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -291,9 +291,6 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { r.HandleFunc(VersionedPath("/containers/{name}/pause"), s.APIHandler(compat.PauseContainer)).Methods(http.MethodPost) // Added non version path to URI to support docker non versioned paths r.HandleFunc("/containers/{name}/pause", s.APIHandler(compat.PauseContainer)).Methods(http.MethodPost) - r.HandleFunc(VersionedPath("/containers/{name}/rename"), s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodPost) - // Added non version path to URI to support docker non versioned paths - r.HandleFunc("/containers/{name}/rename", s.APIHandler(compat.UnsupportedHandler)).Methods(http.MethodPost) // swagger:operation POST /containers/{name}/restart compat restartContainer // --- // tags: @@ -610,6 +607,36 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // $ref: "#/responses/InternalError" r.HandleFunc(VersionedPath("/containers/{name}/export"), s.APIHandler(compat.ExportContainer)).Methods(http.MethodGet) r.HandleFunc("/containers/{name}/export", s.APIHandler(compat.ExportContainer)).Methods(http.MethodGet) + // swagger:operation POST /containers/{name}/rename compat renameContainer + // --- + // tags: + // - containers (compat) + // summary: Rename an existing container + // description: Change the name of an existing container. + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: Full or partial ID or full name of the container to rename + // - in: query + // name: name + // type: string + // required: true + // description: New name for the container + // produces: + // - application/json + // responses: + // 204: + // description: no error + // 404: + // $ref: "#/responses/NoSuchContainer" + // 409: + // $ref: "#/responses/ConflictError" + // 500: + // $ref: "#/responses/InternalError" + r.HandleFunc(VersionedPath("/containers/{name}/rename"), s.APIHandler(compat.RenameContainer)).Methods(http.MethodPost) + r.HandleFunc("/containers/{name}/rename", s.APIHandler(compat.RenameContainer)).Methods(http.MethodPost) /* libpod endpoints @@ -1463,5 +1490,34 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // 500: // $ref: "#/responses/InternalError" r.HandleFunc(VersionedPath("/libpod/containers/{name}/init"), s.APIHandler(libpod.InitContainer)).Methods(http.MethodPost) + // swagger:operation POST /libpod/containers/{name}/rename libpod libpodRenameContainer + // --- + // tags: + // - containers + // summary: Rename an existing container + // description: Change the name of an existing container. + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: Full or partial ID or full name of the container to rename + // - in: query + // name: name + // type: string + // required: true + // description: New name for the container + // produces: + // - application/json + // responses: + // 204: + // description: no error + // 404: + // $ref: "#/responses/NoSuchContainer" + // 409: + // $ref: "#/responses/ConflictError" + // 500: + // $ref: "#/responses/InternalError" + r.HandleFunc(VersionedPath("/libpod/containers/{name}/rename"), s.APIHandler(compat.RenameContainer)).Methods(http.MethodPost) return nil } diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index 7e6de8783..8d0c0800b 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -797,10 +797,6 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // summary: Load image // description: Load an image (oci-archive or docker-archive) stream. // parameters: - // - in: query - // name: reference - // description: "Optional Name[:TAG] for the image" - // type: string // - in: formData // name: upload // description: tarball of container image diff --git a/pkg/api/server/register_networks.go b/pkg/api/server/register_networks.go index e6c85d244..967d7da76 100644 --- a/pkg/api/server/register_networks.go +++ b/pkg/api/server/register_networks.go @@ -9,6 +9,19 @@ import ( ) func (s *APIServer) registerNetworkHandlers(r *mux.Router) error { + // swagger:operation POST /networks/prune compat compatPruneNetwork + // --- + // tags: + // - networks (compat) + // Summary: Delete unused networks + // description: Not supported + // produces: + // - application/json + // responses: + // 404: + // $ref: "#/responses/NoSuchNetwork" + r.HandleFunc(VersionedPath("/networks/prune"), compat.UnsupportedHandler).Methods(http.MethodPost) + r.HandleFunc("/networks/prune", compat.UnsupportedHandler).Methods(http.MethodPost) // swagger:operation DELETE /networks/{name} compat compatRemoveNetwork // --- // tags: diff --git a/pkg/api/server/swagger.go b/pkg/api/server/swagger.go index 6d349bb7d..d4fc33442 100644 --- a/pkg/api/server/swagger.go +++ b/pkg/api/server/swagger.go @@ -4,6 +4,8 @@ import ( "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" + "github.com/containers/podman/v2/pkg/errorhandling" ) // No such image @@ -11,7 +13,7 @@ import ( type swagErrNoSuchImage struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -20,7 +22,7 @@ type swagErrNoSuchImage struct { type swagErrNoSuchContainer struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -29,7 +31,7 @@ type swagErrNoSuchContainer struct { type swagErrNoSuchNetwork struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -38,7 +40,7 @@ type swagErrNoSuchNetwork struct { type swagErrNoSuchExecInstance struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -47,7 +49,7 @@ type swagErrNoSuchExecInstance struct { type swagErrNoSuchVolume struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -56,7 +58,7 @@ type swagErrNoSuchVolume struct { type swagErrNoSuchPod struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -65,7 +67,7 @@ type swagErrNoSuchPod struct { type swagErrNoSuchManifest struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -74,7 +76,7 @@ type swagErrNoSuchManifest struct { type swagInternalError struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -83,7 +85,7 @@ type swagInternalError struct { type swagConflictError struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -92,7 +94,7 @@ type swagConflictError struct { type swagBadParamError struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -101,7 +103,7 @@ type swagBadParamError struct { type swagContainerAlreadyStartedError struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -110,7 +112,7 @@ type swagContainerAlreadyStartedError struct { type swagContainerAlreadyStopped struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -119,7 +121,7 @@ type swagContainerAlreadyStopped struct { type swagPodAlreadyStartedError struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -128,7 +130,7 @@ type swagPodAlreadyStartedError struct { type swagPodAlreadyStopped struct { // in:body Body struct { - entities.ErrorModel + errorhandling.ErrorModel } } @@ -170,7 +172,7 @@ type ok struct { // swagger:response VolumePruneResponse type swagVolumePruneResponse struct { // in:body - Body []entities.VolumePruneReport + Body []reports.PruneReport } // Volume create response diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go index 650aa9ac5..1081a0e61 100644 --- a/pkg/bindings/containers/containers.go +++ b/pkg/bindings/containers/containers.go @@ -12,6 +12,7 @@ import ( "github.com/containers/podman/v2/pkg/api/handlers" "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -49,11 +50,11 @@ func List(ctx context.Context, options *ListOptions) ([]entities.ListContainer, // used for more granular selection of containers. The main error returned indicates if there were runtime // errors like finding containers. Errors specific to the removal of a container are in the PruneContainerResponse // structure. -func Prune(ctx context.Context, options *PruneOptions) (*entities.ContainerPruneReport, error) { +func Prune(ctx context.Context, options *PruneOptions) ([]*reports.PruneReport, error) { if options == nil { options = new(PruneOptions) } - var reports *entities.ContainerPruneReport + var reports []*reports.PruneReport conn, err := bindings.GetClient(ctx) if err != nil { return nil, err diff --git a/pkg/bindings/containers/rename.go b/pkg/bindings/containers/rename.go new file mode 100644 index 000000000..0e8c7f198 --- /dev/null +++ b/pkg/bindings/containers/rename.go @@ -0,0 +1,28 @@ +package containers + +import ( + "context" + "net/http" + + "github.com/containers/podman/v2/pkg/bindings" +) + +// Rename an existing container. +func Rename(ctx context.Context, nameOrID string, options *RenameOptions) error { + if options == nil { + options = new(RenameOptions) + } + conn, err := bindings.GetClient(ctx) + if err != nil { + return err + } + params, err := options.ToParams() + if err != nil { + return err + } + response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/rename", params, nil, nameOrID) + if err != nil { + return err + } + return response.Process(nil) +} diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go index 24402e982..10f553e52 100644 --- a/pkg/bindings/containers/types.go +++ b/pkg/bindings/containers/types.go @@ -194,6 +194,13 @@ type InitOptions struct{} // ShouldRestartOptions type ShouldRestartOptions struct{} +//go:generate go run ../generator/generator.go RenameOptions +// RenameOptions are options for renaming containers. +// The Name field is required. +type RenameOptions struct { + Name *string +} + //go:generate go run ../generator/generator.go ResizeTTYOptions // ResizeTTYOptions are optional options for resizing // container TTYs diff --git a/pkg/bindings/containers/types_attach_options.go b/pkg/bindings/containers/types_attach_options.go index 4ffb8ab17..6d8c1cb01 100644 --- a/pkg/bindings/containers/types_attach_options.go +++ b/pkg/bindings/containers/types_attach_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:18.566804404 -0600 CST m=+0.000258831 */ // Changed @@ -39,6 +38,7 @@ func (o *AttachOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_checkpoint_options.go b/pkg/bindings/containers/types_checkpoint_options.go index d03dc8231..ec766de4a 100644 --- a/pkg/bindings/containers/types_checkpoint_options.go +++ b/pkg/bindings/containers/types_checkpoint_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:18.714853285 -0600 CST m=+0.000319103 */ // Changed @@ -39,6 +38,7 @@ func (o *CheckpointOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_commit_options.go b/pkg/bindings/containers/types_commit_options.go index a8b215141..b745bebe2 100644 --- a/pkg/bindings/containers/types_commit_options.go +++ b/pkg/bindings/containers/types_commit_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:18.420656951 -0600 CST m=+0.000259662 */ // Changed @@ -39,6 +38,7 @@ func (o *CommitOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_create_options.go b/pkg/bindings/containers/types_create_options.go index 4dbce0203..4b9574cf1 100644 --- a/pkg/bindings/containers/types_create_options.go +++ b/pkg/bindings/containers/types_create_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:19.011789618 -0600 CST m=+0.000259413 */ // Changed @@ -39,6 +38,7 @@ func (o *CreateOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_diff_options.go b/pkg/bindings/containers/types_diff_options.go index be3bbf554..55fa6930d 100644 --- a/pkg/bindings/containers/types_diff_options.go +++ b/pkg/bindings/containers/types_diff_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:19.159128927 -0600 CST m=+0.000255635 */ // Changed @@ -39,6 +38,7 @@ func (o *DiffOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_execinspect_options.go b/pkg/bindings/containers/types_execinspect_options.go index 3c4c870be..c5d1f931a 100644 --- a/pkg/bindings/containers/types_execinspect_options.go +++ b/pkg/bindings/containers/types_execinspect_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:19.303239014 -0600 CST m=+0.000256861 */ // Changed @@ -39,6 +38,7 @@ func (o *ExecInspectOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_execstart_options.go b/pkg/bindings/containers/types_execstart_options.go index 66fdc82cb..9ecb70a3e 100644 --- a/pkg/bindings/containers/types_execstart_options.go +++ b/pkg/bindings/containers/types_execstart_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:19.447714428 -0600 CST m=+0.000257278 */ // Changed @@ -39,6 +38,7 @@ func (o *ExecStartOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_execstartandattach_options.go b/pkg/bindings/containers/types_execstartandattach_options.go index 43900d29d..a5a691e35 100644 --- a/pkg/bindings/containers/types_execstartandattach_options.go +++ b/pkg/bindings/containers/types_execstartandattach_options.go @@ -6,6 +6,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -13,8 +14,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:22.827903078 -0600 CST m=+0.000269906 */ // Changed @@ -41,6 +40,7 @@ func (o *ExecStartAndAttachOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_export_options.go b/pkg/bindings/containers/types_export_options.go index e325bd2cd..55e413c72 100644 --- a/pkg/bindings/containers/types_export_options.go +++ b/pkg/bindings/containers/types_export_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:22.101679998 -0600 CST m=+0.000261669 */ // Changed @@ -39,6 +38,7 @@ func (o *ExportOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_healthcheck_options.go b/pkg/bindings/containers/types_healthcheck_options.go index 8c4300366..9d8b25bf4 100644 --- a/pkg/bindings/containers/types_healthcheck_options.go +++ b/pkg/bindings/containers/types_healthcheck_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:19.593883686 -0600 CST m=+0.000289845 */ // Changed @@ -39,6 +38,7 @@ func (o *HealthCheckOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_init_options.go b/pkg/bindings/containers/types_init_options.go index 655362f62..6fb5795c0 100644 --- a/pkg/bindings/containers/types_init_options.go +++ b/pkg/bindings/containers/types_init_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:22.245077233 -0600 CST m=+0.000255461 */ // Changed @@ -39,6 +38,7 @@ func (o *InitOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_inspect_options.go b/pkg/bindings/containers/types_inspect_options.go index 884f5524d..722372414 100644 --- a/pkg/bindings/containers/types_inspect_options.go +++ b/pkg/bindings/containers/types_inspect_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:20.635987603 -0600 CST m=+0.000260270 */ // Changed @@ -39,6 +38,7 @@ func (o *InspectOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_kill_options.go b/pkg/bindings/containers/types_kill_options.go index 3d6fa6224..dd84f0d9f 100644 --- a/pkg/bindings/containers/types_kill_options.go +++ b/pkg/bindings/containers/types_kill_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:20.781581076 -0600 CST m=+0.000259040 */ // Changed @@ -39,6 +38,7 @@ func (o *KillOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_list_options.go b/pkg/bindings/containers/types_list_options.go index dd74d37b7..43326fa59 100644 --- a/pkg/bindings/containers/types_list_options.go +++ b/pkg/bindings/containers/types_list_options.go @@ -12,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:20.199081744 -0600 CST m=+0.000270626 */ // Changed diff --git a/pkg/bindings/containers/types_log_options.go b/pkg/bindings/containers/types_log_options.go index a6958242f..364f29de4 100644 --- a/pkg/bindings/containers/types_log_options.go +++ b/pkg/bindings/containers/types_log_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:18.273264471 -0600 CST m=+0.000274536 */ // Changed @@ -39,6 +38,7 @@ func (o *LogOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_mount_options.go b/pkg/bindings/containers/types_mount_options.go index c0e253094..6f4349b73 100644 --- a/pkg/bindings/containers/types_mount_options.go +++ b/pkg/bindings/containers/types_mount_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:19.740822464 -0600 CST m=+0.000250074 */ // Changed @@ -39,6 +38,7 @@ func (o *MountOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_mountedcontainerpaths_options.go b/pkg/bindings/containers/types_mountedcontainerpaths_options.go index e368ff131..0d8b69654 100644 --- a/pkg/bindings/containers/types_mountedcontainerpaths_options.go +++ b/pkg/bindings/containers/types_mountedcontainerpaths_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:20.048233253 -0600 CST m=+0.000307223 */ // Changed @@ -39,6 +38,7 @@ func (o *MountedContainerPathsOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_pause_options.go b/pkg/bindings/containers/types_pause_options.go index 26ad86793..0cc65f64e 100644 --- a/pkg/bindings/containers/types_pause_options.go +++ b/pkg/bindings/containers/types_pause_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:20.929891294 -0600 CST m=+0.000261081 */ // Changed @@ -39,6 +38,7 @@ func (o *PauseOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_prune_options.go b/pkg/bindings/containers/types_prune_options.go index e3c0f4de7..10adf0a2a 100644 --- a/pkg/bindings/containers/types_prune_options.go +++ b/pkg/bindings/containers/types_prune_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:20.344278799 -0600 CST m=+0.000263499 */ // Changed @@ -39,6 +38,7 @@ func (o *PruneOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_remove_options.go b/pkg/bindings/containers/types_remove_options.go index 6f59f0ed5..e21fb41f7 100644 --- a/pkg/bindings/containers/types_remove_options.go +++ b/pkg/bindings/containers/types_remove_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:20.489968735 -0600 CST m=+0.000264450 */ // Changed @@ -39,6 +38,7 @@ func (o *RemoveOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_rename_options.go b/pkg/bindings/containers/types_rename_options.go new file mode 100644 index 000000000..b7a723f7a --- /dev/null +++ b/pkg/bindings/containers/types_rename_options.go @@ -0,0 +1,104 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + "strings" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +This file is generated automatically by go generate. Do not edit. +*/ + +// Changed +func (o *RenameOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *RenameOptions) ToParams() (url.Values, error) { + params := url.Values{} + if o == nil { + return params, nil + } + json := jsoniter.ConfigCompatibleWithStandardLibrary + s := reflect.ValueOf(o) + if reflect.Ptr == s.Kind() { + s = s.Elem() + } + sType := s.Type() + for i := 0; i < s.NumField(); i++ { + fieldName := sType.Field(i).Name + if !o.Changed(fieldName) { + continue + } + fieldName = strings.ToLower(fieldName) + f := s.Field(i) + if reflect.Ptr == f.Kind() { + f = f.Elem() + } + switch f.Kind() { + case reflect.Bool: + params.Set(fieldName, strconv.FormatBool(f.Bool())) + case reflect.String: + params.Set(fieldName, f.String()) + case reflect.Int, reflect.Int64: + // f.Int() is always an int64 + params.Set(fieldName, strconv.FormatInt(f.Int(), 10)) + case reflect.Uint, reflect.Uint64: + // f.Uint() is always an uint64 + params.Set(fieldName, strconv.FormatUint(f.Uint(), 10)) + case reflect.Slice: + typ := reflect.TypeOf(f.Interface()).Elem() + switch typ.Kind() { + case reflect.String: + sl := f.Slice(0, f.Len()) + s, ok := sl.Interface().([]string) + if !ok { + return nil, errors.New("failed to convert to string slice") + } + for _, val := range s { + params.Add(fieldName, val) + } + default: + return nil, errors.Errorf("unknown slice type %s", f.Kind().String()) + } + case reflect.Map: + lowerCaseKeys := make(map[string][]string) + iter := f.MapRange() + for iter.Next() { + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + + } + s, err := json.MarshalToString(lowerCaseKeys) + if err != nil { + return nil, err + } + + params.Set(fieldName, s) + } + } + return params, nil +} + +// WithName +func (o *RenameOptions) WithName(value string) *RenameOptions { + v := &value + o.Name = v + return o +} + +// GetName +func (o *RenameOptions) GetName() string { + var name string + if o.Name == nil { + return name + } + return *o.Name +} diff --git a/pkg/bindings/containers/types_resizeexectty_options.go b/pkg/bindings/containers/types_resizeexectty_options.go index 33bb4e78b..0212adeb2 100644 --- a/pkg/bindings/containers/types_resizeexectty_options.go +++ b/pkg/bindings/containers/types_resizeexectty_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:22.680735758 -0600 CST m=+0.000267081 */ // Changed @@ -39,6 +38,7 @@ func (o *ResizeExecTTYOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_resizetty_options.go b/pkg/bindings/containers/types_resizetty_options.go index 29ec54988..cee607902 100644 --- a/pkg/bindings/containers/types_resizetty_options.go +++ b/pkg/bindings/containers/types_resizetty_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:22.535788375 -0600 CST m=+0.000266528 */ // Changed @@ -39,6 +38,7 @@ func (o *ResizeTTYOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_restart_options.go b/pkg/bindings/containers/types_restart_options.go index 13ac099b1..8dcc6b5b7 100644 --- a/pkg/bindings/containers/types_restart_options.go +++ b/pkg/bindings/containers/types_restart_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:21.076709643 -0600 CST m=+0.000303354 */ // Changed @@ -39,6 +38,7 @@ func (o *RestartOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_restore_options.go b/pkg/bindings/containers/types_restore_options.go index be6e94736..491d678a5 100644 --- a/pkg/bindings/containers/types_restore_options.go +++ b/pkg/bindings/containers/types_restore_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:18.861536405 -0600 CST m=+0.000300026 */ // Changed @@ -39,6 +38,7 @@ func (o *RestoreOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_shouldrestart_options.go b/pkg/bindings/containers/types_shouldrestart_options.go index c833d0d8b..30ab618c7 100644 --- a/pkg/bindings/containers/types_shouldrestart_options.go +++ b/pkg/bindings/containers/types_shouldrestart_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:22.388596051 -0600 CST m=+0.000253693 */ // Changed @@ -39,6 +38,7 @@ func (o *ShouldRestartOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_start_options.go b/pkg/bindings/containers/types_start_options.go index 5918af89b..4050a8993 100644 --- a/pkg/bindings/containers/types_start_options.go +++ b/pkg/bindings/containers/types_start_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:21.221364502 -0600 CST m=+0.000276575 */ // Changed @@ -39,6 +38,7 @@ func (o *StartOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_stats_options.go b/pkg/bindings/containers/types_stats_options.go index f821ea1cd..74f419913 100644 --- a/pkg/bindings/containers/types_stats_options.go +++ b/pkg/bindings/containers/types_stats_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:21.370213399 -0600 CST m=+0.000264334 */ // Changed @@ -39,6 +38,7 @@ func (o *StatsOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_stop_options.go b/pkg/bindings/containers/types_stop_options.go index 14d7633a0..db692dbf0 100644 --- a/pkg/bindings/containers/types_stop_options.go +++ b/pkg/bindings/containers/types_stop_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:21.95469621 -0600 CST m=+0.000261399 */ // Changed @@ -39,6 +38,7 @@ func (o *StopOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_top_options.go b/pkg/bindings/containers/types_top_options.go index 95a1ee686..5f2717c28 100644 --- a/pkg/bindings/containers/types_top_options.go +++ b/pkg/bindings/containers/types_top_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:21.515867629 -0600 CST m=+0.000257106 */ // Changed @@ -39,6 +38,7 @@ func (o *TopOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_unmount_options.go b/pkg/bindings/containers/types_unmount_options.go index a29bd8216..060327c4a 100644 --- a/pkg/bindings/containers/types_unmount_options.go +++ b/pkg/bindings/containers/types_unmount_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:19.891657824 -0600 CST m=+0.000326668 */ // Changed @@ -39,6 +38,7 @@ func (o *UnmountOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_unpause_options.go b/pkg/bindings/containers/types_unpause_options.go index 44c077df2..e02bf2c95 100644 --- a/pkg/bindings/containers/types_unpause_options.go +++ b/pkg/bindings/containers/types_unpause_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:21.661356277 -0600 CST m=+0.000262608 */ // Changed @@ -39,6 +38,7 @@ func (o *UnpauseOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/containers/types_wait_options.go b/pkg/bindings/containers/types_wait_options.go index 18d36c377..470d67611 100644 --- a/pkg/bindings/containers/types_wait_options.go +++ b/pkg/bindings/containers/types_wait_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" "github.com/containers/podman/v2/libpod/define" jsoniter "github.com/json-iterator/go" @@ -12,8 +13,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 13:33:21.809397978 -0600 CST m=+0.000267049 */ // Changed @@ -40,6 +39,7 @@ func (o *WaitOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/errors.go b/pkg/bindings/errors.go index 603299389..e75ce898d 100644 --- a/pkg/bindings/errors.go +++ b/pkg/bindings/errors.go @@ -4,7 +4,7 @@ import ( "encoding/json" "io/ioutil" - "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/errorhandling" "github.com/pkg/errors" ) @@ -13,7 +13,7 @@ var ( ) func handleError(data []byte) error { - e := entities.ErrorModel{} + e := errorhandling.ErrorModel{} if err := json.Unmarshal(data, &e); err != nil { return err } @@ -36,7 +36,7 @@ func (a APIResponse) Process(unmarshalInto interface{}) error { } func CheckResponseCode(inError error) (int, error) { - e, ok := inError.(entities.ErrorModel) + e, ok := inError.(errorhandling.ErrorModel) if !ok { return -1, errors.New("error is not type ErrorModel") } diff --git a/pkg/bindings/generate/types_kube_options.go b/pkg/bindings/generate/types_kube_options.go index 68488aaee..5fb965c9f 100644 --- a/pkg/bindings/generate/types_kube_options.go +++ b/pkg/bindings/generate/types_kube_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:20.522950566 -0600 CST m=+0.000154384 */ // Changed @@ -39,6 +38,7 @@ func (o *KubeOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/generate/types_systemd_options.go b/pkg/bindings/generate/types_systemd_options.go index 0e8a46aa0..ce7286b3a 100644 --- a/pkg/bindings/generate/types_systemd_options.go +++ b/pkg/bindings/generate/types_systemd_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:20.661450253 -0600 CST m=+0.000135779 */ // Changed @@ -39,6 +38,7 @@ func (o *SystemdOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/generator/generator.go b/pkg/bindings/generator/generator.go index 8c79aebae..6a7f600a8 100644 --- a/pkg/bindings/generator/generator.go +++ b/pkg/bindings/generator/generator.go @@ -19,13 +19,10 @@ var bodyTmpl = `package {{.PackageName}} import ( {{range $import := .Imports}} {{$import}} {{end}} - ) /* This file is generated automatically by go generate. Do not edit. - -Created {{.Date}} */ // Changed @@ -52,6 +49,7 @@ func (o *{{.StructName}}) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() @@ -239,7 +237,7 @@ func main() { closed = true // go fmt file - gofmt := exec.Command("gofmt", "-w", "-s", out.Name()) + gofmt := exec.Command("go", "fmt", out.Name()) gofmt.Stderr = os.Stdout if err := gofmt.Run(); err != nil { fmt.Println(err) diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go index 9beb493c8..ae6962c8c 100644 --- a/pkg/bindings/images/images.go +++ b/pkg/bindings/images/images.go @@ -12,6 +12,7 @@ import ( "github.com/containers/podman/v2/pkg/auth" "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/pkg/errors" ) @@ -112,20 +113,13 @@ func History(ctx context.Context, nameOrID string, options *HistoryOptions) ([]* return history, response.Process(&history) } -func Load(ctx context.Context, r io.Reader, options *LoadOptions) (*entities.ImageLoadReport, error) { - if options == nil { - options = new(LoadOptions) - } +func Load(ctx context.Context, r io.Reader) (*entities.ImageLoadReport, error) { var report entities.ImageLoadReport conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params, err := options.ToParams() - if err != nil { - return nil, err - } - response, err := conn.DoRequest(r, http.MethodPost, "/images/load", params, nil) + response, err := conn.DoRequest(r, http.MethodPost, "/images/load", nil, nil) if err != nil { return nil, err } @@ -163,9 +157,9 @@ func Export(ctx context.Context, nameOrIDs []string, w io.Writer, options *Expor // Prune removes unused images from local storage. The optional filters can be used to further // define which images should be pruned. -func Prune(ctx context.Context, options *PruneOptions) ([]string, error) { +func Prune(ctx context.Context, options *PruneOptions) ([]*reports.PruneReport, error) { var ( - deleted []string + deleted []*reports.PruneReport ) if options == nil { options = new(PruneOptions) @@ -182,7 +176,8 @@ func Prune(ctx context.Context, options *PruneOptions) ([]string, error) { if err != nil { return deleted, err } - return deleted, response.Process(&deleted) + err = response.Process(&deleted) + return deleted, err } // Tag adds an additional name to locally-stored image. Both the tag and repo parameters are required. diff --git a/pkg/bindings/images/types_diff_options.go b/pkg/bindings/images/types_diff_options.go index d27c8945e..34a5bf2df 100644 --- a/pkg/bindings/images/types_diff_options.go +++ b/pkg/bindings/images/types_diff_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:26.320022698 -0600 CST m=+0.000277796 */ // Changed @@ -39,6 +38,7 @@ func (o *DiffOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_export_options.go b/pkg/bindings/images/types_export_options.go index 078b27fc0..172cb2b5c 100644 --- a/pkg/bindings/images/types_export_options.go +++ b/pkg/bindings/images/types_export_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:27.173810543 -0600 CST m=+0.000239871 */ // Changed @@ -39,6 +38,7 @@ func (o *ExportOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_get_options.go b/pkg/bindings/images/types_get_options.go index 1161657f7..c91ddb170 100644 --- a/pkg/bindings/images/types_get_options.go +++ b/pkg/bindings/images/types_get_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:26.609005517 -0600 CST m=+0.000241828 */ // Changed @@ -39,6 +38,7 @@ func (o *GetOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_history_options.go b/pkg/bindings/images/types_history_options.go index 6f9854e03..bd4224cd8 100644 --- a/pkg/bindings/images/types_history_options.go +++ b/pkg/bindings/images/types_history_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:26.890854681 -0600 CST m=+0.000243668 */ // Changed @@ -39,6 +38,7 @@ func (o *HistoryOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_import_options.go b/pkg/bindings/images/types_import_options.go index f5e6c8f7e..81eda946e 100644 --- a/pkg/bindings/images/types_import_options.go +++ b/pkg/bindings/images/types_import_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:27.740585278 -0600 CST m=+0.000340441 */ // Changed @@ -39,6 +38,7 @@ func (o *ImportOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_list_options.go b/pkg/bindings/images/types_list_options.go index 209d72e34..5dc4242fc 100644 --- a/pkg/bindings/images/types_list_options.go +++ b/pkg/bindings/images/types_list_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:26.462967928 -0600 CST m=+0.000289760 */ // Changed @@ -39,6 +38,7 @@ func (o *ListOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_load_options.go b/pkg/bindings/images/types_load_options.go index 6bba573d4..7bbd56c09 100644 --- a/pkg/bindings/images/types_load_options.go +++ b/pkg/bindings/images/types_load_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:27.031848205 -0600 CST m=+0.000279409 */ // Changed @@ -39,6 +38,7 @@ func (o *LoadOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_prune_options.go b/pkg/bindings/images/types_prune_options.go index c29fdae12..c290bb379 100644 --- a/pkg/bindings/images/types_prune_options.go +++ b/pkg/bindings/images/types_prune_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:27.316938584 -0600 CST m=+0.000239843 */ // Changed @@ -39,6 +38,7 @@ func (o *PruneOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_pull_options.go b/pkg/bindings/images/types_pull_options.go index 07f3e079d..5163a6341 100644 --- a/pkg/bindings/images/types_pull_options.go +++ b/pkg/bindings/images/types_pull_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" "github.com/containers/common/pkg/config" jsoniter "github.com/json-iterator/go" @@ -12,8 +13,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:28.164648348 -0600 CST m=+0.000243264 */ // Changed @@ -40,6 +39,7 @@ func (o *PullOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_push_options.go b/pkg/bindings/images/types_push_options.go index f9ce1b835..15210f30b 100644 --- a/pkg/bindings/images/types_push_options.go +++ b/pkg/bindings/images/types_push_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:27.881232044 -0600 CST m=+0.000242458 */ // Changed @@ -39,6 +38,7 @@ func (o *PushOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_remove_options.go b/pkg/bindings/images/types_remove_options.go index c9692c2b7..66a6bea7d 100644 --- a/pkg/bindings/images/types_remove_options.go +++ b/pkg/bindings/images/types_remove_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:26.180391541 -0600 CST m=+0.000290244 */ // Changed @@ -39,6 +38,7 @@ func (o *RemoveOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_search_options.go b/pkg/bindings/images/types_search_options.go index e6168ac33..299d27505 100644 --- a/pkg/bindings/images/types_search_options.go +++ b/pkg/bindings/images/types_search_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:28.023569573 -0600 CST m=+0.000245548 */ // Changed @@ -39,6 +38,7 @@ func (o *SearchOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_tag_options.go b/pkg/bindings/images/types_tag_options.go index f6396e590..40cd4a35b 100644 --- a/pkg/bindings/images/types_tag_options.go +++ b/pkg/bindings/images/types_tag_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:27.457906682 -0600 CST m=+0.000245071 */ // Changed @@ -39,6 +38,7 @@ func (o *TagOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_tree_options.go b/pkg/bindings/images/types_tree_options.go index fb2493f85..a671fa4e0 100644 --- a/pkg/bindings/images/types_tree_options.go +++ b/pkg/bindings/images/types_tree_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:26.749625305 -0600 CST m=+0.000267624 */ // Changed @@ -39,6 +38,7 @@ func (o *TreeOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/images/types_untag_options.go b/pkg/bindings/images/types_untag_options.go index 8faf5c14e..e38c5f18e 100644 --- a/pkg/bindings/images/types_untag_options.go +++ b/pkg/bindings/images/types_untag_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:27.600379913 -0600 CST m=+0.000251449 */ // Changed @@ -39,6 +38,7 @@ func (o *UntagOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/manifests/types_add_options.go b/pkg/bindings/manifests/types_add_options.go index d45effedc..1e588c668 100644 --- a/pkg/bindings/manifests/types_add_options.go +++ b/pkg/bindings/manifests/types_add_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:57:55.92237379 -0600 CST m=+0.000150701 */ // Changed @@ -39,6 +38,7 @@ func (o *AddOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/manifests/types_create_options.go b/pkg/bindings/manifests/types_create_options.go index da07f1abf..3a564a92b 100644 --- a/pkg/bindings/manifests/types_create_options.go +++ b/pkg/bindings/manifests/types_create_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:57:55.784206871 -0600 CST m=+0.000157049 */ // Changed @@ -39,6 +38,7 @@ func (o *CreateOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/manifests/types_inspect_options.go b/pkg/bindings/manifests/types_inspect_options.go index 0d32d5bb9..2af4190d4 100644 --- a/pkg/bindings/manifests/types_inspect_options.go +++ b/pkg/bindings/manifests/types_inspect_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:57:55.631746481 -0600 CST m=+0.000143104 */ // Changed @@ -39,6 +38,7 @@ func (o *InspectOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/manifests/types_push_options.go b/pkg/bindings/manifests/types_push_options.go index 4226733c9..1d689f699 100644 --- a/pkg/bindings/manifests/types_push_options.go +++ b/pkg/bindings/manifests/types_push_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:57:56.197438009 -0600 CST m=+0.000149060 */ // Changed @@ -39,6 +38,7 @@ func (o *PushOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/manifests/types_remove_options.go b/pkg/bindings/manifests/types_remove_options.go index f99220f6c..3b35c38b8 100644 --- a/pkg/bindings/manifests/types_remove_options.go +++ b/pkg/bindings/manifests/types_remove_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:57:56.059954408 -0600 CST m=+0.000146015 */ // Changed @@ -39,6 +38,7 @@ func (o *RemoveOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/network/types_connect_options.go b/pkg/bindings/network/types_connect_options.go index 6fcc4536e..b6081ba57 100644 --- a/pkg/bindings/network/types_connect_options.go +++ b/pkg/bindings/network/types_connect_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:34.075822786 -0600 CST m=+0.000167237 */ // Changed @@ -39,6 +38,7 @@ func (o *ConnectOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/network/types_create_options.go b/pkg/bindings/network/types_create_options.go index daec6a254..5b0abe870 100644 --- a/pkg/bindings/network/types_create_options.go +++ b/pkg/bindings/network/types_create_options.go @@ -5,6 +5,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -12,8 +13,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:33.37307678 -0600 CST m=+0.000176739 */ // Changed @@ -40,6 +39,7 @@ func (o *CreateOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() @@ -193,9 +193,9 @@ func (o *CreateOptions) WithIPRange(value net.IPNet) *CreateOptions { // GetIPRange func (o *CreateOptions) GetIPRange() net.IPNet { - var ipRange net.IPNet + var iPRange net.IPNet if o.IPRange == nil { - return ipRange + return iPRange } return *o.IPRange } diff --git a/pkg/bindings/network/types_disconnect_options.go b/pkg/bindings/network/types_disconnect_options.go index 821279976..8b2a9cb71 100644 --- a/pkg/bindings/network/types_disconnect_options.go +++ b/pkg/bindings/network/types_disconnect_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:33.936088242 -0600 CST m=+0.000168680 */ // Changed @@ -39,6 +38,7 @@ func (o *DisconnectOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/network/types_inspect_options.go b/pkg/bindings/network/types_inspect_options.go index 7704904ce..cec5ef7b2 100644 --- a/pkg/bindings/network/types_inspect_options.go +++ b/pkg/bindings/network/types_inspect_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:33.520438264 -0600 CST m=+0.000172934 */ // Changed @@ -39,6 +38,7 @@ func (o *InspectOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/network/types_list_options.go b/pkg/bindings/network/types_list_options.go index bbd2a71db..6a33fb7b6 100644 --- a/pkg/bindings/network/types_list_options.go +++ b/pkg/bindings/network/types_list_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:33.796794129 -0600 CST m=+0.000180368 */ // Changed @@ -39,6 +38,7 @@ func (o *ListOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/network/types_remove_options.go b/pkg/bindings/network/types_remove_options.go index b24835ae4..861fe1f2c 100644 --- a/pkg/bindings/network/types_remove_options.go +++ b/pkg/bindings/network/types_remove_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:33.658228151 -0600 CST m=+0.000172527 */ // Changed @@ -39,6 +38,7 @@ func (o *RemoveOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/play/types_kube_options.go b/pkg/bindings/play/types_kube_options.go index 91cdd30aa..5aec4b479 100644 --- a/pkg/bindings/play/types_kube_options.go +++ b/pkg/bindings/play/types_kube_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:02.386833736 -0600 CST m=+0.000171080 */ // Changed @@ -39,6 +38,7 @@ func (o *KubeOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_create_options.go b/pkg/bindings/pods/types_create_options.go index b6cf0fc53..b501d1151 100644 --- a/pkg/bindings/pods/types_create_options.go +++ b/pkg/bindings/pods/types_create_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:40.05490508 -0600 CST m=+0.000156396 */ // Changed @@ -39,6 +38,7 @@ func (o *CreateOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_inspect_options.go b/pkg/bindings/pods/types_inspect_options.go index 1c881ce9c..a2eb25fef 100644 --- a/pkg/bindings/pods/types_inspect_options.go +++ b/pkg/bindings/pods/types_inspect_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:40.258801519 -0600 CST m=+0.000175055 */ // Changed @@ -39,6 +38,7 @@ func (o *InspectOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_kill_options.go b/pkg/bindings/pods/types_kill_options.go index cb5bdfd01..f9cad3579 100644 --- a/pkg/bindings/pods/types_kill_options.go +++ b/pkg/bindings/pods/types_kill_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:40.398857339 -0600 CST m=+0.000160135 */ // Changed @@ -39,6 +38,7 @@ func (o *KillOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_list_options.go b/pkg/bindings/pods/types_list_options.go index d095bf3db..02e7adf2d 100644 --- a/pkg/bindings/pods/types_list_options.go +++ b/pkg/bindings/pods/types_list_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:40.818123838 -0600 CST m=+0.000164328 */ // Changed @@ -39,6 +38,7 @@ func (o *ListOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_pause_options.go b/pkg/bindings/pods/types_pause_options.go index 06ee6f81d..2e4fdb4a6 100644 --- a/pkg/bindings/pods/types_pause_options.go +++ b/pkg/bindings/pods/types_pause_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:40.538407099 -0600 CST m=+0.000193274 */ // Changed @@ -39,6 +38,7 @@ func (o *PauseOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_prune_options.go b/pkg/bindings/pods/types_prune_options.go index 6610aa7cc..616ad6dc9 100644 --- a/pkg/bindings/pods/types_prune_options.go +++ b/pkg/bindings/pods/types_prune_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:40.678507342 -0600 CST m=+0.000183891 */ // Changed @@ -39,6 +38,7 @@ func (o *PruneOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_remove_options.go b/pkg/bindings/pods/types_remove_options.go index 8f18e43c9..6960d8839 100644 --- a/pkg/bindings/pods/types_remove_options.go +++ b/pkg/bindings/pods/types_remove_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:41.80320679 -0600 CST m=+0.000158149 */ // Changed @@ -39,6 +38,7 @@ func (o *RemoveOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_restart_options.go b/pkg/bindings/pods/types_restart_options.go index 9030de1e7..427833044 100644 --- a/pkg/bindings/pods/types_restart_options.go +++ b/pkg/bindings/pods/types_restart_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:40.958315383 -0600 CST m=+0.000168360 */ // Changed @@ -39,6 +38,7 @@ func (o *RestartOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_start_options.go b/pkg/bindings/pods/types_start_options.go index 0fce21099..e98798459 100644 --- a/pkg/bindings/pods/types_start_options.go +++ b/pkg/bindings/pods/types_start_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:41.099102916 -0600 CST m=+0.000159629 */ // Changed @@ -39,6 +38,7 @@ func (o *StartOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_stats_options.go b/pkg/bindings/pods/types_stats_options.go index d38a9a115..845a534a3 100644 --- a/pkg/bindings/pods/types_stats_options.go +++ b/pkg/bindings/pods/types_stats_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:41.658228243 -0600 CST m=+0.000160769 */ // Changed @@ -39,6 +38,7 @@ func (o *StatsOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_stop_options.go b/pkg/bindings/pods/types_stop_options.go index ac698b8c5..86000eb57 100644 --- a/pkg/bindings/pods/types_stop_options.go +++ b/pkg/bindings/pods/types_stop_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:41.237892781 -0600 CST m=+0.000155040 */ // Changed @@ -39,6 +38,7 @@ func (o *StopOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_top_options.go b/pkg/bindings/pods/types_top_options.go index 895f62957..ada0b1e25 100644 --- a/pkg/bindings/pods/types_top_options.go +++ b/pkg/bindings/pods/types_top_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:41.375876994 -0600 CST m=+0.000154839 */ // Changed @@ -39,6 +38,7 @@ func (o *TopOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/pods/types_unpause_options.go b/pkg/bindings/pods/types_unpause_options.go index 3d647cf25..6a9ee8fcd 100644 --- a/pkg/bindings/pods/types_unpause_options.go +++ b/pkg/bindings/pods/types_unpause_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:41.515225789 -0600 CST m=+0.000158667 */ // Changed @@ -39,6 +38,7 @@ func (o *UnpauseOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/system/types_disk_options.go b/pkg/bindings/system/types_disk_options.go index f7d2cca06..c5eb2f94c 100644 --- a/pkg/bindings/system/types_disk_options.go +++ b/pkg/bindings/system/types_disk_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:08.087362343 -0600 CST m=+0.000150636 */ // Changed @@ -39,6 +38,7 @@ func (o *DiskOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/system/types_events_options.go b/pkg/bindings/system/types_events_options.go index 6dd64b055..2e95339e6 100644 --- a/pkg/bindings/system/types_events_options.go +++ b/pkg/bindings/system/types_events_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:07.675150173 -0600 CST m=+0.000140977 */ // Changed @@ -39,6 +38,7 @@ func (o *EventsOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/system/types_info_options.go b/pkg/bindings/system/types_info_options.go index 3a8960e1b..263513b0f 100644 --- a/pkg/bindings/system/types_info_options.go +++ b/pkg/bindings/system/types_info_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:08.233760126 -0600 CST m=+0.000142369 */ // Changed @@ -39,6 +38,7 @@ func (o *InfoOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/system/types_prune_options.go b/pkg/bindings/system/types_prune_options.go index 2cd3c8000..a9a6a6cda 100644 --- a/pkg/bindings/system/types_prune_options.go +++ b/pkg/bindings/system/types_prune_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:07.812719858 -0600 CST m=+0.000143214 */ // Changed @@ -39,6 +38,7 @@ func (o *PruneOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/system/types_version_options.go b/pkg/bindings/system/types_version_options.go index 4974e8d8f..be07581fa 100644 --- a/pkg/bindings/system/types_version_options.go +++ b/pkg/bindings/system/types_version_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:07.950759332 -0600 CST m=+0.000140376 */ // Changed @@ -39,6 +38,7 @@ func (o *VersionOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/test/common_test.go b/pkg/bindings/test/common_test.go index 232d7136f..c2b1347d2 100644 --- a/pkg/bindings/test/common_test.go +++ b/pkg/bindings/test/common_test.go @@ -182,7 +182,7 @@ func (b *bindingTest) RestoreImagesFromCache() { } } func (b *bindingTest) restoreImageFromCache(i testImage) { - p := b.runPodman([]string{"load", "-i", filepath.Join(ImageCacheDir, i.tarballName), i.name}) + p := b.runPodman([]string{"load", "-i", filepath.Join(ImageCacheDir, i.tarballName)}) p.Wait(45) } diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go index 2ab5e45d0..fa601e7e5 100644 --- a/pkg/bindings/test/containers_test.go +++ b/pkg/bindings/test/containers_test.go @@ -8,6 +8,7 @@ import ( "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/bindings/containers" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/containers/podman/v2/pkg/specgen" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -533,8 +534,8 @@ var _ = Describe("Podman containers ", func() { // Prune container should return no errors and one pruned container ID. pruneResponse, err := containers.Prune(bt.conn, nil) Expect(err).To(BeNil()) - Expect(len(pruneResponse.Err)).To(Equal(0)) - Expect(len(pruneResponse.ID)).To(Equal(1)) + Expect(len(reports.PruneReportsErrs(pruneResponse))).To(Equal(0)) + Expect(len(reports.PruneReportsIds(pruneResponse))).To(Equal(1)) }) It("podman prune stopped containers with filters", func() { @@ -558,8 +559,8 @@ var _ = Describe("Podman containers ", func() { } pruneResponse, err = containers.Prune(bt.conn, new(containers.PruneOptions).WithFilters(filtersIncorrect)) Expect(err).To(BeNil()) - Expect(len(pruneResponse.Err)).To(Equal(0)) - Expect(len(pruneResponse.ID)).To(Equal(0)) + Expect(len(reports.PruneReportsIds(pruneResponse))).To(Equal(0)) + Expect(len(reports.PruneReportsErrs(pruneResponse))).To(Equal(0)) // Valid filter params container should be pruned now. filters := map[string][]string{ @@ -567,8 +568,8 @@ var _ = Describe("Podman containers ", func() { } pruneResponse, err = containers.Prune(bt.conn, new(containers.PruneOptions).WithFilters(filters)) Expect(err).To(BeNil()) - Expect(len(pruneResponse.Err)).To(Equal(0)) - Expect(len(pruneResponse.ID)).To(Equal(1)) + Expect(len(reports.PruneReportsErrs(pruneResponse))).To(Equal(0)) + Expect(len(reports.PruneReportsIds(pruneResponse))).To(Equal(1)) }) It("podman prune running containers", func() { @@ -585,7 +586,7 @@ var _ = Describe("Podman containers ", func() { // Prune. Should return no error no prune response ID. pruneResponse, err := containers.Prune(bt.conn, nil) Expect(err).To(BeNil()) - Expect(len(pruneResponse.ID)).To(Equal(0)) + Expect(len(pruneResponse)).To(Equal(0)) }) It("podman inspect bogus container", func() { diff --git a/pkg/bindings/test/images_test.go b/pkg/bindings/test/images_test.go index e178f4219..81959e813 100644 --- a/pkg/bindings/test/images_test.go +++ b/pkg/bindings/test/images_test.go @@ -9,6 +9,7 @@ import ( "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/bindings/containers" "github.com/containers/podman/v2/pkg/bindings/images" + dreports "github.com/containers/podman/v2/pkg/domain/entities/reports" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/onsi/gomega/gexec" @@ -218,7 +219,7 @@ var _ = Describe("Podman images", func() { f, err := os.Open(filepath.Join(ImageCacheDir, alpine.tarballName)) defer f.Close() Expect(err).To(BeNil()) - names, err := images.Load(bt.conn, f, nil) + names, err := images.Load(bt.conn, f) Expect(err).To(BeNil()) Expect(names.Names[0]).To(Equal(alpine.name)) exists, err = images.Exists(bt.conn, alpine.name) @@ -233,14 +234,9 @@ var _ = Describe("Podman images", func() { exists, err = images.Exists(bt.conn, alpine.name) Expect(err).To(BeNil()) Expect(exists).To(BeFalse()) - newName := "quay.io/newname:fizzle" - options := new(images.LoadOptions).WithReference(newName) - names, err = images.Load(bt.conn, f, options) + names, err = images.Load(bt.conn, f) Expect(err).To(BeNil()) Expect(names.Names[0]).To(Equal(alpine.name)) - exists, err = images.Exists(bt.conn, newName) - Expect(err).To(BeNil()) - Expect(exists).To(BeTrue()) // load with a bad repo name should trigger a 500 f, err = os.Open(filepath.Join(ImageCacheDir, alpine.tarballName)) @@ -250,11 +246,6 @@ var _ = Describe("Podman images", func() { exists, err = images.Exists(bt.conn, alpine.name) Expect(err).To(BeNil()) Expect(exists).To(BeFalse()) - options = new(images.LoadOptions).WithReference("quay.io/newName:fizzle") - _, err = images.Load(bt.conn, f, options) - Expect(err).ToNot(BeNil()) - code, _ := bindings.CheckResponseCode(err) - Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) }) It("Export Image", func() { @@ -355,7 +346,7 @@ var _ = Describe("Podman images", func() { results, err := images.Prune(bt.conn, options) Expect(err).NotTo(HaveOccurred()) Expect(len(results)).To(BeNumerically(">", 0)) - Expect(results).To(ContainElement("docker.io/library/alpine:latest")) + Expect(dreports.PruneReportsIds(results)).To(ContainElement("docker.io/library/alpine:latest")) }) // TODO: we really need to extent to pull tests once we have a more sophisticated CI. diff --git a/pkg/bindings/test/system_test.go b/pkg/bindings/test/system_test.go index 25fda5575..44067b61d 100644 --- a/pkg/bindings/test/system_test.go +++ b/pkg/bindings/test/system_test.go @@ -10,6 +10,7 @@ import ( "github.com/containers/podman/v2/pkg/bindings/system" "github.com/containers/podman/v2/pkg/bindings/volumes" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/onsi/gomega/gexec" @@ -80,12 +81,12 @@ var _ = Describe("Podman system", func() { systemPruneResponse, err := system.Prune(bt.conn, options) Expect(err).To(BeNil()) Expect(len(systemPruneResponse.PodPruneReport)).To(Equal(1)) - Expect(len(systemPruneResponse.ContainerPruneReport.ID)).To(Equal(1)) - Expect(len(systemPruneResponse.ImagePruneReport.Report.Id)). + Expect(len(systemPruneResponse.ContainerPruneReports)).To(Equal(1)) + Expect(len(systemPruneResponse.ImagePruneReports)). To(BeNumerically(">", 0)) - Expect(systemPruneResponse.ImagePruneReport.Report.Id). + Expect(reports.PruneReportsIds(systemPruneResponse.ImagePruneReports)). To(ContainElement("docker.io/library/alpine:latest")) - Expect(len(systemPruneResponse.VolumePruneReport)).To(Equal(0)) + Expect(len(systemPruneResponse.VolumePruneReports)).To(Equal(0)) }) It("podman system prune running alpine container", func() { @@ -114,14 +115,14 @@ var _ = Describe("Podman system", func() { systemPruneResponse, err := system.Prune(bt.conn, options) Expect(err).To(BeNil()) Expect(len(systemPruneResponse.PodPruneReport)).To(Equal(1)) - Expect(len(systemPruneResponse.ContainerPruneReport.ID)).To(Equal(1)) - Expect(len(systemPruneResponse.ImagePruneReport.Report.Id)). + Expect(len(systemPruneResponse.ContainerPruneReports)).To(Equal(1)) + Expect(len(systemPruneResponse.ImagePruneReports)). To(BeNumerically(">", 0)) // Alpine image should not be pruned as used by running container - Expect(systemPruneResponse.ImagePruneReport.Report.Id). + Expect(reports.PruneReportsIds(systemPruneResponse.ImagePruneReports)). ToNot(ContainElement("docker.io/library/alpine:latest")) // Though unused volume is available it should not be pruned as flag set to false. - Expect(len(systemPruneResponse.VolumePruneReport)).To(Equal(0)) + Expect(len(systemPruneResponse.VolumePruneReports)).To(Equal(0)) }) It("podman system prune running alpine container volume prune", func() { @@ -149,14 +150,14 @@ var _ = Describe("Podman system", func() { systemPruneResponse, err := system.Prune(bt.conn, options) Expect(err).To(BeNil()) Expect(len(systemPruneResponse.PodPruneReport)).To(Equal(0)) - Expect(len(systemPruneResponse.ContainerPruneReport.ID)).To(Equal(1)) - Expect(len(systemPruneResponse.ImagePruneReport.Report.Id)). + Expect(len(systemPruneResponse.ContainerPruneReports)).To(Equal(1)) + Expect(len(systemPruneResponse.ImagePruneReports)). To(BeNumerically(">", 0)) // Alpine image should not be pruned as used by running container - Expect(systemPruneResponse.ImagePruneReport.Report.Id). + Expect(reports.PruneReportsIds(systemPruneResponse.ImagePruneReports)). ToNot(ContainElement("docker.io/library/alpine:latest")) // Volume should be pruned now as flag set true - Expect(len(systemPruneResponse.VolumePruneReport)).To(Equal(1)) + Expect(len(systemPruneResponse.VolumePruneReports)).To(Equal(1)) }) It("podman system prune running alpine container volume prune --filter", func() { @@ -197,14 +198,14 @@ var _ = Describe("Podman system", func() { // This check **should** be "Equal(0)" since we are passing label // filters however the Prune function doesn't seem to pass filters // to each component. - Expect(len(systemPruneResponse.ContainerPruneReport.ID)).To(Equal(1)) - Expect(len(systemPruneResponse.ImagePruneReport.Report.Id)). + Expect(len(systemPruneResponse.ContainerPruneReports)).To(Equal(1)) + Expect(len(systemPruneResponse.ImagePruneReports)). To(BeNumerically(">", 0)) // Alpine image should not be pruned as used by running container - Expect(systemPruneResponse.ImagePruneReport.Report.Id). + Expect(reports.PruneReportsIds(systemPruneResponse.ImagePruneReports)). ToNot(ContainElement("docker.io/library/alpine:latest")) // Volume shouldn't be pruned because the PruneOptions filters doesn't match - Expect(len(systemPruneResponse.VolumePruneReport)).To(Equal(0)) + Expect(len(systemPruneResponse.VolumePruneReports)).To(Equal(0)) // Fix filter and re prune f["label"] = []string{"label1=value1"} @@ -213,6 +214,6 @@ var _ = Describe("Podman system", func() { Expect(err).To(BeNil()) // Volume should be pruned because the PruneOptions filters now match - Expect(len(systemPruneResponse.VolumePruneReport)).To(Equal(1)) + Expect(len(systemPruneResponse.VolumePruneReports)).To(Equal(1)) }) }) diff --git a/pkg/bindings/test/volumes_test.go b/pkg/bindings/test/volumes_test.go index e0d854b66..1f1da3cfa 100644 --- a/pkg/bindings/test/volumes_test.go +++ b/pkg/bindings/test/volumes_test.go @@ -10,6 +10,7 @@ import ( "github.com/containers/podman/v2/pkg/bindings/containers" "github.com/containers/podman/v2/pkg/bindings/volumes" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/onsi/gomega/gexec" @@ -166,7 +167,7 @@ var _ = Describe("Podman volumes", func() { session.Wait(45) vols, err = volumes.Prune(connText, nil) Expect(err).To(BeNil()) - Expect(len(vols)).To(BeNumerically("==", 1)) + Expect(len(reports.PruneReportsIds(vols))).To(BeNumerically("==", 1)) _, err = volumes.Inspect(connText, "homer", nil) Expect(err).To(BeNil()) diff --git a/pkg/bindings/volumes/types_create_options.go b/pkg/bindings/volumes/types_create_options.go index 80bdac2d2..171090afe 100644 --- a/pkg/bindings/volumes/types_create_options.go +++ b/pkg/bindings/volumes/types_create_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:14.043860791 -0600 CST m=+0.000188944 */ // Changed @@ -39,6 +38,7 @@ func (o *CreateOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/volumes/types_inspect_options.go b/pkg/bindings/volumes/types_inspect_options.go index ba8c70b63..3a1d396a7 100644 --- a/pkg/bindings/volumes/types_inspect_options.go +++ b/pkg/bindings/volumes/types_inspect_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:14.189902005 -0600 CST m=+0.000151439 */ // Changed @@ -39,6 +38,7 @@ func (o *InspectOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/volumes/types_list_options.go b/pkg/bindings/volumes/types_list_options.go index 99dec132c..56033a575 100644 --- a/pkg/bindings/volumes/types_list_options.go +++ b/pkg/bindings/volumes/types_list_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:14.326721724 -0600 CST m=+0.000172471 */ // Changed @@ -39,6 +38,7 @@ func (o *ListOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/volumes/types_prune_options.go b/pkg/bindings/volumes/types_prune_options.go index cdbc03fc9..c043d69d0 100644 --- a/pkg/bindings/volumes/types_prune_options.go +++ b/pkg/bindings/volumes/types_prune_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:14.463307398 -0600 CST m=+0.000180868 */ // Changed @@ -39,6 +38,7 @@ func (o *PruneOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/volumes/types_remove_options.go b/pkg/bindings/volumes/types_remove_options.go index 923d1353c..1f8ba4e22 100644 --- a/pkg/bindings/volumes/types_remove_options.go +++ b/pkg/bindings/volumes/types_remove_options.go @@ -4,6 +4,7 @@ import ( "net/url" "reflect" "strconv" + "strings" jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" @@ -11,8 +12,6 @@ import ( /* This file is generated automatically by go generate. Do not edit. - -Created 2020-12-18 15:58:14.60278922 -0600 CST m=+0.000134408 */ // Changed @@ -39,6 +38,7 @@ func (o *RemoveOptions) ToParams() (url.Values, error) { if !o.Changed(fieldName) { continue } + fieldName = strings.ToLower(fieldName) f := s.Field(i) if reflect.Ptr == f.Kind() { f = f.Elem() diff --git a/pkg/bindings/volumes/volumes.go b/pkg/bindings/volumes/volumes.go index 626f52d39..fe081eb46 100644 --- a/pkg/bindings/volumes/volumes.go +++ b/pkg/bindings/volumes/volumes.go @@ -7,6 +7,7 @@ import ( "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" jsoniter "github.com/json-iterator/go" ) @@ -77,9 +78,9 @@ func List(ctx context.Context, options *ListOptions) ([]*entities.VolumeListRepo } // Prune removes unused volumes from the local filesystem. -func Prune(ctx context.Context, options *PruneOptions) ([]*entities.VolumePruneReport, error) { +func Prune(ctx context.Context, options *PruneOptions) ([]*reports.PruneReport, error) { var ( - pruned []*entities.VolumePruneReport + pruned []*reports.PruneReport ) conn, err := bindings.GetClient(ctx) if err != nil { diff --git a/pkg/checkpoint/checkpoint_restore.go b/pkg/checkpoint/checkpoint_restore.go index 9de04266f..f6cd3b38f 100644 --- a/pkg/checkpoint/checkpoint_restore.go +++ b/pkg/checkpoint/checkpoint_restore.go @@ -8,6 +8,7 @@ import ( "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/image" + "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/errorhandling" "github.com/containers/podman/v2/pkg/util" "github.com/containers/storage/pkg/archive" @@ -36,10 +37,10 @@ func crImportFromJSON(filePath string, v interface{}) error { // CRImportCheckpoint it the function which imports the information // from checkpoint tarball and re-creates the container from that information -func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input string, name string) ([]*libpod.Container, error) { +func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOptions entities.RestoreOptions) ([]*libpod.Container, error) { // First get the container definition from the // tarball to a temporary directory - archiveFile, err := os.Open(input) + archiveFile, err := os.Open(restoreOptions.Import) if err != nil { return nil, errors.Wrap(err, "failed to open checkpoint archive for import") } @@ -53,6 +54,7 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input stri "rootfs-diff.tar", "network.status", "deleted.files", + "volumes", }, } dir, err := ioutil.TempDir("", "checkpoint") @@ -66,7 +68,7 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input stri }() err = archive.Untar(archiveFile, dir, options) if err != nil { - return nil, errors.Wrapf(err, "Unpacking of checkpoint archive %s failed", input) + return nil, errors.Wrapf(err, "Unpacking of checkpoint archive %s failed", restoreOptions.Import) } // Load spec.dump from temporary directory @@ -82,17 +84,30 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, input stri } // This should not happen as checkpoints with these options are not exported. - if (len(config.Dependencies) > 0) || (len(config.NamedVolumes) > 0) { - return nil, errors.Errorf("Cannot import checkpoints of containers with named volumes or dependencies") + if len(config.Dependencies) > 0 { + return nil, errors.Errorf("Cannot import checkpoints of containers with dependencies") + } + + // Volumes included in the checkpoint should not exist + if !restoreOptions.IgnoreVolumes { + for _, vol := range config.NamedVolumes { + exists, err := runtime.HasVolume(vol.Name) + if err != nil { + return nil, err + } + if exists { + return nil, errors.Errorf("volume with name %s already exists. Use --ignore-volumes to not restore content of volumes", vol.Name) + } + } } ctrID := config.ID newName := false // Check if the restored container gets a new name - if name != "" { + if restoreOptions.Name != "" { config.ID = "" - config.Name = name + config.Name = restoreOptions.Name newName = true } diff --git a/pkg/domain/entities/container_ps.go b/pkg/domain/entities/container_ps.go index ff3b087ed..6709ca48a 100644 --- a/pkg/domain/entities/container_ps.go +++ b/pkg/domain/entities/container_ps.go @@ -43,6 +43,8 @@ type ListContainer struct { // Namespaces the container belongs to. Requires the // namespace boolean to be true Namespaces ListContainerNamespaces + // The network names assigned to the container + Networks []string // The process id of the container Pid int // If the container is part of Pod, the Pod ID. Requires the pod diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index b8d49d067..d8576c101 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -173,10 +173,13 @@ type CheckpointOptions struct { All bool Export string IgnoreRootFS bool + IgnoreVolumes bool Keep bool Latest bool LeaveRunning bool TCPEstablished bool + PreCheckPoint bool + WithPrevious bool } type CheckpointReport struct { @@ -187,6 +190,7 @@ type CheckpointReport struct { type RestoreOptions struct { All bool IgnoreRootFS bool + IgnoreVolumes bool IgnoreStaticIP bool IgnoreStaticMAC bool Import string @@ -194,6 +198,7 @@ type RestoreOptions struct { Latest bool Name string TCPEstablished bool + ImportPrevious string } type RestoreReport struct { @@ -390,13 +395,6 @@ type ContainerPruneOptions struct { Filters url.Values `json:"filters" schema:"filters"` } -// ContainerPruneReport describes the results after pruning the -// stopped containers. -type ContainerPruneReport struct { - ID map[string]int64 - Err map[string]error -} - // ContainerPortOptions describes the options to obtain // port information on containers type ContainerPortOptions struct { @@ -436,3 +434,9 @@ type ContainerStatsReport struct { // Results, set when there is no error. Stats []define.ContainerStats } + +// ContainerRenameOptions describes input options for renaming a container. +type ContainerRenameOptions struct { + // NewName is the new name that will be given to the container. + NewName string +} diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 80127ea45..d2552770c 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -6,6 +6,7 @@ import ( "github.com/containers/common/pkg/config" "github.com/containers/podman/v2/libpod/define" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/containers/podman/v2/pkg/specgen" "github.com/spf13/cobra" ) @@ -35,7 +36,8 @@ type ContainerEngine interface { ContainerMount(ctx context.Context, nameOrIDs []string, options ContainerMountOptions) ([]*ContainerMountReport, error) ContainerPause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error) ContainerPort(ctx context.Context, nameOrID string, options ContainerPortOptions) ([]*ContainerPortReport, error) - ContainerPrune(ctx context.Context, options ContainerPruneOptions) (*ContainerPruneReport, error) + ContainerPrune(ctx context.Context, options ContainerPruneOptions) ([]*reports.PruneReport, error) + ContainerRename(ctr context.Context, nameOrID string, options ContainerRenameOptions) error ContainerRestart(ctx context.Context, namesOrIds []string, options RestartOptions) ([]*RestartReport, error) ContainerRestore(ctx context.Context, namesOrIds []string, options RestoreOptions) ([]*RestoreReport, error) ContainerRm(ctx context.Context, namesOrIds []string, options RmOptions) ([]*RmReport, error) @@ -85,6 +87,6 @@ type ContainerEngine interface { VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IDOrNameResponse, error) VolumeInspect(ctx context.Context, namesOrIds []string, opts InspectOptions) ([]*VolumeInspectReport, []error, error) VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error) - VolumePrune(ctx context.Context, options VolumePruneOptions) ([]*VolumePruneReport, error) + VolumePrune(ctx context.Context, options VolumePruneOptions) ([]*reports.PruneReport, error) VolumeRm(ctx context.Context, namesOrIds []string, opts VolumeRmOptions) ([]*VolumeRmReport, error) } diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go index 7f33d8e9d..26a136f13 100644 --- a/pkg/domain/entities/engine_image.go +++ b/pkg/domain/entities/engine_image.go @@ -4,6 +4,7 @@ import ( "context" "github.com/containers/common/pkg/config" + "github.com/containers/podman/v2/pkg/domain/entities/reports" ) type ImageEngine interface { @@ -17,7 +18,7 @@ type ImageEngine interface { List(ctx context.Context, opts ImageListOptions) ([]*ImageSummary, error) Load(ctx context.Context, opts ImageLoadOptions) (*ImageLoadReport, error) Mount(ctx context.Context, images []string, options ImageMountOptions) ([]*ImageMountReport, error) - Prune(ctx context.Context, opts ImagePruneOptions) (*ImagePruneReport, error) + Prune(ctx context.Context, opts ImagePruneOptions) ([]*reports.PruneReport, 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) diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index 67910a34c..0805152c3 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -247,11 +247,6 @@ type ImagePruneOptions struct { Filter []string `json:"filter" schema:"filter"` } -type ImagePruneReport struct { - Report Report - Size int64 -} - type ImageTagOptions struct{} type ImageUntagOptions struct{} @@ -261,8 +256,6 @@ type ImageInspectReport struct { } type ImageLoadOptions struct { - Name string - Tag string Input string Quiet bool SignaturePolicy string diff --git a/pkg/domain/entities/play.go b/pkg/domain/entities/play.go index 0b42e1a3f..6883fe6c5 100644 --- a/pkg/domain/entities/play.go +++ b/pkg/domain/entities/play.go @@ -40,6 +40,9 @@ type PlayKubePod struct { Containers []string // Logs - non-fatal errors and log messages while processing. Logs []string + // ContainerErrors - any errors that occurred while starting containers + // in the pod. + ContainerErrors []string } // PlayKubeReport contains the results of running play kube. diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go index 426419833..32900d536 100644 --- a/pkg/domain/entities/pods.go +++ b/pkg/domain/entities/pods.go @@ -28,8 +28,10 @@ type ListPodsReport struct { InfraId string //nolint Name string Namespace string - Status string - Labels map[string]string + // Network names connected to infra container + Networks []string + Status string + Labels map[string]string } type ListPodContainer struct { @@ -210,15 +212,16 @@ type PodStatsOptions struct { // PodStatsReport includes pod-resource statistics data. type PodStatsReport struct { - CPU string - MemUsage string - Mem string - NetIO string - BlockIO string - PIDS string - Pod string - CID string - Name string + CPU string + MemUsage string + MemUsageBytes string + Mem string + NetIO string + BlockIO string + PIDS string + Pod string + CID string + Name string } // ValidatePodStatsOptions validates the specified slice and options. Allows diff --git a/pkg/domain/entities/reports/prune.go b/pkg/domain/entities/reports/prune.go new file mode 100644 index 000000000..5494ac3ae --- /dev/null +++ b/pkg/domain/entities/reports/prune.go @@ -0,0 +1,40 @@ +package reports + +type PruneReport struct { + Id string //nolint + Err error + Size uint64 +} + +func PruneReportsIds(r []*PruneReport) []string { + ids := make([]string, 0, len(r)) + for _, v := range r { + if v == nil || v.Id == "" { + continue + } + ids = append(ids, v.Id) + } + return ids +} + +func PruneReportsErrs(r []*PruneReport) []error { + errs := make([]error, 0, len(r)) + for _, v := range r { + if v == nil || v.Err == nil { + continue + } + errs = append(errs, v.Err) + } + return errs +} + +func PruneReportsSize(r []*PruneReport) uint64 { + size := uint64(0) + for _, v := range r { + if v == nil { + continue + } + size = size + v.Size + } + return size +} diff --git a/pkg/domain/entities/system.go b/pkg/domain/entities/system.go index d5118f6a8..99fa947f0 100644 --- a/pkg/domain/entities/system.go +++ b/pkg/domain/entities/system.go @@ -4,6 +4,7 @@ import ( "time" "github.com/containers/podman/v2/libpod/define" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/docker/docker/api/types" "github.com/spf13/cobra" ) @@ -24,10 +25,11 @@ type SystemPruneOptions struct { // SystemPruneReport provides report after system prune is executed. type SystemPruneReport struct { - PodPruneReport []*PodPruneReport - *ContainerPruneReport - *ImagePruneReport - VolumePruneReport []*VolumePruneReport + PodPruneReport []*PodPruneReport + ContainerPruneReports []*reports.PruneReport + ImagePruneReports []*reports.PruneReport + VolumePruneReports []*reports.PruneReport + ReclaimedSpace uint64 } // SystemMigrateOptions describes the options needed for the diff --git a/pkg/domain/entities/types.go b/pkg/domain/entities/types.go index 12135c2b1..e5473dc62 100644 --- a/pkg/domain/entities/types.go +++ b/pkg/domain/entities/types.go @@ -1,7 +1,6 @@ package entities import ( - "errors" "net" "github.com/containers/buildah/imagebuildah" @@ -90,29 +89,6 @@ type ContainerCreateResponse struct { Warnings []string `json:"Warnings"` } -type ErrorModel struct { - // API root cause formatted for automated parsing - // example: API root cause - Because string `json:"cause"` - // human error message, formatted for a human to read - // example: human error message - Message string `json:"message"` - // http response code - ResponseCode int `json:"response"` -} - -func (e ErrorModel) Error() string { - return e.Message -} - -func (e ErrorModel) Cause() error { - return errors.New(e.Because) -} - -func (e ErrorModel) Code() int { - return e.ResponseCode -} - // BuildOptions describe the options for building container images. type BuildOptions struct { imagebuildah.BuildOptions diff --git a/pkg/domain/entities/volumes.go b/pkg/domain/entities/volumes.go index e6b29e374..c826ee389 100644 --- a/pkg/domain/entities/volumes.go +++ b/pkg/domain/entities/volumes.go @@ -2,8 +2,8 @@ package entities import ( "net/url" - "time" + "github.com/containers/podman/v2/libpod/define" docker_api_types "github.com/docker/docker/api/types" docker_api_types_volume "github.com/docker/docker/api/types/volume" ) @@ -26,38 +26,7 @@ type IDOrNameResponse struct { } type VolumeConfigResponse struct { - // Name is the name of the volume. - Name string `json:"Name"` - // Driver is the driver used to create the volume. - // This will be properly implemented in a future version. - Driver string `json:"Driver"` - // Mountpoint is the path on the host where the volume is mounted. - Mountpoint string `json:"Mountpoint"` - // CreatedAt is the date and time the volume was created at. This is not - // stored for older Libpod volumes; if so, it will be omitted. - CreatedAt time.Time `json:"CreatedAt,omitempty"` - // Status is presently unused and provided only for Docker compatibility. - // In the future it will be used to return information on the volume's - // current state. - Status map[string]string `json:"Status,omitempty"` - // Labels includes the volume's configured labels, key:value pairs that - // can be passed during volume creation to provide information for third - // party tools. - Labels map[string]string `json:"Labels"` - // Scope is unused and provided solely for Docker compatibility. It is - // unconditionally set to "local". - Scope string `json:"Scope"` - // Options is a set of options that were used when creating the volume. - // It is presently not used. - Options map[string]string `json:"Options"` - // UID is the UID that the volume was created with. - UID int `json:"UID"` - // GID is the GID that the volume was created with. - GID int `json:"GID"` - // Anonymous indicates that the volume was created as an anonymous - // volume for a specific container, and will be be removed when any - // container using it is removed. - Anonymous bool `json:"Anonymous"` + define.InspectVolumeData } // VolumeInfo Volume list response @@ -116,11 +85,6 @@ type VolumePruneOptions struct { Filters url.Values `json:"filters" schema:"filters"` } -type VolumePruneReport struct { - Err error - Id string //nolint -} - type VolumeListOptions struct { Filter map[string][]string } diff --git a/pkg/domain/filters/containers.go b/pkg/domain/filters/containers.go index 6abdd6b57..1de5aca91 100644 --- a/pkg/domain/filters/containers.go +++ b/pkg/domain/filters/containers.go @@ -7,6 +7,7 @@ import ( "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" + "github.com/containers/podman/v2/libpod/network" "github.com/containers/podman/v2/pkg/timetype" "github.com/containers/podman/v2/pkg/util" "github.com/pkg/errors" @@ -34,7 +35,7 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo filterValue = "" } for labelKey, labelValue := range labels { - if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) { + if labelKey == filterKey && (filterValue == "" || labelValue == filterValue) { matched = true break } @@ -233,7 +234,24 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo } return false }, nil - + case "network": + return func(c *libpod.Container) bool { + networks, _, err := c.Networks() + // if err or no networks, quick out + if err != nil || len(networks) == 0 { + return false + } + for _, net := range networks { + netID := network.GetNetworkID(net) + for _, val := range filterValues { + // match by network name or id + if val == net || val == netID { + return true + } + } + } + return false + }, nil } return nil, errors.Errorf("%s is an invalid filter", filter) } diff --git a/pkg/domain/filters/pods.go b/pkg/domain/filters/pods.go index 7e6b7f2cc..ce7028d2a 100644 --- a/pkg/domain/filters/pods.go +++ b/pkg/domain/filters/pods.go @@ -6,6 +6,7 @@ import ( "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" + "github.com/containers/podman/v2/libpod/network" "github.com/containers/podman/v2/pkg/util" "github.com/pkg/errors" ) @@ -123,7 +124,7 @@ func GeneratePodFilterFunc(filter string, filterValues []string) ( filterValue = "" } for labelKey, labelValue := range labels { - if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) { + if labelKey == filterKey && (filterValue == "" || labelValue == filterValue) { matched = true break } @@ -134,6 +135,29 @@ func GeneratePodFilterFunc(filter string, filterValues []string) ( } return true }, nil + case "network": + return func(p *libpod.Pod) bool { + infra, err := p.InfraContainer() + // no infra, quick out + if err != nil { + return false + } + networks, _, err := infra.Networks() + // if err or no networks, quick out + if err != nil || len(networks) == 0 { + return false + } + for _, net := range networks { + netID := network.GetNetworkID(net) + for _, val := range filterValues { + // match by network name or id + if val == net || val == netID { + return true + } + } + } + return false + }, nil } return nil, errors.Errorf("%s is an invalid filter", filter) } diff --git a/pkg/domain/filters/volumes.go b/pkg/domain/filters/volumes.go index 69bef4961..7890459f5 100644 --- a/pkg/domain/filters/volumes.go +++ b/pkg/domain/filters/volumes.go @@ -39,7 +39,7 @@ func GenerateVolumeFilters(filters url.Values) ([]libpod.VolumeFilter, error) { } vf = append(vf, func(v *libpod.Volume) bool { for labelKey, labelValue := range v.Labels() { - if labelKey == filterKey && ("" == filterVal || labelValue == filterVal) { + if labelKey == filterKey && (filterVal == "" || labelValue == filterVal) { return true } } @@ -56,7 +56,7 @@ func GenerateVolumeFilters(filters url.Values) ([]libpod.VolumeFilter, error) { } vf = append(vf, func(v *libpod.Volume) bool { for labelKey, labelValue := range v.Options() { - if labelKey == filterKey && ("" == filterVal || labelValue == filterVal) { + if labelKey == filterKey && (filterVal == "" || labelValue == filterVal) { return true } } diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index cfb3421ba..a8f4d44a8 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -21,6 +21,7 @@ import ( "github.com/containers/podman/v2/pkg/cgroups" "github.com/containers/podman/v2/pkg/checkpoint" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" dfilters "github.com/containers/podman/v2/pkg/domain/filters" "github.com/containers/podman/v2/pkg/domain/infra/abi/terminal" parallelctr "github.com/containers/podman/v2/pkg/parallel/ctr" @@ -112,15 +113,7 @@ func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []strin } func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []string, options entities.PauseUnPauseOptions) ([]*entities.PauseUnpauseReport, error) { - var ( - err error - ) - ctrs := []*libpod.Container{} //nolint - if options.All { - ctrs, err = ic.Libpod.GetAllContainers() - } else { - ctrs, err = getContainersByContext(false, false, namesOrIds, ic.Libpod) - } + ctrs, err := getContainersByContext(options.All, false, namesOrIds, ic.Libpod) if err != nil { return nil, err } @@ -133,15 +126,7 @@ func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []stri } func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []string, options entities.PauseUnPauseOptions) ([]*entities.PauseUnpauseReport, error) { - var ( - err error - ) - ctrs := []*libpod.Container{} //nolint - if options.All { - ctrs, err = ic.Libpod.GetAllContainers() - } else { - ctrs, err = getContainersByContext(false, false, namesOrIds, ic.Libpod) - } + ctrs, err := getContainersByContext(options.All, false, namesOrIds, ic.Libpod) if err != nil { return nil, err } @@ -204,7 +189,7 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin return reports, nil } -func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities.ContainerPruneOptions) (*entities.ContainerPruneReport, error) { +func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities.ContainerPruneOptions) ([]*reports.PruneReport, error) { filterFuncs := make([]libpod.ContainerFilter, 0, len(options.Filters)) for k, v := range options.Filters { generatedFunc, err := dfilters.GenerateContainerFilterFuncs(k, v, ic.Libpod) @@ -213,19 +198,7 @@ func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities. } filterFuncs = append(filterFuncs, generatedFunc) } - return ic.pruneContainersHelper(filterFuncs) -} - -func (ic *ContainerEngine) pruneContainersHelper(filterFuncs []libpod.ContainerFilter) (*entities.ContainerPruneReport, error) { - prunedContainers, pruneErrors, err := ic.Libpod.PruneContainers(filterFuncs) - if err != nil { - return nil, err - } - report := entities.ContainerPruneReport{ - ID: prunedContainers, - Err: pruneErrors, - } - return &report, nil + return ic.Libpod.PruneContainers(filterFuncs) } func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []string, options entities.KillOptions) ([]*entities.KillReport, error) { @@ -498,7 +471,10 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [ TCPEstablished: options.TCPEstablished, TargetFile: options.Export, IgnoreRootfs: options.IgnoreRootFS, + IgnoreVolumes: options.IgnoreVolumes, KeepRunning: options.LeaveRunning, + PreCheckPoint: options.PreCheckPoint, + WithPrevious: options.WithPrevious, } if options.All { @@ -536,8 +512,10 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st TargetFile: options.Import, Name: options.Name, IgnoreRootfs: options.IgnoreRootFS, + IgnoreVolumes: options.IgnoreVolumes, IgnoreStaticIP: options.IgnoreStaticIP, IgnoreStaticMAC: options.IgnoreStaticMAC, + ImportPrevious: options.ImportPrevious, } filterFuncs := []libpod.ContainerFilter{ @@ -549,7 +527,7 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st switch { case options.Import != "": - cons, err = checkpoint.CRImportCheckpoint(ctx, ic.Libpod, options.Import, options.Name) + cons, err = checkpoint.CRImportCheckpoint(ctx, ic.Libpod, options) case options.All: cons, err = ic.Libpod.GetContainers(filterFuncs...) default: @@ -924,7 +902,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta if err := ic.Libpod.RemoveContainer(ctx, ctr, false, true); err != nil { if errors.Cause(err) == define.ErrNoSuchCtr || errors.Cause(err) == define.ErrCtrRemoved { - logrus.Warnf("Container %s does not exist: %v", ctr.ID(), err) + logrus.Infof("Container %s was already removed, skipping --rm", ctr.ID()) } else { logrus.Errorf("Error removing container %s: %v", ctr.ID(), err) } @@ -1334,3 +1312,17 @@ func (ic *ContainerEngine) ShouldRestart(ctx context.Context, nameOrID string) ( return &entities.BoolReport{Value: ctr.ShouldRestart(ctx)}, nil } + +// ContainerRename renames the given container. +func (ic *ContainerEngine) ContainerRename(ctx context.Context, nameOrID string, opts entities.ContainerRenameOptions) error { + ctr, err := ic.Libpod.LookupContainer(nameOrID) + if err != nil { + return err + } + + if _, err := ic.Libpod.RenameContainer(ctx, ctr, opts.NewName); err != nil { + return err + } + + return nil +} diff --git a/pkg/domain/infra/abi/containers_stat.go b/pkg/domain/infra/abi/containers_stat.go index 5b43ee2f4..931e77026 100644 --- a/pkg/domain/infra/abi/containers_stat.go +++ b/pkg/domain/infra/abi/containers_stat.go @@ -144,16 +144,29 @@ func resolveContainerPaths(container *libpod.Container, mountPoint string, conta } if volume != nil { logrus.Debugf("Container path %q resolved to volume %q on path %q", containerPath, volume.Name(), searchPath) + + // TODO: We really need to force the volume to mount + // before doing this, but that API is not exposed + // externally right now and doing so is beyond the scope + // of this commit. + mountPoint, err := volume.MountPoint() + if err != nil { + return "", "", err + } + if mountPoint == "" { + return "", "", errors.Errorf("volume %s is not mounted, cannot copy into it", volume.Name()) + } + // We found a matching volume for searchPath. We now // need to first find the relative path of our input // path to the searchPath, and then join it with the // volume's mount point. pathRelativeToVolume := strings.TrimPrefix(pathRelativeToContainerMountPoint, searchPath) - absolutePathOnTheVolumeMount, err := securejoin.SecureJoin(volume.MountPoint(), pathRelativeToVolume) + absolutePathOnTheVolumeMount, err := securejoin.SecureJoin(mountPoint, pathRelativeToVolume) if err != nil { return "", "", err } - return volume.MountPoint(), absolutePathOnTheVolumeMount, nil + return mountPoint, absolutePathOnTheVolumeMount, nil } if mount := findBindMount(container, searchPath); mount != nil { diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 394ba359c..1c233d9d5 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -22,8 +22,8 @@ import ( "github.com/containers/image/v5/types" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/image" - libpodImage "github.com/containers/podman/v2/libpod/image" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" domainUtils "github.com/containers/podman/v2/pkg/domain/utils" "github.com/containers/podman/v2/pkg/rootless" "github.com/containers/podman/v2/pkg/util" @@ -49,19 +49,12 @@ func (ir *ImageEngine) Exists(_ context.Context, nameOrID string) (*entities.Boo return &entities.BoolReport{Value: err == nil}, 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) +func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) ([]*reports.PruneReport, error) { + reports, err := ir.Libpod.ImageRuntime().PruneImages(ctx, opts.All, opts.Filter) if err != nil { return nil, err } - - report := entities.ImagePruneReport{ - Report: entities.Report{ - Id: results, - Err: nil, - }, - } - return &report, nil + return reports, err } func (ir *ImageEngine) History(ctx context.Context, nameOrID string, opts entities.ImageHistoryOptions) (*entities.ImageHistoryReport, error) { @@ -212,7 +205,7 @@ func (ir *ImageEngine) Unmount(ctx context.Context, nameOrIDs []string, options return reports, nil } -func ToDomainHistoryLayer(layer *libpodImage.History) entities.ImageHistoryLayer { +func ToDomainHistoryLayer(layer *image.History) entities.ImageHistoryLayer { l := entities.ImageHistoryLayer{} l.ID = layer.ID l.Created = *layer.Created @@ -460,19 +453,7 @@ func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) if err != nil { return nil, err } - names := strings.Split(name, ",") - if len(names) <= 1 { - newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name) - if err != nil { - return nil, errors.Wrap(err, "image loaded but no additional tags were created") - } - if len(opts.Name) > 0 { - if err := newImage.TagImage(fmt.Sprintf("%s:%s", opts.Name, opts.Tag)); err != nil { - return nil, errors.Wrapf(err, "error adding %q to image %q", opts.Name, newImage.InputName) - } - } - } - return &entities.ImageLoadReport{Names: names}, nil + return &entities.ImageLoadReport{Names: strings.Split(name, ",")}, nil } func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOptions) (*entities.ImageImportReport, error) { diff --git a/pkg/domain/infra/abi/images_list.go b/pkg/domain/infra/abi/images_list.go index c4b0b7712..2d3b9f36a 100644 --- a/pkg/domain/infra/abi/images_list.go +++ b/pkg/domain/infra/abi/images_list.go @@ -44,7 +44,10 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) } e.Labels, err = img.Labels(ctx) if err != nil { - return nil, errors.Wrapf(err, "error retrieving label for image %q: you may need to remove the image to resolve the error", img.ID()) + // Ignore empty manifest lists. + if errors.Cause(err) != libpodImage.ErrImageIsBareList { + return nil, errors.Wrapf(err, "error retrieving label for image %q: you may need to remove the image to resolve the error", img.ID()) + } } ctnrs, err := img.Containers() diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go index 0c734d10d..a68ed8788 100644 --- a/pkg/domain/infra/abi/manifest.go +++ b/pkg/domain/infra/abi/manifest.go @@ -13,7 +13,6 @@ import ( "github.com/containers/buildah/manifests" buildahManifests "github.com/containers/buildah/pkg/manifests" - "github.com/containers/buildah/util" buildahUtil "github.com/containers/buildah/util" cp "github.com/containers/image/v5/copy" "github.com/containers/image/v5/docker" @@ -60,7 +59,7 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte } } sc := ir.Libpod.SystemContext() - refs, err := util.ResolveNameToReferences(ir.Libpod.GetStore(), sc, name) + refs, err := buildahUtil.ResolveNameToReferences(ir.Libpod.GetStore(), sc, name) if err != nil { return nil, err } diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index cbc74a2f2..70c7104f1 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -10,6 +10,7 @@ import ( "github.com/containers/image/v5/types" "github.com/containers/podman/v2/libpod" + "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/image" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/specgen/generate" @@ -251,21 +252,13 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY } if options.Start != types.OptionalBoolFalse { - //start the containers + // Start the containers podStartErrors, err := pod.Start(ctx) - if err != nil { + if err != nil && errors.Cause(err) != define.ErrPodPartialFail { return nil, err } - - // Previous versions of playkube started containers individually and then - // looked for errors. Because we now use the uber-Pod start call, we should - // iterate the map of possible errors and return one if there is a problem. This - // keeps the behavior the same - - for _, e := range podStartErrors { - if e != nil { - return nil, e - } + for id, err := range podStartErrors { + playKubePod.ContainerErrors = append(playKubePod.ContainerErrors, errors.Wrapf(err, "error starting container %s", id).Error()) } } diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go index f108b770c..2a8445c9f 100644 --- a/pkg/domain/infra/abi/pods.go +++ b/pkg/domain/infra/abi/pods.go @@ -333,6 +333,17 @@ func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOpti if err != nil { return nil, err } + networks := []string{} + if len(infraID) > 0 { + infra, err := p.InfraContainer() + if err != nil { + return nil, err + } + networks, _, err = infra.Networks() + if err != nil { + return nil, err + } + } reports = append(reports, &entities.ListPodsReport{ Cgroup: p.CgroupParent(), Containers: lpcs, @@ -341,6 +352,7 @@ func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOpti InfraId: infraID, Name: p.Name(), Namespace: p.Namespace(), + Networks: networks, Status: status, Labels: p.Labels(), }) diff --git a/pkg/domain/infra/abi/pods_stats.go b/pkg/domain/infra/abi/pods_stats.go index 16c10710a..29bcbe087 100644 --- a/pkg/domain/infra/abi/pods_stats.go +++ b/pkg/domain/infra/abi/pods_stats.go @@ -44,15 +44,16 @@ func (ic *ContainerEngine) podsToStatsReport(pods []*libpod.Pod) ([]*entities.Po podID := pods[i].ID()[:12] for j := range podStats { r := entities.PodStatsReport{ - CPU: floatToPercentString(podStats[j].CPU), - MemUsage: combineHumanValues(podStats[j].MemUsage, podStats[j].MemLimit), - Mem: floatToPercentString(podStats[j].MemPerc), - NetIO: combineHumanValues(podStats[j].NetInput, podStats[j].NetOutput), - BlockIO: combineHumanValues(podStats[j].BlockInput, podStats[j].BlockOutput), - PIDS: pidsToString(podStats[j].PIDs), - CID: podStats[j].ContainerID[:12], - Name: podStats[j].Name, - Pod: podID, + CPU: floatToPercentString(podStats[j].CPU), + MemUsage: combineHumanValues(podStats[j].MemUsage, podStats[j].MemLimit), + MemUsageBytes: combineBytesValues(podStats[j].MemUsage, podStats[j].MemLimit), + Mem: floatToPercentString(podStats[j].MemPerc), + NetIO: combineHumanValues(podStats[j].NetInput, podStats[j].NetOutput), + BlockIO: combineHumanValues(podStats[j].BlockInput, podStats[j].BlockOutput), + PIDS: pidsToString(podStats[j].PIDs), + CID: podStats[j].ContainerID[:12], + Name: podStats[j].Name, + Pod: podID, } reports = append(reports, &r) } @@ -68,6 +69,13 @@ func combineHumanValues(a, b uint64) string { return fmt.Sprintf("%s / %s", units.HumanSize(float64(a)), units.HumanSize(float64(b))) } +func combineBytesValues(a, b uint64) string { + if a == 0 && b == 0 { + return "-- / --" + } + return fmt.Sprintf("%s / %s", units.BytesSize(float64(a)), units.BytesSize(float64(b))) +} + func floatToPercentString(f float64) string { strippedFloat, err := utils.RemoveScientificNotationFromFloat(f) if err != nil || strippedFloat == 0 { diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index 5f6c95d4f..f29b98696 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -16,6 +16,7 @@ import ( "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/cgroups" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/containers/podman/v2/pkg/rootless" "github.com/containers/podman/v2/pkg/util" "github.com/containers/podman/v2/utils" @@ -161,15 +162,11 @@ func movePauseProcessToScope(r *libpod.Runtime) error { return utils.RunUnderSystemdScope(int(pid), "user.slice", "podman-pause.scope") } -// checkInput can be used to verify any of the globalopt values -func checkInput() error { // nolint:deadcode,unused - return nil -} - // SystemPrune removes unused data from the system. Pruning pods, containers, volumes and images. func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.SystemPruneOptions) (*entities.SystemPruneReport, error) { var systemPruneReport = new(entities.SystemPruneReport) var filters []string + reclaimedSpace := (uint64)(0) found := true for found { found = false @@ -186,42 +183,26 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys containerPruneOptions := entities.ContainerPruneOptions{} containerPruneOptions.Filters = (url.Values)(options.Filters) - containerPruneReport, err := ic.ContainerPrune(ctx, containerPruneOptions) + containerPruneReports, err := ic.ContainerPrune(ctx, containerPruneOptions) if err != nil { return nil, err } - if len(containerPruneReport.ID) > 0 { - found = true - } - if systemPruneReport.ContainerPruneReport == nil { - systemPruneReport.ContainerPruneReport = containerPruneReport - } else { - for name, val := range containerPruneReport.ID { - systemPruneReport.ContainerPruneReport.ID[name] = val - } - } + reclaimedSpace = reclaimedSpace + reports.PruneReportsSize(containerPruneReports) + systemPruneReport.ContainerPruneReports = append(systemPruneReport.ContainerPruneReports, containerPruneReports...) for k, v := range options.Filters { filters = append(filters, fmt.Sprintf("%s=%s", k, v[0])) } - results, err := ic.Libpod.ImageRuntime().PruneImages(ctx, options.All, filters) + imagePruneReports, err := ic.Libpod.ImageRuntime().PruneImages(ctx, options.All, filters) + reclaimedSpace = reclaimedSpace + reports.PruneReportsSize(imagePruneReports) if err != nil { return nil, err } - if len(results) > 0 { + if len(imagePruneReports) > 0 { found = true } - if systemPruneReport.ImagePruneReport == nil { - systemPruneReport.ImagePruneReport = &entities.ImagePruneReport{ - Report: entities.Report{ - Id: results, - Err: nil, - }, - } - } else { - systemPruneReport.ImagePruneReport.Report.Id = append(systemPruneReport.ImagePruneReport.Report.Id, results...) - } + systemPruneReport.ImagePruneReports = append(systemPruneReport.ImagePruneReports, imagePruneReports...) if options.Volume { volumePruneOptions := entities.VolumePruneOptions{} volumePruneOptions.Filters = (url.Values)(options.Filters) @@ -232,9 +213,11 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys if len(volumePruneReport) > 0 { found = true } - systemPruneReport.VolumePruneReport = append(systemPruneReport.VolumePruneReport, volumePruneReport...) + reclaimedSpace = reclaimedSpace + reports.PruneReportsSize(volumePruneReport) + systemPruneReport.VolumePruneReports = append(systemPruneReport.VolumePruneReports, volumePruneReport...) } } + systemPruneReport.ReclaimedSpace = reclaimedSpace return systemPruneReport, nil } @@ -329,7 +312,17 @@ func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.System var reclaimableSize int64 for _, v := range vols { var consInUse int - volSize, err := sizeOfPath(v.MountPoint()) + mountPoint, err := v.MountPoint() + if err != nil { + return nil, err + } + if mountPoint == "" { + // We can't get any info on this volume, as it's not + // mounted. + // TODO: fix this. + continue + } + volSize, err := sizeOfPath(mountPoint) if err != nil { return nil, err } diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go index 515e52754..823605052 100644 --- a/pkg/domain/infra/abi/volumes.go +++ b/pkg/domain/infra/abi/volumes.go @@ -6,6 +6,7 @@ import ( "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/containers/podman/v2/pkg/domain/filters" "github.com/containers/podman/v2/pkg/domain/infra/abi/parse" "github.com/pkg/errors" @@ -102,32 +103,19 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin } reports := make([]*entities.VolumeInspectReport, 0, len(vols)) for _, v := range vols { - var uid, gid int - uid, err = v.UID() - if err != nil { - return nil, nil, err - } - gid, err = v.GID() + inspectOut, err := v.Inspect() if err != nil { return nil, nil, err } config := entities.VolumeConfigResponse{ - Name: v.Name(), - Driver: v.Driver(), - Mountpoint: v.MountPoint(), - CreatedAt: v.CreatedTime(), - Labels: v.Labels(), - Scope: v.Scope(), - Options: v.Options(), - UID: uid, - GID: gid, + InspectVolumeData: *inspectOut, } reports = append(reports, &entities.VolumeInspectReport{VolumeConfigResponse: &config}) } return reports, errs, nil } -func (ic *ContainerEngine) VolumePrune(ctx context.Context, options entities.VolumePruneOptions) ([]*entities.VolumePruneReport, error) { +func (ic *ContainerEngine) VolumePrune(ctx context.Context, options entities.VolumePruneOptions) ([]*reports.PruneReport, error) { filterFuncs, err := filters.GenerateVolumeFilters(options.Filters) if err != nil { return nil, err @@ -135,19 +123,12 @@ func (ic *ContainerEngine) VolumePrune(ctx context.Context, options entities.Vol return ic.pruneVolumesHelper(ctx, filterFuncs) } -func (ic *ContainerEngine) pruneVolumesHelper(ctx context.Context, filterFuncs []libpod.VolumeFilter) ([]*entities.VolumePruneReport, error) { +func (ic *ContainerEngine) pruneVolumesHelper(ctx context.Context, filterFuncs []libpod.VolumeFilter) ([]*reports.PruneReport, error) { pruned, err := ic.Libpod.PruneVolumes(ctx, filterFuncs) if err != nil { return nil, err } - reports := make([]*entities.VolumePruneReport, 0, len(pruned)) - for k, v := range pruned { - reports = append(reports, &entities.VolumePruneReport{ - Err: v, - Id: k, - }) - } - return reports, nil + return pruned, nil } func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeListOptions) ([]*entities.VolumeListReport, error) { @@ -161,25 +142,12 @@ func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeL } reports := make([]*entities.VolumeListReport, 0, len(vols)) for _, v := range vols { - var uid, gid int - uid, err = v.UID() - if err != nil { - return nil, err - } - gid, err = v.GID() + inspectOut, err := v.Inspect() if err != nil { return nil, err } config := entities.VolumeConfigResponse{ - Name: v.Name(), - Driver: v.Driver(), - Mountpoint: v.MountPoint(), - CreatedAt: v.CreatedTime(), - Labels: v.Labels(), - Scope: v.Scope(), - Options: v.Options(), - UID: uid, - GID: gid, + InspectVolumeData: *inspectOut, } reports = append(reports, &entities.VolumeListReport{VolumeConfigResponse: config}) } diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 0db985dff..4ee623703 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -18,6 +18,7 @@ import ( "github.com/containers/podman/v2/pkg/api/handlers" "github.com/containers/podman/v2/pkg/bindings/containers" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/containers/podman/v2/pkg/errorhandling" "github.com/containers/podman/v2/pkg/specgen" "github.com/containers/podman/v2/pkg/util" @@ -196,7 +197,7 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, return reports, nil } -func (ic *ContainerEngine) ContainerPrune(ctx context.Context, opts entities.ContainerPruneOptions) (*entities.ContainerPruneReport, error) { +func (ic *ContainerEngine) ContainerPrune(ctx context.Context, opts entities.ContainerPruneOptions) ([]*reports.PruneReport, error) { options := new(containers.PruneOptions).WithFilters(opts.Filters) return containers.Prune(ic.ClientCtx, options) } @@ -210,7 +211,7 @@ func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []st for _, name := range namesOrIds { inspect, err := containers.Inspect(ic.ClientCtx, name, options) if err != nil { - errModel, ok := err.(entities.ErrorModel) + errModel, ok := err.(errorhandling.ErrorModel) if !ok { return nil, nil, err } @@ -819,3 +820,8 @@ func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []stri func (ic *ContainerEngine) ShouldRestart(_ context.Context, id string) (bool, error) { return containers.ShouldRestart(ic.ClientCtx, id, nil) } + +// ContainerRename renames the given container. +func (ic *ContainerEngine) ContainerRename(ctx context.Context, nameOrID string, opts entities.ContainerRenameOptions) error { + return containers.Rename(ic.ClientCtx, nameOrID, new(containers.RenameOptions).WithName(opts.NewName)) +} diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index 10bf9682c..7a4aa1fbc 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -8,15 +8,15 @@ import ( "strings" "time" - "github.com/containers/podman/v2/libpod/image" - - "github.com/containers/image/v5/types" - "github.com/containers/common/pkg/config" "github.com/containers/image/v5/docker/reference" + "github.com/containers/image/v5/types" + "github.com/containers/podman/v2/libpod/image" images "github.com/containers/podman/v2/pkg/bindings/images" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" "github.com/containers/podman/v2/pkg/domain/utils" + "github.com/containers/podman/v2/pkg/errorhandling" utils2 "github.com/containers/podman/v2/utils" "github.com/pkg/errors" ) @@ -90,26 +90,18 @@ func (ir *ImageEngine) History(ctx context.Context, nameOrID string, opts entiti return &history, nil } -func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) { +func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) ([]*reports.PruneReport, error) { filters := make(map[string][]string, len(opts.Filter)) for _, filter := range opts.Filter { f := strings.Split(filter, "=") filters[f[0]] = f[1:] } options := new(images.PruneOptions).WithAll(opts.All).WithFilters(filters) - results, err := images.Prune(ir.ClientCtx, options) + reports, err := images.Prune(ir.ClientCtx, options) if err != nil { return nil, err } - - report := entities.ImagePruneReport{ - Report: entities.Report{ - Id: results, - Err: nil, - }, - Size: 0, - } - return &report, nil + return reports, nil } func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, opts entities.ImagePullOptions) (*entities.ImagePullReport, error) { @@ -194,7 +186,7 @@ func (ir *ImageEngine) Inspect(ctx context.Context, namesOrIDs []string, opts en for _, i := range namesOrIDs { r, err := images.GetImage(ir.ClientCtx, i, options) if err != nil { - errModel, ok := err.(entities.ErrorModel) + errModel, ok := err.(errorhandling.ErrorModel) if !ok { return nil, nil, err } @@ -222,12 +214,7 @@ func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) if fInfo.IsDir() { return nil, errors.Errorf("remote client supports archives only but %q is a directory", opts.Input) } - ref := opts.Name - if len(opts.Tag) > 0 { - ref += ":" + opts.Tag - } - options := new(images.LoadOptions).WithReference(ref) - return images.Load(ir.ClientCtx, f, options) + return images.Load(ir.ClientCtx, f) } func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOptions) (*entities.ImageImportReport, error) { diff --git a/pkg/domain/infra/tunnel/network.go b/pkg/domain/infra/tunnel/network.go index 9afb8db02..d4e827580 100644 --- a/pkg/domain/infra/tunnel/network.go +++ b/pkg/domain/infra/tunnel/network.go @@ -5,6 +5,7 @@ import ( "github.com/containers/podman/v2/pkg/bindings/network" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/errorhandling" "github.com/pkg/errors" ) @@ -22,7 +23,7 @@ func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []stri for _, name := range namesOrIds { report, err := network.Inspect(ic.ClientCtx, name, options) if err != nil { - errModel, ok := err.(entities.ErrorModel) + errModel, ok := err.(errorhandling.ErrorModel) if !ok { return nil, nil, err } diff --git a/pkg/domain/infra/tunnel/volumes.go b/pkg/domain/infra/tunnel/volumes.go index e6ad4e0c5..f21336828 100644 --- a/pkg/domain/infra/tunnel/volumes.go +++ b/pkg/domain/infra/tunnel/volumes.go @@ -5,6 +5,8 @@ import ( "github.com/containers/podman/v2/pkg/bindings/volumes" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/entities/reports" + "github.com/containers/podman/v2/pkg/errorhandling" "github.com/pkg/errors" ) @@ -54,7 +56,7 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin for _, id := range namesOrIds { data, err := volumes.Inspect(ic.ClientCtx, id, nil) if err != nil { - errModel, ok := err.(entities.ErrorModel) + errModel, ok := err.(errorhandling.ErrorModel) if !ok { return nil, nil, err } @@ -69,7 +71,7 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin return reports, errs, nil } -func (ic *ContainerEngine) VolumePrune(ctx context.Context, opts entities.VolumePruneOptions) ([]*entities.VolumePruneReport, error) { +func (ic *ContainerEngine) VolumePrune(ctx context.Context, opts entities.VolumePruneOptions) ([]*reports.PruneReport, error) { options := new(volumes.PruneOptions).WithFilters(opts.Filters) return volumes.Prune(ic.ClientCtx, options) } diff --git a/pkg/errorhandling/errorhandling.go b/pkg/errorhandling/errorhandling.go index 21df261fb..b1923be98 100644 --- a/pkg/errorhandling/errorhandling.go +++ b/pkg/errorhandling/errorhandling.go @@ -70,3 +70,27 @@ func CloseQuiet(f *os.File) { func Contains(err error, sub error) bool { return strings.Contains(err.Error(), sub.Error()) } + +// ErrorModel is used in remote connections with podman +type ErrorModel struct { + // API root cause formatted for automated parsing + // example: API root cause + Because string `json:"cause"` + // human error message, formatted for a human to read + // example: human error message + Message string `json:"message"` + // http response code + ResponseCode int `json:"response"` +} + +func (e ErrorModel) Error() string { + return e.Message +} + +func (e ErrorModel) Cause() error { + return errors.New(e.Because) +} + +func (e ErrorModel) Code() int { + return e.ResponseCode +} diff --git a/pkg/inspect/inspect.go b/pkg/inspect/inspect.go index 27bff46a0..67c6a5c03 100644 --- a/pkg/inspect/inspect.go +++ b/pkg/inspect/inspect.go @@ -4,7 +4,7 @@ import ( "time" "github.com/containers/image/v5/manifest" - "github.com/containers/podman/v2/libpod/driver" + "github.com/containers/podman/v2/libpod/define" "github.com/opencontainers/go-digest" v1 "github.com/opencontainers/image-spec/specs-go/v1" ) @@ -25,7 +25,7 @@ type ImageData struct { Os string `json:"Os"` Size int64 `json:"Size"` VirtualSize int64 `json:"VirtualSize"` - GraphDriver *driver.Data `json:"GraphDriver"` + GraphDriver *define.DriverData `json:"GraphDriver"` RootFS *RootFS `json:"RootFS"` Labels map[string]string `json:"Labels"` Annotations map[string]string `json:"Annotations"` diff --git a/pkg/netns/netns_linux.go b/pkg/netns/netns_linux.go index ed5241632..6817a3abd 100644 --- a/pkg/netns/netns_linux.go +++ b/pkg/netns/netns_linux.go @@ -45,7 +45,7 @@ func getNSRunDir() (string, error) { } return filepath.Join(rootlessDir, "netns"), nil } - return "/var/run/netns", nil + return "/run/netns", nil } // NewNS creates a new persistent (bind-mounted) network namespace and returns @@ -80,7 +80,7 @@ func NewNS() (ns.NetNS, error) { return nil, fmt.Errorf("mount --make-rshared %s failed: %q", nsRunDir, err) } - // Recursively remount /var/run/netns on itself. The recursive flag is + // Recursively remount /run/netns on itself. The recursive flag is // so that any existing netns bindmounts are carried over. err = unix.Mount(nsRunDir, nsRunDir, "none", unix.MS_BIND|unix.MS_REC, "") if err != nil { diff --git a/pkg/ps/ps.go b/pkg/ps/ps.go index 9e0dcb728..dc577890a 100644 --- a/pkg/ps/ps.go +++ b/pkg/ps/ps.go @@ -178,6 +178,11 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities return entities.ListContainer{}, err } + networks, _, err := ctr.Networks() + if err != nil { + return entities.ListContainer{}, err + } + ps := entities.ListContainer{ AutoRemove: ctr.AutoRemove(), Command: conConfig.Command, @@ -192,6 +197,7 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities Labels: conConfig.Labels, Mounts: ctr.UserVolumes(), Names: []string{conConfig.Name}, + Networks: networks, Pid: pid, Pod: conConfig.Pod, Ports: portMappings, diff --git a/pkg/rootless/rootless.go b/pkg/rootless/rootless.go index 799c793d8..df35c0d6b 100644 --- a/pkg/rootless/rootless.go +++ b/pkg/rootless/rootless.go @@ -2,10 +2,12 @@ package rootless import ( "os" + "sort" "sync" "github.com/containers/storage" "github.com/opencontainers/runc/libcontainer/user" + spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" ) @@ -50,24 +52,151 @@ func TryJoinPauseProcess(pausePidPath string) (bool, int, error) { } var ( - availableGids int64 - availableGidsErr error - availableGidsOnce sync.Once + uidMap []user.IDMap + uidMapError error + uidMapOnce sync.Once + + gidMap []user.IDMap + gidMapError error + gidMapOnce sync.Once ) -// GetAvailableGids returns how many GIDs are available in the +// GetAvailableUidMap returns the UID mappings in the // current user namespace. -func GetAvailableGids() (int64, error) { - availableGidsOnce.Do(func() { - idMap, err := user.ParseIDMapFile("/proc/self/gid_map") +func GetAvailableUidMap() ([]user.IDMap, error) { + uidMapOnce.Do(func() { + var err error + uidMap, err = user.ParseIDMapFile("/proc/self/uid_map") if err != nil { - availableGidsErr = err + uidMapError = err return } - availableGids = int64(0) - for _, r := range idMap { - availableGids += r.Count + }) + return uidMap, uidMapError +} + +// GetAvailableGidMap returns the GID mappings in the +// current user namespace. +func GetAvailableGidMap() ([]user.IDMap, error) { + gidMapOnce.Do(func() { + var err error + gidMap, err = user.ParseIDMapFile("/proc/self/gid_map") + if err != nil { + gidMapError = err + return } }) - return availableGids, availableGidsErr + return gidMap, gidMapError +} + +// GetAvailableIDMaps returns the UID and GID mappings in the +// current user namespace. +func GetAvailableIDMaps() ([]user.IDMap, []user.IDMap, error) { + u, err := GetAvailableUidMap() + if err != nil { + return nil, nil, err + } + g, err := GetAvailableGidMap() + if err != nil { + return nil, nil, err + } + return u, g, nil +} + +func countAvailableIDs(mappings []user.IDMap) int64 { + availableUids := int64(0) + for _, r := range mappings { + availableUids += r.Count + } + return availableUids +} + +// GetAvailableUids returns how many UIDs are available in the +// current user namespace. +func GetAvailableUids() (int64, error) { + uids, err := GetAvailableUidMap() + if err != nil { + return -1, err + } + + return countAvailableIDs(uids), nil +} + +// GetAvailableGids returns how many GIDs are available in the +// current user namespace. +func GetAvailableGids() (int64, error) { + gids, err := GetAvailableGidMap() + if err != nil { + return -1, err + } + + return countAvailableIDs(gids), nil +} + +// findIDInMappings find the the mapping that contains the specified ID. +// It assumes availableMappings is sorted by ID. +func findIDInMappings(id int64, availableMappings []user.IDMap) *user.IDMap { + i := sort.Search(len(availableMappings), func(i int) bool { + return availableMappings[i].ID >= id + }) + if i < 0 || i >= len(availableMappings) { + return nil + } + r := &availableMappings[i] + if id >= r.ID && id < r.ID+r.Count { + return r + } + return nil +} + +// MaybeSplitMappings checks whether the specified OCI mappings are possible +// in the current user namespace or the specified ranges must be split. +func MaybeSplitMappings(mappings []spec.LinuxIDMapping, availableMappings []user.IDMap) []spec.LinuxIDMapping { + var ret []spec.LinuxIDMapping + var overflow spec.LinuxIDMapping + overflow.Size = 0 + consumed := 0 + sort.Slice(availableMappings, func(i, j int) bool { + return availableMappings[i].ID < availableMappings[j].ID + }) + for { + cur := overflow + // if there is no overflow left from the previous request, get the next one + if cur.Size == 0 { + if consumed == len(mappings) { + // all done + return ret + } + cur = mappings[consumed] + consumed++ + } + + // Find the range where the first specified ID is present + r := findIDInMappings(int64(cur.HostID), availableMappings) + if r == nil { + // The requested range is not available. Just return the original request + // and let other layers deal with it. + return mappings + } + + offsetInRange := cur.HostID - uint32(r.ID) + + usableIDs := uint32(r.Count) - offsetInRange + + // the current range can satisfy the whole request + if usableIDs >= cur.Size { + // reset the overflow + overflow.Size = 0 + } else { + // the current range can satisfy the request partially + // so move the rest to overflow + overflow.Size = cur.Size - usableIDs + overflow.ContainerID = cur.ContainerID + usableIDs + overflow.HostID = cur.HostID + usableIDs + + // and cap to the usableIDs count + cur.Size = usableIDs + } + ret = append(ret, cur) + } } diff --git a/pkg/rootless/rootless_test.go b/pkg/rootless/rootless_test.go new file mode 100644 index 000000000..ef574099c --- /dev/null +++ b/pkg/rootless/rootless_test.go @@ -0,0 +1,101 @@ +package rootless + +import ( + "reflect" + "testing" + + "github.com/opencontainers/runc/libcontainer/user" + spec "github.com/opencontainers/runtime-spec/specs-go" +) + +func TestMaybeSplitMappings(t *testing.T) { + mappings := []spec.LinuxIDMapping{ + { + ContainerID: 0, + HostID: 0, + Size: 2, + }, + } + desiredMappings := []spec.LinuxIDMapping{ + { + ContainerID: 0, + HostID: 0, + Size: 1, + }, + { + ContainerID: 1, + HostID: 1, + Size: 1, + }, + } + availableMappings := []user.IDMap{ + { + ID: 1, + ParentID: 1000000, + Count: 65536, + }, + { + ID: 0, + ParentID: 1000, + Count: 1, + }, + } + newMappings := MaybeSplitMappings(mappings, availableMappings) + if !reflect.DeepEqual(newMappings, desiredMappings) { + t.Fatal("wrong mappings generated") + } + + mappings = []spec.LinuxIDMapping{ + { + ContainerID: 0, + HostID: 0, + Size: 2, + }, + } + desiredMappings = []spec.LinuxIDMapping{ + { + ContainerID: 0, + HostID: 0, + Size: 2, + }, + } + availableMappings = []user.IDMap{ + { + ID: 0, + ParentID: 1000000, + Count: 65536, + }, + } + newMappings = MaybeSplitMappings(mappings, availableMappings) + + if !reflect.DeepEqual(newMappings, desiredMappings) { + t.Fatal("wrong mappings generated") + } + + mappings = []spec.LinuxIDMapping{ + { + ContainerID: 0, + HostID: 0, + Size: 1, + }, + } + desiredMappings = []spec.LinuxIDMapping{ + { + ContainerID: 0, + HostID: 0, + Size: 1, + }, + } + availableMappings = []user.IDMap{ + { + ID: 10000, + ParentID: 10000, + Count: 65536, + }, + } + + newMappings = MaybeSplitMappings(mappings, availableMappings) + if !reflect.DeepEqual(newMappings, desiredMappings) { + t.Fatal("wrong mappings generated") + } +} diff --git a/pkg/signal/signal_linux_mipsx.go b/pkg/signal/signal_linux_mipsx.go index 67638e30a..45c9d5af1 100644 --- a/pkg/signal/signal_linux_mipsx.go +++ b/pkg/signal/signal_linux_mipsx.go @@ -19,6 +19,8 @@ import ( const ( sigrtmin = 34 sigrtmax = 127 + + SIGWINCH = syscall.SIGWINCH ) // signalMap is a map of Linux signals. diff --git a/pkg/specgen/generate/config_linux.go b/pkg/specgen/generate/config_linux.go index e0b039fb7..1290a8eb6 100644 --- a/pkg/specgen/generate/config_linux.go +++ b/pkg/specgen/generate/config_linux.go @@ -21,9 +21,6 @@ var ( errNotADevice = errors.New("not a device node") ) -func u32Ptr(i int64) *uint32 { u := uint32(i); return &u } -func fmPtr(i int64) *os.FileMode { fm := os.FileMode(i); return &fm } - func addPrivilegedDevices(g *generate.Generator) error { hostDevices, err := getDevices("/dev") if err != nil { diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index 42fea0277..2feb1d3b2 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -100,15 +100,9 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat if err != nil { return nil, err } - // First transform the os env into a map. We need it for the labels later in - // any case. - osEnv, err := envLib.ParseSlice(os.Environ()) - if err != nil { - return nil, errors.Wrap(err, "error parsing host environment variables") - } // Get Default Environment from containers.conf - defaultEnvs, err := envLib.ParseSlice(rtc.GetDefaultEnv()) + defaultEnvs, err := envLib.ParseSlice(rtc.GetDefaultEnvEx(s.EnvHost, s.HTTPProxy)) if err != nil { return nil, errors.Wrap(err, "error parsing fields in containers.conf") } @@ -133,6 +127,12 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat defaultEnvs = envLib.Join(defaultEnvs, envs) } + // First transform the os env into a map. We need it for the labels later in + // any case. + osEnv, err := envLib.ParseSlice(os.Environ()) + if err != nil { + return nil, errors.Wrap(err, "error parsing host environment variables") + } // Caller Specified defaults if s.EnvHost { defaultEnvs = envLib.Join(defaultEnvs, osEnv) @@ -282,8 +282,8 @@ func finishThrottleDevices(s *specgen.SpecGenerator) error { if err := unix.Stat(k, &statT); err != nil { return err } - v.Major = (int64(unix.Major(statT.Rdev))) - v.Minor = (int64(unix.Minor(statT.Rdev))) + v.Major = (int64(unix.Major(uint64(statT.Rdev)))) + v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) s.ResourceLimits.BlockIO.ThrottleReadBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleReadBpsDevice, v) } } @@ -293,8 +293,8 @@ func finishThrottleDevices(s *specgen.SpecGenerator) error { if err := unix.Stat(k, &statT); err != nil { return err } - v.Major = (int64(unix.Major(statT.Rdev))) - v.Minor = (int64(unix.Minor(statT.Rdev))) + v.Major = (int64(unix.Major(uint64(statT.Rdev)))) + v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteBpsDevice, v) } } @@ -304,8 +304,8 @@ func finishThrottleDevices(s *specgen.SpecGenerator) error { if err := unix.Stat(k, &statT); err != nil { return err } - v.Major = (int64(unix.Major(statT.Rdev))) - v.Minor = (int64(unix.Minor(statT.Rdev))) + v.Major = (int64(unix.Major(uint64(statT.Rdev)))) + v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice, v) } } @@ -315,8 +315,8 @@ func finishThrottleDevices(s *specgen.SpecGenerator) error { if err := unix.Stat(k, &statT); err != nil { return err } - v.Major = (int64(unix.Major(statT.Rdev))) - v.Minor = (int64(unix.Minor(statT.Rdev))) + v.Major = (int64(unix.Major(uint64(statT.Rdev)))) + v.Minor = (int64(unix.Minor(uint64(statT.Rdev)))) s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleWriteIOPSDevice, v) } } diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index e5b09dcd8..e39a700eb 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - "github.com/containers/buildah/pkg/parse" + "github.com/containers/common/pkg/parse" "github.com/containers/podman/v2/libpod/image" ann "github.com/containers/podman/v2/pkg/annotations" "github.com/containers/podman/v2/pkg/specgen" @@ -129,24 +129,20 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener // TODO: We don't understand why specgen does not take of this, but // integration tests clearly pointed out that it was required. - s.Command = []string{} imageData, err := opts.Image.Inspect(ctx) if err != nil { return nil, err } s.WorkDir = "/" - // We will use "Docker field name" internally here to avoid confusion - // and reference the "Kubernetes field name" when referencing the YAML - // ref: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#notes - entrypoint := []string{} - cmd := []string{} + // Entrypoint/Command handling is based off of + // https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#notes if imageData != nil && imageData.Config != nil { if imageData.Config.WorkingDir != "" { s.WorkDir = imageData.Config.WorkingDir } // Pull entrypoint and cmd from image - entrypoint = imageData.Config.Entrypoint - cmd = imageData.Config.Cmd + s.Entrypoint = imageData.Config.Entrypoint + s.Command = imageData.Config.Cmd s.Labels = imageData.Config.Labels if len(imageData.Config.StopSignal) > 0 { stopSignal, err := util.ParseSignal(imageData.Config.StopSignal) @@ -158,16 +154,15 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener } // If only the yaml.Command is specified, set it as the entrypoint and drop the image Cmd if len(opts.Container.Command) != 0 { - entrypoint = opts.Container.Command - cmd = []string{} + s.Entrypoint = opts.Container.Command + s.Command = []string{} } // Only override the cmd field if yaml.Args is specified // Keep the image entrypoint, or the yaml.command if specified if len(opts.Container.Args) != 0 { - cmd = opts.Container.Args + s.Command = opts.Container.Args } - s.Command = append(entrypoint, cmd...) // FIXME, // we are currently ignoring imageData.Config.ExposedPorts if opts.Container.WorkingDir != "" { diff --git a/pkg/specgen/generate/kube/volume.go b/pkg/specgen/generate/kube/volume.go index bb8edabb7..f5687f60d 100644 --- a/pkg/specgen/generate/kube/volume.go +++ b/pkg/specgen/generate/kube/volume.go @@ -3,7 +3,7 @@ package kube import ( "os" - "github.com/containers/buildah/pkg/parse" + "github.com/containers/common/pkg/parse" "github.com/containers/podman/v2/libpod" "github.com/pkg/errors" "github.com/sirupsen/logrus" diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go index 036c7b7a1..f66ad6101 100644 --- a/pkg/specgen/generate/namespaces.go +++ b/pkg/specgen/generate/namespaces.go @@ -236,6 +236,9 @@ func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod. case specgen.Private: fallthrough case specgen.Bridge: + if postConfigureNetNS && rootless.IsRootless() { + return nil, errors.New("CNI networks not supported with user namespaces") + } portMappings, err := createPortMappings(ctx, s, img) if err != nil { return nil, err @@ -364,7 +367,9 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt // namespaces? g.SetHostname(hostname) } - g.AddProcessEnv("HOSTNAME", hostname) + if _, ok := s.Env["HOSTNAME"]; !ok && s.Hostname != "" { + g.AddProcessEnv("HOSTNAME", hostname) + } // User switch s.UserNS.NSMode { diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index ba68de6fd..e62131244 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -110,7 +110,7 @@ func makeCommand(ctx context.Context, s *specgen.SpecGenerator, img *image.Image // Only use image command if the user did not manually set an // entrypoint. command := s.Command - if (command == nil || len(command) == 0) && img != nil && (s.Entrypoint == nil || len(s.Entrypoint) == 0) { + if len(command) == 0 && img != nil && len(s.Entrypoint) == 0 { newCmd, err := img.Cmd(ctx) if err != nil { return nil, err @@ -138,10 +138,23 @@ func makeCommand(ctx context.Context, s *specgen.SpecGenerator, img *image.Image return finalCommand, nil } +// canMountSys is a best-effort heuristic to detect whether mounting a new sysfs is permitted in the container +func canMountSys(isRootless, isNewUserns bool, s *specgen.SpecGenerator) bool { + if s.NetNS.IsHost() && (isRootless || isNewUserns) { + return false + } + if isNewUserns { + switch s.NetNS.NSMode { + case specgen.Slirp, specgen.Private, specgen.NoNetwork, specgen.Bridge: + return true + default: + return false + } + } + return true +} + func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, newImage *image.Image, mounts []spec.Mount, pod *libpod.Pod, finalCmd []string) (*spec.Spec, error) { - var ( - inUserNS bool - ) cgroupPerm := "ro" g, err := generate.New("linux") if err != nil { @@ -151,23 +164,11 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt g.RemoveMount("/dev/shm") g.HostSpecific = true addCgroup := true - canMountSys := true isRootless := rootless.IsRootless() - if isRootless { - inUserNS = true - } - if !s.UserNS.IsHost() { - if s.UserNS.IsContainer() || s.UserNS.IsPath() { - inUserNS = true - } - if s.UserNS.IsPrivate() { - inUserNS = true - } - } - if inUserNS && s.NetNS.NSMode != specgen.NoNetwork { - canMountSys = false - } + isNewUserns := s.UserNS.IsContainer() || s.UserNS.IsPath() || s.UserNS.IsPrivate() + + canMountSys := canMountSys(isRootless, isNewUserns, s) if s.Privileged && canMountSys { cgroupPerm = "rw" @@ -232,6 +233,8 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt g.AddMount(devPts) } + inUserNS := isRootless || isNewUserns + if inUserNS && s.IpcNS.IsHost() { g.RemoveMount("/dev/mqueue") devMqueue := spec.Mount{ diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security.go index 0c97dc496..390b19beb 100644 --- a/pkg/specgen/generate/security.go +++ b/pkg/specgen/generate/security.go @@ -133,13 +133,13 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, configSpec := g.Config configSpec.Process.Capabilities.Ambient = []string{} configSpec.Process.Capabilities.Bounding = caplist - configSpec.Process.Capabilities.Inheritable = caplist user := strings.Split(s.User, ":")[0] if (user == "" && s.UserNS.NSMode != specgen.KeepID) || user == "root" || user == "0" { configSpec.Process.Capabilities.Effective = caplist configSpec.Process.Capabilities.Permitted = caplist + configSpec.Process.Capabilities.Inheritable = caplist } else { userCaps, err := capabilities.MergeCapabilities(nil, s.CapAdd, nil) if err != nil { @@ -147,6 +147,7 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, } configSpec.Process.Capabilities.Effective = userCaps configSpec.Process.Capabilities.Permitted = userCaps + configSpec.Process.Capabilities.Inheritable = userCaps // Ambient capabilities were added to Linux 4.3. Set ambient // capabilities only when the kernel supports them. @@ -172,12 +173,16 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, // Clear default Seccomp profile from Generator for unconfined containers // and privileged containers which do not specify a seccomp profile. - if s.SeccompProfilePath == "unconfined" || (s.Privileged && (s.SeccompProfilePath == config.SeccompOverridePath || s.SeccompProfilePath == config.SeccompDefaultPath)) { + if s.SeccompProfilePath == "unconfined" || (s.Privileged && (s.SeccompProfilePath == "" || s.SeccompProfilePath == config.SeccompOverridePath || s.SeccompProfilePath == config.SeccompDefaultPath)) { configSpec.Linux.Seccomp = nil } g.SetRootReadonly(s.ReadOnlyFilesystem) + noUseIPC := s.IpcNS.NSMode == specgen.FromContainer || s.IpcNS.NSMode == specgen.FromPod || s.IpcNS.NSMode == specgen.Host + noUseNet := s.NetNS.NSMode == specgen.FromContainer || s.NetNS.NSMode == specgen.FromPod || s.NetNS.NSMode == specgen.Host + noUseUTS := s.UtsNS.NSMode == specgen.FromContainer || s.UtsNS.NSMode == specgen.FromPod || s.UtsNS.NSMode == specgen.Host + // Add default sysctls defaultSysctls, err := util.ValidateSysctls(rtc.Sysctls()) if err != nil { @@ -186,20 +191,20 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, for sysctlKey, sysctlVal := range defaultSysctls { // Ignore mqueue sysctls if --ipc=host - if s.IpcNS.IsHost() && strings.HasPrefix(sysctlKey, "fs.mqueue.") { + if noUseIPC && strings.HasPrefix(sysctlKey, "fs.mqueue.") { logrus.Infof("Sysctl %s=%s ignored in containers.conf, since IPC Namespace set to host", sysctlKey, sysctlVal) continue } // Ignore net sysctls if --net=host - if s.NetNS.IsHost() && strings.HasPrefix(sysctlKey, "net.") { + if noUseNet && strings.HasPrefix(sysctlKey, "net.") { logrus.Infof("Sysctl %s=%s ignored in containers.conf, since Network Namespace set to host", sysctlKey, sysctlVal) continue } // Ignore uts sysctls if --uts=host - if s.UtsNS.IsHost() && (strings.HasPrefix(sysctlKey, "kernel.domainname") || strings.HasPrefix(sysctlKey, "kernel.hostname")) { + if noUseUTS && (strings.HasPrefix(sysctlKey, "kernel.domainname") || strings.HasPrefix(sysctlKey, "kernel.hostname")) { logrus.Infof("Sysctl %s=%s ignored in containers.conf, since UTS Namespace set to host", sysctlKey, sysctlVal) continue } diff --git a/pkg/specgen/generate/storage.go b/pkg/specgen/generate/storage.go index f523ac5bf..63713726e 100644 --- a/pkg/specgen/generate/storage.go +++ b/pkg/specgen/generate/storage.go @@ -124,14 +124,10 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru // named volumes, and vice versa. // We'll delete the conflicts here as we supersede. for dest := range unifiedMounts { - if _, ok := baseVolumes[dest]; ok { - delete(baseVolumes, dest) - } + delete(baseVolumes, dest) } for dest := range unifiedVolumes { - if _, ok := baseMounts[dest]; ok { - delete(baseMounts, dest) - } + delete(baseMounts, dest) } // Supersede volumes-from/image volumes with unified volumes from above. diff --git a/pkg/specgen/generate/validate.go b/pkg/specgen/generate/validate.go index f0ab4b994..77cccad3e 100644 --- a/pkg/specgen/generate/validate.go +++ b/pkg/specgen/generate/validate.go @@ -48,7 +48,7 @@ func verifyContainerResourcesCgroupV1(s *specgen.SpecGenerator) ([]string, error warnings = append(warnings, "Your kernel does not support memory swappiness capabilities, or the cgroup is not mounted. Memory swappiness discarded.") memory.Swappiness = nil } else { - if *memory.Swappiness < 0 || *memory.Swappiness > 100 { + if *memory.Swappiness > 100 { return warnings, errors.Errorf("invalid value: %v, valid memory swappiness range is 0-100", *memory.Swappiness) } } diff --git a/pkg/specgen/volumes.go b/pkg/specgen/volumes.go index a4f42d715..83634b4ef 100644 --- a/pkg/specgen/volumes.go +++ b/pkg/specgen/volumes.go @@ -4,7 +4,7 @@ import ( "path/filepath" "strings" - "github.com/containers/buildah/pkg/parse" + "github.com/containers/common/pkg/parse" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" diff --git a/pkg/systemd/generate/common.go b/pkg/systemd/generate/common.go index 52a214883..8901298db 100644 --- a/pkg/systemd/generate/common.go +++ b/pkg/systemd/generate/common.go @@ -11,6 +11,11 @@ import ( // is set to the unit's (unique) name. const EnvVariable = "PODMAN_SYSTEMD_UNIT" +// minTimeoutStopSec is the minimal stop timeout for generated systemd units. +// Once exceeded, processes of the services are killed and the cgroup(s) are +// cleaned up. +const minTimeoutStopSec = 60 + // RestartPolicies includes all valid restart policies to be used in a unit // file. var RestartPolicies = []string{"no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", "always"} @@ -66,3 +71,30 @@ func quoteArguments(command []string) []string { } return command } + +func removeDetachArg(args []string, argCount int) []string { + // "--detach=false" could also be in the container entrypoint + // split them off so we do not remove it there + realArgs := args[len(args)-argCount:] + flagArgs := removeArg("-d=false", args[:len(args)-argCount]) + flagArgs = removeArg("--detach=false", flagArgs) + return append(flagArgs, realArgs...) +} + +func removeReplaceArg(args []string, argCount int) []string { + // "--replace=false" could also be in the container entrypoint + // split them off so we do not remove it there + realArgs := args[len(args)-argCount:] + flagArgs := removeArg("--replace=false", args[:len(args)-argCount]) + return append(flagArgs, realArgs...) +} + +func removeArg(arg string, args []string) []string { + newArgs := []string{} + for _, a := range args { + if a != arg { + newArgs = append(newArgs, a) + } + } + return newArgs +} diff --git a/pkg/systemd/generate/containers.go b/pkg/systemd/generate/containers.go index d84125fc7..b64b2593c 100644 --- a/pkg/systemd/generate/containers.go +++ b/pkg/systemd/generate/containers.go @@ -14,6 +14,7 @@ import ( "github.com/containers/podman/v2/version" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "github.com/spf13/pflag" ) // containerInfo contains data required for generating a container's systemd @@ -44,6 +45,9 @@ type containerInfo struct { // Executable is the path to the podman executable. Will be auto-filled if // left empty. Executable string + // RootFlags contains the root flags which were used to create the container + // Only used with --new + RootFlags string // TimeStamp at the time of creating the unit file. Will be set internally. TimeStamp string // CreateCommand is the full command plus arguments of the process the @@ -55,6 +59,8 @@ type containerInfo struct { ExecStartPre string // ExecStart of the unit. ExecStart string + // TimeoutStopSec of the unit. + TimeoutStopSec uint // ExecStop of the unit. ExecStop string // ExecStopPost of the unit. @@ -74,6 +80,7 @@ After={{- range $index, $value := .BoundToServices -}}{{if $index}} {{end}}{{ $v [Service] Environment={{.EnvVariable}}=%n Restart={{.RestartPolicy}} +TimeoutStopSec={{.TimeoutStopSec}} {{- if .ExecStartPre}} ExecStartPre={{.ExecStartPre}} {{- end}} @@ -81,7 +88,6 @@ ExecStart={{.ExecStart}} ExecStop={{.ExecStop}} ExecStopPost={{.ExecStopPost}} PIDFile={{.PIDFile}} -KillMode=none Type=forking [Install] @@ -183,22 +189,30 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst info.ContainerIDFile = "%t/" + info.ServiceName + ".ctr-id" // The create command must at least have three arguments: // /usr/bin/podman run $IMAGE - index := 2 - if info.CreateCommand[1] == "container" { - index = 3 + index := 0 + for i, arg := range info.CreateCommand { + if arg == "run" || arg == "create" { + index = i + 1 + break + } } - if len(info.CreateCommand) < index+1 { + if index == 0 { return "", errors.Errorf("container's create command is too short or invalid: %v", info.CreateCommand) } // We're hard-coding the first five arguments and append the // CreateCommand with a stripped command and subcommand. - startCommand := []string{ - info.Executable, + startCommand := []string{info.Executable} + if index > 2 { + // include root flags + info.RootFlags = strings.Join(quoteArguments(info.CreateCommand[1:index-1]), " ") + startCommand = append(startCommand, info.CreateCommand[1:index-1]...) + } + startCommand = append(startCommand, "run", "--conmon-pidfile", "{{.PIDFile}}", "--cidfile", "{{.ContainerIDFile}}", "--cgroups=no-conmon", - } + ) // If the container is in a pod, make sure that the // --pod-id-file is set correctly. if info.pod != nil { @@ -208,22 +222,26 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst } // Presence check for certain flags/options. - hasDetachParam := false - hasNameParam := false - hasReplaceParam := false - for _, p := range info.CreateCommand[index:] { - switch p { - case "--detach", "-d": - hasDetachParam = true - case "--name": - hasNameParam = true - case "--replace": - hasReplaceParam = true - } - if strings.HasPrefix(p, "--name=") { - hasNameParam = true - } + fs := pflag.NewFlagSet("args", pflag.ContinueOnError) + fs.ParseErrorsWhitelist.UnknownFlags = true + fs.Usage = func() {} + fs.SetInterspersed(false) + fs.BoolP("detach", "d", false, "") + fs.String("name", "", "") + fs.Bool("replace", false, "") + fs.Parse(info.CreateCommand[index:]) + + hasDetachParam, err := fs.GetBool("detach") + if err != nil { + return "", err } + hasNameParam := fs.Lookup("name").Changed + hasReplaceParam, err := fs.GetBool("replace") + if err != nil { + return "", err + } + + remainingCmd := info.CreateCommand[index:] if !hasDetachParam { // Enforce detaching @@ -238,6 +256,13 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst // will wait the `podman run` command exit until failed // with timeout error. startCommand = append(startCommand, "-d") + + if fs.Changed("detach") { + // this can only happen if --detach=false is set + // in that case we need to remove it otherwise we + // would overwrite the previous detach arg to false + remainingCmd = removeDetachArg(remainingCmd, fs.NArg()) + } } if hasNameParam && !hasReplaceParam { // Enforce --replace for named containers. This will @@ -245,16 +270,25 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst // start after system crashes (see // github.com/containers/podman/issues/5485). startCommand = append(startCommand, "--replace") + + if fs.Changed("replace") { + // this can only happen if --replace=false is set + // in that case we need to remove it otherwise we + // would overwrite the previous replace arg to false + remainingCmd = removeReplaceArg(remainingCmd, fs.NArg()) + } } - startCommand = append(startCommand, info.CreateCommand[index:]...) + startCommand = append(startCommand, remainingCmd...) startCommand = quoteArguments(startCommand) info.ExecStartPre = "/bin/rm -f {{.PIDFile}} {{.ContainerIDFile}}" info.ExecStart = strings.Join(startCommand, " ") - info.ExecStop = "{{.Executable}} stop --ignore --cidfile {{.ContainerIDFile}} {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}}" - info.ExecStopPost = "{{.Executable}} rm --ignore -f --cidfile {{.ContainerIDFile}}" + info.ExecStop = "{{.Executable}} {{if .RootFlags}}{{ .RootFlags}} {{end}}stop --ignore --cidfile {{.ContainerIDFile}} {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}}" + info.ExecStopPost = "{{.Executable}} {{if .RootFlags}}{{ .RootFlags}} {{end}}rm --ignore -f --cidfile {{.ContainerIDFile}}" } + info.TimeoutStopSec = minTimeoutStopSec + info.StopTimeout + if info.PodmanVersion == "" { info.PodmanVersion = version.Version.String() } diff --git a/pkg/systemd/generate/containers_test.go b/pkg/systemd/generate/containers_test.go index d27062ef3..c8e65bfe3 100644 --- a/pkg/systemd/generate/containers_test.go +++ b/pkg/systemd/generate/containers_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/stretchr/testify/assert" ) func TestValidateRestartPolicyContainer(t *testing.T) { @@ -48,11 +49,11 @@ After=network-online.target [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always +TimeoutStopSec=82 ExecStart=/usr/bin/podman start 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 -ExecStop=/usr/bin/podman stop -t 10 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 -ExecStopPost=/usr/bin/podman stop -t 10 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 -PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid -KillMode=none +ExecStop=/usr/bin/podman stop -t 22 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 +ExecStopPost=/usr/bin/podman stop -t 22 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 +PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking [Install] @@ -71,11 +72,11 @@ After=network-online.target [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always +TimeoutStopSec=70 ExecStart=/usr/bin/podman start foobar ExecStop=/usr/bin/podman stop -t 10 foobar ExecStopPost=/usr/bin/podman stop -t 10 foobar -PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid -KillMode=none +PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking [Install] @@ -96,11 +97,11 @@ After=a.service b.service c.service pod.service [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always +TimeoutStopSec=70 ExecStart=/usr/bin/podman start foobar ExecStop=/usr/bin/podman stop -t 10 foobar ExecStopPost=/usr/bin/podman stop -t 10 foobar -PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid -KillMode=none +PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking [Install] @@ -119,12 +120,12 @@ After=network-online.target [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always +TimeoutStopSec=70 ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d --replace --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN "foo=arg \"with \" space" -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id +ExecStart=/usr/bin/podman container run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d --replace --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN "foo=arg \"with \" space" +ExecStop=/usr/bin/podman container stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 +ExecStopPost=/usr/bin/podman container rm --ignore -f --cidfile %t/jadda-jadda.ctr-id PIDFile=%t/jadda-jadda.pid -KillMode=none Type=forking [Install] @@ -143,12 +144,12 @@ After=network-online.target [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always +TimeoutStopSec=70 ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon --replace -d --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42 +ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id PIDFile=%t/jadda-jadda.pid -KillMode=none Type=forking [Install] @@ -167,12 +168,12 @@ After=network-online.target [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always +TimeoutStopSec=70 ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon --pod-id-file /tmp/pod-foobar.pod-id-file --replace -d --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42 +ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id PIDFile=%t/jadda-jadda.pid -KillMode=none Type=forking [Install] @@ -191,12 +192,12 @@ After=network-online.target [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always +TimeoutStopSec=70 ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon --replace --detach --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42 +ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id PIDFile=%t/jadda-jadda.pid -KillMode=none Type=forking [Install] @@ -215,12 +216,113 @@ After=network-online.target [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always +TimeoutStopSec=70 ExecStartPre=/bin/rm -f %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.pid %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.ctr-id ExecStart=/usr/bin/podman run --conmon-pidfile %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.pid --cidfile %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.ctr-id --cgroups=no-conmon -d awesome-image:latest ExecStop=/usr/bin/podman stop --ignore --cidfile %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.ctr-id -t 10 ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.ctr-id PIDFile=%t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.pid -KillMode=none +Type=forking + +[Install] +WantedBy=multi-user.target default.target +` + + genGoodNewDetach := func(detachparam string) string { + goodNewDetach := `# jadda-jadda.service +# autogenerated by Podman CI + +[Unit] +Description=Podman jadda-jadda.service +Documentation=man:podman-generate-systemd(1) +Wants=network.target +After=network-online.target + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=always +TimeoutStopSec=102 +ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id +ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon ` + + detachparam + + ` awesome-image:latest +ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42 +ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id +PIDFile=%t/jadda-jadda.pid +Type=forking + +[Install] +WantedBy=multi-user.target default.target +` + return goodNewDetach + } + + goodNameNewDetachFalseWithCmd := `# jadda-jadda.service +# autogenerated by Podman CI + +[Unit] +Description=Podman jadda-jadda.service +Documentation=man:podman-generate-systemd(1) +Wants=network.target +After=network-online.target + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=always +TimeoutStopSec=102 +ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id +ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d --replace --name test -p 80:80 awesome-image:latest somecmd --detach=false +ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42 +ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id +PIDFile=%t/jadda-jadda.pid +Type=forking + +[Install] +WantedBy=multi-user.target default.target +` + + goodNewRootFlags := `# jadda-jadda.service +# autogenerated by Podman CI + +[Unit] +Description=Podman jadda-jadda.service +Documentation=man:podman-generate-systemd(1) +Wants=network.target +After=network-online.target + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=always +TimeoutStopSec=102 +ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id +ExecStart=/usr/bin/podman --events-backend none --runroot /root run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d awesome-image:latest +ExecStop=/usr/bin/podman --events-backend none --runroot /root stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42 +ExecStopPost=/usr/bin/podman --events-backend none --runroot /root rm --ignore -f --cidfile %t/jadda-jadda.ctr-id +PIDFile=%t/jadda-jadda.pid +Type=forking + +[Install] +WantedBy=multi-user.target default.target +` + + goodContainerCreate := `# jadda-jadda.service +# autogenerated by Podman CI + +[Unit] +Description=Podman jadda-jadda.service +Documentation=man:podman-generate-systemd(1) +Wants=network.target +After=network-online.target + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=always +TimeoutStopSec=70 +ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id +ExecStart=/usr/bin/podman container run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d awesome-image:latest +ExecStop=/usr/bin/podman container stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 +ExecStopPost=/usr/bin/podman container rm --ignore -f --cidfile %t/jadda-jadda.ctr-id +PIDFile=%t/jadda-jadda.pid Type=forking [Install] @@ -241,8 +343,8 @@ WantedBy=multi-user.target default.target ServiceName: "container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", ContainerNameOrID: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 10, + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 22, PodmanVersion: "CI", EnvVariable: EnvVariable, }, @@ -256,7 +358,7 @@ WantedBy=multi-user.target default.target ServiceName: "container-foobar", ContainerNameOrID: "foobar", RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", EnvVariable: EnvVariable, @@ -271,7 +373,7 @@ WantedBy=multi-user.target default.target ServiceName: "container-foobar", ContainerNameOrID: "foobar", RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", BoundToServices: []string{"pod", "a", "b", "c"}, @@ -286,7 +388,7 @@ WantedBy=multi-user.target default.target Executable: "/usr/bin/podman", ServiceName: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", RestartPolicy: "never", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", EnvVariable: EnvVariable, @@ -301,8 +403,8 @@ WantedBy=multi-user.target default.target ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 42, + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, PodmanVersion: "CI", CreateCommand: []string{"I'll get stripped", "container", "run", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN", "foo=arg \"with \" space"}, EnvVariable: EnvVariable, @@ -317,10 +419,10 @@ WantedBy=multi-user.target default.target ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 42, + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, PodmanVersion: "CI", - CreateCommand: []string{"I'll get stripped", "container", "run", "-d", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"}, + CreateCommand: []string{"I'll get stripped", "run", "-d", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"}, EnvVariable: EnvVariable, }, goodWithExplicitShortDetachParam, @@ -333,10 +435,10 @@ WantedBy=multi-user.target default.target ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 42, + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, PodmanVersion: "CI", - CreateCommand: []string{"I'll get stripped", "container", "run", "-d", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"}, + CreateCommand: []string{"I'll get stripped", "run", "-d", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"}, EnvVariable: EnvVariable, pod: &podInfo{ PodIDFile: "/tmp/pod-foobar.pod-id-file", @@ -352,10 +454,10 @@ WantedBy=multi-user.target default.target ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 42, + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, PodmanVersion: "CI", - CreateCommand: []string{"I'll get stripped", "container", "run", "--detach", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"}, + CreateCommand: []string{"I'll get stripped", "run", "--detach", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN"}, EnvVariable: EnvVariable, }, goodNameNewDetach, @@ -368,16 +470,144 @@ WantedBy=multi-user.target default.target ServiceName: "container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", ContainerNameOrID: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", - CreateCommand: []string{"I'll get stripped", "container", "run", "awesome-image:latest"}, + CreateCommand: []string{"I'll get stripped", "run", "awesome-image:latest"}, EnvVariable: EnvVariable, }, goodIDNew, true, false, }, + {"good with explicit detach=true param", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "run", "--detach=true", "awesome-image:latest"}, + EnvVariable: EnvVariable, + }, + genGoodNewDetach("--detach=true"), + true, + false, + }, + {"good with explicit detach=false param", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "run", "--detach=false", "awesome-image:latest"}, + EnvVariable: EnvVariable, + }, + genGoodNewDetach("-d"), + true, + false, + }, + {"good with explicit detach=false param", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "run", "--name", "test", "-p", "80:80", "--detach=false", "awesome-image:latest", "somecmd", "--detach=false"}, + EnvVariable: EnvVariable, + }, + goodNameNewDetachFalseWithCmd, + true, + false, + }, + {"good with multiple detach=false params", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "run", "--name", "test", "-p", "80:80", "--detach=false", "--detach=false", "awesome-image:latest", "somecmd", "--detach=false"}, + EnvVariable: EnvVariable, + }, + goodNameNewDetachFalseWithCmd, + true, + false, + }, + {"good with multiple shorthand params detach first", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "run", "-dti", "awesome-image:latest"}, + EnvVariable: EnvVariable, + }, + genGoodNewDetach("-dti"), + true, + false, + }, + {"good with multiple shorthand params detach last", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "run", "-tid", "awesome-image:latest"}, + EnvVariable: EnvVariable, + }, + genGoodNewDetach("-tid"), + true, + false, + }, + {"good with root flags", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "--events-backend", "none", "--runroot", "/root", "run", "awesome-image:latest"}, + EnvVariable: EnvVariable, + }, + goodNewRootFlags, + true, + false, + }, + {"good with container create", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + RestartPolicy: "always", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "container", "create", "awesome-image:latest"}, + EnvVariable: EnvVariable, + }, + goodContainerCreate, + true, + false, + }, } for _, tt := range tests { test := tt @@ -390,9 +620,7 @@ WantedBy=multi-user.target default.target t.Errorf("CreateContainerSystemdUnit() error = \n%v, wantErr \n%v", err, test.wantErr) return } - if got != test.want { - t.Errorf("CreateContainerSystemdUnit() = \n%v\n---------> want\n%v", got, test.want) - } + assert.Equal(t, test.want, got) }) } } diff --git a/pkg/systemd/generate/pods.go b/pkg/systemd/generate/pods.go index 3c57b03fb..7678a240f 100644 --- a/pkg/systemd/generate/pods.go +++ b/pkg/systemd/generate/pods.go @@ -14,6 +14,7 @@ import ( "github.com/containers/podman/v2/version" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "github.com/spf13/pflag" ) // podInfo contains data required for generating a pod's systemd @@ -44,6 +45,9 @@ type podInfo struct { // Executable is the path to the podman executable. Will be auto-filled if // left empty. Executable string + // RootFlags contains the root flags which were used to create the container + // Only used with --new + RootFlags string // TimeStamp at the time of creating the unit file. Will be set internally. TimeStamp string // CreateCommand is the full command plus arguments of the process the @@ -60,6 +64,8 @@ type podInfo struct { ExecStartPre2 string // ExecStart of the unit. ExecStart string + // TimeoutStopSec of the unit. + TimeoutStopSec uint // ExecStop of the unit. ExecStop string // ExecStopPost of the unit. @@ -72,6 +78,7 @@ Before={{- range $index, $value := .RequiredServices -}}{{if $index}} {{end}}{{ [Service] Environment={{.EnvVariable}}=%n Restart={{.RestartPolicy}} +TimeoutStopSec={{.TimeoutStopSec}} {{- if .ExecStartPre1}} ExecStartPre={{.ExecStartPre1}} {{- end}} @@ -82,7 +89,6 @@ ExecStart={{.ExecStart}} ExecStop={{.ExecStop}} ExecStopPost={{.ExecStopPost}} PIDFile={{.PIDFile}} -KillMode=none Type=forking [Install] @@ -262,7 +268,8 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions) if podCreateIndex == 0 { return "", errors.Errorf("pod does not appear to be created via `podman pod create`: %v", info.CreateCommand) } - podRootArgs = info.CreateCommand[0 : podCreateIndex-2] + podRootArgs = info.CreateCommand[1 : podCreateIndex-1] + info.RootFlags = strings.Join(quoteArguments(podRootArgs), " ") podCreateArgs = filterPodFlags(info.CreateCommand[podCreateIndex+1:]) } // We're hard-coding the first five arguments and append the @@ -275,17 +282,26 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions) "--pod-id-file", "{{.PodIDFile}}"}...) // Presence check for certain flags/options. - hasNameParam := false - hasReplaceParam := false - for _, p := range podCreateArgs { - switch p { - case "--name": - hasNameParam = true - case "--replace": - hasReplaceParam = true - } + fs := pflag.NewFlagSet("args", pflag.ContinueOnError) + fs.ParseErrorsWhitelist.UnknownFlags = true + fs.Usage = func() {} + fs.SetInterspersed(false) + fs.String("name", "", "") + fs.Bool("replace", false, "") + fs.Parse(podCreateArgs) + + hasNameParam := fs.Lookup("name").Changed + hasReplaceParam, err := fs.GetBool("replace") + if err != nil { + return "", err } if hasNameParam && !hasReplaceParam { + if fs.Changed("replace") { + // this can only happen if --replace=false is set + // in that case we need to remove it otherwise we + // would overwrite the previous replace arg to false + podCreateArgs = removeReplaceArg(podCreateArgs, fs.NArg()) + } podCreateArgs = append(podCreateArgs, "--replace") } @@ -294,10 +310,12 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions) info.ExecStartPre1 = "/bin/rm -f {{.PIDFile}} {{.PodIDFile}}" info.ExecStartPre2 = strings.Join(startCommand, " ") - info.ExecStart = "{{.Executable}} pod start --pod-id-file {{.PodIDFile}}" - info.ExecStop = "{{.Executable}} pod stop --ignore --pod-id-file {{.PodIDFile}} {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}}" - info.ExecStopPost = "{{.Executable}} pod rm --ignore -f --pod-id-file {{.PodIDFile}}" + info.ExecStart = "{{.Executable}} {{if .RootFlags}}{{ .RootFlags}} {{end}}pod start --pod-id-file {{.PodIDFile}}" + info.ExecStop = "{{.Executable}} {{if .RootFlags}}{{ .RootFlags}} {{end}}pod stop --ignore --pod-id-file {{.PodIDFile}} {{if (ge .StopTimeout 0)}}-t {{.StopTimeout}}{{end}}" + info.ExecStopPost = "{{.Executable}} {{if .RootFlags}}{{ .RootFlags}} {{end}}pod rm --ignore -f --pod-id-file {{.PodIDFile}}" } + info.TimeoutStopSec = minTimeoutStopSec + info.StopTimeout + if info.PodmanVersion == "" { info.PodmanVersion = version.Version.String() } diff --git a/pkg/systemd/generate/pods_test.go b/pkg/systemd/generate/pods_test.go index 7f1f63b7e..1c6330160 100644 --- a/pkg/systemd/generate/pods_test.go +++ b/pkg/systemd/generate/pods_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/stretchr/testify/assert" ) func TestValidateRestartPolicyPod(t *testing.T) { @@ -50,11 +51,11 @@ Before=container-1.service container-2.service [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=always +TimeoutStopSec=102 ExecStart=/usr/bin/podman start jadda-jadda-infra -ExecStop=/usr/bin/podman stop -t 10 jadda-jadda-infra -ExecStopPost=/usr/bin/podman stop -t 10 jadda-jadda-infra -PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid -KillMode=none +ExecStop=/usr/bin/podman stop -t 42 jadda-jadda-infra +ExecStopPost=/usr/bin/podman stop -t 42 jadda-jadda-infra +PIDFile=/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid Type=forking [Install] @@ -75,13 +76,67 @@ Before=container-1.service container-2.service [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Restart=on-failure +TimeoutStopSec=70 ExecStartPre=/bin/rm -f %t/pod-123abc.pid %t/pod-123abc.pod-id ExecStartPre=/usr/bin/podman pod create --infra-conmon-pidfile %t/pod-123abc.pid --pod-id-file %t/pod-123abc.pod-id --name foo "bar=arg with space" --replace ExecStart=/usr/bin/podman pod start --pod-id-file %t/pod-123abc.pod-id ExecStop=/usr/bin/podman pod stop --ignore --pod-id-file %t/pod-123abc.pod-id -t 10 ExecStopPost=/usr/bin/podman pod rm --ignore -f --pod-id-file %t/pod-123abc.pod-id PIDFile=%t/pod-123abc.pid -KillMode=none +Type=forking + +[Install] +WantedBy=multi-user.target default.target +` + + podGoodNamedNewWithRootArgs := `# pod-123abc.service +# autogenerated by Podman CI + +[Unit] +Description=Podman pod-123abc.service +Documentation=man:podman-generate-systemd(1) +Wants=network.target +After=network-online.target +Requires=container-1.service container-2.service +Before=container-1.service container-2.service + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=on-failure +TimeoutStopSec=70 +ExecStartPre=/bin/rm -f %t/pod-123abc.pid %t/pod-123abc.pod-id +ExecStartPre=/usr/bin/podman --events-backend none --runroot /root pod create --infra-conmon-pidfile %t/pod-123abc.pid --pod-id-file %t/pod-123abc.pod-id --name foo "bar=arg with space" --replace +ExecStart=/usr/bin/podman --events-backend none --runroot /root pod start --pod-id-file %t/pod-123abc.pod-id +ExecStop=/usr/bin/podman --events-backend none --runroot /root pod stop --ignore --pod-id-file %t/pod-123abc.pod-id -t 10 +ExecStopPost=/usr/bin/podman --events-backend none --runroot /root pod rm --ignore -f --pod-id-file %t/pod-123abc.pod-id +PIDFile=%t/pod-123abc.pid +Type=forking + +[Install] +WantedBy=multi-user.target default.target +` + + podGoodNamedNewWithReplaceFalse := `# pod-123abc.service +# autogenerated by Podman CI + +[Unit] +Description=Podman pod-123abc.service +Documentation=man:podman-generate-systemd(1) +Wants=network.target +After=network-online.target +Requires=container-1.service container-2.service +Before=container-1.service container-2.service + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=on-failure +TimeoutStopSec=70 +ExecStartPre=/bin/rm -f %t/pod-123abc.pid %t/pod-123abc.pod-id +ExecStartPre=/usr/bin/podman pod create --infra-conmon-pidfile %t/pod-123abc.pid --pod-id-file %t/pod-123abc.pod-id --name foo --replace +ExecStart=/usr/bin/podman pod start --pod-id-file %t/pod-123abc.pod-id +ExecStop=/usr/bin/podman pod stop --ignore --pod-id-file %t/pod-123abc.pod-id -t 10 +ExecStopPost=/usr/bin/podman pod rm --ignore -f --pod-id-file %t/pod-123abc.pod-id +PIDFile=%t/pod-123abc.pid Type=forking [Install] @@ -101,10 +156,27 @@ WantedBy=multi-user.target default.target ServiceName: "pod-123abc", InfraNameOrID: "jadda-jadda-infra", RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 10, + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, PodmanVersion: "CI", RequiredServices: []string{"container-1", "container-2"}, + CreateCommand: []string{"podman", "pod", "create", "--name", "foo", "bar=arg with space"}, + }, + podGood, + false, + false, + }, + {"pod with root args", + podInfo{ + Executable: "/usr/bin/podman", + ServiceName: "pod-123abc", + InfraNameOrID: "jadda-jadda-infra", + RestartPolicy: "always", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 42, + PodmanVersion: "CI", + RequiredServices: []string{"container-1", "container-2"}, + CreateCommand: []string{"podman", "--events-backend", "none", "--runroot", "/root", "pod", "create", "--name", "foo", "bar=arg with space"}, }, podGood, false, @@ -116,7 +188,7 @@ WantedBy=multi-user.target default.target ServiceName: "pod-123abc", InfraNameOrID: "jadda-jadda-infra", RestartPolicy: "on-failure", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", RequiredServices: []string{"container-1", "container-2"}, @@ -126,6 +198,38 @@ WantedBy=multi-user.target default.target true, false, }, + {"pod --new with root args", + podInfo{ + Executable: "/usr/bin/podman", + ServiceName: "pod-123abc", + InfraNameOrID: "jadda-jadda-infra", + RestartPolicy: "on-failure", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + RequiredServices: []string{"container-1", "container-2"}, + CreateCommand: []string{"podman", "--events-backend", "none", "--runroot", "/root", "pod", "create", "--name", "foo", "bar=arg with space"}, + }, + podGoodNamedNewWithRootArgs, + true, + false, + }, + {"pod --new with --replace=false", + podInfo{ + Executable: "/usr/bin/podman", + ServiceName: "pod-123abc", + InfraNameOrID: "jadda-jadda-infra", + RestartPolicy: "on-failure", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + RequiredServices: []string{"container-1", "container-2"}, + CreateCommand: []string{"podman", "pod", "create", "--name", "foo", "--replace=false"}, + }, + podGoodNamedNewWithReplaceFalse, + true, + false, + }, } for _, tt := range tests { @@ -139,9 +243,7 @@ WantedBy=multi-user.target default.target t.Errorf("CreatePodSystemdUnit() error = \n%v, wantErr \n%v", err, test.wantErr) return } - if got != test.want { - t.Errorf("CreatePodSystemdUnit() = \n%v\n---------> want\n%v", got, test.want) - } + assert.Equal(t, test.want, got) }) } } |