diff options
Diffstat (limited to 'pkg')
55 files changed, 1330 insertions, 363 deletions
diff --git a/pkg/adapter/client.go b/pkg/adapter/client.go index 5774ebe72..a1b2bd507 100644 --- a/pkg/adapter/client.go +++ b/pkg/adapter/client.go @@ -12,7 +12,7 @@ import ( "github.com/varlink/go/varlink" ) -var remoteEndpoint *Endpoint +var remoteEndpoint *Endpoint // nolint: deadcode,unused func (r RemoteRuntime) RemoteEndpoint() (remoteEndpoint *Endpoint, err error) { remoteConfigConnections, err := remoteclientconfig.ReadRemoteConfig(r.config) @@ -26,8 +26,8 @@ func (r RemoteRuntime) RemoteEndpoint() (remoteEndpoint *Endpoint, err error) { remoteEndpoint, err = newBridgeConnection(bridge, nil, r.cmd.LogLevel) // if an environment variable for podman_varlink_address is defined, // we used that as passed - } else if address := os.Getenv("PODMAN_VARLINK_ADDRESS"); address != "" { - logrus.Debug("creating a varlink address based on env variable: %s", address) + } else if address := os.Getenv("PODMAN_VARLINK_ADDRESS"); address != "" { // nolint:gocritic + logrus.Debugf("creating a varlink address based on env variable: %s", address) remoteEndpoint, err = newSocketConnection(address) // if the user provides a remote host, we use it to configure a bridge connection } else if len(r.cmd.RemoteHost) > 0 { @@ -35,7 +35,7 @@ func (r RemoteRuntime) RemoteEndpoint() (remoteEndpoint *Endpoint, err error) { if len(r.cmd.RemoteUserName) < 1 { return nil, errors.New("you must provide a username when providing a remote host name") } - rc := remoteclientconfig.RemoteConnection{r.cmd.RemoteHost, r.cmd.RemoteUserName, false, r.cmd.Port, r.cmd.IdentityFile, r.cmd.IgnoreHosts} + rc := remoteclientconfig.RemoteConnection{r.cmd.RemoteHost, r.cmd.RemoteUserName, false, r.cmd.Port, r.cmd.IdentityFile, r.cmd.IgnoreHosts} // nolint: govet remoteEndpoint, err = newBridgeConnection("", &rc, r.cmd.LogLevel) // if the user has a config file with connections in it } else if len(remoteConfigConnections.Connections) > 0 { @@ -59,7 +59,7 @@ func (r RemoteRuntime) RemoteEndpoint() (remoteEndpoint *Endpoint, err error) { logrus.Debug("creating a varlink address based default root address") remoteEndpoint, err = newSocketConnection(DefaultVarlinkAddress) } - return + return // nolint: nakedret } // Connect provides a varlink connection diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go index fc8b524d6..777605896 100644 --- a/pkg/adapter/containers_remote.go +++ b/pkg/adapter/containers_remote.go @@ -341,7 +341,7 @@ func (r *LocalRuntime) RemoveContainers(ctx context.Context, cli *cliconfig.RmVa failures[ctr] = errors.Wrapf(err, "Failed to evict container: %q", id) continue } - ok = append(ok, string(id)) + ok = append(ok, id) } return ok, failures, nil } @@ -432,7 +432,7 @@ func BatchContainerOp(ctr *Container, opts shared.PsOptions) (shared.BatchContai // Log one or more containers over a varlink connection func (r *LocalRuntime) Log(c *cliconfig.LogsValues, options *logs.LogOptions) error { // GetContainersLogs - reply, err := iopodman.GetContainersLogs().Send(r.Conn, uint64(varlink.More), c.InputArgs, c.Follow, c.Latest, options.Since.Format(time.RFC3339Nano), int64(c.Tail), c.Timestamps) + reply, err := iopodman.GetContainersLogs().Send(r.Conn, uint64(varlink.More), c.InputArgs, c.Follow, c.Latest, options.Since.Format(time.RFC3339Nano), c.Tail, c.Timestamps) if err != nil { return errors.Wrapf(err, "failed to get container logs") } @@ -753,15 +753,15 @@ func (r *LocalRuntime) attach(ctx context.Context, stdin, stdout *os.File, cid s return nil, nil, err } defer cancel() - defer restoreTerminal(oldTermState) + defer restoreTerminal(oldTermState) // nolint: errcheck logrus.SetFormatter(&RawTtyFormatter{}) - term.SetRawTerminal(os.Stdin.Fd()) + term.SetRawTerminal(os.Stdin.Fd()) // nolint: errcheck } reply, err := iopodman.Attach().Send(r.Conn, varlink.Upgrade, cid, detachKeys, start) if err != nil { - restoreTerminal(oldTermState) + restoreTerminal(oldTermState) // nolint: errcheck return nil, nil, err } @@ -769,7 +769,7 @@ func (r *LocalRuntime) attach(ctx context.Context, stdin, stdout *os.File, cid s _, err = reply() if err != nil { - restoreTerminal(oldTermState) + restoreTerminal(oldTermState) // nolint: errcheck return nil, nil, err } @@ -857,7 +857,7 @@ func (r *LocalRuntime) Restart(ctx context.Context, c *cliconfig.RestartValues) useTimeout := c.Flag("timeout").Changed || c.Flag("time").Changed inputTimeout := c.Timeout - if c.Latest { + if c.Latest { // nolint: gocritic lastCtr, err := r.GetLatestContainer() if err != nil { return nil, nil, errors.Wrapf(err, "unable to get latest container") @@ -1042,10 +1042,10 @@ func (r *LocalRuntime) ExecContainer(ctx context.Context, cli *cliconfig.ExecVal return ec, err } defer cancel() - defer restoreTerminal(oldTermState) + defer restoreTerminal(oldTermState) // nolint: errcheck logrus.SetFormatter(&RawTtyFormatter{}) - term.SetRawTerminal(os.Stdin.Fd()) + term.SetRawTerminal(os.Stdin.Fd()) // nolint: errcheck } opts := iopodman.ExecOpts{ @@ -1082,7 +1082,7 @@ func (r *LocalRuntime) ExecContainer(ctx context.Context, cli *cliconfig.ExecVal return ec, err } -func configureVarlinkAttachStdio(reader *bufio.Reader, writer *bufio.Writer, stdin *os.File, stdout *os.File, oldTermState *term.State, resize chan remotecommand.TerminalSize, ecChan chan int) chan error { +func configureVarlinkAttachStdio(reader *bufio.Reader, writer *bufio.Writer, stdin *os.File, stdout *os.File, oldTermState *term.State, resize chan remotecommand.TerminalSize, ecChan chan int) chan error { // nolint: interfacer errChan := make(chan error, 1) // These are the special writers that encode input from the client. varlinkStdinWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.ToStdin) @@ -1092,7 +1092,7 @@ func configureVarlinkAttachStdio(reader *bufio.Reader, writer *bufio.Writer, std go func() { // Read from the wire and direct to stdout or stderr err := virtwriter.Reader(reader, stdout, os.Stderr, nil, nil, ecChan) - defer restoreTerminal(oldTermState) + defer restoreTerminal(oldTermState) // nolint: errcheck sendGenericError(ecChan) errChan <- err }() @@ -1101,13 +1101,13 @@ func configureVarlinkAttachStdio(reader *bufio.Reader, writer *bufio.Writer, std for termResize := range resize { b, err := json.Marshal(termResize) if err != nil { - defer restoreTerminal(oldTermState) + defer restoreTerminal(oldTermState) // nolint: errcheck,staticcheck sendGenericError(ecChan) errChan <- err } _, err = varlinkResizeWriter.Write(b) if err != nil { - defer restoreTerminal(oldTermState) + defer restoreTerminal(oldTermState) // nolint: errcheck,staticcheck sendGenericError(ecChan) errChan <- err } @@ -1117,7 +1117,7 @@ func configureVarlinkAttachStdio(reader *bufio.Reader, writer *bufio.Writer, std // Takes stdinput and sends it over the wire after being encoded go func() { if _, err := io.Copy(varlinkStdinWriter, stdin); err != nil { - defer restoreTerminal(oldTermState) + defer restoreTerminal(oldTermState) // nolint: errcheck sendGenericError(ecChan) errChan <- err } diff --git a/pkg/adapter/pods_remote.go b/pkg/adapter/pods_remote.go index ebd10a92a..4c6eea9a7 100644 --- a/pkg/adapter/pods_remote.go +++ b/pkg/adapter/pods_remote.go @@ -515,10 +515,10 @@ func (p *Pod) GetPodStats(previousContainerStats map[string]*libpod.ContainerSta newStats := varlinkapi.ContainerStatsToLibpodContainerStats(stats) // If the container wasn't running, don't include it // but also suppress the error - if err != nil && errors.Cause(err) != define.ErrCtrStateInvalid { + if err != nil && errors.Cause(err) != define.ErrCtrStateInvalid { // nolint: govet return nil, err } - if err == nil { + if err == nil { // nolint: govet newContainerStats[c.ID()] = &newStats } } diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go index a4ac660ea..c511b70f1 100644 --- a/pkg/adapter/runtime_remote.go +++ b/pkg/adapter/runtime_remote.go @@ -180,7 +180,7 @@ type Pod struct { type remotepod struct { config *libpod.PodConfig state *libpod.PodInspectState - containers []libpod.PodContainerInfo + containers []libpod.PodContainerInfo // nolint: structcheck Runtime *LocalRuntime } @@ -627,7 +627,7 @@ func (r *LocalRuntime) SendFileOverVarlink(source string) (string, error) { return "", err } logrus.Debugf("sending %s over varlink connection", source) - reply, err := iopodman.SendFile().Send(r.Conn, varlink.Upgrade, "", int64(fileInfo.Size())) + reply, err := iopodman.SendFile().Send(r.Conn, varlink.Upgrade, "", fileInfo.Size()) if err != nil { return "", err } @@ -754,9 +754,7 @@ func (r *LocalRuntime) InspectVolumes(ctx context.Context, c *cliconfig.VolumeIn volumes = append(volumes, vol.Name()) } } else { - for _, arg := range c.InputArgs { - volumes = append(volumes, arg) - } + volumes = append(volumes, c.InputArgs...) } for _, vol := range volumes { @@ -855,7 +853,7 @@ func (r *LocalRuntime) SaveImage(ctx context.Context, c *cliconfig.SaveValues) e } } - if err != nil { + if err != nil { // nolint: govet return err } @@ -927,7 +925,7 @@ func IsImageNotFound(err error) bool { if errors.Cause(err) == image.ErrNoSuchImage { return true } - switch err.(type) { + switch err.(type) { // nolint: gocritic case *iopodman.ImageNotFound: return true } @@ -991,7 +989,7 @@ func (r *LocalRuntime) Events(c *cliconfig.EventValues) error { Time: eTime, Type: eType, } - if c.Format == formats.JSONString { + if c.Format == formats.JSONString { // nolint: gocritic jsonStr, err := event.ToJSONString() if err != nil { return errors.Wrapf(err, "unable to format json") @@ -1008,6 +1006,7 @@ func (r *LocalRuntime) Events(c *cliconfig.EventValues) error { return err } } + if _, err := w.Write([]byte("\n")); err != nil { return err } @@ -1040,7 +1039,7 @@ func stringToChangeType(change string) archive.ChangeType { return archive.ChangeAdd case "D": return archive.ChangeDelete - default: + default: // nolint: gocritic,stylecheck logrus.Errorf("'%s' is unknown archive type", change) fallthrough case "C": diff --git a/pkg/api/handlers/compat/changes.go b/pkg/api/handlers/compat/changes.go new file mode 100644 index 000000000..6907c487e --- /dev/null +++ b/pkg/api/handlers/compat/changes.go @@ -0,0 +1,20 @@ +package compat + +import ( + "net/http" + + "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/api/handlers/utils" +) + +func Changes(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value("runtime").(*libpod.Runtime) + + id := utils.GetName(r) + changes, err := runtime.GetDiff("", id) + if err != nil { + utils.InternalServerError(w, err) + return + } + utils.WriteJSON(w, 200, changes) +} diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index c53af0f26..3f6aca502 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -261,6 +261,7 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) { var until time.Time if _, found := r.URL.Query()["until"]; found { + // FIXME: until != since but the logs backend does not yet support until. since, err = util.ParseInputTime(query.Until) if err != nil { utils.BadRequest(w, "until", query.Until, err) diff --git a/pkg/api/handlers/compat/events.go b/pkg/api/handlers/compat/events.go index 0f72ef328..8ef32716d 100644 --- a/pkg/api/handlers/compat/events.go +++ b/pkg/api/handlers/compat/events.go @@ -1,7 +1,6 @@ package compat import ( - "encoding/json" "fmt" "net/http" @@ -10,6 +9,7 @@ import ( "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/api/handlers/utils" "github.com/gorilla/schema" + jsoniter "github.com/json-iterator/go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -48,14 +48,27 @@ func GetEvents(w http.ResponseWriter, r *http.Request) { }() if eventsError != nil { utils.InternalServerError(w, eventsError) + close(eventChannel) return } - coder := json.NewEncoder(w) - coder.SetEscapeHTML(true) + // If client disappears we need to stop listening for events + go func(done <-chan struct{}) { + <-done + close(eventChannel) + }(r.Context().Done()) + // Headers need to be written out before turning Writer() over to json encoder w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) + if flusher, ok := w.(http.Flusher); ok { + flusher.Flush() + } + + json := jsoniter.ConfigCompatibleWithStandardLibrary + coder := json.NewEncoder(w) + coder.SetEscapeHTML(true) + for event := range eventChannel { e := handlers.EventToApiEvent(event) if err := coder.Encode(e); err != nil { diff --git a/pkg/api/handlers/compat/images_search.go b/pkg/api/handlers/compat/images_search.go index 7283b22c4..8da685527 100644 --- a/pkg/api/handlers/compat/images_search.go +++ b/pkg/api/handlers/compat/images_search.go @@ -57,6 +57,7 @@ func SearchImages(w http.ResponseWriter, r *http.Request) { Filter: filter, Limit: query.Limit, } + results, err := image.SearchImages(query.Term, options) if err != nil { utils.BadRequest(w, "term", query.Term, err) diff --git a/pkg/api/handlers/compat/swagger.go b/pkg/api/handlers/compat/swagger.go index cbd8e61fb..f1aabf987 100644 --- a/pkg/api/handlers/compat/swagger.go +++ b/pkg/api/handlers/compat/swagger.go @@ -2,6 +2,7 @@ package compat import ( "github.com/containers/libpod/pkg/api/handlers/utils" + "github.com/containers/storage/pkg/archive" ) // Create container @@ -25,3 +26,12 @@ type swagCtrWaitResponse struct { } } } + +// Object Changes +// swagger:response Changes +type swagChangesResponse struct { + // in:body + Body struct { + Changes []archive.Change + } +} diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go index 5cbfb11eb..086bef847 100644 --- a/pkg/api/handlers/libpod/containers.go +++ b/pkg/api/handlers/libpod/containers.go @@ -285,3 +285,23 @@ func Restore(w http.ResponseWriter, r *http.Request) { } utils.WriteResponse(w, http.StatusOK, entities.RestoreReport{Id: ctr.ID()}) } + +func InitContainer(w http.ResponseWriter, r *http.Request) { + name := utils.GetName(r) + runtime := r.Context().Value("runtime").(*libpod.Runtime) + ctr, err := runtime.LookupContainer(name) + if err != nil { + utils.ContainerNotFound(w, name, err) + return + } + err = ctr.Init(r.Context()) + if errors.Cause(err) == define.ErrCtrStateInvalid { + utils.Error(w, "container already initialized", http.StatusNotModified, err) + return + } + if err != nil { + utils.InternalServerError(w, err) + return + } + utils.WriteResponse(w, http.StatusNoContent, "") +} diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index 850de4598..a42d06205 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -645,3 +645,56 @@ func UntagImage(w http.ResponseWriter, r *http.Request) { } utils.WriteResponse(w, http.StatusCreated, "") } + +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"` + Filters []string `json:"filters"` + TLSVerify bool `json:"tlsVerify"` + }{ + // 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 + } + + options := image.SearchOptions{ + Limit: query.Limit, + } + 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 + } + + searchResults, err := image.SearchImages(query.Term, options) + if err != nil { + utils.BadRequest(w, "term", query.Term, err) + return + } + // Convert from image.SearchResults to entities.ImageSearchReport. We don't + // want to leak any low-level packages into the remote client, which + // requires converting. + reports := make([]entities.ImageSearchReport, len(searchResults)) + for i := range searchResults { + reports[i].Index = searchResults[i].Index + reports[i].Name = searchResults[i].Name + reports[i].Description = searchResults[i].Index + reports[i].Stars = searchResults[i].Stars + reports[i].Official = searchResults[i].Official + reports[i].Automated = searchResults[i].Automated + } + + utils.WriteResponse(w, http.StatusOK, reports) +} diff --git a/pkg/api/server/handler_api.go b/pkg/api/server/handler_api.go index 30a1680c9..7a7db12f3 100644 --- a/pkg/api/server/handler_api.go +++ b/pkg/api/server/handler_api.go @@ -19,7 +19,7 @@ func (s *APIServer) APIHandler(h http.HandlerFunc) http.HandlerFunc { if err != nil { buf := make([]byte, 1<<20) n := runtime.Stack(buf, true) - log.Warnf("Recovering from podman handler panic: %v, %s", err, buf[:n]) + log.Warnf("Recovering from API handler panic: %v, %s", err, buf[:n]) // Try to inform client things went south... won't work if handler already started writing response body utils.InternalServerError(w, fmt.Errorf("%v", err)) } @@ -27,12 +27,7 @@ func (s *APIServer) APIHandler(h http.HandlerFunc) http.HandlerFunc { // Wrapper to hide some boiler plate fn := func(w http.ResponseWriter, r *http.Request) { - // Connection counting, ugh. Needed to support the sliding window for idle checking. - s.ConnectionCh <- EnterHandler - defer func() { s.ConnectionCh <- ExitHandler }() - - log.Debugf("APIHandler -- Method: %s URL: %s (conn %d/%d)", - r.Method, r.URL.String(), s.ActiveConnections, s.TotalConnections) + log.Debugf("APIHandler -- Method: %s URL: %s", r.Method, r.URL.String()) if err := r.ParseForm(); err != nil { log.Infof("Failed Request: unable to parse form: %q", err) diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index 9ee900a11..8b9a9e312 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -1377,5 +1377,62 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // 500: // $ref: "#/responses/InternalError" r.HandleFunc(VersionedPath("/libpod/containers/{name}/restore"), s.APIHandler(libpod.Restore)).Methods(http.MethodPost) + // swagger:operation GET /containers/{name}/changes libpod libpodChangesContainer + // swagger:operation GET /libpod/containers/{name}/changes compat changesContainer + // --- + // tags: + // - containers + // - containers (compat) + // summary: Report on changes to container's filesystem; adds, deletes or modifications. + // description: | + // Returns which files in a container's filesystem have been added, deleted, or modified. The Kind of modification can be one of: + // + // 0: Modified + // 1: Added + // 2: Deleted + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: the name or id of the container + // responses: + // 200: + // description: Array of Changes + // content: + // application/json: + // schema: + // $ref: "#/responses/Changes" + // 404: + // $ref: "#/responses/NoSuchContainer" + // 500: + // $ref: "#/responses/InternalError" + r.HandleFunc(VersionedPath("/containers/{name}/changes"), s.APIHandler(compat.Changes)) + r.HandleFunc("/containers/{name}/changes", s.APIHandler(compat.Changes)) + r.HandleFunc(VersionedPath("/libpod/containers/{name}/changes"), s.APIHandler(compat.Changes)) + // swagger:operation POST /libpod/containers/{name}/init libpod libpodInitContainer + // --- + // tags: + // - containers + // summary: Initialize a container + // description: Performs all tasks necessary for initializing the container but does not start the container. + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: the name or ID of the container + // produces: + // - application/json + // responses: + // 204: + // description: no error + // 304: + // description: container already initialized + // 404: + // $ref: "#/responses/NoSuchContainer" + // 500: + // $ref: "#/responses/InternalError" + r.HandleFunc(VersionedPath("/libpod/containers/{name}/init"), s.APIHandler(libpod.InitContainer)).Methods(http.MethodPost) return nil } diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go index d45423096..7dd887037 100644 --- a/pkg/api/server/register_images.go +++ b/pkg/api/server/register_images.go @@ -919,7 +919,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // $ref: "#/responses/DocsSearchResponse" // 500: // $ref: '#/responses/InternalError' - r.Handle(VersionedPath("/libpod/images/search"), s.APIHandler(compat.SearchImages)).Methods(http.MethodGet) + r.Handle(VersionedPath("/libpod/images/search"), s.APIHandler(libpod.SearchImages)).Methods(http.MethodGet) // swagger:operation DELETE /libpod/images/{name:.*} libpod libpodRemoveImage // --- // tags: @@ -1125,5 +1125,36 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error { // 500: // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/libpod/images/{name:.*}/untag"), s.APIHandler(libpod.UntagImage)).Methods(http.MethodPost) + + // swagger:operation GET /libpod/images/{name}/changes libpod libpodChangesImages + // --- + // tags: + // - images + // summary: Report on changes to images's filesystem; adds, deletes or modifications. + // description: | + // Returns which files in a images's filesystem have been added, deleted, or modified. The Kind of modification can be one of: + // + // 0: Modified + // 1: Added + // 2: Deleted + // parameters: + // - in: path + // name: name + // type: string + // required: true + // description: the name or id of the container + // responses: + // 200: + // description: Array of Changes + // content: + // application/json: + // schema: + // $ref: "#/responses/Changes" + // 404: + // $ref: "#/responses/NoSuchContainer" + // 500: + // $ref: "#/responses/InternalError" + r.HandleFunc(VersionedPath("/libpod/images/{name}/changes"), s.APIHandler(compat.Changes)) + return nil } diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go index 59f1f95cb..5f1a86183 100644 --- a/pkg/api/server/server.go +++ b/pkg/api/server/server.go @@ -2,11 +2,14 @@ package server import ( "context" + "log" "net" "net/http" "os" "os/signal" + "runtime" "strings" + "sync" "syscall" "time" @@ -20,26 +23,19 @@ import ( ) type APIServer struct { - http.Server // The HTTP work happens here - *schema.Decoder // Decoder for Query parameters to structs - context.Context // Context to carry objects to handlers - *libpod.Runtime // Where the real work happens - net.Listener // mux for routing HTTP API calls to libpod routines - context.CancelFunc // Stop APIServer - *time.Timer // Hold timer for sliding window - time.Duration // Duration of client access sliding window - ActiveConnections uint64 // Number of handlers holding a connection - TotalConnections uint64 // Number of connections handled - ConnectionCh chan int // Channel for signalling handler enter/exit + http.Server // The HTTP work happens here + *schema.Decoder // Decoder for Query parameters to structs + context.Context // Context to carry objects to handlers + *libpod.Runtime // Where the real work happens + net.Listener // mux for routing HTTP API calls to libpod routines + context.CancelFunc // Stop APIServer + idleTracker *IdleTracker // Track connections to support idle shutdown } // Number of seconds to wait for next request, if exceeded shutdown server const ( DefaultServiceDuration = 300 * time.Second UnlimitedServiceDuration = 0 * time.Second - EnterHandler = 1 - ExitHandler = -1 - NOOPHandler = 0 ) // NewServer will create and configure a new API server with all defaults @@ -56,7 +52,7 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li // If listener not provided try socket activation protocol if listener == nil { if _, found := os.LookupEnv("LISTEN_FDS"); !found { - return nil, errors.Errorf("Cannot create Server, no listener provided and socket activation protocol is not active.") + return nil, errors.Errorf("Cannot create API Server, no listener provided and socket activation protocol is not active.") } listeners, err := activation.Listeners() @@ -70,17 +66,20 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li } router := mux.NewRouter().UseEncodedPath() + idle := NewIdleTracker(duration) + server := APIServer{ Server: http.Server{ Handler: router, ReadHeaderTimeout: 20 * time.Second, IdleTimeout: duration, + ConnState: idle.ConnState, + ErrorLog: log.New(logrus.StandardLogger().Out, "", 0), }, - Decoder: handlers.NewAPIDecoder(), - Runtime: runtime, - Listener: *listener, - Duration: duration, - ConnectionCh: make(chan int), + Decoder: handlers.NewAPIDecoder(), + idleTracker: idle, + Listener: *listener, + Runtime: runtime, } router.NotFoundHandler = http.HandlerFunc( @@ -120,11 +119,11 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li router.Walk(func(route *mux.Route, r *mux.Router, ancestors []*mux.Route) error { // nolint path, err := route.GetPathTemplate() if err != nil { - path = "" + path = "<N/A>" } methods, err := route.GetMethods() if err != nil { - methods = []string{} + methods = []string{"<N/A>"} } logrus.Debugf("Methods: %s Path: %s", strings.Join(methods, ", "), path) return nil @@ -136,24 +135,20 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li // Serve starts responding to HTTP requests func (s *APIServer) Serve() error { - // This is initialized here as Timer is not needed until Serve'ing - if s.Duration > 0 { - s.Timer = time.AfterFunc(s.Duration, func() { - s.ConnectionCh <- NOOPHandler - }) - go s.ReadChannelWithTimeout() - } else { - go s.ReadChannelNoTimeout() - } - sigChan := make(chan os.Signal, 1) signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM) errChan := make(chan error, 1) go func() { + <-s.idleTracker.Done() + logrus.Debugf("API Server idle for %v", s.idleTracker.Duration) + _ = s.Shutdown() + }() + + go func() { err := s.Server.Serve(s.Listener) if err != nil && err != http.ErrServerClosed { - errChan <- errors.Wrap(err, "Failed to start APIServer") + errChan <- errors.Wrap(err, "failed to start API server") return } errChan <- nil @@ -169,72 +164,30 @@ func (s *APIServer) Serve() error { return nil } -func (s *APIServer) ReadChannelWithTimeout() { - // stalker to count the connections. Should the timer expire it will shutdown the service. - for delta := range s.ConnectionCh { - switch delta { - case EnterHandler: - s.Timer.Stop() - s.ActiveConnections += 1 - s.TotalConnections += 1 - case ExitHandler: - s.Timer.Stop() - s.ActiveConnections -= 1 - if s.ActiveConnections == 0 { - // Server will be shutdown iff the timer expires before being reset or stopped - s.Timer = time.AfterFunc(s.Duration, func() { - if err := s.Shutdown(); err != nil { - logrus.Errorf("Failed to shutdown APIServer: %v", err) - os.Exit(1) - } - }) - } else { - s.Timer.Reset(s.Duration) - } - case NOOPHandler: - // push the check out another duration... - s.Timer.Reset(s.Duration) - default: - logrus.Warnf("ConnectionCh received unsupported input %d", delta) - } - } -} - -func (s *APIServer) ReadChannelNoTimeout() { - // stalker to count the connections. - for delta := range s.ConnectionCh { - switch delta { - case EnterHandler: - s.ActiveConnections += 1 - s.TotalConnections += 1 - case ExitHandler: - s.ActiveConnections -= 1 - case NOOPHandler: - default: - logrus.Warnf("ConnectionCh received unsupported input %d", delta) - } - } -} - // Shutdown is a clean shutdown waiting on existing clients func (s *APIServer) Shutdown() error { + if logrus.IsLevelEnabled(logrus.DebugLevel) { + _, file, line, _ := runtime.Caller(1) + logrus.Debugf("APIServer.Shutdown by %s:%d, %d/%d connection(s)", + file, line, s.idleTracker.ActiveConnections(), s.idleTracker.TotalConnections()) + } + // Duration == 0 flags no auto-shutdown of the server - if s.Duration == 0 { + if s.idleTracker.Duration == 0 { logrus.Debug("APIServer.Shutdown ignored as Duration == 0") return nil } - logrus.Debugf("APIServer.Shutdown called %v, conn %d/%d", time.Now(), s.ActiveConnections, s.TotalConnections) - // Gracefully shutdown server - ctx, cancel := context.WithTimeout(context.Background(), s.Duration) + // Gracefully shutdown server, duration of wait same as idle window + ctx, cancel := context.WithTimeout(context.Background(), s.idleTracker.Duration) defer cancel() - go func() { err := s.Server.Shutdown(ctx) if err != nil && err != context.Canceled && err != http.ErrServerClosed { logrus.Errorf("Failed to cleanly shutdown APIServer: %s", err.Error()) } }() + <-ctx.Done() return nil } @@ -242,3 +195,55 @@ func (s *APIServer) Shutdown() error { func (s *APIServer) Close() error { return s.Server.Close() } + +type IdleTracker struct { + active map[net.Conn]struct{} + total int + mux sync.Mutex + timer *time.Timer + Duration time.Duration +} + +func NewIdleTracker(idle time.Duration) *IdleTracker { + return &IdleTracker{ + active: make(map[net.Conn]struct{}), + Duration: idle, + timer: time.NewTimer(idle), + } +} + +func (t *IdleTracker) ConnState(conn net.Conn, state http.ConnState) { + t.mux.Lock() + defer t.mux.Unlock() + + oldActive := len(t.active) + logrus.Debugf("IdleTracker %p:%v %d/%d connection(s)", conn, state, t.ActiveConnections(), t.TotalConnections()) + switch state { + case http.StateNew, http.StateActive, http.StateHijacked: + t.active[conn] = struct{}{} + // stop the timer if we transitioned from idle + if oldActive == 0 { + t.timer.Stop() + } + t.total += 1 + case http.StateIdle, http.StateClosed: + delete(t.active, conn) + // Restart the timer if we've become idle + if oldActive > 0 && len(t.active) == 0 { + t.timer.Stop() + t.timer.Reset(t.Duration) + } + } +} + +func (t *IdleTracker) ActiveConnections() int { + return len(t.active) +} + +func (t *IdleTracker) TotalConnections() int { + return t.total +} + +func (t *IdleTracker) Done() <-chan time.Time { + return t.timer.C +} diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go index a188d73a0..963f0ec57 100644 --- a/pkg/bindings/containers/containers.go +++ b/pkg/bindings/containers/containers.go @@ -12,6 +12,7 @@ import ( "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/bindings" "github.com/containers/libpod/pkg/domain/entities" + "github.com/pkg/errors" ) // List obtains a list of containers in local storage. All parameters to this method are optional. @@ -316,3 +317,21 @@ func Export(ctx context.Context, nameOrID string, w io.Writer) error { } return response.Process(nil) } + +// 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 { + conn, err := bindings.GetClient(ctx) + if err != nil { + return err + } + response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/init", nil, nameOrID) + if err != nil { + return err + } + if response.StatusCode == http.StatusNotModified { + return errors.Wrapf(define.ErrCtrStateInvalid, "container %s has already been created in runtime", nameOrID) + } + return response.Process(nil) +} diff --git a/pkg/bindings/containers/diff.go b/pkg/bindings/containers/diff.go new file mode 100644 index 000000000..82070ca9a --- /dev/null +++ b/pkg/bindings/containers/diff.go @@ -0,0 +1,24 @@ +package containers + +import ( + "context" + "net/http" + + "github.com/containers/libpod/pkg/bindings" + "github.com/containers/storage/pkg/archive" +) + +// Diff provides the changes between two container layers +func Diff(ctx context.Context, nameOrId string) ([]archive.Change, error) { + conn, err := bindings.GetClient(ctx) + if err != nil { + return nil, err + } + + response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/changes", nil, nameOrId) + if err != nil { + return nil, err + } + var changes []archive.Change + return changes, response.Process(&changes) +} diff --git a/pkg/bindings/images/diff.go b/pkg/bindings/images/diff.go new file mode 100644 index 000000000..cfdd06a97 --- /dev/null +++ b/pkg/bindings/images/diff.go @@ -0,0 +1,24 @@ +package images + +import ( + "context" + "net/http" + + "github.com/containers/libpod/pkg/bindings" + "github.com/containers/storage/pkg/archive" +) + +// Diff provides the changes between two container layers +func Diff(ctx context.Context, nameOrId string) ([]archive.Change, error) { + conn, err := bindings.GetClient(ctx) + if err != nil { + return nil, err + } + + response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/changes", nil, nameOrId) + if err != nil { + return nil, err + } + var changes []archive.Change + return changes, response.Process(&changes) +} diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go index 1b3df609b..3550c3968 100644 --- a/pkg/bindings/images/images.go +++ b/pkg/bindings/images/images.go @@ -2,7 +2,6 @@ package images import ( "context" - "errors" "fmt" "io" "net/http" @@ -13,6 +12,7 @@ import ( "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/bindings" "github.com/containers/libpod/pkg/domain/entities" + "github.com/pkg/errors" ) // Exists a lightweight way to determine if an image exists in local storage. It returns a @@ -308,3 +308,34 @@ func Push(ctx context.Context, source string, destination string, options entiti _, err = conn.DoRequest(nil, http.MethodPost, path, params) return err } + +// 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) { + 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)) + for _, f := range opts.Filters { + params.Set("filters", f) + } + + if opts.TLSVerify != types.OptionalBoolUndefined { + val := bool(opts.TLSVerify == types.OptionalBoolTrue) + params.Set("tlsVerify", strconv.FormatBool(val)) + } + + response, err := conn.DoRequest(nil, http.MethodGet, "/images/search", params) + if err != nil { + return nil, err + } + + results := []entities.ImageSearchReport{} + if err := response.Process(&results); err != nil { + return nil, err + } + + return results, nil +} diff --git a/pkg/bindings/images/search.go b/pkg/bindings/images/search.go deleted file mode 100644 index 183ff3d77..000000000 --- a/pkg/bindings/images/search.go +++ /dev/null @@ -1,41 +0,0 @@ -package images - -import ( - "context" - "net/http" - "net/url" - "strconv" - - "github.com/containers/libpod/libpod/image" - "github.com/containers/libpod/pkg/bindings" -) - -// Search looks for the given image (term) in container image registries. The optional limit parameter sets -// a maximum number of results returned. The optional filters parameter allow for more specific image -// searches. -func Search(ctx context.Context, term string, limit *int, filters map[string][]string) ([]image.SearchResult, error) { - var ( - searchResults []image.SearchResult - ) - conn, err := bindings.GetClient(ctx) - if err != nil { - return nil, err - } - params := url.Values{} - params.Set("term", term) - if limit != nil { - params.Set("limit", strconv.Itoa(*limit)) - } - if filters != nil { - stringFilter, err := bindings.FiltersToString(filters) - if err != nil { - return nil, err - } - params.Set("filters", stringFilter) - } - response, err := conn.DoRequest(nil, http.MethodGet, "/images/search", params) - if err != nil { - return searchResults, nil - } - return searchResults, response.Process(&searchResults) -} diff --git a/pkg/bindings/system/info.go b/pkg/bindings/system/info.go index f8269cfd8..13e12645d 100644 --- a/pkg/bindings/system/info.go +++ b/pkg/bindings/system/info.go @@ -9,15 +9,15 @@ import ( ) // Info returns information about the libpod environment and its stores -func Info(ctx context.Context) (define.Info, error) { +func Info(ctx context.Context) (*define.Info, error) { info := define.Info{} conn, err := bindings.GetClient(ctx) if err != nil { - return info, err + return nil, err } response, err := conn.DoRequest(nil, http.MethodGet, "/info", nil) if err != nil { - return info, err + return nil, err } - return info, response.Process(&info) + return &info, response.Process(&info) } diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go index a31181958..c6501ac9e 100644 --- a/pkg/bindings/test/containers_test.go +++ b/pkg/bindings/test/containers_test.go @@ -410,4 +410,125 @@ var _ = Describe("Podman containers ", func() { _, err = containers.Top(bt.conn, cid, []string{"Me,Neither"}) Expect(err).To(BeNil()) }) + + It("podman bogus container does not exist in local storage", func() { + // Bogus container existence check should fail + containerExists, err := containers.Exists(bt.conn, "foobar") + Expect(err).To(BeNil()) + Expect(containerExists).To(BeFalse()) + }) + + It("podman container exists in local storage by name", func() { + // Container existence check by name should work + var name = "top" + _, err := bt.RunTopContainer(&name, &bindings.PFalse, nil) + Expect(err).To(BeNil()) + containerExists, err := containers.Exists(bt.conn, name) + Expect(err).To(BeNil()) + Expect(containerExists).To(BeTrue()) + }) + + It("podman container exists in local storage by ID", func() { + // Container existence check by ID should work + var name = "top" + cid, err := bt.RunTopContainer(&name, &bindings.PFalse, nil) + Expect(err).To(BeNil()) + containerExists, err := containers.Exists(bt.conn, cid) + Expect(err).To(BeNil()) + Expect(containerExists).To(BeTrue()) + }) + + It("podman container exists in local storage by short ID", func() { + // Container existence check by short ID should work + var name = "top" + cid, err := bt.RunTopContainer(&name, &bindings.PFalse, nil) + Expect(err).To(BeNil()) + containerExists, err := containers.Exists(bt.conn, cid[0:12]) + Expect(err).To(BeNil()) + Expect(containerExists).To(BeTrue()) + }) + + It("podman kill bogus container", func() { + // Killing bogus container should return 404 + err := containers.Kill(bt.conn, "foobar", "SIGTERM") + Expect(err).ToNot(BeNil()) + code, _ := bindings.CheckResponseCode(err) + Expect(code).To(BeNumerically("==", http.StatusNotFound)) + }) + + It("podman kill a running container by name with SIGINT", func() { + // Killing a running container should work + var name = "top" + _, err := bt.RunTopContainer(&name, &bindings.PFalse, nil) + Expect(err).To(BeNil()) + err = containers.Kill(bt.conn, name, "SIGINT") + Expect(err).To(BeNil()) + _, err = containers.Exists(bt.conn, name) + Expect(err).To(BeNil()) + }) + + It("podman kill a running container by ID with SIGTERM", func() { + // Killing a running container by ID should work + var name = "top" + cid, err := bt.RunTopContainer(&name, &bindings.PFalse, nil) + Expect(err).To(BeNil()) + err = containers.Kill(bt.conn, cid, "SIGTERM") + Expect(err).To(BeNil()) + _, err = containers.Exists(bt.conn, cid) + Expect(err).To(BeNil()) + }) + + It("podman kill a running container by ID with SIGKILL", func() { + // Killing a running container by ID with TERM should work + var name = "top" + cid, err := bt.RunTopContainer(&name, &bindings.PFalse, nil) + Expect(err).To(BeNil()) + err = containers.Kill(bt.conn, cid, "SIGKILL") + Expect(err).To(BeNil()) + }) + + It("podman kill a running container by bogus signal", func() { + //Killing a running container by bogus signal should fail + var name = "top" + cid, err := bt.RunTopContainer(&name, &bindings.PFalse, nil) + Expect(err).To(BeNil()) + err = containers.Kill(bt.conn, cid, "foobar") + Expect(err).ToNot(BeNil()) + code, _ := bindings.CheckResponseCode(err) + Expect(code).To(BeNumerically("==", http.StatusInternalServerError)) + }) + + It("podman kill latest container with SIGTERM", 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) + Expect(err).To(BeNil()) + err = containers.Kill(bt.conn, containerLatestList[0].Names(), "SIGTERM") + Expect(err).To(BeNil()) + }) + + It("container init on a bogus container", func() { + err := containers.ContainerInit(bt.conn, "doesnotexist") + Expect(err).ToNot(BeNil()) + code, _ := bindings.CheckResponseCode(err) + Expect(code).To(BeNumerically("==", http.StatusNotFound)) + }) + + It("container init", func() { + s := specgen.NewSpecGenerator(alpine.name) + ctr, err := containers.CreateWithSpec(bt.conn, s) + Expect(err).To(BeNil()) + err = containers.ContainerInit(bt.conn, ctr.ID) + Expect(err).To(BeNil()) + // trying to init again should be an error + err = containers.ContainerInit(bt.conn, ctr.ID) + Expect(err).ToNot(BeNil()) + }) + }) diff --git a/pkg/bindings/test/images_test.go b/pkg/bindings/test/images_test.go index 992720196..58210efd0 100644 --- a/pkg/bindings/test/images_test.go +++ b/pkg/bindings/test/images_test.go @@ -314,11 +314,11 @@ var _ = Describe("Podman images", func() { }) It("Search for an image", func() { - imgs, err := images.Search(bt.conn, "alpine", nil, nil) + reports, err := images.Search(bt.conn, "alpine", entities.ImageSearchOptions{}) Expect(err).To(BeNil()) - Expect(len(imgs)).To(BeNumerically(">", 1)) + Expect(len(reports)).To(BeNumerically(">", 1)) var foundAlpine bool - for _, i := range imgs { + for _, i := range reports { if i.Name == "docker.io/library/alpine" { foundAlpine = true break @@ -327,23 +327,20 @@ var _ = Describe("Podman images", func() { Expect(foundAlpine).To(BeTrue()) // Search for alpine with a limit of 10 - ten := 10 - imgs, err = images.Search(bt.conn, "docker.io/alpine", &ten, nil) + reports, err = images.Search(bt.conn, "docker.io/alpine", entities.ImageSearchOptions{Limit: 10}) Expect(err).To(BeNil()) - Expect(len(imgs)).To(BeNumerically("<=", 10)) + Expect(len(reports)).To(BeNumerically("<=", 10)) // Search for alpine with stars greater than 100 - filters := make(map[string][]string) - filters["stars"] = []string{"100"} - imgs, err = images.Search(bt.conn, "docker.io/alpine", nil, filters) + reports, err = images.Search(bt.conn, "docker.io/alpine", entities.ImageSearchOptions{Filters: []string{"stars=100"}}) Expect(err).To(BeNil()) - for _, i := range imgs { + for _, i := range reports { Expect(i.Stars).To(BeNumerically(">=", 100)) } // Search with a fqdn - imgs, err = images.Search(bt.conn, "quay.io/libpod/alpine_nginx", nil, nil) - Expect(len(imgs)).To(BeNumerically(">=", 1)) + reports, err = images.Search(bt.conn, "quay.io/libpod/alpine_nginx", entities.ImageSearchOptions{}) + Expect(len(reports)).To(BeNumerically(">=", 1)) }) It("Prune images", func() { diff --git a/pkg/cgroups/pids.go b/pkg/cgroups/pids.go index 65b9b5b34..92f82553b 100644 --- a/pkg/cgroups/pids.go +++ b/pkg/cgroups/pids.go @@ -44,8 +44,12 @@ func (c *pidHandler) Destroy(ctr *CgroupControl) error { // Stat fills a metrics structure with usage stats for the controller func (c *pidHandler) Stat(ctr *CgroupControl, m *Metrics) error { - var PIDRoot string + if ctr.path != "" { + // nothing we can do to retrieve the pids.current path + return nil + } + var PIDRoot string if ctr.cgroup2 { PIDRoot = filepath.Join(cgroupRoot, ctr.path) } else { diff --git a/pkg/domain/entities/container_ps.go b/pkg/domain/entities/container_ps.go index ceafecebc..f07b0364f 100644 --- a/pkg/domain/entities/container_ps.go +++ b/pkg/domain/entities/container_ps.go @@ -1,19 +1,23 @@ package entities import ( + "fmt" "sort" + "strconv" "strings" + "time" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" "github.com/cri-o/ocicni/pkg/ocicni" + "github.com/docker/go-units" "github.com/pkg/errors" ) // Listcontainer describes a container suitable for listing type ListContainer struct { // Container command - Command []string + Cmd []string // Container creation time Created int64 // If container has exited/stopped @@ -33,7 +37,7 @@ type ListContainer struct { // User volume mounts Mounts []string // The names assigned to the container - Names []string + ContainerNames []string // Namespaces the container belongs to. Requires the // namespace boolean to be true Namespaces ListContainerNamespaces @@ -46,13 +50,69 @@ type ListContainer struct { // boolean to be set PodName string // Port mappings - Ports []ocicni.PortMapping + PortMappings []ocicni.PortMapping // Size of the container rootfs. Requires the size boolean to be true - Size *shared.ContainerSize + ContainerSize *shared.ContainerSize // Time when container started StartedAt int64 // State of container - State string + ContainerState string +} + +// State returns the container state in human duration +func (l ListContainer) State() string { + var state string + switch l.ContainerState { + case "running": + t := units.HumanDuration(time.Since(time.Unix(l.StartedAt, 0))) + state = "Up " + t + " ago" + case "configured": + state = "Created" + case "exited", "stopped": + t := units.HumanDuration(time.Since(time.Unix(l.ExitedAt, 0))) + state = fmt.Sprintf("Exited (%d) %s ago", l.ExitCode, t) + default: + state = l.ContainerState + } + return state +} + +// Command returns the container command in string format +func (l ListContainer) Command() string { + return strings.Join(l.Cmd, " ") +} + +// Size returns the rootfs and virtual sizes in human duration in +// and output form (string) suitable for ps +func (l ListContainer) Size() string { + virt := units.HumanSizeWithPrecision(float64(l.ContainerSize.RootFsSize), 3) + s := units.HumanSizeWithPrecision(float64(l.ContainerSize.RwSize), 3) + return fmt.Sprintf("%s (virtual %s)", s, virt) +} + +// Names returns the container name in string format +func (l ListContainer) Names() string { + return l.ContainerNames[0] +} + +// Ports converts from Portmappings to the string form +// required by ps +func (l ListContainer) Ports() string { + if len(l.PortMappings) < 1 { + return "" + } + return portsToString(l.PortMappings) +} + +// CreatedAt returns the container creation time in string format. podman +// and docker both return a timestamped value for createdat +func (l ListContainer) CreatedAt() string { + return time.Unix(l.Created, 0).String() +} + +// CreateHuman allows us to output the created time in human readable format +func (l ListContainer) CreatedHuman() string { + return units.HumanDuration(time.Since(time.Unix(l.Created, 0))) + " ago" } // ListContainer Namespaces contains the identifiers of the container's Linux namespaces @@ -93,7 +153,7 @@ func (a SortListContainers) Swap(i, j int) { a[i], a[j] = a[j], a[i] } type psSortedCommand struct{ SortListContainers } func (a psSortedCommand) Less(i, j int) bool { - return strings.Join(a.SortListContainers[i].Command, " ") < strings.Join(a.SortListContainers[j].Command, " ") + return strings.Join(a.SortListContainers[i].Cmd, " ") < strings.Join(a.SortListContainers[j].Cmd, " ") } type psSortedId struct{ SortListContainers } @@ -111,7 +171,7 @@ func (a psSortedImage) Less(i, j int) bool { type psSortedNames struct{ SortListContainers } func (a psSortedNames) Less(i, j int) bool { - return a.SortListContainers[i].Names[0] < a.SortListContainers[j].Names[0] + return a.SortListContainers[i].ContainerNames[0] < a.SortListContainers[j].ContainerNames[0] } type psSortedPod struct{ SortListContainers } @@ -129,16 +189,16 @@ func (a psSortedRunningFor) Less(i, j int) bool { type psSortedStatus struct{ SortListContainers } func (a psSortedStatus) Less(i, j int) bool { - return a.SortListContainers[i].State < a.SortListContainers[j].State + return a.SortListContainers[i].ContainerState < a.SortListContainers[j].ContainerState } type psSortedSize struct{ SortListContainers } func (a psSortedSize) Less(i, j int) bool { - if a.SortListContainers[i].Size == nil || a.SortListContainers[j].Size == nil { + if a.SortListContainers[i].ContainerSize == nil || a.SortListContainers[j].ContainerSize == nil { return false } - return a.SortListContainers[i].Size.RootFsSize < a.SortListContainers[j].Size.RootFsSize + return a.SortListContainers[i].ContainerSize.RootFsSize < a.SortListContainers[j].ContainerSize.RootFsSize } type PsSortedCreateTime struct{ SortListContainers } @@ -172,3 +232,94 @@ func SortPsOutput(sortBy string, psOutput SortListContainers) (SortListContainer } return psOutput, nil } + +// portsToString converts the ports used to a string of the from "port1, port2" +// and also groups a continuous list of ports into a readable format. +func portsToString(ports []ocicni.PortMapping) string { + type portGroup struct { + first int32 + last int32 + } + var portDisplay []string + if len(ports) == 0 { + return "" + } + //Sort the ports, so grouping continuous ports become easy. + sort.Slice(ports, func(i, j int) bool { + return comparePorts(ports[i], ports[j]) + }) + + // portGroupMap is used for grouping continuous ports. + portGroupMap := make(map[string]*portGroup) + var groupKeyList []string + + for _, v := range ports { + + hostIP := v.HostIP + if hostIP == "" { + hostIP = "0.0.0.0" + } + // If hostPort and containerPort are not same, consider as individual port. + if v.ContainerPort != v.HostPort { + portDisplay = append(portDisplay, fmt.Sprintf("%s:%d->%d/%s", hostIP, v.HostPort, v.ContainerPort, v.Protocol)) + continue + } + + portMapKey := fmt.Sprintf("%s/%s", hostIP, v.Protocol) + + portgroup, ok := portGroupMap[portMapKey] + if !ok { + portGroupMap[portMapKey] = &portGroup{first: v.ContainerPort, last: v.ContainerPort} + // This list is required to traverse portGroupMap. + groupKeyList = append(groupKeyList, portMapKey) + continue + } + + if portgroup.last == (v.ContainerPort - 1) { + portgroup.last = v.ContainerPort + continue + } + } + // For each portMapKey, format group list and appned to output string. + for _, portKey := range groupKeyList { + group := portGroupMap[portKey] + portDisplay = append(portDisplay, formatGroup(portKey, group.first, group.last)) + } + return strings.Join(portDisplay, ", ") +} + +func comparePorts(i, j ocicni.PortMapping) bool { + if i.ContainerPort != j.ContainerPort { + return i.ContainerPort < j.ContainerPort + } + + if i.HostIP != j.HostIP { + return i.HostIP < j.HostIP + } + + if i.HostPort != j.HostPort { + return i.HostPort < j.HostPort + } + + return i.Protocol < j.Protocol +} + +// formatGroup returns the group as <IP:startPort:lastPort->startPort:lastPort/Proto> +// e.g 0.0.0.0:1000-1006->1000-1006/tcp. +func formatGroup(key string, start, last int32) string { + parts := strings.Split(key, "/") + groupType := parts[0] + var ip string + if len(parts) > 1 { + ip = parts[0] + groupType = parts[1] + } + group := strconv.Itoa(int(start)) + if start != last { + group = fmt.Sprintf("%s-%d", group, last) + } + if ip != "" { + group = fmt.Sprintf("%s:%s->%s", ip, group, group) + } + return fmt.Sprintf("%s/%s", group, groupType) +} diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 5d302058b..4508f9c2c 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -172,6 +172,26 @@ type AttachOptions struct { Stderr *os.File } +// ContainerLogsOptions describes the options to extract container logs. +type ContainerLogsOptions struct { + // Show extra details provided to the logs. + Details bool + // Follow the log output. + Follow bool + // Display logs for the latest container only. Ignored on the remote client. + Latest bool + // Show container names in the output. + Names bool + // Show logs since this timestamp. + Since time.Time + // Number of lines to display at the end of the output. + Tail int64 + // Show timestamps in the logs. + Timestamps bool + // Write the logs to Writer. + Writer io.Writer +} + // ExecOptions describes the cli values to exec into // a container type ExecOptions struct { @@ -245,3 +265,35 @@ type ContainerRunReport struct { ExitCode int Id string } + +// ContainerCleanupOptions are the CLI values for the +// cleanup command +type ContainerCleanupOptions struct { + All bool + Latest bool + Remove bool + RemoveImage bool +} + +// ContainerCleanupReport describes the response from a +// container cleanup +type ContainerCleanupReport struct { + CleanErr error + Id string + RmErr error + RmiErr error +} + +// ContainerInitOptions describes input options +// for the container init cli +type ContainerInitOptions struct { + All bool + Latest bool +} + +// ContainerInitReport describes the results of a +// container init +type ContainerInitReport struct { + Err error + Id string +} diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 576ce1658..d23006a38 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -9,29 +9,34 @@ import ( type ContainerEngine interface { ContainerAttach(ctx context.Context, nameOrId string, options AttachOptions) error - ContainerCommit(ctx context.Context, nameOrId string, options CommitOptions) (*CommitReport, error) ContainerCheckpoint(ctx context.Context, namesOrIds []string, options CheckpointOptions) ([]*CheckpointReport, error) - ContainerRestore(ctx context.Context, namesOrIds []string, options RestoreOptions) ([]*RestoreReport, error) + ContainerCleanup(ctx context.Context, namesOrIds []string, options ContainerCleanupOptions) ([]*ContainerCleanupReport, error) + ContainerCommit(ctx context.Context, nameOrId string, options CommitOptions) (*CommitReport, 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) (int, error) ContainerExists(ctx context.Context, nameOrId string) (*BoolReport, error) - ContainerInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]*ContainerInspectReport, error) ContainerExport(ctx context.Context, nameOrId string, options ContainerExportOptions) error + ContainerInit(ctx context.Context, namesOrIds []string, options ContainerInitOptions) ([]*ContainerInitReport, error) + ContainerInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]*ContainerInspectReport, error) ContainerKill(ctx context.Context, namesOrIds []string, options KillOptions) ([]*KillReport, error) - ContainerPause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error) ContainerList(ctx context.Context, options ContainerListOptions) ([]ListContainer, error) + ContainerPause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error) + ContainerLogs(ctx context.Context, containers []string, options ContainerLogsOptions) 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) - ContainerStart(ctx context.Context, namesOrIds []string, options ContainerStartOptions) ([]*ContainerStartReport, error) ContainerRun(ctx context.Context, opts ContainerRunOptions) (*ContainerRunReport, error) + ContainerStart(ctx context.Context, namesOrIds []string, options ContainerStartOptions) ([]*ContainerStartReport, error) ContainerStop(ctx context.Context, namesOrIds []string, options StopOptions) ([]*StopReport, error) ContainerTop(ctx context.Context, options TopOptions) (*StringSliceReport, error) ContainerUnpause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error) ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error) HealthCheckRun(ctx context.Context, nameOrId string, options HealthCheckOptions) (*define.HealthCheckResults, error) - + Info(ctx context.Context) (*define.Info, error) PodCreate(ctx context.Context, opts PodCreateOptions) (*PodCreateReport, error) PodExists(ctx context.Context, nameOrId string) (*BoolReport, error) + PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error) PodKill(ctx context.Context, namesOrIds []string, options PodKillOptions) ([]*PodKillReport, error) PodPause(ctx context.Context, namesOrIds []string, options PodPauseOptions) ([]*PodPauseReport, error) PodPs(ctx context.Context, options PodPSOptions) ([]*ListPodsReport, error) @@ -41,8 +46,8 @@ type ContainerEngine interface { PodStop(ctx context.Context, namesOrIds []string, options PodStopOptions) ([]*PodStopReport, error) PodTop(ctx context.Context, options PodTopOptions) (*StringSliceReport, error) PodUnpause(ctx context.Context, namesOrIds []string, options PodunpauseOptions) ([]*PodUnpauseReport, error) - PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error) - + RestService(ctx context.Context, opts ServiceOptions) error + VarlinkService(ctx context.Context, opts ServiceOptions) error VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IdOrNameResponse, error) VolumeInspect(ctx context.Context, namesOrIds []string, opts VolumeInspectOptions) ([]*VolumeInspectReport, error) VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error) diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go index a28bfc548..3110898a8 100644 --- a/pkg/domain/entities/engine_image.go +++ b/pkg/domain/entities/engine_image.go @@ -6,16 +6,18 @@ import ( type ImageEngine interface { Delete(ctx context.Context, nameOrId []string, opts ImageDeleteOptions) (*ImageDeleteReport, error) + Diff(ctx context.Context, nameOrId string, options DiffOptions) (*DiffReport, error) Exists(ctx context.Context, nameOrId string) (*BoolReport, error) History(ctx context.Context, nameOrId string, opts ImageHistoryOptions) (*ImageHistoryReport, error) + Import(ctx context.Context, opts ImageImportOptions) (*ImageImportReport, error) Inspect(ctx context.Context, names []string, opts InspectOptions) (*ImageInspectReport, error) List(ctx context.Context, opts ImageListOptions) ([]*ImageSummary, error) + Load(ctx context.Context, opts ImageLoadOptions) (*ImageLoadReport, error) Prune(ctx context.Context, opts ImagePruneOptions) (*ImagePruneReport, error) Pull(ctx context.Context, rawImage string, opts ImagePullOptions) (*ImagePullReport, error) - Tag(ctx context.Context, nameOrId string, tags []string, options ImageTagOptions) error - Untag(ctx context.Context, nameOrId string, tags []string, options ImageUntagOptions) error - Load(ctx context.Context, opts ImageLoadOptions) (*ImageLoadReport, error) - Import(ctx context.Context, opts ImageImportOptions) (*ImageImportReport, error) Push(ctx context.Context, source string, destination string, opts ImagePushOptions) error Save(ctx context.Context, nameOrId string, tags []string, options ImageSaveOptions) error + Tag(ctx context.Context, nameOrId string, tags []string, options ImageTagOptions) error + Untag(ctx context.Context, nameOrId string, tags []string, options ImageUntagOptions) error + Search(ctx context.Context, term string, opts ImageSearchOptions) ([]ImageSearchReport, error) } diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index bc8a34c13..53a5f4951 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -181,6 +181,37 @@ type ImagePushOptions struct { TLSVerify types.OptionalBool } +// ImageSearchOptions are the arguments for searching images. +type ImageSearchOptions struct { + // Authfile is the path to the authentication file. Ignored for remote + // calls. + Authfile string + // Filters for the search results. + Filters []string + // Limit the number of results. + Limit int + // NoTrunc will not truncate the output. + NoTrunc bool + // TLSVerify to enable/disable HTTPS and certificate verification. + TLSVerify types.OptionalBool +} + +// ImageSearchReport is the response from searching images. +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 string + // Description of the image. + Description string + // Stars is the number of stars of the image. + Stars int + // Official indicates if it's an official image. + Official string + // Automated indicates if the image was created by an automated build. + Automated string +} + type ImageListOptions struct { All bool `json:"all" schema:"all"` Filter []string `json:"Filter,omitempty"` diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go index cd2e79961..9ca8ff43c 100644 --- a/pkg/domain/entities/pods.go +++ b/pkg/domain/entities/pods.go @@ -1,6 +1,7 @@ package entities import ( + "strings" "time" "github.com/containers/libpod/libpod" @@ -121,7 +122,9 @@ func (p PodCreateOptions) ToPodSpecGen(s *specgen.PodSpecGenerator) { s.Hostname = p.Hostname s.Labels = p.Labels s.NoInfra = !p.Infra - s.InfraCommand = []string{p.InfraCommand} + if len(p.InfraCommand) > 0 { + s.InfraCommand = strings.Split(p.InfraCommand, " ") + } s.InfraImage = p.InfraImage s.SharedNamespaces = p.Share diff --git a/pkg/domain/entities/system.go b/pkg/domain/entities/system.go new file mode 100644 index 000000000..3ddc04293 --- /dev/null +++ b/pkg/domain/entities/system.go @@ -0,0 +1,14 @@ +package entities + +import ( + "time" + + "github.com/spf13/cobra" +) + +// ServiceOptions provides the input for starting an API Service +type ServiceOptions struct { + URI string // Path to unix domain socket service should listen on + Timeout time.Duration // duration of inactivity the service should wait before shutting down + Command *cobra.Command // CLI command provided. Used in V1 code +} diff --git a/pkg/domain/entities/types.go b/pkg/domain/entities/types.go index dd7aaa07f..7e35957f4 100644 --- a/pkg/domain/entities/types.go +++ b/pkg/domain/entities/types.go @@ -4,6 +4,7 @@ import ( "net" "github.com/containers/libpod/pkg/specgen" + "github.com/containers/storage/pkg/archive" "github.com/cri-o/ocicni/pkg/ocicni" ) @@ -49,3 +50,15 @@ type InspectOptions struct { Latest bool `json:",omitempty"` Size bool `json:",omitempty"` } + +// All API and CLI diff commands and diff sub-commands use the same options +type DiffOptions struct { + Format string `json:",omitempty"` // CLI only + Latest bool `json:",omitempty"` // API and CLI, only supported by containers + Archive bool `json:",omitempty"` // CLI only +} + +// DiffReport provides changes for object +type DiffReport struct { + Changes []archive.Change +} diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 828ee56f0..a3a0a8202 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -4,9 +4,11 @@ package abi import ( "context" + "fmt" "io/ioutil" "strconv" "strings" + "sync" "github.com/containers/buildah" "github.com/containers/image/v5/manifest" @@ -14,6 +16,7 @@ import ( "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/events" "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/libpod/logs" "github.com/containers/libpod/pkg/checkpoint" "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/domain/infra/abi/terminal" @@ -599,9 +602,9 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri ExitCode: 125, } if err := ctr.Start(ctx, ctr.PodID() != ""); err != nil { - //if lastError != nil { + // if lastError != nil { // fmt.Fprintln(os.Stderr, lastError) - //} + // } report.Err = err if errors.Cause(err) == define.ErrWillDeadlock { report.Err = errors.Wrapf(err, "please run 'podman system renumber' to resolve deadlocks") @@ -623,6 +626,19 @@ func (ic *ContainerEngine) ContainerList(ctx context.Context, options entities.C return ps.GetContainerLists(ic.Libpod, options) } +// ContainerDiff provides changes to given container +func (ic *ContainerEngine) ContainerDiff(ctx context.Context, nameOrId string, opts entities.DiffOptions) (*entities.DiffReport, error) { + if opts.Latest { + ctnr, err := ic.Libpod.GetLatestContainer() + if err != nil { + return nil, errors.Wrap(err, "unable to get latest container") + } + nameOrId = ctnr.ID() + } + changes, err := ic.Libpod.GetDiff("", nameOrId) + return &entities.DiffReport{Changes: changes}, err +} + func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) { var ( joinPod bool @@ -696,3 +712,99 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta } return &report, nil } + +func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []string, options entities.ContainerLogsOptions) error { + if options.Writer == nil { + return errors.New("no io.Writer set for container logs") + } + + var wg sync.WaitGroup + + ctrs, err := getContainersByContext(false, options.Latest, containers, ic.Libpod) + if err != nil { + return err + } + + logOpts := &logs.LogOptions{ + Multi: len(ctrs) > 1, + Details: options.Details, + Follow: options.Follow, + Since: options.Since, + Tail: options.Tail, + Timestamps: options.Timestamps, + UseName: options.Names, + WaitGroup: &wg, + } + + chSize := len(ctrs) * int(options.Tail) + if chSize <= 0 { + chSize = 1 + } + logChannel := make(chan *logs.LogLine, chSize) + + if err := ic.Libpod.Log(ctrs, logOpts, logChannel); err != nil { + return err + } + + go func() { + wg.Wait() + close(logChannel) + }() + + for line := range logChannel { + fmt.Fprintln(options.Writer, line.String(logOpts)) + } + + return nil +} + +func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []string, options entities.ContainerCleanupOptions) ([]*entities.ContainerCleanupReport, error) { + var reports []*entities.ContainerCleanupReport + ctrs, err := getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod) + if err != nil { + return nil, err + } + for _, ctr := range ctrs { + var err error + report := entities.ContainerCleanupReport{Id: ctr.ID()} + if options.Remove { + err = ic.Libpod.RemoveContainer(ctx, ctr, false, true) + if err != nil { + report.RmErr = errors.Wrapf(err, "failed to cleanup and remove container %v", ctr.ID()) + } + } else { + err := ctr.Cleanup(ctx) + if err != nil { + report.CleanErr = errors.Wrapf(err, "failed to cleanup container %v", ctr.ID()) + } + } + + if options.RemoveImage { + _, imageName := ctr.Image() + ctrImage, err := ic.Libpod.ImageRuntime().NewFromLocal(imageName) + if err != nil { + report.RmiErr = err + reports = append(reports, &report) + continue + } + _, err = ic.Libpod.RemoveImage(ctx, ctrImage, false) + report.RmiErr = err + } + reports = append(reports, &report) + } + return reports, nil +} + +func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []string, options entities.ContainerInitOptions) ([]*entities.ContainerInitReport, error) { + var reports []*entities.ContainerInitReport + ctrs, err := getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod) + if err != nil { + return nil, err + } + for _, ctr := range ctrs { + report := entities.ContainerInitReport{Id: ctr.ID()} + report.Err = ctr.Init(ctx) + reports = append(reports, &report) + } + return reports, nil +} diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 9d706a112..402bbb45e 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -15,6 +15,7 @@ import ( "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/transports/alltransports" "github.com/containers/image/v5/types" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/image" libpodImage "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/domain/entities" @@ -27,10 +28,11 @@ import ( ) func (ir *ImageEngine) Exists(_ context.Context, nameOrId string) (*entities.BoolReport, error) { - if _, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId); err != nil { - return &entities.BoolReport{}, nil + _, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId) + if err != nil && errors.Cause(err) != define.ErrNoSuchImage { + return nil, err } - return &entities.BoolReport{Value: true}, nil + return &entities.BoolReport{Value: err == nil}, nil } func (ir *ImageEngine) Delete(ctx context.Context, nameOrId []string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) { @@ -392,8 +394,10 @@ func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) if err != nil { return nil, errors.Wrap(err, "image loaded but no additional tags were created") } - if err := newImage.TagImage(opts.Name); err != nil { - return nil, errors.Wrapf(err, "error adding %q to image %q", opts.Name, newImage.InputName) + if len(opts.Name) > 0 { + if err := newImage.TagImage(fmt.Sprintf("%s:%s", opts.Name, opts.Tag)); err != nil { + return nil, errors.Wrapf(err, "error adding %q to image %q", opts.Name, newImage.InputName) + } } return &entities.ImageLoadReport{Name: name}, nil } @@ -413,3 +417,46 @@ func (ir *ImageEngine) Save(ctx context.Context, nameOrId string, tags []string, } return newImage.Save(ctx, nameOrId, options.Format, options.Output, tags, options.Quiet, options.Compress) } + +func (ir *ImageEngine) Diff(_ context.Context, nameOrId string, _ entities.DiffOptions) (*entities.DiffReport, error) { + changes, err := ir.Libpod.GetDiff("", nameOrId) + if err != nil { + return nil, err + } + return &entities.DiffReport{Changes: changes}, nil +} + +func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.ImageSearchOptions) ([]entities.ImageSearchReport, error) { + filter, err := image.ParseSearchFilter(opts.Filters) + if err != nil { + return nil, err + } + + searchOpts := image.SearchOptions{ + Authfile: opts.Authfile, + Filter: *filter, + Limit: opts.Limit, + NoTrunc: opts.NoTrunc, + InsecureSkipTLSVerify: opts.TLSVerify, + } + + searchResults, err := image.SearchImages(term, searchOpts) + if err != nil { + return nil, err + } + + // Convert from image.SearchResults to entities.ImageSearchReport. We don't + // want to leak any low-level packages into the remote client, which + // requires converting. + reports := make([]entities.ImageSearchReport, len(searchResults)) + for i := range searchResults { + reports[i].Index = searchResults[i].Index + reports[i].Name = searchResults[i].Name + reports[i].Description = searchResults[i].Index + reports[i].Stars = searchResults[i].Stars + reports[i].Official = searchResults[i].Official + reports[i].Automated = searchResults[i].Automated + } + + return reports, nil +} diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go new file mode 100644 index 000000000..adec94f6c --- /dev/null +++ b/pkg/domain/infra/abi/system.go @@ -0,0 +1,90 @@ +// +build ABISupport + +package abi + +import ( + "context" + "net" + "strings" + + "github.com/containers/libpod/libpod/define" + api "github.com/containers/libpod/pkg/api/server" + "github.com/containers/libpod/pkg/domain/entities" + iopodman "github.com/containers/libpod/pkg/varlink" + iopodmanAPI "github.com/containers/libpod/pkg/varlinkapi" + "github.com/containers/libpod/version" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/varlink/go/varlink" +) + +func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) { + return ic.Libpod.Info() +} + +func (ic *ContainerEngine) RestService(_ context.Context, opts entities.ServiceOptions) error { + var ( + listener net.Listener + err error + ) + + if opts.URI != "" { + fields := strings.Split(opts.URI, ":") + if len(fields) == 1 { + return errors.Errorf("%s is an invalid socket destination", opts.URI) + } + address := strings.Join(fields[1:], ":") + listener, err = net.Listen(fields[0], address) + if err != nil { + return errors.Wrapf(err, "unable to create socket %s", opts.URI) + } + } + + server, err := api.NewServerWithSettings(ic.Libpod, opts.Timeout, &listener) + if err != nil { + return err + } + defer func() { + if err := server.Shutdown(); err != nil { + logrus.Warnf("Error when stopping API service: %s", err) + } + }() + + err = server.Serve() + _ = listener.Close() + return err +} + +func (ic *ContainerEngine) VarlinkService(_ context.Context, opts entities.ServiceOptions) error { + var varlinkInterfaces = []*iopodman.VarlinkInterface{ + iopodmanAPI.New(opts.Command, ic.Libpod), + } + + service, err := varlink.NewService( + "Atomic", + "podman", + version.Version, + "https://github.com/containers/libpod", + ) + if err != nil { + return errors.Wrapf(err, "unable to create new varlink service") + } + + for _, i := range varlinkInterfaces { + if err := service.RegisterInterface(i); err != nil { + return errors.Errorf("unable to register varlink interface %v", i) + } + } + + // Run the varlink server at the given address + if err = service.Listen(opts.URI, opts.Timeout); err != nil { + switch err.(type) { + case varlink.ServiceTimeoutError: + logrus.Infof("varlink service expired (use --timeout to increase session time beyond %s ms, 0 means never timeout)", opts.Timeout.String()) + return nil + default: + return errors.Wrapf(err, "unable to start varlink service") + } + } + return nil +} diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index e96200c5b..1a430e3d0 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -242,7 +242,7 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [ } // narrow the list to running only for _, c := range allCtrs { - if c.State == define.ContainerStateRunning.String() { + if c.ContainerState == define.ContainerStateRunning.String() { ctrs = append(ctrs, c) } } @@ -276,7 +276,7 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st } // narrow the list to exited only for _, c := range allCtrs { - if c.State == define.ContainerStateExited.String() { + if c.ContainerState == define.ContainerStateExited.String() { ctrs = append(ctrs, c) } } @@ -305,6 +305,11 @@ func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecG return &entities.ContainerCreateReport{Id: response.ID}, nil } +func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []string, options entities.ContainerLogsOptions) error { + // The endpoint is not ready yet and requires some more work. + return errors.New("not implemented yet") +} + func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string, options entities.AttachOptions) error { return errors.New("not implemented") } @@ -324,3 +329,28 @@ func (ic *ContainerEngine) ContainerList(ctx context.Context, options entities.C func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) { return nil, errors.New("not implemented") } + +func (ic *ContainerEngine) ContainerDiff(ctx context.Context, nameOrId string, _ entities.DiffOptions) (*entities.DiffReport, error) { + changes, err := containers.Diff(ic.ClientCxt, nameOrId) + return &entities.DiffReport{Changes: changes}, err +} + +func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []string, options entities.ContainerCleanupOptions) ([]*entities.ContainerCleanupReport, error) { + return nil, errors.New("not implemented") +} + +func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []string, options entities.ContainerInitOptions) ([]*entities.ContainerInitReport, error) { + var reports []*entities.ContainerInitReport + ctrs, err := getContainersByContext(ic.ClientCxt, options.All, namesOrIds) + if err != nil { + return nil, err + } + for _, ctr := range ctrs { + err := containers.ContainerInit(ic.ClientCxt, ctr.ID) + reports = append(reports, &entities.ContainerInitReport{ + Err: err, + Id: ctr.ID, + }) + } + return reports, nil +} diff --git a/pkg/domain/infra/tunnel/helpers.go b/pkg/domain/infra/tunnel/helpers.go index 682d60d6a..4d7e45897 100644 --- a/pkg/domain/infra/tunnel/helpers.go +++ b/pkg/domain/infra/tunnel/helpers.go @@ -30,7 +30,7 @@ func getContainersByContext(contextWithConnection context.Context, all bool, nam for _, id := range namesOrIds { var found bool for _, con := range c { - if id == con.ID || strings.HasPrefix(con.ID, id) || util.StringInSlice(id, con.Names) { + if id == con.ID || strings.HasPrefix(con.ID, id) || util.StringInSlice(id, con.ContainerNames) { cons = append(cons, con) found = true break diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index 516914a68..54f2e8334 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -241,3 +241,16 @@ func (ir *ImageEngine) Save(ctx context.Context, nameOrId string, tags []string, } return utils2.UntarToFileSystem(options.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) + if err != nil { + return nil, err + } + return &entities.DiffReport{Changes: changes}, nil +} + +func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.ImageSearchOptions) ([]entities.ImageSearchReport, error) { + return images.Search(ir.ClientCxt, term, opts) +} diff --git a/pkg/domain/infra/tunnel/system.go b/pkg/domain/infra/tunnel/system.go index 5bafef1fe..7c7a55c05 100644 --- a/pkg/domain/infra/tunnel/system.go +++ b/pkg/domain/infra/tunnel/system.go @@ -1 +1,22 @@ package tunnel + +import ( + "context" + "errors" + + "github.com/containers/libpod/libpod/define" + "github.com/containers/libpod/pkg/bindings/system" + "github.com/containers/libpod/pkg/domain/entities" +) + +func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) { + return system.Info(ic.ClientCxt) +} + +func (ic *ContainerEngine) RestService(_ context.Context, _ entities.ServiceOptions) error { + panic(errors.New("rest service is not supported when tunneling")) +} + +func (ic *ContainerEngine) VarlinkService(_ context.Context, _ entities.ServiceOptions) error { + panic(errors.New("varlink service is not supported when tunneling")) +} diff --git a/pkg/ps/ps.go b/pkg/ps/ps.go index 58fcc2c21..9217fa595 100644 --- a/pkg/ps/ps.go +++ b/pkg/ps/ps.go @@ -148,23 +148,23 @@ func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities } ps := entities.ListContainer{ - Command: conConfig.Command, - Created: conConfig.CreatedTime.Unix(), - Exited: exited, - ExitCode: exitCode, - ExitedAt: exitedTime.Unix(), - ID: conConfig.ID, - Image: conConfig.RootfsImageName, - IsInfra: conConfig.IsInfra, - Labels: conConfig.Labels, - Mounts: ctr.UserVolumes(), - Names: []string{conConfig.Name}, - Pid: pid, - Pod: conConfig.Pod, - Ports: conConfig.PortMappings, - Size: size, - StartedAt: startedTime.Unix(), - State: conState.String(), + Cmd: conConfig.Command, + Created: conConfig.CreatedTime.Unix(), + Exited: exited, + ExitCode: exitCode, + ExitedAt: exitedTime.Unix(), + ID: conConfig.ID, + Image: conConfig.RootfsImageName, + IsInfra: conConfig.IsInfra, + Labels: conConfig.Labels, + Mounts: ctr.UserVolumes(), + ContainerNames: []string{conConfig.Name}, + Pid: pid, + Pod: conConfig.Pod, + PortMappings: conConfig.PortMappings, + ContainerSize: size, + StartedAt: startedTime.Unix(), + ContainerState: conState.String(), } if opts.Pod && len(conConfig.Pod) > 0 { pod, err := rt.GetPod(conConfig.Pod) diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c index 6643bfbbf..da52a7217 100644 --- a/pkg/rootless/rootless_linux.c +++ b/pkg/rootless/rootless_linux.c @@ -288,6 +288,7 @@ static void __attribute__((constructor)) init() char *cwd = getcwd (NULL, 0); char uid_fmt[16]; char gid_fmt[16]; + size_t len; if (cwd == NULL) { @@ -295,13 +296,13 @@ static void __attribute__((constructor)) init() _exit (EXIT_FAILURE); } - if (strlen (xdg_runtime_dir) >= PATH_MAX - strlen (suffix)) + len = snprintf (path, PATH_MAX, "%s%s", xdg_runtime_dir, suffix); + if (len >= PATH_MAX) { fprintf (stderr, "invalid value for XDG_RUNTIME_DIR: %s", strerror (ENAMETOOLONG)); exit (EXIT_FAILURE); } - sprintf (path, "%s%s", xdg_runtime_dir, suffix); fd = open (path, O_RDONLY); if (fd < 0) { diff --git a/pkg/signal/signal_linux.go b/pkg/signal/signal_linux.go index 76ab16ec7..e6e0f1ca1 100644 --- a/pkg/signal/signal_linux.go +++ b/pkg/signal/signal_linux.go @@ -129,14 +129,15 @@ func StopCatch(sigc chan os.Signal) { // ParseSignalNameOrNumber translates a string to a valid syscall signal. Input // can be a name or number representation i.e. "KILL" "9" func ParseSignalNameOrNumber(rawSignal string) (syscall.Signal, error) { - s, err := ParseSignal(rawSignal) + basename := strings.TrimPrefix(rawSignal, "-") + s, err := ParseSignal(basename) if err == nil { return s, nil } for k, v := range signalMap { - if k == strings.ToUpper(rawSignal) { + if k == strings.ToUpper(basename) { return v, nil } } - return -1, fmt.Errorf("invalid signal: %s", rawSignal) + return -1, fmt.Errorf("invalid signal: %s", basename) } diff --git a/pkg/specgen/pod_validate.go b/pkg/specgen/pod_validate.go index 50309f096..92026309f 100644 --- a/pkg/specgen/pod_validate.go +++ b/pkg/specgen/pod_validate.go @@ -25,7 +25,7 @@ func (p *PodSpecGenerator) validate() error { return exclusivePodOptions("NoInfra", "InfraImage") } if len(p.SharedNamespaces) > 0 { - return exclusivePodOptions("NoInfo", "SharedNamespaces") + return exclusivePodOptions("NoInfra", "SharedNamespaces") } } diff --git a/pkg/varlinkapi/attach.go b/pkg/varlinkapi/attach.go index 34f351669..db977ee5c 100644 --- a/pkg/varlinkapi/attach.go +++ b/pkg/varlinkapi/attach.go @@ -44,7 +44,7 @@ func setupStreams(call iopodman.VarlinkCall) (*bufio.Reader, *bufio.Writer, *io. } // Attach connects to a containers console -func (i *LibpodAPI) Attach(call iopodman.VarlinkCall, name string, detachKeys string, start bool) error { +func (i *VarlinkAPI) Attach(call iopodman.VarlinkCall, name string, detachKeys string, start bool) error { var finalErr error resize := make(chan remotecommand.TerminalSize) errChan := make(chan error) diff --git a/pkg/varlinkapi/config.go b/pkg/varlinkapi/config.go index c69dc794a..cc787eca2 100644 --- a/pkg/varlinkapi/config.go +++ b/pkg/varlinkapi/config.go @@ -3,21 +3,20 @@ package varlinkapi import ( - "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/libpod" iopodman "github.com/containers/libpod/pkg/varlink" "github.com/spf13/cobra" ) -// LibpodAPI is the basic varlink struct for libpod -type LibpodAPI struct { +// VarlinkAPI is the basic varlink struct for libpod +type VarlinkAPI struct { Cli *cobra.Command iopodman.VarlinkInterface Runtime *libpod.Runtime } // New creates a new varlink client -func New(cli *cliconfig.PodmanCommand, runtime *libpod.Runtime) *iopodman.VarlinkInterface { - lp := LibpodAPI{Cli: cli.Command, Runtime: runtime} +func New(cli *cobra.Command, runtime *libpod.Runtime) *iopodman.VarlinkInterface { + lp := VarlinkAPI{Cli: cli, Runtime: runtime} return iopodman.VarlinkNew(&lp) } diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go index 2d051470f..66b3e4095 100644 --- a/pkg/varlinkapi/containers.go +++ b/pkg/varlinkapi/containers.go @@ -30,7 +30,7 @@ import ( ) // ListContainers ... -func (i *LibpodAPI) ListContainers(call iopodman.VarlinkCall) error { +func (i *VarlinkAPI) ListContainers(call iopodman.VarlinkCall) error { var ( listContainers []iopodman.Container ) @@ -54,7 +54,7 @@ func (i *LibpodAPI) ListContainers(call iopodman.VarlinkCall) error { return call.ReplyListContainers(listContainers) } -func (i *LibpodAPI) Ps(call iopodman.VarlinkCall, opts iopodman.PsOpts) error { +func (i *VarlinkAPI) Ps(call iopodman.VarlinkCall, opts iopodman.PsOpts) error { var ( containers []iopodman.PsContainer ) @@ -106,7 +106,7 @@ func (i *LibpodAPI) Ps(call iopodman.VarlinkCall, opts iopodman.PsOpts) error { } // GetContainer ... -func (i *LibpodAPI) GetContainer(call iopodman.VarlinkCall, id string) error { +func (i *VarlinkAPI) GetContainer(call iopodman.VarlinkCall, id string) error { ctr, err := i.Runtime.LookupContainer(id) if err != nil { return call.ReplyContainerNotFound(id, err.Error()) @@ -123,7 +123,7 @@ func (i *LibpodAPI) GetContainer(call iopodman.VarlinkCall, id string) error { } // GetContainersByContext returns a slice of container ids based on all, latest, or a list -func (i *LibpodAPI) GetContainersByContext(call iopodman.VarlinkCall, all, latest bool, input []string) error { +func (i *VarlinkAPI) GetContainersByContext(call iopodman.VarlinkCall, all, latest bool, input []string) error { var ids []string ctrs, err := shortcuts.GetContainersByContext(all, latest, input, i.Runtime) @@ -141,7 +141,7 @@ func (i *LibpodAPI) GetContainersByContext(call iopodman.VarlinkCall, all, lates } // GetContainersByStatus returns a slice of containers filtered by a libpod status -func (i *LibpodAPI) GetContainersByStatus(call iopodman.VarlinkCall, statuses []string) error { +func (i *VarlinkAPI) GetContainersByStatus(call iopodman.VarlinkCall, statuses []string) error { var ( filterFuncs []libpod.ContainerFilter containers []iopodman.Container @@ -172,7 +172,7 @@ func (i *LibpodAPI) GetContainersByStatus(call iopodman.VarlinkCall, statuses [] } // InspectContainer ... -func (i *LibpodAPI) InspectContainer(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) InspectContainer(call iopodman.VarlinkCall, name string) error { ctr, err := i.Runtime.LookupContainer(name) if err != nil { return call.ReplyContainerNotFound(name, err.Error()) @@ -189,7 +189,7 @@ func (i *LibpodAPI) InspectContainer(call iopodman.VarlinkCall, name string) err } // ListContainerProcesses ... -func (i *LibpodAPI) ListContainerProcesses(call iopodman.VarlinkCall, name string, opts []string) error { +func (i *VarlinkAPI) ListContainerProcesses(call iopodman.VarlinkCall, name string, opts []string) error { ctr, err := i.Runtime.LookupContainer(name) if err != nil { return call.ReplyContainerNotFound(name, err.Error()) @@ -216,7 +216,7 @@ func (i *LibpodAPI) ListContainerProcesses(call iopodman.VarlinkCall, name strin } // GetContainerLogs ... -func (i *LibpodAPI) GetContainerLogs(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) GetContainerLogs(call iopodman.VarlinkCall, name string) error { var logs []string ctr, err := i.Runtime.LookupContainer(name) if err != nil { @@ -277,7 +277,7 @@ func (i *LibpodAPI) GetContainerLogs(call iopodman.VarlinkCall, name string) err } // ListContainerChanges ... -func (i *LibpodAPI) ListContainerChanges(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) ListContainerChanges(call iopodman.VarlinkCall, name string) error { changes, err := i.Runtime.GetDiff("", name) if err != nil { return call.ReplyErrorOccurred(err.Error()) @@ -297,7 +297,7 @@ func (i *LibpodAPI) ListContainerChanges(call iopodman.VarlinkCall, name string) } // ExportContainer ... -func (i *LibpodAPI) ExportContainer(call iopodman.VarlinkCall, name, outPath string) error { +func (i *VarlinkAPI) ExportContainer(call iopodman.VarlinkCall, name, outPath string) error { ctr, err := i.Runtime.LookupContainer(name) if err != nil { return call.ReplyContainerNotFound(name, err.Error()) @@ -319,7 +319,7 @@ func (i *LibpodAPI) ExportContainer(call iopodman.VarlinkCall, name, outPath str } // GetContainerStats ... -func (i *LibpodAPI) GetContainerStats(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) GetContainerStats(call iopodman.VarlinkCall, name string) error { if rootless.IsRootless() { cgroupv2, err := cgroups.IsCgroup2UnifiedMode() if err != nil { @@ -359,7 +359,7 @@ func (i *LibpodAPI) GetContainerStats(call iopodman.VarlinkCall, name string) er } // StartContainer ... -func (i *LibpodAPI) StartContainer(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) StartContainer(call iopodman.VarlinkCall, name string) error { ctr, err := i.Runtime.LookupContainer(name) if err != nil { return call.ReplyContainerNotFound(name, err.Error()) @@ -382,7 +382,7 @@ func (i *LibpodAPI) StartContainer(call iopodman.VarlinkCall, name string) error } // InitContainer initializes the container given by Varlink. -func (i *LibpodAPI) InitContainer(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) InitContainer(call iopodman.VarlinkCall, name string) error { ctr, err := i.Runtime.LookupContainer(name) if err != nil { return call.ReplyContainerNotFound(name, err.Error()) @@ -397,7 +397,7 @@ func (i *LibpodAPI) InitContainer(call iopodman.VarlinkCall, name string) error } // StopContainer ... -func (i *LibpodAPI) StopContainer(call iopodman.VarlinkCall, name string, timeout int64) error { +func (i *VarlinkAPI) StopContainer(call iopodman.VarlinkCall, name string, timeout int64) error { ctr, err := i.Runtime.LookupContainer(name) if err != nil { return call.ReplyContainerNotFound(name, err.Error()) @@ -415,7 +415,7 @@ func (i *LibpodAPI) StopContainer(call iopodman.VarlinkCall, name string, timeou } // RestartContainer ... -func (i *LibpodAPI) RestartContainer(call iopodman.VarlinkCall, name string, timeout int64) error { +func (i *VarlinkAPI) RestartContainer(call iopodman.VarlinkCall, name string, timeout int64) error { ctr, err := i.Runtime.LookupContainer(name) if err != nil { return call.ReplyContainerNotFound(name, err.Error()) @@ -427,7 +427,7 @@ func (i *LibpodAPI) RestartContainer(call iopodman.VarlinkCall, name string, tim } // ContainerExists looks in local storage for the existence of a container -func (i *LibpodAPI) ContainerExists(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) ContainerExists(call iopodman.VarlinkCall, name string) error { _, err := i.Runtime.LookupContainer(name) if errors.Cause(err) == define.ErrNoSuchCtr { return call.ReplyContainerExists(1) @@ -440,7 +440,7 @@ func (i *LibpodAPI) ContainerExists(call iopodman.VarlinkCall, name string) erro // KillContainer kills a running container. If you want to use the default SIGTERM signal, just send a -1 // for the signal arg. -func (i *LibpodAPI) KillContainer(call iopodman.VarlinkCall, name string, signal int64) error { +func (i *VarlinkAPI) KillContainer(call iopodman.VarlinkCall, name string, signal int64) error { killSignal := uint(syscall.SIGTERM) if signal != -1 { killSignal = uint(signal) @@ -456,7 +456,7 @@ func (i *LibpodAPI) KillContainer(call iopodman.VarlinkCall, name string, signal } // PauseContainer ... -func (i *LibpodAPI) PauseContainer(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) PauseContainer(call iopodman.VarlinkCall, name string) error { ctr, err := i.Runtime.LookupContainer(name) if err != nil { return call.ReplyContainerNotFound(name, err.Error()) @@ -468,7 +468,7 @@ func (i *LibpodAPI) PauseContainer(call iopodman.VarlinkCall, name string) error } // UnpauseContainer ... -func (i *LibpodAPI) UnpauseContainer(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) UnpauseContainer(call iopodman.VarlinkCall, name string) error { ctr, err := i.Runtime.LookupContainer(name) if err != nil { return call.ReplyContainerNotFound(name, err.Error()) @@ -480,7 +480,7 @@ func (i *LibpodAPI) UnpauseContainer(call iopodman.VarlinkCall, name string) err } // WaitContainer ... -func (i *LibpodAPI) WaitContainer(call iopodman.VarlinkCall, name string, interval int64) error { +func (i *VarlinkAPI) WaitContainer(call iopodman.VarlinkCall, name string, interval int64) error { ctr, err := i.Runtime.LookupContainer(name) if err != nil { return call.ReplyContainerNotFound(name, err.Error()) @@ -493,7 +493,7 @@ func (i *LibpodAPI) WaitContainer(call iopodman.VarlinkCall, name string, interv } // RemoveContainer ... -func (i *LibpodAPI) RemoveContainer(call iopodman.VarlinkCall, name string, force bool, removeVolumes bool) error { +func (i *VarlinkAPI) RemoveContainer(call iopodman.VarlinkCall, name string, force bool, removeVolumes bool) error { ctx := getContext() ctr, err := i.Runtime.LookupContainer(name) if err != nil { @@ -512,7 +512,7 @@ func (i *LibpodAPI) RemoveContainer(call iopodman.VarlinkCall, name string, forc } // EvictContainer ... -func (i *LibpodAPI) EvictContainer(call iopodman.VarlinkCall, name string, removeVolumes bool) error { +func (i *VarlinkAPI) EvictContainer(call iopodman.VarlinkCall, name string, removeVolumes bool) error { ctx := getContext() id, err := i.Runtime.EvictContainer(ctx, name, removeVolumes) if err != nil { @@ -522,7 +522,7 @@ func (i *LibpodAPI) EvictContainer(call iopodman.VarlinkCall, name string, remov } // DeleteStoppedContainers ... -func (i *LibpodAPI) DeleteStoppedContainers(call iopodman.VarlinkCall) error { +func (i *VarlinkAPI) DeleteStoppedContainers(call iopodman.VarlinkCall) error { ctx := getContext() var deletedContainers []string containers, err := i.Runtime.GetAllContainers() @@ -545,7 +545,7 @@ func (i *LibpodAPI) DeleteStoppedContainers(call iopodman.VarlinkCall) error { } // GetAttachSockets ... -func (i *LibpodAPI) GetAttachSockets(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) GetAttachSockets(call iopodman.VarlinkCall, name string) error { ctr, err := i.Runtime.LookupContainer(name) if err != nil { return call.ReplyContainerNotFound(name, err.Error()) @@ -578,7 +578,7 @@ func (i *LibpodAPI) GetAttachSockets(call iopodman.VarlinkCall, name string) err } // ContainerCheckpoint ... -func (i *LibpodAPI) ContainerCheckpoint(call iopodman.VarlinkCall, name string, keep, leaveRunning, tcpEstablished bool) error { +func (i *VarlinkAPI) ContainerCheckpoint(call iopodman.VarlinkCall, name string, keep, leaveRunning, tcpEstablished bool) error { ctx := getContext() ctr, err := i.Runtime.LookupContainer(name) if err != nil { @@ -597,7 +597,7 @@ func (i *LibpodAPI) ContainerCheckpoint(call iopodman.VarlinkCall, name string, } // ContainerRestore ... -func (i *LibpodAPI) ContainerRestore(call iopodman.VarlinkCall, name string, keep, tcpEstablished bool) error { +func (i *VarlinkAPI) ContainerRestore(call iopodman.VarlinkCall, name string, keep, tcpEstablished bool) error { ctx := getContext() ctr, err := i.Runtime.LookupContainer(name) if err != nil { @@ -615,7 +615,7 @@ func (i *LibpodAPI) ContainerRestore(call iopodman.VarlinkCall, name string, kee } // ContainerConfig returns just the container.config struct -func (i *LibpodAPI) ContainerConfig(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) ContainerConfig(call iopodman.VarlinkCall, name string) error { ctr, err := i.Runtime.LookupContainer(name) if err != nil { return call.ReplyContainerNotFound(name, err.Error()) @@ -629,7 +629,7 @@ func (i *LibpodAPI) ContainerConfig(call iopodman.VarlinkCall, name string) erro } // ContainerArtifacts returns an untouched container's artifact in string format -func (i *LibpodAPI) ContainerArtifacts(call iopodman.VarlinkCall, name, artifactName string) error { +func (i *VarlinkAPI) ContainerArtifacts(call iopodman.VarlinkCall, name, artifactName string) error { ctr, err := i.Runtime.LookupContainer(name) if err != nil { return call.ReplyContainerNotFound(name, err.Error()) @@ -646,7 +646,7 @@ func (i *LibpodAPI) ContainerArtifacts(call iopodman.VarlinkCall, name, artifact } // ContainerInspectData returns the inspect data of a container in string format -func (i *LibpodAPI) ContainerInspectData(call iopodman.VarlinkCall, name string, size bool) error { +func (i *VarlinkAPI) ContainerInspectData(call iopodman.VarlinkCall, name string, size bool) error { ctr, err := i.Runtime.LookupContainer(name) if err != nil { return call.ReplyContainerNotFound(name, err.Error()) @@ -664,7 +664,7 @@ func (i *LibpodAPI) ContainerInspectData(call iopodman.VarlinkCall, name string, } // ContainerStateData returns a container's state data in string format -func (i *LibpodAPI) ContainerStateData(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) ContainerStateData(call iopodman.VarlinkCall, name string) error { ctr, err := i.Runtime.LookupContainer(name) if err != nil { return call.ReplyContainerNotFound(name, err.Error()) @@ -682,7 +682,7 @@ func (i *LibpodAPI) ContainerStateData(call iopodman.VarlinkCall, name string) e // GetContainerStatsWithHistory is a varlink endpoint that returns container stats based on current and // previous statistics -func (i *LibpodAPI) GetContainerStatsWithHistory(call iopodman.VarlinkCall, prevStats iopodman.ContainerStats) error { +func (i *VarlinkAPI) GetContainerStatsWithHistory(call iopodman.VarlinkCall, prevStats iopodman.ContainerStats) error { con, err := i.Runtime.LookupContainer(prevStats.Id) if err != nil { return call.ReplyContainerNotFound(prevStats.Id, err.Error()) @@ -711,7 +711,7 @@ func (i *LibpodAPI) GetContainerStatsWithHistory(call iopodman.VarlinkCall, prev } // Spec ... -func (i *LibpodAPI) Spec(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) Spec(call iopodman.VarlinkCall, name string) error { ctr, err := i.Runtime.LookupContainer(name) if err != nil { return call.ReplyErrorOccurred(err.Error()) @@ -727,7 +727,7 @@ func (i *LibpodAPI) Spec(call iopodman.VarlinkCall, name string) error { } // GetContainersLogs is the varlink endpoint to obtain one or more container logs -func (i *LibpodAPI) GetContainersLogs(call iopodman.VarlinkCall, names []string, follow, latest bool, since string, tail int64, timestamps bool) error { +func (i *VarlinkAPI) GetContainersLogs(call iopodman.VarlinkCall, names []string, follow, latest bool, since string, tail int64, timestamps bool) error { var wg sync.WaitGroup if call.WantsMore() { call.Continues = true @@ -784,7 +784,7 @@ func newPodmanLogLine(line *logs.LogLine) iopodman.LogLine { } // Top displays information about a container's running processes -func (i *LibpodAPI) Top(call iopodman.VarlinkCall, nameOrID string, descriptors []string) error { +func (i *VarlinkAPI) Top(call iopodman.VarlinkCall, nameOrID string, descriptors []string) error { ctr, err := i.Runtime.LookupContainer(nameOrID) if err != nil { return call.ReplyContainerNotFound(ctr.ID(), err.Error()) @@ -797,7 +797,7 @@ func (i *LibpodAPI) Top(call iopodman.VarlinkCall, nameOrID string, descriptors } // ExecContainer is the varlink endpoint to execute a command in a container -func (i *LibpodAPI) ExecContainer(call iopodman.VarlinkCall, opts iopodman.ExecOpts) error { +func (i *VarlinkAPI) ExecContainer(call iopodman.VarlinkCall, opts iopodman.ExecOpts) error { if !call.WantsUpgrade() { return call.ReplyErrorOccurred("client must use upgraded connection to exec") } @@ -901,7 +901,7 @@ func (i *LibpodAPI) ExecContainer(call iopodman.VarlinkCall, opts iopodman.ExecO } // HealthCheckRun executes defined container's healthcheck command and returns the container's health status. -func (i *LibpodAPI) HealthCheckRun(call iopodman.VarlinkCall, nameOrID string) error { +func (i *VarlinkAPI) HealthCheckRun(call iopodman.VarlinkCall, nameOrID string) error { hcStatus, err := i.Runtime.HealthCheck(nameOrID) if err != nil && hcStatus != libpod.HealthCheckFailure { return call.ReplyErrorOccurred(err.Error()) diff --git a/pkg/varlinkapi/containers_create.go b/pkg/varlinkapi/containers_create.go index bbd4d59f1..c1c1f6674 100644 --- a/pkg/varlinkapi/containers_create.go +++ b/pkg/varlinkapi/containers_create.go @@ -8,7 +8,7 @@ import ( ) // CreateContainer ... -func (i *LibpodAPI) CreateContainer(call iopodman.VarlinkCall, config iopodman.Create) error { +func (i *VarlinkAPI) CreateContainer(call iopodman.VarlinkCall, config iopodman.Create) error { generic := shared.VarlinkCreateToGeneric(config) ctr, _, err := shared.CreateContainer(getContext(), &generic, i.Runtime) if err != nil { diff --git a/pkg/varlinkapi/events.go b/pkg/varlinkapi/events.go index 4ae2d1cb2..33938f08b 100644 --- a/pkg/varlinkapi/events.go +++ b/pkg/varlinkapi/events.go @@ -3,7 +3,6 @@ package varlinkapi import ( - "fmt" "time" "github.com/containers/libpod/libpod/events" @@ -11,7 +10,7 @@ import ( ) // GetEvents is a remote endpoint to get events from the event log -func (i *LibpodAPI) GetEvents(call iopodman.VarlinkCall, filter []string, since string, until string) error { +func (i *VarlinkAPI) GetEvents(call iopodman.VarlinkCall, filter []string, since string, until string) error { var ( fromStart bool eventsError error @@ -43,9 +42,9 @@ func (i *LibpodAPI) GetEvents(call iopodman.VarlinkCall, filter []string, since Id: event.ID, Image: event.Image, Name: event.Name, - Status: fmt.Sprintf("%s", event.Status), + Status: string(event.Status), Time: event.Time.Format(time.RFC3339Nano), - Type: fmt.Sprintf("%s", event.Type), + Type: string(event.Type), }) if !call.Continues { // For a one-shot on events, we break out here diff --git a/pkg/varlinkapi/generate.go b/pkg/varlinkapi/generate.go index c19c8dede..81a0df68e 100644 --- a/pkg/varlinkapi/generate.go +++ b/pkg/varlinkapi/generate.go @@ -10,7 +10,7 @@ import ( ) // GenerateKube ... -func (i *LibpodAPI) GenerateKube(call iopodman.VarlinkCall, name string, service bool) error { +func (i *VarlinkAPI) GenerateKube(call iopodman.VarlinkCall, name string, service bool) error { pod, serv, err := shared.GenerateKube(name, service, i.Runtime) if err != nil { return call.ReplyErrorOccurred(err.Error()) diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go index c3b4bd9ae..49bd0b0cb 100644 --- a/pkg/varlinkapi/images.go +++ b/pkg/varlinkapi/images.go @@ -35,14 +35,14 @@ import ( ) // ListImagesWithFilters returns a list of images that have been filtered -func (i *LibpodAPI) ListImagesWithFilters(call iopodman.VarlinkCall, filters []string) error { +func (i *VarlinkAPI) ListImagesWithFilters(call iopodman.VarlinkCall, filters []string) error { images, err := i.Runtime.ImageRuntime().GetImagesWithFilters(filters) if err != nil { return call.ReplyErrorOccurred(fmt.Sprintf("unable to get list of images %q", err)) } imageList, err := imagesToImageList(images) if err != nil { - return call.ReplyErrorOccurred(fmt.Sprintf("unable to parse response", err)) + return call.ReplyErrorOccurred("unable to parse response " + err.Error()) } return call.ReplyListImagesWithFilters(imageList) } @@ -50,34 +50,34 @@ func (i *LibpodAPI) ListImagesWithFilters(call iopodman.VarlinkCall, filters []s // imagesToImageList converts a slice of Images to an imagelist for varlink responses func imagesToImageList(images []*image.Image) ([]iopodman.Image, error) { var imageList []iopodman.Image - for _, image := range images { - labels, _ := image.Labels(getContext()) - containers, _ := image.Containers() - repoDigests, err := image.RepoDigests() + for _, img := range images { + labels, _ := img.Labels(getContext()) + containers, _ := img.Containers() + repoDigests, err := img.RepoDigests() if err != nil { return nil, err } - size, _ := image.Size(getContext()) - isParent, err := image.IsParent(context.TODO()) + size, _ := img.Size(getContext()) + isParent, err := img.IsParent(context.TODO()) if err != nil { return nil, err } i := iopodman.Image{ - Id: image.ID(), - Digest: string(image.Digest()), - ParentId: image.Parent, - RepoTags: image.Names(), + Id: img.ID(), + Digest: string(img.Digest()), + ParentId: img.Parent, + RepoTags: img.Names(), RepoDigests: repoDigests, - Created: image.Created().Format(time.RFC3339), + Created: img.Created().Format(time.RFC3339), Size: int64(*size), - VirtualSize: image.VirtualSize, + VirtualSize: img.VirtualSize, Containers: int64(len(containers)), Labels: labels, IsParent: isParent, - ReadOnly: image.IsReadOnly(), - History: image.NamesHistory(), + ReadOnly: img.IsReadOnly(), + History: img.NamesHistory(), } imageList = append(imageList, i) } @@ -86,20 +86,20 @@ func imagesToImageList(images []*image.Image) ([]iopodman.Image, error) { // ListImages lists all the images in the store // It requires no inputs. -func (i *LibpodAPI) ListImages(call iopodman.VarlinkCall) error { +func (i *VarlinkAPI) ListImages(call iopodman.VarlinkCall) error { images, err := i.Runtime.ImageRuntime().GetImages() if err != nil { - return call.ReplyErrorOccurred(fmt.Sprintf("unable to get list of images %q", err)) + return call.ReplyErrorOccurred("unable to get list of images " + err.Error()) } imageList, err := imagesToImageList(images) if err != nil { - return call.ReplyErrorOccurred(fmt.Sprintf("unable to parse response", err)) + return call.ReplyErrorOccurred("unable to parse response " + err.Error()) } return call.ReplyListImages(imageList) } // GetImage returns a single image in the form of a Image -func (i *LibpodAPI) GetImage(call iopodman.VarlinkCall, id string) error { +func (i *VarlinkAPI) GetImage(call iopodman.VarlinkCall, id string) error { newImage, err := i.Runtime.ImageRuntime().NewFromLocal(id) if err != nil { return call.ReplyImageNotFound(id, err.Error()) @@ -139,7 +139,7 @@ func (i *LibpodAPI) GetImage(call iopodman.VarlinkCall, id string) error { } // BuildImage ... -func (i *LibpodAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildInfo) error { +func (i *VarlinkAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildInfo) error { var ( namespace []buildah.NamespaceOption imageID string @@ -302,7 +302,7 @@ func (i *LibpodAPI) BuildImage(call iopodman.VarlinkCall, config iopodman.BuildI // InspectImage returns an image's inspect information as a string that can be serialized. // Requires an image ID or name -func (i *LibpodAPI) InspectImage(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) InspectImage(call iopodman.VarlinkCall, name string) error { newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name) if err != nil { return call.ReplyImageNotFound(name, err.Error()) @@ -320,7 +320,7 @@ func (i *LibpodAPI) InspectImage(call iopodman.VarlinkCall, name string) error { // HistoryImage returns the history of the image's layers // Requires an image or name -func (i *LibpodAPI) HistoryImage(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) HistoryImage(call iopodman.VarlinkCall, name string) error { newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name) if err != nil { return call.ReplyImageNotFound(name, err.Error()) @@ -345,7 +345,7 @@ func (i *LibpodAPI) HistoryImage(call iopodman.VarlinkCall, name string) error { } // PushImage pushes an local image to registry -func (i *LibpodAPI) PushImage(call iopodman.VarlinkCall, name, tag string, compress bool, format string, removeSignatures bool, signBy string) error { +func (i *VarlinkAPI) PushImage(call iopodman.VarlinkCall, name, tag string, compress bool, format string, removeSignatures bool, signBy string) error { var ( manifestType string ) @@ -437,7 +437,7 @@ func (i *LibpodAPI) PushImage(call iopodman.VarlinkCall, name, tag string, compr } // TagImage accepts an image name and tag as strings and tags an image in the local store. -func (i *LibpodAPI) TagImage(call iopodman.VarlinkCall, name, tag string) error { +func (i *VarlinkAPI) TagImage(call iopodman.VarlinkCall, name, tag string) error { newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name) if err != nil { return call.ReplyImageNotFound(name, err.Error()) @@ -449,7 +449,7 @@ func (i *LibpodAPI) TagImage(call iopodman.VarlinkCall, name, tag string) error } // UntagImage accepts an image name and tag as strings and removes the tag from the local store. -func (i *LibpodAPI) UntagImage(call iopodman.VarlinkCall, name, tag string) error { +func (i *VarlinkAPI) UntagImage(call iopodman.VarlinkCall, name, tag string) error { newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name) if err != nil { return call.ReplyImageNotFound(name, err.Error()) @@ -462,7 +462,7 @@ func (i *LibpodAPI) UntagImage(call iopodman.VarlinkCall, name, tag string) erro // RemoveImage accepts a image name or ID as a string and force bool to determine if it should // remove the image even if being used by stopped containers -func (i *LibpodAPI) RemoveImage(call iopodman.VarlinkCall, name string, force bool) error { +func (i *VarlinkAPI) RemoveImage(call iopodman.VarlinkCall, name string, force bool) error { ctx := getContext() newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name) if err != nil { @@ -477,7 +477,7 @@ func (i *LibpodAPI) RemoveImage(call iopodman.VarlinkCall, name string, force bo // RemoveImageWithResponse accepts an image name and force bool. It returns details about what // was done in removeimageresponse struct. -func (i *LibpodAPI) RemoveImageWithResponse(call iopodman.VarlinkCall, name string, force bool) error { +func (i *VarlinkAPI) RemoveImageWithResponse(call iopodman.VarlinkCall, name string, force bool) error { ir := iopodman.RemoveImageResponse{} ctx := getContext() newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name) @@ -495,7 +495,7 @@ func (i *LibpodAPI) RemoveImageWithResponse(call iopodman.VarlinkCall, name stri // SearchImages searches all registries configured in /etc/containers/registries.conf for an image // Requires an image name and a search limit as int -func (i *LibpodAPI) SearchImages(call iopodman.VarlinkCall, query string, limit *int64, filter iopodman.ImageSearchFilter) error { +func (i *VarlinkAPI) SearchImages(call iopodman.VarlinkCall, query string, limit *int64, filter iopodman.ImageSearchFilter) error { // Transform all arguments to proper types first argLimit := 0 argIsOfficial := types.OptionalBoolUndefined @@ -543,7 +543,7 @@ func (i *LibpodAPI) SearchImages(call iopodman.VarlinkCall, query string, limit // DeleteUnusedImages deletes any images that do not have containers associated with it. // TODO Filters are not implemented -func (i *LibpodAPI) DeleteUnusedImages(call iopodman.VarlinkCall) error { +func (i *VarlinkAPI) DeleteUnusedImages(call iopodman.VarlinkCall) error { images, err := i.Runtime.ImageRuntime().GetImages() if err != nil { return call.ReplyErrorOccurred(err.Error()) @@ -565,7 +565,7 @@ func (i *LibpodAPI) DeleteUnusedImages(call iopodman.VarlinkCall) error { } // Commit ... -func (i *LibpodAPI) Commit(call iopodman.VarlinkCall, name, imageName string, changes []string, author, message string, pause bool, manifestType string) error { +func (i *VarlinkAPI) Commit(call iopodman.VarlinkCall, name, imageName string, changes []string, author, message string, pause bool, manifestType string) error { var ( newImage *image.Image log []string @@ -643,7 +643,7 @@ func (i *LibpodAPI) Commit(call iopodman.VarlinkCall, name, imageName string, ch } // ImportImage imports an image from a tarball to the image store -func (i *LibpodAPI) ImportImage(call iopodman.VarlinkCall, source, reference, message string, changes []string, delete bool) error { +func (i *VarlinkAPI) ImportImage(call iopodman.VarlinkCall, source, reference, message string, changes []string, delete bool) error { configChanges, err := util.GetImageConfig(changes) if err != nil { return call.ReplyErrorOccurred(err.Error()) @@ -670,7 +670,7 @@ func (i *LibpodAPI) ImportImage(call iopodman.VarlinkCall, source, reference, me // ExportImage exports an image to the provided destination // destination must have the transport type!! -func (i *LibpodAPI) ExportImage(call iopodman.VarlinkCall, name, destination string, compress bool, tags []string) error { +func (i *VarlinkAPI) ExportImage(call iopodman.VarlinkCall, name, destination string, compress bool, tags []string) error { newImage, err := i.Runtime.ImageRuntime().NewFromLocal(name) if err != nil { return call.ReplyImageNotFound(name, err.Error()) @@ -688,7 +688,7 @@ func (i *LibpodAPI) ExportImage(call iopodman.VarlinkCall, name, destination str } // PullImage pulls an image from a registry to the image store. -func (i *LibpodAPI) PullImage(call iopodman.VarlinkCall, name string, creds iopodman.AuthConfig) error { +func (i *VarlinkAPI) PullImage(call iopodman.VarlinkCall, name string, creds iopodman.AuthConfig) error { var ( imageID string err error @@ -760,7 +760,7 @@ func (i *LibpodAPI) PullImage(call iopodman.VarlinkCall, name string, creds iopo } // ImageExists returns bool as to whether the input image exists in local storage -func (i *LibpodAPI) ImageExists(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) ImageExists(call iopodman.VarlinkCall, name string) error { _, err := i.Runtime.ImageRuntime().NewFromLocal(name) if errors.Cause(err) == image.ErrNoSuchImage { return call.ReplyImageExists(1) @@ -772,7 +772,7 @@ func (i *LibpodAPI) ImageExists(call iopodman.VarlinkCall, name string) error { } // ContainerRunlabel ... -func (i *LibpodAPI) ContainerRunlabel(call iopodman.VarlinkCall, input iopodman.Runlabel) error { +func (i *VarlinkAPI) ContainerRunlabel(call iopodman.VarlinkCall, input iopodman.Runlabel) error { ctx := getContext() dockerRegistryOptions := image.DockerRegistryOptions{} stdErr := os.Stderr @@ -798,7 +798,7 @@ func (i *LibpodAPI) ContainerRunlabel(call iopodman.VarlinkCall, input iopodman. } // ImagesPrune .... -func (i *LibpodAPI) ImagesPrune(call iopodman.VarlinkCall, all bool, filter []string) error { +func (i *VarlinkAPI) ImagesPrune(call iopodman.VarlinkCall, all bool, filter []string) error { prunedImages, err := i.Runtime.ImageRuntime().PruneImages(context.TODO(), all, []string{}) if err != nil { return call.ReplyErrorOccurred(err.Error()) @@ -807,7 +807,7 @@ func (i *LibpodAPI) ImagesPrune(call iopodman.VarlinkCall, all bool, filter []st } // ImageSave .... -func (i *LibpodAPI) ImageSave(call iopodman.VarlinkCall, options iopodman.ImageSaveOptions) error { +func (i *VarlinkAPI) ImageSave(call iopodman.VarlinkCall, options iopodman.ImageSaveOptions) error { newImage, err := i.Runtime.ImageRuntime().NewFromLocal(options.Name) if err != nil { if errors.Cause(err) == define.ErrNoSuchImage { @@ -905,7 +905,7 @@ func (i *LibpodAPI) ImageSave(call iopodman.VarlinkCall, options iopodman.ImageS } // LoadImage ... -func (i *LibpodAPI) LoadImage(call iopodman.VarlinkCall, name, inputFile string, deleteInputFile, quiet bool) error { +func (i *VarlinkAPI) LoadImage(call iopodman.VarlinkCall, name, inputFile string, deleteInputFile, quiet bool) error { var ( names string writer io.Writer @@ -974,7 +974,7 @@ func (i *LibpodAPI) LoadImage(call iopodman.VarlinkCall, name, inputFile string, } // Diff ... -func (i *LibpodAPI) Diff(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) Diff(call iopodman.VarlinkCall, name string) error { var response []iopodman.DiffInfo changes, err := i.Runtime.GetDiff("", name) if err != nil { @@ -987,7 +987,7 @@ func (i *LibpodAPI) Diff(call iopodman.VarlinkCall, name string) error { } // GetLayersMapWithImageInfo is a development only endpoint to obtain layer information for an image. -func (i *LibpodAPI) GetLayersMapWithImageInfo(call iopodman.VarlinkCall) error { +func (i *VarlinkAPI) GetLayersMapWithImageInfo(call iopodman.VarlinkCall) error { layerInfo, err := image.GetLayersMapWithImageInfo(i.Runtime.ImageRuntime()) if err != nil { return call.ReplyErrorOccurred(err.Error()) @@ -1000,7 +1000,7 @@ func (i *LibpodAPI) GetLayersMapWithImageInfo(call iopodman.VarlinkCall) error { } // BuildImageHierarchyMap ... -func (i *LibpodAPI) BuildImageHierarchyMap(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) BuildImageHierarchyMap(call iopodman.VarlinkCall, name string) error { img, err := i.Runtime.ImageRuntime().NewFromLocal(name) if err != nil { return call.ReplyErrorOccurred(err.Error()) @@ -1024,7 +1024,7 @@ func (i *LibpodAPI) BuildImageHierarchyMap(call iopodman.VarlinkCall, name strin } // ImageTree returns the image tree string for the provided image name or ID -func (i *LibpodAPI) ImageTree(call iopodman.VarlinkCall, nameOrID string, whatRequires bool) error { +func (i *VarlinkAPI) ImageTree(call iopodman.VarlinkCall, nameOrID string, whatRequires bool) error { img, err := i.Runtime.ImageRuntime().NewFromLocal(nameOrID) if err != nil { return call.ReplyErrorOccurred(err.Error()) diff --git a/pkg/varlinkapi/mount.go b/pkg/varlinkapi/mount.go index 2450f6fd9..6e1eed644 100644 --- a/pkg/varlinkapi/mount.go +++ b/pkg/varlinkapi/mount.go @@ -5,7 +5,7 @@ package varlinkapi import iopodman "github.com/containers/libpod/pkg/varlink" // ListContainerMounts ... -func (i *LibpodAPI) ListContainerMounts(call iopodman.VarlinkCall) error { +func (i *VarlinkAPI) ListContainerMounts(call iopodman.VarlinkCall) error { mounts := make(map[string]string) allContainers, err := i.Runtime.GetAllContainers() if err != nil { @@ -24,7 +24,7 @@ func (i *LibpodAPI) ListContainerMounts(call iopodman.VarlinkCall) error { } // MountContainer ... -func (i *LibpodAPI) MountContainer(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) MountContainer(call iopodman.VarlinkCall, name string) error { container, err := i.Runtime.LookupContainer(name) if err != nil { return call.ReplyErrorOccurred(err.Error()) @@ -37,7 +37,7 @@ func (i *LibpodAPI) MountContainer(call iopodman.VarlinkCall, name string) error } // UnmountContainer ... -func (i *LibpodAPI) UnmountContainer(call iopodman.VarlinkCall, name string, force bool) error { +func (i *VarlinkAPI) UnmountContainer(call iopodman.VarlinkCall, name string, force bool) error { container, err := i.Runtime.LookupContainer(name) if err != nil { return call.ReplyErrorOccurred(err.Error()) diff --git a/pkg/varlinkapi/pods.go b/pkg/varlinkapi/pods.go index 79ffb6677..94add1b6c 100644 --- a/pkg/varlinkapi/pods.go +++ b/pkg/varlinkapi/pods.go @@ -14,7 +14,7 @@ import ( ) // CreatePod ... -func (i *LibpodAPI) CreatePod(call iopodman.VarlinkCall, create iopodman.PodCreate) error { +func (i *VarlinkAPI) CreatePod(call iopodman.VarlinkCall, create iopodman.PodCreate) error { var options []libpod.PodCreateOption if create.Infra { options = append(options, libpod.WithInfraContainer()) @@ -61,7 +61,7 @@ func (i *LibpodAPI) CreatePod(call iopodman.VarlinkCall, create iopodman.PodCrea } // ListPods ... -func (i *LibpodAPI) ListPods(call iopodman.VarlinkCall) error { +func (i *VarlinkAPI) ListPods(call iopodman.VarlinkCall) error { var ( listPods []iopodman.ListPodData ) @@ -82,7 +82,7 @@ func (i *LibpodAPI) ListPods(call iopodman.VarlinkCall) error { } // GetPod ... -func (i *LibpodAPI) GetPod(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) GetPod(call iopodman.VarlinkCall, name string) error { pod, err := i.Runtime.LookupPod(name) if err != nil { return call.ReplyPodNotFound(name, err.Error()) @@ -98,7 +98,7 @@ func (i *LibpodAPI) GetPod(call iopodman.VarlinkCall, name string) error { } // GetPodsByStatus returns a slice of pods filtered by a libpod status -func (i *LibpodAPI) GetPodsByStatus(call iopodman.VarlinkCall, statuses []string) error { +func (i *VarlinkAPI) GetPodsByStatus(call iopodman.VarlinkCall, statuses []string) error { filterFuncs := func(p *libpod.Pod) bool { state, _ := shared.GetPodStatus(p) for _, status := range statuses { @@ -120,7 +120,7 @@ func (i *LibpodAPI) GetPodsByStatus(call iopodman.VarlinkCall, statuses []string } // InspectPod ... -func (i *LibpodAPI) InspectPod(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) InspectPod(call iopodman.VarlinkCall, name string) error { pod, err := i.Runtime.LookupPod(name) if err != nil { return call.ReplyPodNotFound(name, err.Error()) @@ -137,7 +137,7 @@ func (i *LibpodAPI) InspectPod(call iopodman.VarlinkCall, name string) error { } // StartPod ... -func (i *LibpodAPI) StartPod(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) StartPod(call iopodman.VarlinkCall, name string) error { pod, err := i.Runtime.LookupPod(name) if err != nil { return call.ReplyPodNotFound(name, err.Error()) @@ -158,7 +158,7 @@ func (i *LibpodAPI) StartPod(call iopodman.VarlinkCall, name string) error { } // StopPod ... -func (i *LibpodAPI) StopPod(call iopodman.VarlinkCall, name string, timeout int64) error { +func (i *VarlinkAPI) StopPod(call iopodman.VarlinkCall, name string, timeout int64) error { pod, err := i.Runtime.LookupPod(name) if err != nil { return call.ReplyPodNotFound(name, err.Error()) @@ -172,7 +172,7 @@ func (i *LibpodAPI) StopPod(call iopodman.VarlinkCall, name string, timeout int6 } // RestartPod ... -func (i *LibpodAPI) RestartPod(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) RestartPod(call iopodman.VarlinkCall, name string) error { pod, err := i.Runtime.LookupPod(name) if err != nil { return call.ReplyPodNotFound(name, err.Error()) @@ -194,7 +194,7 @@ func (i *LibpodAPI) RestartPod(call iopodman.VarlinkCall, name string) error { // KillPod kills the running containers in a pod. If you want to use the default SIGTERM signal, // just send a -1 for the signal arg. -func (i *LibpodAPI) KillPod(call iopodman.VarlinkCall, name string, signal int64) error { +func (i *VarlinkAPI) KillPod(call iopodman.VarlinkCall, name string, signal int64) error { killSignal := uint(syscall.SIGTERM) if signal != -1 { killSignal = uint(signal) @@ -213,7 +213,7 @@ func (i *LibpodAPI) KillPod(call iopodman.VarlinkCall, name string, signal int64 } // PausePod ... -func (i *LibpodAPI) PausePod(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) PausePod(call iopodman.VarlinkCall, name string) error { pod, err := i.Runtime.LookupPod(name) if err != nil { return call.ReplyPodNotFound(name, err.Error()) @@ -227,7 +227,7 @@ func (i *LibpodAPI) PausePod(call iopodman.VarlinkCall, name string) error { } // UnpausePod ... -func (i *LibpodAPI) UnpausePod(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) UnpausePod(call iopodman.VarlinkCall, name string) error { pod, err := i.Runtime.LookupPod(name) if err != nil { return call.ReplyPodNotFound(name, err.Error()) @@ -241,7 +241,7 @@ func (i *LibpodAPI) UnpausePod(call iopodman.VarlinkCall, name string) error { } // RemovePod ... -func (i *LibpodAPI) RemovePod(call iopodman.VarlinkCall, name string, force bool) error { +func (i *VarlinkAPI) RemovePod(call iopodman.VarlinkCall, name string, force bool) error { ctx := getContext() pod, err := i.Runtime.LookupPod(name) if err != nil { @@ -255,7 +255,7 @@ func (i *LibpodAPI) RemovePod(call iopodman.VarlinkCall, name string, force bool } // GetPodStats ... -func (i *LibpodAPI) GetPodStats(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) GetPodStats(call iopodman.VarlinkCall, name string) error { pod, err := i.Runtime.LookupPod(name) if err != nil { return call.ReplyPodNotFound(name, err.Error()) @@ -291,7 +291,7 @@ func (i *LibpodAPI) GetPodStats(call iopodman.VarlinkCall, name string) error { } // GetPodsByContext returns a slice of pod ids based on all, latest, or a list -func (i *LibpodAPI) GetPodsByContext(call iopodman.VarlinkCall, all, latest bool, input []string) error { +func (i *VarlinkAPI) GetPodsByContext(call iopodman.VarlinkCall, all, latest bool, input []string) error { var podids []string pods, err := shortcuts.GetPodsByContext(all, latest, input, i.Runtime) @@ -305,7 +305,7 @@ func (i *LibpodAPI) GetPodsByContext(call iopodman.VarlinkCall, all, latest bool } // PodStateData returns a container's state data in string format -func (i *LibpodAPI) PodStateData(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) PodStateData(call iopodman.VarlinkCall, name string) error { pod, err := i.Runtime.LookupPod(name) if err != nil { return call.ReplyErrorOccurred(err.Error()) @@ -322,7 +322,7 @@ func (i *LibpodAPI) PodStateData(call iopodman.VarlinkCall, name string) error { } // TopPod provides the top stats for a given or latest pod -func (i *LibpodAPI) TopPod(call iopodman.VarlinkCall, name string, latest bool, descriptors []string) error { +func (i *VarlinkAPI) TopPod(call iopodman.VarlinkCall, name string, latest bool, descriptors []string) error { var ( pod *libpod.Pod err error diff --git a/pkg/varlinkapi/system.go b/pkg/varlinkapi/system.go index 7bee643c2..82efe9b5d 100644 --- a/pkg/varlinkapi/system.go +++ b/pkg/varlinkapi/system.go @@ -16,7 +16,7 @@ import ( ) // GetVersion ... -func (i *LibpodAPI) GetVersion(call iopodman.VarlinkCall) error { +func (i *VarlinkAPI) GetVersion(call iopodman.VarlinkCall) error { versionInfo, err := define.GetVersion() if err != nil { return err @@ -33,7 +33,7 @@ func (i *LibpodAPI) GetVersion(call iopodman.VarlinkCall) error { } // GetInfo returns details about the podman host and its stores -func (i *LibpodAPI) GetInfo(call iopodman.VarlinkCall) error { +func (i *VarlinkAPI) GetInfo(call iopodman.VarlinkCall) error { versionInfo, err := define.GetVersion() if err != nil { return err @@ -106,7 +106,7 @@ func (i *LibpodAPI) GetInfo(call iopodman.VarlinkCall) error { } // GetVersion ... -func (i *LibpodAPI) Reset(call iopodman.VarlinkCall) error { +func (i *VarlinkAPI) Reset(call iopodman.VarlinkCall) error { if err := i.Runtime.Reset(context.TODO()); err != nil { logrus.Errorf("Reset Failed: %v", err) if err := call.ReplyErrorOccurred(err.Error()); err != nil { diff --git a/pkg/varlinkapi/transfers.go b/pkg/varlinkapi/transfers.go index 654da276e..9df8ffcdc 100644 --- a/pkg/varlinkapi/transfers.go +++ b/pkg/varlinkapi/transfers.go @@ -4,7 +4,6 @@ package varlinkapi import ( "bufio" - "fmt" "io" "io/ioutil" "os" @@ -14,7 +13,7 @@ import ( ) // SendFile allows a client to send a file to the varlink server -func (i *LibpodAPI) SendFile(call iopodman.VarlinkCall, ftype string, length int64) error { +func (i *VarlinkAPI) SendFile(call iopodman.VarlinkCall, ftype string, length int64) error { if !call.WantsUpgrade() { return call.ReplyErrorOccurred("client must use upgraded connection to send files") } @@ -40,14 +39,14 @@ func (i *LibpodAPI) SendFile(call iopodman.VarlinkCall, ftype string, length int logrus.Debugf("successfully received %s", outputFile.Name()) // Send an ACK to the client - call.Call.Writer.WriteString(fmt.Sprintf("%s:", outputFile.Name())) + call.Call.Writer.WriteString(outputFile.Name()) call.Call.Writer.Flush() return nil } // ReceiveFile allows the varlink server to send a file to a client -func (i *LibpodAPI) ReceiveFile(call iopodman.VarlinkCall, filepath string, delete bool) error { +func (i *VarlinkAPI) ReceiveFile(call iopodman.VarlinkCall, filepath string, delete bool) error { if !call.WantsUpgrade() { return call.ReplyErrorOccurred("client must use upgraded connection to send files") } diff --git a/pkg/varlinkapi/volumes.go b/pkg/varlinkapi/volumes.go index b0c3608c4..ff72c3869 100644 --- a/pkg/varlinkapi/volumes.go +++ b/pkg/varlinkapi/volumes.go @@ -11,7 +11,7 @@ import ( ) // VolumeCreate creates a libpod volume based on input from a varlink connection -func (i *LibpodAPI) VolumeCreate(call iopodman.VarlinkCall, options iopodman.VolumeCreateOpts) error { +func (i *VarlinkAPI) VolumeCreate(call iopodman.VarlinkCall, options iopodman.VolumeCreateOpts) error { var volumeOptions []libpod.VolumeCreateOption if len(options.VolumeName) > 0 { @@ -38,7 +38,7 @@ func (i *LibpodAPI) VolumeCreate(call iopodman.VarlinkCall, options iopodman.Vol } // VolumeRemove removes volumes by options.All or options.Volumes -func (i *LibpodAPI) VolumeRemove(call iopodman.VarlinkCall, options iopodman.VolumeRemoveOpts) error { +func (i *VarlinkAPI) VolumeRemove(call iopodman.VarlinkCall, options iopodman.VolumeRemoveOpts) error { success, failed, err := shared.SharedRemoveVolumes(getContext(), i.Runtime, options.Volumes, options.All, options.Force) if err != nil { return call.ReplyErrorOccurred(err.Error()) @@ -52,7 +52,7 @@ func (i *LibpodAPI) VolumeRemove(call iopodman.VarlinkCall, options iopodman.Vol } // GetVolumes returns all the volumes known to the remote system -func (i *LibpodAPI) GetVolumes(call iopodman.VarlinkCall, args []string, all bool) error { +func (i *VarlinkAPI) GetVolumes(call iopodman.VarlinkCall, args []string, all bool) error { var ( err error reply []*libpod.Volume @@ -87,7 +87,7 @@ func (i *LibpodAPI) GetVolumes(call iopodman.VarlinkCall, args []string, all boo } // InspectVolume inspects a single volume, returning its JSON as a string. -func (i *LibpodAPI) InspectVolume(call iopodman.VarlinkCall, name string) error { +func (i *VarlinkAPI) InspectVolume(call iopodman.VarlinkCall, name string) error { vol, err := i.Runtime.LookupVolume(name) if err != nil { return call.ReplyErrorOccurred(err.Error()) @@ -104,7 +104,7 @@ func (i *LibpodAPI) InspectVolume(call iopodman.VarlinkCall, name string) error } // VolumesPrune removes unused images via a varlink call -func (i *LibpodAPI) VolumesPrune(call iopodman.VarlinkCall) error { +func (i *VarlinkAPI) VolumesPrune(call iopodman.VarlinkCall) error { var ( prunedErrors []string prunedNames []string |