summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/cirrus/lib.sh25
-rw-r--r--pkg/api/handlers/compat/containers_top.go81
-rw-r--r--pkg/api/handlers/libpod/pods.go82
-rw-r--r--pkg/api/handlers/types.go6
-rw-r--r--pkg/api/handlers/utils/handler.go8
-rw-r--r--pkg/api/handlers/utils/handler_test.go48
-rw-r--r--pkg/api/server/register_containers.go17
-rw-r--r--pkg/api/server/register_images.go8
-rw-r--r--pkg/api/server/register_pods.go15
-rw-r--r--pkg/api/server/swagger.go14
-rw-r--r--pkg/bindings/connection.go5
-rw-r--r--pkg/bindings/errors.go29
-rw-r--r--pkg/bindings/pods/pods.go14
-rw-r--r--pkg/bindings/test/common_test.go17
-rw-r--r--pkg/bindings/test/pods_test.go26
-rw-r--r--pkg/domain/entities/images.go1
-rw-r--r--pkg/domain/entities/reports/prune.go6
-rw-r--r--pkg/domain/infra/abi/images.go2
-rw-r--r--pkg/domain/infra/tunnel/containers.go2
-rw-r--r--pkg/domain/infra/tunnel/images.go2
-rw-r--r--pkg/domain/infra/tunnel/network.go2
-rw-r--r--pkg/domain/infra/tunnel/secrets.go4
-rw-r--r--pkg/domain/infra/tunnel/volumes.go2
-rw-r--r--pkg/errorhandling/errorhandling.go14
-rw-r--r--test/apiv2/10-images.at3
-rw-r--r--test/apiv2/40-pods.at4
-rw-r--r--test/apiv2/python/rest_api/test_v2_0_0_container.py103
-rw-r--r--test/system/035-logs.bats28
-rw-r--r--test/system/070-build.bats20
-rw-r--r--test/system/130-kill.bats15
-rw-r--r--test/system/500-networking.bats8
31 files changed, 488 insertions, 123 deletions
diff --git a/contrib/cirrus/lib.sh b/contrib/cirrus/lib.sh
index 9a7bfba8f..9b7c613f5 100644
--- a/contrib/cirrus/lib.sh
+++ b/contrib/cirrus/lib.sh
@@ -143,6 +143,8 @@ setup_rootless() {
local rootless_uid
local rootless_gid
local env_var_val
+ local akfilepath
+ local sshcmd
# Only do this once; established by setup_environment.sh
# shellcheck disable=SC2154
@@ -169,24 +171,25 @@ setup_rootless() {
ssh-keygen -P "" -f "$HOME/.ssh/id_rsa"
msg "Allowing ssh key for $ROOTLESS_USER"
+ akfilepath="/home/$ROOTLESS_USER/.ssh/authorized_keys"
(umask 077 && mkdir "/home/$ROOTLESS_USER/.ssh")
chown -R $ROOTLESS_USER:$ROOTLESS_USER "/home/$ROOTLESS_USER/.ssh"
install -o $ROOTLESS_USER -g $ROOTLESS_USER -m 0600 \
- "$HOME/.ssh/id_rsa.pub" "/home/$ROOTLESS_USER/.ssh/authorized_keys"
+ "$HOME/.ssh/id_rsa.pub" "$akfilepath"
# Makes debugging easier
- cat /root/.ssh/authorized_keys >> "/home/$ROOTLESS_USER/.ssh/authorized_keys"
-
- msg "Configuring subuid and subgid"
- grep -q "${ROOTLESS_USER}" /etc/subuid || \
- echo "${ROOTLESS_USER}:$[rootless_uid * 100]:65536" | \
- tee -a /etc/subuid >> /etc/subgid
+ cat /root/.ssh/authorized_keys >> "$akfilepath"
msg "Ensure the ssh daemon is up and running within 5 minutes"
systemctl start sshd
- lilto ssh $ROOTLESS_USER@localhost \
- -o UserKnownHostsFile=/dev/null \
- -o StrictHostKeyChecking=no \
- -o CheckHostIP=no true
+ sshcmd="ssh $ROOTLESS_USER@localhost
+ -o UserKnownHostsFile=/dev/null
+ -o StrictHostKeyChecking=no
+ -o CheckHostIP=no"
+ lilto $sshcmd true # retry until sshd is up
+
+ msg "Configuring rootless user self-access to ssh to localhost"
+ $sshcmd ssh-keygen -P '""' -f "/home/$ROOTLESS_USER/.ssh/id_rsa"
+ cat "/home/$ROOTLESS_USER/.ssh/id_rsa" >> "$akfilepath"
}
install_test_configs() {
diff --git a/pkg/api/handlers/compat/containers_top.go b/pkg/api/handlers/compat/containers_top.go
index b5debd37d..545320ad9 100644
--- a/pkg/api/handlers/compat/containers_top.go
+++ b/pkg/api/handlers/compat/containers_top.go
@@ -1,8 +1,11 @@
package compat
import (
+ "encoding/json"
+ "fmt"
"net/http"
"strings"
+ "time"
"github.com/containers/podman/v3/libpod"
"github.com/containers/podman/v3/pkg/api/handlers"
@@ -10,20 +13,24 @@ import (
api "github.com/containers/podman/v3/pkg/api/types"
"github.com/gorilla/schema"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
func TopContainer(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
- defaultValue := "-ef"
+ psArgs := "-ef"
if utils.IsLibpodRequest(r) {
- defaultValue = ""
+ psArgs = ""
}
query := struct {
+ Delay int `schema:"delay"`
PsArgs string `schema:"ps_args"`
+ Stream bool `schema:"stream"`
}{
- PsArgs: defaultValue,
+ Delay: 5,
+ PsArgs: psArgs,
}
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
@@ -31,6 +38,12 @@ func TopContainer(w http.ResponseWriter, r *http.Request) {
return
}
+ if query.Delay < 1 {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ fmt.Errorf("\"delay\" parameter of value %d < 1", query.Delay))
+ return
+ }
+
name := utils.GetName(r)
c, err := runtime.LookupContainer(name)
if err != nil {
@@ -38,26 +51,56 @@ func TopContainer(w http.ResponseWriter, r *http.Request) {
return
}
- output, err := c.Top([]string{query.PsArgs})
- if err != nil {
- utils.InternalServerError(w, err)
- return
+ // We are committed now - all errors logged but not reported to client, ship has sailed
+ w.WriteHeader(http.StatusOK)
+ w.Header().Set("Content-Type", "application/json")
+ if f, ok := w.(http.Flusher); ok {
+ f.Flush()
}
- var body = handlers.ContainerTopOKBody{}
- if len(output) > 0 {
- body.Titles = strings.Split(output[0], "\t")
- for i := range body.Titles {
- body.Titles[i] = strings.TrimSpace(body.Titles[i])
- }
+ encoder := json.NewEncoder(w)
+
+loop: // break out of for/select infinite` loop
+ for {
+ select {
+ case <-r.Context().Done():
+ break loop
+ default:
+ output, err := c.Top([]string{query.PsArgs})
+ if err != nil {
+ logrus.Infof("Error from %s %q : %v", r.Method, r.URL, err)
+ break loop
+ }
+
+ if len(output) > 0 {
+ body := handlers.ContainerTopOKBody{}
+ body.Titles = strings.Split(output[0], "\t")
+ for i := range body.Titles {
+ body.Titles[i] = strings.TrimSpace(body.Titles[i])
+ }
+
+ for _, line := range output[1:] {
+ process := strings.Split(line, "\t")
+ for i := range process {
+ process[i] = strings.TrimSpace(process[i])
+ }
+ body.Processes = append(body.Processes, process)
+ }
+
+ if err := encoder.Encode(body); err != nil {
+ logrus.Infof("Error from %s %q : %v", r.Method, r.URL, err)
+ break loop
+ }
+ if f, ok := w.(http.Flusher); ok {
+ f.Flush()
+ }
+ }
- for _, line := range output[1:] {
- process := strings.Split(line, "\t")
- for i := range process {
- process[i] = strings.TrimSpace(process[i])
+ if query.Stream {
+ time.Sleep(time.Duration(query.Delay) * time.Second)
+ } else {
+ break loop
}
- body.Processes = append(body.Processes, process)
}
}
- utils.WriteJSON(w, http.StatusOK, body)
}
diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go
index 1e64de0ee..2ba292579 100644
--- a/pkg/api/handlers/libpod/pods.go
+++ b/pkg/api/handlers/libpod/pods.go
@@ -5,6 +5,7 @@ import (
"fmt"
"net/http"
"strings"
+ "time"
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v3/libpod"
@@ -363,10 +364,17 @@ func PodTop(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
+ psArgs := "-ef"
+ if utils.IsLibpodRequest(r) {
+ psArgs = ""
+ }
query := struct {
+ Delay int `schema:"delay"`
PsArgs string `schema:"ps_args"`
+ Stream bool `schema:"stream"`
}{
- PsArgs: "",
+ Delay: 5,
+ PsArgs: psArgs,
}
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
@@ -374,31 +382,71 @@ func PodTop(w http.ResponseWriter, r *http.Request) {
return
}
- name := utils.GetName(r)
- pod, err := runtime.LookupPod(name)
- if err != nil {
- utils.PodNotFound(w, name, err)
+ if query.Delay < 1 {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ fmt.Errorf("\"delay\" parameter of value %d < 1", query.Delay))
return
}
- args := []string{}
- if query.PsArgs != "" {
- args = append(args, query.PsArgs)
- }
- output, err := pod.GetPodPidInformation(args)
+ name := utils.GetName(r)
+ pod, err := runtime.LookupPod(name)
if err != nil {
- utils.InternalServerError(w, err)
+ utils.PodNotFound(w, name, err)
return
}
- var body = handlers.PodTopOKBody{}
- if len(output) > 0 {
- body.Titles = strings.Split(output[0], "\t")
- for _, line := range output[1:] {
- body.Processes = append(body.Processes, strings.Split(line, "\t"))
+ // We are committed now - all errors logged but not reported to client, ship has sailed
+ w.WriteHeader(http.StatusOK)
+ w.Header().Set("Content-Type", "application/json")
+ if f, ok := w.(http.Flusher); ok {
+ f.Flush()
+ }
+
+ encoder := json.NewEncoder(w)
+
+loop: // break out of for/select infinite` loop
+ for {
+ select {
+ case <-r.Context().Done():
+ break loop
+ default:
+ output, err := pod.GetPodPidInformation([]string{query.PsArgs})
+ if err != nil {
+ logrus.Infof("Error from %s %q : %v", r.Method, r.URL, err)
+ break loop
+ }
+
+ if len(output) > 0 {
+ var body = handlers.PodTopOKBody{}
+ body.Titles = strings.Split(output[0], "\t")
+ for i := range body.Titles {
+ body.Titles[i] = strings.TrimSpace(body.Titles[i])
+ }
+
+ for _, line := range output[1:] {
+ process := strings.Split(line, "\t")
+ for i := range process {
+ process[i] = strings.TrimSpace(process[i])
+ }
+ body.Processes = append(body.Processes, process)
+ }
+
+ if err := encoder.Encode(body); err != nil {
+ logrus.Infof("Error from %s %q : %v", r.Method, r.URL, err)
+ break loop
+ }
+ if f, ok := w.(http.Flusher); ok {
+ f.Flush()
+ }
+ }
+
+ if query.Stream {
+ time.Sleep(time.Duration(query.Delay) * time.Second)
+ } else {
+ break loop
+ }
}
}
- utils.WriteJSON(w, http.StatusOK, body)
}
func PodKill(w http.ResponseWriter, r *http.Request) {
diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go
index b90154e30..c63cf8f0e 100644
--- a/pkg/api/handlers/types.go
+++ b/pkg/api/handlers/types.go
@@ -42,9 +42,9 @@ type ContainersPruneReport struct {
}
type LibpodContainersPruneReport struct {
- ID string `json:"id"`
- SpaceReclaimed int64 `json:"space"`
- PruneError string `json:"error"`
+ ID string `json:"Id"`
+ SpaceReclaimed int64 `json:"Size"`
+ PruneError string `json:"Err,omitempty"`
}
type Info struct {
diff --git a/pkg/api/handlers/utils/handler.go b/pkg/api/handlers/utils/handler.go
index 29139a98e..96b7a957c 100644
--- a/pkg/api/handlers/utils/handler.go
+++ b/pkg/api/handlers/utils/handler.go
@@ -145,12 +145,12 @@ func MarshalErrorSliceJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) {
}
}
-func MarshalErrorJSONIsEmpty(_ unsafe.Pointer) bool {
- return false
+func MarshalErrorJSONIsEmpty(ptr unsafe.Pointer) bool {
+ return *((*error)(ptr)) == nil
}
-func MarshalErrorSliceJSONIsEmpty(_ unsafe.Pointer) bool {
- return false
+func MarshalErrorSliceJSONIsEmpty(ptr unsafe.Pointer) bool {
+ return len(*((*[]error)(ptr))) <= 0
}
// WriteJSON writes an interface value encoded as JSON to w
diff --git a/pkg/api/handlers/utils/handler_test.go b/pkg/api/handlers/utils/handler_test.go
index 18a1d2678..5957e7d74 100644
--- a/pkg/api/handlers/utils/handler_test.go
+++ b/pkg/api/handlers/utils/handler_test.go
@@ -138,3 +138,51 @@ func TestEqualVersion(t *testing.T) {
rr.Body.String(), expected)
}
}
+
+func TestErrorEncoderFuncOmit(t *testing.T) {
+ data, err := json.Marshal(struct {
+ Err error `json:"err,omitempty"`
+ Errs []error `json:"errs,omitempty"`
+ }{})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ dataAsMap := make(map[string]interface{})
+ err = json.Unmarshal(data, &dataAsMap)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, ok := dataAsMap["err"]
+ if ok {
+ t.Errorf("the `err` field should have been omitted")
+ }
+ _, ok = dataAsMap["errs"]
+ if ok {
+ t.Errorf("the `errs` field should have been omitted")
+ }
+
+ dataAsMap = make(map[string]interface{})
+ data, err = json.Marshal(struct {
+ Err error `json:"err"`
+ Errs []error `json:"errs"`
+ }{})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = json.Unmarshal(data, &dataAsMap)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, ok = dataAsMap["err"]
+ if !ok {
+ t.Errorf("the `err` field shouldn't have been omitted")
+ }
+ _, ok = dataAsMap["errs"]
+ if !ok {
+ t.Errorf("the `errs` field shouldn't have been omitted")
+ }
+}
diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go
index 8dcea1301..c4919182b 100644
--- a/pkg/api/server/register_containers.go
+++ b/pkg/api/server/register_containers.go
@@ -442,6 +442,7 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// - in: query
// name: ps_args
// type: string
+ // default: -ef
// description: arguments to pass to ps such as aux. Requires ps(1) to be installed in the container if no ps(1) compatible AIX descriptors are used.
// produces:
// - application/json
@@ -1142,19 +1143,23 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// name: name
// type: string
// required: true
- // description: |
- // Name of container to query for processes
- // (As of version 1.xx)
+ // description: Name of container to query for processes (As of version 1.xx)
// - in: query
// name: stream
// type: boolean
- // default: true
- // description: Stream the output
+ // description: when true, repeatedly stream the latest output (As of version 4.0)
+ // - in: query
+ // name: delay
+ // type: integer
+ // description: if streaming, delay in seconds between updates. Must be >1. (As of version 4.0)
+ // default: 5
// - in: query
// name: ps_args
// type: string
// default: -ef
- // description: arguments to pass to ps such as aux. Requires ps(1) to be installed in the container if no ps(1) compatible AIX descriptors are used.
+ // description: |
+ // arguments to pass to ps such as aux.
+ // Requires ps(1) to be installed in the container if no ps(1) compatible AIX descriptors are used.
// produces:
// - application/json
// responses:
diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go
index 95a8b4939..38ceea271 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -103,7 +103,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// - application/json
// responses:
// 200:
- // $ref: "#/responses/DockerImageSummary"
+ // $ref: "#/responses/DockerImageSummaryResponse"
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/images/json"), s.APIHandler(compat.GetImages)).Methods(http.MethodGet)
@@ -837,7 +837,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// - application/json
// responses:
// 200:
- // $ref: "#/responses/DockerImageSummary"
+ // $ref: "#/responses/LibpodImageSummaryResponse"
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/libpod/images/json"), s.APIHandler(libpod.GetImages)).Methods(http.MethodGet)
@@ -967,7 +967,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// - application/json
// responses:
// 200:
- // $ref: "#/responses/DocsImageDeleteResponse"
+ // $ref: "#/responses/DocsLibpodImagesRemoveResponse"
// 400:
// $ref: "#/responses/BadParamError"
// 404:
@@ -1069,7 +1069,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// - application/json
// responses:
// 200:
- // $ref: "#/responses/DocsImageDeleteResponse"
+ // $ref: "#/responses/DocsLibpodPruneResponse"
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/libpod/images/prune"), s.APIHandler(libpod.PruneImages)).Methods(http.MethodPost)
diff --git a/pkg/api/server/register_pods.go b/pkg/api/server/register_pods.go
index de3669a0a..16a7bbb4c 100644
--- a/pkg/api/server/register_pods.go
+++ b/pkg/api/server/register_pods.go
@@ -296,18 +296,23 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// name: name
// type: string
// required: true
- // description: |
- // Name of pod to query for processes
+ // description: Name of pod to query for processes
// - in: query
// name: stream
// type: boolean
- // default: true
- // description: Stream the output
+ // description: when true, repeatedly stream the latest output (As of version 4.0)
+ // - in: query
+ // name: delay
+ // type: integer
+ // description: if streaming, delay in seconds between updates. Must be >1. (As of version 4.0)
+ // default: 5
// - in: query
// name: ps_args
// type: string
// default: -ef
- // description: arguments to pass to ps such as aux. Requires ps(1) to be installed in the container if no ps(1) compatible AIX descriptors are used.
+ // description: |
+ // arguments to pass to ps such as aux.
+ // Requires ps(1) to be installed in the container if no ps(1) compatible AIX descriptors are used.
// responses:
// 200:
// $ref: "#/responses/DocsPodTopResponse"
diff --git a/pkg/api/server/swagger.go b/pkg/api/server/swagger.go
index 0fd66652e..3f8f6f9c5 100644
--- a/pkg/api/server/swagger.go
+++ b/pkg/api/server/swagger.go
@@ -6,6 +6,7 @@ import (
"github.com/containers/podman/v3/pkg/domain/entities"
"github.com/containers/podman/v3/pkg/domain/entities/reports"
"github.com/containers/podman/v3/pkg/errorhandling"
+ docker "github.com/docker/docker/api/types"
)
// No such image
@@ -134,9 +135,16 @@ type swagPodAlreadyStopped struct {
}
}
-// Image summary
-// swagger:response DockerImageSummary
-type swagImageSummary struct {
+// Image summary for compat API
+// swagger:response DockerImageSummaryResponse
+type swagDockerImageSummaryResponse struct {
+ // in:body
+ Body []docker.ImageSummary
+}
+
+// Image summary for libpod API
+// swagger:response LibpodImageSummaryResponse
+type swagLibpodImageSummaryResponse struct {
// in:body
Body []entities.ImageSummary
}
diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go
index dc75dac5a..a2be44ab4 100644
--- a/pkg/bindings/connection.go
+++ b/pkg/bindings/connection.go
@@ -390,6 +390,11 @@ func (h *APIResponse) IsClientError() bool {
return h.Response.StatusCode/100 == 4
}
+// IsConflictError returns true if the response code is 409
+func (h *APIResponse) IsConflictError() bool {
+ return h.Response.StatusCode == 409
+}
+
// IsServerError returns true if the response code is 5xx
func (h *APIResponse) IsServerError() bool {
return h.Response.StatusCode/100 == 5
diff --git a/pkg/bindings/errors.go b/pkg/bindings/errors.go
index 9c311d912..ec837b39c 100644
--- a/pkg/bindings/errors.go
+++ b/pkg/bindings/errors.go
@@ -12,17 +12,22 @@ var (
ErrNotImplemented = errors.New("function not implemented")
)
-func handleError(data []byte) error {
- e := errorhandling.ErrorModel{}
- if err := json.Unmarshal(data, &e); err != nil {
+func handleError(data []byte, unmarshalErrorInto interface{}) error {
+ if err := json.Unmarshal(data, unmarshalErrorInto); err != nil {
return err
}
- return e
+ return unmarshalErrorInto.(error)
}
// Process drains the response body, and processes the HTTP status code
// Note: Closing the response.Body is left to the caller
func (h APIResponse) Process(unmarshalInto interface{}) error {
+ return h.ProcessWithError(unmarshalInto, &errorhandling.ErrorModel{})
+}
+
+// Process drains the response body, and processes the HTTP status code
+// Note: Closing the response.Body is left to the caller
+func (h APIResponse) ProcessWithError(unmarshalInto interface{}, unmarshalErrorInto interface{}) error {
data, err := ioutil.ReadAll(h.Response.Body)
if err != nil {
return errors.Wrap(err, "unable to process API response")
@@ -33,14 +38,22 @@ func (h APIResponse) Process(unmarshalInto interface{}) error {
}
return nil
}
+
+ if h.IsConflictError() {
+ return handleError(data, unmarshalErrorInto)
+ }
+
// TODO should we add a debug here with the response code?
- return handleError(data)
+ return handleError(data, &errorhandling.ErrorModel{})
}
func CheckResponseCode(inError error) (int, error) {
- e, ok := inError.(errorhandling.ErrorModel)
- if !ok {
+ switch e := inError.(type) {
+ case *errorhandling.ErrorModel:
+ return e.Code(), nil
+ case *errorhandling.PodConflictErrorModel:
+ return e.Code(), nil
+ default:
return -1, errors.New("error is not type ErrorModel")
}
- return e.Code(), nil
}
diff --git a/pkg/bindings/pods/pods.go b/pkg/bindings/pods/pods.go
index a1a431a3b..3b5832373 100644
--- a/pkg/bindings/pods/pods.go
+++ b/pkg/bindings/pods/pods.go
@@ -9,6 +9,7 @@ import (
"github.com/containers/podman/v3/pkg/api/handlers"
"github.com/containers/podman/v3/pkg/bindings"
"github.com/containers/podman/v3/pkg/domain/entities"
+ "github.com/containers/podman/v3/pkg/errorhandling"
jsoniter "github.com/json-iterator/go"
)
@@ -97,7 +98,7 @@ func Kill(ctx context.Context, nameOrID string, options *KillOptions) (*entities
}
defer response.Body.Close()
- return &report, response.Process(&report)
+ return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{})
}
// Pause pauses all running containers in a given pod.
@@ -117,7 +118,7 @@ func Pause(ctx context.Context, nameOrID string, options *PauseOptions) (*entiti
}
defer response.Body.Close()
- return &report, response.Process(&report)
+ return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{})
}
// Prune by default removes all non-running pods in local storage.
@@ -184,7 +185,7 @@ func Restart(ctx context.Context, nameOrID string, options *RestartOptions) (*en
}
defer response.Body.Close()
- return &report, response.Process(&report)
+ return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{})
}
// Remove deletes a Pod from from local storage. The optional force parameter denotes
@@ -232,7 +233,8 @@ func Start(ctx context.Context, nameOrID string, options *StartOptions) (*entiti
report.Id = nameOrID
return &report, nil
}
- return &report, response.Process(&report)
+
+ return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{})
}
// Stop stops all containers in a Pod. The optional timeout parameter can be
@@ -260,7 +262,7 @@ func Stop(ctx context.Context, nameOrID string, options *StopOptions) (*entities
report.Id = nameOrID
return &report, nil
}
- return &report, response.Process(&report)
+ return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{})
}
// Top gathers statistics about the running processes in a pod. The nameOrID can be a pod name
@@ -316,7 +318,7 @@ func Unpause(ctx context.Context, nameOrID string, options *UnpauseOptions) (*en
}
defer response.Body.Close()
- return &report, response.Process(&report)
+ return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{})
}
// Stats display resource-usage statistics of one or more pods.
diff --git a/pkg/bindings/test/common_test.go b/pkg/bindings/test/common_test.go
index 91ebe21fc..d996595bf 100644
--- a/pkg/bindings/test/common_test.go
+++ b/pkg/bindings/test/common_test.go
@@ -225,12 +225,23 @@ func (b *bindingTest) RunTopContainer(containerName *string, podName *string) (s
// This method creates a pod with the given pod name.
// Podname is an optional parameter
func (b *bindingTest) Podcreate(name *string) {
+ b.PodcreateAndExpose(name, nil)
+}
+
+// This method creates a pod with the given pod name and publish port.
+// Podname is an optional parameter
+// port is an optional parameter
+func (b *bindingTest) PodcreateAndExpose(name *string, port *string) {
+ command := []string{"pod", "create"}
if name != nil {
podname := *name
- b.runPodman([]string{"pod", "create", "--name", podname}).Wait(45)
- } else {
- b.runPodman([]string{"pod", "create"}).Wait(45)
+ command = append(command, "--name", podname)
+ }
+ if port != nil {
+ podport := *port
+ command = append(command, "--publish", podport)
}
+ b.runPodman(command).Wait(45)
}
// StringInSlice returns a boolean based on whether a given
diff --git a/pkg/bindings/test/pods_test.go b/pkg/bindings/test/pods_test.go
index 5331cf439..879d4d00d 100644
--- a/pkg/bindings/test/pods_test.go
+++ b/pkg/bindings/test/pods_test.go
@@ -1,6 +1,7 @@
package test_bindings
import (
+ "fmt"
"net/http"
"strings"
"time"
@@ -9,7 +10,9 @@ import (
"github.com/containers/podman/v3/pkg/bindings"
"github.com/containers/podman/v3/pkg/bindings/pods"
"github.com/containers/podman/v3/pkg/domain/entities"
+ "github.com/containers/podman/v3/pkg/errorhandling"
"github.com/containers/podman/v3/pkg/specgen"
+ "github.com/containers/podman/v3/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
@@ -208,6 +211,29 @@ var _ = Describe("Podman pods", func() {
}
})
+ It("start pod with port conflict", func() {
+ randomport, err := utils.GetRandomPort()
+ Expect(err).To(BeNil())
+
+ portPublish := fmt.Sprintf("%d:%d", randomport, randomport)
+ var podwithport string = "newpodwithport"
+ bt.PodcreateAndExpose(&podwithport, &portPublish)
+
+ // Start pod and expose port 12345
+ _, err = pods.Start(bt.conn, podwithport, nil)
+ Expect(err).To(BeNil())
+
+ // Start another pod and expose same port 12345
+ var podwithport2 string = "newpodwithport2"
+ bt.PodcreateAndExpose(&podwithport2, &portPublish)
+
+ _, err = pods.Start(bt.conn, podwithport2, nil)
+ Expect(err).ToNot(BeNil())
+ code, _ := bindings.CheckResponseCode(err)
+ Expect(code).To(BeNumerically("==", http.StatusConflict))
+ Expect(err).To(BeAssignableToTypeOf(&errorhandling.PodConflictErrorModel{}))
+ })
+
It("start stop restart pod", func() {
// Start an invalid pod
_, err = pods.Start(bt.conn, "dummyName", nil)
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
index ac5e6f410..38cdc8f2f 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -50,6 +50,7 @@ func (i *Image) Id() string { // nolint
return i.ID
}
+// swagger:model LibpodImageSummary
type ImageSummary struct {
ID string `json:"Id"`
ParentId string // nolint
diff --git a/pkg/domain/entities/reports/prune.go b/pkg/domain/entities/reports/prune.go
index 5494ac3ae..219e35b67 100644
--- a/pkg/domain/entities/reports/prune.go
+++ b/pkg/domain/entities/reports/prune.go
@@ -1,9 +1,9 @@
package reports
type PruneReport struct {
- Id string //nolint
- Err error
- Size uint64
+ Id string `json:"Id"` //nolint
+ Err error `json:"Err,omitempty"`
+ Size uint64 `json:"Size"`
}
func PruneReportsIds(r []*PruneReport) []string {
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index 8878bf128..7aa202334 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -57,7 +57,7 @@ func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOption
pruneOptions.Filters = append(pruneOptions.Filters, "containers=false")
}
- var pruneReports []*reports.PruneReport
+ pruneReports := make([]*reports.PruneReport, 0)
// Now prune all images until we converge.
numPreviouslyRemovedImages := 1
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index 3f78ba7bc..5b5a1912c 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -228,7 +228,7 @@ func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []st
for _, name := range namesOrIds {
inspect, err := containers.Inspect(ic.ClientCtx, name, options)
if err != nil {
- errModel, ok := err.(errorhandling.ErrorModel)
+ errModel, ok := err.(*errorhandling.ErrorModel)
if !ok {
return nil, nil, err
}
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index b8af2de68..e17f746a5 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -188,7 +188,7 @@ func (ir *ImageEngine) Inspect(ctx context.Context, namesOrIDs []string, opts en
for _, i := range namesOrIDs {
r, err := images.GetImage(ir.ClientCtx, i, options)
if err != nil {
- errModel, ok := err.(errorhandling.ErrorModel)
+ errModel, ok := err.(*errorhandling.ErrorModel)
if !ok {
return nil, nil, err
}
diff --git a/pkg/domain/infra/tunnel/network.go b/pkg/domain/infra/tunnel/network.go
index 79fba1943..069982d30 100644
--- a/pkg/domain/infra/tunnel/network.go
+++ b/pkg/domain/infra/tunnel/network.go
@@ -25,7 +25,7 @@ func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []stri
for _, name := range namesOrIds {
report, err := network.Inspect(ic.ClientCtx, name, options)
if err != nil {
- errModel, ok := err.(errorhandling.ErrorModel)
+ errModel, ok := err.(*errorhandling.ErrorModel)
if !ok {
return nil, nil, err
}
diff --git a/pkg/domain/infra/tunnel/secrets.go b/pkg/domain/infra/tunnel/secrets.go
index 6337c7fbe..e5fa200bd 100644
--- a/pkg/domain/infra/tunnel/secrets.go
+++ b/pkg/domain/infra/tunnel/secrets.go
@@ -28,7 +28,7 @@ func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string
for _, name := range nameOrIDs {
inspected, err := secrets.Inspect(ic.ClientCtx, name, nil)
if err != nil {
- errModel, ok := err.(errorhandling.ErrorModel)
+ errModel, ok := err.(*errorhandling.ErrorModel)
if !ok {
return nil, nil, err
}
@@ -67,7 +67,7 @@ func (ic *ContainerEngine) SecretRm(ctx context.Context, nameOrIDs []string, opt
for _, name := range nameOrIDs {
secret, err := secrets.Inspect(ic.ClientCtx, name, nil)
if err != nil {
- errModel, ok := err.(errorhandling.ErrorModel)
+ errModel, ok := err.(*errorhandling.ErrorModel)
if !ok {
return nil, err
}
diff --git a/pkg/domain/infra/tunnel/volumes.go b/pkg/domain/infra/tunnel/volumes.go
index cfd1574c3..ccb363935 100644
--- a/pkg/domain/infra/tunnel/volumes.go
+++ b/pkg/domain/infra/tunnel/volumes.go
@@ -59,7 +59,7 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin
for _, id := range namesOrIds {
data, err := volumes.Inspect(ic.ClientCtx, id, nil)
if err != nil {
- errModel, ok := err.(errorhandling.ErrorModel)
+ errModel, ok := err.(*errorhandling.ErrorModel)
if !ok {
return nil, nil, err
}
diff --git a/pkg/errorhandling/errorhandling.go b/pkg/errorhandling/errorhandling.go
index 44a0c3efd..04110b62a 100644
--- a/pkg/errorhandling/errorhandling.go
+++ b/pkg/errorhandling/errorhandling.go
@@ -83,6 +83,12 @@ func Contains(err error, sub error) bool {
return strings.Contains(err.Error(), sub.Error())
}
+// PodConflictErrorModel is used in remote connections with podman
+type PodConflictErrorModel struct {
+ Errs []string
+ Id string //nolint
+}
+
// ErrorModel is used in remote connections with podman
type ErrorModel struct {
// API root cause formatted for automated parsing
@@ -106,3 +112,11 @@ func (e ErrorModel) Cause() error {
func (e ErrorModel) Code() int {
return e.ResponseCode
}
+
+func (e PodConflictErrorModel) Error() string {
+ return strings.Join(e.Errs, ",")
+}
+
+func (e PodConflictErrorModel) Code() int {
+ return 409
+}
diff --git a/test/apiv2/10-images.at b/test/apiv2/10-images.at
index d3fde9f9d..b7bcaf81d 100644
--- a/test/apiv2/10-images.at
+++ b/test/apiv2/10-images.at
@@ -218,6 +218,9 @@ if ! grep -q '400 Bad Request' "${TMPD}/headers.txt"; then
BUILD_TEST_ERROR="1"
fi
+t POST libpod/images/prune 200
+t POST libpod/images/prune 200 length=0 []
+
cleanBuildTest
if [[ "${BUILD_TEST_ERROR}" ]]; then
exit 1
diff --git a/test/apiv2/40-pods.at b/test/apiv2/40-pods.at
index 985b26411..f45e85f61 100644
--- a/test/apiv2/40-pods.at
+++ b/test/apiv2/40-pods.at
@@ -110,11 +110,11 @@ t GET libpod/pods/fakename/top 404 \
.cause="no such pod"
t GET libpod/pods/foo/top 200 \
- .Processes[0][-1]="/pause " \
+ .Processes[0][-1]="/pause" \
.Titles[-1]="COMMAND"
t GET libpod/pods/foo/top?ps_args=args,pid 200 \
- .Processes[0][0]="/pause " \
+ .Processes[0][0]="/pause" \
.Processes[0][1]="1" \
.Titles[0]="COMMAND" \
.Titles[1]="PID" \
diff --git a/test/apiv2/python/rest_api/test_v2_0_0_container.py b/test/apiv2/python/rest_api/test_v2_0_0_container.py
index 853e9da88..101044bbb 100644
--- a/test/apiv2/python/rest_api/test_v2_0_0_container.py
+++ b/test/apiv2/python/rest_api/test_v2_0_0_container.py
@@ -1,8 +1,11 @@
+import multiprocessing
+import queue
import random
+import threading
import unittest
-import json
import requests
+import time
from dateutil.parser import parse
from .fixtures import APITestCase
@@ -16,7 +19,10 @@ class ContainerTestCase(APITestCase):
self.assertEqual(len(obj), 1)
def test_list_filters(self):
- r = requests.get(self.podman_url + "/v1.40/containers/json?filters%3D%7B%22status%22%3A%5B%22running%22%5D%7D")
+ r = requests.get(
+ self.podman_url
+ + "/v1.40/containers/json?filters%3D%7B%22status%22%3A%5B%22running%22%5D%7D"
+ )
self.assertEqual(r.status_code, 200, r.text)
payload = r.json()
containerAmnt = len(payload)
@@ -33,18 +39,18 @@ class ContainerTestCase(APITestCase):
self.assertId(r.content)
_ = parse(r.json()["Created"])
-
r = requests.post(
self.podman_url + "/v1.40/containers/create?name=topcontainer",
- json={"Cmd": ["top"],
- "Image": "alpine:latest",
- "Healthcheck": {
- "Test": ["CMD", "pidof", "top"],
- "Interval": 5000000000,
- "Timeout": 2000000000,
- "Retries": 3,
- "StartPeriod": 5000000000
- }
+ json={
+ "Cmd": ["top"],
+ "Image": "alpine:latest",
+ "Healthcheck": {
+ "Test": ["CMD", "pidof", "top"],
+ "Interval": 5000000000,
+ "Timeout": 2000000000,
+ "Retries": 3,
+ "StartPeriod": 5000000000,
+ },
},
)
self.assertEqual(r.status_code, 201, r.text)
@@ -67,7 +73,7 @@ class ContainerTestCase(APITestCase):
self.assertEqual(r.status_code, 200, r.text)
self.assertId(r.content)
out = r.json()
- hc = out["Config"]["Healthcheck"]["Test"]
+ hc = out["Config"]["Healthcheck"]["Test"]
self.assertListEqual(["CMD", "pidof", "top"], hc)
r = requests.post(self.podman_url + f"/v1.40/containers/{container_id}/start")
@@ -84,7 +90,9 @@ class ContainerTestCase(APITestCase):
self.assertIn(r.status_code, (200, 409), r.text)
if r.status_code == 200:
self.assertId(r.content)
- r = requests.get(self.uri(self.resolve_container("/containers/{}/stats?stream=false&one-shot=true")))
+ r = requests.get(
+ self.uri(self.resolve_container("/containers/{}/stats?stream=false&one-shot=true"))
+ )
self.assertIn(r.status_code, (200, 409), r.text)
if r.status_code == 200:
self.assertId(r.content)
@@ -136,9 +144,15 @@ class ContainerTestCase(APITestCase):
payload = r.json()
container_id = payload["Id"]
self.assertIsNotNone(container_id)
- r = requests.get(self.podman_url + f"/v1.40/containers/{payload['Id']}/logs?follow=false&stdout=true&until=0")
+ r = requests.get(
+ self.podman_url
+ + f"/v1.40/containers/{payload['Id']}/logs?follow=false&stdout=true&until=0"
+ )
self.assertEqual(r.status_code, 200, r.text)
- r = requests.get(self.podman_url + f"/v1.40/containers/{payload['Id']}/logs?follow=false&stdout=true&until=1")
+ r = requests.get(
+ self.podman_url
+ + f"/v1.40/containers/{payload['Id']}/logs?follow=false&stdout=true&until=1"
+ )
self.assertEqual(r.status_code, 200, r.text)
def test_commit(self):
@@ -257,6 +271,63 @@ class ContainerTestCase(APITestCase):
r = requests.delete(self.podman_url + f"/v1.40/containers/{container_id}")
self.assertEqual(r.status_code, 204, r.text)
+ def test_top_no_stream(self):
+ uri = self.uri(self.resolve_container("/containers/{}/top"))
+ q = queue.Queue()
+
+ def _impl(fifo):
+ fifo.put(requests.get(uri, params={"stream": False}, timeout=2))
+
+ top = threading.Thread(target=_impl, args=(q,))
+ top.start()
+ time.sleep(2)
+ self.assertFalse(top.is_alive(), f"GET {uri} failed to return in 2s")
+
+ qr = q.get(False)
+ self.assertEqual(qr.status_code, 200, qr.text)
+
+ qr.close()
+ top.join()
+
+ def test_top_stream(self):
+ uri = self.uri(self.resolve_container("/containers/{}/top"))
+ q = queue.Queue()
+
+ stop_thread = False
+
+ def _impl(fifo, stop):
+ try:
+ with requests.get(uri, params={"stream": True, "delay": 1}, stream=True) as r:
+ r.raise_for_status()
+ fifo.put(r)
+ for buf in r.iter_lines(chunk_size=None):
+ if stop():
+ break
+ fifo.put(buf)
+ except Exception:
+ pass
+
+ top = threading.Thread(target=_impl, args=(q, (lambda: stop_thread)))
+ top.start()
+ time.sleep(4)
+ self.assertTrue(top.is_alive(), f"GET {uri} exited too soon")
+ stop_thread = True
+
+ for _ in range(10):
+ try:
+ qr = q.get_nowait()
+ if qr is not None:
+ self.assertEqual(qr.status_code, 200)
+ qr.close()
+ break
+ except queue.Empty:
+ pass
+ finally:
+ time.sleep(1)
+ else:
+ self.fail("Server failed to respond in 10s")
+ top.join()
+
if __name__ == "__main__":
unittest.main()
diff --git a/test/system/035-logs.bats b/test/system/035-logs.bats
index 44b66676e..7fb3e62e4 100644
--- a/test/system/035-logs.bats
+++ b/test/system/035-logs.bats
@@ -30,6 +30,17 @@ load helpers
run_podman rm $cid
}
+function _additional_events_backend() {
+ local driver=$1
+ # Since PR#10431, 'logs -f' with journald driver is only supported with journald events backend.
+ if [[ $driver = "journald" ]]; then
+ run_podman info --format '{{.Host.EventLogger}}' >/dev/null
+ if [[ $output != "journald" ]]; then
+ echo "--events-backend journald"
+ fi
+ fi
+}
+
function _log_test_multi() {
local driver=$1
@@ -42,10 +53,12 @@ function _log_test_multi() {
etc='.*'
fi
+ local events_backend=$(_additional_events_backend $driver)
+
# Simple helper to make the container starts, below, easier to read
local -a cid
doit() {
- run_podman run --log-driver=$driver --rm -d --name "$1" $IMAGE sh -c "$2";
+ run_podman ${events_backend} run --log-driver=$driver --rm -d --name "$1" $IMAGE sh -c "$2";
cid+=($(echo "${output:0:12}"))
}
@@ -57,7 +70,7 @@ function _log_test_multi() {
doit c1 "echo a;sleep 10;echo d;sleep 3"
doit c2 "sleep 1;echo b;sleep 2;echo c;sleep 3"
- run_podman logs -f c1 c2
+ run_podman ${events_backend} logs -f c1 c2
is "$output" \
"${cid[0]} a$etc
${cid[1]} b$etc
@@ -187,15 +200,20 @@ function _log_test_follow() {
contentA=$(random_string)
contentB=$(random_string)
contentC=$(random_string)
+ local events_backend=$(_additional_events_backend $driver)
+
+ if [[ -n "${events_backend}" ]]; then
+ skip_if_remote "remote does not support --events-backend"
+ fi
# Note: it seems we need at least three log lines to hit #11461.
- run_podman run --log-driver=$driver --name $cname $IMAGE sh -c "echo $contentA; echo $contentB; echo $contentC"
- run_podman logs -f $cname
+ run_podman ${events_backend} run --log-driver=$driver --name $cname $IMAGE sh -c "echo $contentA; echo $contentB; echo $contentC"
+ run_podman ${events_backend} logs -f $cname
is "$output" "$contentA
$contentB
$contentC" "logs -f on exitted container works"
- run_podman rm -t 0 -f $cname
+ run_podman ${events_backend} rm -t 0 -f $cname
}
@test "podman logs - --follow k8s-file" {
diff --git a/test/system/070-build.bats b/test/system/070-build.bats
index 1d24eef15..3c47b1f5b 100644
--- a/test/system/070-build.bats
+++ b/test/system/070-build.bats
@@ -39,6 +39,7 @@ EOF
cat >$dockerfile <<EOF
FROM $IMAGE
RUN echo $rand_content > /$rand_filename
+VOLUME /a/b/c
VOLUME ['/etc/foo', '/etc/bar']
EOF
@@ -56,6 +57,25 @@ EOF
/\[/etc/foo,
/etc/bar]" "weird VOLUME gets converted to directories with brackets and comma"
+ # Now confirm that each volume got a unique device ID
+ run_podman run --rm build_test stat -c '%D' / /a /a/b /a/b/c /\[ /\[/etc /\[/etc/foo, /etc /etc/bar\]
+ # First, the non-volumes should all be the same...
+ is "${lines[0]}" "${lines[1]}" "devnum( / ) = devnum( /a )"
+ is "${lines[0]}" "${lines[2]}" "devnum( / ) = devnum( /a/b )"
+ is "${lines[0]}" "${lines[4]}" "devnum( / ) = devnum( /[ )"
+ is "${lines[0]}" "${lines[5]}" "devnum( / ) = devnum( /[etc )"
+ is "${lines[0]}" "${lines[7]}" "devnum( / ) = devnum( /etc )"
+ is "${lines[6]}" "${lines[8]}" "devnum( /[etc/foo, ) = devnum( /etc/bar] )"
+ # ...then, each volume should be different
+ if [[ "${lines[0]}" = "${lines[3]}" ]]; then
+ die "devnum( / ) (${lines[0]}) = devnum( volume0 ) (${lines[3]}) -- they should differ"
+ fi
+ if [[ "${lines[0]}" = "${lines[6]}" ]]; then
+ die "devnum( / ) (${lines[0]}) = devnum( volume1 ) (${lines[6]}) -- they should differ"
+ fi
+ # FIXME: is this expected? I thought /a/b/c and /[etc/foo, would differ
+ is "${lines[3]}" "${lines[6]}" "devnum( volume0 ) = devnum( volume1 )"
+
run_podman rmi -f build_test
}
diff --git a/test/system/130-kill.bats b/test/system/130-kill.bats
index d85f0a6a9..1ff3a7b61 100644
--- a/test/system/130-kill.bats
+++ b/test/system/130-kill.bats
@@ -6,9 +6,22 @@
load helpers
@test "podman kill - test signal handling in containers" {
+
+ # Prepare for 'logs -f'
+ run_podman info --format '{{.Host.LogDriver}}'
+ log_driver=$output
+ run_podman info --format '{{.Host.EventLogger}}'
+ event_logger=$output
+ opt_log_driver=
+ if [ $log_driver = "journald" ] && [ $event_logger != "journald" ]; then
+ # Since PR#10431, 'logs -f' with journald driver is only supported with journald events backend.
+ # Set '--log driver' temporally because remote doesn't support '--events-backend'.
+ opt_log_driver="--log-driver k8s-file"
+ fi
+
# Start a container that will handle all signals by emitting 'got: N'
local -a signals=(1 2 3 4 5 6 8 10 12 13 14 15 16 20 21 22 23 24 25 26 64)
- run_podman run -d $IMAGE sh -c \
+ run_podman run -d ${opt_log_driver} $IMAGE sh -c \
"for i in ${signals[*]}; do trap \"echo got: \$i\" \$i; done;
echo READY;
while ! test -e /stop; do sleep 0.05; done;
diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats
index cb73cf24d..b3471b425 100644
--- a/test/system/500-networking.bats
+++ b/test/system/500-networking.bats
@@ -444,6 +444,14 @@ load helpers
die "MAC address did not change after podman network disconnect/connect"
fi
+ # FIXME FIXME FIXME: #11825: bodhi tests are failing, remote+rootless only,
+ # with "dnsmasq: failed to create inotify". This error has never occurred
+ # in CI, and Ed has been unable to reproduce it on 1minutetip. This next
+ # line is a suggestion from Paul Holzinger for trying to shed light on
+ # the system context before the failure. This output will be invisible
+ # if the test passes.
+ for foo in /proc/\*/fd/*; do readlink -f $foo; done |grep '^/proc/.*inotify' |cut -d/ -f3 | xargs -I '{}' -- ps --no-headers -o '%p %U %a' -p '{}' |uniq -c |sort -n
+
# connect a second network
run_podman network connect $netname2 $cid