diff options
Diffstat (limited to 'pkg')
221 files changed, 14083 insertions, 2404 deletions
diff --git a/pkg/annotations/annotations.go b/pkg/annotations/annotations.go index 19b1029d1..8badab20d 100644 --- a/pkg/annotations/annotations.go +++ b/pkg/annotations/annotations.go @@ -94,7 +94,7 @@ const ( // StdinOnce is the stdin_once annotation StdinOnce = "io.kubernetes.cri-o.StdinOnce" - // Volumes is the volumes annotatoin + // Volumes is the volumes annotation Volumes = "io.kubernetes.cri-o.Volumes" // HostNetwork indicates whether the host network namespace is used or not diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index 7a3e5dd84..0f89c859e 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "net/http" + "sort" "strconv" "strings" "syscall" @@ -13,6 +14,8 @@ import ( "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/ps" "github.com/containers/podman/v2/pkg/signal" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" @@ -78,10 +81,6 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) { } func ListContainers(w http.ResponseWriter, r *http.Request) { - var ( - containers []*libpod.Container - err error - ) runtime := r.Context().Value("runtime").(*libpod.Runtime) decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { @@ -97,22 +96,48 @@ func ListContainers(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return } - if query.All { - containers, err = runtime.GetAllContainers() - } else { - containers, err = runtime.GetRunningContainers() + + filterFuncs := make([]libpod.ContainerFilter, 0, len(query.Filters)) + all := query.All || query.Limit > 0 + if len(query.Filters) > 0 { + for k, v := range query.Filters { + generatedFunc, err := filters.GenerateContainerFilterFuncs(k, v, runtime) + if err != nil { + utils.InternalServerError(w, err) + return + } + filterFuncs = append(filterFuncs, generatedFunc) + } } + + // Docker thinks that if status is given as an input, then we should override + // the all setting and always deal with all containers. + if len(query.Filters["status"]) > 0 { + all = true + } + if !all { + runningOnly, err := filters.GenerateContainerFilterFuncs("status", []string{define.ContainerStateRunning.String()}, runtime) + if err != nil { + utils.InternalServerError(w, err) + return + } + filterFuncs = append(filterFuncs, runningOnly) + } + + containers, err := runtime.GetContainers(filterFuncs...) if err != nil { utils.InternalServerError(w, err) return } if _, found := r.URL.Query()["limit"]; found && query.Limit > 0 { - last := query.Limit - if len(containers) > last { - containers = containers[len(containers)-last:] + // Sort the libpod containers + sort.Sort(ps.SortCreateTime{SortContainers: containers}) + // we should perform the lopping before we start getting + // the expensive information on containers + if len(containers) > query.Limit { + containers = containers[:query.Limit] } } - // TODO filters still need to be applied var list = make([]*handlers.Container, len(containers)) for i, ctnr := range containers { api, err := LibpodToContainer(ctnr, query.Size) diff --git a/pkg/api/handlers/compat/containers_archive.go b/pkg/api/handlers/compat/containers_archive.go index d8197415c..083c72ce8 100644 --- a/pkg/api/handlers/compat/containers_archive.go +++ b/pkg/api/handlers/compat/containers_archive.go @@ -3,11 +3,13 @@ package compat import ( "fmt" "net/http" + "os" "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/api/handlers/utils" "github.com/containers/podman/v2/pkg/copy" + "github.com/containers/podman/v2/pkg/domain/infra/abi" "github.com/gorilla/schema" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -44,58 +46,47 @@ func handleHeadAndGet(w http.ResponseWriter, r *http.Request, decoder *schema.De } containerName := utils.GetName(r) - - ctr, err := runtime.LookupContainer(containerName) - if errors.Cause(err) == define.ErrNoSuchCtr { - utils.Error(w, "Not found.", http.StatusNotFound, errors.Wrap(err, "the container doesn't exists")) + containerEngine := abi.ContainerEngine{Libpod: runtime} + statReport, err := containerEngine.ContainerStat(r.Context(), containerName, query.Path) + + // NOTE + // The statReport may actually be set even in case of an error. That's + // the case when we're looking at a symlink pointing to nirvana. In + // such cases, we really need the FileInfo but we also need the error. + if statReport != nil { + statHeader, err := copy.EncodeFileInfo(&statReport.FileInfo) + if err != nil { + utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) + return + } + w.Header().Add(copy.XDockerContainerPathStatHeader, statHeader) + } + + if errors.Cause(err) == define.ErrNoSuchCtr || errors.Cause(err) == copy.ENOENT { + // 404 is returned for an absent container and path. The + // clients must deal with it accordingly. + utils.Error(w, "Not found.", http.StatusNotFound, err) return } else if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) return } - source, err := copy.CopyItemForContainer(ctr, query.Path, true, true) - defer source.CleanUp() - if err != nil { - utils.Error(w, "Not found.", http.StatusNotFound, errors.Wrapf(err, "error stating container path %q", query.Path)) - return - } - - // NOTE: Docker always sets the header. - info, err := source.Stat() - if err != nil { - utils.Error(w, "Not found.", http.StatusNotFound, errors.Wrapf(err, "error stating container path %q", query.Path)) - return - } - statHeader, err := copy.EncodeFileInfo(info) - if err != nil { - utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) - return - } - w.Header().Add(copy.XDockerContainerPathStatHeader, statHeader) - // Our work is done when the user is interested in the header only. if r.Method == http.MethodHead { w.WriteHeader(http.StatusOK) return } - // Alright, the users wants data from the container. - destination, err := copy.CopyItemForWriter(w) - if err != nil { - utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) - return - } - - copier, err := copy.GetCopier(&source, &destination, false) + copyFunc, err := containerEngine.ContainerCopyToArchive(r.Context(), containerName, query.Path, w) if err != nil { utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) return } + w.Header().Set("Content-Type", "application/x-tar") w.WriteHeader(http.StatusOK) - if err := copier.Copy(); err != nil { - logrus.Errorf("Error during copy: %v", err) - return + if err := copyFunc(); err != nil { + logrus.Error(err.Error()) } } @@ -113,36 +104,22 @@ func handlePut(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder, return } - ctrName := utils.GetName(r) - - ctr, err := runtime.LookupContainer(ctrName) - if err != nil { - utils.Error(w, "Not found", http.StatusNotFound, errors.Wrapf(err, "the %s container doesn't exists", ctrName)) - return - } + containerName := utils.GetName(r) + containerEngine := abi.ContainerEngine{Libpod: runtime} - destination, err := copy.CopyItemForContainer(ctr, query.Path, true, false) - defer destination.CleanUp() - if err != nil { - utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) + copyFunc, err := containerEngine.ContainerCopyFromArchive(r.Context(), containerName, query.Path, r.Body) + if errors.Cause(err) == define.ErrNoSuchCtr || os.IsNotExist(err) { + // 404 is returned for an absent container and path. The + // clients must deal with it accordingly. + utils.Error(w, "Not found.", http.StatusNotFound, errors.Wrap(err, "the container doesn't exists")) return - } - - source, err := copy.CopyItemForReader(r.Body) - defer source.CleanUp() - if err != nil { - utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) + } else if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) return } - copier, err := copy.GetCopier(&source, &destination, false) - if err != nil { - utils.Error(w, "Something went wrong", http.StatusInternalServerError, err) - return - } w.WriteHeader(http.StatusOK) - if err := copier.Copy(); err != nil { - logrus.Errorf("Error during copy: %v", err) - return + if err := copyFunc(); err != nil { + logrus.Error(err.Error()) } } diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go index 409a74de2..6e85872b2 100644 --- a/pkg/api/handlers/compat/containers_create.go +++ b/pkg/api/handlers/compat/containers_create.go @@ -66,7 +66,20 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "make cli opts()")) return } - sg := specgen.NewSpecGenerator(newImage.ID(), cliOpts.RootFS) + + imgNameOrID := newImage.ID() + // if the img had multi names with the same sha256 ID, should use the InputName, not the ID + if len(newImage.Names()) > 1 { + imageRef, err := utils.ParseDockerReference(newImage.InputName) + if err != nil { + utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) + return + } + // maybe the InputName has no tag, so use full name to display + imgNameOrID = imageRef.DockerReference().String() + } + + sg := specgen.NewSpecGenerator(imgNameOrID, cliOpts.RootFS) if err := common.FillOutSpecGen(sg, cliOpts, args); err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "fill out specgen")) return diff --git a/pkg/api/handlers/compat/containers_prune.go b/pkg/api/handlers/compat/containers_prune.go index 2cfeebcce..b3d26b8f4 100644 --- a/pkg/api/handlers/compat/containers_prune.go +++ b/pkg/api/handlers/compat/containers_prune.go @@ -4,19 +4,14 @@ import ( "net/http" "github.com/containers/podman/v2/libpod" - lpfilters "github.com/containers/podman/v2/libpod/filters" "github.com/containers/podman/v2/pkg/api/handlers/utils" - "github.com/containers/podman/v2/pkg/domain/entities" - "github.com/docker/docker/api/types" + "github.com/containers/podman/v2/pkg/domain/entities/reports" + "github.com/containers/podman/v2/pkg/domain/filters" "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) @@ -29,7 +24,7 @@ func PruneContainers(w http.ResponseWriter, r *http.Request) { } filterFuncs := make([]libpod.ContainerFilter, 0, len(query.Filters)) for k, v := range query.Filters { - generatedFunc, err := lpfilters.GenerateContainerFilterFuncs(k, v, runtime) + generatedFunc, err := filters.GenerateContainerFilterFuncs(k, v, runtime) if err != nil { utils.InternalServerError(w, err) return @@ -49,36 +44,21 @@ func PruneContainers(w http.ResponseWriter, r *http.Request) { return } - prunedContainers, pruneErrors, err := runtime.PruneContainers(filterFuncs) + report, 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) - } - } - report := types.ContainersPruneReport{ - ContainersDeleted: delContainers, - SpaceReclaimed: uint64(space), - } utils.WriteResponse(w, http.StatusOK, report) } func PruneContainersHelper(w http.ResponseWriter, r *http.Request, filterFuncs []libpod.ContainerFilter) ( - *entities.ContainerPruneReport, error) { + []*reports.PruneReport, error) { runtime := r.Context().Value("runtime").(*libpod.Runtime) - prunedContainers, pruneErrors, err := runtime.PruneContainers(filterFuncs) + reports, err := runtime.PruneContainers(filterFuncs) if err != nil { utils.InternalServerError(w, err) return nil, err } - - report := &entities.ContainerPruneReport{ - Err: pruneErrors, - ID: prunedContainers, - } - return report, nil + return reports, nil } diff --git a/pkg/api/handlers/compat/events.go b/pkg/api/handlers/compat/events.go index f74491a8f..82a74e419 100644 --- a/pkg/api/handlers/compat/events.go +++ b/pkg/api/handlers/compat/events.go @@ -33,6 +33,8 @@ func filtersFromRequest(r *http.Request) ([]string, error) { if _, found := r.URL.Query()["filters"]; found { raw = []byte(r.Form.Get("filters")) + } else if _, found := r.URL.Query()["Filters"]; found { + raw = []byte(r.Form.Get("Filters")) } else { return []string{}, nil } @@ -73,7 +75,7 @@ func GetEvents(w http.ResponseWriter, r *http.Request) { ) // NOTE: the "filters" parameter is extracted separately for backwards - // compat via `fitlerFromRequest()`. + // compat via `filterFromRequest()`. query := struct { Since string `schema:"since"` Until string `schema:"until"` @@ -95,7 +97,6 @@ func GetEvents(w http.ResponseWriter, r *http.Request) { utils.Error(w, "failed to parse parameters", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return } - eventChannel := make(chan *events.Event) errorChannel := make(chan error) @@ -110,6 +111,7 @@ func GetEvents(w http.ResponseWriter, r *http.Request) { Until: query.Until, } errorChannel <- runtime.Events(r.Context(), readOpts) + }() var flush = func() {} @@ -130,8 +132,8 @@ func GetEvents(w http.ResponseWriter, r *http.Request) { if err != nil { // FIXME StatusOK already sent above cannot send 500 here utils.InternalServerError(w, err) - return } + return case evt := <-eventChannel: if evt == nil { continue diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go index a51dd8ed3..9d7503aba 100644 --- a/pkg/api/handlers/compat/images.go +++ b/pkg/api/handlers/compat/images.go @@ -22,6 +22,7 @@ import ( "github.com/gorilla/schema" "github.com/opencontainers/go-digest" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) // mergeNameAndTagOrDigest creates an image reference as string from the @@ -98,21 +99,23 @@ func PruneImages(w http.ResponseWriter, r *http.Request) { filters = append(filters, fmt.Sprintf("%s=%s", k, val)) } } - pruneCids, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, filters) + imagePruneReports, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, filters) if err != nil { utils.InternalServerError(w, err) return } - for _, p := range pruneCids { + reclaimedSpace := uint64(0) + for _, p := range imagePruneReports { idr = append(idr, types.ImageDeleteResponseItem{ - Deleted: p, + Deleted: p.Id, }) + reclaimedSpace = reclaimedSpace + p.Size } // 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 + SpaceReclaimed: reclaimedSpace, } utils.WriteResponse(w, http.StatusOK, handlers.ImagesPruneReport{ImagesPruneReport: ipr}) } @@ -386,6 +389,12 @@ func LoadImages(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to create tempfile")) return } + defer func() { + err := os.Remove(f.Name()) + if err != nil { + logrus.Errorf("Failed to remove temporary file: %v.", err) + } + }() if err := SaveFromBody(f, r); err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to write temporary file")) return diff --git a/pkg/api/handlers/compat/images_search.go b/pkg/api/handlers/compat/images_search.go index b3ceae3ee..6808cdad5 100644 --- a/pkg/api/handlers/compat/images_search.go +++ b/pkg/api/handlers/compat/images_search.go @@ -8,6 +8,7 @@ import ( "github.com/containers/podman/v2/libpod/image" "github.com/containers/podman/v2/pkg/api/handlers/utils" "github.com/containers/podman/v2/pkg/auth" + "github.com/docker/docker/api/types/registry" "github.com/gorilla/schema" "github.com/pkg/errors" ) @@ -77,5 +78,18 @@ func SearchImages(w http.ResponseWriter, r *http.Request) { utils.BadRequest(w, "term", query.Term, err) return } - utils.WriteResponse(w, http.StatusOK, results) + + compatResults := make([]registry.SearchResult, 0, len(results)) + for _, result := range results { + compatResult := registry.SearchResult{ + Name: result.Name, + Description: result.Description, + StarCount: result.Stars, + IsAutomated: result.Automated == "[OK]", + IsOfficial: result.Official == "[OK]", + } + compatResults = append(compatResults, compatResult) + } + + utils.WriteResponse(w, http.StatusOK, compatResults) } diff --git a/pkg/api/handlers/compat/info.go b/pkg/api/handlers/compat/info.go index 4b3a390f1..5adbeb031 100644 --- a/pkg/api/handlers/compat/info.go +++ b/pkg/api/handlers/compat/info.go @@ -48,7 +48,7 @@ func GetInfo(w http.ResponseWriter, r *http.Request) { stateInfo := getContainersState(runtime) sysInfo := sysinfo.New(true) - // FIXME: Need to expose if runtime supports Checkpoint'ing + // FIXME: Need to expose if runtime supports Checkpointing // liveRestoreEnabled := criu.CheckForCriu() && configInfo.RuntimeSupportsCheckpoint() info := &handlers.Info{Info: docker.Info{ diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go index fe13971b0..f0b922885 100644 --- a/pkg/api/handlers/compat/networks.go +++ b/pkg/api/handlers/compat/networks.go @@ -131,7 +131,7 @@ func getNetworkResourceByNameOrID(nameOrID string, runtime *libpod.Runtime, filt Name: conf.Name, ID: network.GetNetworkID(conf.Name), Created: time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)), // nolint: unconvert - Scope: "", + Scope: "local", Driver: network.DefaultNetworkDriver, EnableIPv6: false, IPAM: dockerNetwork.IPAM{ @@ -197,7 +197,7 @@ func ListNetworks(w http.ResponseWriter, r *http.Request) { } var reports []*types.NetworkResource - logrus.Errorf("netNames: %q", strings.Join(netNames, ", ")) + logrus.Debugf("netNames: %q", strings.Join(netNames, ", ")) for _, name := range netNames { report, err := getNetworkResourceByNameOrID(name, runtime, query.Filters) if err != nil { @@ -239,7 +239,7 @@ func CreateNetwork(w http.ResponseWriter, r *http.Request) { Internal: networkCreate.Internal, Labels: networkCreate.Labels, } - if networkCreate.IPAM != nil && networkCreate.IPAM.Config != nil { + if networkCreate.IPAM != nil && len(networkCreate.IPAM.Config) > 0 { if len(networkCreate.IPAM.Config) > 1 { utils.InternalServerError(w, errors.New("compat network create can only support one IPAM config")) return diff --git a/pkg/api/handlers/compat/resize.go b/pkg/api/handlers/compat/resize.go index bdc051d73..cc8c6ef0a 100644 --- a/pkg/api/handlers/compat/resize.go +++ b/pkg/api/handlers/compat/resize.go @@ -20,8 +20,8 @@ func ResizeTTY(w http.ResponseWriter, r *http.Request) { // /containers/{id}/resize query := struct { - height uint16 `schema:"h"` - width uint16 `schema:"w"` + Height uint16 `schema:"h"` + Width uint16 `schema:"w"` }{ // override any golang type defaults } @@ -33,8 +33,8 @@ func ResizeTTY(w http.ResponseWriter, r *http.Request) { } sz := remotecommand.TerminalSize{ - Width: query.width, - Height: query.height, + Width: query.Width, + Height: query.Height, } var status int diff --git a/pkg/api/handlers/compat/volumes.go b/pkg/api/handlers/compat/volumes.go index 71b848932..1188d8f84 100644 --- a/pkg/api/handlers/compat/volumes.go +++ b/pkg/api/handlers/compat/volumes.go @@ -3,6 +3,7 @@ package compat import ( "encoding/json" "net/http" + "net/url" "time" "github.com/containers/podman/v2/libpod" @@ -207,7 +208,7 @@ func RemoveVolume(w http.ResponseWriter, r *http.Request) { * using the volume at the same time". * * With this in mind, we only consider the `force` query parameter when we - * hunt for specified volume by name, using it to seletively return a 204 + * hunt for specified volume by name, using it to selectively return a 204 * or blow up depending on `force` being truthy or falsey/unset * respectively. */ @@ -230,7 +231,7 @@ func RemoveVolume(w http.ResponseWriter, r *http.Request) { utils.VolumeNotFound(w, name, err) } else { // Volume does not exist and `force` is truthy - this emulates what - // Docker would do when told to `force` removal of a nonextant + // Docker would do when told to `force` removal of a nonexistent // volume utils.WriteResponse(w, http.StatusNoContent, nil) } @@ -254,22 +255,23 @@ func PruneVolumes(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return } - // TODO: We have no ability to pass pruning filters to `PruneVolumes()` so - // we'll explicitly reject the request if we see any - if len(query.Filters) > 0 { - utils.InternalServerError(w, errors.New("filters for pruning volumes is not implemented")) + + f := (url.Values)(query.Filters) + filterFuncs, err := filters.GenerateVolumeFilters(f) + if err != nil { + utils.Error(w, "Something when wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse filters for %s", f.Encode())) return } - pruned, err := runtime.PruneVolumes(r.Context()) + pruned, err := runtime.PruneVolumes(r.Context(), filterFuncs) if err != nil { utils.InternalServerError(w, err) return } prunedIds := make([]string, 0, len(pruned)) - for k := range pruned { + for _, v := range pruned { // XXX: This drops any pruning per-volume error messages on the floor - prunedIds = append(prunedIds, k) + prunedIds = append(prunedIds, v.Id) } pruneResponse := docker_api_types.VolumesPruneReport{ VolumesDeleted: prunedIds, diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index 6145207ca..979a8adc4 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -51,7 +51,7 @@ func ImageExists(w http.ResponseWriter, r *http.Request) { return } if !report.Value { - utils.Error(w, "Something went wrong.", http.StatusNotFound, errors.Wrapf(nil, "failed to find image %s", name)) + utils.Error(w, "Something went wrong.", http.StatusNotFound, errors.Errorf("failed to find image %s", name)) return } utils.WriteResponse(w, http.StatusNoContent, "") @@ -156,12 +156,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) { @@ -265,8 +265,14 @@ func ExportImages(w http.ResponseWriter, r *http.Request) { // Format is mandatory! Currently, we only support multi-image docker // archives. + if len(query.References) > 1 && query.Format != define.V2s2Archive { + utils.Error(w, "unsupported format", http.StatusInternalServerError, errors.Errorf("multi-image archives must use format of %s", define.V2s2Archive)) + return + + } + switch query.Format { - case define.V2s2Archive: + case define.V2s2Archive, define.OCIArchive: tmpfile, err := ioutil.TempFile("", "api.tar") if err != nil { utils.Error(w, "unable to create tmpfile", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile")) @@ -277,6 +283,13 @@ func ExportImages(w http.ResponseWriter, r *http.Request) { utils.Error(w, "unable to close tmpfile", http.StatusInternalServerError, errors.Wrap(err, "unable to close tempfile")) return } + case define.OCIManifestDir, define.V2s2ManifestDir: + tmpdir, err := ioutil.TempDir("", "save") + if err != nil { + utils.Error(w, "unable to create tmpdir", http.StatusInternalServerError, errors.Wrap(err, "unable to create tmpdir")) + return + } + output = tmpdir default: utils.Error(w, "unsupported format", http.StatusInternalServerError, errors.Errorf("unsupported format %q", query.Format)) return @@ -287,7 +300,7 @@ func ExportImages(w http.ResponseWriter, r *http.Request) { opts := entities.ImageSaveOptions{ Compress: query.Compress, Format: query.Format, - MultiImageArchive: true, + MultiImageArchive: len(query.References) > 1, Output: output, RemoveSignatures: true, } @@ -414,7 +427,6 @@ func PushImage(w http.ResponseWriter, r *http.Request) { }{ // 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 @@ -607,12 +619,12 @@ func UntagImage(w http.ResponseWriter, r *http.Request) { func SearchImages(w http.ResponseWriter, r *http.Request) { decoder := r.Context().Value("decoder").(*schema.Decoder) query := struct { - Term string `json:"term"` - Limit int `json:"limit"` - NoTrunc bool `json:"noTrunc"` - Filters []string `json:"filters"` - TLSVerify bool `json:"tlsVerify"` - ListTags bool `json:"listTags"` + Term string `json:"term"` + Limit int `json:"limit"` + NoTrunc bool `json:"noTrunc"` + Filters map[string][]string `json:"filters"` + TLSVerify bool `json:"tlsVerify"` + ListTags bool `json:"listTags"` }{ // This is where you can override the golang default value for one of fields } @@ -622,24 +634,44 @@ func SearchImages(w http.ResponseWriter, r *http.Request) { return } + filter := image.SearchFilter{} + if len(query.Filters) > 0 { + if len(query.Filters["stars"]) > 0 { + stars, err := strconv.Atoi(query.Filters["stars"][0]) + if err != nil { + utils.InternalServerError(w, err) + return + } + filter.Stars = stars + } + if len(query.Filters["is-official"]) > 0 { + isOfficial, err := strconv.ParseBool(query.Filters["is-official"][0]) + if err != nil { + utils.InternalServerError(w, err) + return + } + filter.IsOfficial = types.NewOptionalBool(isOfficial) + } + if len(query.Filters["is-automated"]) > 0 { + isAutomated, err := strconv.ParseBool(query.Filters["is-automated"][0]) + if err != nil { + utils.InternalServerError(w, err) + return + } + filter.IsAutomated = types.NewOptionalBool(isAutomated) + } + } options := image.SearchOptions{ Limit: query.Limit, NoTrunc: query.NoTrunc, ListTags: query.ListTags, + Filter: filter, } + if _, found := r.URL.Query()["tlsVerify"]; found { options.InsecureSkipTLSVerify = types.NewOptionalBool(!query.TLSVerify) } - if _, found := r.URL.Query()["filters"]; found { - filter, err := image.ParseSearchFilter(query.Filters) - if err != nil { - utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse filters parameter for %s", r.URL.String())) - return - } - options.Filter = *filter - } - _, authfile, key, err := auth.GetCredentials(r) if err != nil { utils.Error(w, "failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "failed to parse %q header for %s", key, r.URL.String())) @@ -678,10 +710,7 @@ func ImagesBatchRemove(w http.ResponseWriter, r *http.Request) { All bool `schema:"all"` Force bool `schema:"force"` Images []string `schema:"images"` - }{ - All: false, - Force: false, - } + }{} if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, @@ -690,10 +719,8 @@ func ImagesBatchRemove(w http.ResponseWriter, r *http.Request) { } opts := entities.ImageRemoveOptions{All: query.All, Force: query.Force} - imageEngine := abi.ImageEngine{Libpod: runtime} rmReport, rmErrors := imageEngine.Remove(r.Context(), query.Images, opts) - strErrs := errorhandling.ErrorsToStrings(rmErrors) report := handlers.LibpodImagesRemoveReport{ImageRemoveReport: *rmReport, Errors: strErrs} utils.WriteResponse(w, http.StatusOK, report) 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/system.go b/pkg/api/handlers/libpod/system.go index b157dfc7b..130e563ae 100644 --- a/pkg/api/handlers/libpod/system.go +++ b/pkg/api/handlers/libpod/system.go @@ -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(w, 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 b0d40fd8b..6f9537515 100644 --- a/pkg/api/handlers/libpod/volumes.go +++ b/pkg/api/handlers/libpod/volumes.go @@ -3,11 +3,13 @@ package libpod import ( "encoding/json" "net/http" + "net/url" "github.com/containers/podman/v2/libpod" "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" @@ -177,20 +179,30 @@ 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) ) - pruned, err := runtime.PruneVolumes(r.Context()) + query := struct { + Filters map[string][]string `schema:"filters"` + }{ + // override any golang type defaults + } + + if err := decoder.Decode(&query, r.URL.Query()); err != nil { + return nil, err + } + + f := (url.Values)(query.Filters) + filterFuncs, err := filters.GenerateVolumeFilters(f) 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, - }) + + reports, err := runtime.PruneVolumes(r.Context(), filterFuncs) + if err != nil { + return nil, err } return reports, nil } diff --git a/pkg/api/handlers/utils/pods.go b/pkg/api/handlers/utils/pods.go index 7506dbfd1..0fe3a308b 100644 --- a/pkg/api/handlers/utils/pods.go +++ b/pkg/api/handlers/utils/pods.go @@ -4,8 +4,8 @@ import ( "net/http" "github.com/containers/podman/v2/libpod" - lpfilters "github.com/containers/podman/v2/libpod/filters" "github.com/containers/podman/v2/pkg/domain/entities" + dfilters "github.com/containers/podman/v2/pkg/domain/filters" "github.com/gorilla/schema" ) @@ -31,7 +31,7 @@ func GetPods(w http.ResponseWriter, r *http.Request) ([]*entities.ListPodsReport filters := make([]libpod.PodFilter, 0, len(query.Filters)) for k, v := range query.Filters { - f, err := lpfilters.GeneratePodFilterFunc(k, v) + f, err := dfilters.GeneratePodFilterFunc(k, v) if err != nil { return nil, err } diff --git a/pkg/api/server/register_archive.go b/pkg/api/server/register_archive.go index b2d2543c4..b20d89cc2 100644 --- a/pkg/api/server/register_archive.go +++ b/pkg/api/server/register_archive.go @@ -4,11 +4,10 @@ import ( "net/http" "github.com/containers/podman/v2/pkg/api/handlers/compat" - "github.com/containers/podman/v2/pkg/api/handlers/libpod" "github.com/gorilla/mux" ) -func (s *APIServer) registerAchiveHandlers(r *mux.Router) error { +func (s *APIServer) registerArchiveHandlers(r *mux.Router) error { // swagger:operation PUT /containers/{name}/archive compat putArchive // --- // summary: Put files into a container @@ -92,7 +91,7 @@ func (s *APIServer) registerAchiveHandlers(r *mux.Router) error { Libpod */ - // swagger:operation POST /libpod/containers/{name}/copy libpod libpodPutArchive + // swagger:operation POST /libpod/containers/{name}/archive libpod libpodPutArchive // --- // summary: Copy files into a container // description: Copy a tar archive of files into a container @@ -133,7 +132,7 @@ func (s *APIServer) registerAchiveHandlers(r *mux.Router) error { // 500: // $ref: "#/responses/InternalError" - // swagger:operation GET /libpod/containers/{name}/copy libpod libpodGetArchive + // swagger:operation GET /libpod/containers/{name}/archive libpod libpodGetArchive // --- // summary: Copy files from a container // description: Copy a tar archive of files from a container @@ -164,8 +163,7 @@ func (s *APIServer) registerAchiveHandlers(r *mux.Router) error { // $ref: "#/responses/NoSuchContainer" // 500: // $ref: "#/responses/InternalError" - r.HandleFunc(VersionedPath("/libpod/containers/{name}/copy"), s.APIHandler(libpod.Archive)).Methods(http.MethodGet, http.MethodPost) - r.HandleFunc(VersionedPath("/libpod/containers/{name}/archive"), s.APIHandler(libpod.Archive)).Methods(http.MethodGet, http.MethodPost) + r.HandleFunc(VersionedPath("/libpod/containers/{name}/archive"), s.APIHandler(compat.Archive)).Methods(http.MethodGet, http.MethodPut, http.MethodHead) return nil } diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index 870c6a90c..b80dea545 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -690,6 +690,7 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // - `label`=(`key` or `"key=value"`) of an container label // - `name=<name>` a container's name // - `network`=(`<network id>` or `<network name>`) + // - `pod`=(`<pod id>` or `<pod name>`) // - `publish`=(`<port>[/<proto>]` or `<startport-endport>/[<proto>]`) // - `since`=(`<container id>` or `<container name>`) // - `status`=(`created`, `restarting`, `running`, `removing`, `paused`, `exited` or `dead`) diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index c2423218a..7e6de8783 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -666,7 +666,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // - in: query // name: destination // type: string - // description: Allows for pushing the image to a different destintation than the image refers to. + // description: Allows for pushing the image to a different destination than the image refers to. // - in: query // name: tlsVerify // description: Require TLS verification. diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go index 09b6079e4..046f6561c 100644 --- a/pkg/api/server/server.go +++ b/pkg/api/server/server.go @@ -108,7 +108,7 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li for _, fn := range []func(*mux.Router) error{ server.registerAuthHandlers, - server.registerAchiveHandlers, + server.registerArchiveHandlers, server.registerContainersHandlers, server.registerDistributionHandlers, server.registerEventsHandlers, diff --git a/pkg/api/server/swagger.go b/pkg/api/server/swagger.go index 6d349bb7d..45253e01a 100644 --- a/pkg/api/server/swagger.go +++ b/pkg/api/server/swagger.go @@ -4,6 +4,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" ) // No such image @@ -170,7 +171,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/autoupdate/autoupdate.go b/pkg/autoupdate/autoupdate.go index 44709f291..77851f534 100644 --- a/pkg/autoupdate/autoupdate.go +++ b/pkg/autoupdate/autoupdate.go @@ -44,7 +44,7 @@ var supportedPolicies = map[string]Policy{ "image": PolicyNewImage, } -// LookupPolicy looksup the corresponding Policy for the specified +// LookupPolicy looks up the corresponding Policy for the specified // string. If none is found, an errors is returned including the list of // supported policies. // diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go index f2cb3147c..7b26037eb 100644 --- a/pkg/bindings/connection.go +++ b/pkg/bindings/connection.go @@ -182,30 +182,65 @@ func pingNewConnection(ctx context.Context) error { func sshClient(_url *url.URL, secure bool, passPhrase string, identity string) (Connection, error) { // if you modify the authmethods or their conditionals, you will also need to make similar // changes in the client (currently cmd/podman/system/connection/add getUDS). - authMethods := []ssh.AuthMethod{} + + var signers []ssh.Signer // order Signers are appended to this list determines which key is presented to server + if len(identity) > 0 { - auth, err := terminal.PublicKey(identity, []byte(passPhrase)) + s, err := terminal.PublicKey(identity, []byte(passPhrase)) if err != nil { return Connection{}, errors.Wrapf(err, "failed to parse identity %q", identity) } - logrus.Debugf("public key signer enabled for identity %q", identity) - authMethods = append(authMethods, auth) + + signers = append(signers, s) + logrus.Debugf("SSH Ident Key %q %s %s", identity, ssh.FingerprintSHA256(s.PublicKey()), s.PublicKey().Type()) } if sock, found := os.LookupEnv("SSH_AUTH_SOCK"); found { - logrus.Debugf("Found SSH_AUTH_SOCK %q, ssh-agent signer enabled", sock) + logrus.Debugf("Found SSH_AUTH_SOCK %q, ssh-agent signer(s) enabled", sock) c, err := net.Dial("unix", sock) if err != nil { return Connection{}, err } - a := agent.NewClient(c) - authMethods = append(authMethods, ssh.PublicKeysCallback(a.Signers)) + + agentSigners, err := agent.NewClient(c).Signers() + if err != nil { + return Connection{}, err + } + signers = append(signers, agentSigners...) + + if logrus.IsLevelEnabled(logrus.DebugLevel) { + for _, s := range agentSigners { + logrus.Debugf("SSH Agent Key %s %s", ssh.FingerprintSHA256(s.PublicKey()), s.PublicKey().Type()) + } + } + } + + var authMethods []ssh.AuthMethod + if len(signers) > 0 { + var dedup = make(map[string]ssh.Signer) + // Dedup signers based on fingerprint, ssh-agent keys override CONTAINER_SSHKEY + for _, s := range signers { + fp := ssh.FingerprintSHA256(s.PublicKey()) + if _, found := dedup[fp]; found { + logrus.Debugf("Dedup SSH Key %s %s", ssh.FingerprintSHA256(s.PublicKey()), s.PublicKey().Type()) + } + dedup[fp] = s + } + + var uniq []ssh.Signer + for _, s := range dedup { + uniq = append(uniq, s) + } + authMethods = append(authMethods, ssh.PublicKeysCallback(func() ([]ssh.Signer, error) { + return uniq, nil + })) } if pw, found := _url.User.Password(); found { authMethods = append(authMethods, ssh.Password(pw)) } + if len(authMethods) == 0 { callback := func() (string, error) { pass, err := terminal.ReadPassword("Login password:") diff --git a/pkg/bindings/containers/archive.go b/pkg/bindings/containers/archive.go new file mode 100644 index 000000000..d1bbc0b95 --- /dev/null +++ b/pkg/bindings/containers/archive.go @@ -0,0 +1,92 @@ +package containers + +import ( + "context" + "io" + "net/http" + "net/url" + + "github.com/containers/podman/v2/pkg/bindings" + "github.com/containers/podman/v2/pkg/copy" + "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/pkg/errors" +) + +// Stat checks if the specified path is on the container. Note that the stat +// report may be set even in case of an error. This happens when the path +// resolves to symlink pointing to a non-existent path. +func Stat(ctx context.Context, nameOrID string, path string) (*entities.ContainerStatReport, error) { + conn, err := bindings.GetClient(ctx) + if err != nil { + return nil, err + } + params := url.Values{} + params.Set("path", path) + + response, err := conn.DoRequest(nil, http.MethodHead, "/containers/%s/archive", params, nil, nameOrID) + if err != nil { + return nil, err + } + + var finalErr error + if response.StatusCode == http.StatusNotFound { + finalErr = copy.ENOENT + } else if response.StatusCode != http.StatusOK { + finalErr = errors.New(response.Status) + } + + var statReport *entities.ContainerStatReport + + fileInfo, err := copy.ExtractFileInfoFromHeader(&response.Header) + if err != nil && finalErr == nil { + return nil, err + } + + if fileInfo != nil { + statReport = &entities.ContainerStatReport{FileInfo: *fileInfo} + } + + return statReport, finalErr +} + +func CopyFromArchive(ctx context.Context, nameOrID string, path string, reader io.Reader) (entities.ContainerCopyFunc, error) { + conn, err := bindings.GetClient(ctx) + if err != nil { + return nil, err + } + params := url.Values{} + params.Set("path", path) + + return func() error { + response, err := conn.DoRequest(reader, http.MethodPut, "/containers/%s/archive", params, nil, nameOrID) + if err != nil { + return err + } + if response.StatusCode != http.StatusOK { + return errors.New(response.Status) + } + return response.Process(nil) + }, nil +} + +func CopyToArchive(ctx context.Context, nameOrID string, path string, writer io.Writer) (entities.ContainerCopyFunc, error) { + conn, err := bindings.GetClient(ctx) + if err != nil { + return nil, err + } + params := url.Values{} + params.Set("path", path) + + response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/archive", params, nil, nameOrID) + if err != nil { + return nil, err + } + if response.StatusCode != http.StatusOK { + return nil, response.Process(nil) + } + + return func() error { + _, err := io.Copy(writer, response.Body) + return err + }, nil +} diff --git a/pkg/bindings/containers/attach.go b/pkg/bindings/containers/attach.go index 91b155fc4..69ae7a32f 100644 --- a/pkg/bindings/containers/attach.go +++ b/pkg/bindings/containers/attach.go @@ -26,7 +26,10 @@ import ( ) // Attach attaches to a running container -func Attach(ctx context.Context, nameOrID string, detachKeys *string, logs, stream *bool, stdin io.Reader, stdout io.Writer, stderr io.Writer, attachReady chan bool) error { +func Attach(ctx context.Context, nameOrID string, stdin io.Reader, stdout io.Writer, stderr io.Writer, attachReady chan bool, options *AttachOptions) error { + if options == nil { + options = new(AttachOptions) + } isSet := struct { stdin bool stdout bool @@ -55,27 +58,24 @@ func Attach(ctx context.Context, nameOrID string, detachKeys *string, logs, stre } // Do we need to wire in stdin? - ctnr, err := Inspect(ctx, nameOrID, bindings.PFalse) + ctnr, err := Inspect(ctx, nameOrID, new(InspectOptions).WithSize(false)) if err != nil { return err } - params := url.Values{} + params, err := options.ToParams() + if err != nil { + return err + } detachKeysInBytes := []byte{} - if detachKeys != nil { - params.Add("detachKeys", *detachKeys) + if options.Changed("DetachKeys") { + params.Add("detachKeys", options.GetDetachKeys()) - detachKeysInBytes, err = term.ToBytes(*detachKeys) + detachKeysInBytes, err = term.ToBytes(options.GetDetachKeys()) if err != nil { return errors.Wrapf(err, "invalid detach keys") } } - if logs != nil { - params.Add("logs", fmt.Sprintf("%t", *logs)) - } - if stream != nil { - params.Add("stream", fmt.Sprintf("%t", *stream)) - } if isSet.stdin { params.Add("stdin", "true") } @@ -278,13 +278,19 @@ func DemuxFrame(r io.Reader, buffer []byte, length int) (frame []byte, err error } // ResizeContainerTTY sets container's TTY height and width in characters -func ResizeContainerTTY(ctx context.Context, nameOrID string, height *int, width *int) error { - return resizeTTY(ctx, bindings.JoinURL("containers", nameOrID, "resize"), height, width) +func ResizeContainerTTY(ctx context.Context, nameOrID string, options *ResizeTTYOptions) error { + if options == nil { + options = new(ResizeTTYOptions) + } + return resizeTTY(ctx, bindings.JoinURL("containers", nameOrID, "resize"), options.Height, options.Width) } // ResizeExecTTY sets session's TTY height and width in characters -func ResizeExecTTY(ctx context.Context, nameOrID string, height *int, width *int) error { - return resizeTTY(ctx, bindings.JoinURL("exec", nameOrID, "resize"), height, width) +func ResizeExecTTY(ctx context.Context, nameOrID string, options *ResizeExecTTYOptions) error { + if options == nil { + options = new(ResizeExecTTYOptions) + } + return resizeTTY(ctx, bindings.JoinURL("exec", nameOrID, "resize"), options.Height, options.Width) } // resizeTTY set size of TTY of container @@ -337,9 +343,9 @@ func attachHandleResize(ctx, winCtx context.Context, winChange chan os.Signal, i var resizeErr error if isExec { - resizeErr = ResizeExecTTY(ctx, id, &h, &w) + resizeErr = ResizeExecTTY(ctx, id, new(ResizeExecTTYOptions).WithHeight(h).WithWidth(w)) } else { - resizeErr = ResizeContainerTTY(ctx, id, &h, &w) + resizeErr = ResizeContainerTTY(ctx, id, new(ResizeTTYOptions).WithHeight(h).WithWidth(w)) } if resizeErr != nil { logrus.Warnf("failed to resize TTY: %v", err) @@ -361,7 +367,10 @@ func setRawTerminal(file *os.File) (*terminal.State, error) { } // ExecStartAndAttach starts and attaches to a given exec session. -func ExecStartAndAttach(ctx context.Context, sessionID string, streams *define.AttachStreams) error { +func ExecStartAndAttach(ctx context.Context, sessionID string, options *ExecStartAndAttachOptions) error { + if options == nil { + options = new(ExecStartAndAttachOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return err @@ -450,10 +459,10 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, streams *define.A go attachHandleResize(ctx, winCtx, winChange, true, sessionID, terminalFile) } - if streams.AttachInput { + if options.GetAttachInput() { go func() { logrus.Debugf("Copying STDIN to socket") - _, err := utils.CopyDetachable(socket, streams.InputStream, []byte{}) + _, err := utils.CopyDetachable(socket, options.InputStream, []byte{}) if err != nil { logrus.Error("failed to write input to service: " + err.Error()) } @@ -463,11 +472,11 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, streams *define.A buffer := make([]byte, 1024) if isTerm { logrus.Debugf("Handling terminal attach to exec") - if !streams.AttachOutput { + if !options.GetAttachOutput() { return fmt.Errorf("exec session %s has a terminal and must have STDOUT enabled", sessionID) } // If not multiplex'ed, read from server and write to stdout - _, err := utils.CopyDetachable(streams.OutputStream, socket, []byte{}) + _, err := utils.CopyDetachable(options.GetOutputStream(), socket, []byte{}) if err != nil { return err } @@ -489,22 +498,22 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, streams *define.A switch { case fd == 0: - if streams.AttachInput { + if options.GetAttachInput() { // Write STDIN to STDOUT (echoing characters // typed by another attach session) - if _, err := streams.OutputStream.Write(frame[0:l]); err != nil { + if _, err := options.GetOutputStream().Write(frame[0:l]); err != nil { return err } } case fd == 1: - if streams.AttachOutput { - if _, err := streams.OutputStream.Write(frame[0:l]); err != nil { + if options.GetAttachOutput() { + if _, err := options.GetOutputStream().Write(frame[0:l]); err != nil { return err } } case fd == 2: - if streams.AttachError { - if _, err := streams.ErrorStream.Write(frame[0:l]); err != nil { + if options.GetAttachError() { + if _, err := options.GetErrorStream().Write(frame[0:l]); err != nil { return err } } diff --git a/pkg/bindings/containers/checkpoint.go b/pkg/bindings/containers/checkpoint.go index f466f8a34..c250558a6 100644 --- a/pkg/bindings/containers/checkpoint.go +++ b/pkg/bindings/containers/checkpoint.go @@ -3,8 +3,6 @@ package containers import ( "context" "net/http" - "net/url" - "strconv" "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/domain/entities" @@ -12,27 +10,18 @@ import ( // Checkpoint checkpoints the given container (identified by nameOrID). All additional // options are options and allow for more fine grained control of the checkpoint process. -func Checkpoint(ctx context.Context, nameOrID string, keep, leaveRunning, tcpEstablished, ignoreRootFS *bool, export *string) (*entities.CheckpointReport, error) { +func Checkpoint(ctx context.Context, nameOrID string, options *CheckpointOptions) (*entities.CheckpointReport, error) { var report entities.CheckpointReport + if options == nil { + options = new(CheckpointOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if keep != nil { - params.Set("keep", strconv.FormatBool(*keep)) - } - if leaveRunning != nil { - params.Set("leaveRunning", strconv.FormatBool(*leaveRunning)) - } - if tcpEstablished != nil { - params.Set("TCPestablished", strconv.FormatBool(*tcpEstablished)) - } - if ignoreRootFS != nil { - params.Set("ignoreRootFS", strconv.FormatBool(*ignoreRootFS)) - } - if export != nil { - params.Set("export", *export) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/checkpoint", params, nil, nameOrID) if err != nil { @@ -43,33 +32,23 @@ func Checkpoint(ctx context.Context, nameOrID string, keep, leaveRunning, tcpEst // Restore restores a checkpointed container to running. The container is identified by the nameOrID option. All // additional options are optional and allow finer control of the restore process. -func Restore(ctx context.Context, nameOrID string, keep, tcpEstablished, ignoreRootFS, ignoreStaticIP, ignoreStaticMAC *bool, name, importArchive *string) (*entities.RestoreReport, error) { +func Restore(ctx context.Context, nameOrID string, options *RestoreOptions) (*entities.RestoreReport, error) { var report entities.RestoreReport + if options == nil { + options = new(RestoreOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if keep != nil { - params.Set("keep", strconv.FormatBool(*keep)) - } - if tcpEstablished != nil { - params.Set("TCPestablished", strconv.FormatBool(*tcpEstablished)) - } - if ignoreRootFS != nil { - params.Set("ignoreRootFS", strconv.FormatBool(*ignoreRootFS)) - } - if ignoreStaticIP != nil { - params.Set("ignoreStaticIP", strconv.FormatBool(*ignoreStaticIP)) - } - if ignoreStaticMAC != nil { - params.Set("ignoreStaticMAC", strconv.FormatBool(*ignoreStaticMAC)) - } - if name != nil { - params.Set("name", *name) + params, err := options.ToParams() + if err != nil { + return nil, err } - if importArchive != nil { - params.Set("import", *importArchive) + // The import key is a reserved golang term + params.Del("ImportArchive") + if i := options.GetImportAchive(); options.Changed("ImportArchive") { + params.Set("import", i) } response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/restore", params, nil, nameOrID) if err != nil { diff --git a/pkg/bindings/containers/commit.go b/pkg/bindings/containers/commit.go index 9ab4456a3..6205c75bd 100644 --- a/pkg/bindings/containers/commit.go +++ b/pkg/bindings/containers/commit.go @@ -3,8 +3,6 @@ package containers import ( "context" "net/http" - "net/url" - "strconv" "github.com/containers/podman/v2/pkg/api/handlers" "github.com/containers/podman/v2/pkg/bindings" @@ -12,35 +10,20 @@ import ( // Commit creates a container image from a container. The container is defined by nameOrID. Use // the CommitOptions for finer grain control on characteristics of the resulting image. -func Commit(ctx context.Context, nameOrID string, options CommitOptions) (handlers.IDResponse, error) { +func Commit(ctx context.Context, nameOrID string, options *CommitOptions) (handlers.IDResponse, error) { + if options == nil { + options = new(CommitOptions) + } id := handlers.IDResponse{} conn, err := bindings.GetClient(ctx) if err != nil { return id, err } - params := url.Values{} - params.Set("container", nameOrID) - if options.Author != nil { - params.Set("author", *options.Author) - } - for _, change := range options.Changes { - params.Set("changes", change) - } - if options.Comment != nil { - params.Set("comment", *options.Comment) - } - if options.Format != nil { - params.Set("format", *options.Format) - } - if options.Pause != nil { - params.Set("pause", strconv.FormatBool(*options.Pause)) - } - if options.Repo != nil { - params.Set("repo", *options.Repo) - } - if options.Tag != nil { - params.Set("tag", *options.Tag) + params, err := options.ToParams() + if err != nil { + return handlers.IDResponse{}, err } + params.Set("container", nameOrID) response, err := conn.DoRequest(nil, http.MethodPost, "/commit", params, nil) if err != nil { return id, err diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go index 4331ae6c2..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" ) @@ -25,34 +26,18 @@ var ( // the most recent number of containers. The pod and size booleans indicate that pod information and rootfs // size information should also be included. Finally, the sync bool synchronizes the OCI runtime and // container state. -func List(ctx context.Context, filters map[string][]string, all *bool, last *int, namespace, size, sync *bool) ([]entities.ListContainer, error) { // nolint:typecheck +func List(ctx context.Context, options *ListOptions) ([]entities.ListContainer, error) { // nolint:typecheck + if options == nil { + options = new(ListOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } var containers []entities.ListContainer - params := url.Values{} - if all != nil { - params.Set("all", strconv.FormatBool(*all)) - } - if last != nil { - params.Set("limit", strconv.Itoa(*last)) - } - if size != nil { - params.Set("size", strconv.FormatBool(*size)) - } - if sync != nil { - params.Set("sync", strconv.FormatBool(*sync)) - } - if namespace != nil { - params.Set("namespace", strconv.FormatBool(*namespace)) - } - if filters != nil { - filterString, err := bindings.FiltersToString(filters) - if err != nil { - return nil, err - } - params.Set("filters", filterString) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodGet, "/containers/json", params, nil) if err != nil { @@ -65,19 +50,18 @@ func List(ctx context.Context, filters map[string][]string, all *bool, last *int // 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, filters map[string][]string) (*entities.ContainerPruneReport, error) { - var reports *entities.ContainerPruneReport +func Prune(ctx context.Context, options *PruneOptions) ([]*reports.PruneReport, error) { + if options == nil { + options = new(PruneOptions) + } + var reports []*reports.PruneReport conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if filters != nil { - filterString, err := bindings.FiltersToString(filters) - if err != nil { - return nil, err - } - params.Set("filters", filterString) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodPost, "/containers/prune", params, nil) if err != nil { @@ -89,17 +73,20 @@ func Prune(ctx context.Context, filters map[string][]string) (*entities.Containe // Remove removes a container from local storage. The force bool designates // that the container should be removed forcibly (example, even it is running). The volumes // bool dictates that a container's volumes should also be removed. -func Remove(ctx context.Context, nameOrID string, force, volumes *bool) error { +func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) error { + if options == nil { + options = new(RemoveOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return err } params := url.Values{} - if force != nil { - params.Set("force", strconv.FormatBool(*force)) + if v := options.GetVolumes(); options.Changed("Volumes") { + params.Set("v", strconv.FormatBool(v)) } - if volumes != nil { - params.Set("v", strconv.FormatBool(*volumes)) + if force := options.GetForce(); options.Changed("Force") { + params.Set("force", strconv.FormatBool(force)) } response, err := conn.DoRequest(nil, http.MethodDelete, "/containers/%s", params, nil, nameOrID) if err != nil { @@ -112,14 +99,17 @@ func Remove(ctx context.Context, nameOrID string, force, volumes *bool) error { // or a partial/full ID. The size bool determines whether the size of the container's root filesystem // should be calculated. Calculating the size of a container requires extra work from the filesystem and // is therefore slower. -func Inspect(ctx context.Context, nameOrID string, size *bool) (*define.InspectContainerData, error) { +func Inspect(ctx context.Context, nameOrID string, options *InspectOptions) (*define.InspectContainerData, error) { + if options == nil { + options = new(InspectOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if size != nil { - params.Set("size", strconv.FormatBool(*size)) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/json", params, nil, nameOrID) if err != nil { @@ -132,12 +122,18 @@ func Inspect(ctx context.Context, nameOrID string, size *bool) (*define.InspectC // Kill sends a given signal to a given container. The signal should be the string // representation of a signal like 'SIGKILL'. The nameOrID can be a container name // or a partial/full ID -func Kill(ctx context.Context, nameOrID string, sig string) error { +func Kill(ctx context.Context, nameOrID string, sig string, options *KillOptions) error { + if options == nil { + options = new(KillOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return err } - params := url.Values{} + params, err := options.ToParams() + if err != nil { + return err + } params.Set("signal", sig) response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/kill", params, nil, nameOrID) if err != nil { @@ -149,7 +145,11 @@ func Kill(ctx context.Context, nameOrID string, sig string) error { // Pause pauses a given container. The nameOrID can be a container name // or a partial/full ID. -func Pause(ctx context.Context, nameOrID string) error { +func Pause(ctx context.Context, nameOrID string, options *PauseOptions) error { + if options == nil { + options = new(PauseOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return err @@ -164,14 +164,17 @@ func Pause(ctx context.Context, nameOrID string) error { // Restart restarts a running container. The nameOrID can be a container name // or a partial/full ID. The optional timeout specifies the number of seconds to wait // for the running container to stop before killing it. -func Restart(ctx context.Context, nameOrID string, timeout *int) error { +func Restart(ctx context.Context, nameOrID string, options *RestartOptions) error { + if options == nil { + options = new(RestartOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return err } params := url.Values{} - if timeout != nil { - params.Set("t", strconv.Itoa(*timeout)) + if options.Changed("Timeout") { + params.Set("t", strconv.Itoa(options.GetTimeout())) } response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/restart", params, nil, nameOrID) if err != nil { @@ -183,15 +186,18 @@ func Restart(ctx context.Context, nameOrID string, timeout *int) error { // Start starts a non-running container.The nameOrID can be a container name // or a partial/full ID. The optional parameter for detach keys are to override the default // detach key sequence. -func Start(ctx context.Context, nameOrID string, detachKeys *string) error { +func Start(ctx context.Context, nameOrID string, options *StartOptions) error { + if options == nil { + options = new(StartOptions) + } logrus.Infof("Going to start container %q", nameOrID) conn, err := bindings.GetClient(ctx) if err != nil { return err } - params := url.Values{} - if detachKeys != nil { - params.Set("detachKeys", *detachKeys) + params, err := options.ToParams() + if err != nil { + return err } response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/start", params, nil, nameOrID) if err != nil { @@ -200,14 +206,18 @@ func Start(ctx context.Context, nameOrID string, detachKeys *string) error { return response.Process(nil) } -func Stats(ctx context.Context, containers []string, stream *bool) (chan entities.ContainerStatsReport, error) { +func Stats(ctx context.Context, containers []string, options *StatsOptions) (chan entities.ContainerStatsReport, error) { + if options == nil { + options = new(StatsOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if stream != nil { - params.Set("stream", strconv.FormatBool(*stream)) + params, err := options.ToParams() + if err != nil { + return nil, err } for _, c := range containers { params.Add("containers", c) @@ -225,8 +235,8 @@ func Stats(ctx context.Context, containers []string, stream *bool) (chan entitie dec := json.NewDecoder(response.Body) doStream := true - if stream != nil { - doStream = *stream + if options.Changed("Stream") { + doStream = options.GetStream() } streamLabel: // label to flatten the scope @@ -253,16 +263,18 @@ func Stats(ctx context.Context, containers []string, stream *bool) (chan entitie // Top gathers statistics about the running processes in a container. The nameOrID can be a container name // or a partial/full ID. The descriptors allow for specifying which data to collect from the process. -func Top(ctx context.Context, nameOrID string, descriptors []string) ([]string, error) { +func Top(ctx context.Context, nameOrID string, options *TopOptions) ([]string, error) { + if options == nil { + options = new(TopOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } params := url.Values{} - - if len(descriptors) > 0 { - // flatten the slice into one string - params.Set("ps_args", strings.Join(descriptors, ",")) + if options.Changed("Descriptors") { + ps_args := strings.Join(options.GetDescriptors(), ",") + params.Add("ps_args", ps_args) } response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/top", params, nil, nameOrID) if err != nil { @@ -287,7 +299,11 @@ func Top(ctx context.Context, nameOrID string, descriptors []string) ([]string, // Unpause resumes the given paused container. The nameOrID can be a container name // or a partial/full ID. -func Unpause(ctx context.Context, nameOrID string) error { +func Unpause(ctx context.Context, nameOrID string, options *UnpauseOptions) error { + if options == nil { + options = new(UnpauseOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return err @@ -302,15 +318,18 @@ func Unpause(ctx context.Context, nameOrID string) error { // Wait blocks until the given container reaches a condition. If not provided, the condition will // default to stopped. If the condition is stopped, an exit code for the container will be provided. The // nameOrID can be a container name or a partial/full ID. -func Wait(ctx context.Context, nameOrID string, condition *define.ContainerStatus) (int32, error) { // nolint +func Wait(ctx context.Context, nameOrID string, options *WaitOptions) (int32, error) { // nolint + if options == nil { + options = new(WaitOptions) + } var exitCode int32 conn, err := bindings.GetClient(ctx) if err != nil { return exitCode, err } params := url.Values{} - if condition != nil { - params.Set("condition", condition.String()) + if options.Changed("Condition") { + params.Set("condition", options.GetCondition().String()) } response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/wait", params, nil, nameOrID) if err != nil { @@ -338,14 +357,17 @@ func Exists(ctx context.Context, nameOrID string, external bool) (bool, error) { // Stop stops a running container. The timeout is optional. The nameOrID can be a container name // or a partial/full ID -func Stop(ctx context.Context, nameOrID string, timeout *uint) error { - params := url.Values{} - conn, err := bindings.GetClient(ctx) +func Stop(ctx context.Context, nameOrID string, options *StopOptions) error { + if options == nil { + options = new(StopOptions) + } + params, err := options.ToParams() if err != nil { return err } - if timeout != nil { - params.Set("t", strconv.Itoa(int(*timeout))) + conn, err := bindings.GetClient(ctx) + if err != nil { + return err } response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/stop", params, nil, nameOrID) if err != nil { @@ -356,7 +378,11 @@ func Stop(ctx context.Context, nameOrID string, timeout *uint) error { // Export creates a tarball of the given name or ID of a container. It // requires an io.Writer be provided to write the tarball. -func Export(ctx context.Context, nameOrID string, w io.Writer) error { +func Export(ctx context.Context, nameOrID string, w io.Writer, options *ExportOptions) error { + if options == nil { + options = new(ExportOptions) + } + _ = options params := url.Values{} conn, err := bindings.GetClient(ctx) if err != nil { @@ -376,7 +402,11 @@ func Export(ctx context.Context, nameOrID string, w io.Writer) error { // ContainerInit takes a created container and executes all of the // preparations to run the container except it will not start // or attach to the container -func ContainerInit(ctx context.Context, nameOrID string) error { +func ContainerInit(ctx context.Context, nameOrID string, options *InitOptions) error { + if options == nil { + options = new(InitOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return err @@ -391,7 +421,11 @@ func ContainerInit(ctx context.Context, nameOrID string) error { return response.Process(nil) } -func ShouldRestart(ctx context.Context, nameOrID string) (bool, error) { +func ShouldRestart(ctx context.Context, nameOrID string, options *ShouldRestartOptions) (bool, error) { + if options == nil { + options = new(ShouldRestartOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return false, err diff --git a/pkg/bindings/containers/create.go b/pkg/bindings/containers/create.go index 5c29ec577..177cf2e9c 100644 --- a/pkg/bindings/containers/create.go +++ b/pkg/bindings/containers/create.go @@ -11,8 +11,12 @@ import ( jsoniter "github.com/json-iterator/go" ) -func CreateWithSpec(ctx context.Context, s *specgen.SpecGenerator) (entities.ContainerCreateResponse, error) { +func CreateWithSpec(ctx context.Context, s *specgen.SpecGenerator, options *CreateOptions) (entities.ContainerCreateResponse, error) { var ccr entities.ContainerCreateResponse + if options == nil { + options = new(CreateOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return ccr, err diff --git a/pkg/bindings/containers/diff.go b/pkg/bindings/containers/diff.go index 1478bd940..015172360 100644 --- a/pkg/bindings/containers/diff.go +++ b/pkg/bindings/containers/diff.go @@ -9,7 +9,11 @@ import ( ) // Diff provides the changes between two container layers -func Diff(ctx context.Context, nameOrID string) ([]archive.Change, error) { +func Diff(ctx context.Context, nameOrID string, options *DiffOptions) ([]archive.Change, error) { + if options == nil { + options = new(DiffOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err diff --git a/pkg/bindings/containers/exec.go b/pkg/bindings/containers/exec.go index e080077c8..98ca975a0 100644 --- a/pkg/bindings/containers/exec.go +++ b/pkg/bindings/containers/exec.go @@ -50,7 +50,11 @@ func ExecCreate(ctx context.Context, nameOrID string, config *handlers.ExecCreat // ExecInspect inspects an existing exec session, returning detailed information // about it. -func ExecInspect(ctx context.Context, sessionID string) (*define.InspectExecSession, error) { +func ExecInspect(ctx context.Context, sessionID string, options *ExecInspectOptions) (*define.InspectExecSession, error) { + if options == nil { + options = new(ExecInspectOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err @@ -72,7 +76,11 @@ func ExecInspect(ctx context.Context, sessionID string) (*define.InspectExecSess } // ExecStart starts (but does not attach to) a given exec session. -func ExecStart(ctx context.Context, sessionID string) error { +func ExecStart(ctx context.Context, sessionID string, options *ExecStartOptions) error { + if options == nil { + options = new(ExecStartOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return err diff --git a/pkg/bindings/containers/healthcheck.go b/pkg/bindings/containers/healthcheck.go index 9de6ffbe0..44b27629b 100644 --- a/pkg/bindings/containers/healthcheck.go +++ b/pkg/bindings/containers/healthcheck.go @@ -10,7 +10,11 @@ import ( // RunHealthCheck executes the container's healthcheck and returns the health status of the // container. -func RunHealthCheck(ctx context.Context, nameOrID string) (*define.HealthCheckResults, error) { +func RunHealthCheck(ctx context.Context, nameOrID string, options *HealthCheckOptions) (*define.HealthCheckResults, error) { + if options == nil { + options = new(HealthCheckOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err diff --git a/pkg/bindings/containers/logs.go b/pkg/bindings/containers/logs.go index a73517bac..04307d880 100644 --- a/pkg/bindings/containers/logs.go +++ b/pkg/bindings/containers/logs.go @@ -5,7 +5,6 @@ import ( "fmt" "io" "net/http" - "net/url" "strconv" "github.com/containers/podman/v2/pkg/bindings" @@ -14,35 +13,20 @@ import ( // Logs obtains a container's logs given the options provided. The logs are then sent to the // stdout|stderr channels as strings. -func Logs(ctx context.Context, nameOrID string, opts LogOptions, stdoutChan, stderrChan chan string) error { +func Logs(ctx context.Context, nameOrID string, options *LogOptions, stdoutChan, stderrChan chan string) error { + if options == nil { + options = new(LogOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return err } - params := url.Values{} - if opts.Follow != nil { - params.Set("follow", strconv.FormatBool(*opts.Follow)) - } - if opts.Since != nil { - params.Set("since", *opts.Since) - } - if opts.Stderr != nil { - params.Set("stderr", strconv.FormatBool(*opts.Stderr)) - } - if opts.Stdout != nil { - params.Set("stdout", strconv.FormatBool(*opts.Stdout)) - } - if opts.Tail != nil { - params.Set("tail", *opts.Tail) - } - if opts.Timestamps != nil { - params.Set("timestamps", strconv.FormatBool(*opts.Timestamps)) - } - if opts.Until != nil { - params.Set("until", *opts.Until) + params, err := options.ToParams() + if err != nil { + return err } // The API requires either stdout|stderr be used. If neither are specified, we specify stdout - if opts.Stdout == nil && opts.Stderr == nil { + if options.Stdout == nil && options.Stderr == nil { params.Set("stdout", strconv.FormatBool(true)) } response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/logs", params, nil, nameOrID) diff --git a/pkg/bindings/containers/mount.go b/pkg/bindings/containers/mount.go index 4c2e0c188..4fd9f89bc 100644 --- a/pkg/bindings/containers/mount.go +++ b/pkg/bindings/containers/mount.go @@ -9,7 +9,11 @@ import ( // Mount mounts an existing container to the filesystem. It returns the path // of the mounted container in string format. -func Mount(ctx context.Context, nameOrID string) (string, error) { +func Mount(ctx context.Context, nameOrID string, options *MountOptions) (string, error) { + if options == nil { + options = new(MountOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return "", err @@ -26,7 +30,11 @@ func Mount(ctx context.Context, nameOrID string) (string, error) { // Unmount unmounts a container from the filesystem. The container must not be running // or the unmount will fail. -func Unmount(ctx context.Context, nameOrID string) error { +func Unmount(ctx context.Context, nameOrID string, options *UnmountOptions) error { + if options == nil { + options = new(UnmountOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return err @@ -39,7 +47,11 @@ func Unmount(ctx context.Context, nameOrID string) error { } // GetMountedContainerPaths returns a map of mounted containers and their mount locations. -func GetMountedContainerPaths(ctx context.Context) (map[string]string, error) { +func GetMountedContainerPaths(ctx context.Context, options *MountedContainerPathsOptions) (map[string]string, error) { + if options == nil { + options = new(MountedContainerPathsOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err diff --git a/pkg/bindings/containers/types.go b/pkg/bindings/containers/types.go index f288c2944..24402e982 100644 --- a/pkg/bindings/containers/types.go +++ b/pkg/bindings/containers/types.go @@ -1,5 +1,13 @@ package containers +import ( + "bufio" + "io" + + "github.com/containers/podman/v2/libpod/define" +) + +//go:generate go run ../generator/generator.go LogOptions // LogOptions describe finer control of log content or // how the content is formatted. type LogOptions struct { @@ -12,6 +20,7 @@ type LogOptions struct { Until *string } +//go:generate go run ../generator/generator.go CommitOptions // CommitOptions describe details about the resulting committed // image as defined by repo and tag. None of these options // are required. @@ -24,3 +33,200 @@ type CommitOptions struct { Repo *string Tag *string } + +//go:generate go run ../generator/generator.go AttachOptions +// AttachOptions are optional options for attaching to containers +type AttachOptions struct { + DetachKeys *string + Logs *bool + Stream *bool +} + +//go:generate go run ../generator/generator.go CheckpointOptions +// CheckpointOptions are optional options for checkpointing containers +type CheckpointOptions struct { + Export *string + IgnoreRootfs *bool + Keep *bool + LeaveRunning *bool + TCPEstablished *bool +} + +//go:generate go run ../generator/generator.go RestoreOptions +// RestoreOptions are optional options for restoring containers +type RestoreOptions struct { + IgnoreRootfs *bool + IgnoreStaticIP *bool + IgnoreStaticMAC *bool + ImportAchive *string + Keep *bool + Name *string + TCPEstablished *bool +} + +//go:generate go run ../generator/generator.go CreateOptions +// CreateOptions are optional options for creating containers +type CreateOptions struct{} + +//go:generate go run ../generator/generator.go DiffOptions +// DiffOptions are optional options for creating containers +type DiffOptions struct{} + +//go:generate go run ../generator/generator.go ExecInspectOptions +// ExecInspectOptions are optional options for inspecting +// exec sessions +type ExecInspectOptions struct{} + +//go:generate go run ../generator/generator.go ExecStartOptions +// ExecStartOptions are optional options for starting +// exec sessions +type ExecStartOptions struct{} + +//go:generate go run ../generator/generator.go HealthCheckOptions +// HealthCheckOptions are optional options for checking +// the health of a container +type HealthCheckOptions struct{} + +//go:generate go run ../generator/generator.go MountOptions +// MountOptions are optional options for mounting +// containers +type MountOptions struct{} + +//go:generate go run ../generator/generator.go UnmountOptions +// UnmountOptions are optional options for unmounting +// containers +type UnmountOptions struct{} + +//go:generate go run ../generator/generator.go MountedContainerPathsOptions +// MountedContainerPathsOptions are optional options for getting +// container mount paths +type MountedContainerPathsOptions struct{} + +//go:generate go run ../generator/generator.go ListOptions +// ListOptions are optional options for listing containers +type ListOptions struct { + All *bool + Filters map[string][]string + Last *int + Namespace *bool + Size *bool + Sync *bool +} + +//go:generate go run ../generator/generator.go PruneOptions +// PruneOptions are optional options for pruning containers +type PruneOptions struct { + Filters map[string][]string +} + +//go:generate go run ../generator/generator.go RemoveOptions +// RemoveOptions are optional options for removing containers +type RemoveOptions struct { + Force *bool + Volumes *bool +} + +//go:generate go run ../generator/generator.go InspectOptions +// InspectOptions are optional options for inspecting containers +type InspectOptions struct { + Size *bool +} + +//go:generate go run ../generator/generator.go KillOptions +// KillOptions are optional options for killing containers +type KillOptions struct { +} + +//go:generate go run ../generator/generator.go PauseOptions +// PauseOptions are optional options for pausing containers +type PauseOptions struct{} + +//go:generate go run ../generator/generator.go RestartOptions +// RestartOptions are optional options for restarting containers +type RestartOptions struct { + Timeout *int +} + +//go:generate go run ../generator/generator.go StartOptions +// StartOptions are optional options for starting containers +type StartOptions struct { + DetachKeys *string +} + +//go:generate go run ../generator/generator.go StatsOptions +// StatsOptions are optional options for getting stats on containers +type StatsOptions struct { + Stream *bool +} + +//go:generate go run ../generator/generator.go TopOptions +// TopOptions are optional options for getting running +// processes in containers +type TopOptions struct { + Descriptors *[]string +} + +//go:generate go run ../generator/generator.go UnpauseOptions +// UnpauseOptions are optional options for unpausing containers +type UnpauseOptions struct{} + +//go:generate go run ../generator/generator.go WaitOptions +// WaitOptions are optional options for waiting on containers +type WaitOptions struct { + Condition *define.ContainerStatus +} + +//go:generate go run ../generator/generator.go StopOptions +// StopOptions are optional options for stopping containers +type StopOptions struct { + Timeout *uint +} + +//go:generate go run ../generator/generator.go ExportOptions +// ExportOptions are optional options for exporting containers +type ExportOptions struct{} + +//go:generate go run ../generator/generator.go InitOptions +// InitOptions are optional options for initing containers +type InitOptions struct{} + +//go:generate go run ../generator/generator.go ShouldRestartOptions +// ShouldRestartOptions +type ShouldRestartOptions struct{} + +//go:generate go run ../generator/generator.go ResizeTTYOptions +// ResizeTTYOptions are optional options for resizing +// container TTYs +type ResizeTTYOptions struct { + Height *int + Width *int +} + +//go:generate go run ../generator/generator.go ResizeExecTTYOptions +// ResizeExecTTYOptions are optional options for resizing +// container ExecTTYs +type ResizeExecTTYOptions struct { + Height *int + Width *int +} + +//go:generate go run ../generator/generator.go ExecStartAndAttachOptions +// ExecStartAndAttachOptions are optional options for resizing +// container ExecTTYs +type ExecStartAndAttachOptions struct { + // OutputStream will be attached to container's STDOUT + OutputStream *io.WriteCloser + // ErrorStream will be attached to container's STDERR + ErrorStream *io.WriteCloser + // InputStream will be attached to container's STDIN + InputStream *bufio.Reader + // AttachOutput is whether to attach to STDOUT + // If false, stdout will not be attached + AttachOutput *bool + // AttachError is whether to attach to STDERR + // If false, stdout will not be attached + AttachError *bool + // AttachInput is whether to attach to STDIN + // If false, stdout will not be attached + AttachInput *bool +} diff --git a/pkg/bindings/containers/types_attach_options.go b/pkg/bindings/containers/types_attach_options.go new file mode 100644 index 000000000..4ffb8ab17 --- /dev/null +++ b/pkg/bindings/containers/types_attach_options.go @@ -0,0 +1,136 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *AttachOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *AttachOptions) 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 + } + 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 +} + +// WithDetachKeys +func (o *AttachOptions) WithDetachKeys(value string) *AttachOptions { + v := &value + o.DetachKeys = v + return o +} + +// GetDetachKeys +func (o *AttachOptions) GetDetachKeys() string { + var detachKeys string + if o.DetachKeys == nil { + return detachKeys + } + return *o.DetachKeys +} + +// WithLogs +func (o *AttachOptions) WithLogs(value bool) *AttachOptions { + v := &value + o.Logs = v + return o +} + +// GetLogs +func (o *AttachOptions) GetLogs() bool { + var logs bool + if o.Logs == nil { + return logs + } + return *o.Logs +} + +// WithStream +func (o *AttachOptions) WithStream(value bool) *AttachOptions { + v := &value + o.Stream = v + return o +} + +// GetStream +func (o *AttachOptions) GetStream() bool { + var stream bool + if o.Stream == nil { + return stream + } + return *o.Stream +} diff --git a/pkg/bindings/containers/types_checkpoint_options.go b/pkg/bindings/containers/types_checkpoint_options.go new file mode 100644 index 000000000..d03dc8231 --- /dev/null +++ b/pkg/bindings/containers/types_checkpoint_options.go @@ -0,0 +1,168 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *CheckpointOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *CheckpointOptions) 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 + } + 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 +} + +// WithExport +func (o *CheckpointOptions) WithExport(value string) *CheckpointOptions { + v := &value + o.Export = v + return o +} + +// GetExport +func (o *CheckpointOptions) GetExport() string { + var export string + if o.Export == nil { + return export + } + return *o.Export +} + +// WithIgnoreRootfs +func (o *CheckpointOptions) WithIgnoreRootfs(value bool) *CheckpointOptions { + v := &value + o.IgnoreRootfs = v + return o +} + +// GetIgnoreRootfs +func (o *CheckpointOptions) GetIgnoreRootfs() bool { + var ignoreRootfs bool + if o.IgnoreRootfs == nil { + return ignoreRootfs + } + return *o.IgnoreRootfs +} + +// WithKeep +func (o *CheckpointOptions) WithKeep(value bool) *CheckpointOptions { + v := &value + o.Keep = v + return o +} + +// GetKeep +func (o *CheckpointOptions) GetKeep() bool { + var keep bool + if o.Keep == nil { + return keep + } + return *o.Keep +} + +// WithLeaveRunning +func (o *CheckpointOptions) WithLeaveRunning(value bool) *CheckpointOptions { + v := &value + o.LeaveRunning = v + return o +} + +// GetLeaveRunning +func (o *CheckpointOptions) GetLeaveRunning() bool { + var leaveRunning bool + if o.LeaveRunning == nil { + return leaveRunning + } + return *o.LeaveRunning +} + +// WithTCPEstablished +func (o *CheckpointOptions) WithTCPEstablished(value bool) *CheckpointOptions { + v := &value + o.TCPEstablished = v + return o +} + +// GetTCPEstablished +func (o *CheckpointOptions) GetTCPEstablished() bool { + var tCPEstablished bool + if o.TCPEstablished == nil { + return tCPEstablished + } + return *o.TCPEstablished +} diff --git a/pkg/bindings/containers/types_commit_options.go b/pkg/bindings/containers/types_commit_options.go new file mode 100644 index 000000000..a8b215141 --- /dev/null +++ b/pkg/bindings/containers/types_commit_options.go @@ -0,0 +1,200 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *CommitOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *CommitOptions) 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 + } + 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 +} + +// WithAuthor +func (o *CommitOptions) WithAuthor(value string) *CommitOptions { + v := &value + o.Author = v + return o +} + +// GetAuthor +func (o *CommitOptions) GetAuthor() string { + var author string + if o.Author == nil { + return author + } + return *o.Author +} + +// WithChanges +func (o *CommitOptions) WithChanges(value []string) *CommitOptions { + v := value + o.Changes = v + return o +} + +// GetChanges +func (o *CommitOptions) GetChanges() []string { + var changes []string + if o.Changes == nil { + return changes + } + return o.Changes +} + +// WithComment +func (o *CommitOptions) WithComment(value string) *CommitOptions { + v := &value + o.Comment = v + return o +} + +// GetComment +func (o *CommitOptions) GetComment() string { + var comment string + if o.Comment == nil { + return comment + } + return *o.Comment +} + +// WithFormat +func (o *CommitOptions) WithFormat(value string) *CommitOptions { + v := &value + o.Format = v + return o +} + +// GetFormat +func (o *CommitOptions) GetFormat() string { + var format string + if o.Format == nil { + return format + } + return *o.Format +} + +// WithPause +func (o *CommitOptions) WithPause(value bool) *CommitOptions { + v := &value + o.Pause = v + return o +} + +// GetPause +func (o *CommitOptions) GetPause() bool { + var pause bool + if o.Pause == nil { + return pause + } + return *o.Pause +} + +// WithRepo +func (o *CommitOptions) WithRepo(value string) *CommitOptions { + v := &value + o.Repo = v + return o +} + +// GetRepo +func (o *CommitOptions) GetRepo() string { + var repo string + if o.Repo == nil { + return repo + } + return *o.Repo +} + +// WithTag +func (o *CommitOptions) WithTag(value string) *CommitOptions { + v := &value + o.Tag = v + return o +} + +// GetTag +func (o *CommitOptions) GetTag() string { + var tag string + if o.Tag == nil { + return tag + } + return *o.Tag +} diff --git a/pkg/bindings/containers/types_create_options.go b/pkg/bindings/containers/types_create_options.go new file mode 100644 index 000000000..4dbce0203 --- /dev/null +++ b/pkg/bindings/containers/types_create_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *CreateOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *CreateOptions) 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 + } + 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 +} diff --git a/pkg/bindings/containers/types_diff_options.go b/pkg/bindings/containers/types_diff_options.go new file mode 100644 index 000000000..be3bbf554 --- /dev/null +++ b/pkg/bindings/containers/types_diff_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *DiffOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *DiffOptions) 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 + } + 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 +} diff --git a/pkg/bindings/containers/types_execinspect_options.go b/pkg/bindings/containers/types_execinspect_options.go new file mode 100644 index 000000000..3c4c870be --- /dev/null +++ b/pkg/bindings/containers/types_execinspect_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *ExecInspectOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ExecInspectOptions) 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 + } + 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 +} diff --git a/pkg/bindings/containers/types_execstart_options.go b/pkg/bindings/containers/types_execstart_options.go new file mode 100644 index 000000000..66fdc82cb --- /dev/null +++ b/pkg/bindings/containers/types_execstart_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *ExecStartOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ExecStartOptions) 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 + } + 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 +} diff --git a/pkg/bindings/containers/types_execstartandattach_options.go b/pkg/bindings/containers/types_execstartandattach_options.go new file mode 100644 index 000000000..43900d29d --- /dev/null +++ b/pkg/bindings/containers/types_execstartandattach_options.go @@ -0,0 +1,186 @@ +package containers + +import ( + "bufio" + "io" + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *ExecStartAndAttachOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ExecStartAndAttachOptions) 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 + } + 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 +} + +// WithOutputStream +func (o *ExecStartAndAttachOptions) WithOutputStream(value io.WriteCloser) *ExecStartAndAttachOptions { + v := &value + o.OutputStream = v + return o +} + +// GetOutputStream +func (o *ExecStartAndAttachOptions) GetOutputStream() io.WriteCloser { + var outputStream io.WriteCloser + if o.OutputStream == nil { + return outputStream + } + return *o.OutputStream +} + +// WithErrorStream +func (o *ExecStartAndAttachOptions) WithErrorStream(value io.WriteCloser) *ExecStartAndAttachOptions { + v := &value + o.ErrorStream = v + return o +} + +// GetErrorStream +func (o *ExecStartAndAttachOptions) GetErrorStream() io.WriteCloser { + var errorStream io.WriteCloser + if o.ErrorStream == nil { + return errorStream + } + return *o.ErrorStream +} + +// WithInputStream +func (o *ExecStartAndAttachOptions) WithInputStream(value bufio.Reader) *ExecStartAndAttachOptions { + v := &value + o.InputStream = v + return o +} + +// GetInputStream +func (o *ExecStartAndAttachOptions) GetInputStream() bufio.Reader { + var inputStream bufio.Reader + if o.InputStream == nil { + return inputStream + } + return *o.InputStream +} + +// WithAttachOutput +func (o *ExecStartAndAttachOptions) WithAttachOutput(value bool) *ExecStartAndAttachOptions { + v := &value + o.AttachOutput = v + return o +} + +// GetAttachOutput +func (o *ExecStartAndAttachOptions) GetAttachOutput() bool { + var attachOutput bool + if o.AttachOutput == nil { + return attachOutput + } + return *o.AttachOutput +} + +// WithAttachError +func (o *ExecStartAndAttachOptions) WithAttachError(value bool) *ExecStartAndAttachOptions { + v := &value + o.AttachError = v + return o +} + +// GetAttachError +func (o *ExecStartAndAttachOptions) GetAttachError() bool { + var attachError bool + if o.AttachError == nil { + return attachError + } + return *o.AttachError +} + +// WithAttachInput +func (o *ExecStartAndAttachOptions) WithAttachInput(value bool) *ExecStartAndAttachOptions { + v := &value + o.AttachInput = v + return o +} + +// GetAttachInput +func (o *ExecStartAndAttachOptions) GetAttachInput() bool { + var attachInput bool + if o.AttachInput == nil { + return attachInput + } + return *o.AttachInput +} diff --git a/pkg/bindings/containers/types_export_options.go b/pkg/bindings/containers/types_export_options.go new file mode 100644 index 000000000..e325bd2cd --- /dev/null +++ b/pkg/bindings/containers/types_export_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *ExportOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ExportOptions) 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 + } + 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 +} diff --git a/pkg/bindings/containers/types_healthcheck_options.go b/pkg/bindings/containers/types_healthcheck_options.go new file mode 100644 index 000000000..8c4300366 --- /dev/null +++ b/pkg/bindings/containers/types_healthcheck_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *HealthCheckOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *HealthCheckOptions) 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 + } + 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 +} diff --git a/pkg/bindings/containers/types_init_options.go b/pkg/bindings/containers/types_init_options.go new file mode 100644 index 000000000..655362f62 --- /dev/null +++ b/pkg/bindings/containers/types_init_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *InitOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *InitOptions) 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 + } + 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 +} diff --git a/pkg/bindings/containers/types_inspect_options.go b/pkg/bindings/containers/types_inspect_options.go new file mode 100644 index 000000000..884f5524d --- /dev/null +++ b/pkg/bindings/containers/types_inspect_options.go @@ -0,0 +1,104 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *InspectOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *InspectOptions) 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 + } + 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 +} + +// WithSize +func (o *InspectOptions) WithSize(value bool) *InspectOptions { + v := &value + o.Size = v + return o +} + +// GetSize +func (o *InspectOptions) GetSize() bool { + var size bool + if o.Size == nil { + return size + } + return *o.Size +} diff --git a/pkg/bindings/containers/types_kill_options.go b/pkg/bindings/containers/types_kill_options.go new file mode 100644 index 000000000..3d6fa6224 --- /dev/null +++ b/pkg/bindings/containers/types_kill_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *KillOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *KillOptions) 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 + } + 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 +} diff --git a/pkg/bindings/containers/types_list_options.go b/pkg/bindings/containers/types_list_options.go new file mode 100644 index 000000000..dd74d37b7 --- /dev/null +++ b/pkg/bindings/containers/types_list_options.go @@ -0,0 +1,186 @@ +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. + +Created 2020-12-18 13:33:20.199081744 -0600 CST m=+0.000270626 +*/ + +// Changed +func (o *ListOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ListOptions) 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 +} + +// WithAll +func (o *ListOptions) WithAll(value bool) *ListOptions { + v := &value + o.All = v + return o +} + +// GetAll +func (o *ListOptions) GetAll() bool { + var all bool + if o.All == nil { + return all + } + return *o.All +} + +// WithFilters +func (o *ListOptions) WithFilters(value map[string][]string) *ListOptions { + v := value + o.Filters = v + return o +} + +// GetFilters +func (o *ListOptions) GetFilters() map[string][]string { + var filters map[string][]string + if o.Filters == nil { + return filters + } + return o.Filters +} + +// WithLast +func (o *ListOptions) WithLast(value int) *ListOptions { + v := &value + o.Last = v + return o +} + +// GetLast +func (o *ListOptions) GetLast() int { + var last int + if o.Last == nil { + return last + } + return *o.Last +} + +// WithNamespace +func (o *ListOptions) WithNamespace(value bool) *ListOptions { + v := &value + o.Namespace = v + return o +} + +// GetNamespace +func (o *ListOptions) GetNamespace() bool { + var namespace bool + if o.Namespace == nil { + return namespace + } + return *o.Namespace +} + +// WithSize +func (o *ListOptions) WithSize(value bool) *ListOptions { + v := &value + o.Size = v + return o +} + +// GetSize +func (o *ListOptions) GetSize() bool { + var size bool + if o.Size == nil { + return size + } + return *o.Size +} + +// WithSync +func (o *ListOptions) WithSync(value bool) *ListOptions { + v := &value + o.Sync = v + return o +} + +// GetSync +func (o *ListOptions) GetSync() bool { + var sync bool + if o.Sync == nil { + return sync + } + return *o.Sync +} diff --git a/pkg/bindings/containers/types_log_options.go b/pkg/bindings/containers/types_log_options.go new file mode 100644 index 000000000..a6958242f --- /dev/null +++ b/pkg/bindings/containers/types_log_options.go @@ -0,0 +1,200 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *LogOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *LogOptions) 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 + } + 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 +} + +// WithFollow +func (o *LogOptions) WithFollow(value bool) *LogOptions { + v := &value + o.Follow = v + return o +} + +// GetFollow +func (o *LogOptions) GetFollow() bool { + var follow bool + if o.Follow == nil { + return follow + } + return *o.Follow +} + +// WithSince +func (o *LogOptions) WithSince(value string) *LogOptions { + v := &value + o.Since = v + return o +} + +// GetSince +func (o *LogOptions) GetSince() string { + var since string + if o.Since == nil { + return since + } + return *o.Since +} + +// WithStderr +func (o *LogOptions) WithStderr(value bool) *LogOptions { + v := &value + o.Stderr = v + return o +} + +// GetStderr +func (o *LogOptions) GetStderr() bool { + var stderr bool + if o.Stderr == nil { + return stderr + } + return *o.Stderr +} + +// WithStdout +func (o *LogOptions) WithStdout(value bool) *LogOptions { + v := &value + o.Stdout = v + return o +} + +// GetStdout +func (o *LogOptions) GetStdout() bool { + var stdout bool + if o.Stdout == nil { + return stdout + } + return *o.Stdout +} + +// WithTail +func (o *LogOptions) WithTail(value string) *LogOptions { + v := &value + o.Tail = v + return o +} + +// GetTail +func (o *LogOptions) GetTail() string { + var tail string + if o.Tail == nil { + return tail + } + return *o.Tail +} + +// WithTimestamps +func (o *LogOptions) WithTimestamps(value bool) *LogOptions { + v := &value + o.Timestamps = v + return o +} + +// GetTimestamps +func (o *LogOptions) GetTimestamps() bool { + var timestamps bool + if o.Timestamps == nil { + return timestamps + } + return *o.Timestamps +} + +// WithUntil +func (o *LogOptions) WithUntil(value string) *LogOptions { + v := &value + o.Until = v + return o +} + +// GetUntil +func (o *LogOptions) GetUntil() string { + var until string + if o.Until == nil { + return until + } + return *o.Until +} diff --git a/pkg/bindings/containers/types_mount_options.go b/pkg/bindings/containers/types_mount_options.go new file mode 100644 index 000000000..c0e253094 --- /dev/null +++ b/pkg/bindings/containers/types_mount_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *MountOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *MountOptions) 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 + } + 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 +} diff --git a/pkg/bindings/containers/types_mountedcontainerpaths_options.go b/pkg/bindings/containers/types_mountedcontainerpaths_options.go new file mode 100644 index 000000000..e368ff131 --- /dev/null +++ b/pkg/bindings/containers/types_mountedcontainerpaths_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *MountedContainerPathsOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *MountedContainerPathsOptions) 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 + } + 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 +} diff --git a/pkg/bindings/containers/types_pause_options.go b/pkg/bindings/containers/types_pause_options.go new file mode 100644 index 000000000..26ad86793 --- /dev/null +++ b/pkg/bindings/containers/types_pause_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *PauseOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *PauseOptions) 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 + } + 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 +} diff --git a/pkg/bindings/containers/types_prune_options.go b/pkg/bindings/containers/types_prune_options.go new file mode 100644 index 000000000..e3c0f4de7 --- /dev/null +++ b/pkg/bindings/containers/types_prune_options.go @@ -0,0 +1,104 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *PruneOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *PruneOptions) 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 + } + 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 +} + +// WithFilters +func (o *PruneOptions) WithFilters(value map[string][]string) *PruneOptions { + v := value + o.Filters = v + return o +} + +// GetFilters +func (o *PruneOptions) GetFilters() map[string][]string { + var filters map[string][]string + if o.Filters == nil { + return filters + } + return o.Filters +} diff --git a/pkg/bindings/containers/types_remove_options.go b/pkg/bindings/containers/types_remove_options.go new file mode 100644 index 000000000..6f59f0ed5 --- /dev/null +++ b/pkg/bindings/containers/types_remove_options.go @@ -0,0 +1,120 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *RemoveOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *RemoveOptions) 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 + } + 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 +} + +// WithForce +func (o *RemoveOptions) WithForce(value bool) *RemoveOptions { + v := &value + o.Force = v + return o +} + +// GetForce +func (o *RemoveOptions) GetForce() bool { + var force bool + if o.Force == nil { + return force + } + return *o.Force +} + +// WithVolumes +func (o *RemoveOptions) WithVolumes(value bool) *RemoveOptions { + v := &value + o.Volumes = v + return o +} + +// GetVolumes +func (o *RemoveOptions) GetVolumes() bool { + var volumes bool + if o.Volumes == nil { + return volumes + } + return *o.Volumes +} diff --git a/pkg/bindings/containers/types_resizeexectty_options.go b/pkg/bindings/containers/types_resizeexectty_options.go new file mode 100644 index 000000000..33bb4e78b --- /dev/null +++ b/pkg/bindings/containers/types_resizeexectty_options.go @@ -0,0 +1,120 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *ResizeExecTTYOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ResizeExecTTYOptions) 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 + } + 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 +} + +// WithHeight +func (o *ResizeExecTTYOptions) WithHeight(value int) *ResizeExecTTYOptions { + v := &value + o.Height = v + return o +} + +// GetHeight +func (o *ResizeExecTTYOptions) GetHeight() int { + var height int + if o.Height == nil { + return height + } + return *o.Height +} + +// WithWidth +func (o *ResizeExecTTYOptions) WithWidth(value int) *ResizeExecTTYOptions { + v := &value + o.Width = v + return o +} + +// GetWidth +func (o *ResizeExecTTYOptions) GetWidth() int { + var width int + if o.Width == nil { + return width + } + return *o.Width +} diff --git a/pkg/bindings/containers/types_resizetty_options.go b/pkg/bindings/containers/types_resizetty_options.go new file mode 100644 index 000000000..29ec54988 --- /dev/null +++ b/pkg/bindings/containers/types_resizetty_options.go @@ -0,0 +1,120 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *ResizeTTYOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ResizeTTYOptions) 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 + } + 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 +} + +// WithHeight +func (o *ResizeTTYOptions) WithHeight(value int) *ResizeTTYOptions { + v := &value + o.Height = v + return o +} + +// GetHeight +func (o *ResizeTTYOptions) GetHeight() int { + var height int + if o.Height == nil { + return height + } + return *o.Height +} + +// WithWidth +func (o *ResizeTTYOptions) WithWidth(value int) *ResizeTTYOptions { + v := &value + o.Width = v + return o +} + +// GetWidth +func (o *ResizeTTYOptions) GetWidth() int { + var width int + if o.Width == nil { + return width + } + return *o.Width +} diff --git a/pkg/bindings/containers/types_restart_options.go b/pkg/bindings/containers/types_restart_options.go new file mode 100644 index 000000000..13ac099b1 --- /dev/null +++ b/pkg/bindings/containers/types_restart_options.go @@ -0,0 +1,104 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *RestartOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *RestartOptions) 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 + } + 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 +} + +// WithTimeout +func (o *RestartOptions) WithTimeout(value int) *RestartOptions { + v := &value + o.Timeout = v + return o +} + +// GetTimeout +func (o *RestartOptions) GetTimeout() int { + var timeout int + if o.Timeout == nil { + return timeout + } + return *o.Timeout +} diff --git a/pkg/bindings/containers/types_restore_options.go b/pkg/bindings/containers/types_restore_options.go new file mode 100644 index 000000000..be6e94736 --- /dev/null +++ b/pkg/bindings/containers/types_restore_options.go @@ -0,0 +1,200 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *RestoreOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *RestoreOptions) 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 + } + 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 +} + +// WithIgnoreRootfs +func (o *RestoreOptions) WithIgnoreRootfs(value bool) *RestoreOptions { + v := &value + o.IgnoreRootfs = v + return o +} + +// GetIgnoreRootfs +func (o *RestoreOptions) GetIgnoreRootfs() bool { + var ignoreRootfs bool + if o.IgnoreRootfs == nil { + return ignoreRootfs + } + return *o.IgnoreRootfs +} + +// WithIgnoreStaticIP +func (o *RestoreOptions) WithIgnoreStaticIP(value bool) *RestoreOptions { + v := &value + o.IgnoreStaticIP = v + return o +} + +// GetIgnoreStaticIP +func (o *RestoreOptions) GetIgnoreStaticIP() bool { + var ignoreStaticIP bool + if o.IgnoreStaticIP == nil { + return ignoreStaticIP + } + return *o.IgnoreStaticIP +} + +// WithIgnoreStaticMAC +func (o *RestoreOptions) WithIgnoreStaticMAC(value bool) *RestoreOptions { + v := &value + o.IgnoreStaticMAC = v + return o +} + +// GetIgnoreStaticMAC +func (o *RestoreOptions) GetIgnoreStaticMAC() bool { + var ignoreStaticMAC bool + if o.IgnoreStaticMAC == nil { + return ignoreStaticMAC + } + return *o.IgnoreStaticMAC +} + +// WithImportAchive +func (o *RestoreOptions) WithImportAchive(value string) *RestoreOptions { + v := &value + o.ImportAchive = v + return o +} + +// GetImportAchive +func (o *RestoreOptions) GetImportAchive() string { + var importAchive string + if o.ImportAchive == nil { + return importAchive + } + return *o.ImportAchive +} + +// WithKeep +func (o *RestoreOptions) WithKeep(value bool) *RestoreOptions { + v := &value + o.Keep = v + return o +} + +// GetKeep +func (o *RestoreOptions) GetKeep() bool { + var keep bool + if o.Keep == nil { + return keep + } + return *o.Keep +} + +// WithName +func (o *RestoreOptions) WithName(value string) *RestoreOptions { + v := &value + o.Name = v + return o +} + +// GetName +func (o *RestoreOptions) GetName() string { + var name string + if o.Name == nil { + return name + } + return *o.Name +} + +// WithTCPEstablished +func (o *RestoreOptions) WithTCPEstablished(value bool) *RestoreOptions { + v := &value + o.TCPEstablished = v + return o +} + +// GetTCPEstablished +func (o *RestoreOptions) GetTCPEstablished() bool { + var tCPEstablished bool + if o.TCPEstablished == nil { + return tCPEstablished + } + return *o.TCPEstablished +} diff --git a/pkg/bindings/containers/types_shouldrestart_options.go b/pkg/bindings/containers/types_shouldrestart_options.go new file mode 100644 index 000000000..c833d0d8b --- /dev/null +++ b/pkg/bindings/containers/types_shouldrestart_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *ShouldRestartOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ShouldRestartOptions) 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 + } + 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 +} diff --git a/pkg/bindings/containers/types_start_options.go b/pkg/bindings/containers/types_start_options.go new file mode 100644 index 000000000..5918af89b --- /dev/null +++ b/pkg/bindings/containers/types_start_options.go @@ -0,0 +1,104 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *StartOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *StartOptions) 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 + } + 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 +} + +// WithDetachKeys +func (o *StartOptions) WithDetachKeys(value string) *StartOptions { + v := &value + o.DetachKeys = v + return o +} + +// GetDetachKeys +func (o *StartOptions) GetDetachKeys() string { + var detachKeys string + if o.DetachKeys == nil { + return detachKeys + } + return *o.DetachKeys +} diff --git a/pkg/bindings/containers/types_stats_options.go b/pkg/bindings/containers/types_stats_options.go new file mode 100644 index 000000000..f821ea1cd --- /dev/null +++ b/pkg/bindings/containers/types_stats_options.go @@ -0,0 +1,104 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *StatsOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *StatsOptions) 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 + } + 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 +} + +// WithStream +func (o *StatsOptions) WithStream(value bool) *StatsOptions { + v := &value + o.Stream = v + return o +} + +// GetStream +func (o *StatsOptions) GetStream() bool { + var stream bool + if o.Stream == nil { + return stream + } + return *o.Stream +} diff --git a/pkg/bindings/containers/types_stop_options.go b/pkg/bindings/containers/types_stop_options.go new file mode 100644 index 000000000..14d7633a0 --- /dev/null +++ b/pkg/bindings/containers/types_stop_options.go @@ -0,0 +1,104 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *StopOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *StopOptions) 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 + } + 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 +} + +// WithTimeout +func (o *StopOptions) WithTimeout(value uint) *StopOptions { + v := &value + o.Timeout = v + return o +} + +// GetTimeout +func (o *StopOptions) GetTimeout() uint { + var timeout uint + if o.Timeout == nil { + return timeout + } + return *o.Timeout +} diff --git a/pkg/bindings/containers/types_top_options.go b/pkg/bindings/containers/types_top_options.go new file mode 100644 index 000000000..95a1ee686 --- /dev/null +++ b/pkg/bindings/containers/types_top_options.go @@ -0,0 +1,104 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *TopOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *TopOptions) 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 + } + 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 +} + +// WithDescriptors +func (o *TopOptions) WithDescriptors(value []string) *TopOptions { + v := &value + o.Descriptors = v + return o +} + +// GetDescriptors +func (o *TopOptions) GetDescriptors() []string { + var descriptors []string + if o.Descriptors == nil { + return descriptors + } + return *o.Descriptors +} diff --git a/pkg/bindings/containers/types_unmount_options.go b/pkg/bindings/containers/types_unmount_options.go new file mode 100644 index 000000000..a29bd8216 --- /dev/null +++ b/pkg/bindings/containers/types_unmount_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *UnmountOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *UnmountOptions) 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 + } + 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 +} diff --git a/pkg/bindings/containers/types_unpause_options.go b/pkg/bindings/containers/types_unpause_options.go new file mode 100644 index 000000000..44c077df2 --- /dev/null +++ b/pkg/bindings/containers/types_unpause_options.go @@ -0,0 +1,88 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *UnpauseOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *UnpauseOptions) 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 + } + 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 +} diff --git a/pkg/bindings/containers/types_wait_options.go b/pkg/bindings/containers/types_wait_options.go new file mode 100644 index 000000000..18d36c377 --- /dev/null +++ b/pkg/bindings/containers/types_wait_options.go @@ -0,0 +1,105 @@ +package containers + +import ( + "net/url" + "reflect" + "strconv" + + "github.com/containers/podman/v2/libpod/define" + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *WaitOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *WaitOptions) 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 + } + 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 +} + +// WithCondition +func (o *WaitOptions) WithCondition(value define.ContainerStatus) *WaitOptions { + v := &value + o.Condition = v + return o +} + +// GetCondition +func (o *WaitOptions) GetCondition() define.ContainerStatus { + var condition define.ContainerStatus + if o.Condition == nil { + return condition + } + return *o.Condition +} diff --git a/pkg/bindings/generate/generate.go b/pkg/bindings/generate/generate.go index 8d0146ec1..29eb39557 100644 --- a/pkg/bindings/generate/generate.go +++ b/pkg/bindings/generate/generate.go @@ -4,31 +4,23 @@ import ( "context" "errors" "net/http" - "net/url" - "strconv" "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/domain/entities" ) -func Systemd(ctx context.Context, nameOrID string, options entities.GenerateSystemdOptions) (*entities.GenerateSystemdReport, error) { +func Systemd(ctx context.Context, nameOrID string, options *SystemdOptions) (*entities.GenerateSystemdReport, error) { + if options == nil { + options = new(SystemdOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - - params.Set("useName", strconv.FormatBool(options.Name)) - params.Set("new", strconv.FormatBool(options.New)) - if options.RestartPolicy != "" { - params.Set("restartPolicy", options.RestartPolicy) - } - if options.StopTimeout != nil { - params.Set("stopTimeout", strconv.FormatUint(uint64(*options.StopTimeout), 10)) + params, err := options.ToParams() + if err != nil { + return nil, err } - params.Set("containerPrefix", options.ContainerPrefix) - params.Set("podPrefix", options.PodPrefix) - params.Set("separator", options.Separator) response, err := conn.DoRequest(nil, http.MethodGet, "/generate/%s/systemd", params, nil, nameOrID) if err != nil { @@ -38,7 +30,10 @@ func Systemd(ctx context.Context, nameOrID string, options entities.GenerateSyst return report, response.Process(&report.Units) } -func Kube(ctx context.Context, nameOrIDs []string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) { +func Kube(ctx context.Context, nameOrIDs []string, options *KubeOptions) (*entities.GenerateKubeReport, error) { + if options == nil { + options = new(KubeOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err @@ -46,12 +41,14 @@ func Kube(ctx context.Context, nameOrIDs []string, options entities.GenerateKube if len(nameOrIDs) < 1 { return nil, errors.New("must provide the name or ID of one container or pod") } - params := url.Values{} + + params, err := options.ToParams() + if err != nil { + return nil, err + } for _, name := range nameOrIDs { params.Add("names", name) } - params.Set("service", strconv.FormatBool(options.Service)) - response, err := conn.DoRequest(nil, http.MethodGet, "/generate/kube", params, nil) if err != nil { return nil, err diff --git a/pkg/bindings/generate/types.go b/pkg/bindings/generate/types.go new file mode 100644 index 000000000..4e9d7a0ff --- /dev/null +++ b/pkg/bindings/generate/types.go @@ -0,0 +1,27 @@ +package generate + +//go:generate go run ../generator/generator.go KubeOptions +// KubeOptions are optional options for generating kube YAML files +type KubeOptions struct { + // Service - generate YAML for a Kubernetes _service_ object. + Service *bool +} + +//go:generate go run ../generator/generator.go SystemdOptions +// SystemdOptions are optional options for generating systemd files +type SystemdOptions struct { + // Name - use container/pod name instead of its ID. + UseName *bool + // New - create a new container instead of starting a new one. + New *bool + // RestartPolicy - systemd restart policy. + RestartPolicy *string + // StopTimeout - time when stopping the container. + StopTimeout *uint + // ContainerPrefix - systemd unit name prefix for containers + ContainerPrefix *string + // PodPrefix - systemd unit name prefix for pods + PodPrefix *string + // Separator - systemd unit name separator between name/id and prefix + Separator *string +} diff --git a/pkg/bindings/generate/types_kube_options.go b/pkg/bindings/generate/types_kube_options.go new file mode 100644 index 000000000..68488aaee --- /dev/null +++ b/pkg/bindings/generate/types_kube_options.go @@ -0,0 +1,104 @@ +package generate + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *KubeOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *KubeOptions) 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 + } + 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 +} + +// WithService +func (o *KubeOptions) WithService(value bool) *KubeOptions { + v := &value + o.Service = v + return o +} + +// GetService +func (o *KubeOptions) GetService() bool { + var service bool + if o.Service == nil { + return service + } + return *o.Service +} diff --git a/pkg/bindings/generate/types_systemd_options.go b/pkg/bindings/generate/types_systemd_options.go new file mode 100644 index 000000000..0e8a46aa0 --- /dev/null +++ b/pkg/bindings/generate/types_systemd_options.go @@ -0,0 +1,200 @@ +package generate + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *SystemdOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *SystemdOptions) 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 + } + 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 +} + +// WithUseName +func (o *SystemdOptions) WithUseName(value bool) *SystemdOptions { + v := &value + o.UseName = v + return o +} + +// GetUseName +func (o *SystemdOptions) GetUseName() bool { + var useName bool + if o.UseName == nil { + return useName + } + return *o.UseName +} + +// WithNew +func (o *SystemdOptions) WithNew(value bool) *SystemdOptions { + v := &value + o.New = v + return o +} + +// GetNew +func (o *SystemdOptions) GetNew() bool { + var new bool + if o.New == nil { + return new + } + return *o.New +} + +// WithRestartPolicy +func (o *SystemdOptions) WithRestartPolicy(value string) *SystemdOptions { + v := &value + o.RestartPolicy = v + return o +} + +// GetRestartPolicy +func (o *SystemdOptions) GetRestartPolicy() string { + var restartPolicy string + if o.RestartPolicy == nil { + return restartPolicy + } + return *o.RestartPolicy +} + +// WithStopTimeout +func (o *SystemdOptions) WithStopTimeout(value uint) *SystemdOptions { + v := &value + o.StopTimeout = v + return o +} + +// GetStopTimeout +func (o *SystemdOptions) GetStopTimeout() uint { + var stopTimeout uint + if o.StopTimeout == nil { + return stopTimeout + } + return *o.StopTimeout +} + +// WithContainerPrefix +func (o *SystemdOptions) WithContainerPrefix(value string) *SystemdOptions { + v := &value + o.ContainerPrefix = v + return o +} + +// GetContainerPrefix +func (o *SystemdOptions) GetContainerPrefix() string { + var containerPrefix string + if o.ContainerPrefix == nil { + return containerPrefix + } + return *o.ContainerPrefix +} + +// WithPodPrefix +func (o *SystemdOptions) WithPodPrefix(value string) *SystemdOptions { + v := &value + o.PodPrefix = v + return o +} + +// GetPodPrefix +func (o *SystemdOptions) GetPodPrefix() string { + var podPrefix string + if o.PodPrefix == nil { + return podPrefix + } + return *o.PodPrefix +} + +// WithSeparator +func (o *SystemdOptions) WithSeparator(value string) *SystemdOptions { + v := &value + o.Separator = v + return o +} + +// GetSeparator +func (o *SystemdOptions) GetSeparator() string { + var separator string + if o.Separator == nil { + return separator + } + return *o.Separator +} diff --git a/pkg/bindings/generator/generator.go b/pkg/bindings/generator/generator.go new file mode 100644 index 000000000..8c79aebae --- /dev/null +++ b/pkg/bindings/generator/generator.go @@ -0,0 +1,261 @@ +package main + +import ( + "errors" + "fmt" + "go/ast" + "go/parser" + "go/token" + "io/ioutil" + "os" + "os/exec" + "strings" + "text/template" + "time" +) + +var bodyTmpl = `package {{.PackageName}} + +import ( +{{range $import := .Imports}} {{$import}} +{{end}} + +) + +/* +This file is generated automatically by go generate. Do not edit. + +Created {{.Date}} +*/ + +// Changed +func (o *{{.StructName}}) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *{{.StructName}}) 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 + } + 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 +} +` + +var fieldTmpl = ` +// With{{.Name}} +func(o *{{.StructName}}) With{{.Name}}(value {{.Type}}) *{{.StructName}} { + v := {{.TypedValue}} + o.{{.Name}} = v + return o +} + +// Get{{.Name}} +func(o *{{.StructName}}) Get{{.Name}}() {{.Type}} { + var {{.ZeroName}} {{.Type}} + if o.{{.Name}} == nil { + return {{.ZeroName}} + } + return {{.TypedName}} +} +` + +type fieldStruct struct { + Name string + StructName string + Type string + TypedName string + TypedValue string + ZeroName string +} + +func main() { + var ( + closed bool + fieldStructs []fieldStruct + ) + srcFile := os.Getenv("GOFILE") + pkg := os.Getenv("GOPACKAGE") + inputStructName := os.Args[1] + b, err := ioutil.ReadFile(srcFile) + if err != nil { + panic(err) + } + fset := token.NewFileSet() // positions are relative to fset + f, err := parser.ParseFile(fset, "", b, parser.ParseComments) + if err != nil { + panic(err) + } + // always add reflect + imports := []string{"\"reflect\""} + for _, imp := range f.Imports { + imports = append(imports, imp.Path.Value) + } + + out, err := os.Create(strings.TrimRight(srcFile, ".go") + "_" + strings.Replace(strings.ToLower(inputStructName), "options", "_options", 1) + ".go") + if err != nil { + panic(err) + } + defer func() { + if !closed { + out.Close() + } + }() + bodyStruct := struct { + PackageName string + Imports []string + Date string + StructName string + }{ + PackageName: pkg, + Imports: imports, + Date: time.Now().String(), + StructName: inputStructName, + } + + body := template.Must(template.New("body").Parse(bodyTmpl)) + fields := template.Must(template.New("fields").Parse(fieldTmpl)) + ast.Inspect(f, func(n ast.Node) bool { + ref, refOK := n.(*ast.TypeSpec) + if refOK { + if ref.Name.Name == inputStructName { + x := ref.Type.(*ast.StructType) + for _, field := range x.Fields.List { + var ( + name, zeroName, typedName, typedValue string + ) + if len(field.Names) > 0 { + name = field.Names[0].Name + if len(name) < 1 { + panic(errors.New("bad name")) + } + } + for k, v := range name { + zeroName = strings.ToLower(string(v)) + name[k+1:] + break + } + //sub := "*" + typeExpr := field.Type + switch field.Type.(type) { + case *ast.MapType, *ast.StructType, *ast.ArrayType: + typedName = "o." + name + typedValue = "value" + default: + typedName = "*o." + name + typedValue = "&value" + } + start := typeExpr.Pos() - 1 + end := typeExpr.End() - 1 + fieldType := strings.Replace(string(b[start:end]), "*", "", 1) + fStruct := fieldStruct{ + Name: name, + StructName: inputStructName, + Type: fieldType, + TypedName: typedName, + TypedValue: typedValue, + ZeroName: zeroName, + } + fieldStructs = append(fieldStructs, fStruct) + } // for + + // create the body + if err := body.Execute(out, bodyStruct); err != nil { + fmt.Println(err) + os.Exit(1) + } + + // create with func from the struct fields + for _, fs := range fieldStructs { + if err := fields.Execute(out, fs); err != nil { + fmt.Println(err) + os.Exit(1) + } + } + + // close out file + if err := out.Close(); err != nil { + fmt.Println(err) + os.Exit(1) + } + closed = true + + // go fmt file + gofmt := exec.Command("gofmt", "-w", "-s", out.Name()) + gofmt.Stderr = os.Stdout + if err := gofmt.Run(); err != nil { + fmt.Println(err) + os.Exit(1) + } + + // go import file + goimport := exec.Command("goimports", "-w", out.Name()) + goimport.Stderr = os.Stdout + if err := goimport.Run(); err != nil { + fmt.Println(err) + os.Exit(1) + } + } + + } + return true + }) +} diff --git a/pkg/bindings/images/diff.go b/pkg/bindings/images/diff.go index 10d50b0fd..8802c15e2 100644 --- a/pkg/bindings/images/diff.go +++ b/pkg/bindings/images/diff.go @@ -9,7 +9,11 @@ import ( ) // Diff provides the changes between two container layers -func Diff(ctx context.Context, nameOrID string) ([]archive.Change, error) { +func Diff(ctx context.Context, nameOrID string, options *DiffOptions) ([]archive.Change, error) { + if options == nil { + options = new(DiffOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go index 2d3035d8d..ecdd1f553 100644 --- a/pkg/bindings/images/images.go +++ b/pkg/bindings/images/images.go @@ -8,11 +8,11 @@ import ( "net/url" "strconv" - "github.com/containers/image/v5/types" "github.com/containers/podman/v2/pkg/api/handlers" "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" ) @@ -32,22 +32,18 @@ func Exists(ctx context.Context, nameOrID string) (bool, error) { // List returns a list of images in local storage. The all boolean and filters parameters are optional // ways to alter the image query. -func List(ctx context.Context, all *bool, filters map[string][]string) ([]*entities.ImageSummary, error) { +func List(ctx context.Context, options *ListOptions) ([]*entities.ImageSummary, error) { + if options == nil { + options = new(ListOptions) + } var imageSummary []*entities.ImageSummary conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if all != nil { - params.Set("all", strconv.FormatBool(*all)) - } - if filters != nil { - strFilters, err := bindings.FiltersToString(filters) - if err != nil { - return nil, err - } - params.Set("filters", strFilters) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodGet, "/images/json", params, nil) if err != nil { @@ -58,14 +54,17 @@ func List(ctx context.Context, all *bool, filters map[string][]string) ([]*entit // Get performs an image inspect. To have the on-disk size of the image calculated, you can // use the optional size parameter. -func GetImage(ctx context.Context, nameOrID string, size *bool) (*entities.ImageInspectReport, error) { +func GetImage(ctx context.Context, nameOrID string, options *GetOptions) (*entities.ImageInspectReport, error) { + if options == nil { + options = new(GetOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if size != nil { - params.Set("size", strconv.FormatBool(*size)) + params, err := options.ToParams() + if err != nil { + return nil, err } inspectedData := entities.ImageInspectReport{} response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/json", params, nil, nameOrID) @@ -76,15 +75,18 @@ func GetImage(ctx context.Context, nameOrID string, size *bool) (*entities.Image } // Tree retrieves a "tree" based representation of the given image -func Tree(ctx context.Context, nameOrID string, whatRequires *bool) (*entities.ImageTreeReport, error) { +func Tree(ctx context.Context, nameOrID string, options *TreeOptions) (*entities.ImageTreeReport, error) { + if options == nil { + options = new(TreeOptions) + } var report entities.ImageTreeReport conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if whatRequires != nil { - params.Set("size", strconv.FormatBool(*whatRequires)) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/tree", params, nil, nameOrID) if err != nil { @@ -94,7 +96,11 @@ func Tree(ctx context.Context, nameOrID string, whatRequires *bool) (*entities.I } // History returns the parent layers of an image. -func History(ctx context.Context, nameOrID string) ([]*handlers.HistoryResponse, error) { +func History(ctx context.Context, nameOrID string, options *HistoryOptions) ([]*handlers.HistoryResponse, error) { + if options == nil { + options = new(HistoryOptions) + } + _ = options var history []*handlers.HistoryResponse conn, err := bindings.GetClient(ctx) if err != nil { @@ -107,15 +113,18 @@ func History(ctx context.Context, nameOrID string) ([]*handlers.HistoryResponse, return history, response.Process(&history) } -func Load(ctx context.Context, r io.Reader, name *string) (*entities.ImageLoadReport, error) { +func Load(ctx context.Context, r io.Reader, options *LoadOptions) (*entities.ImageLoadReport, error) { + if options == nil { + options = new(LoadOptions) + } var report entities.ImageLoadReport conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if name != nil { - params.Set("reference", *name) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(r, http.MethodPost, "/images/load", params, nil) if err != nil { @@ -124,49 +133,24 @@ func Load(ctx context.Context, r io.Reader, name *string) (*entities.ImageLoadRe return &report, response.Process(&report) } -func MultiExport(ctx context.Context, namesOrIds []string, w io.Writer, format *string, compress *bool) error { - conn, err := bindings.GetClient(ctx) - if err != nil { - return err - } - params := url.Values{} - if format != nil { - params.Set("format", *format) - } - if compress != nil { - params.Set("compress", strconv.FormatBool(*compress)) - } - for _, ref := range namesOrIds { - params.Add("references", ref) +// Export saves images from local storage as a tarball or image archive. The optional format +// parameter is used to change the format of the output. +func Export(ctx context.Context, nameOrIDs []string, w io.Writer, options *ExportOptions) error { + if options == nil { + options = new(ExportOptions) } - response, err := conn.DoRequest(nil, http.MethodGet, "/images/export", params, nil) + conn, err := bindings.GetClient(ctx) if err != nil { return err } - - if response.StatusCode/100 == 2 || response.StatusCode/100 == 3 { - _, err = io.Copy(w, response.Body) - return err - } - return response.Process(nil) - -} - -// Export saves an image from local storage as a tarball or image archive. The optional format -// parameter is used to change the format of the output. -func Export(ctx context.Context, nameOrID string, w io.Writer, format *string, compress *bool) error { - conn, err := bindings.GetClient(ctx) + params, err := options.ToParams() if err != nil { return err } - params := url.Values{} - if format != nil { - params.Set("format", *format) - } - if compress != nil { - params.Set("compress", strconv.FormatBool(*compress)) + for _, ref := range nameOrIDs { + params.Add("references", ref) } - response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/get", params, nil, nameOrID) + response, err := conn.DoRequest(nil, http.MethodGet, "/images/export", params, nil) if err != nil { return err } @@ -180,34 +164,35 @@ func Export(ctx context.Context, nameOrID string, w io.Writer, format *string, c // 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, all *bool, filters map[string][]string) ([]string, error) { +func Prune(ctx context.Context, options *PruneOptions) ([]*reports.PruneReport, error) { var ( - deleted []string + deleted []*reports.PruneReport ) + if options == nil { + options = new(PruneOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if all != nil { - params.Set("all", strconv.FormatBool(*all)) - } - if filters != nil { - stringFilter, err := bindings.FiltersToString(filters) - if err != nil { - return nil, err - } - params.Set("filters", stringFilter) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodPost, "/images/prune", params, nil) 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. -func Tag(ctx context.Context, nameOrID, tag, repo string) error { +func Tag(ctx context.Context, nameOrID, tag, repo string, options *TagOptions) error { + if options == nil { + options = new(TagOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return err @@ -223,7 +208,11 @@ func Tag(ctx context.Context, nameOrID, tag, repo string) error { } // Untag removes a name from locally-stored image. Both the tag and repo parameters are required. -func Untag(ctx context.Context, nameOrID, tag, repo string) error { +func Untag(ctx context.Context, nameOrID, tag, repo string, options *UntagOptions) error { + if options == nil { + options = new(UntagOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return err @@ -241,27 +230,21 @@ func Untag(ctx context.Context, nameOrID, tag, repo string) error { // Imports adds the given image to the local image store. This can be done by file and the given reader // or via the url parameter. Additional metadata can be associated with the image by using the changes and // message parameters. The image can also be tagged given a reference. One of url OR r must be provided. -func Import(ctx context.Context, changes []string, message, reference, u *string, r io.Reader) (*entities.ImageImportReport, error) { +func Import(ctx context.Context, r io.Reader, options *ImportOptions) (*entities.ImageImportReport, error) { + if options == nil { + options = new(ImportOptions) + } var report entities.ImageImportReport - if r != nil && u != nil { + if r != nil && options.URL != nil { return nil, errors.New("url and r parameters cannot be used together") } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - for _, change := range changes { - params.Add("changes", change) - } - if message != nil { - params.Set("message", *message) - } - if reference != nil { - params.Set("reference", *reference) - } - if u != nil { - params.Set("url", *u) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(r, http.MethodPost, "/images/import", params, nil) if err != nil { @@ -275,25 +258,31 @@ func Import(ctx context.Context, changes []string, message, reference, u *string // The destination must be a reference to a registry (i.e., of docker transport // or be normalized to one). Other transports are rejected as they do not make // sense in a remote context. -func Push(ctx context.Context, source string, destination string, options entities.ImagePushOptions) error { +func Push(ctx context.Context, source string, destination string, options *PushOptions) error { + if options == nil { + options = new(PushOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return err } - // TODO: have a global system context we can pass around (1st argument) - header, err := auth.Header(nil, auth.XRegistryAuthHeader, options.Authfile, options.Username, options.Password) + header, err := auth.Header(nil, auth.XRegistryAuthHeader, options.GetAuthfile(), options.GetUsername(), options.GetPassword()) if err != nil { return err } - params := url.Values{} - params.Set("destination", destination) - if options.SkipTLSVerify != types.OptionalBoolUndefined { - // Note: we have to verify if skipped is false. - verifyTLS := bool(options.SkipTLSVerify == types.OptionalBoolFalse) - params.Set("tlsVerify", strconv.FormatBool(verifyTLS)) + params, err := options.ToParams() + if err != nil { + return err + } + //SkipTLSVerify is special. We need to delete the param added by + //toparams and change the key and flip the bool + if options.SkipTLSVerify != nil { + params.Del("SkipTLSVerify") + params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) } + params.Set("destination", destination) path := fmt.Sprintf("/images/%s/push", source) response, err := conn.DoRequest(nil, http.MethodPost, path, params, header) @@ -305,28 +294,28 @@ func Push(ctx context.Context, source string, destination string, options entiti } // Search is the binding for libpod's v2 endpoints for Search images. -func Search(ctx context.Context, term string, opts entities.ImageSearchOptions) ([]entities.ImageSearchReport, error) { +func Search(ctx context.Context, term string, options *SearchOptions) ([]entities.ImageSearchReport, error) { + if options == nil { + options = new(SearchOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - params.Set("term", term) - params.Set("limit", strconv.Itoa(opts.Limit)) - params.Set("noTrunc", strconv.FormatBool(opts.NoTrunc)) - params.Set("listTags", strconv.FormatBool(opts.ListTags)) - for _, f := range opts.Filters { - params.Set("filters", f) + params, err := options.ToParams() + if err != nil { + return nil, err } + params.Set("term", term) - if opts.SkipTLSVerify != types.OptionalBoolUndefined { - // Note: we have to verify if skipped is false. - verifyTLS := bool(opts.SkipTLSVerify == types.OptionalBoolFalse) - params.Set("tlsVerify", strconv.FormatBool(verifyTLS)) + // Note: we have to verify if skipped is false. + if options.SkipTLSVerify != nil { + params.Del("SkipTLSVerify") + params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) } // TODO: have a global system context we can pass around (1st argument) - header, err := auth.Header(nil, auth.XRegistryAuthHeader, opts.Authfile, "", "") + header, err := auth.Header(nil, auth.XRegistryAuthHeader, options.GetAuthfile(), "", "") if err != nil { return nil, err } diff --git a/pkg/bindings/images/pull.go b/pkg/bindings/images/pull.go index c827b3283..5669c704e 100644 --- a/pkg/bindings/images/pull.go +++ b/pkg/bindings/images/pull.go @@ -8,11 +8,9 @@ import ( "io" "io/ioutil" "net/http" - "net/url" "os" "strconv" - "github.com/containers/image/v5/types" "github.com/containers/podman/v2/pkg/auth" "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/domain/entities" @@ -23,26 +21,28 @@ import ( // `rawImage` must be a reference to a registry (i.e., of docker transport or be // normalized to one). Other transports are rejected as they do not make sense // in a remote context. Progress reported on stderr -func Pull(ctx context.Context, rawImage string, options entities.ImagePullOptions) ([]string, error) { +func Pull(ctx context.Context, rawImage string, options *PullOptions) ([]string, error) { + if options == nil { + options = new(PullOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} + params, err := options.ToParams() + if err != nil { + return nil, err + } params.Set("reference", rawImage) - params.Set("overrideArch", options.OverrideArch) - params.Set("overrideOS", options.OverrideOS) - params.Set("overrideVariant", options.OverrideVariant) - if options.SkipTLSVerify != types.OptionalBoolUndefined { + if options.SkipTLSVerify != nil { + params.Del("SkipTLSVerify") // Note: we have to verify if skipped is false. - verifyTLS := bool(options.SkipTLSVerify == types.OptionalBoolFalse) - params.Set("tlsVerify", strconv.FormatBool(verifyTLS)) + params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) } - params.Set("allTags", strconv.FormatBool(options.AllTags)) // TODO: have a global system context we can pass around (1st argument) - header, err := auth.Header(nil, auth.XRegistryAuthHeader, options.Authfile, options.Username, options.Password) + header, err := auth.Header(nil, auth.XRegistryAuthHeader, options.GetAuthfile(), options.GetUsername(), options.GetPassword()) if err != nil { return nil, err } @@ -59,7 +59,7 @@ func Pull(ctx context.Context, rawImage string, options entities.ImagePullOption // Historically pull writes status to stderr stderr := io.Writer(os.Stderr) - if options.Quiet { + if options.GetQuiet() { stderr = ioutil.Discard } diff --git a/pkg/bindings/images/rm.go b/pkg/bindings/images/rm.go index 9685b75e4..e652e66aa 100644 --- a/pkg/bindings/images/rm.go +++ b/pkg/bindings/images/rm.go @@ -3,8 +3,6 @@ package images import ( "context" "net/http" - "net/url" - "strconv" "github.com/containers/podman/v2/pkg/api/handlers" "github.com/containers/podman/v2/pkg/bindings" @@ -12,8 +10,12 @@ import ( "github.com/containers/podman/v2/pkg/errorhandling" ) -// BachtRemove removes a batch of images from the local storage. -func BatchRemove(ctx context.Context, images []string, opts entities.ImageRemoveOptions) (*entities.ImageRemoveReport, []error) { +// Remove removes one or more images from the local storage. Use optional force option to remove an +// image, even if it's used by containers. +func Remove(ctx context.Context, images []string, options *RemoveOptions) (*entities.ImageRemoveReport, []error) { + if options == nil { + options = new(RemoveOptions) + } // FIXME - bindings tests are missing for this endpoint. Once the CI is // re-enabled for bindings, we need to add them. At the time of writing, // the tests don't compile. @@ -23,13 +25,13 @@ func BatchRemove(ctx context.Context, images []string, opts entities.ImageRemove return nil, []error{err} } - params := url.Values{} - params.Set("all", strconv.FormatBool(opts.All)) - params.Set("force", strconv.FormatBool(opts.Force)) - for _, i := range images { - params.Add("images", i) + params, err := options.ToParams() + if err != nil { + return nil, nil + } + for _, image := range images { + params.Add("images", image) } - response, err := conn.DoRequest(nil, http.MethodDelete, "/images/remove", params, nil) if err != nil { return nil, []error{err} @@ -40,26 +42,3 @@ func BatchRemove(ctx context.Context, images []string, opts entities.ImageRemove return &report.ImageRemoveReport, errorhandling.StringsToErrors(report.Errors) } - -// Remove removes an image from the local storage. Use force to remove an -// image, even if it's used by containers. -func Remove(ctx context.Context, nameOrID string, force bool) (*entities.ImageRemoveReport, error) { - var report handlers.LibpodImagesRemoveReport - conn, err := bindings.GetClient(ctx) - if err != nil { - return nil, err - } - - params := url.Values{} - params.Set("force", strconv.FormatBool(force)) - response, err := conn.DoRequest(nil, http.MethodDelete, "/images/%s", params, nil, nameOrID) - if err != nil { - return nil, err - } - if err := response.Process(&report); err != nil { - return nil, err - } - - errs := errorhandling.StringsToErrors(report.Errors) - return &report.ImageRemoveReport, errorhandling.JoinErrors(errs) -} diff --git a/pkg/bindings/images/types.go b/pkg/bindings/images/types.go new file mode 100644 index 000000000..3adb4356b --- /dev/null +++ b/pkg/bindings/images/types.go @@ -0,0 +1,193 @@ +package images + +import ( + "github.com/containers/buildah/imagebuildah" + "github.com/containers/common/pkg/config" +) + +//go:generate go run ../generator/generator.go RemoveOptions +// RemoveOptions are optional options for image removal +type RemoveOptions struct { + // All removes all images + All *bool + // Forces removes all containers based on the image + Force *bool +} + +//go:generate go run ../generator/generator.go DiffOptions +// DiffOptions are optional options image diffs +type DiffOptions struct { +} + +//go:generate go run ../generator/generator.go ListOptions +// ListOptions are optional options for listing images +type ListOptions struct { + // All lists all image in the image store including dangling images + All *bool + // filters that can be used to get a more specific list of images + Filters map[string][]string +} + +//go:generate go run ../generator/generator.go GetOptions +// GetOptions are optional options for inspecting an image +type GetOptions struct { + // Size computes the amount of storage the image consumes + Size *bool +} + +//go:generate go run ../generator/generator.go TreeOptions +// TreeOptions are optional options for a tree-based representation +// of the image +type TreeOptions struct { + // WhatRequires ... + WhatRequires *bool +} + +//go:generate go run ../generator/generator.go HistoryOptions +// HistoryOptions are optional options image history +type HistoryOptions struct { +} + +//go:generate go run ../generator/generator.go LoadOptions +// LoadOptions are optional options for loading an image +type LoadOptions struct { + // Reference is the name of the loaded image + Reference *string +} + +//go:generate go run ../generator/generator.go ExportOptions +// ExportOptions are optional options for exporting images +type ExportOptions struct { + // Compress the image + Compress *bool + // Format of the output + Format *string +} + +//go:generate go run ../generator/generator.go PruneOptions +// PruneOptions are optional options for pruning images +type PruneOptions struct { + // Prune all images + All *bool + // Filters to apply when pruning images + Filters map[string][]string +} + +//go:generate go run ../generator/generator.go TagOptions +// TagOptions are optional options for tagging images +type TagOptions struct { +} + +//go:generate go run ../generator/generator.go UntagOptions +// UntagOptions are optional options for untagging images +type UntagOptions struct { +} + +//go:generate go run ../generator/generator.go ImportOptions +// ImportOptions are optional options for importing images +type ImportOptions struct { + // Changes to be applied to the image + Changes *[]string + // Message to be applied to the image + Message *string + // Reference is a tag to be applied to the image + Reference *string + // Url to option image to import. Cannot be used with the reader + URL *string +} + +//go:generate go run ../generator/generator.go PushOptions +// PushOptions are optional options for importing images +type PushOptions struct { + // Authfile is the path to the authentication file. Ignored for remote + // calls. + Authfile *string + // CertDir is the path to certificate directories. Ignored for remote + // calls. + CertDir *string + // Compress tarball image layers when pushing to a directory using the 'dir' + // transport. Default is same compression type as source. Ignored for remote + // calls. + Compress *bool + // Username for authenticating against the registry. + Username *string + // Password for authenticating against the registry. + Password *string + // DigestFile, after copying the image, write the digest of the resulting + // image to the file. Ignored for remote calls. + DigestFile *string + // Format is the Manifest type (oci, v2s1, or v2s2) to use when pushing an + // image using the 'dir' transport. Default is manifest type of source. + // Ignored for remote calls. + Format *string + // Quiet can be specified to suppress pull progress when pulling. Ignored + // for remote calls. + Quiet *bool + // RemoveSignatures, discard any pre-existing signatures in the image. + // Ignored for remote calls. + RemoveSignatures *bool + // SignaturePolicy to use when pulling. Ignored for remote calls. + SignaturePolicy *string + // SignBy adds a signature at the destination using the specified key. + // Ignored for remote calls. + SignBy *string + // SkipTLSVerify to skip HTTPS and certificate verification. + SkipTLSVerify *bool +} + +//go:generate go run ../generator/generator.go SearchOptions +// SearchOptions are optional options for searching images on registries +type SearchOptions struct { + // Authfile is the path to the authentication file. Ignored for remote + // calls. + Authfile *string + // Filters for the search results. + Filters map[string][]string + // Limit the number of results. + Limit *int + // NoTrunc will not truncate the output. + NoTrunc *bool + // SkipTLSVerify to skip HTTPS and certificate verification. + SkipTLSVerify *bool + // ListTags search the available tags of the repository + ListTags *bool +} + +//go:generate go run ../generator/generator.go PullOptions +// PullOptions are optional options for pulling images +type PullOptions struct { + // AllTags can be specified to pull all tags of an image. Note + // that this only works if the image does not include a tag. + AllTags *bool + // Authfile is the path to the authentication file. Ignored for remote + // calls. + Authfile *string + // CertDir is the path to certificate directories. Ignored for remote + // calls. + CertDir *string + // Username for authenticating against the registry. + Username *string + // Password for authenticating against the registry. + Password *string + // OverrideArch will overwrite the local architecture for image pulls. + OverrideArch *string + // OverrideOS will overwrite the local operating system (OS) for image + // pulls. + OverrideOS *string + // OverrideVariant will overwrite the local variant for image pulls. + OverrideVariant *string + // Quiet can be specified to suppress pull progress when pulling. Ignored + // for remote calls. + Quiet *bool + // SignaturePolicy to use when pulling. Ignored for remote calls. + SignaturePolicy *string + // SkipTLSVerify to skip HTTPS and certificate verification. + SkipTLSVerify *bool + // PullPolicy whether to pull new image + PullPolicy *config.PullPolicy +} + +//BuildOptions are optional options for building images +type BuildOptions struct { + imagebuildah.BuildOptions +} diff --git a/pkg/bindings/images/types_diff_options.go b/pkg/bindings/images/types_diff_options.go new file mode 100644 index 000000000..d27c8945e --- /dev/null +++ b/pkg/bindings/images/types_diff_options.go @@ -0,0 +1,88 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *DiffOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *DiffOptions) 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 + } + 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 +} diff --git a/pkg/bindings/images/types_export_options.go b/pkg/bindings/images/types_export_options.go new file mode 100644 index 000000000..078b27fc0 --- /dev/null +++ b/pkg/bindings/images/types_export_options.go @@ -0,0 +1,120 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *ExportOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ExportOptions) 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 + } + 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 +} + +// WithCompress +func (o *ExportOptions) WithCompress(value bool) *ExportOptions { + v := &value + o.Compress = v + return o +} + +// GetCompress +func (o *ExportOptions) GetCompress() bool { + var compress bool + if o.Compress == nil { + return compress + } + return *o.Compress +} + +// WithFormat +func (o *ExportOptions) WithFormat(value string) *ExportOptions { + v := &value + o.Format = v + return o +} + +// GetFormat +func (o *ExportOptions) GetFormat() string { + var format string + if o.Format == nil { + return format + } + return *o.Format +} diff --git a/pkg/bindings/images/types_get_options.go b/pkg/bindings/images/types_get_options.go new file mode 100644 index 000000000..1161657f7 --- /dev/null +++ b/pkg/bindings/images/types_get_options.go @@ -0,0 +1,104 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *GetOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *GetOptions) 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 + } + 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 +} + +// WithSize +func (o *GetOptions) WithSize(value bool) *GetOptions { + v := &value + o.Size = v + return o +} + +// GetSize +func (o *GetOptions) GetSize() bool { + var size bool + if o.Size == nil { + return size + } + return *o.Size +} diff --git a/pkg/bindings/images/types_history_options.go b/pkg/bindings/images/types_history_options.go new file mode 100644 index 000000000..6f9854e03 --- /dev/null +++ b/pkg/bindings/images/types_history_options.go @@ -0,0 +1,88 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *HistoryOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *HistoryOptions) 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 + } + 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 +} diff --git a/pkg/bindings/images/types_import_options.go b/pkg/bindings/images/types_import_options.go new file mode 100644 index 000000000..f5e6c8f7e --- /dev/null +++ b/pkg/bindings/images/types_import_options.go @@ -0,0 +1,152 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *ImportOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ImportOptions) 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 + } + 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 +} + +// WithChanges +func (o *ImportOptions) WithChanges(value []string) *ImportOptions { + v := &value + o.Changes = v + return o +} + +// GetChanges +func (o *ImportOptions) GetChanges() []string { + var changes []string + if o.Changes == nil { + return changes + } + return *o.Changes +} + +// WithMessage +func (o *ImportOptions) WithMessage(value string) *ImportOptions { + v := &value + o.Message = v + return o +} + +// GetMessage +func (o *ImportOptions) GetMessage() string { + var message string + if o.Message == nil { + return message + } + return *o.Message +} + +// WithReference +func (o *ImportOptions) WithReference(value string) *ImportOptions { + v := &value + o.Reference = v + return o +} + +// GetReference +func (o *ImportOptions) GetReference() string { + var reference string + if o.Reference == nil { + return reference + } + return *o.Reference +} + +// WithURL +func (o *ImportOptions) WithURL(value string) *ImportOptions { + v := &value + o.URL = v + return o +} + +// GetURL +func (o *ImportOptions) GetURL() string { + var uRL string + if o.URL == nil { + return uRL + } + return *o.URL +} diff --git a/pkg/bindings/images/types_list_options.go b/pkg/bindings/images/types_list_options.go new file mode 100644 index 000000000..209d72e34 --- /dev/null +++ b/pkg/bindings/images/types_list_options.go @@ -0,0 +1,120 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *ListOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ListOptions) 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 + } + 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 +} + +// WithAll +func (o *ListOptions) WithAll(value bool) *ListOptions { + v := &value + o.All = v + return o +} + +// GetAll +func (o *ListOptions) GetAll() bool { + var all bool + if o.All == nil { + return all + } + return *o.All +} + +// WithFilters +func (o *ListOptions) WithFilters(value map[string][]string) *ListOptions { + v := value + o.Filters = v + return o +} + +// GetFilters +func (o *ListOptions) GetFilters() map[string][]string { + var filters map[string][]string + if o.Filters == nil { + return filters + } + return o.Filters +} diff --git a/pkg/bindings/images/types_load_options.go b/pkg/bindings/images/types_load_options.go new file mode 100644 index 000000000..6bba573d4 --- /dev/null +++ b/pkg/bindings/images/types_load_options.go @@ -0,0 +1,104 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *LoadOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *LoadOptions) 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 + } + 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 +} + +// WithReference +func (o *LoadOptions) WithReference(value string) *LoadOptions { + v := &value + o.Reference = v + return o +} + +// GetReference +func (o *LoadOptions) GetReference() string { + var reference string + if o.Reference == nil { + return reference + } + return *o.Reference +} diff --git a/pkg/bindings/images/types_prune_options.go b/pkg/bindings/images/types_prune_options.go new file mode 100644 index 000000000..c29fdae12 --- /dev/null +++ b/pkg/bindings/images/types_prune_options.go @@ -0,0 +1,120 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *PruneOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *PruneOptions) 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 + } + 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 +} + +// WithAll +func (o *PruneOptions) WithAll(value bool) *PruneOptions { + v := &value + o.All = v + return o +} + +// GetAll +func (o *PruneOptions) GetAll() bool { + var all bool + if o.All == nil { + return all + } + return *o.All +} + +// WithFilters +func (o *PruneOptions) WithFilters(value map[string][]string) *PruneOptions { + v := value + o.Filters = v + return o +} + +// GetFilters +func (o *PruneOptions) GetFilters() map[string][]string { + var filters map[string][]string + if o.Filters == nil { + return filters + } + return o.Filters +} diff --git a/pkg/bindings/images/types_pull_options.go b/pkg/bindings/images/types_pull_options.go new file mode 100644 index 000000000..07f3e079d --- /dev/null +++ b/pkg/bindings/images/types_pull_options.go @@ -0,0 +1,281 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + "github.com/containers/common/pkg/config" + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *PullOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *PullOptions) 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 + } + 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 +} + +// WithAllTags +func (o *PullOptions) WithAllTags(value bool) *PullOptions { + v := &value + o.AllTags = v + return o +} + +// GetAllTags +func (o *PullOptions) GetAllTags() bool { + var allTags bool + if o.AllTags == nil { + return allTags + } + return *o.AllTags +} + +// WithAuthfile +func (o *PullOptions) WithAuthfile(value string) *PullOptions { + v := &value + o.Authfile = v + return o +} + +// GetAuthfile +func (o *PullOptions) GetAuthfile() string { + var authfile string + if o.Authfile == nil { + return authfile + } + return *o.Authfile +} + +// WithCertDir +func (o *PullOptions) WithCertDir(value string) *PullOptions { + v := &value + o.CertDir = v + return o +} + +// GetCertDir +func (o *PullOptions) GetCertDir() string { + var certDir string + if o.CertDir == nil { + return certDir + } + return *o.CertDir +} + +// WithUsername +func (o *PullOptions) WithUsername(value string) *PullOptions { + v := &value + o.Username = v + return o +} + +// GetUsername +func (o *PullOptions) GetUsername() string { + var username string + if o.Username == nil { + return username + } + return *o.Username +} + +// WithPassword +func (o *PullOptions) WithPassword(value string) *PullOptions { + v := &value + o.Password = v + return o +} + +// GetPassword +func (o *PullOptions) GetPassword() string { + var password string + if o.Password == nil { + return password + } + return *o.Password +} + +// WithOverrideArch +func (o *PullOptions) WithOverrideArch(value string) *PullOptions { + v := &value + o.OverrideArch = v + return o +} + +// GetOverrideArch +func (o *PullOptions) GetOverrideArch() string { + var overrideArch string + if o.OverrideArch == nil { + return overrideArch + } + return *o.OverrideArch +} + +// WithOverrideOS +func (o *PullOptions) WithOverrideOS(value string) *PullOptions { + v := &value + o.OverrideOS = v + return o +} + +// GetOverrideOS +func (o *PullOptions) GetOverrideOS() string { + var overrideOS string + if o.OverrideOS == nil { + return overrideOS + } + return *o.OverrideOS +} + +// WithOverrideVariant +func (o *PullOptions) WithOverrideVariant(value string) *PullOptions { + v := &value + o.OverrideVariant = v + return o +} + +// GetOverrideVariant +func (o *PullOptions) GetOverrideVariant() string { + var overrideVariant string + if o.OverrideVariant == nil { + return overrideVariant + } + return *o.OverrideVariant +} + +// WithQuiet +func (o *PullOptions) WithQuiet(value bool) *PullOptions { + v := &value + o.Quiet = v + return o +} + +// GetQuiet +func (o *PullOptions) GetQuiet() bool { + var quiet bool + if o.Quiet == nil { + return quiet + } + return *o.Quiet +} + +// WithSignaturePolicy +func (o *PullOptions) WithSignaturePolicy(value string) *PullOptions { + v := &value + o.SignaturePolicy = v + return o +} + +// GetSignaturePolicy +func (o *PullOptions) GetSignaturePolicy() string { + var signaturePolicy string + if o.SignaturePolicy == nil { + return signaturePolicy + } + return *o.SignaturePolicy +} + +// WithSkipTLSVerify +func (o *PullOptions) WithSkipTLSVerify(value bool) *PullOptions { + v := &value + o.SkipTLSVerify = v + return o +} + +// GetSkipTLSVerify +func (o *PullOptions) GetSkipTLSVerify() bool { + var skipTLSVerify bool + if o.SkipTLSVerify == nil { + return skipTLSVerify + } + return *o.SkipTLSVerify +} + +// WithPullPolicy +func (o *PullOptions) WithPullPolicy(value config.PullPolicy) *PullOptions { + v := &value + o.PullPolicy = v + return o +} + +// GetPullPolicy +func (o *PullOptions) GetPullPolicy() config.PullPolicy { + var pullPolicy config.PullPolicy + if o.PullPolicy == nil { + return pullPolicy + } + return *o.PullPolicy +} diff --git a/pkg/bindings/images/types_push_options.go b/pkg/bindings/images/types_push_options.go new file mode 100644 index 000000000..f9ce1b835 --- /dev/null +++ b/pkg/bindings/images/types_push_options.go @@ -0,0 +1,280 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *PushOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *PushOptions) 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 + } + 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 +} + +// WithAuthfile +func (o *PushOptions) WithAuthfile(value string) *PushOptions { + v := &value + o.Authfile = v + return o +} + +// GetAuthfile +func (o *PushOptions) GetAuthfile() string { + var authfile string + if o.Authfile == nil { + return authfile + } + return *o.Authfile +} + +// WithCertDir +func (o *PushOptions) WithCertDir(value string) *PushOptions { + v := &value + o.CertDir = v + return o +} + +// GetCertDir +func (o *PushOptions) GetCertDir() string { + var certDir string + if o.CertDir == nil { + return certDir + } + return *o.CertDir +} + +// WithCompress +func (o *PushOptions) WithCompress(value bool) *PushOptions { + v := &value + o.Compress = v + return o +} + +// GetCompress +func (o *PushOptions) GetCompress() bool { + var compress bool + if o.Compress == nil { + return compress + } + return *o.Compress +} + +// WithUsername +func (o *PushOptions) WithUsername(value string) *PushOptions { + v := &value + o.Username = v + return o +} + +// GetUsername +func (o *PushOptions) GetUsername() string { + var username string + if o.Username == nil { + return username + } + return *o.Username +} + +// WithPassword +func (o *PushOptions) WithPassword(value string) *PushOptions { + v := &value + o.Password = v + return o +} + +// GetPassword +func (o *PushOptions) GetPassword() string { + var password string + if o.Password == nil { + return password + } + return *o.Password +} + +// WithDigestFile +func (o *PushOptions) WithDigestFile(value string) *PushOptions { + v := &value + o.DigestFile = v + return o +} + +// GetDigestFile +func (o *PushOptions) GetDigestFile() string { + var digestFile string + if o.DigestFile == nil { + return digestFile + } + return *o.DigestFile +} + +// WithFormat +func (o *PushOptions) WithFormat(value string) *PushOptions { + v := &value + o.Format = v + return o +} + +// GetFormat +func (o *PushOptions) GetFormat() string { + var format string + if o.Format == nil { + return format + } + return *o.Format +} + +// WithQuiet +func (o *PushOptions) WithQuiet(value bool) *PushOptions { + v := &value + o.Quiet = v + return o +} + +// GetQuiet +func (o *PushOptions) GetQuiet() bool { + var quiet bool + if o.Quiet == nil { + return quiet + } + return *o.Quiet +} + +// WithRemoveSignatures +func (o *PushOptions) WithRemoveSignatures(value bool) *PushOptions { + v := &value + o.RemoveSignatures = v + return o +} + +// GetRemoveSignatures +func (o *PushOptions) GetRemoveSignatures() bool { + var removeSignatures bool + if o.RemoveSignatures == nil { + return removeSignatures + } + return *o.RemoveSignatures +} + +// WithSignaturePolicy +func (o *PushOptions) WithSignaturePolicy(value string) *PushOptions { + v := &value + o.SignaturePolicy = v + return o +} + +// GetSignaturePolicy +func (o *PushOptions) GetSignaturePolicy() string { + var signaturePolicy string + if o.SignaturePolicy == nil { + return signaturePolicy + } + return *o.SignaturePolicy +} + +// WithSignBy +func (o *PushOptions) WithSignBy(value string) *PushOptions { + v := &value + o.SignBy = v + return o +} + +// GetSignBy +func (o *PushOptions) GetSignBy() string { + var signBy string + if o.SignBy == nil { + return signBy + } + return *o.SignBy +} + +// WithSkipTLSVerify +func (o *PushOptions) WithSkipTLSVerify(value bool) *PushOptions { + v := &value + o.SkipTLSVerify = v + return o +} + +// GetSkipTLSVerify +func (o *PushOptions) GetSkipTLSVerify() bool { + var skipTLSVerify bool + if o.SkipTLSVerify == nil { + return skipTLSVerify + } + return *o.SkipTLSVerify +} diff --git a/pkg/bindings/images/types_remove_options.go b/pkg/bindings/images/types_remove_options.go new file mode 100644 index 000000000..c9692c2b7 --- /dev/null +++ b/pkg/bindings/images/types_remove_options.go @@ -0,0 +1,120 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *RemoveOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *RemoveOptions) 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 + } + 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 +} + +// WithAll +func (o *RemoveOptions) WithAll(value bool) *RemoveOptions { + v := &value + o.All = v + return o +} + +// GetAll +func (o *RemoveOptions) GetAll() bool { + var all bool + if o.All == nil { + return all + } + return *o.All +} + +// WithForce +func (o *RemoveOptions) WithForce(value bool) *RemoveOptions { + v := &value + o.Force = v + return o +} + +// GetForce +func (o *RemoveOptions) GetForce() bool { + var force bool + if o.Force == nil { + return force + } + return *o.Force +} diff --git a/pkg/bindings/images/types_search_options.go b/pkg/bindings/images/types_search_options.go new file mode 100644 index 000000000..e6168ac33 --- /dev/null +++ b/pkg/bindings/images/types_search_options.go @@ -0,0 +1,184 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *SearchOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *SearchOptions) 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 + } + 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 +} + +// WithAuthfile +func (o *SearchOptions) WithAuthfile(value string) *SearchOptions { + v := &value + o.Authfile = v + return o +} + +// GetAuthfile +func (o *SearchOptions) GetAuthfile() string { + var authfile string + if o.Authfile == nil { + return authfile + } + return *o.Authfile +} + +// WithFilters +func (o *SearchOptions) WithFilters(value map[string][]string) *SearchOptions { + v := value + o.Filters = v + return o +} + +// GetFilters +func (o *SearchOptions) GetFilters() map[string][]string { + var filters map[string][]string + if o.Filters == nil { + return filters + } + return o.Filters +} + +// WithLimit +func (o *SearchOptions) WithLimit(value int) *SearchOptions { + v := &value + o.Limit = v + return o +} + +// GetLimit +func (o *SearchOptions) GetLimit() int { + var limit int + if o.Limit == nil { + return limit + } + return *o.Limit +} + +// WithNoTrunc +func (o *SearchOptions) WithNoTrunc(value bool) *SearchOptions { + v := &value + o.NoTrunc = v + return o +} + +// GetNoTrunc +func (o *SearchOptions) GetNoTrunc() bool { + var noTrunc bool + if o.NoTrunc == nil { + return noTrunc + } + return *o.NoTrunc +} + +// WithSkipTLSVerify +func (o *SearchOptions) WithSkipTLSVerify(value bool) *SearchOptions { + v := &value + o.SkipTLSVerify = v + return o +} + +// GetSkipTLSVerify +func (o *SearchOptions) GetSkipTLSVerify() bool { + var skipTLSVerify bool + if o.SkipTLSVerify == nil { + return skipTLSVerify + } + return *o.SkipTLSVerify +} + +// WithListTags +func (o *SearchOptions) WithListTags(value bool) *SearchOptions { + v := &value + o.ListTags = v + return o +} + +// GetListTags +func (o *SearchOptions) GetListTags() bool { + var listTags bool + if o.ListTags == nil { + return listTags + } + return *o.ListTags +} diff --git a/pkg/bindings/images/types_tag_options.go b/pkg/bindings/images/types_tag_options.go new file mode 100644 index 000000000..f6396e590 --- /dev/null +++ b/pkg/bindings/images/types_tag_options.go @@ -0,0 +1,88 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *TagOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *TagOptions) 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 + } + 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 +} diff --git a/pkg/bindings/images/types_tree_options.go b/pkg/bindings/images/types_tree_options.go new file mode 100644 index 000000000..fb2493f85 --- /dev/null +++ b/pkg/bindings/images/types_tree_options.go @@ -0,0 +1,104 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *TreeOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *TreeOptions) 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 + } + 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 +} + +// WithWhatRequires +func (o *TreeOptions) WithWhatRequires(value bool) *TreeOptions { + v := &value + o.WhatRequires = v + return o +} + +// GetWhatRequires +func (o *TreeOptions) GetWhatRequires() bool { + var whatRequires bool + if o.WhatRequires == nil { + return whatRequires + } + return *o.WhatRequires +} diff --git a/pkg/bindings/images/types_untag_options.go b/pkg/bindings/images/types_untag_options.go new file mode 100644 index 000000000..8faf5c14e --- /dev/null +++ b/pkg/bindings/images/types_untag_options.go @@ -0,0 +1,88 @@ +package images + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *UntagOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *UntagOptions) 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 + } + 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 +} diff --git a/pkg/bindings/manifests/manifests.go b/pkg/bindings/manifests/manifests.go index 0330b13e2..ef4e2a908 100644 --- a/pkg/bindings/manifests/manifests.go +++ b/pkg/bindings/manifests/manifests.go @@ -5,11 +5,9 @@ import ( "errors" "net/http" "net/url" - "strconv" "strings" "github.com/containers/image/v5/manifest" - "github.com/containers/podman/v2/libpod/image" "github.com/containers/podman/v2/pkg/api/handlers" "github.com/containers/podman/v2/pkg/bindings" jsoniter "github.com/json-iterator/go" @@ -19,8 +17,11 @@ import ( // the new manifest can also be specified. The all boolean specifies to add all entries // of a list if the name provided is a manifest list. The ID of the new manifest list // is returned as a string. -func Create(ctx context.Context, names, images []string, all *bool) (string, error) { +func Create(ctx context.Context, names, images []string, options *CreateOptions) (string, error) { var idr handlers.IDResponse + if options == nil { + options = new(CreateOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return "", err @@ -28,9 +29,9 @@ func Create(ctx context.Context, names, images []string, all *bool) (string, err if len(names) < 1 { return "", errors.New("creating a manifest requires at least one name argument") } - params := url.Values{} - if all != nil { - params.Set("all", strconv.FormatBool(*all)) + params, err := options.ToParams() + if err != nil { + return "", err } for _, name := range names { params.Add("name", name) @@ -47,8 +48,12 @@ func Create(ctx context.Context, names, images []string, all *bool) (string, err } // Inspect returns a manifest list for a given name. -func Inspect(ctx context.Context, name string) (*manifest.Schema2List, error) { +func Inspect(ctx context.Context, name string, options *InspectOptions) (*manifest.Schema2List, error) { var list manifest.Schema2List + if options == nil { + options = new(InspectOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err @@ -62,8 +67,11 @@ func Inspect(ctx context.Context, name string) (*manifest.Schema2List, error) { // Add adds a manifest to a given manifest list. Additional options for the manifest // can also be specified. The ID of the new manifest list is returned as a string -func Add(ctx context.Context, name string, options image.ManifestAddOpts) (string, error) { +func Add(ctx context.Context, name string, options *AddOptions) (string, error) { var idr handlers.IDResponse + if options == nil { + options = new(AddOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return "", err @@ -82,8 +90,12 @@ func Add(ctx context.Context, name string, options image.ManifestAddOpts) (strin // Remove deletes a manifest entry from a manifest list. Both name and the digest to be // removed are mandatory inputs. The ID of the new manifest list is returned as a string. -func Remove(ctx context.Context, name, digest string) (string, error) { +func Remove(ctx context.Context, name, digest string, options *RemoveOptions) (string, error) { var idr handlers.IDResponse + if options == nil { + options = new(RemoveOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return "", err @@ -100,24 +112,26 @@ func Remove(ctx context.Context, name, digest string) (string, error) { // Push takes a manifest list and pushes to a destination. If the destination is not specified, // the name will be used instead. If the optional all boolean is specified, all images specified // in the list will be pushed as well. -func Push(ctx context.Context, name string, destination *string, all *bool) (string, error) { +func Push(ctx context.Context, name, destination string, options *PushOptions) (string, error) { var ( idr handlers.IDResponse ) - dest := name + if options == nil { + options = new(PushOptions) + } + if len(destination) < 1 { + destination = name + } conn, err := bindings.GetClient(ctx) if err != nil { return "", err } - params := url.Values{} - params.Set("image", name) - if destination != nil { - dest = *destination - } - params.Set("destination", dest) - if all != nil { - params.Set("all", strconv.FormatBool(*all)) + params, err := options.ToParams() + if err != nil { + return "", err } + params.Set("image", name) + params.Set("destination", destination) _, err = conn.DoRequest(nil, http.MethodPost, "/manifests/%s/push", params, nil, name) if err != nil { return "", err diff --git a/pkg/bindings/manifests/types.go b/pkg/bindings/manifests/types.go new file mode 100644 index 000000000..f5054d424 --- /dev/null +++ b/pkg/bindings/manifests/types.go @@ -0,0 +1,36 @@ +package manifests + +//go:generate go run ../generator/generator.go InspectOptions +// InspectOptions are optional options for inspecting manifests +type InspectOptions struct { +} + +//go:generate go run ../generator/generator.go CreateOptions +// CreateOptions are optional options for creating manifests +type CreateOptions struct { + All *bool +} + +//go:generate go run ../generator/generator.go AddOptions +// AddOptions are optional options for adding manifests +type AddOptions struct { + All *bool + Annotation map[string]string + Arch *string + Features []string + Images []string + OS *string + OSVersion *string + Variant *string +} + +//go:generate go run ../generator/generator.go RemoveOptions +// RemoveOptions are optional options for removing manifests +type RemoveOptions struct { +} + +//go:generate go run ../generator/generator.go PushOptions +// RemoveOptions are optional options for pushing manifests +type PushOptions struct { + All *bool +} diff --git a/pkg/bindings/manifests/types_add_options.go b/pkg/bindings/manifests/types_add_options.go new file mode 100644 index 000000000..d45effedc --- /dev/null +++ b/pkg/bindings/manifests/types_add_options.go @@ -0,0 +1,216 @@ +package manifests + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *AddOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *AddOptions) 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 + } + 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 +} + +// WithAll +func (o *AddOptions) WithAll(value bool) *AddOptions { + v := &value + o.All = v + return o +} + +// GetAll +func (o *AddOptions) GetAll() bool { + var all bool + if o.All == nil { + return all + } + return *o.All +} + +// WithAnnotation +func (o *AddOptions) WithAnnotation(value map[string]string) *AddOptions { + v := value + o.Annotation = v + return o +} + +// GetAnnotation +func (o *AddOptions) GetAnnotation() map[string]string { + var annotation map[string]string + if o.Annotation == nil { + return annotation + } + return o.Annotation +} + +// WithArch +func (o *AddOptions) WithArch(value string) *AddOptions { + v := &value + o.Arch = v + return o +} + +// GetArch +func (o *AddOptions) GetArch() string { + var arch string + if o.Arch == nil { + return arch + } + return *o.Arch +} + +// WithFeatures +func (o *AddOptions) WithFeatures(value []string) *AddOptions { + v := value + o.Features = v + return o +} + +// GetFeatures +func (o *AddOptions) GetFeatures() []string { + var features []string + if o.Features == nil { + return features + } + return o.Features +} + +// WithImages +func (o *AddOptions) WithImages(value []string) *AddOptions { + v := value + o.Images = v + return o +} + +// GetImages +func (o *AddOptions) GetImages() []string { + var images []string + if o.Images == nil { + return images + } + return o.Images +} + +// WithOS +func (o *AddOptions) WithOS(value string) *AddOptions { + v := &value + o.OS = v + return o +} + +// GetOS +func (o *AddOptions) GetOS() string { + var oS string + if o.OS == nil { + return oS + } + return *o.OS +} + +// WithOSVersion +func (o *AddOptions) WithOSVersion(value string) *AddOptions { + v := &value + o.OSVersion = v + return o +} + +// GetOSVersion +func (o *AddOptions) GetOSVersion() string { + var oSVersion string + if o.OSVersion == nil { + return oSVersion + } + return *o.OSVersion +} + +// WithVariant +func (o *AddOptions) WithVariant(value string) *AddOptions { + v := &value + o.Variant = v + return o +} + +// GetVariant +func (o *AddOptions) GetVariant() string { + var variant string + if o.Variant == nil { + return variant + } + return *o.Variant +} diff --git a/pkg/bindings/manifests/types_create_options.go b/pkg/bindings/manifests/types_create_options.go new file mode 100644 index 000000000..da07f1abf --- /dev/null +++ b/pkg/bindings/manifests/types_create_options.go @@ -0,0 +1,104 @@ +package manifests + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *CreateOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *CreateOptions) 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 + } + 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 +} + +// WithAll +func (o *CreateOptions) WithAll(value bool) *CreateOptions { + v := &value + o.All = v + return o +} + +// GetAll +func (o *CreateOptions) GetAll() bool { + var all bool + if o.All == nil { + return all + } + return *o.All +} diff --git a/pkg/bindings/manifests/types_inspect_options.go b/pkg/bindings/manifests/types_inspect_options.go new file mode 100644 index 000000000..0d32d5bb9 --- /dev/null +++ b/pkg/bindings/manifests/types_inspect_options.go @@ -0,0 +1,88 @@ +package manifests + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *InspectOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *InspectOptions) 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 + } + 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 +} diff --git a/pkg/bindings/manifests/types_push_options.go b/pkg/bindings/manifests/types_push_options.go new file mode 100644 index 000000000..4226733c9 --- /dev/null +++ b/pkg/bindings/manifests/types_push_options.go @@ -0,0 +1,104 @@ +package manifests + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *PushOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *PushOptions) 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 + } + 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 +} + +// WithAll +func (o *PushOptions) WithAll(value bool) *PushOptions { + v := &value + o.All = v + return o +} + +// GetAll +func (o *PushOptions) GetAll() bool { + var all bool + if o.All == nil { + return all + } + return *o.All +} diff --git a/pkg/bindings/manifests/types_remove_options.go b/pkg/bindings/manifests/types_remove_options.go new file mode 100644 index 000000000..f99220f6c --- /dev/null +++ b/pkg/bindings/manifests/types_remove_options.go @@ -0,0 +1,88 @@ +package manifests + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *RemoveOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *RemoveOptions) 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 + } + 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 +} diff --git a/pkg/bindings/network/network.go b/pkg/bindings/network/network.go index 347f97703..7cd251b0e 100644 --- a/pkg/bindings/network/network.go +++ b/pkg/bindings/network/network.go @@ -2,10 +2,8 @@ package network import ( "context" - "encoding/json" "net/http" "net/url" - "strconv" "strings" "github.com/containers/podman/v2/pkg/bindings" @@ -14,15 +12,18 @@ import ( ) // Create makes a new CNI network configuration -func Create(ctx context.Context, options entities.NetworkCreateOptions, name *string) (*entities.NetworkCreateReport, error) { +func Create(ctx context.Context, options *CreateOptions) (*entities.NetworkCreateReport, error) { var report entities.NetworkCreateReport + if options == nil { + options = new(CreateOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } params := url.Values{} - if name != nil { - params.Set("name", *name) + if options.Name != nil { + params.Set("name", options.GetName()) } networkConfig, err := jsoniter.MarshalToString(options) if err != nil { @@ -37,8 +38,12 @@ func Create(ctx context.Context, options entities.NetworkCreateOptions, name *st } // Inspect returns low level information about a CNI network configuration -func Inspect(ctx context.Context, nameOrID string) ([]entities.NetworkInspectReport, error) { +func Inspect(ctx context.Context, nameOrID string, options *InspectOptions) ([]entities.NetworkInspectReport, error) { var reports []entities.NetworkInspectReport + if options == nil { + options = new(InspectOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err @@ -53,15 +58,18 @@ func Inspect(ctx context.Context, nameOrID string) ([]entities.NetworkInspectRep // Remove deletes a defined CNI network configuration by name. The optional force boolean // will remove all containers associated with the network when set to true. A slice // of NetworkRemoveReports are returned. -func Remove(ctx context.Context, nameOrID string, force *bool) ([]*entities.NetworkRmReport, error) { +func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) ([]*entities.NetworkRmReport, error) { var reports []*entities.NetworkRmReport + if options == nil { + options = new(RemoveOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if force != nil { - params.Set("force", strconv.FormatBool(*force)) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodDelete, "/networks/%s", params, nil, nameOrID) if err != nil { @@ -71,21 +79,20 @@ func Remove(ctx context.Context, nameOrID string, force *bool) ([]*entities.Netw } // List returns a summary of all CNI network configurations -func List(ctx context.Context, options entities.NetworkListOptions) ([]*entities.NetworkListReport, error) { +func List(ctx context.Context, options *ListOptions) ([]*entities.NetworkListReport, error) { var ( netList []*entities.NetworkListReport ) + if options == nil { + options = new(ListOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if options.Filters != nil { - b, err := json.Marshal(options.Filters) - if err != nil { - return nil, err - } - params.Set("filters", string(b)) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodGet, "/networks/json", params, nil) if err != nil { @@ -95,13 +102,28 @@ func List(ctx context.Context, options entities.NetworkListOptions) ([]*entities } // Disconnect removes a container from a given network -func Disconnect(ctx context.Context, networkName string, options entities.NetworkDisconnectOptions) error { +func Disconnect(ctx context.Context, networkName string, ContainerNameOrId string, options *DisconnectOptions) error { + if options == nil { + options = new(DisconnectOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return err } + // No params are used for disconnect params := url.Values{} - body, err := jsoniter.MarshalToString(options) + // Disconnect sends everything in body + disconnect := struct { + Container string + Force bool + }{ + Container: ContainerNameOrId, + } + if force := options.GetForce(); options.Changed("Force") { + disconnect.Force = force + } + + body, err := jsoniter.MarshalToString(disconnect) if err != nil { return err } @@ -114,13 +136,27 @@ func Disconnect(ctx context.Context, networkName string, options entities.Networ } // Connect adds a container to a network -func Connect(ctx context.Context, networkName string, options entities.NetworkConnectOptions) error { +func Connect(ctx context.Context, networkName string, ContainerNameOrId string, options *ConnectOptions) error { + if options == nil { + options = new(ConnectOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return err } + // No params are used in connect params := url.Values{} - body, err := jsoniter.MarshalToString(options) + // Connect sends everything in body + connect := struct { + Container string + Aliases []string + }{ + Container: ContainerNameOrId, + } + if aliases := options.GetAliases(); options.Changed("Aliases") { + connect.Aliases = aliases + } + body, err := jsoniter.MarshalToString(connect) if err != nil { return err } diff --git a/pkg/bindings/network/types.go b/pkg/bindings/network/types.go new file mode 100644 index 000000000..2a7e500dd --- /dev/null +++ b/pkg/bindings/network/types.go @@ -0,0 +1,70 @@ +package network + +import "net" + +//go:generate go run ../generator/generator.go CreateOptions +// CreateOptions are optional options for creating networks +type CreateOptions struct { + // DisableDNS turns off use of DNSMasq for name resolution + // on the network + DisableDNS *bool + // Driver is the name of network driver + Driver *string + // Gateway of the network + Gateway *net.IP + // Internal turns off communication outside the networking + // being created + Internal *bool + // Labels are metadata that can be associated with the network + Labels map[string]string + // MacVLAN is the name of the macvlan network to associate with + MacVLAN *string + // Range is the CIDR description of leasable IP addresses + IPRange *net.IPNet `scheme:"range"` + // Subnet to use + Subnet *net.IPNet + // IPv6 means the network is ipv6 capable + IPv6 *bool + // Options are a mapping of driver options and values. + Options map[string]string + // Name of the network + Name *string +} + +//go:generate go run ../generator/generator.go InspectOptions +// InspectOptions are optional options for inspecting networks +type InspectOptions struct { +} + +//go:generate go run ../generator/generator.go RemoveOptions +// RemoveOptions are optional options for inspecting networks +type RemoveOptions struct { + // Force removes the network even if it is being used + Force *bool +} + +//go:generate go run ../generator/generator.go ListOptions +// ListOptions are optional options for listing networks +type ListOptions struct { + // Filters are applied to the list of networks to be more + // specific on the output + Filters map[string][]string +} + +//go:generate go run ../generator/generator.go DisconnectOptions +// DisconnectOptions are optional options for disconnecting +// containers from a network +type DisconnectOptions struct { + // Force indicates to remove the container from + // the network forcibly + Force *bool +} + +//go:generate go run ../generator/generator.go ConnectOptions +// ConnectOptions are optional options for connecting +// containers from a network +type ConnectOptions struct { + // Aliases are names the container will be known as + // when using the dns plugin + Aliases *[]string +} diff --git a/pkg/bindings/network/types_connect_options.go b/pkg/bindings/network/types_connect_options.go new file mode 100644 index 000000000..6fcc4536e --- /dev/null +++ b/pkg/bindings/network/types_connect_options.go @@ -0,0 +1,104 @@ +package network + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *ConnectOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ConnectOptions) 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 + } + 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 +} + +// WithAliases +func (o *ConnectOptions) WithAliases(value []string) *ConnectOptions { + v := &value + o.Aliases = v + return o +} + +// GetAliases +func (o *ConnectOptions) GetAliases() []string { + var aliases []string + if o.Aliases == nil { + return aliases + } + return *o.Aliases +} diff --git a/pkg/bindings/network/types_create_options.go b/pkg/bindings/network/types_create_options.go new file mode 100644 index 000000000..daec6a254 --- /dev/null +++ b/pkg/bindings/network/types_create_options.go @@ -0,0 +1,265 @@ +package network + +import ( + "net" + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *CreateOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *CreateOptions) 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 + } + 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 +} + +// WithDisableDNS +func (o *CreateOptions) WithDisableDNS(value bool) *CreateOptions { + v := &value + o.DisableDNS = v + return o +} + +// GetDisableDNS +func (o *CreateOptions) GetDisableDNS() bool { + var disableDNS bool + if o.DisableDNS == nil { + return disableDNS + } + return *o.DisableDNS +} + +// WithDriver +func (o *CreateOptions) WithDriver(value string) *CreateOptions { + v := &value + o.Driver = v + return o +} + +// GetDriver +func (o *CreateOptions) GetDriver() string { + var driver string + if o.Driver == nil { + return driver + } + return *o.Driver +} + +// WithGateway +func (o *CreateOptions) WithGateway(value net.IP) *CreateOptions { + v := &value + o.Gateway = v + return o +} + +// GetGateway +func (o *CreateOptions) GetGateway() net.IP { + var gateway net.IP + if o.Gateway == nil { + return gateway + } + return *o.Gateway +} + +// WithInternal +func (o *CreateOptions) WithInternal(value bool) *CreateOptions { + v := &value + o.Internal = v + return o +} + +// GetInternal +func (o *CreateOptions) GetInternal() bool { + var internal bool + if o.Internal == nil { + return internal + } + return *o.Internal +} + +// WithLabels +func (o *CreateOptions) WithLabels(value map[string]string) *CreateOptions { + v := value + o.Labels = v + return o +} + +// GetLabels +func (o *CreateOptions) GetLabels() map[string]string { + var labels map[string]string + if o.Labels == nil { + return labels + } + return o.Labels +} + +// WithMacVLAN +func (o *CreateOptions) WithMacVLAN(value string) *CreateOptions { + v := &value + o.MacVLAN = v + return o +} + +// GetMacVLAN +func (o *CreateOptions) GetMacVLAN() string { + var macVLAN string + if o.MacVLAN == nil { + return macVLAN + } + return *o.MacVLAN +} + +// WithIPRange +func (o *CreateOptions) WithIPRange(value net.IPNet) *CreateOptions { + v := &value + o.IPRange = v + return o +} + +// GetIPRange +func (o *CreateOptions) GetIPRange() net.IPNet { + var ipRange net.IPNet + if o.IPRange == nil { + return ipRange + } + return *o.IPRange +} + +// WithSubnet +func (o *CreateOptions) WithSubnet(value net.IPNet) *CreateOptions { + v := &value + o.Subnet = v + return o +} + +// GetSubnet +func (o *CreateOptions) GetSubnet() net.IPNet { + var subnet net.IPNet + if o.Subnet == nil { + return subnet + } + return *o.Subnet +} + +// WithIPv6 +func (o *CreateOptions) WithIPv6(value bool) *CreateOptions { + v := &value + o.IPv6 = v + return o +} + +// GetIPv6 +func (o *CreateOptions) GetIPv6() bool { + var iPv6 bool + if o.IPv6 == nil { + return iPv6 + } + return *o.IPv6 +} + +// WithOptions +func (o *CreateOptions) WithOptions(value map[string]string) *CreateOptions { + v := value + o.Options = v + return o +} + +// GetOptions +func (o *CreateOptions) GetOptions() map[string]string { + var options map[string]string + if o.Options == nil { + return options + } + return o.Options +} + +// WithName +func (o *CreateOptions) WithName(value string) *CreateOptions { + v := &value + o.Name = v + return o +} + +// GetName +func (o *CreateOptions) GetName() string { + var name string + if o.Name == nil { + return name + } + return *o.Name +} diff --git a/pkg/bindings/network/types_disconnect_options.go b/pkg/bindings/network/types_disconnect_options.go new file mode 100644 index 000000000..821279976 --- /dev/null +++ b/pkg/bindings/network/types_disconnect_options.go @@ -0,0 +1,104 @@ +package network + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *DisconnectOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *DisconnectOptions) 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 + } + 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 +} + +// WithForce +func (o *DisconnectOptions) WithForce(value bool) *DisconnectOptions { + v := &value + o.Force = v + return o +} + +// GetForce +func (o *DisconnectOptions) GetForce() bool { + var force bool + if o.Force == nil { + return force + } + return *o.Force +} diff --git a/pkg/bindings/network/types_inspect_options.go b/pkg/bindings/network/types_inspect_options.go new file mode 100644 index 000000000..7704904ce --- /dev/null +++ b/pkg/bindings/network/types_inspect_options.go @@ -0,0 +1,88 @@ +package network + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *InspectOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *InspectOptions) 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 + } + 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 +} diff --git a/pkg/bindings/network/types_list_options.go b/pkg/bindings/network/types_list_options.go new file mode 100644 index 000000000..bbd2a71db --- /dev/null +++ b/pkg/bindings/network/types_list_options.go @@ -0,0 +1,104 @@ +package network + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *ListOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ListOptions) 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 + } + 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 +} + +// WithFilters +func (o *ListOptions) WithFilters(value map[string][]string) *ListOptions { + v := value + o.Filters = v + return o +} + +// GetFilters +func (o *ListOptions) GetFilters() map[string][]string { + var filters map[string][]string + if o.Filters == nil { + return filters + } + return o.Filters +} diff --git a/pkg/bindings/network/types_remove_options.go b/pkg/bindings/network/types_remove_options.go new file mode 100644 index 000000000..b24835ae4 --- /dev/null +++ b/pkg/bindings/network/types_remove_options.go @@ -0,0 +1,104 @@ +package network + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *RemoveOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *RemoveOptions) 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 + } + 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 +} + +// WithForce +func (o *RemoveOptions) WithForce(value bool) *RemoveOptions { + v := &value + o.Force = v + return o +} + +// GetForce +func (o *RemoveOptions) GetForce() bool { + var force bool + if o.Force == nil { + return force + } + return *o.Force +} diff --git a/pkg/bindings/play/play.go b/pkg/bindings/play/play.go index cfb40d74b..9ee02a093 100644 --- a/pkg/bindings/play/play.go +++ b/pkg/bindings/play/play.go @@ -3,18 +3,19 @@ package play import ( "context" "net/http" - "net/url" "os" "strconv" - "github.com/containers/image/v5/types" "github.com/containers/podman/v2/pkg/auth" "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/domain/entities" ) -func Kube(ctx context.Context, path string, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) { +func Kube(ctx context.Context, path string, options *KubeOptions) (*entities.PlayKubeReport, error) { var report entities.PlayKubeReport + if options == nil { + options = new(KubeOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err @@ -26,18 +27,19 @@ func Kube(ctx context.Context, path string, options entities.PlayKubeOptions) (* } defer f.Close() - params := url.Values{} - params.Set("network", options.Network) - params.Set("logDriver", options.LogDriver) - if options.SkipTLSVerify != types.OptionalBoolUndefined { - params.Set("tlsVerify", strconv.FormatBool(options.SkipTLSVerify != types.OptionalBoolTrue)) + params, err := options.ToParams() + if err != nil { + return nil, err + } + if options.SkipTLSVerify != nil { + params.Set("tlsVerify", strconv.FormatBool(options.GetSkipTLSVerify())) } - if options.Start != types.OptionalBoolUndefined { - params.Set("start", strconv.FormatBool(options.Start == types.OptionalBoolTrue)) + if options.Start != nil { + params.Set("start", strconv.FormatBool(options.GetStart())) } // TODO: have a global system context we can pass around (1st argument) - header, err := auth.Header(nil, auth.XRegistryAuthHeader, options.Authfile, options.Username, options.Password) + header, err := auth.Header(nil, auth.XRegistryAuthHeader, options.GetAuthfile(), options.GetUsername(), options.GetPassword()) if err != nil { return nil, err } diff --git a/pkg/bindings/play/types.go b/pkg/bindings/play/types.go new file mode 100644 index 000000000..5fb9a4d41 --- /dev/null +++ b/pkg/bindings/play/types.go @@ -0,0 +1,32 @@ +package play + +//go:generate go run ../generator/generator.go KubeOptions +// KubeOptions are optional options for replaying kube YAML files +type KubeOptions struct { + // Authfile - path to an authentication file. + Authfile *string + // CertDir - to a directory containing TLS certifications and keys. + CertDir *string + // Username for authenticating against the registry. + Username *string + // Password for authenticating against the registry. + Password *string + // Network - name of the CNI network to connect to. + Network *string + // Quiet - suppress output when pulling images. + Quiet *bool + // SignaturePolicy - path to a signature-policy file. + SignaturePolicy *string + // SkipTLSVerify - skip https and certificate validation when + // contacting container registries. + SkipTLSVerify *bool + // SeccompProfileRoot - path to a directory containing seccomp + // profiles. + SeccompProfileRoot *string + // ConfigMaps - slice of pathnames to kubernetes configmap YAMLs. + ConfigMaps *[]string + // LogDriver for the container. For example: journald + LogDriver *string + // Start - don't start the pod if false + Start *bool +} diff --git a/pkg/bindings/play/types_kube_options.go b/pkg/bindings/play/types_kube_options.go new file mode 100644 index 000000000..91cdd30aa --- /dev/null +++ b/pkg/bindings/play/types_kube_options.go @@ -0,0 +1,280 @@ +package play + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *KubeOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *KubeOptions) 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 + } + 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 +} + +// WithAuthfile +func (o *KubeOptions) WithAuthfile(value string) *KubeOptions { + v := &value + o.Authfile = v + return o +} + +// GetAuthfile +func (o *KubeOptions) GetAuthfile() string { + var authfile string + if o.Authfile == nil { + return authfile + } + return *o.Authfile +} + +// WithCertDir +func (o *KubeOptions) WithCertDir(value string) *KubeOptions { + v := &value + o.CertDir = v + return o +} + +// GetCertDir +func (o *KubeOptions) GetCertDir() string { + var certDir string + if o.CertDir == nil { + return certDir + } + return *o.CertDir +} + +// WithUsername +func (o *KubeOptions) WithUsername(value string) *KubeOptions { + v := &value + o.Username = v + return o +} + +// GetUsername +func (o *KubeOptions) GetUsername() string { + var username string + if o.Username == nil { + return username + } + return *o.Username +} + +// WithPassword +func (o *KubeOptions) WithPassword(value string) *KubeOptions { + v := &value + o.Password = v + return o +} + +// GetPassword +func (o *KubeOptions) GetPassword() string { + var password string + if o.Password == nil { + return password + } + return *o.Password +} + +// WithNetwork +func (o *KubeOptions) WithNetwork(value string) *KubeOptions { + v := &value + o.Network = v + return o +} + +// GetNetwork +func (o *KubeOptions) GetNetwork() string { + var network string + if o.Network == nil { + return network + } + return *o.Network +} + +// WithQuiet +func (o *KubeOptions) WithQuiet(value bool) *KubeOptions { + v := &value + o.Quiet = v + return o +} + +// GetQuiet +func (o *KubeOptions) GetQuiet() bool { + var quiet bool + if o.Quiet == nil { + return quiet + } + return *o.Quiet +} + +// WithSignaturePolicy +func (o *KubeOptions) WithSignaturePolicy(value string) *KubeOptions { + v := &value + o.SignaturePolicy = v + return o +} + +// GetSignaturePolicy +func (o *KubeOptions) GetSignaturePolicy() string { + var signaturePolicy string + if o.SignaturePolicy == nil { + return signaturePolicy + } + return *o.SignaturePolicy +} + +// WithSkipTLSVerify +func (o *KubeOptions) WithSkipTLSVerify(value bool) *KubeOptions { + v := &value + o.SkipTLSVerify = v + return o +} + +// GetSkipTLSVerify +func (o *KubeOptions) GetSkipTLSVerify() bool { + var skipTLSVerify bool + if o.SkipTLSVerify == nil { + return skipTLSVerify + } + return *o.SkipTLSVerify +} + +// WithSeccompProfileRoot +func (o *KubeOptions) WithSeccompProfileRoot(value string) *KubeOptions { + v := &value + o.SeccompProfileRoot = v + return o +} + +// GetSeccompProfileRoot +func (o *KubeOptions) GetSeccompProfileRoot() string { + var seccompProfileRoot string + if o.SeccompProfileRoot == nil { + return seccompProfileRoot + } + return *o.SeccompProfileRoot +} + +// WithConfigMaps +func (o *KubeOptions) WithConfigMaps(value []string) *KubeOptions { + v := &value + o.ConfigMaps = v + return o +} + +// GetConfigMaps +func (o *KubeOptions) GetConfigMaps() []string { + var configMaps []string + if o.ConfigMaps == nil { + return configMaps + } + return *o.ConfigMaps +} + +// WithLogDriver +func (o *KubeOptions) WithLogDriver(value string) *KubeOptions { + v := &value + o.LogDriver = v + return o +} + +// GetLogDriver +func (o *KubeOptions) GetLogDriver() string { + var logDriver string + if o.LogDriver == nil { + return logDriver + } + return *o.LogDriver +} + +// WithStart +func (o *KubeOptions) WithStart(value bool) *KubeOptions { + v := &value + o.Start = v + return o +} + +// GetStart +func (o *KubeOptions) GetStart() bool { + var start bool + if o.Start == nil { + return start + } + return *o.Start +} diff --git a/pkg/bindings/pods/pods.go b/pkg/bindings/pods/pods.go index c3f98eaab..4fcaf81f1 100644 --- a/pkg/bindings/pods/pods.go +++ b/pkg/bindings/pods/pods.go @@ -2,10 +2,8 @@ package pods import ( "context" - "errors" "net/http" "net/url" - "strconv" "strings" "github.com/containers/podman/v2/pkg/api/handlers" @@ -15,10 +13,14 @@ import ( jsoniter "github.com/json-iterator/go" ) -func CreatePodFromSpec(ctx context.Context, s *specgen.PodSpecGenerator) (*entities.PodCreateReport, error) { +func CreatePodFromSpec(ctx context.Context, s *specgen.PodSpecGenerator, options *CreateOptions) (*entities.PodCreateReport, error) { var ( pcr entities.PodCreateReport ) + if options == nil { + options = new(CreateOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err @@ -49,10 +51,14 @@ func Exists(ctx context.Context, nameOrID string) (bool, error) { } // Inspect returns low-level information about the given pod. -func Inspect(ctx context.Context, nameOrID string) (*entities.PodInspectReport, error) { +func Inspect(ctx context.Context, nameOrID string, options *InspectOptions) (*entities.PodInspectReport, error) { var ( report entities.PodInspectReport ) + if options == nil { + options = new(InspectOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err @@ -66,17 +72,20 @@ func Inspect(ctx context.Context, nameOrID string) (*entities.PodInspectReport, // Kill sends a SIGTERM to all the containers in a pod. The optional signal parameter // can be used to override SIGTERM. -func Kill(ctx context.Context, nameOrID string, signal *string) (*entities.PodKillReport, error) { +func Kill(ctx context.Context, nameOrID string, options *KillOptions) (*entities.PodKillReport, error) { var ( report entities.PodKillReport ) + if options == nil { + options = new(KillOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if signal != nil { - params.Set("signal", *signal) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/kill", params, nil, nameOrID) if err != nil { @@ -86,8 +95,12 @@ func Kill(ctx context.Context, nameOrID string, signal *string) (*entities.PodKi } // Pause pauses all running containers in a given pod. -func Pause(ctx context.Context, nameOrID string) (*entities.PodPauseReport, error) { +func Pause(ctx context.Context, nameOrID string, options *PauseOptions) (*entities.PodPauseReport, error) { var report entities.PodPauseReport + if options == nil { + options = new(PauseOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err @@ -101,8 +114,12 @@ func Pause(ctx context.Context, nameOrID string) (*entities.PodPauseReport, erro // Prune by default removes all non-running pods in local storage. // And with force set true removes all pods. -func Prune(ctx context.Context) ([]*entities.PodPruneReport, error) { +func Prune(ctx context.Context, options *PruneOptions) ([]*entities.PodPruneReport, error) { var reports []*entities.PodPruneReport + if options == nil { + options = new(PruneOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err @@ -116,21 +133,20 @@ func Prune(ctx context.Context) ([]*entities.PodPruneReport, error) { // List returns all pods in local storage. The optional filters parameter can // be used to refine which pods should be listed. -func List(ctx context.Context, filters map[string][]string) ([]*entities.ListPodsReport, error) { +func List(ctx context.Context, options *ListOptions) ([]*entities.ListPodsReport, error) { var ( podsReports []*entities.ListPodsReport ) + if options == nil { + options = new(ListOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if filters != nil { - stringFilter, err := bindings.FiltersToString(filters) - if err != nil { - return nil, err - } - params.Set("filters", stringFilter) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodGet, "/pods/json", params, nil) if err != nil { @@ -140,8 +156,12 @@ func List(ctx context.Context, filters map[string][]string) ([]*entities.ListPod } // Restart restarts all containers in a pod. -func Restart(ctx context.Context, nameOrID string) (*entities.PodRestartReport, error) { +func Restart(ctx context.Context, nameOrID string, options *RestartOptions) (*entities.PodRestartReport, error) { var report entities.PodRestartReport + if options == nil { + options = new(RestartOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err @@ -155,15 +175,18 @@ func Restart(ctx context.Context, nameOrID string) (*entities.PodRestartReport, // Remove deletes a Pod from from local storage. The optional force parameter denotes // that the Pod can be removed even if in a running state. -func Remove(ctx context.Context, nameOrID string, force *bool) (*entities.PodRmReport, error) { +func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) (*entities.PodRmReport, error) { var report entities.PodRmReport + if options == nil { + options = new(RemoveOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if force != nil { - params.Set("force", strconv.FormatBool(*force)) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodDelete, "/pods/%s", params, nil, nameOrID) if err != nil { @@ -173,8 +196,12 @@ func Remove(ctx context.Context, nameOrID string, force *bool) (*entities.PodRmR } // Start starts all containers in a pod. -func Start(ctx context.Context, nameOrID string) (*entities.PodStartReport, error) { +func Start(ctx context.Context, nameOrID string, options *StartOptions) (*entities.PodStartReport, error) { var report entities.PodStartReport + if options == nil { + options = new(StartOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err @@ -192,15 +219,18 @@ func Start(ctx context.Context, nameOrID string) (*entities.PodStartReport, erro // Stop stops all containers in a Pod. The optional timeout parameter can be // used to override the timeout before the container is killed. -func Stop(ctx context.Context, nameOrID string, timeout *int) (*entities.PodStopReport, error) { +func Stop(ctx context.Context, nameOrID string, options *StopOptions) (*entities.PodStopReport, error) { var report entities.PodStopReport + if options == nil { + options = new(StopOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} - if timeout != nil { - params.Set("t", strconv.Itoa(*timeout)) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodPost, "/pods/%s/stop", params, nil, nameOrID) if err != nil { @@ -215,15 +245,16 @@ func Stop(ctx context.Context, nameOrID string, timeout *int) (*entities.PodStop // Top gathers statistics about the running processes in a pod. The nameOrID can be a pod name // or a partial/full ID. The descriptors allow for specifying which data to collect from each process. -func Top(ctx context.Context, nameOrID string, descriptors []string) ([]string, error) { +func Top(ctx context.Context, nameOrID string, options *TopOptions) ([]string, error) { + if options == nil { + options = new(TopOptions) + } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } params := url.Values{} - - if len(descriptors) > 0 { - // flatten the slice into one string + if descriptors := options.GetDescriptors(); len(descriptors) > 0 { params.Set("ps_args", strings.Join(descriptors, ",")) } response, err := conn.DoRequest(nil, http.MethodGet, "/pods/%s/top", params, nil, nameOrID) @@ -248,7 +279,11 @@ func Top(ctx context.Context, nameOrID string, descriptors []string) ([]string, } // Unpause unpauses all paused containers in a Pod. -func Unpause(ctx context.Context, nameOrID string) (*entities.PodUnpauseReport, error) { +func Unpause(ctx context.Context, nameOrID string, options *UnpauseOptions) (*entities.PodUnpauseReport, error) { + if options == nil { + options = new(UnpauseOptions) + } + _ = options var report entities.PodUnpauseReport conn, err := bindings.GetClient(ctx) if err != nil { @@ -262,19 +297,21 @@ func Unpause(ctx context.Context, nameOrID string) (*entities.PodUnpauseReport, } // Stats display resource-usage statistics of one or more pods. -func Stats(ctx context.Context, namesOrIDs []string, options entities.PodStatsOptions) ([]*entities.PodStatsReport, error) { - if options.Latest { - return nil, errors.New("latest is not supported") +func Stats(ctx context.Context, namesOrIDs []string, options *StatsOptions) ([]*entities.PodStatsReport, error) { + if options == nil { + options = new(StatsOptions) } conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} + params, err := options.ToParams() + if err != nil { + return nil, err + } for _, i := range namesOrIDs { params.Add("namesOrIDs", i) } - params.Set("all", strconv.FormatBool(options.All)) var reports []*entities.PodStatsReport response, err := conn.DoRequest(nil, http.MethodGet, "/pods/stats", params, nil) diff --git a/pkg/bindings/pods/types.go b/pkg/bindings/pods/types.go new file mode 100644 index 000000000..370c1aaa9 --- /dev/null +++ b/pkg/bindings/pods/types.go @@ -0,0 +1,72 @@ +package pods + +//go:generate go run ../generator/generator.go CreateOptions +// CreateOptions are optional options for creating pods +type CreateOptions struct { +} + +//go:generate go run ../generator/generator.go InspectOptions +// InspectOptions are optional options for inspecting pods +type InspectOptions struct { +} + +//go:generate go run ../generator/generator.go KillOptions +// KillOptions are optional options for killing pods +type KillOptions struct { + Signal *string +} + +//go:generate go run ../generator/generator.go PauseOptions +// PauseOptions are optional options for pausing pods +type PauseOptions struct { +} + +//go:generate go run ../generator/generator.go PruneOptions +// PruneOptions are optional options for pruning pods +type PruneOptions struct { +} + +//go:generate go run ../generator/generator.go ListOptions +// ListOptions are optional options for listing pods +type ListOptions struct { + Filters map[string][]string +} + +//go:generate go run ../generator/generator.go RestartOptions +// RestartOptions are optional options for restarting pods +type RestartOptions struct { +} + +//go:generate go run ../generator/generator.go StartOptions +// StartOptions are optional options for starting pods +type StartOptions struct { +} + +//go:generate go run ../generator/generator.go StopOptions +// StopOptions are optional options for stopping pods +type StopOptions struct { + Timeout *int +} + +//go:generate go run ../generator/generator.go TopOptions +// TopOptions are optional options for getting top on pods +type TopOptions struct { + Descriptors []string +} + +//go:generate go run ../generator/generator.go UnpauseOptions +// UnpauseOptions are optional options for unpausinging pods +type UnpauseOptions struct { +} + +//go:generate go run ../generator/generator.go StatsOptions +// StatsOptions are optional options for getting stats of pods +type StatsOptions struct { + All *bool +} + +//go:generate go run ../generator/generator.go RemoveOptions +// RemoveOptions are optional options for removing pods +type RemoveOptions struct { + Force *bool +} diff --git a/pkg/bindings/pods/types_create_options.go b/pkg/bindings/pods/types_create_options.go new file mode 100644 index 000000000..b6cf0fc53 --- /dev/null +++ b/pkg/bindings/pods/types_create_options.go @@ -0,0 +1,88 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *CreateOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *CreateOptions) 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 + } + 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 +} diff --git a/pkg/bindings/pods/types_inspect_options.go b/pkg/bindings/pods/types_inspect_options.go new file mode 100644 index 000000000..1c881ce9c --- /dev/null +++ b/pkg/bindings/pods/types_inspect_options.go @@ -0,0 +1,88 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *InspectOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *InspectOptions) 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 + } + 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 +} diff --git a/pkg/bindings/pods/types_kill_options.go b/pkg/bindings/pods/types_kill_options.go new file mode 100644 index 000000000..cb5bdfd01 --- /dev/null +++ b/pkg/bindings/pods/types_kill_options.go @@ -0,0 +1,104 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *KillOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *KillOptions) 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 + } + 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 +} + +// WithSignal +func (o *KillOptions) WithSignal(value string) *KillOptions { + v := &value + o.Signal = v + return o +} + +// GetSignal +func (o *KillOptions) GetSignal() string { + var signal string + if o.Signal == nil { + return signal + } + return *o.Signal +} diff --git a/pkg/bindings/pods/types_list_options.go b/pkg/bindings/pods/types_list_options.go new file mode 100644 index 000000000..d095bf3db --- /dev/null +++ b/pkg/bindings/pods/types_list_options.go @@ -0,0 +1,104 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *ListOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ListOptions) 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 + } + 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 +} + +// WithFilters +func (o *ListOptions) WithFilters(value map[string][]string) *ListOptions { + v := value + o.Filters = v + return o +} + +// GetFilters +func (o *ListOptions) GetFilters() map[string][]string { + var filters map[string][]string + if o.Filters == nil { + return filters + } + return o.Filters +} diff --git a/pkg/bindings/pods/types_pause_options.go b/pkg/bindings/pods/types_pause_options.go new file mode 100644 index 000000000..06ee6f81d --- /dev/null +++ b/pkg/bindings/pods/types_pause_options.go @@ -0,0 +1,88 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *PauseOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *PauseOptions) 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 + } + 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 +} diff --git a/pkg/bindings/pods/types_prune_options.go b/pkg/bindings/pods/types_prune_options.go new file mode 100644 index 000000000..6610aa7cc --- /dev/null +++ b/pkg/bindings/pods/types_prune_options.go @@ -0,0 +1,88 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *PruneOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *PruneOptions) 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 + } + 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 +} diff --git a/pkg/bindings/pods/types_remove_options.go b/pkg/bindings/pods/types_remove_options.go new file mode 100644 index 000000000..8f18e43c9 --- /dev/null +++ b/pkg/bindings/pods/types_remove_options.go @@ -0,0 +1,104 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *RemoveOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *RemoveOptions) 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 + } + 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 +} + +// WithForce +func (o *RemoveOptions) WithForce(value bool) *RemoveOptions { + v := &value + o.Force = v + return o +} + +// GetForce +func (o *RemoveOptions) GetForce() bool { + var force bool + if o.Force == nil { + return force + } + return *o.Force +} diff --git a/pkg/bindings/pods/types_restart_options.go b/pkg/bindings/pods/types_restart_options.go new file mode 100644 index 000000000..9030de1e7 --- /dev/null +++ b/pkg/bindings/pods/types_restart_options.go @@ -0,0 +1,88 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *RestartOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *RestartOptions) 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 + } + 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 +} diff --git a/pkg/bindings/pods/types_start_options.go b/pkg/bindings/pods/types_start_options.go new file mode 100644 index 000000000..0fce21099 --- /dev/null +++ b/pkg/bindings/pods/types_start_options.go @@ -0,0 +1,88 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *StartOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *StartOptions) 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 + } + 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 +} diff --git a/pkg/bindings/pods/types_stats_options.go b/pkg/bindings/pods/types_stats_options.go new file mode 100644 index 000000000..d38a9a115 --- /dev/null +++ b/pkg/bindings/pods/types_stats_options.go @@ -0,0 +1,104 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *StatsOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *StatsOptions) 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 + } + 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 +} + +// WithAll +func (o *StatsOptions) WithAll(value bool) *StatsOptions { + v := &value + o.All = v + return o +} + +// GetAll +func (o *StatsOptions) GetAll() bool { + var all bool + if o.All == nil { + return all + } + return *o.All +} diff --git a/pkg/bindings/pods/types_stop_options.go b/pkg/bindings/pods/types_stop_options.go new file mode 100644 index 000000000..ac698b8c5 --- /dev/null +++ b/pkg/bindings/pods/types_stop_options.go @@ -0,0 +1,104 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *StopOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *StopOptions) 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 + } + 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 +} + +// WithTimeout +func (o *StopOptions) WithTimeout(value int) *StopOptions { + v := &value + o.Timeout = v + return o +} + +// GetTimeout +func (o *StopOptions) GetTimeout() int { + var timeout int + if o.Timeout == nil { + return timeout + } + return *o.Timeout +} diff --git a/pkg/bindings/pods/types_top_options.go b/pkg/bindings/pods/types_top_options.go new file mode 100644 index 000000000..895f62957 --- /dev/null +++ b/pkg/bindings/pods/types_top_options.go @@ -0,0 +1,104 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *TopOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *TopOptions) 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 + } + 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 +} + +// WithDescriptors +func (o *TopOptions) WithDescriptors(value []string) *TopOptions { + v := value + o.Descriptors = v + return o +} + +// GetDescriptors +func (o *TopOptions) GetDescriptors() []string { + var descriptors []string + if o.Descriptors == nil { + return descriptors + } + return o.Descriptors +} diff --git a/pkg/bindings/pods/types_unpause_options.go b/pkg/bindings/pods/types_unpause_options.go new file mode 100644 index 000000000..3d647cf25 --- /dev/null +++ b/pkg/bindings/pods/types_unpause_options.go @@ -0,0 +1,88 @@ +package pods + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *UnpauseOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *UnpauseOptions) 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 + } + 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 +} diff --git a/pkg/bindings/system/info.go b/pkg/bindings/system/info.go index 79ee5cada..798010333 100644 --- a/pkg/bindings/system/info.go +++ b/pkg/bindings/system/info.go @@ -9,7 +9,11 @@ import ( ) // Info returns information about the libpod environment and its stores -func Info(ctx context.Context) (*define.Info, error) { +func Info(ctx context.Context, options *InfoOptions) (*define.Info, error) { + if options == nil { + options = new(InfoOptions) + } + _ = options info := define.Info{} conn, err := bindings.GetClient(ctx) if err != nil { diff --git a/pkg/bindings/system/system.go b/pkg/bindings/system/system.go index 1203f5c3c..815c967f1 100644 --- a/pkg/bindings/system/system.go +++ b/pkg/bindings/system/system.go @@ -6,8 +6,6 @@ import ( "fmt" "io" "net/http" - "net/url" - "strconv" "time" "github.com/containers/podman/v2/libpod/define" @@ -20,27 +18,14 @@ import ( // Events allows you to monitor libdpod related events like container creation and // removal. The events are then passed to the eventChan provided. The optional cancelChan // can be used to cancel the read of events and close down the HTTP connection. -func Events(ctx context.Context, eventChan chan entities.Event, cancelChan chan bool, since, until *string, filters map[string][]string, stream *bool) error { +func Events(ctx context.Context, eventChan chan entities.Event, cancelChan chan bool, options *EventsOptions) error { conn, err := bindings.GetClient(ctx) if err != nil { return err } - params := url.Values{} - if since != nil { - params.Set("since", *since) - } - if until != nil { - params.Set("until", *until) - } - if stream != nil { - params.Set("stream", strconv.FormatBool(*stream)) - } - if filters != nil { - filterString, err := bindings.FiltersToString(filters) - if err != nil { - return errors.Wrap(err, "invalid filters") - } - params.Set("filters", filterString) + params, err := options.ToParams() + if err != nil { + return err } response, err := conn.DoRequest(nil, http.MethodGet, "/events", params, nil) if err != nil { @@ -74,7 +59,7 @@ func Events(ctx context.Context, eventChan chan entities.Event, cancelChan chan } // Prune removes all unused system data. -func Prune(ctx context.Context, all, volumes *bool) (*entities.SystemPruneReport, error) { +func Prune(ctx context.Context, options *PruneOptions) (*entities.SystemPruneReport, error) { var ( report entities.SystemPruneReport ) @@ -82,12 +67,9 @@ func Prune(ctx context.Context, all, volumes *bool) (*entities.SystemPruneReport if err != nil { return nil, err } - params := url.Values{} - if all != nil { - params.Set("All", strconv.FormatBool(*all)) - } - if volumes != nil { - params.Set("Volumes", strconv.FormatBool(*volumes)) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodPost, "/system/prune", params, nil) if err != nil { @@ -96,10 +78,15 @@ func Prune(ctx context.Context, all, volumes *bool) (*entities.SystemPruneReport return &report, response.Process(&report) } -func Version(ctx context.Context) (*entities.SystemVersionReport, error) { - var report entities.SystemVersionReport - var component entities.ComponentVersion - +func Version(ctx context.Context, options *VersionOptions) (*entities.SystemVersionReport, error) { + var ( + component entities.ComponentVersion + report entities.SystemVersionReport + ) + if options == nil { + options = new(VersionOptions) + } + _ = options version, err := define.GetVersion() if err != nil { return nil, err @@ -140,8 +127,12 @@ func Version(ctx context.Context) (*entities.SystemVersionReport, error) { // DiskUsage returns information about image, container, and volume disk // consumption -func DiskUsage(ctx context.Context) (*entities.SystemDfReport, error) { +func DiskUsage(ctx context.Context, options *DiskOptions) (*entities.SystemDfReport, error) { var report entities.SystemDfReport + if options == nil { + options = new(DiskOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err diff --git a/pkg/bindings/system/types.go b/pkg/bindings/system/types.go new file mode 100644 index 000000000..16960b817 --- /dev/null +++ b/pkg/bindings/system/types.go @@ -0,0 +1,34 @@ +package system + +//go:generate go run ../generator/generator.go EventsOptions +// EventsOptions are optional options for monitoring events +type EventsOptions struct { + Filters map[string][]string + Since *string + Stream *bool + Until *string +} + +//go:generate go run ../generator/generator.go PruneOptions +// PruneOptions are optional options for pruning +type PruneOptions struct { + All *bool + Filters map[string][]string + Volumes *bool +} + +//go:generate go run ../generator/generator.go VersionOptions +// VersionOptions are optional options for getting version info +type VersionOptions struct { +} + +//go:generate go run ../generator/generator.go DiskOptions +// DiskOptions are optional options for getting storage consumption +type DiskOptions struct { +} + +//go:generate go run ../generator/generator.go InfoOptions +// InfoOptions are optional options for getting info +// about libpod +type InfoOptions struct { +} diff --git a/pkg/bindings/system/types_disk_options.go b/pkg/bindings/system/types_disk_options.go new file mode 100644 index 000000000..f7d2cca06 --- /dev/null +++ b/pkg/bindings/system/types_disk_options.go @@ -0,0 +1,88 @@ +package system + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *DiskOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *DiskOptions) 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 + } + 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 +} diff --git a/pkg/bindings/system/types_events_options.go b/pkg/bindings/system/types_events_options.go new file mode 100644 index 000000000..6dd64b055 --- /dev/null +++ b/pkg/bindings/system/types_events_options.go @@ -0,0 +1,152 @@ +package system + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *EventsOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *EventsOptions) 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 + } + 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 +} + +// WithFilters +func (o *EventsOptions) WithFilters(value map[string][]string) *EventsOptions { + v := value + o.Filters = v + return o +} + +// GetFilters +func (o *EventsOptions) GetFilters() map[string][]string { + var filters map[string][]string + if o.Filters == nil { + return filters + } + return o.Filters +} + +// WithSince +func (o *EventsOptions) WithSince(value string) *EventsOptions { + v := &value + o.Since = v + return o +} + +// GetSince +func (o *EventsOptions) GetSince() string { + var since string + if o.Since == nil { + return since + } + return *o.Since +} + +// WithStream +func (o *EventsOptions) WithStream(value bool) *EventsOptions { + v := &value + o.Stream = v + return o +} + +// GetStream +func (o *EventsOptions) GetStream() bool { + var stream bool + if o.Stream == nil { + return stream + } + return *o.Stream +} + +// WithUntil +func (o *EventsOptions) WithUntil(value string) *EventsOptions { + v := &value + o.Until = v + return o +} + +// GetUntil +func (o *EventsOptions) GetUntil() string { + var until string + if o.Until == nil { + return until + } + return *o.Until +} diff --git a/pkg/bindings/system/types_info_options.go b/pkg/bindings/system/types_info_options.go new file mode 100644 index 000000000..3a8960e1b --- /dev/null +++ b/pkg/bindings/system/types_info_options.go @@ -0,0 +1,88 @@ +package system + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *InfoOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *InfoOptions) 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 + } + 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 +} diff --git a/pkg/bindings/system/types_prune_options.go b/pkg/bindings/system/types_prune_options.go new file mode 100644 index 000000000..2cd3c8000 --- /dev/null +++ b/pkg/bindings/system/types_prune_options.go @@ -0,0 +1,136 @@ +package system + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *PruneOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *PruneOptions) 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 + } + 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 +} + +// WithAll +func (o *PruneOptions) WithAll(value bool) *PruneOptions { + v := &value + o.All = v + return o +} + +// GetAll +func (o *PruneOptions) GetAll() bool { + var all bool + if o.All == nil { + return all + } + return *o.All +} + +// WithFilters +func (o *PruneOptions) WithFilters(value map[string][]string) *PruneOptions { + v := value + o.Filters = v + return o +} + +// GetFilters +func (o *PruneOptions) GetFilters() map[string][]string { + var filters map[string][]string + if o.Filters == nil { + return filters + } + return o.Filters +} + +// WithVolumes +func (o *PruneOptions) WithVolumes(value bool) *PruneOptions { + v := &value + o.Volumes = v + return o +} + +// GetVolumes +func (o *PruneOptions) GetVolumes() bool { + var volumes bool + if o.Volumes == nil { + return volumes + } + return *o.Volumes +} diff --git a/pkg/bindings/system/types_version_options.go b/pkg/bindings/system/types_version_options.go new file mode 100644 index 000000000..4974e8d8f --- /dev/null +++ b/pkg/bindings/system/types_version_options.go @@ -0,0 +1,88 @@ +package system + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *VersionOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *VersionOptions) 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 + } + 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 +} diff --git a/pkg/bindings/test/attach_test.go b/pkg/bindings/test/attach_test.go index 12e51e734..9a46f6309 100644 --- a/pkg/bindings/test/attach_test.go +++ b/pkg/bindings/test/attach_test.go @@ -6,7 +6,6 @@ import ( "time" "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/specgen" . "github.com/onsi/ginkgo" @@ -43,7 +42,7 @@ var _ = Describe("Podman containers attach", func() { go func() { <-tickTock.C timeout := uint(5) - err := containers.Stop(bt.conn, id, &timeout) + err := containers.Stop(bt.conn, id, new(containers.StopOptions).WithTimeout(timeout)) if err != nil { GinkgoWriter.Write([]byte(err.Error())) } @@ -53,8 +52,8 @@ var _ = Describe("Podman containers attach", func() { stderr := &bytes.Buffer{} go func() { defer GinkgoRecover() - - err := containers.Attach(bt.conn, id, nil, bindings.PTrue, bindings.PTrue, nil, stdout, stderr, nil) + options := new(containers.AttachOptions).WithLogs(true).WithStream(true) + err := containers.Attach(bt.conn, id, nil, stdout, stderr, nil, options) Expect(err).ShouldNot(HaveOccurred()) }() @@ -69,21 +68,21 @@ var _ = Describe("Podman containers attach", func() { s.Name = "CatAttachTest" s.Terminal = true s.Command = []string{"/bin/cat"} - ctnr, err := containers.CreateWithSpec(bt.conn, s) + ctnr, err := containers.CreateWithSpec(bt.conn, s, nil) Expect(err).ShouldNot(HaveOccurred()) err = containers.Start(bt.conn, ctnr.ID, nil) Expect(err).ShouldNot(HaveOccurred()) wait := define.ContainerStateRunning - _, err = containers.Wait(bt.conn, ctnr.ID, &wait) + _, err = containers.Wait(bt.conn, ctnr.ID, new(containers.WaitOptions).WithCondition(wait)) Expect(err).ShouldNot(HaveOccurred()) tickTock := time.NewTimer(2 * time.Second) go func() { <-tickTock.C timeout := uint(5) - err := containers.Stop(bt.conn, ctnr.ID, &timeout) + err := containers.Stop(bt.conn, ctnr.ID, new(containers.StopOptions).WithTimeout(timeout)) if err != nil { GinkgoWriter.Write([]byte(err.Error())) } @@ -97,8 +96,8 @@ var _ = Describe("Podman containers attach", func() { stderr := &bytes.Buffer{} go func() { defer GinkgoRecover() - - err := containers.Attach(bt.conn, ctnr.ID, nil, bindings.PFalse, bindings.PTrue, stdin, stdout, stderr, nil) + options := new(containers.AttachOptions).WithStream(true) + err := containers.Attach(bt.conn, ctnr.ID, stdin, stdout, stderr, nil, options) Expect(err).ShouldNot(HaveOccurred()) }() diff --git a/pkg/bindings/test/auth_test.go b/pkg/bindings/test/auth_test.go index 4565b82b0..e647b3c36 100644 --- a/pkg/bindings/test/auth_test.go +++ b/pkg/bindings/test/auth_test.go @@ -9,7 +9,6 @@ import ( "github.com/containers/image/v5/types" podmanRegistry "github.com/containers/podman/v2/hack/podman-registry-go" "github.com/containers/podman/v2/pkg/bindings/images" - "github.com/containers/podman/v2/pkg/domain/entities" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/onsi/gomega/gexec" @@ -52,27 +51,19 @@ var _ = Describe("Podman images", func() { imageRef := imageRep + ":" + imageTag // Tag the alpine image and verify it has worked. - err = images.Tag(bt.conn, alpine.shortName, imageTag, imageRep) + err = images.Tag(bt.conn, alpine.shortName, imageTag, imageRep, nil) Expect(err).To(BeNil()) _, err = images.GetImage(bt.conn, imageRef, nil) Expect(err).To(BeNil()) // Now push the image. - pushOpts := entities.ImagePushOptions{ - Username: registry.User, - Password: registry.Password, - SkipTLSVerify: types.OptionalBoolTrue, - } - err = images.Push(bt.conn, imageRef, imageRef, pushOpts) + pushOpts := new(images.PushOptions) + err = images.Push(bt.conn, imageRef, imageRef, pushOpts.WithUsername(registry.User).WithPassword(registry.Password).WithSkipTLSVerify(true)) Expect(err).To(BeNil()) // Now pull the image. - pullOpts := entities.ImagePullOptions{ - Username: registry.User, - Password: registry.Password, - SkipTLSVerify: types.OptionalBoolTrue, - } - _, err = images.Pull(bt.conn, imageRef, pullOpts) + pullOpts := new(images.PullOptions) + _, err = images.Pull(bt.conn, imageRef, pullOpts.WithSkipTLSVerify(true).WithPassword(registry.Password).WithUsername(registry.User)) Expect(err).To(BeNil()) }) @@ -110,33 +101,24 @@ var _ = Describe("Podman images", func() { Expect(err).To(BeNil()) // Tag the alpine image and verify it has worked. - err = images.Tag(bt.conn, alpine.shortName, imageTag, imageRep) + err = images.Tag(bt.conn, alpine.shortName, imageTag, imageRep, nil) Expect(err).To(BeNil()) _, err = images.GetImage(bt.conn, imageRef, nil) Expect(err).To(BeNil()) // Now push the image. - pushOpts := entities.ImagePushOptions{ - Authfile: authFilePath, - SkipTLSVerify: types.OptionalBoolTrue, - } - err = images.Push(bt.conn, imageRef, imageRef, pushOpts) + pushOpts := new(images.PushOptions) + err = images.Push(bt.conn, imageRef, imageRef, pushOpts.WithAuthfile(authFilePath).WithSkipTLSVerify(true)) Expect(err).To(BeNil()) // Now pull the image. - pullOpts := entities.ImagePullOptions{ - Authfile: authFilePath, - SkipTLSVerify: types.OptionalBoolTrue, - } - _, err = images.Pull(bt.conn, imageRef, pullOpts) + pullOpts := new(images.PullOptions) + _, err = images.Pull(bt.conn, imageRef, pullOpts.WithAuthfile(authFilePath).WithSkipTLSVerify(true)) Expect(err).To(BeNil()) // Last, but not least, exercise search. - searchOptions := entities.ImageSearchOptions{ - Authfile: authFilePath, - SkipTLSVerify: types.OptionalBoolTrue, - } - _, err = images.Search(bt.conn, imageRef, searchOptions) + searchOptions := new(images.SearchOptions) + _, err = images.Search(bt.conn, imageRef, searchOptions.WithSkipTLSVerify(true).WithAuthfile(authFilePath)) Expect(err).To(BeNil()) }) diff --git a/pkg/bindings/test/common_test.go b/pkg/bindings/test/common_test.go index 9dff2985f..232d7136f 100644 --- a/pkg/bindings/test/common_test.go +++ b/pkg/bindings/test/common_test.go @@ -198,7 +198,7 @@ func (b *bindingTest) RunTopContainer(containerName *string, insidePod *bool, po if insidePod != nil && podName != nil { s.Pod = *podName } - ctr, err := containers.CreateWithSpec(b.conn, s) + ctr, err := containers.CreateWithSpec(b.conn, s, nil) if err != nil { return "", nil } @@ -207,7 +207,7 @@ func (b *bindingTest) RunTopContainer(containerName *string, insidePod *bool, po return "", err } wait := define.ContainerStateRunning - _, err = containers.Wait(b.conn, ctr.ID, &wait) + _, err = containers.Wait(b.conn, ctr.ID, new(containers.WaitOptions).WithCondition(wait)) return ctr.ID, err } diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go index 15066ff1a..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" @@ -37,7 +38,7 @@ var _ = Describe("Podman containers ", func() { It("podman pause a bogus container", func() { // Pausing bogus container should return 404 - err = containers.Pause(bt.conn, "foobar") + err = containers.Pause(bt.conn, "foobar", nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) @@ -45,7 +46,7 @@ var _ = Describe("Podman containers ", func() { It("podman unpause a bogus container", func() { // Unpausing bogus container should return 404 - err = containers.Unpause(bt.conn, "foobar") + err = containers.Unpause(bt.conn, "foobar", nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) @@ -56,7 +57,7 @@ var _ = Describe("Podman containers ", func() { var name = "top" _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, name) + err = containers.Pause(bt.conn, name, nil) Expect(err).To(BeNil()) // Ensure container is paused @@ -70,7 +71,7 @@ var _ = Describe("Podman containers ", func() { var name = "top" cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, cid) + err = containers.Pause(bt.conn, cid, nil) Expect(err).To(BeNil()) // Ensure container is paused @@ -84,9 +85,9 @@ var _ = Describe("Podman containers ", func() { var name = "top" _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, name) + err = containers.Pause(bt.conn, name, nil) Expect(err).To(BeNil()) - err = containers.Unpause(bt.conn, name) + err = containers.Unpause(bt.conn, name, nil) Expect(err).To(BeNil()) // Ensure container is unpaused @@ -101,11 +102,11 @@ var _ = Describe("Podman containers ", func() { _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) // Pause by name - err = containers.Pause(bt.conn, name) + err = containers.Pause(bt.conn, name, nil) //paused := "paused" //_, err = containers.Wait(bt.conn, cid, &paused) //Expect(err).To(BeNil()) - err = containers.Unpause(bt.conn, name) + err = containers.Unpause(bt.conn, name, nil) Expect(err).To(BeNil()) // Ensure container is unpaused @@ -119,9 +120,9 @@ var _ = Describe("Podman containers ", func() { var name = "top" _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, name) + err = containers.Pause(bt.conn, name, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, name) + err = containers.Pause(bt.conn, name, nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -132,9 +133,9 @@ var _ = Describe("Podman containers ", func() { var name = "top" cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, cid) + err = containers.Pause(bt.conn, cid, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, cid) + err = containers.Pause(bt.conn, cid, nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -147,7 +148,7 @@ var _ = Describe("Podman containers ", func() { Expect(err).To(BeNil()) err = containers.Stop(bt.conn, name, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, name) + err = containers.Pause(bt.conn, name, nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -160,7 +161,7 @@ var _ = Describe("Podman containers ", func() { Expect(err).To(BeNil()) err = containers.Stop(bt.conn, cid, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, cid) + err = containers.Pause(bt.conn, cid, nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -171,9 +172,9 @@ var _ = Describe("Podman containers ", func() { var name = "top" cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, cid) + err = containers.Pause(bt.conn, cid, nil) Expect(err).To(BeNil()) - err = containers.Remove(bt.conn, cid, bindings.PFalse, bindings.PFalse) + err = containers.Remove(bt.conn, cid, nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -184,9 +185,9 @@ var _ = Describe("Podman containers ", func() { var name = "top" cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, cid) + err = containers.Pause(bt.conn, cid, nil) Expect(err).To(BeNil()) - err = containers.Remove(bt.conn, cid, bindings.PTrue, bindings.PFalse) + err = containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithForce(true)) Expect(err).To(BeNil()) }) @@ -195,7 +196,7 @@ var _ = Describe("Podman containers ", func() { var name = "top" _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, name) + err = containers.Pause(bt.conn, name, nil) Expect(err).To(BeNil()) err = containers.Stop(bt.conn, name, nil) Expect(err).ToNot(BeNil()) @@ -208,7 +209,7 @@ var _ = Describe("Podman containers ", func() { var name = "top" cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, cid) + err = containers.Pause(bt.conn, cid, nil) Expect(err).To(BeNil()) err = containers.Stop(bt.conn, cid, nil) Expect(err).ToNot(BeNil()) @@ -280,11 +281,11 @@ var _ = Describe("Podman containers ", func() { _, err := bt.RunTopContainer(&name, nil, nil) Expect(err).To(BeNil()) go func() { - exitCode, err = containers.Wait(bt.conn, name, &pause) + exitCode, err = containers.Wait(bt.conn, name, new(containers.WaitOptions).WithCondition(pause)) errChan <- err close(errChan) }() - err = containers.Pause(bt.conn, name) + err = containers.Pause(bt.conn, name, nil) Expect(err).To(BeNil()) wait := <-errChan Expect(wait).To(BeNil()) @@ -294,11 +295,11 @@ var _ = Describe("Podman containers ", func() { go func() { defer GinkgoRecover() - _, waitErr := containers.Wait(bt.conn, name, &running) + _, waitErr := containers.Wait(bt.conn, name, new(containers.WaitOptions).WithCondition(running)) unpauseErrChan <- waitErr close(unpauseErrChan) }() - err = containers.Unpause(bt.conn, name) + err = containers.Unpause(bt.conn, name, nil) Expect(err).To(BeNil()) unPausewait := <-unpauseErrChan Expect(unPausewait).To(BeNil()) @@ -309,7 +310,7 @@ var _ = Describe("Podman containers ", func() { bt.runPodman([]string{"run", "-d", "--name", "hc", "--health-interval", "disable", "--health-retries", "2", "--health-cmd", "ls / || exit 1", alpine.name, "top"}) // bogus name should result in 404 - _, err := containers.RunHealthCheck(bt.conn, "foobar") + _, err := containers.RunHealthCheck(bt.conn, "foobar", nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) @@ -317,7 +318,7 @@ var _ = Describe("Podman containers ", func() { // a container that has no healthcheck should be a 409 var name = "top" bt.RunTopContainer(&name, bindings.PFalse, nil) - _, err = containers.RunHealthCheck(bt.conn, name) + _, err = containers.RunHealthCheck(bt.conn, name, nil) Expect(err).ToNot(BeNil()) code, _ = bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusConflict)) @@ -355,7 +356,7 @@ var _ = Describe("Podman containers ", func() { s := specgen.NewSpecGenerator(alpine.name, false) s.Terminal = true s.Command = []string{"date", "-R"} - r, err := containers.CreateWithSpec(bt.conn, s) + r, err := containers.CreateWithSpec(bt.conn, s, nil) Expect(err).To(BeNil()) err = containers.Start(bt.conn, r.ID, nil) Expect(err).To(BeNil()) @@ -363,7 +364,7 @@ var _ = Describe("Podman containers ", func() { _, err = containers.Wait(bt.conn, r.ID, nil) Expect(err).To(BeNil()) - opts := containers.LogOptions{Stdout: bindings.PTrue, Follow: bindings.PTrue} + opts := new(containers.LogOptions).WithStdout(true).WithFollow(true) go func() { containers.Logs(bt.conn, r.ID, opts, stdoutChan, nil) }() @@ -387,7 +388,7 @@ var _ = Describe("Podman containers ", func() { Expect(err).To(BeNil()) // With descriptors - output, err := containers.Top(bt.conn, cid, []string{"user,pid,hpid"}) + output, err := containers.Top(bt.conn, cid, new(containers.TopOptions).WithDescriptors([]string{"user", "pid", "hpid"})) Expect(err).To(BeNil()) header := strings.Split(output[0], "\t") for _, d := range []string{"USER", "PID", "HPID"} { @@ -399,7 +400,7 @@ var _ = Describe("Podman containers ", func() { Expect(err).ToNot(BeNil()) // With bogus descriptors - _, err = containers.Top(bt.conn, cid, []string{"Me,Neither"}) + _, err = containers.Top(bt.conn, cid, new(containers.TopOptions).WithDescriptors([]string{"Me,Neither"})) Expect(err).To(BeNil()) }) @@ -442,7 +443,7 @@ var _ = Describe("Podman containers ", func() { It("podman kill bogus container", func() { // Killing bogus container should return 404 - err := containers.Kill(bt.conn, "foobar", "SIGTERM") + err := containers.Kill(bt.conn, "foobar", "SIGTERM", nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) @@ -453,7 +454,7 @@ var _ = Describe("Podman containers ", func() { var name = "top" _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Kill(bt.conn, name, "SIGINT") + err = containers.Kill(bt.conn, name, "SIGINT", nil) Expect(err).To(BeNil()) _, err = containers.Exists(bt.conn, name, false) Expect(err).To(BeNil()) @@ -464,7 +465,7 @@ var _ = Describe("Podman containers ", func() { var name = "top" cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Kill(bt.conn, cid, "SIGTERM") + err = containers.Kill(bt.conn, cid, "SIGTERM", nil) Expect(err).To(BeNil()) _, err = containers.Exists(bt.conn, cid, false) Expect(err).To(BeNil()) @@ -475,7 +476,7 @@ var _ = Describe("Podman containers ", func() { var name = "top" cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Kill(bt.conn, cid, "SIGKILL") + err = containers.Kill(bt.conn, cid, "SIGKILL", nil) Expect(err).To(BeNil()) }) @@ -484,7 +485,7 @@ var _ = Describe("Podman containers ", func() { var name = "top" cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - err = containers.Kill(bt.conn, cid, "foobar") + err = containers.Kill(bt.conn, cid, "foobar", nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -494,19 +495,18 @@ var _ = Describe("Podman containers ", func() { // Killing latest container should work var name1 = "first" var name2 = "second" - var latestContainers = 1 _, err := bt.RunTopContainer(&name1, bindings.PFalse, nil) Expect(err).To(BeNil()) _, err = bt.RunTopContainer(&name2, bindings.PFalse, nil) Expect(err).To(BeNil()) - containerLatestList, err := containers.List(bt.conn, nil, nil, &latestContainers, nil, nil, nil) + containerLatestList, err := containers.List(bt.conn, new(containers.ListOptions).WithLast(1)) Expect(err).To(BeNil()) - err = containers.Kill(bt.conn, containerLatestList[0].Names[0], "SIGTERM") + err = containers.Kill(bt.conn, containerLatestList[0].Names[0], "SIGTERM", nil) Expect(err).To(BeNil()) }) It("container init on a bogus container", func() { - err := containers.ContainerInit(bt.conn, "doesnotexist") + err := containers.ContainerInit(bt.conn, "doesnotexist", nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) @@ -514,12 +514,12 @@ var _ = Describe("Podman containers ", func() { It("container init", func() { s := specgen.NewSpecGenerator(alpine.name, false) - ctr, err := containers.CreateWithSpec(bt.conn, s) + ctr, err := containers.CreateWithSpec(bt.conn, s, nil) Expect(err).To(BeNil()) - err = containers.ContainerInit(bt.conn, ctr.ID) + err = containers.ContainerInit(bt.conn, ctr.ID, nil) Expect(err).To(BeNil()) // trying to init again should be an error - err = containers.ContainerInit(bt.conn, ctr.ID) + err = containers.ContainerInit(bt.conn, ctr.ID, nil) Expect(err).ToNot(BeNil()) }) @@ -534,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() { @@ -550,26 +550,26 @@ var _ = Describe("Podman containers ", func() { filtersIncorrect := map[string][]string{ "status": {"dummy"}, } - pruneResponse, err := containers.Prune(bt.conn, filtersIncorrect) + pruneResponse, err := containers.Prune(bt.conn, new(containers.PruneOptions).WithFilters(filtersIncorrect)) Expect(err).ToNot(BeNil()) // Mismatched filter params no container should be pruned. filtersIncorrect = map[string][]string{ "name": {"r"}, } - pruneResponse, err = containers.Prune(bt.conn, filtersIncorrect) + 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{ "name": {"top"}, } - pruneResponse, err = containers.Prune(bt.conn, filters) + 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() { @@ -586,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() { @@ -620,7 +620,7 @@ var _ = Describe("Podman containers ", func() { var name = "top" _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) - _, err = containers.Inspect(bt.conn, name, bindings.PTrue) + _, err = containers.Inspect(bt.conn, name, new(containers.InspectOptions).WithSize(true)) Expect(err).To(BeNil()) }) @@ -631,12 +631,12 @@ var _ = Describe("Podman containers ", func() { err = containers.Stop(bt.conn, name, nil) Expect(err).To(BeNil()) // Inspecting stopped container with size should succeed - _, err = containers.Inspect(bt.conn, name, bindings.PTrue) + _, err = containers.Inspect(bt.conn, name, new(containers.InspectOptions).WithSize(true)) Expect(err).To(BeNil()) }) It("podman remove bogus container", func() { - err = containers.Remove(bt.conn, "foobar", nil, nil) + err = containers.Remove(bt.conn, "foobar", nil) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) }) @@ -646,7 +646,7 @@ var _ = Describe("Podman containers ", func() { _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, name, nil, nil) + err = containers.Remove(bt.conn, name, nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -657,7 +657,7 @@ var _ = Describe("Podman containers ", func() { cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, cid, nil, nil) + err = containers.Remove(bt.conn, cid, nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -668,7 +668,7 @@ var _ = Describe("Podman containers ", func() { _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, name, bindings.PTrue, nil) + err = containers.Remove(bt.conn, name, new(containers.RemoveOptions).WithForce(true)) Expect(err).To(BeNil()) //code, _ := bindings.CheckResponseCode(err) //Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -679,7 +679,7 @@ var _ = Describe("Podman containers ", func() { cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, cid, bindings.PTrue, nil) + err = containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithForce(true)) Expect(err).To(BeNil()) //code, _ := bindings.CheckResponseCode(err) //Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -690,7 +690,7 @@ var _ = Describe("Podman containers ", func() { _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, name, nil, bindings.PTrue) + err = containers.Remove(bt.conn, name, new(containers.RemoveOptions).WithVolumes(true)) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -701,7 +701,7 @@ var _ = Describe("Podman containers ", func() { cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, cid, nil, bindings.PTrue) + err = containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithVolumes(true)) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -712,7 +712,7 @@ var _ = Describe("Podman containers ", func() { _, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, name, bindings.PTrue, bindings.PTrue) + err = containers.Remove(bt.conn, name, new(containers.RemoveOptions).WithVolumes(true).WithForce(true)) Expect(err).To(BeNil()) //code, _ := bindings.CheckResponseCode(err) //Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -723,7 +723,7 @@ var _ = Describe("Podman containers ", func() { cid, err := bt.RunTopContainer(&name, bindings.PFalse, nil) Expect(err).To(BeNil()) // Removing running container should fail - err = containers.Remove(bt.conn, cid, bindings.PTrue, bindings.PTrue) + err = containers.Remove(bt.conn, cid, new(containers.RemoveOptions).WithForce(true).WithVolumes(true)) Expect(err).To(BeNil()) //code, _ := bindings.CheckResponseCode(err) //Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -739,12 +739,12 @@ var _ = Describe("Podman containers ", func() { s := specgen.NewSpecGenerator(alpine.name, false) s.Terminal = true s.Command = []string{"date", "-R"} - _, err = containers.CreateWithSpec(bt.conn, s) + _, err = containers.CreateWithSpec(bt.conn, s, nil) Expect(err).To(BeNil()) // Validate list container with id filter filters := make(map[string][]string) filters["id"] = []string{cid} - c, err := containers.List(bt.conn, filters, bindings.PTrue, nil, nil, nil, nil) + c, err := containers.List(bt.conn, new(containers.ListOptions).WithFilters(filters).WithAll(true)) Expect(err).To(BeNil()) Expect(len(c)).To(Equal(1)) }) @@ -758,7 +758,7 @@ var _ = Describe("Podman containers ", func() { lastNum := 1 - c, err := containers.List(bt.conn, nil, bindings.PTrue, &lastNum, nil, nil, nil) + c, err := containers.List(bt.conn, new(containers.ListOptions).WithAll(true).WithLast(lastNum)) Expect(err).To(BeNil()) Expect(len(c)).To(Equal(1)) Expect(c[0].PodName).To(Equal(podName)) diff --git a/pkg/bindings/test/create_test.go b/pkg/bindings/test/create_test.go index fd9ce23ca..2d2d657de 100644 --- a/pkg/bindings/test/create_test.go +++ b/pkg/bindings/test/create_test.go @@ -35,7 +35,7 @@ var _ = Describe("Create containers ", func() { s.Command = []string{"top"} s.Terminal = true s.Name = "top" - ctr, err := containers.CreateWithSpec(bt.conn, s) + ctr, err := containers.CreateWithSpec(bt.conn, s, nil) Expect(err).To(BeNil()) data, err := containers.Inspect(bt.conn, ctr.ID, nil) Expect(err).To(BeNil()) diff --git a/pkg/bindings/test/exec_test.go b/pkg/bindings/test/exec_test.go index e3d90b9ea..8e20a192f 100644 --- a/pkg/bindings/test/exec_test.go +++ b/pkg/bindings/test/exec_test.go @@ -43,7 +43,7 @@ var _ = Describe("Podman containers exec", func() { Expect(err).To(BeNil()) Expect(sessionID).To(Not(Equal(""))) - inspectOut, err := containers.ExecInspect(bt.conn, sessionID) + inspectOut, err := containers.ExecInspect(bt.conn, sessionID, nil) Expect(err).To(BeNil()) Expect(inspectOut.ContainerID).To(Equal(cid)) Expect(inspectOut.ProcessConfig.Entrypoint).To(Equal("echo")) @@ -71,7 +71,7 @@ var _ = Describe("Podman containers exec", func() { }) It("Podman exec inspect on invalid session fails", func() { - _, err := containers.ExecInspect(bt.conn, "0000000000000000000000000000000000000000000000000000000000000000") + _, err := containers.ExecInspect(bt.conn, "0000000000000000000000000000000000000000000000000000000000000000", nil) Expect(err).To(Not(BeNil())) }) }) diff --git a/pkg/bindings/test/images_test.go b/pkg/bindings/test/images_test.go index 7d9415f91..c6b9c20f9 100644 --- a/pkg/bindings/test/images_test.go +++ b/pkg/bindings/test/images_test.go @@ -9,7 +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" - "github.com/containers/podman/v2/pkg/domain/entities" + dreports "github.com/containers/podman/v2/pkg/domain/entities/reports" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/onsi/gomega/gexec" @@ -71,12 +71,13 @@ var _ = Describe("Podman images", func() { // Inspect by long name _, err = images.GetImage(bt.conn, alpine.name, nil) Expect(err).To(BeNil()) - // TODO it looks like the images API alwaays returns size regardless + // TODO it looks like the images API always returns size regardless // of bool or not. What should we do ? // Expect(data.Size).To(BeZero()) + options := new(images.GetOptions).WithSize(true) // Enabling the size parameter should result in size being populated - data, err = images.GetImage(bt.conn, alpine.name, bindings.PTrue) + data, err = images.GetImage(bt.conn, alpine.name, options) Expect(err).To(BeNil()) Expect(data.Size).To(BeNumerically(">", 0)) }) @@ -84,23 +85,19 @@ var _ = Describe("Podman images", func() { // Test to validate the remove image api It("remove image", func() { // Remove invalid image should be a 404 - response, err := images.Remove(bt.conn, "foobar5000", false) - Expect(err).ToNot(BeNil()) - Expect(response).To(BeNil()) - code, _ := bindings.CheckResponseCode(err) - Expect(code).To(BeNumerically("==", http.StatusNotFound)) + response, errs := images.Remove(bt.conn, []string{"foobar5000"}, nil) + Expect(len(errs)).To(BeNumerically(">", 0)) + code, _ := bindings.CheckResponseCode(errs[0]) // Remove an image by name, validate image is removed and error is nil inspectData, err := images.GetImage(bt.conn, busybox.shortName, nil) Expect(err).To(BeNil()) - response, err = images.Remove(bt.conn, busybox.shortName, false) - Expect(err).To(BeNil()) - code, _ = bindings.CheckResponseCode(err) + response, errs = images.Remove(bt.conn, []string{busybox.shortName}, nil) + Expect(len(errs)).To(BeZero()) Expect(inspectData.ID).To(Equal(response.Deleted[0])) inspectData, err = images.GetImage(bt.conn, busybox.shortName, nil) code, _ = bindings.CheckResponseCode(err) - Expect(code).To(BeNumerically("==", http.StatusNotFound)) // Start a container with alpine image var top string = "top" @@ -113,23 +110,21 @@ var _ = Describe("Podman images", func() { // try to remove the image "alpine". This should fail since we are not force // deleting hence image cannot be deleted until the container is deleted. - response, err = images.Remove(bt.conn, alpine.shortName, false) - code, _ = bindings.CheckResponseCode(err) - Expect(code).To(BeNumerically("==", http.StatusConflict)) + response, errs = images.Remove(bt.conn, []string{alpine.shortName}, nil) + code, _ = bindings.CheckResponseCode(errs[0]) // Removing the image "alpine" where force = true - response, err = images.Remove(bt.conn, alpine.shortName, true) - Expect(err).To(BeNil()) + options := new(images.RemoveOptions).WithForce(true) + response, errs = images.Remove(bt.conn, []string{alpine.shortName}, options) + Expect(len(errs)).To(BeZero()) // To be extra sure, check if the previously created container // is gone as well. - _, err = containers.Inspect(bt.conn, "top", bindings.PFalse) + _, err = containers.Inspect(bt.conn, "top", nil) code, _ = bindings.CheckResponseCode(err) - Expect(code).To(BeNumerically("==", http.StatusNotFound)) // Now make sure both images are gone. inspectData, err = images.GetImage(bt.conn, busybox.shortName, nil) code, _ = bindings.CheckResponseCode(err) - Expect(code).To(BeNumerically("==", http.StatusNotFound)) inspectData, err = images.GetImage(bt.conn, alpine.shortName, nil) code, _ = bindings.CheckResponseCode(err) @@ -138,14 +133,15 @@ var _ = Describe("Podman images", func() { // Tests to validate the image tag command. It("tag image", func() { + // Validates if invalid image name is given a bad response is encountered. - err = images.Tag(bt.conn, "dummy", "demo", alpine.shortName) + err = images.Tag(bt.conn, "dummy", "demo", alpine.shortName, nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) // Validates if the image is tagged successfully. - err = images.Tag(bt.conn, alpine.shortName, "demo", alpine.shortName) + err = images.Tag(bt.conn, alpine.shortName, "demo", alpine.shortName, nil) Expect(err).To(BeNil()) // Validates if name updates when the image is retagged. @@ -157,7 +153,7 @@ var _ = Describe("Podman images", func() { // Test to validate the List images command. It("List image", func() { // Array to hold the list of images returned - imageSummary, err := images.List(bt.conn, nil, nil) + imageSummary, err := images.List(bt.conn, nil) // There Should be no errors in the response. Expect(err).To(BeNil()) // Since in the begin context two images are created the @@ -167,7 +163,7 @@ var _ = Describe("Podman images", func() { // Adding one more image. There Should be no errors in the response. // And the count should be three now. bt.Pull("testimage:20200929") - imageSummary, err = images.List(bt.conn, nil, nil) + imageSummary, err = images.List(bt.conn, nil) Expect(err).To(BeNil()) Expect(len(imageSummary)).To(Equal(3)) @@ -182,13 +178,15 @@ var _ = Describe("Podman images", func() { // List images with a filter filters := make(map[string][]string) filters["reference"] = []string{alpine.name} - filteredImages, err := images.List(bt.conn, bindings.PFalse, filters) + options := new(images.ListOptions).WithFilters(filters).WithAll(false) + filteredImages, err := images.List(bt.conn, options) Expect(err).To(BeNil()) Expect(len(filteredImages)).To(BeNumerically("==", 1)) // List images with a bad filter filters["name"] = []string{alpine.name} - _, err = images.List(bt.conn, bindings.PFalse, filters) + options = new(images.ListOptions).WithFilters(filters) + _, err = images.List(bt.conn, options) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -213,8 +211,8 @@ var _ = Describe("Podman images", func() { It("Load|Import Image", func() { // load an image - _, err := images.Remove(bt.conn, alpine.name, false) - Expect(err).To(BeNil()) + _, errs := images.Remove(bt.conn, []string{alpine.name}, nil) + Expect(len(errs)).To(BeZero()) exists, err := images.Exists(bt.conn, alpine.name) Expect(err).To(BeNil()) Expect(exists).To(BeFalse()) @@ -231,13 +229,14 @@ var _ = Describe("Podman images", func() { // load with a repo name f, err = os.Open(filepath.Join(ImageCacheDir, alpine.tarballName)) Expect(err).To(BeNil()) - _, err = images.Remove(bt.conn, alpine.name, false) - Expect(err).To(BeNil()) + _, errs = images.Remove(bt.conn, []string{alpine.name}, nil) + Expect(len(errs)).To(BeZero()) exists, err = images.Exists(bt.conn, alpine.name) Expect(err).To(BeNil()) Expect(exists).To(BeFalse()) newName := "quay.io/newname:fizzle" - names, err = images.Load(bt.conn, f, &newName) + options := new(images.LoadOptions).WithReference(newName) + names, err = images.Load(bt.conn, f, options) Expect(err).To(BeNil()) Expect(names.Names[0]).To(Equal(alpine.name)) exists, err = images.Exists(bt.conn, newName) @@ -247,13 +246,13 @@ var _ = Describe("Podman images", func() { // load with a bad repo name should trigger a 500 f, err = os.Open(filepath.Join(ImageCacheDir, alpine.tarballName)) Expect(err).To(BeNil()) - _, err = images.Remove(bt.conn, alpine.name, false) - Expect(err).To(BeNil()) + _, errs = images.Remove(bt.conn, []string{alpine.name}, nil) + Expect(len(errs)).To(BeZero()) exists, err = images.Exists(bt.conn, alpine.name) Expect(err).To(BeNil()) Expect(exists).To(BeFalse()) - badName := "quay.io/newName:fizzle" - _, err = images.Load(bt.conn, f, &badName) + 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)) @@ -265,7 +264,7 @@ var _ = Describe("Podman images", func() { w, err := os.Create(filepath.Join(bt.tempDirPath, alpine.tarballName)) defer w.Close() Expect(err).To(BeNil()) - err = images.Export(bt.conn, alpine.name, w, nil, nil) + err = images.Export(bt.conn, []string{alpine.name}, w, nil) Expect(err).To(BeNil()) _, err = os.Stat(exportPath) Expect(err).To(BeNil()) @@ -275,8 +274,8 @@ var _ = Describe("Podman images", func() { It("Import Image", func() { // load an image - _, err = images.Remove(bt.conn, alpine.name, false) - Expect(err).To(BeNil()) + _, errs := images.Remove(bt.conn, []string{alpine.name}, nil) + Expect(len(errs)).To(BeZero()) exists, err := images.Exists(bt.conn, alpine.name) Expect(err).To(BeNil()) Expect(exists).To(BeFalse()) @@ -285,7 +284,8 @@ var _ = Describe("Podman images", func() { Expect(err).To(BeNil()) changes := []string{"CMD /bin/foobar"} testMessage := "test_import" - _, err = images.Import(bt.conn, changes, &testMessage, &alpine.name, nil, f) + options := new(images.ImportOptions).WithMessage(testMessage).WithChanges(changes).WithReference(alpine.name) + _, err = images.Import(bt.conn, f, options) Expect(err).To(BeNil()) exists, err = images.Exists(bt.conn, alpine.name) Expect(err).To(BeNil()) @@ -298,7 +298,7 @@ var _ = Describe("Podman images", func() { It("History Image", func() { // a bogus name should return a 404 - _, err := images.History(bt.conn, "foobar") + _, err := images.History(bt.conn, "foobar", nil) Expect(err).To(Not(BeNil())) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) @@ -306,7 +306,7 @@ var _ = Describe("Podman images", func() { var foundID bool data, err := images.GetImage(bt.conn, alpine.name, nil) Expect(err).To(BeNil()) - history, err := images.History(bt.conn, alpine.name) + history, err := images.History(bt.conn, alpine.name, nil) Expect(err).To(BeNil()) for _, i := range history { if i.ID == data.ID { @@ -318,7 +318,7 @@ var _ = Describe("Podman images", func() { }) It("Search for an image", func() { - reports, err := images.Search(bt.conn, "alpine", entities.ImageSearchOptions{}) + reports, err := images.Search(bt.conn, "alpine", nil) Expect(err).To(BeNil()) Expect(len(reports)).To(BeNumerically(">", 1)) var foundAlpine bool @@ -331,35 +331,39 @@ var _ = Describe("Podman images", func() { Expect(foundAlpine).To(BeTrue()) // Search for alpine with a limit of 10 - reports, err = images.Search(bt.conn, "docker.io/alpine", entities.ImageSearchOptions{Limit: 10}) + options := new(images.SearchOptions).WithLimit(10) + reports, err = images.Search(bt.conn, "docker.io/alpine", options) Expect(err).To(BeNil()) Expect(len(reports)).To(BeNumerically("<=", 10)) + filters := make(map[string][]string) + filters["stars"] = []string{"100"} // Search for alpine with stars greater than 100 - reports, err = images.Search(bt.conn, "docker.io/alpine", entities.ImageSearchOptions{Filters: []string{"stars=100"}}) + options = new(images.SearchOptions).WithFilters(filters) + reports, err = images.Search(bt.conn, "docker.io/alpine", options) Expect(err).To(BeNil()) for _, i := range reports { Expect(i.Stars).To(BeNumerically(">=", 100)) } // Search with a fqdn - reports, err = images.Search(bt.conn, "quay.io/libpod/alpine_nginx", entities.ImageSearchOptions{}) + reports, err = images.Search(bt.conn, "quay.io/libpod/alpine_nginx", nil) Expect(len(reports)).To(BeNumerically(">=", 1)) }) It("Prune images", func() { - trueBoxed := true - results, err := images.Prune(bt.conn, &trueBoxed, nil) + options := new(images.PruneOptions).WithAll(true) + 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. It("Image Pull", func() { rawImage := "docker.io/library/busybox:latest" - pulledImages, err := images.Pull(bt.conn, rawImage, entities.ImagePullOptions{}) + pulledImages, err := images.Pull(bt.conn, rawImage, nil) Expect(err).NotTo(HaveOccurred()) Expect(len(pulledImages)).To(Equal(1)) @@ -368,11 +372,11 @@ var _ = Describe("Podman images", func() { Expect(exists).To(BeTrue()) // Make sure the normalization AND the full-transport reference works. - _, err = images.Pull(bt.conn, "docker://"+rawImage, entities.ImagePullOptions{}) + _, err = images.Pull(bt.conn, "docker://"+rawImage, nil) Expect(err).NotTo(HaveOccurred()) // The v2 endpoint only supports the docker transport. Let's see if that's really true. - _, err = images.Pull(bt.conn, "bogus-transport:bogus.com/image:reference", entities.ImagePullOptions{}) + _, err = images.Pull(bt.conn, "bogus-transport:bogus.com/image:reference", nil) Expect(err).To(HaveOccurred()) }) }) diff --git a/pkg/bindings/test/info_test.go b/pkg/bindings/test/info_test.go index 6cd5d6724..4d696b59e 100644 --- a/pkg/bindings/test/info_test.go +++ b/pkg/bindings/test/info_test.go @@ -17,7 +17,6 @@ var _ = Describe("Podman info", func() { var ( bt *bindingTest s *gexec.Session - t bool = true ) BeforeEach(func() { @@ -35,23 +34,24 @@ var _ = Describe("Podman info", func() { }) It("podman info", func() { - info, err := system.Info(bt.conn) + info, err := system.Info(bt.conn, nil) Expect(err).To(BeNil()) Expect(info.Host.Arch).To(Equal(runtime.GOARCH)) Expect(info.Host.OS).To(Equal(runtime.GOOS)) - i, err := images.List(bt.conn, &t, nil) + listOptions := new(images.ListOptions) + i, err := images.List(bt.conn, listOptions.WithAll(true)) Expect(err).To(BeNil()) Expect(info.Store.ImageStore.Number).To(Equal(len(i))) }) It("podman info container counts", func() { s := specgen.NewSpecGenerator(alpine.name, false) - _, err := containers.CreateWithSpec(bt.conn, s) + _, err := containers.CreateWithSpec(bt.conn, s, nil) Expect(err).To(BeNil()) idPause, err := bt.RunTopContainer(nil, nil, nil) Expect(err).To(BeNil()) - err = containers.Pause(bt.conn, idPause) + err = containers.Pause(bt.conn, idPause, nil) Expect(err).To(BeNil()) idStop, err := bt.RunTopContainer(nil, nil, nil) @@ -62,7 +62,7 @@ var _ = Describe("Podman info", func() { _, err = bt.RunTopContainer(nil, nil, nil) Expect(err).To(BeNil()) - info, err := system.Info(bt.conn) + info, err := system.Info(bt.conn, nil) Expect(err).To(BeNil()) Expect(info.Store.ContainerStore.Number).To(BeNumerically("==", 4)) diff --git a/pkg/bindings/test/manifests_test.go b/pkg/bindings/test/manifests_test.go index 55fc4cb0d..b93f64b4b 100644 --- a/pkg/bindings/test/manifests_test.go +++ b/pkg/bindings/test/manifests_test.go @@ -4,7 +4,6 @@ import ( "net/http" "time" - "github.com/containers/podman/v2/libpod/image" "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/bindings/images" "github.com/containers/podman/v2/pkg/bindings/manifests" @@ -37,7 +36,7 @@ var _ = Describe("Podman containers ", func() { // create manifest list without images id, err := manifests.Create(bt.conn, []string{"quay.io/libpod/foobar:latest"}, []string{}, nil) Expect(err).To(BeNil()) - list, err := manifests.Inspect(bt.conn, id) + list, err := manifests.Inspect(bt.conn, id, nil) Expect(err).To(BeNil()) Expect(len(list.Manifests)).To(BeZero()) @@ -47,19 +46,19 @@ var _ = Describe("Podman containers ", func() { code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) - _, err = images.Remove(bt.conn, id, false) - Expect(err).To(BeNil()) + _, errs := images.Remove(bt.conn, []string{id}, nil) + Expect(len(errs)).To(BeZero()) // create manifest list with images id, err = manifests.Create(bt.conn, []string{"quay.io/libpod/foobar:latest"}, []string{alpine.name}, nil) Expect(err).To(BeNil()) - list, err = manifests.Inspect(bt.conn, id) + list, err = manifests.Inspect(bt.conn, id, nil) Expect(err).To(BeNil()) Expect(len(list.Manifests)).To(BeNumerically("==", 1)) }) It("inspect bogus manifest", func() { - _, err := manifests.Inspect(bt.conn, "larry") + _, err := manifests.Inspect(bt.conn, "larry", nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) @@ -67,23 +66,23 @@ var _ = Describe("Podman containers ", func() { It("add manifest", func() { // add to bogus should 404 - _, err := manifests.Add(bt.conn, "foobar", image.ManifestAddOpts{}) + _, err := manifests.Add(bt.conn, "foobar", nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) id, err := manifests.Create(bt.conn, []string{"quay.io/libpod/foobar:latest"}, []string{}, nil) Expect(err).To(BeNil()) - opts := image.ManifestAddOpts{Images: []string{alpine.name}} - _, err = manifests.Add(bt.conn, id, opts) + options := new(manifests.AddOptions).WithImages([]string{alpine.name}) + _, err = manifests.Add(bt.conn, id, options) Expect(err).To(BeNil()) - list, err := manifests.Inspect(bt.conn, id) + list, err := manifests.Inspect(bt.conn, id, nil) Expect(err).To(BeNil()) Expect(len(list.Manifests)).To(BeNumerically("==", 1)) // add bogus name to existing list should fail - opts.Images = []string{"larry"} - _, err = manifests.Add(bt.conn, id, opts) + options.WithImages([]string{"larry"}) + _, err = manifests.Add(bt.conn, id, options) Expect(err).ToNot(BeNil()) code, _ = bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -91,29 +90,29 @@ var _ = Describe("Podman containers ", func() { It("remove manifest", func() { // removal on bogus manifest list should be 404 - _, err := manifests.Remove(bt.conn, "larry", "1234") + _, err := manifests.Remove(bt.conn, "larry", "1234", nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) id, err := manifests.Create(bt.conn, []string{"quay.io/libpod/foobar:latest"}, []string{alpine.name}, nil) Expect(err).To(BeNil()) - data, err := manifests.Inspect(bt.conn, id) + data, err := manifests.Inspect(bt.conn, id, nil) Expect(err).To(BeNil()) Expect(len(data.Manifests)).To(BeNumerically("==", 1)) // removal on a good manifest list with a bad digest should be 400 - _, err = manifests.Remove(bt.conn, id, "!234") + _, err = manifests.Remove(bt.conn, id, "!234", nil) Expect(err).ToNot(BeNil()) code, _ = bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusBadRequest)) digest := data.Manifests[0].Digest.String() - _, err = manifests.Remove(bt.conn, id, digest) + _, err = manifests.Remove(bt.conn, id, digest, nil) Expect(err).To(BeNil()) // removal on good manifest with good digest should work - data, err = manifests.Inspect(bt.conn, id) + data, err = manifests.Inspect(bt.conn, id, nil) Expect(err).To(BeNil()) Expect(len(data.Manifests)).To(BeZero()) }) diff --git a/pkg/bindings/test/pods_test.go b/pkg/bindings/test/pods_test.go index 8498de020..38c5997ef 100644 --- a/pkg/bindings/test/pods_test.go +++ b/pkg/bindings/test/pods_test.go @@ -40,13 +40,13 @@ var _ = Describe("Podman pods", func() { It("inspect pod", func() { //Inspect an invalid pod name - _, err := pods.Inspect(bt.conn, "dummyname") + _, err := pods.Inspect(bt.conn, "dummyname", nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) //Inspect an valid pod name - response, err := pods.Inspect(bt.conn, newpod) + response, err := pods.Inspect(bt.conn, newpod, nil) Expect(err).To(BeNil()) Expect(response.Name).To(Equal(newpod)) }) @@ -59,7 +59,7 @@ var _ = Describe("Podman pods", func() { Expect(len(podSummary)).To(Equal(1)) // Start the pod - _, err = pods.Start(bt.conn, newpod) + _, err = pods.Start(bt.conn, newpod, nil) Expect(err).To(BeNil()) // Adding an alpine container to the existing pod @@ -90,7 +90,7 @@ var _ = Describe("Podman pods", func() { bt.Podcreate(&newpod2) // Start the pod - _, err = pods.Start(bt.conn, newpod) + _, err = pods.Start(bt.conn, newpod, nil) Expect(err).To(BeNil()) _, err = bt.RunTopContainer(nil, bindings.PTrue, &newpod) @@ -99,7 +99,8 @@ var _ = Describe("Podman pods", func() { // Expected err with invalid filter params filters := make(map[string][]string) filters["dummy"] = []string{"dummy"} - filteredPods, err := pods.List(bt.conn, filters) + options := new(pods.ListOptions).WithFilters(filters) + filteredPods, err := pods.List(bt.conn, options) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) @@ -107,14 +108,16 @@ var _ = Describe("Podman pods", func() { // Expected empty response with invalid filters filters = make(map[string][]string) filters["name"] = []string{"dummy"} - filteredPods, err = pods.List(bt.conn, filters) + options = new(pods.ListOptions).WithFilters(filters) + filteredPods, err = pods.List(bt.conn, options) Expect(err).To(BeNil()) Expect(len(filteredPods)).To(BeNumerically("==", 0)) // Validate list pod with name filter filters = make(map[string][]string) filters["name"] = []string{newpod2} - filteredPods, err = pods.List(bt.conn, filters) + options = new(pods.ListOptions).WithFilters(filters) + filteredPods, err = pods.List(bt.conn, options) Expect(err).To(BeNil()) Expect(len(filteredPods)).To(BeNumerically("==", 1)) var names []string @@ -125,11 +128,12 @@ var _ = Describe("Podman pods", func() { // Validate list pod with id filter filters = make(map[string][]string) - response, err := pods.Inspect(bt.conn, newpod) + response, err := pods.Inspect(bt.conn, newpod, nil) Expect(err).To(BeNil()) id := response.ID filters["id"] = []string{id} - filteredPods, err = pods.List(bt.conn, filters) + options = new(pods.ListOptions).WithFilters(filters) + filteredPods, err = pods.List(bt.conn, options) Expect(err).To(BeNil()) Expect(len(filteredPods)).To(BeNumerically("==", 1)) names = names[:0] @@ -140,7 +144,8 @@ var _ = Describe("Podman pods", func() { // Using multiple filters filters["name"] = []string{newpod} - filteredPods, err = pods.List(bt.conn, filters) + options = new(pods.ListOptions).WithFilters(filters) + filteredPods, err = pods.List(bt.conn, options) Expect(err).To(BeNil()) Expect(len(filteredPods)).To(BeNumerically("==", 1)) names = names[:0] @@ -164,11 +169,11 @@ var _ = Describe("Podman pods", func() { // This test validates if All running containers within // each specified pod are paused and unpaused - It("pause upause pod", func() { + It("pause unpause pod", func() { // TODO fix this Skip("Pod behavior is jacked right now.") // Pause invalid container - _, err := pods.Pause(bt.conn, "dummyName") + _, err := pods.Pause(bt.conn, "dummyName", nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) @@ -180,9 +185,9 @@ var _ = Describe("Podman pods", func() { // Binding needs to be modified to inspect the pod state. // Since we don't have a pod state we inspect the states of the containers within the pod. // Pause a valid container - _, err = pods.Pause(bt.conn, newpod) + _, err = pods.Pause(bt.conn, newpod, nil) Expect(err).To(BeNil()) - response, err := pods.Inspect(bt.conn, newpod) + response, err := pods.Inspect(bt.conn, newpod, nil) Expect(err).To(BeNil()) Expect(response.State).To(Equal(define.PodStatePaused)) for _, i := range response.Containers { @@ -191,9 +196,9 @@ var _ = Describe("Podman pods", func() { } // Unpause a valid container - _, err = pods.Unpause(bt.conn, newpod) + _, err = pods.Unpause(bt.conn, newpod, nil) Expect(err).To(BeNil()) - response, err = pods.Inspect(bt.conn, newpod) + response, err = pods.Inspect(bt.conn, newpod, nil) Expect(err).To(BeNil()) Expect(response.State).To(Equal(define.PodStateRunning)) for _, i := range response.Containers { @@ -204,7 +209,7 @@ var _ = Describe("Podman pods", func() { It("start stop restart pod", func() { // Start an invalid pod - _, err = pods.Start(bt.conn, "dummyName") + _, err = pods.Start(bt.conn, "dummyName", nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) @@ -216,16 +221,16 @@ var _ = Describe("Podman pods", func() { Expect(code).To(BeNumerically("==", http.StatusNotFound)) // Restart an invalid pod - _, err = pods.Restart(bt.conn, "dummyName") + _, err = pods.Restart(bt.conn, "dummyName", nil) Expect(err).ToNot(BeNil()) code, _ = bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusNotFound)) // Start a valid pod and inspect status of each container - _, err = pods.Start(bt.conn, newpod) + _, err = pods.Start(bt.conn, newpod, nil) Expect(err).To(BeNil()) - response, err := pods.Inspect(bt.conn, newpod) + response, err := pods.Inspect(bt.conn, newpod, nil) Expect(err).To(BeNil()) Expect(response.State).To(Equal(define.PodStateRunning)) for _, i := range response.Containers { @@ -234,13 +239,13 @@ var _ = Describe("Podman pods", func() { } // Start an already running pod - _, err = pods.Start(bt.conn, newpod) + _, err = pods.Start(bt.conn, newpod, nil) Expect(err).To(BeNil()) // Stop the running pods _, err = pods.Stop(bt.conn, newpod, nil) Expect(err).To(BeNil()) - response, _ = pods.Inspect(bt.conn, newpod) + response, _ = pods.Inspect(bt.conn, newpod, nil) Expect(response.State).To(Equal(define.PodStateExited)) for _, i := range response.Containers { Expect(define.StringToContainerStatus(i.State)). @@ -251,9 +256,9 @@ var _ = Describe("Podman pods", func() { _, err = pods.Stop(bt.conn, newpod, nil) Expect(err).To(BeNil()) - _, err = pods.Restart(bt.conn, newpod) + _, err = pods.Restart(bt.conn, newpod, nil) Expect(err).To(BeNil()) - response, _ = pods.Inspect(bt.conn, newpod) + response, _ = pods.Inspect(bt.conn, newpod, nil) Expect(response.State).To(Equal(define.PodStateRunning)) for _, i := range response.Containers { Expect(define.StringToContainerStatus(i.State)). @@ -267,7 +272,7 @@ var _ = Describe("Podman pods", func() { var newpod2 string = "newpod2" bt.Podcreate(&newpod2) // No pods pruned since no pod in exited state - pruneResponse, err := pods.Prune(bt.conn) + pruneResponse, err := pods.Prune(bt.conn, nil) Expect(err).To(BeNil()) podSummary, err := pods.List(bt.conn, nil) Expect(err).To(BeNil()) @@ -276,14 +281,14 @@ var _ = Describe("Podman pods", func() { // Prune only one pod which is in exited state. // Start then stop a pod. // pod moves to exited state one pod should be pruned now. - _, err = pods.Start(bt.conn, newpod) + _, err = pods.Start(bt.conn, newpod, nil) Expect(err).To(BeNil()) _, err = pods.Stop(bt.conn, newpod, nil) Expect(err).To(BeNil()) - response, err := pods.Inspect(bt.conn, newpod) + response, err := pods.Inspect(bt.conn, newpod, nil) Expect(err).To(BeNil()) Expect(response.State).To(Equal(define.PodStateExited)) - pruneResponse, err = pods.Prune(bt.conn) + pruneResponse, err = pods.Prune(bt.conn, nil) Expect(err).To(BeNil()) // Validate status and record pod id of pod to be pruned Expect(response.State).To(Equal(define.PodStateExited)) @@ -298,13 +303,13 @@ var _ = Describe("Podman pods", func() { // Test prune multiple pods. bt.Podcreate(&newpod) - _, err = pods.Start(bt.conn, newpod) + _, err = pods.Start(bt.conn, newpod, nil) Expect(err).To(BeNil()) - _, err = pods.Start(bt.conn, newpod2) + _, err = pods.Start(bt.conn, newpod2, nil) Expect(err).To(BeNil()) _, err = pods.Stop(bt.conn, newpod, nil) Expect(err).To(BeNil()) - response, err = pods.Inspect(bt.conn, newpod) + response, err = pods.Inspect(bt.conn, newpod, nil) Expect(err).To(BeNil()) Expect(response.State).To(Equal(define.PodStateExited)) for _, i := range response.Containers { @@ -313,14 +318,14 @@ var _ = Describe("Podman pods", func() { } _, err = pods.Stop(bt.conn, newpod2, nil) Expect(err).To(BeNil()) - response, err = pods.Inspect(bt.conn, newpod2) + response, err = pods.Inspect(bt.conn, newpod2, nil) Expect(err).To(BeNil()) Expect(response.State).To(Equal(define.PodStateExited)) for _, i := range response.Containers { Expect(define.StringToContainerStatus(i.State)). To(Equal(define.ContainerStateExited)) } - _, err = pods.Prune(bt.conn) + _, err = pods.Prune(bt.conn, nil) Expect(err).To(BeNil()) podSummary, err = pods.List(bt.conn, nil) Expect(err).To(BeNil()) @@ -330,7 +335,7 @@ var _ = Describe("Podman pods", func() { It("simple create pod", func() { ps := specgen.PodSpecGenerator{} ps.Name = "foobar" - _, err := pods.CreatePodFromSpec(bt.conn, &ps) + _, err := pods.CreatePodFromSpec(bt.conn, &ps, nil) Expect(err).To(BeNil()) exists, err := pods.Exists(bt.conn, "foobar") @@ -343,7 +348,7 @@ var _ = Describe("Podman pods", func() { var name string = "podA" bt.Podcreate(&name) - _, err := pods.Start(bt.conn, name) + _, err := pods.Start(bt.conn, name, nil) Expect(err).To(BeNil()) // By name @@ -351,7 +356,8 @@ var _ = Describe("Podman pods", func() { Expect(err).To(BeNil()) // With descriptors - output, err := pods.Top(bt.conn, name, []string{"user,pid,hpid"}) + options := new(pods.TopOptions).WithDescriptors([]string{"user,pid,hpid"}) + output, err := pods.Top(bt.conn, name, options) Expect(err).To(BeNil()) header := strings.Split(output[0], "\t") for _, d := range []string{"USER", "PID", "HPID"} { @@ -363,7 +369,8 @@ var _ = Describe("Podman pods", func() { Expect(err).ToNot(BeNil()) // With bogus descriptors - _, err = pods.Top(bt.conn, name, []string{"Me,Neither"}) + options = new(pods.TopOptions).WithDescriptors([]string{"Me,Neither"}) + _, err = pods.Top(bt.conn, name, options) Expect(err).ToNot(BeNil()) }) }) diff --git a/pkg/bindings/test/system_test.go b/pkg/bindings/test/system_test.go index 82e5c7541..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" @@ -56,8 +57,8 @@ var _ = Describe("Podman system", func() { eventCounter++ } }() - - err = system.Events(bt.conn, binChan, nil, nil, nil, filters, bindings.PFalse) + options := new(system.EventsOptions).WithFilters(filters).WithStream(false) + err = system.Events(bt.conn, binChan, nil, options) Expect(err).To(BeNil()) done.Lock() Expect(eventCounter).To(BeNumerically(">", 0)) @@ -65,7 +66,7 @@ var _ = Describe("Podman system", func() { It("podman system prune - pod,container stopped", func() { // Start and stop a pod to enter in exited state. - _, err := pods.Start(bt.conn, newpod) + _, err := pods.Start(bt.conn, newpod, nil) Expect(err).To(BeNil()) _, err = pods.Stop(bt.conn, newpod, nil) Expect(err).To(BeNil()) @@ -76,20 +77,21 @@ var _ = Describe("Podman system", func() { err = containers.Stop(bt.conn, name, nil) Expect(err).To(BeNil()) - systemPruneResponse, err := system.Prune(bt.conn, bindings.PTrue, bindings.PFalse) + options := new(system.PruneOptions).WithAll(true) + 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() { // Start and stop a pod to enter in exited state. - _, err := pods.Start(bt.conn, newpod) + _, err := pods.Start(bt.conn, newpod, nil) Expect(err).To(BeNil()) _, err = pods.Stop(bt.conn, newpod, nil) Expect(err).To(BeNil()) @@ -107,25 +109,25 @@ var _ = Describe("Podman system", func() { Expect(err).To(BeNil()) // Adding an unused volume - _, err = volumes.Create(bt.conn, entities.VolumeCreateOptions{}) + _, err = volumes.Create(bt.conn, entities.VolumeCreateOptions{}, nil) Expect(err).To(BeNil()) - - systemPruneResponse, err := system.Prune(bt.conn, bindings.PTrue, bindings.PFalse) + options := new(system.PruneOptions).WithAll(true) + 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() { // Start a pod and leave it running - _, err := pods.Start(bt.conn, newpod) + _, err := pods.Start(bt.conn, newpod, nil) Expect(err).To(BeNil()) // Start and stop a container to enter in exited state. @@ -141,19 +143,77 @@ var _ = Describe("Podman system", func() { Expect(err).To(BeNil()) // Adding an unused volume should work - _, err = volumes.Create(bt.conn, entities.VolumeCreateOptions{}) + _, err = volumes.Create(bt.conn, entities.VolumeCreateOptions{}, nil) Expect(err).To(BeNil()) - systemPruneResponse, err := system.Prune(bt.conn, bindings.PTrue, bindings.PTrue) + options := new(system.PruneOptions).WithAll(true).WithVolumes(true) + 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() { + // Start a pod and leave it running + _, err := pods.Start(bt.conn, newpod, nil) + Expect(err).To(BeNil()) + + // Start and stop a container to enter in exited state. + var name = "top" + _, err = bt.RunTopContainer(&name, bindings.PFalse, nil) + Expect(err).To(BeNil()) + err = containers.Stop(bt.conn, name, nil) + Expect(err).To(BeNil()) + + // Start second container and leave in running + var name2 = "top2" + _, err = bt.RunTopContainer(&name2, bindings.PFalse, nil) + Expect(err).To(BeNil()) + + // Adding an unused volume should work + _, err = volumes.Create(bt.conn, entities.VolumeCreateOptions{}, nil) + Expect(err).To(BeNil()) + + // Adding an unused volume with label should work + _, err = volumes.Create(bt.conn, entities.VolumeCreateOptions{Label: map[string]string{ + "label1": "value1", + }}, nil) + Expect(err).To(BeNil()) + + f := make(map[string][]string) + f["label"] = []string{"label1=idontmatch"} + + options := new(system.PruneOptions).WithAll(true).WithVolumes(true).WithFilters(f) + systemPruneResponse, err := system.Prune(bt.conn, options) + Expect(err).To(BeNil()) + Expect(len(systemPruneResponse.PodPruneReport)).To(Equal(0)) + // TODO fix system filter handling so all components can handle filters + // 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.ContainerPruneReports)).To(Equal(1)) + Expect(len(systemPruneResponse.ImagePruneReports)). + To(BeNumerically(">", 0)) + // Alpine image should not be pruned as used by running container + 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.VolumePruneReports)).To(Equal(0)) + + // Fix filter and re prune + f["label"] = []string{"label1=value1"} + options = new(system.PruneOptions).WithAll(true).WithVolumes(true).WithFilters(f) + systemPruneResponse, err = system.Prune(bt.conn, options) + Expect(err).To(BeNil()) + + // Volume should be pruned because the PruneOptions filters now match + Expect(len(systemPruneResponse.VolumePruneReports)).To(Equal(1)) }) }) diff --git a/pkg/bindings/test/volumes_test.go b/pkg/bindings/test/volumes_test.go index dc90d4d00..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" @@ -52,7 +53,7 @@ var _ = Describe("Podman volumes", func() { It("create volume", func() { // create a volume with blank config should work - _, err := volumes.Create(connText, entities.VolumeCreateOptions{}) + _, err := volumes.Create(connText, entities.VolumeCreateOptions{}, nil) Expect(err).To(BeNil()) vcc := entities.VolumeCreateOptions{ @@ -60,21 +61,21 @@ var _ = Describe("Podman volumes", func() { Label: nil, Options: nil, } - vol, err := volumes.Create(connText, vcc) + vol, err := volumes.Create(connText, vcc, nil) Expect(err).To(BeNil()) Expect(vol.Name).To(Equal("foobar")) // create volume with same name should 500 - _, err = volumes.Create(connText, vcc) + _, err = volumes.Create(connText, vcc, nil) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) }) It("inspect volume", func() { - vol, err := volumes.Create(connText, entities.VolumeCreateOptions{}) + vol, err := volumes.Create(connText, entities.VolumeCreateOptions{}, nil) Expect(err).To(BeNil()) - data, err := volumes.Inspect(connText, vol.Name) + data, err := volumes.Inspect(connText, vol.Name, nil) Expect(err).To(BeNil()) Expect(data.Name).To(Equal(vol.Name)) }) @@ -86,13 +87,13 @@ var _ = Describe("Podman volumes", func() { Expect(code).To(BeNumerically("==", http.StatusNotFound)) // Removing an unused volume should work - vol, err := volumes.Create(connText, entities.VolumeCreateOptions{}) + vol, err := volumes.Create(connText, entities.VolumeCreateOptions{}, nil) Expect(err).To(BeNil()) err = volumes.Remove(connText, vol.Name, nil) Expect(err).To(BeNil()) // Removing a volume that is being used without force should be 409 - vol, err = volumes.Create(connText, entities.VolumeCreateOptions{}) + vol, err = volumes.Create(connText, entities.VolumeCreateOptions{}, nil) Expect(err).To(BeNil()) session := bt.runPodman([]string{"run", "-dt", "-v", fmt.Sprintf("%s:/foobar", vol.Name), "--name", "vtest", alpine.name, "top"}) session.Wait(45) @@ -102,10 +103,10 @@ var _ = Describe("Podman volumes", func() { Expect(code).To(BeNumerically("==", http.StatusConflict)) // Removing with a volume in use with force should work with a stopped container - zero := uint(0) - err = containers.Stop(connText, "vtest", &zero) + err = containers.Stop(connText, "vtest", new(containers.StopOptions).WithTimeout(0)) Expect(err).To(BeNil()) - err = volumes.Remove(connText, vol.Name, bindings.PTrue) + options := new(volumes.RemoveOptions).WithForce(true) + err = volumes.Remove(connText, vol.Name, options) Expect(err).To(BeNil()) }) @@ -118,7 +119,7 @@ var _ = Describe("Podman volumes", func() { // create a bunch of named volumes and make verify with list volNames := []string{"homer", "bart", "lisa", "maggie", "marge"} for i := 0; i < 5; i++ { - _, err = volumes.Create(connText, entities.VolumeCreateOptions{Name: volNames[i]}) + _, err = volumes.Create(connText, entities.VolumeCreateOptions{Name: volNames[i]}, nil) Expect(err).To(BeNil()) } vols, err = volumes.List(connText, nil) @@ -131,43 +132,81 @@ var _ = Describe("Podman volumes", func() { // list with bad filter should be 500 filters := make(map[string][]string) filters["foobar"] = []string{"1234"} - _, err = volumes.List(connText, filters) + options := new(volumes.ListOptions).WithFilters(filters) + _, err = volumes.List(connText, options) Expect(err).ToNot(BeNil()) code, _ := bindings.CheckResponseCode(err) Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) filters = make(map[string][]string) filters["name"] = []string{"homer"} - vols, err = volumes.List(connText, filters) + options = new(volumes.ListOptions).WithFilters(filters) + vols, err = volumes.List(connText, options) Expect(err).To(BeNil()) Expect(len(vols)).To(BeNumerically("==", 1)) Expect(vols[0].Name).To(Equal("homer")) }) - // TODO we need to add filtering to tests It("prune unused volume", func() { // Pruning when no volumes present should be ok - _, err := volumes.Prune(connText) + _, err := volumes.Prune(connText, nil) Expect(err).To(BeNil()) // Removing an unused volume should work - _, err = volumes.Create(connText, entities.VolumeCreateOptions{}) + _, err = volumes.Create(connText, entities.VolumeCreateOptions{}, nil) Expect(err).To(BeNil()) - vols, err := volumes.Prune(connText) + vols, err := volumes.Prune(connText, nil) Expect(err).To(BeNil()) Expect(len(vols)).To(BeNumerically("==", 1)) - _, err = volumes.Create(connText, entities.VolumeCreateOptions{Name: "homer"}) + _, err = volumes.Create(connText, entities.VolumeCreateOptions{Name: "homer"}, nil) Expect(err).To(BeNil()) - _, err = volumes.Create(connText, entities.VolumeCreateOptions{}) + _, err = volumes.Create(connText, entities.VolumeCreateOptions{}, nil) Expect(err).To(BeNil()) session := bt.runPodman([]string{"run", "-dt", "-v", fmt.Sprintf("%s:/homer", "homer"), "--name", "vtest", alpine.name, "top"}) session.Wait(45) - vols, err = volumes.Prune(connText) + vols, err = volumes.Prune(connText, nil) + Expect(err).To(BeNil()) + Expect(len(reports.PruneReportsIds(vols))).To(BeNumerically("==", 1)) + _, err = volumes.Inspect(connText, "homer", nil) + Expect(err).To(BeNil()) + + // Removing volume with non matching filter shouldn't prune any volumes + filters := make(map[string][]string) + filters["label"] = []string{"label1=idontmatch"} + _, err = volumes.Create(connText, entities.VolumeCreateOptions{Label: map[string]string{ + "label1": "value1", + }}, nil) + Expect(err).To(BeNil()) + options := new(volumes.PruneOptions).WithFilters(filters) + vols, err = volumes.Prune(connText, options) + Expect(err).To(BeNil()) + Expect(len(vols)).To(BeNumerically("==", 0)) + vol2, err := volumes.Create(connText, entities.VolumeCreateOptions{Label: map[string]string{ + "label1": "value2", + }}, nil) + Expect(err).To(BeNil()) + _, err = volumes.Create(connText, entities.VolumeCreateOptions{Label: map[string]string{ + "label1": "value3", + }}, nil) + Expect(err).To(BeNil()) + + // Removing volume with matching filter label and value should remove specific entry + filters = make(map[string][]string) + filters["label"] = []string{"label1=value2"} + options = new(volumes.PruneOptions).WithFilters(filters) + vols, err = volumes.Prune(connText, options) Expect(err).To(BeNil()) Expect(len(vols)).To(BeNumerically("==", 1)) - _, err = volumes.Inspect(connText, "homer") + Expect(vols[0].Id).To(Equal(vol2.Name)) + + // Removing volumes with matching filter label should remove all matching volumes + filters = make(map[string][]string) + filters["label"] = []string{"label1"} + options = new(volumes.PruneOptions).WithFilters(filters) + vols, err = volumes.Prune(connText, options) Expect(err).To(BeNil()) + Expect(len(vols)).To(BeNumerically("==", 2)) }) }) diff --git a/pkg/bindings/volumes/types.go b/pkg/bindings/volumes/types.go new file mode 100644 index 000000000..379174e33 --- /dev/null +++ b/pkg/bindings/volumes/types.go @@ -0,0 +1,32 @@ +package volumes + +//go:generate go run ../generator/generator.go CreateOptions +// CreateOptions are optional options for creating volumes +type CreateOptions struct { +} + +//go:generate go run ../generator/generator.go InspectOptions +// InspectOptions are optional options for inspecting volumes +type InspectOptions struct { +} + +//go:generate go run ../generator/generator.go ListOptions +// ListOptions are optional options for listing volumes +type ListOptions struct { + // Filters applied to the listing of volumes + Filters map[string][]string +} + +//go:generate go run ../generator/generator.go PruneOptions +// PruneOptions are optional options for pruning volumes +type PruneOptions struct { + // Filters applied to the pruning of volumes + Filters map[string][]string +} + +//go:generate go run ../generator/generator.go RemoveOptions +// RemoveOptions are optional options for removing volumes +type RemoveOptions struct { + // Force removes the volume even if it is being used + Force *bool +} diff --git a/pkg/bindings/volumes/types_create_options.go b/pkg/bindings/volumes/types_create_options.go new file mode 100644 index 000000000..80bdac2d2 --- /dev/null +++ b/pkg/bindings/volumes/types_create_options.go @@ -0,0 +1,88 @@ +package volumes + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *CreateOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *CreateOptions) 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 + } + 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 +} diff --git a/pkg/bindings/volumes/types_inspect_options.go b/pkg/bindings/volumes/types_inspect_options.go new file mode 100644 index 000000000..ba8c70b63 --- /dev/null +++ b/pkg/bindings/volumes/types_inspect_options.go @@ -0,0 +1,88 @@ +package volumes + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *InspectOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *InspectOptions) 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 + } + 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 +} diff --git a/pkg/bindings/volumes/types_list_options.go b/pkg/bindings/volumes/types_list_options.go new file mode 100644 index 000000000..99dec132c --- /dev/null +++ b/pkg/bindings/volumes/types_list_options.go @@ -0,0 +1,104 @@ +package volumes + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *ListOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *ListOptions) 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 + } + 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 +} + +// WithFilters +func (o *ListOptions) WithFilters(value map[string][]string) *ListOptions { + v := value + o.Filters = v + return o +} + +// GetFilters +func (o *ListOptions) GetFilters() map[string][]string { + var filters map[string][]string + if o.Filters == nil { + return filters + } + return o.Filters +} diff --git a/pkg/bindings/volumes/types_prune_options.go b/pkg/bindings/volumes/types_prune_options.go new file mode 100644 index 000000000..cdbc03fc9 --- /dev/null +++ b/pkg/bindings/volumes/types_prune_options.go @@ -0,0 +1,104 @@ +package volumes + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *PruneOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *PruneOptions) 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 + } + 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 +} + +// WithFilters +func (o *PruneOptions) WithFilters(value map[string][]string) *PruneOptions { + v := value + o.Filters = v + return o +} + +// GetFilters +func (o *PruneOptions) GetFilters() map[string][]string { + var filters map[string][]string + if o.Filters == nil { + return filters + } + return o.Filters +} diff --git a/pkg/bindings/volumes/types_remove_options.go b/pkg/bindings/volumes/types_remove_options.go new file mode 100644 index 000000000..923d1353c --- /dev/null +++ b/pkg/bindings/volumes/types_remove_options.go @@ -0,0 +1,104 @@ +package volumes + +import ( + "net/url" + "reflect" + "strconv" + + jsoniter "github.com/json-iterator/go" + "github.com/pkg/errors" +) + +/* +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 +func (o *RemoveOptions) Changed(fieldName string) bool { + r := reflect.ValueOf(o) + value := reflect.Indirect(r).FieldByName(fieldName) + return !value.IsNil() +} + +// ToParams +func (o *RemoveOptions) 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 + } + 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 +} + +// WithForce +func (o *RemoveOptions) WithForce(value bool) *RemoveOptions { + v := &value + o.Force = v + return o +} + +// GetForce +func (o *RemoveOptions) GetForce() bool { + var force bool + if o.Force == nil { + return force + } + return *o.Force +} diff --git a/pkg/bindings/volumes/volumes.go b/pkg/bindings/volumes/volumes.go index 00f1e5720..fe081eb46 100644 --- a/pkg/bindings/volumes/volumes.go +++ b/pkg/bindings/volumes/volumes.go @@ -3,20 +3,23 @@ package volumes import ( "context" "net/http" - "net/url" - "strconv" "strings" "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" ) // Create creates a volume given its configuration. -func Create(ctx context.Context, config entities.VolumeCreateOptions) (*entities.VolumeConfigResponse, error) { +func Create(ctx context.Context, config entities.VolumeCreateOptions, options *CreateOptions) (*entities.VolumeConfigResponse, error) { var ( v entities.VolumeConfigResponse ) + if options == nil { + options = new(CreateOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err @@ -34,10 +37,14 @@ func Create(ctx context.Context, config entities.VolumeCreateOptions) (*entities } // Inspect returns low-level information about a volume. -func Inspect(ctx context.Context, nameOrID string) (*entities.VolumeConfigResponse, error) { +func Inspect(ctx context.Context, nameOrID string, options *InspectOptions) (*entities.VolumeConfigResponse, error) { var ( inspect entities.VolumeConfigResponse ) + if options == nil { + options = new(InspectOptions) + } + _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err @@ -51,7 +58,7 @@ func Inspect(ctx context.Context, nameOrID string) (*entities.VolumeConfigRespon // List returns the configurations for existing volumes in the form of a slice. Optionally, filters // can be used to refine the list of volumes. -func List(ctx context.Context, filters map[string][]string) ([]*entities.VolumeListReport, error) { +func List(ctx context.Context, options *ListOptions) ([]*entities.VolumeListReport, error) { var ( vols []*entities.VolumeListReport ) @@ -59,13 +66,9 @@ func List(ctx context.Context, filters map[string][]string) ([]*entities.VolumeL if err != nil { return nil, err } - params := url.Values{} - if len(filters) > 0 { - strFilters, err := bindings.FiltersToString(filters) - if err != nil { - return nil, err - } - params.Set("filters", strFilters) + params, err := options.ToParams() + if err != nil { + return nil, err } response, err := conn.DoRequest(nil, http.MethodGet, "/volumes/json", params, nil) if err != nil { @@ -75,15 +78,19 @@ func List(ctx context.Context, filters map[string][]string) ([]*entities.VolumeL } // Prune removes unused volumes from the local filesystem. -func Prune(ctx context.Context) ([]*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 { return nil, err } - response, err := conn.DoRequest(nil, http.MethodPost, "/volumes/prune", nil, nil) + params, err := options.ToParams() + if err != nil { + return nil, err + } + response, err := conn.DoRequest(nil, http.MethodPost, "/volumes/prune", params, nil) if err != nil { return nil, err } @@ -92,14 +99,14 @@ func Prune(ctx context.Context) ([]*entities.VolumePruneReport, error) { // Remove deletes the given volume from storage. The optional force parameter // is used to remove a volume even if it is being used by a container. -func Remove(ctx context.Context, nameOrID string, force *bool) error { +func Remove(ctx context.Context, nameOrID string, options *RemoveOptions) error { conn, err := bindings.GetClient(ctx) if err != nil { return err } - params := url.Values{} - if force != nil { - params.Set("force", strconv.FormatBool(*force)) + params, err := options.ToParams() + if err != nil { + return err } response, err := conn.DoRequest(nil, http.MethodDelete, "/volumes/%s", params, nil, nameOrID) if err != nil { diff --git a/pkg/cgroups/cgroups.go b/pkg/cgroups/cgroups.go index 0d7ed05b2..c200dd01a 100644 --- a/pkg/cgroups/cgroups.go +++ b/pkg/cgroups/cgroups.go @@ -22,7 +22,7 @@ import ( var ( // ErrCgroupDeleted means the cgroup was deleted ErrCgroupDeleted = errors.New("cgroup deleted") - // ErrCgroupV1Rootless means the cgroup v1 were attempted to be used in rootless environmen + // ErrCgroupV1Rootless means the cgroup v1 were attempted to be used in rootless environment ErrCgroupV1Rootless = errors.New("no support for CGroups V1 in rootless environments") ) diff --git a/pkg/copy/copy.go b/pkg/copy/copy.go deleted file mode 100644 index 13893deb2..000000000 --- a/pkg/copy/copy.go +++ /dev/null @@ -1,220 +0,0 @@ -package copy - -import ( - "io" - "os" - "path/filepath" - "strings" - - buildahCopiah "github.com/containers/buildah/copier" - "github.com/containers/storage/pkg/archive" - securejoin "github.com/cyphar/filepath-securejoin" - "github.com/pkg/errors" -) - -// ********************************* NOTE ************************************* -// -// Most security bugs are caused by attackers playing around with symlinks -// trying to escape from the container onto the host and/or trick into data -// corruption on the host. Hence, file operations on containers (including -// *stat) should always be handled by `github.com/containers/buildah/copier` -// which makes sure to evaluate files in a chroot'ed environment. -// -// Please make sure to add verbose comments when changing code to make the -// lives of future readers easier. -// -// **************************************************************************** - -// Copier copies data from a source to a destination CopyItem. -type Copier struct { - copyFunc func() error - cleanUpFuncs []deferFunc -} - -// cleanUp releases resources the Copier may hold open. -func (c *Copier) cleanUp() { - for _, f := range c.cleanUpFuncs { - f() - } -} - -// Copy data from a source to a destination CopyItem. -func (c *Copier) Copy() error { - defer c.cleanUp() - return c.copyFunc() -} - -// GetCopiers returns a Copier to copy the source item to destination. Use -// extract to untar the source if it's a tar archive. -func GetCopier(source *CopyItem, destination *CopyItem, extract bool) (*Copier, error) { - copier := &Copier{} - - // First, do the man-page dance. See podman-cp(1) for details. - if err := enforceCopyRules(source, destination); err != nil { - return nil, err - } - - // Destination is a stream (e.g., stdout or an http body). - if destination.info.IsStream { - // Source is a stream (e.g., stdin or an http body). - if source.info.IsStream { - copier.copyFunc = func() error { - _, err := io.Copy(destination.writer, source.reader) - return err - } - return copier, nil - } - root, glob, err := source.buildahGlobs() - if err != nil { - return nil, err - } - copier.copyFunc = func() error { - return buildahCopiah.Get(root, "", source.getOptions(), []string{glob}, destination.writer) - } - return copier, nil - } - - // Destination is either a file or a directory. - if source.info.IsStream { - copier.copyFunc = func() error { - return buildahCopiah.Put(destination.root, destination.resolved, source.putOptions(), source.reader) - } - return copier, nil - } - - tarOptions := &archive.TarOptions{ - Compression: archive.Uncompressed, - CopyPass: true, - } - - root := destination.root - dir := destination.resolved - if !source.info.IsDir { - // When copying a file, make sure to rename the - // destination base path. - nameMap := make(map[string]string) - nameMap[filepath.Base(source.resolved)] = filepath.Base(destination.resolved) - tarOptions.RebaseNames = nameMap - dir = filepath.Dir(dir) - } - - var tarReader io.ReadCloser - if extract && archive.IsArchivePath(source.resolved) { - if !destination.info.IsDir { - return nil, errors.Errorf("cannot extract archive %q to file %q", source.original, destination.original) - } - - reader, err := os.Open(source.resolved) - if err != nil { - return nil, err - } - copier.cleanUpFuncs = append(copier.cleanUpFuncs, func() { reader.Close() }) - - // The stream from stdin may be compressed (e.g., via gzip). - decompressedStream, err := archive.DecompressStream(reader) - if err != nil { - return nil, err - } - - copier.cleanUpFuncs = append(copier.cleanUpFuncs, func() { decompressedStream.Close() }) - tarReader = decompressedStream - } else { - reader, err := archive.TarWithOptions(source.resolved, tarOptions) - if err != nil { - return nil, err - } - copier.cleanUpFuncs = append(copier.cleanUpFuncs, func() { reader.Close() }) - tarReader = reader - } - - copier.copyFunc = func() error { - return buildahCopiah.Put(root, dir, source.putOptions(), tarReader) - } - return copier, nil -} - -// enforceCopyRules enforces the rules for copying from a source to a -// destination as mentioned in the podman-cp(1) man page. Please refer to the -// man page and/or the inline comments for further details. Note that source -// and destination are passed by reference and the their data may be changed. -func enforceCopyRules(source, destination *CopyItem) error { - if source.statError != nil { - return source.statError - } - - // We can copy everything to a stream. - if destination.info.IsStream { - return nil - } - - if source.info.IsStream { - if !(destination.info.IsDir || destination.info.IsStream) { - return errors.New("destination must be a directory or stream when copying from a stream") - } - return nil - } - - // Source is a *directory*. - if source.info.IsDir { - if destination.statError != nil { - // It's okay if the destination does not exist. We - // made sure before that it's parent exists, so it - // would be created while copying. - if os.IsNotExist(destination.statError) { - return nil - } - // Could be a permission error. - return destination.statError - } - - // If the destination exists and is not a directory, we have a - // problem. - if !destination.info.IsDir { - return errors.Errorf("cannot copy directory %q to file %q", source.original, destination.original) - } - - // If the destination exists and is a directory, we need to - // append the source base directory to it. This makes sure - // that copying "/foo/bar" "/tmp" will copy to "/tmp/bar" (and - // not "/tmp"). - newDestination, err := securejoin.SecureJoin(destination.resolved, filepath.Base(source.resolved)) - if err != nil { - return err - } - destination.resolved = newDestination - return nil - } - - // Source is a *file*. - if destination.statError != nil { - // It's okay if the destination does not exist, unless it ends - // with "/". - if !os.IsNotExist(destination.statError) { - return destination.statError - } else if strings.HasSuffix(destination.resolved, "/") { - // Note: this is practically unreachable code as the - // existence of parent directories is enforced early - // on. It's left here as an extra security net. - return errors.Errorf("destination directory %q must exist (trailing %q)", destination.original, "/") - } - // Does not exist and does not end with "/". - return nil - } - - // If the destination is a file, we're good. We will overwrite the - // contents while copying. - if !destination.info.IsDir { - return nil - } - - // If the destination exists and is a directory, we need to append the - // source base directory to it. This makes sure that copying - // "/foo/bar" "/tmp" will copy to "/tmp/bar" (and not "/tmp"). - newDestination, err := securejoin.SecureJoin(destination.resolved, filepath.Base(source.resolved)) - if err != nil { - return err - } - - destination.resolved = newDestination - return nil -} diff --git a/pkg/copy/fileinfo.go b/pkg/copy/fileinfo.go index 08b4eb377..ddb9b629c 100644 --- a/pkg/copy/fileinfo.go +++ b/pkg/copy/fileinfo.go @@ -5,6 +5,7 @@ import ( "encoding/json" "net/http" "os" + "path/filepath" "strings" "time" @@ -15,6 +16,10 @@ import ( // base64 encoded JSON payload of stating a path in a container. const XDockerContainerPathStatHeader = "X-Docker-Container-Path-Stat" +// ENOENT mimics the stdlib's ENOENT and can be used to implement custom logic +// while preserving the user-visible error message. +var ENOENT = errors.New("No such file or directory") + // FileInfo describes a file or directory and is returned by // (*CopyItem).Stat(). type FileInfo struct { @@ -23,7 +28,6 @@ type FileInfo struct { Mode os.FileMode `json:"mode"` ModTime time.Time `json:"mtime"` IsDir bool `json:"isDir"` - IsStream bool `json:"isStream"` LinkTarget string `json:"linkTarget"` } @@ -54,3 +58,54 @@ func ExtractFileInfoFromHeader(header *http.Header) (*FileInfo, error) { return &info, nil } + +// ResolveHostPath resolves the specified, possibly relative, path on the host. +func ResolveHostPath(path string) (*FileInfo, error) { + resolvedHostPath, err := filepath.Abs(path) + if err != nil { + return nil, err + } + resolvedHostPath = PreserveBasePath(path, resolvedHostPath) + + statInfo, err := os.Stat(resolvedHostPath) + if err != nil { + if os.IsNotExist(err) { + return nil, ENOENT + } + return nil, err + } + + return &FileInfo{ + Name: statInfo.Name(), + Size: statInfo.Size(), + Mode: statInfo.Mode(), + ModTime: statInfo.ModTime(), + IsDir: statInfo.IsDir(), + LinkTarget: resolvedHostPath, + }, nil +} + +// PreserveBasePath makes sure that the original base path (e.g., "/" or "./") +// is preserved. The filepath API among tends to clean up a bit too much but +// we *must* preserve this data by all means. +func PreserveBasePath(original, resolved string) string { + // Handle "/" + if strings.HasSuffix(original, "/") { + if !strings.HasSuffix(resolved, "/") { + resolved += "/" + } + return resolved + } + + // Handle "/." + if strings.HasSuffix(original, "/.") { + if strings.HasSuffix(resolved, "/") { // could be root! + resolved += "." + } else if !strings.HasSuffix(resolved, "/.") { + resolved += "/." + } + return resolved + } + + return resolved +} diff --git a/pkg/copy/item.go b/pkg/copy/item.go deleted file mode 100644 index df8bf30b9..000000000 --- a/pkg/copy/item.go +++ /dev/null @@ -1,588 +0,0 @@ -package copy - -import ( - "io" - "os" - "path/filepath" - "strings" - - buildahCopiah "github.com/containers/buildah/copier" - "github.com/containers/buildah/pkg/chrootuser" - "github.com/containers/buildah/util" - "github.com/containers/podman/v2/libpod" - "github.com/containers/podman/v2/libpod/define" - "github.com/containers/podman/v2/pkg/cgroups" - "github.com/containers/podman/v2/pkg/rootless" - "github.com/containers/storage" - "github.com/containers/storage/pkg/archive" - "github.com/containers/storage/pkg/idtools" - securejoin "github.com/cyphar/filepath-securejoin" - "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -// ********************************* NOTE ************************************* -// -// Most security bugs are caused by attackers playing around with symlinks -// trying to escape from the container onto the host and/or trick into data -// corruption on the host. Hence, file operations on containers (including -// *stat) should always be handled by `github.com/containers/buildah/copier` -// which makes sure to evaluate files in a chroot'ed environment. -// -// Please make sure to add verbose comments when changing code to make the -// lives of future readers easier. -// -// **************************************************************************** - -var ( - _stdin = os.Stdin.Name() - _stdout = os.Stdout.Name() -) - -// CopyItem is the source or destination of a copy operation. Use the -// CopyItemFrom* functions to create one for the specific source/destination -// item. -type CopyItem struct { - // The original path provided by the caller. Useful in error messages. - original string - // The resolved path on the host or container. Maybe altered at - // multiple stages when copying. - resolved string - // The root for copying data in a chroot'ed environment. - root string - - // IDPair of the resolved path. - idPair *idtools.IDPair - // Storage ID mappings. - idMappings *storage.IDMappingOptions - - // Internal FileInfo. We really don't want users to mess with a - // CopyItem but only plug and play with it. - info FileInfo - // Error when creating the upper FileInfo. Some errors are non-fatal, - // for instance, when a destination *base* path does not exist. - statError error - - writer io.Writer - reader io.Reader - - // Needed to clean up resources (e.g., unmount a container). - cleanUpFuncs []deferFunc -} - -// deferFunc allows for returning functions that must be deferred at call sites. -type deferFunc func() - -// Stat returns the FileInfo. -func (item *CopyItem) Stat() (*FileInfo, error) { - return &item.info, item.statError -} - -// CleanUp releases resources such as the container mounts. It *must* be -// called even in case of errors. -func (item *CopyItem) CleanUp() { - for _, f := range item.cleanUpFuncs { - f() - } -} - -// CopyItemForWriter returns a CopyItem for the specified io.WriteCloser. Note -// that the returned item can only act as a copy destination. -func CopyItemForWriter(writer io.Writer) (item CopyItem, _ error) { - item.writer = writer - item.info.IsStream = true - return item, nil -} - -// CopyItemForReader returns a CopyItem for the specified io.ReaderCloser. Note -// that the returned item can only act as a copy source. -// -// Note that the specified reader will be auto-decompressed if needed. -func CopyItemForReader(reader io.Reader) (item CopyItem, _ error) { - item.info.IsStream = true - decompressed, err := archive.DecompressStream(reader) - if err != nil { - return item, err - } - item.reader = decompressed - item.cleanUpFuncs = append(item.cleanUpFuncs, func() { - if err := decompressed.Close(); err != nil { - logrus.Errorf("Error closing decompressed reader of copy item: %v", err) - } - }) - return item, nil -} - -// CopyItemForHost creates a CopyItem for the specified host path. It's a -// destination by default. Use isSource to set it as a destination. -// -// Note that callers *must* call (CopyItem).CleanUp(), even in case of errors. -func CopyItemForHost(hostPath string, isSource bool) (item CopyItem, _ error) { - if hostPath == "-" { - if isSource { - hostPath = _stdin - } else { - hostPath = _stdout - } - } - - if hostPath == _stdin { - return CopyItemForReader(os.Stdin) - } - - if hostPath == _stdout { - return CopyItemForWriter(os.Stdout) - } - - // Now do the dance for the host data. - resolvedHostPath, err := filepath.Abs(hostPath) - if err != nil { - return item, err - } - - resolvedHostPath = preserveBasePath(hostPath, resolvedHostPath) - item.original = hostPath - item.resolved = resolvedHostPath - item.root = "/" - - statInfo, statError := os.Stat(resolvedHostPath) - item.statError = statError - - // It exists, we're done. - if statError == nil { - item.info.Name = statInfo.Name() - item.info.Size = statInfo.Size() - item.info.Mode = statInfo.Mode() - item.info.ModTime = statInfo.ModTime() - item.info.IsDir = statInfo.IsDir() - item.info.LinkTarget = resolvedHostPath - return item, nil - } - - // The source must exist, but let's try to give some human-friendly - // errors. - if isSource { - if os.IsNotExist(item.statError) { - return item, errors.Wrapf(os.ErrNotExist, "%q could not be found on the host", hostPath) - } - return item, item.statError // could be a permission error - } - - // If we're a destination, we need to make sure that the parent - // directory exists. - parent := filepath.Dir(resolvedHostPath) - if _, err := os.Stat(parent); err != nil { - if os.IsNotExist(err) { - return item, errors.Wrapf(os.ErrNotExist, "%q could not be found on the host", parent) - } - return item, err - } - - return item, nil -} - -// CopyItemForContainer creates a CopyItem for the specified path on the -// container. It's a destination by default. Use isSource to set it as a -// destination. Note that the container path may resolve to a path outside of -// the container's mount point if the path hits a volume or mount on the -// container. -// -// Note that callers *must* call (CopyItem).CleanUp(), even in case of errors. -func CopyItemForContainer(container *libpod.Container, containerPath string, pause bool, isSource bool) (item CopyItem, _ error) { - // Mount and pause the container. - containerMountPoint, err := item.mountAndPauseContainer(container, pause) - if err != nil { - return item, err - } - - // Make sure that "/" copies the *contents* of the mount point and not - // the directory. - if containerPath == "/" { - containerPath += "/." - } - - // Now resolve the container's path. It may hit a volume, it may hit a - // bind mount, it may be relative. - resolvedRoot, resolvedContainerPath, err := resolveContainerPaths(container, containerMountPoint, containerPath) - if err != nil { - return item, err - } - resolvedContainerPath = preserveBasePath(containerPath, resolvedContainerPath) - - idMappings, idPair, err := getIDMappingsAndPair(container, containerMountPoint) - if err != nil { - return item, err - } - - item.original = containerPath - item.resolved = resolvedContainerPath - item.root = resolvedRoot - item.idMappings = idMappings - item.idPair = idPair - - statInfo, statError := secureStat(resolvedRoot, resolvedContainerPath) - item.statError = statError - - // It exists, we're done. - if statError == nil { - item.info.IsDir = statInfo.IsDir - item.info.Name = filepath.Base(statInfo.Name) - item.info.Size = statInfo.Size - item.info.Mode = statInfo.Mode - item.info.ModTime = statInfo.ModTime - item.info.IsDir = statInfo.IsDir - item.info.LinkTarget = resolvedContainerPath - return item, nil - } - - // The source must exist, but let's try to give some human-friendly - // errors. - if isSource { - if os.IsNotExist(statError) { - return item, errors.Wrapf(os.ErrNotExist, "%q could not be found on container %s (resolved to %q)", containerPath, container.ID(), resolvedContainerPath) - } - return item, item.statError // could be a permission error - } - - // If we're a destination, we need to make sure that the parent - // directory exists. - parent := filepath.Dir(resolvedContainerPath) - if _, err := secureStat(resolvedRoot, parent); err != nil { - if os.IsNotExist(err) { - return item, errors.Wrapf(os.ErrNotExist, "%q could not be found on container %s (resolved to %q)", containerPath, container.ID(), resolvedContainerPath) - } - return item, err - } - - return item, nil -} - -// putOptions returns PUT options for buildah's copier package. -func (item *CopyItem) putOptions() buildahCopiah.PutOptions { - options := buildahCopiah.PutOptions{} - if item.idMappings != nil { - options.UIDMap = item.idMappings.UIDMap - options.GIDMap = item.idMappings.GIDMap - } - if item.idPair != nil { - options.ChownDirs = item.idPair - options.ChownFiles = item.idPair - } - return options -} - -// getOptions returns GET options for buildah's copier package. -func (item *CopyItem) getOptions() buildahCopiah.GetOptions { - options := buildahCopiah.GetOptions{} - if item.idMappings != nil { - options.UIDMap = item.idMappings.UIDMap - options.GIDMap = item.idMappings.GIDMap - } - if item.idPair != nil { - options.ChownDirs = item.idPair - options.ChownFiles = item.idPair - } - return options - -} - -// mount and pause the container. Also set the item's cleanUpFuncs. Those -// *must* be invoked by callers, even in case of errors. -func (item *CopyItem) mountAndPauseContainer(container *libpod.Container, pause bool) (string, error) { - // Make sure to pause and unpause the container. We cannot pause on - // cgroupsv1 as rootless user, in which case we turn off pausing. - if pause && rootless.IsRootless() { - cgroupv2, _ := cgroups.IsCgroup2UnifiedMode() - if !cgroupv2 { - logrus.Debugf("Cannot pause container for copying as a rootless user on cgroupsv1: default to not pause") - pause = false - } - } - - // Mount and unmount the container. - mountPoint, err := container.Mount() - if err != nil { - return "", err - } - - item.cleanUpFuncs = append(item.cleanUpFuncs, func() { - if err := container.Unmount(false); err != nil { - logrus.Errorf("Error unmounting container after copy operation: %v", err) - } - }) - - // Pause and unpause the container. - if pause { - if err := container.Pause(); err != nil { - // Ignore errors when the container isn't running. No - // need to pause. - if errors.Cause(err) != define.ErrCtrStateInvalid { - return "", err - } - } else { - item.cleanUpFuncs = append(item.cleanUpFuncs, func() { - if err := container.Unpause(); err != nil { - logrus.Errorf("Error unpausing container after copy operation: %v", err) - } - }) - } - } - - return mountPoint, nil -} - -// buildahGlobs returns the root, dir and glob used in buildah's copier -// package. -// -// Note that dir is always empty. -func (item *CopyItem) buildahGlobs() (root string, glob string, err error) { - root = item.root - - // If the root and the resolved path are equal, then dir must be empty - // and the glob must be ".". - if filepath.Clean(root) == filepath.Clean(item.resolved) { - glob = "." - return - } - - glob, err = filepath.Rel(root, item.resolved) - return -} - -// preserveBasePath makes sure that the original base path (e.g., "/" or "./") -// is preserved. The filepath API among tends to clean up a bit too much but -// we *must* preserve this data by all means. -func preserveBasePath(original, resolved string) string { - // Handle "/" - if strings.HasSuffix(original, "/") { - if !strings.HasSuffix(resolved, "/") { - resolved += "/" - } - return resolved - } - - // Handle "/." - if strings.HasSuffix(original, "/.") { - if strings.HasSuffix(resolved, "/") { // could be root! - resolved += "." - } else if !strings.HasSuffix(resolved, "/.") { - resolved += "/." - } - return resolved - } - - return resolved -} - -// secureStat extracts file info for path in a chroot'ed environment in root. -func secureStat(root string, path string) (*buildahCopiah.StatForItem, error) { - var glob string - var err error - - // If root and path are equal, then dir must be empty and the glob must - // be ".". - if filepath.Clean(root) == filepath.Clean(path) { - glob = "." - } else { - glob, err = filepath.Rel(root, path) - if err != nil { - return nil, err - } - } - - globStats, err := buildahCopiah.Stat(root, "", buildahCopiah.StatOptions{}, []string{glob}) - if err != nil { - return nil, err - } - - if len(globStats) != 1 { - return nil, errors.Errorf("internal libpod error: secureStat: expected 1 item but got %d", len(globStats)) - } - - stat, exists := globStats[0].Results[glob] // only one glob passed, so that's okay - if !exists { - return stat, os.ErrNotExist - } - - var statErr error - if stat.Error != "" { - statErr = errors.New(stat.Error) - } - return stat, statErr -} - -// resolveContainerPaths resolves the container's mount point and the container -// path as specified by the user. Both may resolve to paths outside of the -// container's mount point when the container path hits a volume or bind mount. -// -// NOTE: We must take volumes and bind mounts into account as, regrettably, we -// can copy to/from stopped containers. In that case, the volumes and bind -// mounts are not present. For running containers, the runtime (e.g., runc or -// crun) takes care of these mounts. For stopped ones, we need to do quite -// some dance, as done below. -func resolveContainerPaths(container *libpod.Container, mountPoint string, containerPath string) (string, string, error) { - // Let's first make sure we have a path relative to the mount point. - pathRelativeToContainerMountPoint := containerPath - if !filepath.IsAbs(containerPath) { - // If the containerPath is not absolute, it's relative to the - // container's working dir. To be extra careful, let's first - // join the working dir with "/", and the add the containerPath - // to it. - pathRelativeToContainerMountPoint = filepath.Join(filepath.Join("/", container.WorkingDir()), containerPath) - } - // NOTE: the secure join makes sure that we follow symlinks. This way, - // we catch scenarios where the container path symlinks to a volume or - // bind mount. - resolvedPathOnTheContainerMountPoint, err := securejoin.SecureJoin(mountPoint, pathRelativeToContainerMountPoint) - if err != nil { - return "", "", err - } - pathRelativeToContainerMountPoint = strings.TrimPrefix(pathRelativeToContainerMountPoint, mountPoint) - pathRelativeToContainerMountPoint = filepath.Join("/", pathRelativeToContainerMountPoint) - - // Now we have an "absolute container Path" but not yet resolved on the - // host (e.g., "/foo/bar/file.txt"). As mentioned above, we need to - // check if "/foo/bar/file.txt" is on a volume or bind mount. To do - // that, we need to walk *down* the paths to the root. Assuming - // volume-1 is mounted to "/foo" and volume-2 is mounted to "/foo/bar", - // we must select "/foo/bar". Once selected, we need to rebase the - // remainder (i.e, "/file.txt") on the volume's mount point on the - // host. Same applies to bind mounts. - - searchPath := pathRelativeToContainerMountPoint - for { - volume, err := findVolume(container, searchPath) - if err != nil { - return "", "", err - } - if volume != nil { - logrus.Debugf("Container path %q resolved to volume %q on path %q", containerPath, volume.Name(), searchPath) - // 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) - if err != nil { - return "", "", err - } - return volume.MountPoint(), absolutePathOnTheVolumeMount, nil - } - - if mount := findBindMount(container, searchPath); mount != nil { - logrus.Debugf("Container path %q resolved to bind mount %q:%q on path %q", containerPath, mount.Source, mount.Destination, searchPath) - // We found a matching bind mount for searchPath. We - // now need to first find the relative path of our - // input path to the searchPath, and then join it with - // the source of the bind mount. - pathRelativeToBindMount := strings.TrimPrefix(pathRelativeToContainerMountPoint, searchPath) - absolutePathOnTheBindMount, err := securejoin.SecureJoin(mount.Source, pathRelativeToBindMount) - if err != nil { - return "", "", err - } - return mount.Source, absolutePathOnTheBindMount, nil - - } - - if searchPath == "/" { - // Cannot go beyond "/", so we're done. - break - } - - // Walk *down* the path (e.g., "/foo/bar/x" -> "/foo/bar"). - searchPath = filepath.Dir(searchPath) - } - - // No volume, no bind mount but just a normal path on the container. - return mountPoint, resolvedPathOnTheContainerMountPoint, nil -} - -// findVolume checks if the specified container path matches a volume inside -// the container. It returns a matching volume or nil. -func findVolume(c *libpod.Container, containerPath string) (*libpod.Volume, error) { - runtime := c.Runtime() - cleanedContainerPath := filepath.Clean(containerPath) - for _, vol := range c.Config().NamedVolumes { - if cleanedContainerPath == filepath.Clean(vol.Dest) { - return runtime.GetVolume(vol.Name) - } - } - return nil, nil -} - -// findBindMount checks if the specified container path matches a bind mount -// inside the container. It returns a matching mount or nil. -func findBindMount(c *libpod.Container, containerPath string) *specs.Mount { - cleanedPath := filepath.Clean(containerPath) - for _, m := range c.Config().Spec.Mounts { - if m.Type != "bind" { - continue - } - if cleanedPath == filepath.Clean(m.Destination) { - mount := m - return &mount - } - } - return nil -} - -// getIDMappingsAndPair returns the ID mappings for the container and the host -// ID pair. -func getIDMappingsAndPair(container *libpod.Container, containerMount string) (*storage.IDMappingOptions, *idtools.IDPair, error) { - user, err := getContainerUser(container, containerMount) - if err != nil { - return nil, nil, err - } - - idMappingOpts, err := container.IDMappings() - if err != nil { - return nil, nil, err - } - - hostUID, hostGID, err := util.GetHostIDs(idtoolsToRuntimeSpec(idMappingOpts.UIDMap), idtoolsToRuntimeSpec(idMappingOpts.GIDMap), user.UID, user.GID) - if err != nil { - return nil, nil, err - } - - idPair := idtools.IDPair{UID: int(hostUID), GID: int(hostGID)} - return &idMappingOpts, &idPair, nil -} - -// getContainerUser returns the specs.User of the container. -func getContainerUser(container *libpod.Container, mountPoint string) (specs.User, error) { - userspec := container.Config().User - - uid, gid, _, err := chrootuser.GetUser(mountPoint, userspec) - u := specs.User{ - UID: uid, - GID: gid, - Username: userspec, - } - - if !strings.Contains(userspec, ":") { - groups, err2 := chrootuser.GetAdditionalGroupsForUser(mountPoint, uint64(u.UID)) - if err2 != nil { - if errors.Cause(err2) != chrootuser.ErrNoSuchUser && err == nil { - err = err2 - } - } else { - u.AdditionalGids = groups - } - } - - return u, err -} - -// idtoolsToRuntimeSpec converts idtools ID mapping to the one of the runtime spec. -func idtoolsToRuntimeSpec(idMaps []idtools.IDMap) (convertedIDMap []specs.LinuxIDMapping) { - for _, idmap := range idMaps { - tempIDMap := specs.LinuxIDMapping{ - ContainerID: uint32(idmap.ContainerID), - HostID: uint32(idmap.HostID), - Size: uint32(idmap.Size), - } - convertedIDMap = append(convertedIDMap, tempIDMap) - } - return convertedIDMap -} diff --git a/pkg/domain/entities/container_ps.go b/pkg/domain/entities/container_ps.go index b4e8446cb..ff3b087ed 100644 --- a/pkg/domain/entities/container_ps.go +++ b/pkg/domain/entities/container_ps.go @@ -12,6 +12,8 @@ import ( // Listcontainer describes a container suitable for listing type ListContainer struct { + // AutoRemove + AutoRemove bool // Container command Command []string // Container creation time diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 39d679eaf..05b9b774e 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -8,6 +8,7 @@ import ( "github.com/containers/image/v5/types" "github.com/containers/podman/v2/libpod/define" + "github.com/containers/podman/v2/pkg/copy" "github.com/containers/podman/v2/pkg/specgen" "github.com/cri-o/ocicni/pkg/ocicni" ) @@ -103,9 +104,10 @@ type TopOptions struct { } type KillOptions struct { - All bool - Latest bool - Signal string + All bool + Latest bool + Signal string + CIDFiles []string } type KillReport struct { @@ -143,6 +145,10 @@ type ContainerInspectReport struct { *define.InspectContainerData } +type ContainerStatReport struct { + copy.FileInfo +} + type CommitOptions struct { Author string Changes []string @@ -227,8 +233,10 @@ type ContainerLogsOptions struct { Tail int64 // Show timestamps in the logs. Timestamps bool - // Write the logs to Writer. - Writer io.Writer + // Write the stdout to this Writer. + StdoutWriter io.Writer + // Write the stderr to this Writer. + StderrWriter io.Writer } // ExecOptions describes the cli values to exec into @@ -382,13 +390,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 { diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 5ad475133..7d38a97f2 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -2,13 +2,17 @@ package entities import ( "context" + "io" "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" ) +type ContainerCopyFunc func() error + type ContainerEngine interface { AutoUpdate(ctx context.Context, options AutoUpdateOptions) (*AutoUpdateReport, []error) Config(ctx context.Context) (*config.Config, error) @@ -16,7 +20,8 @@ type ContainerEngine interface { ContainerCheckpoint(ctx context.Context, namesOrIds []string, options CheckpointOptions) ([]*CheckpointReport, error) ContainerCleanup(ctx context.Context, namesOrIds []string, options ContainerCleanupOptions) ([]*ContainerCleanupReport, error) ContainerCommit(ctx context.Context, nameOrID string, options CommitOptions) (*CommitReport, error) - ContainerCp(ctx context.Context, source, dest string, options ContainerCpOptions) error + ContainerCopyFromArchive(ctx context.Context, nameOrID string, path string, reader io.Reader) (ContainerCopyFunc, error) + ContainerCopyToArchive(ctx context.Context, nameOrID string, path string, writer io.Writer) (ContainerCopyFunc, error) ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*ContainerCreateReport, error) ContainerDiff(ctx context.Context, nameOrID string, options DiffOptions) (*DiffReport, error) ContainerExec(ctx context.Context, nameOrID string, options ExecOptions, streams define.AttachStreams) (int, error) @@ -31,13 +36,14 @@ 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) 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) ContainerRun(ctx context.Context, opts ContainerRunOptions) (*ContainerRunReport, error) ContainerRunlabel(ctx context.Context, label string, image string, args []string, opts ContainerRunlabelOptions) error ContainerStart(ctx context.Context, namesOrIds []string, options ContainerStartOptions) ([]*ContainerStartReport, error) + ContainerStat(ctx context.Context, nameOrDir string, path string) (*ContainerStatReport, error) ContainerStats(ctx context.Context, namesOrIds []string, options ContainerStatsOptions) (chan ContainerStatsReport, error) ContainerStop(ctx context.Context, namesOrIds []string, options StopOptions) ([]*StopReport, error) ContainerTop(ctx context.Context, options TopOptions) (*StringSliceReport, error) @@ -80,6 +86,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) ([]*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 594f9617f..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) @@ -35,6 +36,6 @@ type ImageEngine interface { ManifestAdd(ctx context.Context, opts ManifestAddOptions) (string, error) ManifestAnnotate(ctx context.Context, names []string, opts ManifestAnnotateOptions) (string, error) ManifestRemove(ctx context.Context, names []string) (string, error) - ManifestPush(ctx context.Context, names []string, manifestPushOpts ManifestPushOptions) error + ManifestPush(ctx context.Context, name, destination string, manifestPushOpts ManifestPushOptions) error Sign(ctx context.Context, names []string, options SignOptions) (*SignReport, error) } diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index 81f12bff7..d5f88502a 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -222,7 +222,7 @@ type ImageSearchOptions struct { type ImageSearchReport struct { // Index is the image index (e.g., "docker.io" or "quay.io") Index string - // Name is the canoncical name of the image (e.g., "docker.io/library/alpine"). + // Name is the canonical name of the image (e.g., "docker.io/library/alpine"). Name string // Description of the image. Description string @@ -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{} @@ -344,6 +339,7 @@ type SignOptions struct { Directory string SignBy string CertDir string + All bool } // SignReport describes the result of signing 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 bde2b6ef2..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" ) @@ -17,16 +18,18 @@ type ServiceOptions struct { // SystemPruneOptions provides options to prune system. type SystemPruneOptions struct { - All bool - Volume bool + All bool + Volume bool + Filters map[string][]string `json:"filters" schema:"filters"` } // 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/volumes.go b/pkg/domain/entities/volumes.go index 1bc1e4301..06438f5e9 100644 --- a/pkg/domain/entities/volumes.go +++ b/pkg/domain/entities/volumes.go @@ -1,6 +1,7 @@ package entities import ( + "net/url" "time" docker_api_types "github.com/docker/docker/api/types" @@ -109,9 +110,10 @@ type VolumeInspectReport struct { *VolumeConfigResponse } -type VolumePruneReport struct { - Err error - Id string //nolint +// VolumePruneOptions describes the options needed +// to prune a volume from the CLI +type VolumePruneOptions struct { + Filters url.Values `json:"filters" schema:"filters"` } type VolumeListOptions struct { diff --git a/pkg/domain/filters/containers.go b/pkg/domain/filters/containers.go new file mode 100644 index 000000000..ce6c12b71 --- /dev/null +++ b/pkg/domain/filters/containers.go @@ -0,0 +1,238 @@ +package filters + +import ( + "strconv" + "strings" + "time" + + "github.com/containers/podman/v2/libpod" + "github.com/containers/podman/v2/libpod/define" + "github.com/containers/podman/v2/pkg/timetype" + "github.com/containers/podman/v2/pkg/util" + "github.com/pkg/errors" +) + +// GenerateContainerFilterFuncs return ContainerFilter functions based of filter. +func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpod.Runtime) (func(container *libpod.Container) bool, error) { + switch filter { + case "id": + // we only have to match one ID + return func(c *libpod.Container) bool { + return util.StringMatchRegexSlice(c.ID(), filterValues) + }, nil + case "label": + // we have to match that all given labels exits on that container + return func(c *libpod.Container) bool { + labels := c.Labels() + for _, filterValue := range filterValues { + matched := false + filterArray := strings.SplitN(filterValue, "=", 2) + filterKey := filterArray[0] + if len(filterArray) > 1 { + filterValue = filterArray[1] + } else { + filterValue = "" + } + for labelKey, labelValue := range labels { + if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) { + matched = true + break + } + } + if !matched { + return false + } + } + return true + }, nil + case "name": + // we only have to match one name + return func(c *libpod.Container) bool { + return util.StringMatchRegexSlice(c.Name(), filterValues) + }, nil + case "exited": + var exitCodes []int32 + for _, exitCode := range filterValues { + ec, err := strconv.ParseInt(exitCode, 10, 32) + if err != nil { + return nil, errors.Wrapf(err, "exited code out of range %q", ec) + } + exitCodes = append(exitCodes, int32(ec)) + } + return func(c *libpod.Container) bool { + ec, exited, err := c.ExitCode() + if err == nil && exited { + for _, exitCode := range exitCodes { + if ec == exitCode { + return true + } + } + } + return false + }, nil + case "status": + for _, filterValue := range filterValues { + if !util.StringInSlice(filterValue, []string{"created", "running", "paused", "stopped", "exited", "unknown"}) { + return nil, errors.Errorf("%s is not a valid status", filterValue) + } + } + return func(c *libpod.Container) bool { + status, err := c.State() + if err != nil { + return false + } + state := status.String() + if status == define.ContainerStateConfigured { + state = "created" + } else if status == define.ContainerStateStopped { + state = "exited" + } + for _, filterValue := range filterValues { + if filterValue == "stopped" { + filterValue = "exited" + } + if state == filterValue { + return true + } + } + return false + }, nil + case "ancestor": + // This needs to refine to match docker + // - ancestor=(<image-name>[:tag]|<image-id>| ⟨image@digest⟩) - containers created from an image or a descendant. + return func(c *libpod.Container) bool { + for _, filterValue := range filterValues { + containerConfig := c.Config() + if strings.Contains(containerConfig.RootfsImageID, filterValue) || strings.Contains(containerConfig.RootfsImageName, filterValue) { + return true + } + } + return false + }, nil + case "before": + var createTime time.Time + for _, filterValue := range filterValues { + ctr, err := r.LookupContainer(filterValue) + if err != nil { + return nil, err + } + containerConfig := ctr.Config() + if createTime.IsZero() || createTime.After(containerConfig.CreatedTime) { + createTime = containerConfig.CreatedTime + } + } + return func(c *libpod.Container) bool { + cc := c.Config() + return createTime.After(cc.CreatedTime) + }, nil + case "since": + var createTime time.Time + for _, filterValue := range filterValues { + ctr, err := r.LookupContainer(filterValue) + if err != nil { + return nil, err + } + containerConfig := ctr.Config() + if createTime.IsZero() || createTime.After(containerConfig.CreatedTime) { + createTime = containerConfig.CreatedTime + } + } + return func(c *libpod.Container) bool { + cc := c.Config() + return createTime.Before(cc.CreatedTime) + }, nil + case "volume": + //- volume=(<volume-name>|<mount-point-destination>) + return func(c *libpod.Container) bool { + containerConfig := c.Config() + var dest string + for _, filterValue := range filterValues { + arr := strings.SplitN(filterValue, ":", 2) + source := arr[0] + if len(arr) == 2 { + dest = arr[1] + } + for _, mount := range containerConfig.Spec.Mounts { + if dest != "" && (mount.Source == source && mount.Destination == dest) { + return true + } + if dest == "" && mount.Source == source { + return true + } + } + for _, vname := range containerConfig.NamedVolumes { + if dest != "" && (vname.Name == source && vname.Dest == dest) { + return true + } + if dest == "" && vname.Name == source { + return true + } + } + } + return false + }, nil + case "health": + return func(c *libpod.Container) bool { + hcStatus, err := c.HealthCheckStatus() + if err != nil { + return false + } + for _, filterValue := range filterValues { + if hcStatus == filterValue { + return true + } + } + return false + }, nil + case "until": + if len(filterValues) != 1 { + return nil, errors.Errorf("specify exactly one timestamp for %s", filter) + } + ts, err := timetype.GetTimestamp(filterValues[0], time.Now()) + if err != nil { + return nil, err + } + seconds, nanoseconds, err := timetype.ParseTimestamps(ts, 0) + if err != nil { + return nil, err + } + until := time.Unix(seconds, nanoseconds) + return func(c *libpod.Container) bool { + if !until.IsZero() && c.CreatedTime().After((until)) { + return true + } + return false + }, nil + case "pod": + var pods []*libpod.Pod + for _, podNameOrID := range filterValues { + p, err := r.LookupPod(podNameOrID) + if err != nil { + if errors.Cause(err) == define.ErrNoSuchPod { + continue + } + return nil, err + } + pods = append(pods, p) + } + return func(c *libpod.Container) bool { + // if no pods match, quick out + if len(pods) < 1 { + return false + } + // if the container has no pod id, quick out + if len(c.PodID()) < 1 { + return false + } + for _, p := range pods { + // we already looked up by name or id, so id match + // here is ok + if p.ID() == c.PodID() { + return true + } + } + return false + }, nil + } + return nil, errors.Errorf("%s is an invalid filter", filter) +} diff --git a/pkg/domain/filters/helpers.go b/pkg/domain/filters/helpers.go new file mode 100644 index 000000000..6a5fb68b1 --- /dev/null +++ b/pkg/domain/filters/helpers.go @@ -0,0 +1,20 @@ +package filters + +import ( + "net/url" + "strings" + + "github.com/pkg/errors" +) + +func ParseFilterArgumentsIntoFilters(filters []string) (url.Values, error) { + parsedFilters := make(url.Values) + for _, f := range filters { + t := strings.SplitN(f, "=", 2) + if len(t) < 2 { + return parsedFilters, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f) + } + parsedFilters.Add(t[0], t[1]) + } + return parsedFilters, nil +} diff --git a/pkg/domain/filters/pods.go b/pkg/domain/filters/pods.go new file mode 100644 index 000000000..7e6b7f2cc --- /dev/null +++ b/pkg/domain/filters/pods.go @@ -0,0 +1,139 @@ +package filters + +import ( + "strconv" + "strings" + + "github.com/containers/podman/v2/libpod" + "github.com/containers/podman/v2/libpod/define" + "github.com/containers/podman/v2/pkg/util" + "github.com/pkg/errors" +) + +// GeneratePodFilterFunc takes a filter and filtervalue (key, value) +// and generates a libpod function that can be used to filter +// pods +func GeneratePodFilterFunc(filter string, filterValues []string) ( + func(pod *libpod.Pod) bool, error) { + switch filter { + case "ctr-ids": + return func(p *libpod.Pod) bool { + ctrIds, err := p.AllContainersByID() + if err != nil { + return false + } + for _, id := range ctrIds { + return util.StringMatchRegexSlice(id, filterValues) + } + return false + }, nil + case "ctr-names": + return func(p *libpod.Pod) bool { + ctrs, err := p.AllContainers() + if err != nil { + return false + } + for _, ctr := range ctrs { + return util.StringMatchRegexSlice(ctr.Name(), filterValues) + } + return false + }, nil + case "ctr-number": + return func(p *libpod.Pod) bool { + ctrIds, err := p.AllContainersByID() + if err != nil { + return false + } + for _, filterValue := range filterValues { + fVint, err2 := strconv.Atoi(filterValue) + if err2 != nil { + return false + } + if len(ctrIds) == fVint { + return true + } + } + return false + }, nil + case "ctr-status": + for _, filterValue := range filterValues { + if !util.StringInSlice(filterValue, []string{"created", "running", "paused", "stopped", "exited", "unknown"}) { + return nil, errors.Errorf("%s is not a valid status", filterValue) + } + } + return func(p *libpod.Pod) bool { + ctrStatuses, err := p.Status() + if err != nil { + return false + } + for _, ctrStatus := range ctrStatuses { + state := ctrStatus.String() + if ctrStatus == define.ContainerStateConfigured { + state = "created" + } else if ctrStatus == define.ContainerStateStopped { + state = "exited" + } + for _, filterValue := range filterValues { + if filterValue == "stopped" { + filterValue = "exited" + } + if state == filterValue { + return true + } + } + } + return false + }, nil + case "id": + return func(p *libpod.Pod) bool { + return util.StringMatchRegexSlice(p.ID(), filterValues) + }, nil + case "name": + return func(p *libpod.Pod) bool { + return util.StringMatchRegexSlice(p.Name(), filterValues) + }, nil + case "status": + for _, filterValue := range filterValues { + if !util.StringInSlice(filterValue, []string{"stopped", "running", "paused", "exited", "dead", "created", "degraded"}) { + return nil, errors.Errorf("%s is not a valid pod status", filterValue) + } + } + return func(p *libpod.Pod) bool { + status, err := p.GetPodStatus() + if err != nil { + return false + } + for _, filterValue := range filterValues { + if strings.ToLower(status) == filterValue { + return true + } + } + return false + }, nil + case "label": + return func(p *libpod.Pod) bool { + labels := p.Labels() + for _, filterValue := range filterValues { + matched := false + filterArray := strings.SplitN(filterValue, "=", 2) + filterKey := filterArray[0] + if len(filterArray) > 1 { + filterValue = filterArray[1] + } else { + filterValue = "" + } + for labelKey, labelValue := range labels { + if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) { + matched = true + break + } + } + if !matched { + return false + } + } + return true + }, 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 7819d3cdf..69bef4961 100644 --- a/pkg/domain/filters/volumes.go +++ b/pkg/domain/filters/volumes.go @@ -1,13 +1,14 @@ package filters import ( + "net/url" "strings" "github.com/containers/podman/v2/libpod" "github.com/pkg/errors" ) -func GenerateVolumeFilters(filters map[string][]string) ([]libpod.VolumeFilter, error) { +func GenerateVolumeFilters(filters url.Values) ([]libpod.VolumeFilter, error) { var vf []libpod.VolumeFilter for filter, v := range filters { for _, val := range v { diff --git a/pkg/domain/infra/abi/archive.go b/pkg/domain/infra/abi/archive.go new file mode 100644 index 000000000..809813756 --- /dev/null +++ b/pkg/domain/infra/abi/archive.go @@ -0,0 +1,172 @@ +package abi + +import ( + "context" + "io" + "strings" + + buildahCopiah "github.com/containers/buildah/copier" + "github.com/containers/buildah/pkg/chrootuser" + "github.com/containers/buildah/util" + "github.com/containers/podman/v2/libpod" + "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/storage" + "github.com/containers/storage/pkg/archive" + "github.com/containers/storage/pkg/idtools" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// NOTE: Only the parent directory of the container path must exist. The path +// itself may be created while copying. +func (ic *ContainerEngine) ContainerCopyFromArchive(ctx context.Context, nameOrID string, containerPath string, reader io.Reader) (entities.ContainerCopyFunc, error) { + container, err := ic.Libpod.LookupContainer(nameOrID) + if err != nil { + return nil, err + } + + unmount := func() { + if err := container.Unmount(false); err != nil { + logrus.Errorf("Error unmounting container: %v", err) + } + } + + _, resolvedRoot, resolvedContainerPath, err := ic.containerStat(container, containerPath) + if err != nil { + unmount() + return nil, err + } + + decompressed, err := archive.DecompressStream(reader) + if err != nil { + unmount() + return nil, err + } + + idMappings, idPair, err := getIDMappingsAndPair(container, resolvedRoot) + if err != nil { + unmount() + return nil, err + } + + logrus.Debugf("Container copy *to* %q (resolved: %q) on container %q (ID: %s)", containerPath, resolvedContainerPath, container.Name(), container.ID()) + + return func() error { + defer unmount() + defer decompressed.Close() + putOptions := buildahCopiah.PutOptions{ + UIDMap: idMappings.UIDMap, + GIDMap: idMappings.GIDMap, + ChownDirs: idPair, + ChownFiles: idPair, + } + return buildahCopiah.Put(resolvedRoot, resolvedContainerPath, putOptions, decompressed) + }, nil +} + +func (ic *ContainerEngine) ContainerCopyToArchive(ctx context.Context, nameOrID string, containerPath string, writer io.Writer) (entities.ContainerCopyFunc, error) { + container, err := ic.Libpod.LookupContainer(nameOrID) + if err != nil { + return nil, err + } + + unmount := func() { + if err := container.Unmount(false); err != nil { + logrus.Errorf("Error unmounting container: %v", err) + } + } + + // Make sure that "/" copies the *contents* of the mount point and not + // the directory. + if containerPath == "/" { + containerPath = "/." + } + + _, resolvedRoot, resolvedContainerPath, err := ic.containerStat(container, containerPath) + if err != nil { + unmount() + return nil, err + } + + idMappings, idPair, err := getIDMappingsAndPair(container, resolvedRoot) + if err != nil { + unmount() + return nil, err + } + + logrus.Debugf("Container copy *from* %q (resolved: %q) on container %q (ID: %s)", containerPath, resolvedContainerPath, container.Name(), container.ID()) + + return func() error { + defer container.Unmount(false) + getOptions := buildahCopiah.GetOptions{ + // Unless the specified path ends with ".", we want to copy the base directory. + KeepDirectoryNames: !strings.HasSuffix(resolvedContainerPath, "."), + UIDMap: idMappings.UIDMap, + GIDMap: idMappings.GIDMap, + ChownDirs: idPair, + ChownFiles: idPair, + } + return buildahCopiah.Get(resolvedRoot, "", getOptions, []string{resolvedContainerPath}, writer) + }, nil +} + +// getIDMappingsAndPair returns the ID mappings for the container and the host +// ID pair. +func getIDMappingsAndPair(container *libpod.Container, containerMount string) (*storage.IDMappingOptions, *idtools.IDPair, error) { + user, err := getContainerUser(container, containerMount) + if err != nil { + return nil, nil, err + } + + idMappingOpts, err := container.IDMappings() + if err != nil { + return nil, nil, err + } + + hostUID, hostGID, err := util.GetHostIDs(idtoolsToRuntimeSpec(idMappingOpts.UIDMap), idtoolsToRuntimeSpec(idMappingOpts.GIDMap), user.UID, user.GID) + if err != nil { + return nil, nil, err + } + + idPair := idtools.IDPair{UID: int(hostUID), GID: int(hostGID)} + return &idMappingOpts, &idPair, nil +} + +// getContainerUser returns the specs.User of the container. +func getContainerUser(container *libpod.Container, mountPoint string) (specs.User, error) { + userspec := container.Config().User + + uid, gid, _, err := chrootuser.GetUser(mountPoint, userspec) + u := specs.User{ + UID: uid, + GID: gid, + Username: userspec, + } + + if !strings.Contains(userspec, ":") { + groups, err2 := chrootuser.GetAdditionalGroupsForUser(mountPoint, uint64(u.UID)) + if err2 != nil { + if errors.Cause(err2) != chrootuser.ErrNoSuchUser && err == nil { + err = err2 + } + } else { + u.AdditionalGids = groups + } + } + + return u, err +} + +// idtoolsToRuntimeSpec converts idtools ID mapping to the one of the runtime spec. +func idtoolsToRuntimeSpec(idMaps []idtools.IDMap) (convertedIDMap []specs.LinuxIDMapping) { + for _, idmap := range idMaps { + tempIDMap := specs.LinuxIDMapping{ + ContainerID: uint32(idmap.ContainerID), + HostID: uint32(idmap.HostID), + Size: uint32(idmap.Size), + } + convertedIDMap = append(convertedIDMap, tempIDMap) + } + return convertedIDMap +} diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index ff4277a2e..9d7c2daea 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -16,12 +16,13 @@ import ( "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/events" - lpfilters "github.com/containers/podman/v2/libpod/filters" "github.com/containers/podman/v2/libpod/image" "github.com/containers/podman/v2/libpod/logs" "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" "github.com/containers/podman/v2/pkg/ps" @@ -204,31 +205,27 @@ 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 := lpfilters.GenerateContainerFilterFuncs(k, v, ic.Libpod) + generatedFunc, err := dfilters.GenerateContainerFilterFuncs(k, v, ic.Libpod) if err != nil { return nil, err } 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) { + for _, cidFile := range options.CIDFiles { + content, err := ioutil.ReadFile(cidFile) + if err != nil { + return nil, errors.Wrap(err, "error reading CIDFile") + } + id := strings.Split(string(content), "\n")[0] + namesOrIds = append(namesOrIds, id) + } sig, err := signal.ParseSignalNameOrNumber(options.Signal) if err != nil { return nil, err @@ -246,6 +243,7 @@ func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []strin } return reports, nil } + func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []string, options entities.RestartOptions) ([]*entities.RestartReport, error) { var ( ctrs []*libpod.Container @@ -925,7 +923,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta } func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []string, options entities.ContainerLogsOptions) error { - if options.Writer == nil { + if options.StdoutWriter == nil && options.StderrWriter == nil { return errors.New("no io.Writer set for container logs") } @@ -963,7 +961,7 @@ func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []strin }() for line := range logChannel { - fmt.Fprintln(options.Writer, line.String(logOpts)) + line.Write(options.StdoutWriter, options.StderrWriter, logOpts) } return nil diff --git a/pkg/domain/infra/abi/containers_stat.go b/pkg/domain/infra/abi/containers_stat.go new file mode 100644 index 000000000..5b43ee2f4 --- /dev/null +++ b/pkg/domain/infra/abi/containers_stat.go @@ -0,0 +1,251 @@ +package abi + +import ( + "context" + "os" + "path/filepath" + "strings" + + buildahCopiah "github.com/containers/buildah/copier" + "github.com/containers/podman/v2/libpod" + "github.com/containers/podman/v2/pkg/copy" + "github.com/containers/podman/v2/pkg/domain/entities" + securejoin "github.com/cyphar/filepath-securejoin" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +func (ic *ContainerEngine) containerStat(container *libpod.Container, containerPath string) (*entities.ContainerStatReport, string, string, error) { + containerMountPoint, err := container.Mount() + if err != nil { + return nil, "", "", err + } + + // Make sure that "/" copies the *contents* of the mount point and not + // the directory. + if containerPath == "/" { + containerPath += "/." + } + + // Now resolve the container's path. It may hit a volume, it may hit a + // bind mount, it may be relative. + resolvedRoot, resolvedContainerPath, err := resolveContainerPaths(container, containerMountPoint, containerPath) + if err != nil { + return nil, "", "", err + } + + statInfo, statInfoErr := secureStat(resolvedRoot, resolvedContainerPath) + if statInfoErr != nil { + // Not all errors from secureStat map to ErrNotExist, so we + // have to look into the error string. Turning it into an + // ENOENT let's the API handlers return the correct status code + // which is crucial for the remote client. + if os.IsNotExist(err) || strings.Contains(statInfoErr.Error(), "o such file or directory") { + statInfoErr = copy.ENOENT + } + // If statInfo is nil, there's nothing we can do anymore. A + // non-nil statInfo may indicate a symlink where we must have + // a closer look. + if statInfo == nil { + return nil, "", "", statInfoErr + } + } + + // Now make sure that the info's LinkTarget is relative to the + // container's mount. + var absContainerPath string + + if statInfo.IsSymlink { + // Evaluated symlinks are always relative to the container's mount point. + absContainerPath = statInfo.ImmediateTarget + } else if strings.HasPrefix(resolvedContainerPath, containerMountPoint) { + // If the path is on the container's mount point, strip it off. + absContainerPath = strings.TrimPrefix(resolvedContainerPath, containerMountPoint) + absContainerPath = filepath.Join("/", absContainerPath) + } else { + // No symlink and not on the container's mount point, so let's + // move it back to the original input. It must have evaluated + // to a volume or bind mount but we cannot return host paths. + absContainerPath = containerPath + } + + // Now we need to make sure to preserve the base path as specified by + // the user. The `filepath` packages likes to remove trailing slashes + // and dots that are crucial to the copy logic. + absContainerPath = copy.PreserveBasePath(containerPath, absContainerPath) + resolvedContainerPath = copy.PreserveBasePath(containerPath, resolvedContainerPath) + + info := copy.FileInfo{ + IsDir: statInfo.IsDir, + Name: filepath.Base(absContainerPath), + Size: statInfo.Size, + Mode: statInfo.Mode, + ModTime: statInfo.ModTime, + LinkTarget: absContainerPath, + } + + return &entities.ContainerStatReport{FileInfo: info}, resolvedRoot, resolvedContainerPath, statInfoErr +} + +func (ic *ContainerEngine) ContainerStat(ctx context.Context, nameOrID string, containerPath string) (*entities.ContainerStatReport, error) { + container, err := ic.Libpod.LookupContainer(nameOrID) + if err != nil { + return nil, err + } + + defer func() { + if err := container.Unmount(false); err != nil { + logrus.Errorf("Error unmounting container: %v", err) + } + }() + + statReport, _, _, err := ic.containerStat(container, containerPath) + return statReport, err +} + +// resolveContainerPaths resolves the container's mount point and the container +// path as specified by the user. Both may resolve to paths outside of the +// container's mount point when the container path hits a volume or bind mount. +// +// NOTE: We must take volumes and bind mounts into account as, regrettably, we +// can copy to/from stopped containers. In that case, the volumes and bind +// mounts are not present. For running containers, the runtime (e.g., runc or +// crun) takes care of these mounts. For stopped ones, we need to do quite +// some dance, as done below. +func resolveContainerPaths(container *libpod.Container, mountPoint string, containerPath string) (string, string, error) { + // Let's first make sure we have a path relative to the mount point. + pathRelativeToContainerMountPoint := containerPath + if !filepath.IsAbs(containerPath) { + // If the containerPath is not absolute, it's relative to the + // container's working dir. To be extra careful, let's first + // join the working dir with "/", and the add the containerPath + // to it. + pathRelativeToContainerMountPoint = filepath.Join(filepath.Join("/", container.WorkingDir()), containerPath) + } + resolvedPathOnTheContainerMountPoint := filepath.Join(mountPoint, pathRelativeToContainerMountPoint) + pathRelativeToContainerMountPoint = strings.TrimPrefix(pathRelativeToContainerMountPoint, mountPoint) + pathRelativeToContainerMountPoint = filepath.Join("/", pathRelativeToContainerMountPoint) + + // Now we have an "absolute container Path" but not yet resolved on the + // host (e.g., "/foo/bar/file.txt"). As mentioned above, we need to + // check if "/foo/bar/file.txt" is on a volume or bind mount. To do + // that, we need to walk *down* the paths to the root. Assuming + // volume-1 is mounted to "/foo" and volume-2 is mounted to "/foo/bar", + // we must select "/foo/bar". Once selected, we need to rebase the + // remainder (i.e, "/file.txt") on the volume's mount point on the + // host. Same applies to bind mounts. + + searchPath := pathRelativeToContainerMountPoint + for { + volume, err := findVolume(container, searchPath) + if err != nil { + return "", "", err + } + if volume != nil { + logrus.Debugf("Container path %q resolved to volume %q on path %q", containerPath, volume.Name(), searchPath) + // 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) + if err != nil { + return "", "", err + } + return volume.MountPoint(), absolutePathOnTheVolumeMount, nil + } + + if mount := findBindMount(container, searchPath); mount != nil { + logrus.Debugf("Container path %q resolved to bind mount %q:%q on path %q", containerPath, mount.Source, mount.Destination, searchPath) + // We found a matching bind mount for searchPath. We + // now need to first find the relative path of our + // input path to the searchPath, and then join it with + // the source of the bind mount. + pathRelativeToBindMount := strings.TrimPrefix(pathRelativeToContainerMountPoint, searchPath) + absolutePathOnTheBindMount, err := securejoin.SecureJoin(mount.Source, pathRelativeToBindMount) + if err != nil { + return "", "", err + } + return mount.Source, absolutePathOnTheBindMount, nil + + } + + if searchPath == "/" { + // Cannot go beyond "/", so we're done. + break + } + + // Walk *down* the path (e.g., "/foo/bar/x" -> "/foo/bar"). + searchPath = filepath.Dir(searchPath) + } + + // No volume, no bind mount but just a normal path on the container. + return mountPoint, resolvedPathOnTheContainerMountPoint, nil +} + +// findVolume checks if the specified container path matches a volume inside +// the container. It returns a matching volume or nil. +func findVolume(c *libpod.Container, containerPath string) (*libpod.Volume, error) { + runtime := c.Runtime() + cleanedContainerPath := filepath.Clean(containerPath) + for _, vol := range c.Config().NamedVolumes { + if cleanedContainerPath == filepath.Clean(vol.Dest) { + return runtime.GetVolume(vol.Name) + } + } + return nil, nil +} + +// findBindMount checks if the specified container path matches a bind mount +// inside the container. It returns a matching mount or nil. +func findBindMount(c *libpod.Container, containerPath string) *specs.Mount { + cleanedPath := filepath.Clean(containerPath) + for _, m := range c.Config().Spec.Mounts { + if m.Type != "bind" { + continue + } + if cleanedPath == filepath.Clean(m.Destination) { + mount := m + return &mount + } + } + return nil +} + +// secureStat extracts file info for path in a chroot'ed environment in root. +func secureStat(root string, path string) (*buildahCopiah.StatForItem, error) { + var glob string + var err error + + // If root and path are equal, then dir must be empty and the glob must + // be ".". + if filepath.Clean(root) == filepath.Clean(path) { + glob = "." + } else { + glob, err = filepath.Rel(root, path) + if err != nil { + return nil, err + } + } + + globStats, err := buildahCopiah.Stat(root, "", buildahCopiah.StatOptions{}, []string{glob}) + if err != nil { + return nil, err + } + + if len(globStats) != 1 { + return nil, errors.Errorf("internal error: secureStat: expected 1 item but got %d", len(globStats)) + } + + stat, exists := globStats[0].Results[glob] // only one glob passed, so that's okay + if !exists { + return nil, copy.ENOENT + } + + var statErr error + if stat.Error != "" { + statErr = errors.New(stat.Error) + } + return stat, statErr +} diff --git a/pkg/domain/infra/abi/cp.go b/pkg/domain/infra/abi/cp.go deleted file mode 100644 index 362053cce..000000000 --- a/pkg/domain/infra/abi/cp.go +++ /dev/null @@ -1,70 +0,0 @@ -package abi - -import ( - "context" - - "github.com/containers/podman/v2/libpod" - "github.com/containers/podman/v2/pkg/copy" - "github.com/containers/podman/v2/pkg/domain/entities" -) - -func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, options entities.ContainerCpOptions) error { - // Parse user input. - sourceContainerStr, sourcePath, destContainerStr, destPath, err := copy.ParseSourceAndDestination(source, dest) - if err != nil { - return err - } - - // Look up containers. - var sourceContainer, destContainer *libpod.Container - if len(sourceContainerStr) > 0 { - sourceContainer, err = ic.Libpod.LookupContainer(sourceContainerStr) - if err != nil { - return err - } - } - if len(destContainerStr) > 0 { - destContainer, err = ic.Libpod.LookupContainer(destContainerStr) - if err != nil { - return err - } - } - - var sourceItem, destinationItem copy.CopyItem - - // Source ... container OR host. - if sourceContainer != nil { - sourceItem, err = copy.CopyItemForContainer(sourceContainer, sourcePath, options.Pause, true) - defer sourceItem.CleanUp() - if err != nil { - return err - } - } else { - sourceItem, err = copy.CopyItemForHost(sourcePath, true) - if err != nil { - return err - } - } - - // Destination ... container OR host. - if destContainer != nil { - destinationItem, err = copy.CopyItemForContainer(destContainer, destPath, options.Pause, false) - defer destinationItem.CleanUp() - if err != nil { - return err - } - } else { - destinationItem, err = copy.CopyItemForHost(destPath, false) - defer destinationItem.CleanUp() - if err != nil { - return err - } - } - - // Copy from the host to the container. - copier, err := copy.GetCopier(&sourceItem, &destinationItem, options.Extract) - if err != nil { - return err - } - return copier.Copy() -} diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 57a2bc4cf..19f081abb 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -24,10 +24,13 @@ import ( "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" "github.com/containers/storage" + dockerRef "github.com/docker/distribution/reference" + "github.com/opencontainers/go-digest" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -47,19 +50,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) { @@ -718,9 +714,9 @@ func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entitie logrus.Errorf("unable to close %s image source %q", srcRef.DockerReference().Name(), err) } }() - getManifest, _, err := rawSource.GetManifest(ctx, nil) + topManifestBlob, manifestType, err := rawSource.GetManifest(ctx, nil) if err != nil { - return errors.Wrapf(err, "error getting getManifest") + return errors.Wrapf(err, "error getting manifest blob") } dockerReference := rawSource.Reference().DockerReference() if dockerReference == nil { @@ -743,34 +739,34 @@ func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entitie return err } } - manifestDigest, err := manifest.Digest(getManifest) + manifestDigest, err := manifest.Digest(topManifestBlob) if err != nil { return err } - // create signature - newSig, err := signature.SignDockerManifest(getManifest, dockerReference.String(), mech, options.SignBy) - if err != nil { - return errors.Wrapf(err, "error creating new signature") - } - // create the signstore file - signatureDir := fmt.Sprintf("%s@%s=%s", sigStoreDir, manifestDigest.Algorithm(), manifestDigest.Hex()) - if err := os.MkdirAll(signatureDir, 0751); err != nil { - // The directory is allowed to exist - if !os.IsExist(err) { - logrus.Error(err) - return nil + if options.All { + if !manifest.MIMETypeIsMultiImage(manifestType) { + return errors.Errorf("%s is not a multi-architecture image (manifest type %s)", signimage, manifestType) + } + list, err := manifest.ListFromBlob(topManifestBlob, manifestType) + if err != nil { + return errors.Wrapf(err, "Error parsing manifest list %q", string(topManifestBlob)) + } + instanceDigests := list.Instances() + for _, instanceDigest := range instanceDigests { + digest := instanceDigest + man, _, err := rawSource.GetManifest(ctx, &digest) + if err != nil { + return err + } + if err = putSignature(man, mech, sigStoreDir, instanceDigest, dockerReference, options); err != nil { + return errors.Wrapf(err, "error storing signature for %s, %v", dockerReference.String(), instanceDigest) + } } - } - sigFilename, err := getSigFilename(signatureDir) - if err != nil { - logrus.Errorf("error creating sigstore file: %v", err) return nil } - err = ioutil.WriteFile(filepath.Join(signatureDir, sigFilename), newSig, 0644) - if err != nil { - logrus.Errorf("error storing signature for %s", rawSource.Reference().DockerReference().String()) - return nil + if err = putSignature(topManifestBlob, mech, sigStoreDir, manifestDigest, dockerReference, options); err != nil { + return errors.Wrapf(err, "error storing signature for %s, %v", dockerReference.String(), manifestDigest) } return nil }() @@ -806,3 +802,26 @@ func localPathFromURI(url *url.URL) (string, error) { } return url.Path, nil } + +// putSignature creates signature and saves it to the signstore file +func putSignature(manifestBlob []byte, mech signature.SigningMechanism, sigStoreDir string, instanceDigest digest.Digest, dockerReference dockerRef.Reference, options entities.SignOptions) error { + newSig, err := signature.SignDockerManifest(manifestBlob, dockerReference.String(), mech, options.SignBy) + if err != nil { + return err + } + signatureDir := fmt.Sprintf("%s@%s=%s", sigStoreDir, instanceDigest.Algorithm(), instanceDigest.Hex()) + if err := os.MkdirAll(signatureDir, 0751); err != nil { + // The directory is allowed to exist + if !os.IsExist(err) { + return err + } + } + sigFilename, err := getSigFilename(signatureDir) + if err != nil { + return err + } + if err = ioutil.WriteFile(filepath.Join(signatureDir, sigFilename), newSig, 0644); err != nil { + return err + } + return nil +} diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go index 600d64b1d..0c734d10d 100644 --- a/pkg/domain/infra/abi/manifest.go +++ b/pkg/domain/infra/abi/manifest.go @@ -244,15 +244,16 @@ func (ir *ImageEngine) ManifestRemove(ctx context.Context, names []string) (stri } // ManifestPush pushes a manifest list or image index to the destination -func (ir *ImageEngine) ManifestPush(ctx context.Context, names []string, opts entities.ManifestPushOptions) error { - listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(names[0]) +func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination string, opts entities.ManifestPushOptions) error { + listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(name) if err != nil { - return errors.Wrapf(err, "error retrieving local image from image name %s", names[0]) + return errors.Wrapf(err, "error retrieving local image from image name %s", name) } - dest, err := alltransports.ParseImageName(names[1]) + dest, err := alltransports.ParseImageName(destination) if err != nil { return err } + var manifestType string if opts.Format != "" { switch opts.Format { @@ -267,8 +268,8 @@ func (ir *ImageEngine) ManifestPush(ctx context.Context, names []string, opts en // Set the system context. sys := ir.Libpod.SystemContext() - if sys != nil { - sys = &types.SystemContext{} + if sys == nil { + sys = new(types.SystemContext) } sys.AuthFilePath = opts.Authfile sys.DockerInsecureSkipTLSVerify = opts.SkipTLSVerify @@ -296,12 +297,12 @@ func (ir *ImageEngine) ManifestPush(ctx context.Context, names []string, opts en if !opts.Quiet { options.ReportWriter = os.Stderr } - digest, err := listImage.PushManifest(dest, options) + manDigest, err := listImage.PushManifest(dest, options) if err == nil && opts.Purge { _, err = ir.Libpod.GetStore().DeleteImage(listImage.ID(), true) } if opts.DigestFile != "" { - if err = ioutil.WriteFile(opts.DigestFile, []byte(digest.String()), 0644); err != nil { + if err = ioutil.WriteFile(opts.DigestFile, []byte(manDigest.String()), 0644); err != nil { return buildahUtil.GetFailureCause(err, errors.Wrapf(err, "failed to write digest to file %q", opts.DigestFile)) } } diff --git a/pkg/domain/infra/abi/parse/parse.go b/pkg/domain/infra/abi/parse/parse.go index 37568ea11..6a6380e33 100644 --- a/pkg/domain/infra/abi/parse/parse.go +++ b/pkg/domain/infra/abi/parse/parse.go @@ -38,6 +38,9 @@ func VolumeOptions(opts map[string]string) ([]libpod.VolumeCreateOption, error) } logrus.Debugf("Removing uid= from options and adding WithVolumeUID for UID %d", intUID) libpodOptions = append(libpodOptions, libpod.WithVolumeUID(intUID)) + finalVal = append(finalVal, o) + // set option "UID": "$uid" + volumeOptions["UID"] = splitO[1] case "gid": if len(splitO) != 2 { return nil, errors.Wrapf(define.ErrInvalidArg, "gid option must provide a GID") @@ -48,6 +51,9 @@ func VolumeOptions(opts map[string]string) ([]libpod.VolumeCreateOption, error) } logrus.Debugf("Removing gid= from options and adding WithVolumeGID for GID %d", intGID) libpodOptions = append(libpodOptions, libpod.WithVolumeGID(intGID)) + finalVal = append(finalVal, o) + // set option "GID": "$gid" + volumeOptions["GID"] = splitO[1] default: finalVal = append(finalVal, o) } diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index 3aeb6a2ee..cbc74a2f2 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -212,8 +212,10 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY return nil, errors.Wrapf(err, "Failed to parse image %q", container.Image) } // In kube, if the image is tagged with latest, it should always pull + // but if the domain is localhost, that means the image was built locally + // so do not attempt a pull. if tagged, isTagged := named.(reference.NamedTagged); isTagged { - if tagged.Tag() == image.LatestTag { + if tagged.Tag() == image.LatestTag && reference.Domain(named) != image.DefaultLocalRegistry { pullPolicy = util.PullImageAlways } } @@ -224,7 +226,19 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY return nil, err } - specGen, err := kube.ToSpecGen(ctx, container, container.Image, newImage, volumes, pod.ID(), podName, podInfraID, configMaps, seccompPaths, ctrRestartPolicy) + specgenOpts := kube.CtrSpecGenOptions{ + Container: container, + Image: newImage, + Volumes: volumes, + PodID: pod.ID(), + PodName: podName, + PodInfraID: podInfraID, + ConfigMaps: configMaps, + SeccompPaths: seccompPaths, + RestartPolicy: ctrRestartPolicy, + NetNSIsHost: p.NetNS.IsHost(), + } + specGen, err := kube.ToSpecGen(ctx, &specgenOpts) if err != nil { return nil, err } diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go index 11374e513..f108b770c 100644 --- a/pkg/domain/infra/abi/pods.go +++ b/pkg/domain/infra/abi/pods.go @@ -5,8 +5,8 @@ import ( "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" - lpfilters "github.com/containers/podman/v2/libpod/filters" "github.com/containers/podman/v2/pkg/domain/entities" + dfilters "github.com/containers/podman/v2/pkg/domain/filters" "github.com/containers/podman/v2/pkg/signal" "github.com/containers/podman/v2/pkg/specgen" "github.com/containers/podman/v2/pkg/specgen/generate" @@ -288,7 +288,7 @@ func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOpti filters := make([]libpod.PodFilter, 0, len(options.Filters)) for k, v := range options.Filters { - f, err := lpfilters.GeneratePodFilterFunc(k, v) + f, err := dfilters.GeneratePodFilterFunc(k, v) if err != nil { return nil, err } diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index 7ed58092b..67c018122 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io/ioutil" + "net/url" "os" "os/exec" "path/filepath" @@ -15,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" @@ -168,6 +170,8 @@ func checkInput() error { // nolint:deadcode,unused // 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 @@ -179,51 +183,46 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys found = true } systemPruneReport.PodPruneReport = append(systemPruneReport.PodPruneReport, podPruneReport...) - containerPruneReport, err := ic.pruneContainersHelper(nil) + + // TODO: Figure out cleaner way to handle all of the different PruneOptions + containerPruneOptions := entities.ContainerPruneOptions{} + containerPruneOptions.Filters = (url.Values)(options.Filters) + + 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, nil) + 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 { - volumePruneReport, err := ic.pruneVolumesHelper(ctx) + volumePruneOptions := entities.VolumePruneOptions{} + volumePruneOptions.Filters = (url.Values)(options.Filters) + volumePruneReport, err := ic.VolumePrune(ctx, volumePruneOptions) if err != nil { return nil, err } 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 } diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go index a7262f61b..3c9dd9fc0 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" @@ -127,23 +128,20 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin return reports, errs, nil } -func (ic *ContainerEngine) VolumePrune(ctx context.Context) ([]*entities.VolumePruneReport, error) { - return ic.pruneVolumesHelper(ctx) +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 + } + return ic.pruneVolumesHelper(ctx, filterFuncs) } -func (ic *ContainerEngine) pruneVolumesHelper(ctx context.Context) ([]*entities.VolumePruneReport, error) { - pruned, err := ic.Libpod.PruneVolumes(ctx) +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) { diff --git a/pkg/domain/infra/runtime_abi.go b/pkg/domain/infra/runtime_abi.go index 3a64cb72a..a25d165c9 100644 --- a/pkg/domain/infra/runtime_abi.go +++ b/pkg/domain/infra/runtime_abi.go @@ -21,7 +21,7 @@ func NewContainerEngine(facts *entities.PodmanConfig) (entities.ContainerEngine, return r, err case entities.TunnelMode: ctx, err := bindings.NewConnectionWithIdentity(context.Background(), facts.URI, facts.Identity) - return &tunnel.ContainerEngine{ClientCxt: ctx}, err + return &tunnel.ContainerEngine{ClientCtx: ctx}, err } return nil, fmt.Errorf("runtime mode '%v' is not supported", facts.EngineMode) } @@ -34,7 +34,7 @@ func NewImageEngine(facts *entities.PodmanConfig) (entities.ImageEngine, error) return r, err case entities.TunnelMode: ctx, err := bindings.NewConnectionWithIdentity(context.Background(), facts.URI, facts.Identity) - return &tunnel.ImageEngine{ClientCxt: ctx}, err + return &tunnel.ImageEngine{ClientCtx: ctx}, err } return nil, fmt.Errorf("runtime mode '%v' is not supported", facts.EngineMode) } diff --git a/pkg/domain/infra/runtime_tunnel.go b/pkg/domain/infra/runtime_tunnel.go index 6c85e837e..b8aefaa35 100644 --- a/pkg/domain/infra/runtime_tunnel.go +++ b/pkg/domain/infra/runtime_tunnel.go @@ -5,19 +5,39 @@ package infra import ( "context" "fmt" + "sync" "github.com/containers/podman/v2/pkg/bindings" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/containers/podman/v2/pkg/domain/infra/tunnel" ) +var ( + connectionMutex = &sync.Mutex{} + connection *context.Context +) + +func newConnection(uri string, identity string) (context.Context, error) { + connectionMutex.Lock() + defer connectionMutex.Unlock() + + if connection == nil { + ctx, err := bindings.NewConnectionWithIdentity(context.Background(), uri, identity) + if err != nil { + return ctx, err + } + connection = &ctx + } + return *connection, nil +} + func NewContainerEngine(facts *entities.PodmanConfig) (entities.ContainerEngine, error) { switch facts.EngineMode { case entities.ABIMode: return nil, fmt.Errorf("direct runtime not supported") case entities.TunnelMode: - ctx, err := bindings.NewConnectionWithIdentity(context.Background(), facts.URI, facts.Identity) - return &tunnel.ContainerEngine{ClientCxt: ctx}, err + ctx, err := newConnection(facts.URI, facts.Identity) + return &tunnel.ContainerEngine{ClientCtx: ctx}, err } return nil, fmt.Errorf("runtime mode '%v' is not supported", facts.EngineMode) } @@ -28,8 +48,8 @@ func NewImageEngine(facts *entities.PodmanConfig) (entities.ImageEngine, error) case entities.ABIMode: return nil, fmt.Errorf("direct image runtime not supported") case entities.TunnelMode: - ctx, err := bindings.NewConnectionWithIdentity(context.Background(), facts.URI, facts.Identity) - return &tunnel.ImageEngine{ClientCxt: ctx}, err + ctx, err := newConnection(facts.URI, facts.Identity) + return &tunnel.ImageEngine{ClientCtx: ctx}, err } return nil, fmt.Errorf("runtime mode '%v' is not supported", facts.EngineMode) } diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index e65fef0a4..3366cb425 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -16,9 +16,9 @@ import ( "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/events" "github.com/containers/podman/v2/pkg/api/handlers" - "github.com/containers/podman/v2/pkg/bindings" "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" @@ -31,19 +31,20 @@ func (ic *ContainerEngine) ContainerRunlabel(ctx context.Context, label string, } func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string, options entities.ContainerExistsOptions) (*entities.BoolReport, error) { - exists, err := containers.Exists(ic.ClientCxt, nameOrID, options.External) + exists, err := containers.Exists(ic.ClientCtx, nameOrID, options.External) return &entities.BoolReport{Value: exists}, err } -func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []string, options entities.WaitOptions) ([]entities.WaitReport, error) { - cons, err := getContainersByContext(ic.ClientCxt, false, false, namesOrIds) +func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []string, opts entities.WaitOptions) ([]entities.WaitReport, error) { + cons, err := getContainersByContext(ic.ClientCtx, false, false, namesOrIds) if err != nil { return nil, err } responses := make([]entities.WaitReport, 0, len(cons)) + options := new(containers.WaitOptions).WithCondition(opts.Condition) for _, c := range cons { response := entities.WaitReport{Id: c.ID} - exitCode, err := containers.Wait(ic.ClientCxt, c.ID, &options.Condition) + exitCode, err := containers.Wait(ic.ClientCtx, c.ID, options) if err != nil { response.Error = err } else { @@ -55,34 +56,34 @@ func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []strin } func (ic *ContainerEngine) ContainerPause(ctx context.Context, namesOrIds []string, options entities.PauseUnPauseOptions) ([]*entities.PauseUnpauseReport, error) { - ctrs, err := getContainersByContext(ic.ClientCxt, options.All, false, namesOrIds) + ctrs, err := getContainersByContext(ic.ClientCtx, options.All, false, namesOrIds) if err != nil { return nil, err } reports := make([]*entities.PauseUnpauseReport, 0, len(ctrs)) for _, c := range ctrs { - err := containers.Pause(ic.ClientCxt, c.ID) + err := containers.Pause(ic.ClientCtx, c.ID, nil) reports = append(reports, &entities.PauseUnpauseReport{Id: c.ID, Err: err}) } return reports, nil } func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []string, options entities.PauseUnPauseOptions) ([]*entities.PauseUnpauseReport, error) { - ctrs, err := getContainersByContext(ic.ClientCxt, options.All, false, namesOrIds) + ctrs, err := getContainersByContext(ic.ClientCtx, options.All, false, namesOrIds) if err != nil { return nil, err } reports := make([]*entities.PauseUnpauseReport, 0, len(ctrs)) for _, c := range ctrs { - err := containers.Unpause(ic.ClientCxt, c.ID) + err := containers.Unpause(ic.ClientCtx, c.ID, nil) reports = append(reports, &entities.PauseUnpauseReport{Id: c.ID, Err: err}) } return reports, nil } -func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []string, options entities.StopOptions) ([]*entities.StopReport, error) { +func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []string, opts entities.StopOptions) ([]*entities.StopReport, error) { reports := []*entities.StopReport{} - for _, cidFile := range options.CIDFiles { + for _, cidFile := range opts.CIDFiles { content, err := ioutil.ReadFile(cidFile) if err != nil { return nil, errors.Wrap(err, "error reading CIDFile") @@ -90,19 +91,23 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin id := strings.Split(string(content), "\n")[0] namesOrIds = append(namesOrIds, id) } - ctrs, err := getContainersByContext(ic.ClientCxt, options.All, options.Ignore, namesOrIds) + ctrs, err := getContainersByContext(ic.ClientCtx, opts.All, opts.Ignore, namesOrIds) if err != nil { return nil, err } + options := new(containers.StopOptions) + if to := opts.Timeout; to != nil { + options.WithTimeout(*to) + } for _, c := range ctrs { report := entities.StopReport{Id: c.ID} - if err = containers.Stop(ic.ClientCxt, c.ID, options.Timeout); err != nil { + if err = containers.Stop(ic.ClientCtx, c.ID, options); err != nil { // These first two are considered non-fatal under the right conditions if errors.Cause(err).Error() == define.ErrCtrStopped.Error() { logrus.Debugf("Container %s is already stopped", c.ID) reports = append(reports, &report) continue - } else if options.All && errors.Cause(err).Error() == define.ErrCtrStateInvalid.Error() { + } else if opts.All && errors.Cause(err).Error() == define.ErrCtrStateInvalid.Error() { logrus.Debugf("Container %s is not running, could not stop", c.ID) reports = append(reports, &report) continue @@ -120,8 +125,16 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin return reports, nil } -func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []string, options entities.KillOptions) ([]*entities.KillReport, error) { - ctrs, err := getContainersByContext(ic.ClientCxt, options.All, false, namesOrIds) +func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []string, opts entities.KillOptions) ([]*entities.KillReport, error) { + for _, cidFile := range opts.CIDFiles { + content, err := ioutil.ReadFile(cidFile) + if err != nil { + return nil, errors.Wrap(err, "error reading CIDFile") + } + id := strings.Split(string(content), "\n")[0] + namesOrIds = append(namesOrIds, id) + } + ctrs, err := getContainersByContext(ic.ClientCtx, opts.All, false, namesOrIds) if err != nil { return nil, err } @@ -129,40 +142,38 @@ func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []strin for _, c := range ctrs { reports = append(reports, &entities.KillReport{ Id: c.ID, - Err: containers.Kill(ic.ClientCxt, c.ID, options.Signal), + Err: containers.Kill(ic.ClientCtx, c.ID, opts.Signal, nil), }) } return reports, nil } -func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []string, options entities.RestartOptions) ([]*entities.RestartReport, error) { +func (ic *ContainerEngine) ContainerRestart(ctx context.Context, namesOrIds []string, opts entities.RestartOptions) ([]*entities.RestartReport, error) { var ( reports = []*entities.RestartReport{} - timeout *int ) - if options.Timeout != nil { - t := int(*options.Timeout) - timeout = &t + options := new(containers.RestartOptions) + if to := opts.Timeout; to != nil { + options.WithTimeout(int(*to)) } - - ctrs, err := getContainersByContext(ic.ClientCxt, options.All, false, namesOrIds) + ctrs, err := getContainersByContext(ic.ClientCtx, opts.All, false, namesOrIds) if err != nil { return nil, err } for _, c := range ctrs { - if options.Running && c.State != define.ContainerStateRunning.String() { + if opts.Running && c.State != define.ContainerStateRunning.String() { continue } reports = append(reports, &entities.RestartReport{ Id: c.ID, - Err: containers.Restart(ic.ClientCxt, c.ID, timeout), + Err: containers.Restart(ic.ClientCtx, c.ID, options), }) } return reports, nil } -func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, options entities.RmOptions) ([]*entities.RmReport, error) { - for _, cidFile := range options.CIDFiles { +func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, opts entities.RmOptions) ([]*entities.RmReport, error) { + for _, cidFile := range opts.CIDFiles { content, err := ioutil.ReadFile(cidFile) if err != nil { return nil, errors.Wrap(err, "error reading CIDFile") @@ -170,32 +181,35 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, id := strings.Split(string(content), "\n")[0] namesOrIds = append(namesOrIds, id) } - ctrs, err := getContainersByContext(ic.ClientCxt, options.All, options.Ignore, namesOrIds) + ctrs, err := getContainersByContext(ic.ClientCtx, opts.All, opts.Ignore, namesOrIds) if err != nil { return nil, err } // TODO there is no endpoint for container eviction. Need to discuss reports := make([]*entities.RmReport, 0, len(ctrs)) + options := new(containers.RemoveOptions).WithForce(opts.Force).WithVolumes(opts.Volumes) for _, c := range ctrs { reports = append(reports, &entities.RmReport{ Id: c.ID, - Err: containers.Remove(ic.ClientCxt, c.ID, &options.Force, &options.Volumes), + Err: containers.Remove(ic.ClientCtx, c.ID, options), }) } return reports, nil } -func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities.ContainerPruneOptions) (*entities.ContainerPruneReport, error) { - return containers.Prune(ic.ClientCxt, options.Filters) +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) } -func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]*entities.ContainerInspectReport, []error, error) { +func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, opts entities.InspectOptions) ([]*entities.ContainerInspectReport, []error, error) { var ( reports = make([]*entities.ContainerInspectReport, 0, len(namesOrIds)) errs = []error{} ) + options := new(containers.InspectOptions).WithSize(opts.Size) for _, name := range namesOrIds { - inspect, err := containers.Inspect(ic.ClientCxt, name, &options.Size) + inspect, err := containers.Inspect(ic.ClientCtx, name, options) if err != nil { errModel, ok := err.(entities.ErrorModel) if !ok { @@ -212,30 +226,30 @@ func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []st return reports, errs, nil } -func (ic *ContainerEngine) ContainerTop(ctx context.Context, options entities.TopOptions) (*entities.StringSliceReport, error) { +func (ic *ContainerEngine) ContainerTop(ctx context.Context, opts entities.TopOptions) (*entities.StringSliceReport, error) { switch { - case options.Latest: + case opts.Latest: return nil, errors.New("latest is not supported") - case options.NameOrID == "": + case opts.NameOrID == "": return nil, errors.New("NameOrID must be specified") } - - topOutput, err := containers.Top(ic.ClientCxt, options.NameOrID, options.Descriptors) + options := new(containers.TopOptions).WithDescriptors(opts.Descriptors) + topOutput, err := containers.Top(ic.ClientCtx, opts.NameOrID, options) if err != nil { return nil, err } return &entities.StringSliceReport{Value: topOutput}, nil } -func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrID string, options entities.CommitOptions) (*entities.CommitReport, error) { +func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrID string, opts entities.CommitOptions) (*entities.CommitReport, error) { var ( repo string tag = "latest" ) - if len(options.ImageName) > 0 { - ref, err := reference.Parse(options.ImageName) + if len(opts.ImageName) > 0 { + ref, err := reference.Parse(opts.ImageName) if err != nil { - return nil, errors.Wrapf(err, "error parsing reference %q", options.ImageName) + return nil, errors.Wrapf(err, "error parsing reference %q", opts.ImageName) } if t, ok := ref.(reference.Tagged); ok { tag = t.Tag() @@ -244,19 +258,12 @@ func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrID string, repo = r.Name() } if len(repo) < 1 { - return nil, errors.Errorf("invalid image name %q", options.ImageName) + return nil, errors.Errorf("invalid image name %q", opts.ImageName) } } - commitOpts := containers.CommitOptions{ - Author: &options.Author, - Changes: options.Changes, - Comment: &options.Message, - Format: &options.Format, - Pause: &options.Pause, - Repo: &repo, - Tag: &tag, - } - response, err := containers.Commit(ic.ClientCxt, nameOrID, commitOpts) + options := new(containers.CommitOptions).WithAuthor(opts.Author).WithChanges(opts.Changes).WithComment(opts.Message) + options.WithFormat(opts.Format).WithPause(opts.Pause).WithRepo(repo).WithTag(tag) + response, err := containers.Commit(ic.ClientCtx, nameOrID, options) if err != nil { return nil, err } @@ -274,17 +281,17 @@ func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrID string, return err } } - return containers.Export(ic.ClientCxt, nameOrID, w) + return containers.Export(ic.ClientCtx, nameOrID, w, nil) } -func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds []string, options entities.CheckpointOptions) ([]*entities.CheckpointReport, error) { +func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds []string, opts entities.CheckpointOptions) ([]*entities.CheckpointReport, error) { var ( err error ctrs = []entities.ListContainer{} ) - if options.All { - allCtrs, err := getContainersByContext(ic.ClientCxt, true, false, []string{}) + if opts.All { + allCtrs, err := getContainersByContext(ic.ClientCtx, true, false, []string{}) if err != nil { return nil, err } @@ -296,14 +303,16 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [ } } else { - ctrs, err = getContainersByContext(ic.ClientCxt, false, false, namesOrIds) + ctrs, err = getContainersByContext(ic.ClientCtx, false, false, namesOrIds) if err != nil { return nil, err } } reports := make([]*entities.CheckpointReport, 0, len(ctrs)) + options := new(containers.CheckpointOptions).WithExport(opts.Export).WithIgnoreRootfs(opts.IgnoreRootFS).WithKeep(opts.Keep) + options.WithLeaveRunning(opts.LeaveRunning).WithTCPEstablished(opts.TCPEstablished) for _, c := range ctrs { - report, err := containers.Checkpoint(ic.ClientCxt, c.ID, &options.Keep, &options.LeaveRunning, &options.TCPEstablished, &options.IgnoreRootFS, &options.Export) + report, err := containers.Checkpoint(ic.ClientCtx, c.ID, options) if err != nil { reports = append(reports, &entities.CheckpointReport{Id: c.ID, Err: err}) } @@ -312,13 +321,13 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [ return reports, nil } -func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []string, options entities.RestoreOptions) ([]*entities.RestoreReport, error) { +func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []string, opts entities.RestoreOptions) ([]*entities.RestoreReport, error) { var ( err error ctrs = []entities.ListContainer{} ) - if options.All { - allCtrs, err := getContainersByContext(ic.ClientCxt, true, false, []string{}) + if opts.All { + allCtrs, err := getContainersByContext(ic.ClientCtx, true, false, []string{}) if err != nil { return nil, err } @@ -330,14 +339,15 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st } } else { - ctrs, err = getContainersByContext(ic.ClientCxt, false, false, namesOrIds) + ctrs, err = getContainersByContext(ic.ClientCtx, false, false, namesOrIds) if err != nil { return nil, err } } reports := make([]*entities.RestoreReport, 0, len(ctrs)) + options := new(containers.RestoreOptions) for _, c := range ctrs { - report, err := containers.Restore(ic.ClientCxt, c.ID, &options.Keep, &options.TCPEstablished, &options.IgnoreRootFS, &options.IgnoreStaticIP, &options.IgnoreStaticMAC, &options.Name, &options.Import) + report, err := containers.Restore(ic.ClientCtx, c.ID, options) if err != nil { reports = append(reports, &entities.RestoreReport{Id: c.ID, Err: err}) } @@ -347,7 +357,7 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st } func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*entities.ContainerCreateReport, error) { - response, err := containers.CreateWithSpec(ic.ClientCxt, s) + response, err := containers.CreateWithSpec(ic.ClientCtx, s, nil) if err != nil { return nil, err } @@ -357,25 +367,20 @@ func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecG return &entities.ContainerCreateReport{Id: response.ID}, nil } -func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIDs []string, options entities.ContainerLogsOptions) error { - since := options.Since.Format(time.RFC3339) - tail := strconv.FormatInt(options.Tail, 10) - stdout := options.Writer != nil - opts := containers.LogOptions{ - Follow: &options.Follow, - Since: &since, - Stderr: &stdout, - Stdout: &stdout, - Tail: &tail, - Timestamps: &options.Timestamps, - Until: nil, - } +func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIDs []string, opts entities.ContainerLogsOptions) error { + since := opts.Since.Format(time.RFC3339) + tail := strconv.FormatInt(opts.Tail, 10) + stdout := opts.StdoutWriter != nil + stderr := opts.StderrWriter != nil + options := new(containers.LogOptions).WithFollow(opts.Follow).WithSince(since).WithStderr(stderr) + options.WithStdout(stdout).WithTail(tail) var err error - outCh := make(chan string) + stdoutCh := make(chan string) + stderrCh := make(chan string) ctx, cancel := context.WithCancel(context.Background()) go func() { - err = containers.Logs(ic.ClientCxt, nameOrIDs[0], opts, outCh, outCh) + err = containers.Logs(ic.ClientCtx, nameOrIDs[0], options, stdoutCh, stderrCh) cancel() }() @@ -383,14 +388,20 @@ func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIDs []string, select { case <-ctx.Done(): return err - case line := <-outCh: - _, _ = io.WriteString(options.Writer, line+"\n") + case line := <-stdoutCh: + if opts.StdoutWriter != nil { + _, _ = io.WriteString(opts.StdoutWriter, line+"\n") + } + case line := <-stderrCh: + if opts.StderrWriter != nil { + _, _ = io.WriteString(opts.StderrWriter, line+"\n") + } } } } -func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrID string, options entities.AttachOptions) error { - ctrs, err := getContainersByContext(ic.ClientCxt, false, false, []string{nameOrID}) +func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrID string, opts entities.AttachOptions) error { + ctrs, err := getContainersByContext(ic.ClientCtx, false, false, []string{nameOrID}) if err != nil { return err } @@ -398,8 +409,8 @@ func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrID string, if ctr.State != define.ContainerStateRunning.String() { return errors.Errorf("you can only attach to running containers") } - - return containers.Attach(ic.ClientCxt, nameOrID, &options.DetachKeys, nil, bindings.PTrue, options.Stdin, options.Stdout, options.Stderr, nil) + options := new(containers.AttachOptions).WithStream(true).WithDetachKeys(opts.DetachKeys) + return containers.Attach(ic.ClientCtx, nameOrID, opts.Stdin, opts.Stdout, opts.Stderr, nil, options) } func makeExecConfig(options entities.ExecOptions) *handlers.ExecCreateConfig { @@ -427,16 +438,21 @@ func makeExecConfig(options entities.ExecOptions) *handlers.ExecCreateConfig { func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrID string, options entities.ExecOptions, streams define.AttachStreams) (int, error) { createConfig := makeExecConfig(options) - sessionID, err := containers.ExecCreate(ic.ClientCxt, nameOrID, createConfig) + sessionID, err := containers.ExecCreate(ic.ClientCtx, nameOrID, createConfig) if err != nil { return 125, err } - - if err := containers.ExecStartAndAttach(ic.ClientCxt, sessionID, &streams); err != nil { + startAndAttachOptions := new(containers.ExecStartAndAttachOptions) + startAndAttachOptions.WithOutputStream(streams.OutputStream).WithErrorStream(streams.ErrorStream) + if streams.InputStream != nil { + startAndAttachOptions.WithInputStream(*streams.InputStream) + } + startAndAttachOptions.WithAttachError(streams.AttachError).WithAttachOutput(streams.AttachOutput).WithAttachInput(streams.AttachInput) + if err := containers.ExecStartAndAttach(ic.ClientCtx, sessionID, startAndAttachOptions); err != nil { return 125, err } - inspectOut, err := containers.ExecInspect(ic.ClientCxt, sessionID) + inspectOut, err := containers.ExecInspect(ic.ClientCtx, sessionID, nil) if err != nil { return 125, err } @@ -447,12 +463,12 @@ func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrID string, o func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID string, options entities.ExecOptions) (string, error) { createConfig := makeExecConfig(options) - sessionID, err := containers.ExecCreate(ic.ClientCxt, nameOrID, createConfig) + sessionID, err := containers.ExecCreate(ic.ClientCtx, nameOrID, createConfig) if err != nil { return "", err } - if err := containers.ExecStart(ic.ClientCxt, sessionID); err != nil { + if err := containers.ExecStart(ic.ClientCtx, sessionID, nil); err != nil { return "", err } @@ -462,15 +478,23 @@ func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID s func startAndAttach(ic *ContainerEngine, name string, detachKeys *string, input, output, errput *os.File) error { //nolint attachErr := make(chan error) attachReady := make(chan bool) + options := new(containers.AttachOptions).WithStream(true) + if dk := detachKeys; dk != nil { + options.WithDetachKeys(*dk) + } go func() { - err := containers.Attach(ic.ClientCxt, name, detachKeys, bindings.PFalse, bindings.PTrue, input, output, errput, attachReady) + err := containers.Attach(ic.ClientCtx, name, input, output, errput, attachReady, options) attachErr <- err }() // Wait for the attach to actually happen before starting // the container. select { case <-attachReady: - if err := containers.Start(ic.ClientCxt, name, detachKeys); err != nil { + startOptions := new(containers.StartOptions) + if dk := detachKeys; dk != nil { + startOptions.WithDetachKeys(*dk) + } + if err := containers.Start(ic.ClientCtx, name, startOptions); err != nil { return err } case err := <-attachErr: @@ -483,10 +507,11 @@ func startAndAttach(ic *ContainerEngine, name string, detachKeys *string, input, func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) { reports := []*entities.ContainerStartReport{} var exitCode = define.ExecErrorCodeGeneric - ctrs, err := getContainersByContext(ic.ClientCxt, false, false, namesOrIds) + ctrs, err := getContainersByContext(ic.ClientCtx, false, false, namesOrIds) if err != nil { return nil, err } + removeOptions := new(containers.RemoveOptions).WithVolumes(true).WithForce(false) // There can only be one container if attach was used for i, ctr := range ctrs { name := ctr.ID @@ -515,7 +540,30 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri reports = append(reports, &report) return reports, errors.Wrapf(report.Err, "unable to start container %s", name) } - exitCode, err := containers.Wait(ic.ClientCxt, name, nil) + if ctr.AutoRemove { + // Defer the removal, so we can return early if needed and + // de-spaghetti the code. + defer func() { + shouldRestart, err := containers.ShouldRestart(ic.ClientCtx, ctr.ID, nil) + if err != nil { + logrus.Errorf("Failed to check if %s should restart: %v", ctr.ID, err) + return + } + + if !shouldRestart { + if err := containers.Remove(ic.ClientCtx, ctr.ID, removeOptions); err != nil { + if errorhandling.Contains(err, define.ErrNoSuchCtr) || + errorhandling.Contains(err, define.ErrCtrRemoved) { + logrus.Warnf("Container %s does not exist: %v", ctr.ID, err) + } else { + logrus.Errorf("Error removing container %s: %v", ctr.ID, err) + } + } + } + }() + } + + exitCode, err := containers.Wait(ic.ClientCtx, name, nil) if err == define.ErrNoSuchCtr { // Check events event, err := ic.GetLastContainerEvent(ctx, name, events.Exited) @@ -533,8 +581,20 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri } // Start the container if it's not running already. if !ctrRunning { - err = containers.Start(ic.ClientCxt, name, &options.DetachKeys) + + err = containers.Start(ic.ClientCtx, name, new(containers.StartOptions).WithDetachKeys(options.DetachKeys)) if err != nil { + if ctr.AutoRemove { + rmOptions := new(containers.RemoveOptions).WithForce(false).WithVolumes(true) + if err := containers.Remove(ic.ClientCtx, ctr.ID, rmOptions); err != nil { + if errorhandling.Contains(err, define.ErrNoSuchCtr) || + errorhandling.Contains(err, define.ErrCtrRemoved) { + logrus.Warnf("Container %s does not exist: %v", ctr.ID, err) + } else { + logrus.Errorf("Error removing container %s: %v", ctr.ID, err) + } + } + } report.Err = errors.Wrapf(err, "unable to start container %q", name) report.ExitCode = define.ExitCode(err) reports = append(reports, &report) @@ -547,12 +607,14 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri return reports, nil } -func (ic *ContainerEngine) ContainerList(ctx context.Context, options entities.ContainerListOptions) ([]entities.ListContainer, error) { - return containers.List(ic.ClientCxt, options.Filters, &options.All, &options.Last, &options.Namespace, &options.Size, &options.Sync) +func (ic *ContainerEngine) ContainerList(ctx context.Context, opts entities.ContainerListOptions) ([]entities.ListContainer, error) { + options := new(containers.ListOptions).WithFilters(opts.Filters).WithAll(opts.All).WithLast(opts.Last) + options.WithNamespace(opts.Namespace).WithSize(opts.Size).WithSync(opts.Sync) + return containers.List(ic.ClientCtx, options) } func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) { - con, err := containers.CreateWithSpec(ic.ClientCxt, opts.Spec) + con, err := containers.CreateWithSpec(ic.ClientCtx, opts.Spec, nil) if err != nil { return nil, err } @@ -569,7 +631,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta if opts.Detach { // Detach and return early - err := containers.Start(ic.ClientCxt, con.ID, nil) + err := containers.Start(ic.ClientCtx, con.ID, nil) if err != nil { report.ExitCode = define.ExitCode(err) } @@ -584,7 +646,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta report.ExitCode = define.ExitCode(err) if opts.Rm { - if rmErr := containers.Remove(ic.ClientCxt, con.ID, bindings.PFalse, bindings.PTrue); rmErr != nil { + if rmErr := containers.Remove(ic.ClientCtx, con.ID, new(containers.RemoveOptions).WithForce(false).WithVolumes(true)); rmErr != nil { logrus.Debugf("unable to remove container %s after failing to start and attach to it", con.ID) } } @@ -595,14 +657,14 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta // Defer the removal, so we can return early if needed and // de-spaghetti the code. defer func() { - shouldRestart, err := containers.ShouldRestart(ic.ClientCxt, con.ID) + shouldRestart, err := containers.ShouldRestart(ic.ClientCtx, con.ID, nil) if err != nil { logrus.Errorf("Failed to check if %s should restart: %v", con.ID, err) return } if !shouldRestart { - if err := containers.Remove(ic.ClientCxt, con.ID, bindings.PFalse, bindings.PTrue); err != nil { + if err := containers.Remove(ic.ClientCtx, con.ID, new(containers.RemoveOptions).WithForce(false).WithVolumes(true)); err != nil { if errorhandling.Contains(err, define.ErrNoSuchCtr) || errorhandling.Contains(err, define.ErrCtrRemoved) { logrus.Warnf("Container %s does not exist: %v", con.ID, err) @@ -615,7 +677,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta } // Wait - exitCode, waitErr := containers.Wait(ic.ClientCxt, con.ID, nil) + exitCode, waitErr := containers.Wait(ic.ClientCtx, con.ID, nil) if waitErr == nil { report.ExitCode = int(exitCode) return &report, nil @@ -664,7 +726,7 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta } func (ic *ContainerEngine) ContainerDiff(ctx context.Context, nameOrID string, _ entities.DiffOptions) (*entities.DiffReport, error) { - changes, err := containers.Diff(ic.ClientCxt, nameOrID) + changes, err := containers.Diff(ic.ClientCtx, nameOrID, nil) return &entities.DiffReport{Changes: changes}, err } @@ -673,13 +735,13 @@ func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []st } func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []string, options entities.ContainerInitOptions) ([]*entities.ContainerInitReport, error) { - ctrs, err := getContainersByContext(ic.ClientCxt, options.All, false, namesOrIds) + ctrs, err := getContainersByContext(ic.ClientCtx, options.All, false, namesOrIds) if err != nil { return nil, err } reports := make([]*entities.ContainerInitReport, 0, len(ctrs)) for _, ctr := range ctrs { - err := containers.ContainerInit(ic.ClientCxt, ctr.ID) + err := containers.ContainerInit(ic.ClientCtx, ctr.ID, nil) // When using all, it is NOT considered an error if a container // has already been init'd. if err != nil && options.All && strings.Contains(errors.Cause(err).Error(), define.ErrCtrStateInvalid.Error()) { @@ -713,7 +775,7 @@ func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrID string, o if len(nameOrID) > 0 { namesOrIds = append(namesOrIds, nameOrID) } - ctrs, err := getContainersByContext(ic.ClientCxt, options.All, false, namesOrIds) + ctrs, err := getContainersByContext(ic.ClientCtx, options.All, false, namesOrIds) if err != nil { return nil, err } @@ -731,9 +793,16 @@ func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrID string, o return reports, nil } -func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, options entities.ContainerCpOptions) error { - return nil - // return containers.Copy(ic.ClientCxt, source, dest, options) +func (ic *ContainerEngine) ContainerCopyFromArchive(ctx context.Context, nameOrID string, path string, reader io.Reader) (entities.ContainerCopyFunc, error) { + return containers.CopyFromArchive(ic.ClientCtx, nameOrID, path, reader) +} + +func (ic *ContainerEngine) ContainerCopyToArchive(ctx context.Context, nameOrID string, path string, writer io.Writer) (entities.ContainerCopyFunc, error) { + return containers.CopyToArchive(ic.ClientCtx, nameOrID, path, writer) +} + +func (ic *ContainerEngine) ContainerStat(ctx context.Context, nameOrID string, path string) (*entities.ContainerStatReport, error) { + return containers.Stat(ic.ClientCtx, nameOrID, path) } // Shutdown Libpod engine @@ -744,10 +813,10 @@ func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []stri if options.Latest { return nil, errors.New("latest is not supported for the remote client") } - return containers.Stats(ic.ClientCxt, namesOrIds, &options.Stream) + return containers.Stats(ic.ClientCtx, namesOrIds, new(containers.StatsOptions).WithStream(options.Stream)) } -// ShouldRestart reports back whether the containre will restart +// ShouldRestart reports back whether the container will restart func (ic *ContainerEngine) ShouldRestart(_ context.Context, id string) (bool, error) { - return containers.ShouldRestart(ic.ClientCxt, id) + return containers.ShouldRestart(ic.ClientCtx, id, nil) } diff --git a/pkg/domain/infra/tunnel/events.go b/pkg/domain/infra/tunnel/events.go index 53bae6cef..cec6c749c 100644 --- a/pkg/domain/infra/tunnel/events.go +++ b/pkg/domain/infra/tunnel/events.go @@ -2,7 +2,6 @@ package tunnel import ( "context" - // "fmt" "strings" "github.com/containers/podman/v2/libpod/events" @@ -29,7 +28,8 @@ func (ic *ContainerEngine) Events(ctx context.Context, opts entities.EventsOptio } close(opts.EventChan) }() - return system.Events(ic.ClientCxt, binChan, nil, &opts.Since, &opts.Until, filters, &opts.Stream) + options := new(system.EventsOptions).WithFilters(filters).WithSince(opts.Since).WithStream(opts.Stream).WithUntil(opts.Until) + return system.Events(ic.ClientCtx, binChan, nil, options) } // GetLastContainerEvent takes a container name or ID and an event status and returns diff --git a/pkg/domain/infra/tunnel/generate.go b/pkg/domain/infra/tunnel/generate.go index ebbfa143f..6d68157c1 100644 --- a/pkg/domain/infra/tunnel/generate.go +++ b/pkg/domain/infra/tunnel/generate.go @@ -7,10 +7,16 @@ import ( "github.com/containers/podman/v2/pkg/domain/entities" ) -func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, options entities.GenerateSystemdOptions) (*entities.GenerateSystemdReport, error) { - return generate.Systemd(ic.ClientCxt, nameOrID, options) +func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, opts entities.GenerateSystemdOptions) (*entities.GenerateSystemdReport, error) { + options := new(generate.SystemdOptions).WithUseName(opts.Name).WithContainerPrefix(opts.ContainerPrefix).WithNew(opts.New) + options.WithPodPrefix(opts.PodPrefix).WithRestartPolicy(opts.RestartPolicy).WithSeparator(opts.Separator) + if to := opts.StopTimeout; to != nil { + options.WithStopTimeout(*opts.StopTimeout) + } + return generate.Systemd(ic.ClientCtx, nameOrID, options) } -func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) { - return generate.Kube(ic.ClientCxt, nameOrIDs, options) +func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string, opts entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) { + options := new(generate.KubeOptions).WithService(opts.Service) + return generate.Kube(ic.ClientCtx, nameOrIDs, options) } diff --git a/pkg/domain/infra/tunnel/healthcheck.go b/pkg/domain/infra/tunnel/healthcheck.go index ac28712ce..3daf22647 100644 --- a/pkg/domain/infra/tunnel/healthcheck.go +++ b/pkg/domain/infra/tunnel/healthcheck.go @@ -9,5 +9,5 @@ import ( ) func (ic *ContainerEngine) HealthCheckRun(ctx context.Context, nameOrID string, options entities.HealthCheckOptions) (*define.HealthCheckResults, error) { - return containers.RunHealthCheck(ic.ClientCxt, nameOrID) + return containers.RunHealthCheck(ic.ClientCtx, nameOrID, nil) } diff --git a/pkg/domain/infra/tunnel/helpers.go b/pkg/domain/infra/tunnel/helpers.go index 63f9546be..0a806d860 100644 --- a/pkg/domain/infra/tunnel/helpers.go +++ b/pkg/domain/infra/tunnel/helpers.go @@ -4,7 +4,6 @@ import ( "context" "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/bindings/pods" "github.com/containers/podman/v2/pkg/domain/entities" @@ -18,8 +17,8 @@ func getContainersByContext(contextWithConnection context.Context, all, ignore b if all && len(namesOrIDs) > 0 { return nil, errors.New("cannot lookup containers and all") } - - allContainers, err := containers.List(contextWithConnection, nil, bindings.PTrue, nil, nil, nil, bindings.PTrue) + options := new(containers.ListOptions).WithAll(true).WithSync(true) + allContainers, err := containers.List(contextWithConnection, options) if err != nil { return nil, err } @@ -38,7 +37,7 @@ func getContainersByContext(contextWithConnection context.Context, all, ignore b // First determine if the container exists by doing an inspect. // Inspect takes supports names and IDs and let's us determine // a containers full ID. - inspectData, err := containers.Inspect(contextWithConnection, nameOrID, bindings.PFalse) + inspectData, err := containers.Inspect(contextWithConnection, nameOrID, new(containers.InspectOptions).WithSize(false)) if err != nil { if ignore && errorhandling.Contains(err, define.ErrNoSuchCtr) { continue @@ -90,7 +89,7 @@ func getPodsByContext(contextWithConnection context.Context, all bool, namesOrID // First determine if the pod exists by doing an inspect. // Inspect takes supports names and IDs and let's us determine // a containers full ID. - inspectData, err := pods.Inspect(contextWithConnection, nameOrID) + inspectData, err := pods.Inspect(contextWithConnection, nameOrID, nil) if err != nil { if errorhandling.Contains(err, define.ErrNoSuchPod) { return nil, errors.Wrapf(define.ErrNoSuchPod, "unable to find pod %q", nameOrID) diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index 09931de12..fba60235e 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -4,25 +4,32 @@ import ( "context" "io/ioutil" "os" + "strconv" "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" 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" utils2 "github.com/containers/podman/v2/utils" "github.com/pkg/errors" ) func (ir *ImageEngine) Exists(_ context.Context, nameOrID string) (*entities.BoolReport, error) { - found, err := images.Exists(ir.ClientCxt, nameOrID) + found, err := images.Exists(ir.ClientCtx, nameOrID) return &entities.BoolReport{Value: found}, err } func (ir *ImageEngine) Remove(ctx context.Context, imagesArg []string, opts entities.ImageRemoveOptions) (*entities.ImageRemoveReport, []error) { - return images.BatchRemove(ir.ClientCxt, imagesArg, opts) + options := new(images.RemoveOptions).WithForce(opts.Force).WithAll(opts.All) + return images.Remove(ir.ClientCtx, imagesArg, options) } func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) ([]*entities.ImageSummary, error) { @@ -32,13 +39,14 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) f := strings.Split(filter, "=") filters[f[0]] = f[1:] } - images, err := images.List(ir.ClientCxt, &opts.All, filters) + options := new(images.ListOptions).WithAll(opts.All).WithFilters(filters) + psImages, err := images.List(ir.ClientCtx, options) if err != nil { return nil, err } - is := make([]*entities.ImageSummary, len(images)) - for i, img := range images { + is := make([]*entities.ImageSummary, len(psImages)) + for i, img := range psImages { hold := entities.ImageSummary{} if err := utils.DeepCopy(&hold, img); err != nil { return nil, err @@ -57,7 +65,8 @@ func (ir *ImageEngine) Unmount(ctx context.Context, images []string, options ent } func (ir *ImageEngine) History(ctx context.Context, nameOrID string, opts entities.ImageHistoryOptions) (*entities.ImageHistoryReport, error) { - results, err := images.History(ir.ClientCxt, nameOrID) + options := new(images.HistoryOptions) + results, err := images.History(ir.ClientCtx, nameOrID, options) if err != nil { return nil, err } @@ -82,37 +91,41 @@ 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:] } - - results, err := images.Prune(ir.ClientCxt, &opts.All, filters) + options := new(images.PruneOptions).WithAll(opts.All).WithFilters(filters) + 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, options entities.ImagePullOptions) (*entities.ImagePullReport, error) { - pulledImages, err := images.Pull(ir.ClientCxt, rawImage, options) +func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, opts entities.ImagePullOptions) (*entities.ImagePullReport, error) { + options := new(images.PullOptions) + options.WithAllTags(opts.AllTags).WithAuthfile(opts.Authfile).WithCertDir(opts.CertDir).WithOverrideArch(opts.OverrideArch).WithOverrideOS(opts.OverrideOS) + options.WithOverrideVariant(opts.OverrideVariant).WithPassword(opts.Password).WithPullPolicy(opts.PullPolicy) + if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { + if s == types.OptionalBoolTrue { + options.WithSkipTLSVerify(true) + } else { + options.WithSkipTLSVerify(false) + } + } + options.WithQuiet(opts.Quiet).WithSignaturePolicy(opts.SignaturePolicy).WithUsername(opts.Username) + pulledImages, err := images.Pull(ir.ClientCtx, rawImage, options) if err != nil { return nil, err } return &entities.ImagePullReport{Images: pulledImages}, nil } -func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, options entities.ImageTagOptions) error { +func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, opt entities.ImageTagOptions) error { + options := new(images.TagOptions) for _, newTag := range tags { var ( tag, repo string @@ -130,16 +143,17 @@ func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, if len(repo) < 1 { return errors.Errorf("invalid image name %q", nameOrID) } - if err := images.Tag(ir.ClientCxt, nameOrID, tag, repo); err != nil { + if err := images.Tag(ir.ClientCtx, nameOrID, tag, repo, options); err != nil { return err } } return nil } -func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string, options entities.ImageUntagOptions) error { +func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string, opt entities.ImageUntagOptions) error { + options := new(images.UntagOptions) if len(tags) == 0 { - return images.Untag(ir.ClientCxt, nameOrID, "", "") + return images.Untag(ir.ClientCtx, nameOrID, "", "", options) } for _, newTag := range tags { @@ -159,7 +173,7 @@ func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string if len(repo) < 1 { return errors.Errorf("invalid image name %q", nameOrID) } - if err := images.Untag(ir.ClientCxt, nameOrID, tag, repo); err != nil { + if err := images.Untag(ir.ClientCtx, nameOrID, tag, repo, options); err != nil { return err } } @@ -167,10 +181,11 @@ func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string } func (ir *ImageEngine) Inspect(ctx context.Context, namesOrIDs []string, opts entities.InspectOptions) ([]*entities.ImageInspectReport, []error, error) { + options := new(images.GetOptions).WithSize(opts.Size) reports := []*entities.ImageInspectReport{} errs := []error{} for _, i := range namesOrIDs { - r, err := images.GetImage(ir.ClientCxt, i, &opts.Size) + r, err := images.GetImage(ir.ClientCtx, i, options) if err != nil { errModel, ok := err.(entities.ErrorModel) if !ok { @@ -204,68 +219,73 @@ func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) if len(opts.Tag) > 0 { ref += ":" + opts.Tag } - return images.Load(ir.ClientCxt, f, &ref) + options := new(images.LoadOptions).WithReference(ref) + return images.Load(ir.ClientCtx, f, options) } func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOptions) (*entities.ImageImportReport, error) { var ( - err error - sourceURL *string - f *os.File + err error + f *os.File ) + options := new(images.ImportOptions).WithChanges(opts.Changes).WithMessage(opts.Message).WithReference(opts.Reference) if opts.SourceIsURL { - sourceURL = &opts.Source + options.WithURL(opts.Source) } else { f, err = os.Open(opts.Source) if err != nil { return nil, err } } - return images.Import(ir.ClientCxt, opts.Changes, &opts.Message, &opts.Reference, sourceURL, f) + return images.Import(ir.ClientCtx, f, options) } -func (ir *ImageEngine) Push(ctx context.Context, source string, destination string, options entities.ImagePushOptions) error { - return images.Push(ir.ClientCxt, source, destination, options) +func (ir *ImageEngine) Push(ctx context.Context, source string, destination string, opts entities.ImagePushOptions) error { + options := new(images.PushOptions) + options.WithUsername(opts.Username).WithSignaturePolicy(opts.SignaturePolicy).WithQuiet(opts.Quiet) + options.WithPassword(opts.Password).WithCertDir(opts.CertDir).WithAuthfile(opts.Authfile) + options.WithCompress(opts.Compress).WithDigestFile(opts.DigestFile).WithFormat(opts.Format) + options.WithRemoveSignatures(opts.RemoveSignatures).WithSignBy(opts.SignBy) + + if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { + if s == types.OptionalBoolTrue { + options.WithSkipTLSVerify(true) + } else { + options.WithSkipTLSVerify(false) + } + } + return images.Push(ir.ClientCtx, source, destination, options) } -func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, options entities.ImageSaveOptions) error { +func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, opts entities.ImageSaveOptions) error { var ( f *os.File err error ) - switch options.Format { + options := new(images.ExportOptions).WithFormat(opts.Format).WithCompress(opts.Compress) + + switch opts.Format { case "oci-dir", "docker-dir": f, err = ioutil.TempFile("", "podman_save") if err == nil { defer func() { _ = os.Remove(f.Name()) }() } default: - f, err = os.Create(options.Output) + f, err = os.Create(opts.Output) } if err != nil { return err } - if options.MultiImageArchive { - exErr := images.MultiExport(ir.ClientCxt, append([]string{nameOrID}, tags...), f, &options.Format, &options.Compress) - if err := f.Close(); err != nil { - return err - } - if exErr != nil { - return exErr - } - } else { - // FIXME: tags are entirely ignored here but shouldn't. - exErr := images.Export(ir.ClientCxt, nameOrID, f, &options.Format, &options.Compress) - if err := f.Close(); err != nil { - return err - } - if exErr != nil { - return exErr - } + exErr := images.Export(ir.ClientCtx, append([]string{nameOrID}, tags...), f, options) + if err := f.Close(); err != nil { + return err + } + if exErr != nil { + return exErr } - if options.Format != "oci-dir" && options.Format != "docker-dir" { + if opts.Format != "oci-dir" && opts.Format != "docker-dir" { return nil } @@ -273,25 +293,26 @@ func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, if err != nil { return err } - info, err := os.Stat(options.Output) + info, err := os.Stat(opts.Output) switch { case err == nil: if info.Mode().IsRegular() { - return errors.Errorf("%q already exists as a regular file", options.Output) + return errors.Errorf("%q already exists as a regular file", opts.Output) } case os.IsNotExist(err): - if err := os.Mkdir(options.Output, 0755); err != nil { + if err := os.Mkdir(opts.Output, 0755); err != nil { return err } default: return err } - return utils2.UntarToFileSystem(options.Output, f, nil) + return utils2.UntarToFileSystem(opts.Output, f, nil) } // Diff reports the changes to the given image func (ir *ImageEngine) Diff(ctx context.Context, nameOrID string, _ entities.DiffOptions) (*entities.DiffReport, error) { - changes, err := images.Diff(ir.ClientCxt, nameOrID) + options := new(images.DiffOptions) + changes, err := images.Diff(ir.ClientCtx, nameOrID, options) if err != nil { return nil, err } @@ -299,7 +320,34 @@ func (ir *ImageEngine) Diff(ctx context.Context, nameOrID string, _ entities.Dif } func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.ImageSearchOptions) ([]entities.ImageSearchReport, error) { - return images.Search(ir.ClientCxt, term, opts) + mappedFilters := make(map[string][]string) + filters, err := image.ParseSearchFilter(opts.Filters) + if err != nil { + return nil, err + } + if stars := filters.Stars; stars > 0 { + mappedFilters["stars"] = []string{strconv.Itoa(stars)} + } + + if official := filters.IsOfficial; official != types.OptionalBoolUndefined { + mappedFilters["is-official"] = []string{strconv.FormatBool(official == types.OptionalBoolTrue)} + } + + if automated := filters.IsAutomated; automated != types.OptionalBoolUndefined { + mappedFilters["is-automated"] = []string{strconv.FormatBool(automated == types.OptionalBoolTrue)} + } + + options := new(images.SearchOptions) + options.WithAuthfile(opts.Authfile).WithFilters(mappedFilters).WithLimit(opts.Limit) + options.WithListTags(opts.ListTags).WithNoTrunc(opts.NoTrunc) + if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { + if s == types.OptionalBoolTrue { + options.WithSkipTLSVerify(true) + } else { + options.WithSkipTLSVerify(false) + } + } + return images.Search(ir.ClientCtx, term, options) } func (ir *ImageEngine) Config(_ context.Context) (*config.Config, error) { @@ -307,7 +355,7 @@ func (ir *ImageEngine) Config(_ context.Context) (*config.Config, error) { } func (ir *ImageEngine) Build(_ context.Context, containerFiles []string, opts entities.BuildOptions) (*entities.BuildReport, error) { - report, err := images.Build(ir.ClientCxt, containerFiles, opts) + report, err := images.Build(ir.ClientCtx, containerFiles, opts) if err != nil { return nil, err } @@ -326,7 +374,8 @@ func (ir *ImageEngine) Build(_ context.Context, containerFiles []string, opts en } func (ir *ImageEngine) Tree(ctx context.Context, nameOrID string, opts entities.ImageTreeOptions) (*entities.ImageTreeReport, error) { - return images.Tree(ir.ClientCxt, nameOrID, &opts.WhatRequires) + options := new(images.TreeOptions).WithWhatRequires(opts.WhatRequires) + return images.Tree(ir.ClientCtx, nameOrID, options) } // Shutdown Libpod engine diff --git a/pkg/domain/infra/tunnel/manifest.go b/pkg/domain/infra/tunnel/manifest.go index a09b502b4..c71349fe0 100644 --- a/pkg/domain/infra/tunnel/manifest.go +++ b/pkg/domain/infra/tunnel/manifest.go @@ -6,7 +6,6 @@ import ( "fmt" "strings" - "github.com/containers/podman/v2/libpod/image" "github.com/containers/podman/v2/pkg/bindings/manifests" "github.com/containers/podman/v2/pkg/domain/entities" "github.com/pkg/errors" @@ -14,7 +13,8 @@ import ( // ManifestCreate implements manifest create via ImageEngine func (ir *ImageEngine) ManifestCreate(ctx context.Context, names, images []string, opts entities.ManifestCreateOptions) (string, error) { - imageID, err := manifests.Create(ir.ClientCxt, names, images, &opts.All) + options := new(manifests.CreateOptions).WithAll(opts.All) + imageID, err := manifests.Create(ir.ClientCtx, names, images, options) if err != nil { return imageID, errors.Wrapf(err, "error creating manifest") } @@ -23,7 +23,7 @@ func (ir *ImageEngine) ManifestCreate(ctx context.Context, names, images []strin // ManifestInspect returns contents of manifest list with given name func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte, error) { - list, err := manifests.Inspect(ir.ClientCxt, name) + list, err := manifests.Inspect(ir.ClientCtx, name, nil) if err != nil { return nil, errors.Wrapf(err, "error getting content of manifest list or image %s", name) } @@ -37,15 +37,8 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string) ([]byte // ManifestAdd adds images to the manifest list func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAddOptions) (string, error) { - manifestAddOpts := image.ManifestAddOpts{ - All: opts.All, - Arch: opts.Arch, - Features: opts.Features, - Images: opts.Images, - OS: opts.OS, - OSVersion: opts.OSVersion, - Variant: opts.Variant, - } + options := new(manifests.AddOptions).WithAll(opts.All).WithArch(opts.Arch).WithVariant(opts.Variant) + options.WithFeatures(opts.Features).WithImages(opts.Images).WithOS(opts.OS).WithOSVersion(opts.OSVersion) if len(opts.Annotation) != 0 { annotations := make(map[string]string) for _, annotationSpec := range opts.Annotation { @@ -55,9 +48,10 @@ func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAd } annotations[spec[0]] = spec[1] } - manifestAddOpts.Annotation = annotations + options.WithAnnotation(annotations) } - listID, err := manifests.Add(ir.ClientCxt, opts.Images[1], manifestAddOpts) + + listID, err := manifests.Add(ir.ClientCtx, opts.Images[1], options) if err != nil { return listID, errors.Wrapf(err, "error adding to manifest list %s", opts.Images[1]) } @@ -71,7 +65,7 @@ func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, names []string, opt // ManifestRemove removes the digest from manifest list func (ir *ImageEngine) ManifestRemove(ctx context.Context, names []string) (string, error) { - updatedListID, err := manifests.Remove(ir.ClientCxt, names[0], names[1]) + updatedListID, err := manifests.Remove(ir.ClientCtx, names[0], names[1], nil) if err != nil { return updatedListID, errors.Wrapf(err, "error removing from manifest %s", names[0]) } @@ -79,7 +73,8 @@ func (ir *ImageEngine) ManifestRemove(ctx context.Context, names []string) (stri } // ManifestPush pushes a manifest list or image index to the destination -func (ir *ImageEngine) ManifestPush(ctx context.Context, names []string, opts entities.ManifestPushOptions) error { - _, err := manifests.Push(ir.ClientCxt, names[0], &names[1], &opts.All) +func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination string, opts entities.ManifestPushOptions) error { + options := new(manifests.PushOptions).WithAll(opts.All) + _, err := manifests.Push(ir.ClientCtx, name, destination, options) return err } diff --git a/pkg/domain/infra/tunnel/network.go b/pkg/domain/infra/tunnel/network.go index 4845980f6..9afb8db02 100644 --- a/pkg/domain/infra/tunnel/network.go +++ b/pkg/domain/infra/tunnel/network.go @@ -8,17 +8,19 @@ import ( "github.com/pkg/errors" ) -func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]*entities.NetworkListReport, error) { - return network.List(ic.ClientCxt, options) +func (ic *ContainerEngine) NetworkList(ctx context.Context, opts entities.NetworkListOptions) ([]*entities.NetworkListReport, error) { + options := new(network.ListOptions).WithFilters(opts.Filters) + return network.List(ic.ClientCtx, options) } -func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]entities.NetworkInspectReport, []error, error) { +func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, opts entities.InspectOptions) ([]entities.NetworkInspectReport, []error, error) { var ( reports = make([]entities.NetworkInspectReport, 0, len(namesOrIds)) errs = []error{} ) + options := new(network.InspectOptions) for _, name := range namesOrIds { - report, err := network.Inspect(ic.ClientCxt, name) + report, err := network.Inspect(ic.ClientCtx, name, options) if err != nil { errModel, ok := err.(entities.ErrorModel) if !ok { @@ -35,14 +37,15 @@ func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []stri return reports, errs, nil } -func (ic *ContainerEngine) NetworkReload(ctx context.Context, names []string, options entities.NetworkReloadOptions) ([]*entities.NetworkReloadReport, error) { +func (ic *ContainerEngine) NetworkReload(ctx context.Context, names []string, opts entities.NetworkReloadOptions) ([]*entities.NetworkReloadReport, error) { return nil, errors.New("not implemented") } -func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) { +func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, opts entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) { reports := make([]*entities.NetworkRmReport, 0, len(namesOrIds)) + options := new(network.RemoveOptions).WithForce(opts.Force) for _, name := range namesOrIds { - response, err := network.Remove(ic.ClientCxt, name, &options.Force) + response, err := network.Remove(ic.ClientCtx, name, options) if err != nil { report := &entities.NetworkRmReport{ Name: name, @@ -56,16 +59,21 @@ func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, o return reports, nil } -func (ic *ContainerEngine) NetworkCreate(ctx context.Context, name string, options entities.NetworkCreateOptions) (*entities.NetworkCreateReport, error) { - return network.Create(ic.ClientCxt, options, &name) +func (ic *ContainerEngine) NetworkCreate(ctx context.Context, name string, opts entities.NetworkCreateOptions) (*entities.NetworkCreateReport, error) { + options := new(network.CreateOptions).WithName(name).WithDisableDNS(opts.DisableDNS).WithDriver(opts.Driver).WithGateway(opts.Gateway) + options.WithInternal(opts.Internal).WithIPRange(opts.Range).WithIPv6(opts.IPv6).WithLabels(opts.Labels).WithIPv6(opts.IPv6) + options.WithMacVLAN(opts.MacVLAN).WithOptions(opts.Options).WithSubnet(opts.Subnet) + return network.Create(ic.ClientCtx, options) } // NetworkDisconnect removes a container from a given network -func (ic *ContainerEngine) NetworkDisconnect(ctx context.Context, networkname string, options entities.NetworkDisconnectOptions) error { - return network.Disconnect(ic.ClientCxt, networkname, options) +func (ic *ContainerEngine) NetworkDisconnect(ctx context.Context, networkname string, opts entities.NetworkDisconnectOptions) error { + options := new(network.DisconnectOptions).WithForce(opts.Force) + return network.Disconnect(ic.ClientCtx, networkname, opts.Container, options) } // NetworkConnect removes a container from a given network -func (ic *ContainerEngine) NetworkConnect(ctx context.Context, networkname string, options entities.NetworkConnectOptions) error { - return network.Connect(ic.ClientCxt, networkname, options) +func (ic *ContainerEngine) NetworkConnect(ctx context.Context, networkname string, opts entities.NetworkConnectOptions) error { + options := new(network.ConnectOptions).WithAliases(opts.Aliases) + return network.Connect(ic.ClientCtx, networkname, opts.Container, options) } diff --git a/pkg/domain/infra/tunnel/play.go b/pkg/domain/infra/tunnel/play.go index 26f23093b..2318b9caa 100644 --- a/pkg/domain/infra/tunnel/play.go +++ b/pkg/domain/infra/tunnel/play.go @@ -3,10 +3,21 @@ package tunnel import ( "context" + "github.com/containers/image/v5/types" "github.com/containers/podman/v2/pkg/bindings/play" "github.com/containers/podman/v2/pkg/domain/entities" ) -func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) { - return play.Kube(ic.ClientCxt, path, options) +func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, opts entities.PlayKubeOptions) (*entities.PlayKubeReport, error) { + options := new(play.KubeOptions).WithAuthfile(opts.Authfile).WithUsername(opts.Username).WithPassword(opts.Password) + options.WithCertDir(opts.CertDir).WithQuiet(opts.Quiet).WithSignaturePolicy(opts.SignaturePolicy).WithConfigMaps(opts.ConfigMaps) + options.WithLogDriver(opts.LogDriver).WithNetwork(opts.Network).WithSeccompProfileRoot(opts.SeccompProfileRoot) + + if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { + options.WithSkipTLSVerify(s == types.OptionalBoolTrue) + } + if start := opts.Start; start != types.OptionalBoolUndefined { + options.WithStart(start == types.OptionalBoolTrue) + } + return play.Kube(ic.ClientCtx, path, options) } diff --git a/pkg/domain/infra/tunnel/pods.go b/pkg/domain/infra/tunnel/pods.go index ee4978787..aa7b92fa7 100644 --- a/pkg/domain/infra/tunnel/pods.go +++ b/pkg/domain/infra/tunnel/pods.go @@ -12,23 +12,24 @@ import ( ) func (ic *ContainerEngine) PodExists(ctx context.Context, nameOrID string) (*entities.BoolReport, error) { - exists, err := pods.Exists(ic.ClientCxt, nameOrID) + exists, err := pods.Exists(ic.ClientCtx, nameOrID) return &entities.BoolReport{Value: exists}, err } -func (ic *ContainerEngine) PodKill(ctx context.Context, namesOrIds []string, options entities.PodKillOptions) ([]*entities.PodKillReport, error) { - _, err := util.ParseSignal(options.Signal) +func (ic *ContainerEngine) PodKill(ctx context.Context, namesOrIds []string, opts entities.PodKillOptions) ([]*entities.PodKillReport, error) { + _, err := util.ParseSignal(opts.Signal) if err != nil { return nil, err } - foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) + foundPods, err := getPodsByContext(ic.ClientCtx, opts.All, namesOrIds) if err != nil { return nil, err } reports := make([]*entities.PodKillReport, 0, len(foundPods)) + options := new(pods.KillOptions).WithSignal(opts.Signal) for _, p := range foundPods { - response, err := pods.Kill(ic.ClientCxt, p.Id, &options.Signal) + response, err := pods.Kill(ic.ClientCtx, p.Id, options) if err != nil { report := entities.PodKillReport{ Errs: []error{err}, @@ -43,13 +44,13 @@ func (ic *ContainerEngine) PodKill(ctx context.Context, namesOrIds []string, opt } func (ic *ContainerEngine) PodPause(ctx context.Context, namesOrIds []string, options entities.PodPauseOptions) ([]*entities.PodPauseReport, error) { - foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) + foundPods, err := getPodsByContext(ic.ClientCtx, options.All, namesOrIds) if err != nil { return nil, err } reports := make([]*entities.PodPauseReport, 0, len(foundPods)) for _, p := range foundPods { - response, err := pods.Pause(ic.ClientCxt, p.Id) + response, err := pods.Pause(ic.ClientCtx, p.Id, nil) if err != nil { report := entities.PodPauseReport{ Errs: []error{err}, @@ -64,13 +65,13 @@ func (ic *ContainerEngine) PodPause(ctx context.Context, namesOrIds []string, op } func (ic *ContainerEngine) PodUnpause(ctx context.Context, namesOrIds []string, options entities.PodunpauseOptions) ([]*entities.PodUnpauseReport, error) { - foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) + foundPods, err := getPodsByContext(ic.ClientCtx, options.All, namesOrIds) if err != nil { return nil, err } reports := make([]*entities.PodUnpauseReport, 0, len(foundPods)) for _, p := range foundPods { - response, err := pods.Unpause(ic.ClientCxt, p.Id) + response, err := pods.Unpause(ic.ClientCtx, p.Id, nil) if err != nil { report := entities.PodUnpauseReport{ Errs: []error{err}, @@ -84,18 +85,19 @@ func (ic *ContainerEngine) PodUnpause(ctx context.Context, namesOrIds []string, return reports, nil } -func (ic *ContainerEngine) PodStop(ctx context.Context, namesOrIds []string, options entities.PodStopOptions) ([]*entities.PodStopReport, error) { +func (ic *ContainerEngine) PodStop(ctx context.Context, namesOrIds []string, opts entities.PodStopOptions) ([]*entities.PodStopReport, error) { timeout := -1 - foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) - if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchPod) { + foundPods, err := getPodsByContext(ic.ClientCtx, opts.All, namesOrIds) + if err != nil && !(opts.Ignore && errors.Cause(err) == define.ErrNoSuchPod) { return nil, err } - if options.Timeout != -1 { - timeout = options.Timeout + if opts.Timeout != -1 { + timeout = opts.Timeout } reports := make([]*entities.PodStopReport, 0, len(foundPods)) + options := new(pods.StopOptions).WithTimeout(timeout) for _, p := range foundPods { - response, err := pods.Stop(ic.ClientCxt, p.Id, &timeout) + response, err := pods.Stop(ic.ClientCtx, p.Id, options) if err != nil { report := entities.PodStopReport{ Errs: []error{err}, @@ -110,13 +112,13 @@ func (ic *ContainerEngine) PodStop(ctx context.Context, namesOrIds []string, opt } func (ic *ContainerEngine) PodRestart(ctx context.Context, namesOrIds []string, options entities.PodRestartOptions) ([]*entities.PodRestartReport, error) { - foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) + foundPods, err := getPodsByContext(ic.ClientCtx, options.All, namesOrIds) if err != nil { return nil, err } reports := make([]*entities.PodRestartReport, 0, len(foundPods)) for _, p := range foundPods { - response, err := pods.Restart(ic.ClientCxt, p.Id) + response, err := pods.Restart(ic.ClientCtx, p.Id, nil) if err != nil { report := entities.PodRestartReport{ Errs: []error{err}, @@ -131,13 +133,13 @@ func (ic *ContainerEngine) PodRestart(ctx context.Context, namesOrIds []string, } func (ic *ContainerEngine) PodStart(ctx context.Context, namesOrIds []string, options entities.PodStartOptions) ([]*entities.PodStartReport, error) { - foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) + foundPods, err := getPodsByContext(ic.ClientCtx, options.All, namesOrIds) if err != nil { return nil, err } reports := make([]*entities.PodStartReport, 0, len(foundPods)) for _, p := range foundPods { - response, err := pods.Start(ic.ClientCxt, p.Id) + response, err := pods.Start(ic.ClientCtx, p.Id, nil) if err != nil { report := entities.PodStartReport{ Errs: []error{err}, @@ -151,14 +153,15 @@ func (ic *ContainerEngine) PodStart(ctx context.Context, namesOrIds []string, op return reports, nil } -func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, options entities.PodRmOptions) ([]*entities.PodRmReport, error) { - foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) - if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchPod) { +func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, opts entities.PodRmOptions) ([]*entities.PodRmReport, error) { + foundPods, err := getPodsByContext(ic.ClientCtx, opts.All, namesOrIds) + if err != nil && !(opts.Ignore && errors.Cause(err) == define.ErrNoSuchPod) { return nil, err } reports := make([]*entities.PodRmReport, 0, len(foundPods)) + options := new(pods.RemoveOptions).WithForce(opts.Force) for _, p := range foundPods { - response, err := pods.Remove(ic.ClientCxt, p.Id, &options.Force) + response, err := pods.Remove(ic.ClientCtx, p.Id, options) if err != nil { report := entities.PodRmReport{ Err: err, @@ -173,32 +176,33 @@ func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, optio } func (ic *ContainerEngine) PodPrune(ctx context.Context, opts entities.PodPruneOptions) ([]*entities.PodPruneReport, error) { - return pods.Prune(ic.ClientCxt) + return pods.Prune(ic.ClientCtx, nil) } func (ic *ContainerEngine) PodCreate(ctx context.Context, opts entities.PodCreateOptions) (*entities.PodCreateReport, error) { podSpec := specgen.NewPodSpecGenerator() opts.ToPodSpecGen(podSpec) - return pods.CreatePodFromSpec(ic.ClientCxt, podSpec) + return pods.CreatePodFromSpec(ic.ClientCtx, podSpec, nil) } -func (ic *ContainerEngine) PodTop(ctx context.Context, options entities.PodTopOptions) (*entities.StringSliceReport, error) { +func (ic *ContainerEngine) PodTop(ctx context.Context, opts entities.PodTopOptions) (*entities.StringSliceReport, error) { switch { - case options.Latest: + case opts.Latest: return nil, errors.New("latest is not supported") - case options.NameOrID == "": + case opts.NameOrID == "": return nil, errors.New("NameOrID must be specified") } - - topOutput, err := pods.Top(ic.ClientCxt, options.NameOrID, options.Descriptors) + options := new(pods.TopOptions).WithDescriptors(opts.Descriptors) + topOutput, err := pods.Top(ic.ClientCtx, opts.NameOrID, options) if err != nil { return nil, err } return &entities.StringSliceReport{Value: topOutput}, nil } -func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOptions) ([]*entities.ListPodsReport, error) { - return pods.List(ic.ClientCxt, options.Filters) +func (ic *ContainerEngine) PodPs(ctx context.Context, opts entities.PodPSOptions) ([]*entities.ListPodsReport, error) { + options := new(pods.ListOptions).WithFilters(opts.Filters) + return pods.List(ic.ClientCtx, options) } func (ic *ContainerEngine) PodInspect(ctx context.Context, options entities.PodInspectOptions) (*entities.PodInspectReport, error) { @@ -208,9 +212,10 @@ func (ic *ContainerEngine) PodInspect(ctx context.Context, options entities.PodI case options.NameOrID == "": return nil, errors.New("NameOrID must be specified") } - return pods.Inspect(ic.ClientCxt, options.NameOrID) + return pods.Inspect(ic.ClientCtx, options.NameOrID, nil) } -func (ic *ContainerEngine) PodStats(ctx context.Context, namesOrIds []string, options entities.PodStatsOptions) ([]*entities.PodStatsReport, error) { - return pods.Stats(ic.ClientCxt, namesOrIds, options) +func (ic *ContainerEngine) PodStats(ctx context.Context, namesOrIds []string, opts entities.PodStatsOptions) ([]*entities.PodStatsReport, error) { + options := new(pods.StatsOptions).WithAll(opts.All) + return pods.Stats(ic.ClientCtx, namesOrIds, options) } diff --git a/pkg/domain/infra/tunnel/runtime.go b/pkg/domain/infra/tunnel/runtime.go index 357e2c390..6542ea5b7 100644 --- a/pkg/domain/infra/tunnel/runtime.go +++ b/pkg/domain/infra/tunnel/runtime.go @@ -6,15 +6,15 @@ import ( // Image-related runtime using an ssh-tunnel to utilize Podman service type ImageEngine struct { - ClientCxt context.Context + ClientCtx context.Context } // Container-related runtime using an ssh-tunnel to utilize Podman service type ContainerEngine struct { - ClientCxt context.Context + ClientCtx context.Context } // Container-related runtime using an ssh-tunnel to utilize Podman service type SystemEngine struct { - ClientCxt context.Context + ClientCtx context.Context } diff --git a/pkg/domain/infra/tunnel/system.go b/pkg/domain/infra/tunnel/system.go index f3e8fbcb1..a46b164a5 100644 --- a/pkg/domain/infra/tunnel/system.go +++ b/pkg/domain/infra/tunnel/system.go @@ -11,7 +11,7 @@ import ( ) func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) { - return system.Info(ic.ClientCxt) + return system.Info(ic.ClientCtx, nil) } func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) error { @@ -19,12 +19,13 @@ func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) } // SystemPrune prunes unused data from the system. -func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.SystemPruneOptions) (*entities.SystemPruneReport, error) { - return system.Prune(ic.ClientCxt, &options.All, &options.Volume) +func (ic *ContainerEngine) SystemPrune(ctx context.Context, opts entities.SystemPruneOptions) (*entities.SystemPruneReport, error) { + options := new(system.PruneOptions).WithAll(opts.All).WithVolumes(opts.Volume).WithFilters(opts.Filters) + return system.Prune(ic.ClientCtx, options) } func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.SystemDfOptions) (*entities.SystemDfReport, error) { - return system.DiskUsage(ic.ClientCxt) + return system.DiskUsage(ic.ClientCtx, nil) } func (ic *ContainerEngine) Unshare(ctx context.Context, args []string) error { @@ -32,5 +33,5 @@ func (ic *ContainerEngine) Unshare(ctx context.Context, args []string) error { } func (ic ContainerEngine) Version(ctx context.Context) (*entities.SystemVersionReport, error) { - return system.Version(ic.ClientCxt) + return system.Version(ic.ClientCtx, nil) } diff --git a/pkg/domain/infra/tunnel/volumes.go b/pkg/domain/infra/tunnel/volumes.go index c0df2bb7b..10e8d7da8 100644 --- a/pkg/domain/infra/tunnel/volumes.go +++ b/pkg/domain/infra/tunnel/volumes.go @@ -5,11 +5,12 @@ 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/pkg/errors" ) func (ic *ContainerEngine) VolumeCreate(ctx context.Context, opts entities.VolumeCreateOptions) (*entities.IDOrNameResponse, error) { - response, err := volumes.Create(ic.ClientCxt, opts) + response, err := volumes.Create(ic.ClientCtx, opts, nil) if err != nil { return nil, err } @@ -18,7 +19,7 @@ func (ic *ContainerEngine) VolumeCreate(ctx context.Context, opts entities.Volum func (ic *ContainerEngine) VolumeRm(ctx context.Context, namesOrIds []string, opts entities.VolumeRmOptions) ([]*entities.VolumeRmReport, error) { if opts.All { - vols, err := volumes.List(ic.ClientCxt, nil) + vols, err := volumes.List(ic.ClientCtx, nil) if err != nil { return nil, err } @@ -28,8 +29,9 @@ func (ic *ContainerEngine) VolumeRm(ctx context.Context, namesOrIds []string, op } reports := make([]*entities.VolumeRmReport, 0, len(namesOrIds)) for _, id := range namesOrIds { + options := new(volumes.RemoveOptions).WithForce(opts.Force) reports = append(reports, &entities.VolumeRmReport{ - Err: volumes.Remove(ic.ClientCxt, id, &opts.Force), + Err: volumes.Remove(ic.ClientCtx, id, options), Id: id, }) } @@ -42,7 +44,7 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin errs = []error{} ) if opts.All { - vols, err := volumes.List(ic.ClientCxt, nil) + vols, err := volumes.List(ic.ClientCtx, nil) if err != nil { return nil, nil, err } @@ -51,7 +53,7 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin } } for _, id := range namesOrIds { - data, err := volumes.Inspect(ic.ClientCxt, id) + data, err := volumes.Inspect(ic.ClientCtx, id, nil) if err != nil { errModel, ok := err.(entities.ErrorModel) if !ok { @@ -68,10 +70,12 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin return reports, errs, nil } -func (ic *ContainerEngine) VolumePrune(ctx context.Context) ([]*entities.VolumePruneReport, error) { - return volumes.Prune(ic.ClientCxt) +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) } func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeListOptions) ([]*entities.VolumeListReport, error) { - return volumes.List(ic.ClientCxt, opts.Filter) + options := new(volumes.ListOptions).WithFilters(opts.Filter) + return volumes.List(ic.ClientCtx, options) } diff --git a/pkg/errorhandling/errorhandling.go b/pkg/errorhandling/errorhandling.go index ca6b60bc5..21df261fb 100644 --- a/pkg/errorhandling/errorhandling.go +++ b/pkg/errorhandling/errorhandling.go @@ -19,7 +19,12 @@ func JoinErrors(errs []error) error { // blank lines when printing the error. var multiE *multierror.Error multiE = multierror.Append(multiE, errs...) - return errors.New(strings.TrimSpace(multiE.ErrorOrNil().Error())) + + finalErr := multiE.ErrorOrNil() + if finalErr == nil { + return finalErr + } + return errors.New(strings.TrimSpace(finalErr.Error())) } // ErrorsToString converts the slice of errors into a slice of corresponding diff --git a/pkg/hooks/README.md b/pkg/hooks/README.md index 61fbbcac6..f6a03a775 100644 --- a/pkg/hooks/README.md +++ b/pkg/hooks/README.md @@ -9,7 +9,7 @@ This can cause some performance issues. Also a lot of hooks just check if certain configuration is set and then exit early, without doing anything. For example the [oci-systemd-hook][] only executes if the command is `init` or `systemd`, otherwise it just exits. This means if we automatically enabled all hooks, every container would have to execute `oci-systemd-hook`, even if they don't run systemd inside of the container. -Performance would also suffer if we exectuted each hook at each stage ([pre-start][], [post-start][], and [post-stop][]). +Performance would also suffer if we executed each hook at each stage ([pre-start][], [post-start][], and [post-stop][]). The hooks configuration is documented in [`oci-hooks.5`](docs/oci-hooks.5.md). diff --git a/pkg/hooks/exec/exec.go b/pkg/hooks/exec/exec.go index 77b350573..f6b6636ad 100644 --- a/pkg/hooks/exec/exec.go +++ b/pkg/hooks/exec/exec.go @@ -1,4 +1,4 @@ -// Package exec provides utilities for executing Open Container Initative runtime hooks. +// Package exec provides utilities for executing Open Container Initiative runtime hooks. package exec import ( diff --git a/pkg/hooks/hooks.go b/pkg/hooks/hooks.go index 6257529ab..6cb81e573 100644 --- a/pkg/hooks/hooks.go +++ b/pkg/hooks/hooks.go @@ -46,7 +46,7 @@ type namedHook struct { // // extensionStages allows callers to add additional stages beyond // those specified in the OCI Runtime Specification and to control -// OCI-defined stages instead of delagating to the OCI runtime. See +// OCI-defined stages instead of delegating to the OCI runtime. See // Hooks() for more information. func New(ctx context.Context, directories []string, extensionStages []string) (manager *Manager, err error) { manager = &Manager{ diff --git a/pkg/netns/netns_linux.go b/pkg/netns/netns_linux.go index ff5691203..ed5241632 100644 --- a/pkg/netns/netns_linux.go +++ b/pkg/netns/netns_linux.go @@ -162,7 +162,7 @@ func NewNS() (ns.NetNS, error) { // bind mount the netns from the current thread (from /proc) onto the // mount point. This causes the namespace to persist, even when there // are no threads in the ns. Make this a shared mount; it needs to be - // back-propogated to the host + // back-propagated to the host err = unix.Mount(threadNsPath, nsPath, "none", unix.MS_BIND|unix.MS_SHARED|unix.MS_REC, "") if err != nil { err = fmt.Errorf("failed to bind mount ns at %s: %v", nsPath, err) diff --git a/pkg/ps/ps.go b/pkg/ps/ps.go index cfdf3ee49..9e0dcb728 100644 --- a/pkg/ps/ps.go +++ b/pkg/ps/ps.go @@ -11,8 +11,8 @@ import ( "github.com/containers/podman/v2/libpod" "github.com/containers/podman/v2/libpod/define" - lpfilters "github.com/containers/podman/v2/libpod/filters" "github.com/containers/podman/v2/pkg/domain/entities" + "github.com/containers/podman/v2/pkg/domain/filters" psdefine "github.com/containers/podman/v2/pkg/ps/define" "github.com/containers/storage" "github.com/pkg/errors" @@ -27,7 +27,7 @@ func GetContainerLists(runtime *libpod.Runtime, options entities.ContainerListOp all := options.All || options.Last > 0 if len(options.Filters) > 0 { for k, v := range options.Filters { - generatedFunc, err := lpfilters.GenerateContainerFilterFuncs(k, v, runtime) + generatedFunc, err := filters.GenerateContainerFilterFuncs(k, v, runtime) if err != nil { return nil, err } @@ -41,7 +41,7 @@ func GetContainerLists(runtime *libpod.Runtime, options entities.ContainerListOp all = true } if !all { - runningOnly, err := lpfilters.GenerateContainerFilterFuncs("status", []string{define.ContainerStateRunning.String()}, runtime) + runningOnly, err := filters.GenerateContainerFilterFuncs("status", []string{define.ContainerStateRunning.String()}, runtime) if err != nil { return nil, err } @@ -179,24 +179,25 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities } ps := entities.ListContainer{ - Command: conConfig.Command, - Created: conConfig.CreatedTime, - Exited: exited, - ExitCode: exitCode, - ExitedAt: exitedTime.Unix(), - ID: conConfig.ID, - Image: conConfig.RootfsImageName, - ImageID: conConfig.RootfsImageID, - IsInfra: conConfig.IsInfra, - Labels: conConfig.Labels, - Mounts: ctr.UserVolumes(), - Names: []string{conConfig.Name}, - Pid: pid, - Pod: conConfig.Pod, - Ports: portMappings, - Size: size, - StartedAt: startedTime.Unix(), - State: conState.String(), + AutoRemove: ctr.AutoRemove(), + Command: conConfig.Command, + Created: conConfig.CreatedTime, + Exited: exited, + ExitCode: exitCode, + ExitedAt: exitedTime.Unix(), + ID: conConfig.ID, + Image: conConfig.RootfsImageName, + ImageID: conConfig.RootfsImageID, + IsInfra: conConfig.IsInfra, + Labels: conConfig.Labels, + Mounts: ctr.UserVolumes(), + Names: []string{conConfig.Name}, + Pid: pid, + Pod: conConfig.Pod, + Ports: portMappings, + Size: size, + StartedAt: startedTime.Unix(), + State: conState.String(), } if opts.Pod && len(conConfig.Pod) > 0 { podName, err := rt.GetName(conConfig.Pod) diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index f029f24cb..d4b1a0dfd 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -257,7 +257,7 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (_ boo uidsMapped = err == nil } if !uidsMapped { - logrus.Warnf("using rootless single mapping into the namespace. This might break some images. Check /etc/subuid and /etc/subgid for adding subids") + logrus.Warnf("using rootless single mapping into the namespace. This might break some images. Check /etc/subuid and /etc/subgid for adding sub*ids") setgroups := fmt.Sprintf("/proc/%d/setgroups", pid) err = ioutil.WriteFile(setgroups, []byte("deny\n"), 0666) if err != nil { diff --git a/pkg/seccomp/seccomp.go b/pkg/seccomp/seccomp.go index eeba46a72..4502c608f 100644 --- a/pkg/seccomp/seccomp.go +++ b/pkg/seccomp/seccomp.go @@ -30,7 +30,7 @@ var supportedPolicies = map[string]Policy{ "image": PolicyImage, } -// LookupPolicy looksup the corresponding Policy for the specified +// LookupPolicy looks up the corresponding Policy for the specified // string. If none is found, an errors is returned including the list of // supported policies. // diff --git a/pkg/signal/signal_linux.go b/pkg/signal/signal_linux.go index 72ab1b97b..305b9d21f 100644 --- a/pkg/signal/signal_linux.go +++ b/pkg/signal/signal_linux.go @@ -1,4 +1,5 @@ // +build linux +// +build !mips,!mipsle,!mips64,!mips64le // Signal handling for Linux only. package signal diff --git a/pkg/signal/signal_linux_mipsx.go b/pkg/signal/signal_linux_mipsx.go new file mode 100644 index 000000000..67638e30a --- /dev/null +++ b/pkg/signal/signal_linux_mipsx.go @@ -0,0 +1,106 @@ +// +build linux +// +build mips mipsle mips64 mips64le + +// Special signal handling for mips architecture +package signal + +// Copyright 2013-2018 Docker, Inc. + +// NOTE: this package has originally been copied from github.com/docker/docker. + +import ( + "os" + "os/signal" + "syscall" + + "golang.org/x/sys/unix" +) + +const ( + sigrtmin = 34 + sigrtmax = 127 +) + +// signalMap is a map of Linux signals. +var signalMap = map[string]syscall.Signal{ + "ABRT": unix.SIGABRT, + "ALRM": unix.SIGALRM, + "BUS": unix.SIGBUS, + "CHLD": unix.SIGCHLD, + "CLD": unix.SIGCLD, + "CONT": unix.SIGCONT, + "FPE": unix.SIGFPE, + "HUP": unix.SIGHUP, + "ILL": unix.SIGILL, + "INT": unix.SIGINT, + "IO": unix.SIGIO, + "IOT": unix.SIGIOT, + "KILL": unix.SIGKILL, + "PIPE": unix.SIGPIPE, + "POLL": unix.SIGPOLL, + "PROF": unix.SIGPROF, + "PWR": unix.SIGPWR, + "QUIT": unix.SIGQUIT, + "SEGV": unix.SIGSEGV, + "EMT": unix.SIGEMT, + "STOP": unix.SIGSTOP, + "SYS": unix.SIGSYS, + "TERM": unix.SIGTERM, + "TRAP": unix.SIGTRAP, + "TSTP": unix.SIGTSTP, + "TTIN": unix.SIGTTIN, + "TTOU": unix.SIGTTOU, + "URG": unix.SIGURG, + "USR1": unix.SIGUSR1, + "USR2": unix.SIGUSR2, + "VTALRM": unix.SIGVTALRM, + "WINCH": unix.SIGWINCH, + "XCPU": unix.SIGXCPU, + "XFSZ": unix.SIGXFSZ, + "RTMIN": sigrtmin, + "RTMIN+1": sigrtmin + 1, + "RTMIN+2": sigrtmin + 2, + "RTMIN+3": sigrtmin + 3, + "RTMIN+4": sigrtmin + 4, + "RTMIN+5": sigrtmin + 5, + "RTMIN+6": sigrtmin + 6, + "RTMIN+7": sigrtmin + 7, + "RTMIN+8": sigrtmin + 8, + "RTMIN+9": sigrtmin + 9, + "RTMIN+10": sigrtmin + 10, + "RTMIN+11": sigrtmin + 11, + "RTMIN+12": sigrtmin + 12, + "RTMIN+13": sigrtmin + 13, + "RTMIN+14": sigrtmin + 14, + "RTMIN+15": sigrtmin + 15, + "RTMAX-14": sigrtmax - 14, + "RTMAX-13": sigrtmax - 13, + "RTMAX-12": sigrtmax - 12, + "RTMAX-11": sigrtmax - 11, + "RTMAX-10": sigrtmax - 10, + "RTMAX-9": sigrtmax - 9, + "RTMAX-8": sigrtmax - 8, + "RTMAX-7": sigrtmax - 7, + "RTMAX-6": sigrtmax - 6, + "RTMAX-5": sigrtmax - 5, + "RTMAX-4": sigrtmax - 4, + "RTMAX-3": sigrtmax - 3, + "RTMAX-2": sigrtmax - 2, + "RTMAX-1": sigrtmax - 1, + "RTMAX": sigrtmax, +} + +// CatchAll catches all signals and relays them to the specified channel. +func CatchAll(sigc chan os.Signal) { + handledSigs := make([]os.Signal, 0, len(signalMap)) + for _, s := range signalMap { + handledSigs = append(handledSigs, s) + } + signal.Notify(sigc, handledSigs...) +} + +// StopCatch stops catching the signals and closes the specified channel. +func StopCatch(sigc chan os.Signal) { + signal.Stop(sigc) + close(sigc) +} diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index c7e62d185..42fea0277 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -163,7 +163,7 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat return nil, err } - // labels from the image that dont exist already + // labels from the image that don't exist already if len(labels) > 0 && s.Labels == nil { s.Labels = make(map[string]string) } diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go index 5cc7891ac..e5b09dcd8 100644 --- a/pkg/specgen/generate/kube/kube.go +++ b/pkg/specgen/generate/kube/kube.go @@ -30,7 +30,7 @@ func ToPodGen(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec) p.Hostname = podName } if podYAML.Spec.HostNetwork { - p.NetNS.Value = "host" + p.NetNS.NSMode = specgen.Host } if podYAML.Spec.HostAliases != nil { hosts := make([]string, 0, len(podYAML.Spec.HostAliases)) @@ -47,30 +47,53 @@ func ToPodGen(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec) return p, nil } -func ToSpecGen(ctx context.Context, containerYAML v1.Container, iid string, newImage *image.Image, volumes map[string]*KubeVolume, podID, podName, infraID string, configMaps []v1.ConfigMap, seccompPaths *KubeSeccompPaths, restartPolicy string) (*specgen.SpecGenerator, error) { - s := specgen.NewSpecGenerator(iid, false) +type CtrSpecGenOptions struct { + // Container as read from the pod yaml + Container v1.Container + // Image available to use (pulled or found local) + Image *image.Image + // Volumes for all containers + Volumes map[string]*KubeVolume + // PodID of the parent pod + PodID string + // PodName of the parent pod + PodName string + // PodInfraID as the infrastructure container id + PodInfraID string + // ConfigMaps the configuration maps for environment variables + ConfigMaps []v1.ConfigMap + // SeccompPaths for finding the seccomp profile path + SeccompPaths *KubeSeccompPaths + // RestartPolicy defines the restart policy of the container + RestartPolicy string + // NetNSIsHost tells the container to use the host netns + NetNSIsHost bool +} + +func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGenerator, error) { + s := specgen.NewSpecGenerator(opts.Container.Image, false) - // podName should be non-empty for Deployment objects to be able to create + // pod name should be non-empty for Deployment objects to be able to create // multiple pods having containers with unique names - if len(podName) < 1 { - return nil, errors.Errorf("kubeContainerToCreateConfig got empty podName") + if len(opts.PodName) < 1 { + return nil, errors.Errorf("got empty pod name on container creation when playing kube") } - s.Name = fmt.Sprintf("%s-%s", podName, containerYAML.Name) + s.Name = fmt.Sprintf("%s-%s", opts.PodName, opts.Container.Name) - s.Terminal = containerYAML.TTY + s.Terminal = opts.Container.TTY - s.Pod = podID + s.Pod = opts.PodID - setupSecurityContext(s, containerYAML) + setupSecurityContext(s, opts.Container) // Since we prefix the container name with pod name to work-around the uniqueness requirement, // the seccomp profile should reference the actual container name from the YAML // but apply to the containers with the prefixed name - s.SeccompProfilePath = seccompPaths.FindForContainer(containerYAML.Name) + s.SeccompProfilePath = opts.SeccompPaths.FindForContainer(opts.Container.Name) s.ResourceLimits = &spec.LinuxResources{} - milliCPU, err := quantityToInt64(containerYAML.Resources.Limits.Cpu()) + milliCPU, err := quantityToInt64(opts.Container.Resources.Limits.Cpu()) if err != nil { return nil, errors.Wrap(err, "Failed to set CPU quota") } @@ -82,12 +105,12 @@ func ToSpecGen(ctx context.Context, containerYAML v1.Container, iid string, newI } } - limit, err := quantityToInt64(containerYAML.Resources.Limits.Memory()) + limit, err := quantityToInt64(opts.Container.Resources.Limits.Memory()) if err != nil { return nil, errors.Wrap(err, "Failed to set memory limit") } - memoryRes, err := quantityToInt64(containerYAML.Resources.Requests.Memory()) + memoryRes, err := quantityToInt64(opts.Container.Resources.Requests.Memory()) if err != nil { return nil, errors.Wrap(err, "Failed to set memory reservation") } @@ -104,19 +127,26 @@ func ToSpecGen(ctx context.Context, containerYAML v1.Container, iid string, newI s.ResourceLimits.Memory.Reservation = &memoryRes } - // TODO: We dont understand why specgen does not take of this, but + // 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 := newImage.Inspect(ctx) + 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{} if imageData != nil && imageData.Config != nil { if imageData.Config.WorkingDir != "" { s.WorkDir = imageData.Config.WorkingDir } - s.Command = imageData.Config.Entrypoint + // Pull entrypoint and cmd from image + entrypoint = imageData.Config.Entrypoint + cmd = imageData.Config.Cmd s.Labels = imageData.Config.Labels if len(imageData.Config.StopSignal) > 0 { stopSignal, err := util.ParseSignal(imageData.Config.StopSignal) @@ -126,22 +156,27 @@ func ToSpecGen(ctx context.Context, containerYAML v1.Container, iid string, newI s.StopSignal = &stopSignal } } - if len(containerYAML.Command) != 0 { - s.Command = containerYAML.Command + // 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{} } - // doc https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#notes - if len(containerYAML.Args) != 0 { - s.Command = append(s.Command, containerYAML.Args...) + // 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 = append(entrypoint, cmd...) // FIXME, // we are currently ignoring imageData.Config.ExposedPorts - if containerYAML.WorkingDir != "" { - s.WorkDir = containerYAML.WorkingDir + if opts.Container.WorkingDir != "" { + s.WorkDir = opts.Container.WorkingDir } annotations := make(map[string]string) - if infraID != "" { - annotations[ann.SandboxID] = infraID + if opts.PodInfraID != "" { + annotations[ann.SandboxID] = opts.PodInfraID annotations[ann.ContainerType] = ann.ContainerTypeContainer } s.Annotations = annotations @@ -153,13 +188,13 @@ func ToSpecGen(ctx context.Context, containerYAML v1.Container, iid string, newI envs[keyval[0]] = keyval[1] } - for _, env := range containerYAML.Env { - value := envVarValue(env, configMaps) + for _, env := range opts.Container.Env { + value := envVarValue(env, opts.ConfigMaps) envs[env.Name] = value } - for _, envFrom := range containerYAML.EnvFrom { - cmEnvs := envVarsFromConfigMap(envFrom, configMaps) + for _, envFrom := range opts.Container.EnvFrom { + cmEnvs := envVarsFromConfigMap(envFrom, opts.ConfigMaps) for k, v := range cmEnvs { envs[k] = v @@ -167,8 +202,8 @@ func ToSpecGen(ctx context.Context, containerYAML v1.Container, iid string, newI } s.Env = envs - for _, volume := range containerYAML.VolumeMounts { - volumeSource, exists := volumes[volume.Name] + for _, volume := range opts.Container.VolumeMounts { + volumeSource, exists := opts.Volumes[volume.Name] if !exists { return nil, errors.Errorf("Volume mount %s specified for container but not configured in volumes", volume.Name) } @@ -200,7 +235,11 @@ func ToSpecGen(ctx context.Context, containerYAML v1.Container, iid string, newI } } - s.RestartPolicy = restartPolicy + s.RestartPolicy = opts.RestartPolicy + + if opts.NetNSIsHost { + s.NetNS.NSMode = specgen.Host + } return s, nil } diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index c24dcf4c0..ba68de6fd 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -319,7 +319,7 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt } // BIND MOUNTS - configSpec.Mounts = SupercedeUserMounts(mounts, configSpec.Mounts) + configSpec.Mounts = SupersedeUserMounts(mounts, configSpec.Mounts) // Process mounts to ensure correct options if err := InitFSMounts(configSpec.Mounts); err != nil { return nil, err diff --git a/pkg/specgen/generate/security.go b/pkg/specgen/generate/security.go index b69bd9091..9fceec7b3 100644 --- a/pkg/specgen/generate/security.go +++ b/pkg/specgen/generate/security.go @@ -115,7 +115,7 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, if err != nil { return errors.Wrapf(err, "capabilities requested by user or image are not valid: %q", strings.Join(capsRequired, ",")) } else { - // Verify all capRequiered are in the capList + // Verify all capRequired are in the capList for _, cap := range capsRequired { if !util.StringInSlice(cap, caplist) { privCapsRequired = append(privCapsRequired, cap) @@ -141,7 +141,7 @@ func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, configSpec.Process.Capabilities.Effective = caplist configSpec.Process.Capabilities.Permitted = caplist } else { - userCaps, err := capabilities.NormalizeCapabilities(s.CapAdd) + userCaps, err := capabilities.MergeCapabilities(nil, s.CapAdd, nil) if err != nil { return errors.Wrapf(err, "capabilities requested by user are not valid: %q", strings.Join(s.CapAdd, ",")) } @@ -172,7 +172,7 @@ 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 } diff --git a/pkg/specgen/generate/storage.go b/pkg/specgen/generate/storage.go index 331a5c5bf..f523ac5bf 100644 --- a/pkg/specgen/generate/storage.go +++ b/pkg/specgen/generate/storage.go @@ -366,7 +366,7 @@ func addContainerInitBinary(s *specgen.SpecGenerator, path string) (spec.Mount, // TODO: Should we unmount subtree mounts? E.g., if /tmp/ is mounted by // one mount, and we already have /tmp/a and /tmp/b, should we remove // the /tmp/a and /tmp/b mounts in favor of the more general /tmp? -func SupercedeUserMounts(mounts []spec.Mount, configMount []spec.Mount) []spec.Mount { +func SupersedeUserMounts(mounts []spec.Mount, configMount []spec.Mount) []spec.Mount { if len(mounts) > 0 { // If we have overlappings mounts, remove them from the spec in favor of // the user-added volume mounts diff --git a/pkg/specgen/pod_validate.go b/pkg/specgen/pod_validate.go index a6c61a203..7c81f3f9f 100644 --- a/pkg/specgen/pod_validate.go +++ b/pkg/specgen/pod_validate.go @@ -48,7 +48,7 @@ func (p *PodSpecGenerator) Validate() error { } if p.NoInfra { if p.NetNS.NSMode != Default && p.NetNS.NSMode != "" { - return errors.New("NoInfra and network modes cannot be used toegther") + return errors.New("NoInfra and network modes cannot be used together") } if p.StaticIP != nil { return exclusivePodOptions("NoInfra", "StaticIP") diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index 964b89fa4..a6cc0a730 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -19,7 +19,7 @@ type LogConfig struct { // Only available if LogDriver is set to "json-file" or "k8s-file". // Optional. Path string `json:"path,omitempty"` - // Size is the maximimup size of the log file + // Size is the maximum size of the log file // Optional. Size int64 `json:"size,omitempty"` // A set of options to accompany the log driver. @@ -302,7 +302,7 @@ type ContainerSecurityConfig struct { IDMappings *storage.IDMappingOptions `json:"idmappings,omitempty"` // ReadOnlyFilesystem indicates that everything will be mounted // as read-only - ReadOnlyFilesystem bool `json:"read_only_filesystem,omittempty"` + ReadOnlyFilesystem bool `json:"read_only_filesystem,omitempty"` // Umask is the umask the init process of the container will be run with. Umask string `json:"umask,omitempty"` // ProcOpts are the options used for the proc mount. diff --git a/pkg/systemd/generate/common.go b/pkg/systemd/generate/common.go index 52a214883..fb921cd72 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"} diff --git a/pkg/systemd/generate/containers.go b/pkg/systemd/generate/containers.go index 8090bcd3d..cfa02dc9d 100644 --- a/pkg/systemd/generate/containers.go +++ b/pkg/systemd/generate/containers.go @@ -55,6 +55,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 +76,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 +84,6 @@ ExecStart={{.ExecStart}} ExecStop={{.ExecStop}} ExecStopPost={{.ExecStopPost}} PIDFile={{.PIDFile}} -KillMode=none Type=forking [Install] @@ -191,7 +193,7 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst 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 subcomand. + // CreateCommand with a stripped command and subcommand. startCommand := []string{ info.Executable, "run", @@ -241,7 +243,7 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst } if hasNameParam && !hasReplaceParam { // Enforce --replace for named containers. This will - // make systemd units more robuts as it allows them to + // make systemd units more robust as it allows them to // start after system crashes (see // github.com/containers/podman/issues/5485). startCommand = append(startCommand, "--replace") @@ -255,6 +257,8 @@ func executeContainerTemplate(info *containerInfo, options entities.GenerateSyst info.ExecStopPost = "{{.Executable}} 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..b9fb8fee6 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 +ExecStop=/usr/bin/podman stop -t 22 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 +ExecStopPost=/usr/bin/podman stop -t 22 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid -KillMode=none 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 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 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 +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] @@ -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,12 @@ 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] @@ -242,7 +243,7 @@ WantedBy=multi-user.target default.target ContainerNameOrID: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", RestartPolicy: "always", PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 10, + StopTimeout: 22, PodmanVersion: "CI", EnvVariable: EnvVariable, }, @@ -302,7 +303,7 @@ WantedBy=multi-user.target default.target ContainerNameOrID: "jadda-jadda", RestartPolicy: "always", PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 42, + 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, @@ -318,7 +319,7 @@ WantedBy=multi-user.target default.target ContainerNameOrID: "jadda-jadda", RestartPolicy: "always", PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 42, + 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"}, EnvVariable: EnvVariable, @@ -334,7 +335,7 @@ WantedBy=multi-user.target default.target ContainerNameOrID: "jadda-jadda", RestartPolicy: "always", PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 42, + 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"}, EnvVariable: EnvVariable, @@ -353,7 +354,7 @@ WantedBy=multi-user.target default.target ContainerNameOrID: "jadda-jadda", RestartPolicy: "always", PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 42, + 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"}, EnvVariable: EnvVariable, @@ -390,9 +391,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 234a60380..fc582e42a 100644 --- a/pkg/systemd/generate/pods.go +++ b/pkg/systemd/generate/pods.go @@ -60,6 +60,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 +74,7 @@ Before={{- range $index, $value := .RequiredServices -}}{{if $index}} {{end}}{{ [Service] Environment={{.EnvVariable}}=%n Restart={{.RestartPolicy}} +TimeoutStopSec={{.TimeoutStopSec}} {{- if .ExecStartPre1}} ExecStartPre={{.ExecStartPre1}} {{- end}} @@ -82,7 +85,6 @@ ExecStart={{.ExecStart}} ExecStop={{.ExecStop}} ExecStopPost={{.ExecStopPost}} PIDFile={{.PIDFile}} -KillMode=none Type=forking [Install] @@ -266,7 +268,7 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions) podCreateArgs = filterPodFlags(info.CreateCommand[podCreateIndex+1:]) } // We're hard-coding the first five arguments and append the - // CreateCommand with a stripped command and subcomand. + // CreateCommand with a stripped command and subcommand. startCommand := []string{info.Executable} startCommand = append(startCommand, podRootArgs...) startCommand = append(startCommand, @@ -298,6 +300,8 @@ func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions) 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.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..6d925ecd2 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 +ExecStop=/usr/bin/podman stop -t 42 jadda-jadda-infra +ExecStopPost=/usr/bin/podman stop -t 42 jadda-jadda-infra PIDFile=/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid -KillMode=none Type=forking [Install] @@ -75,13 +76,13 @@ 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] @@ -102,7 +103,7 @@ WantedBy=multi-user.target default.target InfraNameOrID: "jadda-jadda-infra", RestartPolicy: "always", PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 10, + StopTimeout: 42, PodmanVersion: "CI", RequiredServices: []string{"container-1", "container-2"}, }, @@ -139,9 +140,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) }) } } diff --git a/pkg/terminal/util.go b/pkg/terminal/util.go index 169bec2af..231b47974 100644 --- a/pkg/terminal/util.go +++ b/pkg/terminal/util.go @@ -61,7 +61,7 @@ func ReadPassword(prompt string) (pw []byte, err error) { } } -func PublicKey(path string, passphrase []byte) (ssh.AuthMethod, error) { +func PublicKey(path string, passphrase []byte) (ssh.Signer, error) { key, err := ioutil.ReadFile(path) if err != nil { return nil, err @@ -75,12 +75,9 @@ func PublicKey(path string, passphrase []byte) (ssh.AuthMethod, error) { if len(passphrase) == 0 { passphrase = ReadPassphrase() } - signer, err = ssh.ParsePrivateKeyWithPassphrase(key, passphrase) - if err != nil { - return nil, err - } + return ssh.ParsePrivateKeyWithPassphrase(key, passphrase) } - return ssh.PublicKeys(signer), nil + return signer, nil } func ReadPassphrase() []byte { diff --git a/pkg/util/utils_linux.go b/pkg/util/utils_linux.go index e4957f442..288137ca5 100644 --- a/pkg/util/utils_linux.go +++ b/pkg/util/utils_linux.go @@ -6,7 +6,6 @@ import ( "path/filepath" "syscall" - "github.com/containers/podman/v2/pkg/rootless" "github.com/containers/psgo" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -53,19 +52,3 @@ func FindDeviceNodes() (map[string]string, error) { return nodes, nil } - -// CheckRootlessUIDRange checks the uid within the rootless container is in the range from /etc/subuid -func CheckRootlessUIDRange(uid int) error { - uids, _, err := rootless.GetConfiguredMappings() - if err != nil { - return err - } - total := 0 - for _, u := range uids { - total += u.Size - } - if uid > total { - return errors.Errorf("requested user's UID %d is too large for the rootless user namespace", uid) - } - return nil -} diff --git a/pkg/util/utils_supported.go b/pkg/util/utils_supported.go index a63c76415..21022eb7c 100644 --- a/pkg/util/utils_supported.go +++ b/pkg/util/utils_supported.go @@ -114,7 +114,7 @@ func GetRootlessPauseProcessPidPath() (string, error) { // files. func GetRootlessPauseProcessPidPathGivenDir(libpodTmpDir string) (string, error) { if libpodTmpDir == "" { - return "", errors.Errorf("must provide non-empty tmporary directory") + return "", errors.Errorf("must provide non-empty temporary directory") } return filepath.Join(libpodTmpDir, "pause.pid"), nil } diff --git a/pkg/util/utils_unsupported.go b/pkg/util/utils_unsupported.go index f8d5a37c1..62805d7c8 100644 --- a/pkg/util/utils_unsupported.go +++ b/pkg/util/utils_unsupported.go @@ -10,8 +10,3 @@ import ( func FindDeviceNodes() (map[string]string, error) { return nil, errors.Errorf("not supported on non-Linux OSes") } - -// CheckRootlessUIDRange is not implemented anywhere except Linux. -func CheckRootlessUIDRange(uid int) error { - return nil -} |