Skip to content

Commit

Permalink
Switch podman stop/kill/wait handlers to use abi
Browse files Browse the repository at this point in the history
Change API Handlers to use the same functions that the
local podman uses.

At the same time:

 implement remote API for --all and --ignore flags for podman stop
 implement remote API for --all flags for podman stop

Signed-off-by: Daniel J Walsh <[email protected]>
  • Loading branch information
rhatdan committed Jan 29, 2021
1 parent 8f3bcf6 commit 073f76c
Show file tree
Hide file tree
Showing 22 changed files with 344 additions and 174 deletions.
15 changes: 13 additions & 2 deletions cmd/podman/containers/kill.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package containers

import (
"context"
"errors"
"fmt"
"io/ioutil"
"strings"

"github.com/containers/common/pkg/completion"
"github.com/containers/podman/v2/cmd/podman/common"
Expand All @@ -12,6 +13,7 @@ import (
"github.com/containers/podman/v2/cmd/podman/validate"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/signal"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -59,7 +61,7 @@ func killFlags(cmd *cobra.Command) {
flags.StringVarP(&killOptions.Signal, signalFlagName, "s", "KILL", "Signal to send to the container")
_ = cmd.RegisterFlagCompletionFunc(signalFlagName, common.AutocompleteStopSignal)
cidfileFlagName := "cidfile"
flags.StringArrayVar(&killOptions.CIDFiles, cidfileFlagName, []string{}, "Read the container ID from the file")
flags.StringArrayVar(&cidFiles, cidfileFlagName, []string{}, "Read the container ID from the file")
_ = cmd.RegisterFlagCompletionFunc(cidfileFlagName, completion.AutocompleteDefault)
}

Expand Down Expand Up @@ -94,6 +96,15 @@ func kill(_ *cobra.Command, args []string) error {
if sig < 1 || sig > 64 {
return errors.New("valid signals are 1 through 64")
}
for _, cidFile := range cidFiles {
content, err := ioutil.ReadFile(string(cidFile))
if err != nil {
return errors.Wrap(err, "error reading CIDFile")
}
id := strings.Split(string(content), "\n")[0]
args = append(args, id)
}

responses, err := registry.ContainerEngine().ContainerKill(context.Background(), args, killOptions)
if err != nil {
return err
Expand Down
14 changes: 13 additions & 1 deletion cmd/podman/containers/stop.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ package containers
import (
"context"
"fmt"
"io/ioutil"
"strings"

"github.com/containers/common/pkg/completion"
"github.com/containers/podman/v2/cmd/podman/common"
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/cmd/podman/utils"
"github.com/containers/podman/v2/cmd/podman/validate"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -58,7 +61,7 @@ func stopFlags(cmd *cobra.Command) {
flags.BoolVarP(&stopOptions.Ignore, "ignore", "i", false, "Ignore errors when a specified container is missing")

cidfileFlagName := "cidfile"
flags.StringArrayVarP(&stopOptions.CIDFiles, cidfileFlagName, "", nil, "Read the container ID from the file")
flags.StringArrayVar(&cidFiles, cidfileFlagName, nil, "Read the container ID from the file")
_ = cmd.RegisterFlagCompletionFunc(cidfileFlagName, completion.AutocompleteDefault)

timeFlagName := "time"
Expand Down Expand Up @@ -97,6 +100,15 @@ func stop(cmd *cobra.Command, args []string) error {
stopOptions.Timeout = &stopTimeout
}

for _, cidFile := range cidFiles {
content, err := ioutil.ReadFile(string(cidFile))
if err != nil {
return errors.Wrap(err, "error reading CIDFile")
}
id := strings.Split(string(content), "\n")[0]
args = append(args, id)
}

responses, err := registry.ContainerEngine().ContainerStop(context.Background(), args, stopOptions)
if err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion cmd/podman/containers/wait.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func waitFlags(cmd *cobra.Command) {
flags := cmd.Flags()

intervalFlagName := "interval"
flags.StringVarP(&waitInterval, intervalFlagName, "i", "250ns", "Time Interval to wait before polling for completion")
flags.StringVarP(&waitInterval, intervalFlagName, "i", "250ms", "Time Interval to wait before polling for completion")
_ = cmd.RegisterFlagCompletionFunc(intervalFlagName, completion.AutocompleteNone)

conditionFlagName := "condition"
Expand Down
92 changes: 51 additions & 41 deletions pkg/api/handlers/compat/containers.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ import (
func RemoveContainer(w http.ResponseWriter, r *http.Request) {
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
All bool `schema:"all"`
Force bool `schema:"force"`
Ignore bool `schema:"ignore"`
Link bool `schema:"link"`
Volumes bool `schema:"v"`
Force bool `schema:"force"`
Ignore bool `schema:"ignore"`
Link bool `schema:"link"`
DockerVolumes bool `schema:"v"`
LibpodVolumes bool `schema:"volumes"`
}{
// override any golang type defaults
}
Expand All @@ -46,23 +46,26 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) {
return
}

if query.Link && !utils.IsLibpodRequest(r) {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
utils.ErrLinkNotSupport)
return
options := entities.RmOptions{
Force: query.Force,
Ignore: query.Ignore,
}
if utils.IsLibpodRequest(r) {
options.Volumes = query.LibpodVolumes
} else {
if query.Link {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
utils.ErrLinkNotSupport)
return
}
options.Volumes = query.DockerVolumes
}

runtime := r.Context().Value("runtime").(*libpod.Runtime)
// Now use the ABI implementation to prevent us from having duplicate
// code.
containerEngine := abi.ContainerEngine{Libpod: runtime}
name := utils.GetName(r)
options := entities.RmOptions{
All: query.All,
Force: query.Force,
Volumes: query.Volumes,
Ignore: query.Ignore,
}
report, err := containerEngine.ContainerRm(r.Context(), []string{name}, options)
if err != nil {
if errors.Cause(err) == define.ErrNoSuchCtr {
Expand Down Expand Up @@ -193,45 +196,48 @@ func KillContainer(w http.ResponseWriter, r *http.Request) {
return
}

sig, err := signal.ParseSignalNameOrNumber(query.Signal)
if err != nil {
utils.InternalServerError(w, err)
return
}
// Now use the ABI implementation to prevent us from having duplicate
// code.
containerEngine := abi.ContainerEngine{Libpod: runtime}
name := utils.GetName(r)
con, err := runtime.LookupContainer(name)
if err != nil {
utils.ContainerNotFound(w, name, err)
return
options := entities.KillOptions{
Signal: query.Signal,
}

state, err := con.State()
report, err := containerEngine.ContainerKill(r.Context(), []string{name}, options)
if err != nil {
utils.InternalServerError(w, err)
return
}
if errors.Cause(err) == define.ErrCtrStateInvalid ||
errors.Cause(err) == define.ErrCtrStopped {
utils.Error(w, fmt.Sprintf("Container %s is not running", name), http.StatusConflict, err)
return
}
if errors.Cause(err) == define.ErrNoSuchCtr {
utils.ContainerNotFound(w, name, err)
return
}

// If the Container is stopped already, send a 409
if state == define.ContainerStateStopped || state == define.ContainerStateExited {
utils.Error(w, fmt.Sprintf("Container %s is not running", name), http.StatusConflict, errors.New(fmt.Sprintf("Cannot kill Container %s, it is not running", name)))
utils.InternalServerError(w, err)
return
}

signal := uint(sig)

err = con.Kill(signal)
if err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "unable to kill Container %s", name))
if len(report) > 0 && report[0].Err != nil {
utils.InternalServerError(w, report[0].Err)
return
}

// Docker waits for the container to stop if the signal is 0 or
// SIGKILL.
if !utils.IsLibpodRequest(r) && (signal == 0 || syscall.Signal(signal) == syscall.SIGKILL) {
if _, err = con.Wait(); err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "failed to wait for Container %s", con.ID()))
if !utils.IsLibpodRequest(r) {
sig, err := signal.ParseSignalNameOrNumber(query.Signal)
if err != nil {
utils.InternalServerError(w, err)
return
}
if sig == 0 || syscall.Signal(sig) == syscall.SIGKILL {
var opts entities.WaitOptions
if _, err := containerEngine.ContainerWait(r.Context(), []string{name}, opts); err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err)
return
}
}
}
// Success
utils.WriteResponse(w, http.StatusNoContent, nil)
Expand All @@ -242,6 +248,10 @@ func WaitContainer(w http.ResponseWriter, r *http.Request) {
// /{version}/containers/(name)/wait
exitCode, err := utils.WaitContainer(w, r)
if err != nil {
if errors.Cause(err) == define.ErrNoSuchCtr {
logrus.Warnf("container not found %q: %v", utils.GetName(r), err)
return
}
logrus.Warnf("failed to wait on container %q: %v", mux.Vars(r)["name"], err)
return
}
Expand Down
42 changes: 30 additions & 12 deletions pkg/api/handlers/compat/containers_restart.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,60 @@ import (
"net/http"

"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/domain/infra/abi"
"github.com/gorilla/schema"
"github.com/pkg/errors"
)

func RestartContainer(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
// Now use the ABI implementation to prevent us from having duplicate
// code.
containerEngine := abi.ContainerEngine{Libpod: runtime}

// /{version}/containers/(name)/restart
query := struct {
Timeout int `schema:"t"`
All bool `schema:"all"`
DockerTimeout uint `schema:"t"`
LibpodTimeout uint `schema:"timeout"`
}{
// Override golang default values for types
// override any golang type defaults
}
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
utils.BadRequest(w, "url", r.URL.String(), errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
return
}

name := utils.GetName(r)
con, err := runtime.LookupContainer(name)
if err != nil {
utils.ContainerNotFound(w, name, err)
return
}

timeout := con.StopTimeout()
if _, found := r.URL.Query()["t"]; found {
timeout = uint(query.Timeout)
options := entities.RestartOptions{
All: query.All,
Timeout: &query.DockerTimeout,
}
if utils.IsLibpodRequest(r) {
options.Timeout = &query.LibpodTimeout
}
report, err := containerEngine.ContainerRestart(r.Context(), []string{name}, options)
if err != nil {
if errors.Cause(err) == define.ErrNoSuchCtr {
utils.ContainerNotFound(w, name, err)
return
}

if err := con.RestartWithTimeout(r.Context(), timeout); err != nil {
utils.InternalServerError(w, err)
return
}

if len(report) > 0 && report[0].Err != nil {
utils.InternalServerError(w, report[0].Err)
return
}

// Success
utils.WriteResponse(w, http.StatusNoContent, nil)
}
44 changes: 33 additions & 11 deletions pkg/api/handlers/compat/containers_stop.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,24 @@ import (
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/domain/infra/abi"
"github.com/gorilla/schema"
"github.com/pkg/errors"
)

func StopContainer(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
// Now use the ABI implementation to prevent us from having duplicate
// code.
containerEngine := abi.ContainerEngine{Libpod: runtime}

// /{version}/containers/(name)/stop
query := struct {
Timeout int `schema:"t"`
Ignore bool `schema:"ignore"`
DockerTimeout uint `schema:"t"`
LibpodTimeout uint `schema:"timeout"`
}{
// override any golang type defaults
}
Expand All @@ -27,31 +34,46 @@ func StopContainer(w http.ResponseWriter, r *http.Request) {
}

name := utils.GetName(r)

options := entities.StopOptions{
Ignore: query.Ignore,
}
if utils.IsLibpodRequest(r) {
if query.LibpodTimeout > 0 {
options.Timeout = &query.LibpodTimeout
}
} else {
if query.DockerTimeout > 0 {
options.Timeout = &query.DockerTimeout
}
}
con, err := runtime.LookupContainer(name)
if err != nil {
utils.ContainerNotFound(w, name, err)
return
}

state, err := con.State()
if err != nil {
utils.InternalServerError(w, errors.Wrapf(err, "unable to get state for Container %s", name))
utils.InternalServerError(w, err)
return
}
// If the Container is stopped already, send a 304
if state == define.ContainerStateStopped || state == define.ContainerStateExited {
utils.WriteResponse(w, http.StatusNotModified, nil)
return
}
report, err := containerEngine.ContainerStop(r.Context(), []string{name}, options)
if err != nil {
if errors.Cause(err) == define.ErrNoSuchCtr {
utils.ContainerNotFound(w, name, err)
return
}

var stopError error
if query.Timeout > 0 {
stopError = con.StopWithTimeout(uint(query.Timeout))
} else {
stopError = con.Stop()
utils.InternalServerError(w, err)
return
}
if stopError != nil {
utils.InternalServerError(w, errors.Wrapf(stopError, "failed to stop %s", name))

if len(report) > 0 && report[0].Err != nil {
utils.InternalServerError(w, report[0].Err)
return
}

Expand Down
Loading

0 comments on commit 073f76c

Please sign in to comment.