summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--OWNERS2
-rw-r--r--docs/source/markdown/podman-run.1.md9
-rw-r--r--libpod/container_internal_linux.go24
-rw-r--r--pkg/api/handlers/compat/containers.go7
-rw-r--r--pkg/api/handlers/compat/containers_pause.go2
-rw-r--r--pkg/api/handlers/compat/containers_restart.go2
-rw-r--r--pkg/api/handlers/compat/containers_start.go4
-rw-r--r--pkg/api/handlers/compat/containers_stop.go4
-rw-r--r--pkg/api/handlers/compat/containers_unpause.go2
-rw-r--r--pkg/api/handlers/compat/images_push.go1
-rw-r--r--pkg/api/handlers/types.go16
-rw-r--r--pkg/domain/entities/images.go22
-rw-r--r--pkg/domain/infra/abi/images_list.go2
-rw-r--r--pkg/util/utils.go5
-rw-r--r--test/apiv2/20-containers.at2
-rw-r--r--test/apiv2/rest_api/test_rest_v2_0_0.py96
-rw-r--r--test/system/030-run.bats24
-rw-r--r--test/system/035-logs.bats3
-rw-r--r--utils/utils_supported.go2
19 files changed, 174 insertions, 55 deletions
diff --git a/OWNERS b/OWNERS
index 7cbef7f37..823385f46 100644
--- a/OWNERS
+++ b/OWNERS
@@ -8,6 +8,7 @@ approvers:
- TomSweeneyRedHat
- vrothberg
- umohnani8
+ - Luap99
reviewers:
- baude
- edsantiago
@@ -20,3 +21,4 @@ reviewers:
- ashley-cui
- QiWang19
- umohnani8
+ - Luap99
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index 1038906c0..53c5b2d4b 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -26,9 +26,12 @@ Several files will be automatically created within the container. These include
_/etc/hosts_, _/etc/hostname_, and _/etc/resolv.conf_ to manage networking.
These will be based on the host's version of the files, though they can be
customized with options (for example, **--dns** will override the host's DNS
-servers in the created _resolv.conf_). Additionally, an empty file is created in
-each container to indicate to programs they are running in a container. This file
-is located at _/run/.containerenv_.
+servers in the created _resolv.conf_). Additionally, a container environment
+file is created in each container to indicate to programs they are running in a
+container. This file is located at _/run/.containerenv_. When using the
+--privileged flag the .containerenv contains name/value pairs indicating the
+container engine version, whether the engine is running in rootless mode, the
+container name and id, as well as the image name and id that the container is based on.
When running from a user defined network namespace, the _/etc/netns/NSNAME/resolv.conf_
will be used if it exists, otherwise _/etc/resolv.conf_ will be used.
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 9a417d75e..72eaeac8e 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -35,6 +35,7 @@ import (
"github.com/containers/podman/v2/pkg/rootless"
"github.com/containers/podman/v2/pkg/util"
"github.com/containers/podman/v2/utils"
+ "github.com/containers/podman/v2/version"
"github.com/containers/storage/pkg/archive"
"github.com/containers/storage/pkg/idtools"
securejoin "github.com/cyphar/filepath-securejoin"
@@ -1423,11 +1424,26 @@ func (c *Container) makeBindMounts() error {
}
}
- // Make .containerenv
- // Empty file, so no need to recreate if it exists
+ // Make .containerenv if it does not exist
if _, ok := c.state.BindMounts["/run/.containerenv"]; !ok {
- // Empty string for now, but we may consider populating this later
- containerenvPath, err := c.writeStringToRundir(".containerenv", "")
+ var containerenv string
+ isRootless := 0
+ if rootless.IsRootless() {
+ isRootless = 1
+ }
+ imageID, imageName := c.Image()
+
+ if c.Privileged() {
+ // Populate the .containerenv with container information
+ containerenv = fmt.Sprintf(`engine="podman-%s"
+name=%q
+id=%q
+image=%q
+imageid=%q
+rootless=%d
+`, version.Version.String(), c.Name(), c.ID(), imageName, imageID, isRootless)
+ }
+ containerenvPath, err := c.writeStringToRundir(".containerenv", containerenv)
if err != nil {
return errors.Wrapf(err, "error creating containerenv file for container %s", c.ID())
}
diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go
index 5886455e7..7a3e5dd84 100644
--- a/pkg/api/handlers/compat/containers.go
+++ b/pkg/api/handlers/compat/containers.go
@@ -17,6 +17,7 @@ import (
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/go-connections/nat"
+ "github.com/gorilla/mux"
"github.com/gorilla/schema"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -73,7 +74,7 @@ func RemoveContainer(w http.ResponseWriter, r *http.Request) {
utils.InternalServerError(w, err)
return
}
- utils.WriteResponse(w, http.StatusNoContent, "")
+ utils.WriteResponse(w, http.StatusNoContent, nil)
}
func ListContainers(w http.ResponseWriter, r *http.Request) {
@@ -207,7 +208,7 @@ func KillContainer(w http.ResponseWriter, r *http.Request) {
}
}
// Success
- utils.WriteResponse(w, http.StatusNoContent, "")
+ utils.WriteResponse(w, http.StatusNoContent, nil)
}
func WaitContainer(w http.ResponseWriter, r *http.Request) {
@@ -215,8 +216,10 @@ func WaitContainer(w http.ResponseWriter, r *http.Request) {
// /{version}/containers/(name)/wait
exitCode, err := utils.WaitContainer(w, r)
if err != nil {
+ logrus.Warnf("failed to wait on container %q: %v", mux.Vars(r)["name"], err)
return
}
+
utils.WriteResponse(w, http.StatusOK, handlers.ContainerWaitOKBody{
StatusCode: int(exitCode),
Error: struct {
diff --git a/pkg/api/handlers/compat/containers_pause.go b/pkg/api/handlers/compat/containers_pause.go
index 8712969c0..a7e0a66f1 100644
--- a/pkg/api/handlers/compat/containers_pause.go
+++ b/pkg/api/handlers/compat/containers_pause.go
@@ -24,5 +24,5 @@ func PauseContainer(w http.ResponseWriter, r *http.Request) {
return
}
// Success
- utils.WriteResponse(w, http.StatusNoContent, "")
+ utils.WriteResponse(w, http.StatusNoContent, nil)
}
diff --git a/pkg/api/handlers/compat/containers_restart.go b/pkg/api/handlers/compat/containers_restart.go
index f4d8f06a1..e8928596a 100644
--- a/pkg/api/handlers/compat/containers_restart.go
+++ b/pkg/api/handlers/compat/containers_restart.go
@@ -41,5 +41,5 @@ func RestartContainer(w http.ResponseWriter, r *http.Request) {
}
// Success
- utils.WriteResponse(w, http.StatusNoContent, "")
+ utils.WriteResponse(w, http.StatusNoContent, nil)
}
diff --git a/pkg/api/handlers/compat/containers_start.go b/pkg/api/handlers/compat/containers_start.go
index 6236b1357..726da6f99 100644
--- a/pkg/api/handlers/compat/containers_start.go
+++ b/pkg/api/handlers/compat/containers_start.go
@@ -39,12 +39,12 @@ func StartContainer(w http.ResponseWriter, r *http.Request) {
return
}
if state == define.ContainerStateRunning {
- utils.WriteResponse(w, http.StatusNotModified, "")
+ utils.WriteResponse(w, http.StatusNotModified, nil)
return
}
if err := con.Start(r.Context(), len(con.PodID()) > 0); err != nil {
utils.InternalServerError(w, err)
return
}
- utils.WriteResponse(w, http.StatusNoContent, "")
+ utils.WriteResponse(w, http.StatusNoContent, nil)
}
diff --git a/pkg/api/handlers/compat/containers_stop.go b/pkg/api/handlers/compat/containers_stop.go
index 13fe25338..8bc58cf59 100644
--- a/pkg/api/handlers/compat/containers_stop.go
+++ b/pkg/api/handlers/compat/containers_stop.go
@@ -40,7 +40,7 @@ func StopContainer(w http.ResponseWriter, r *http.Request) {
}
// If the Container is stopped already, send a 304
if state == define.ContainerStateStopped || state == define.ContainerStateExited {
- utils.WriteResponse(w, http.StatusNotModified, "")
+ utils.WriteResponse(w, http.StatusNotModified, nil)
return
}
@@ -56,5 +56,5 @@ func StopContainer(w http.ResponseWriter, r *http.Request) {
}
// Success
- utils.WriteResponse(w, http.StatusNoContent, "")
+ utils.WriteResponse(w, http.StatusNoContent, nil)
}
diff --git a/pkg/api/handlers/compat/containers_unpause.go b/pkg/api/handlers/compat/containers_unpause.go
index f87b95b64..760e85814 100644
--- a/pkg/api/handlers/compat/containers_unpause.go
+++ b/pkg/api/handlers/compat/containers_unpause.go
@@ -24,5 +24,5 @@ func UnpauseContainer(w http.ResponseWriter, r *http.Request) {
}
// Success
- utils.WriteResponse(w, http.StatusNoContent, "")
+ utils.WriteResponse(w, http.StatusNoContent, nil)
}
diff --git a/pkg/api/handlers/compat/images_push.go b/pkg/api/handlers/compat/images_push.go
index 12593a68c..0f3da53e8 100644
--- a/pkg/api/handlers/compat/images_push.go
+++ b/pkg/api/handlers/compat/images_push.go
@@ -81,5 +81,4 @@ func PushImage(w http.ResponseWriter, r *http.Request) {
}
utils.WriteResponse(w, http.StatusOK, "")
-
}
diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go
index 40cf16807..25e7640b6 100644
--- a/pkg/api/handlers/types.go
+++ b/pkg/api/handlers/types.go
@@ -145,13 +145,14 @@ type PodCreateConfig struct {
Share string `json:"share"`
}
+// HistoryResponse provides details on image layers
type HistoryResponse struct {
- ID string `json:"Id"`
- Created int64 `json:"Created"`
- CreatedBy string `json:"CreatedBy"`
- Tags []string `json:"Tags"`
- Size int64 `json:"Size"`
- Comment string `json:"Comment"`
+ ID string `json:"Id"`
+ Created int64
+ CreatedBy string
+ Tags []string
+ Size int64
+ Comment string
}
type ImageLayer struct{}
@@ -213,6 +214,7 @@ func ImageToImageSummary(l *libpodImage.Image) (*entities.ImageSummary, error) {
ID: l.ID(),
ParentId: l.Parent,
RepoTags: repoTags,
+ RepoDigests: digests,
Created: l.Created().Unix(),
Size: int64(*size),
SharedSize: 0,
@@ -223,7 +225,6 @@ func ImageToImageSummary(l *libpodImage.Image) (*entities.ImageSummary, error) {
Dangling: l.Dangling(),
Names: l.Names(),
Digest: string(l.Digest()),
- Digests: digests,
ConfigDigest: string(l.ConfigDigest),
History: l.NamesHistory(),
}
@@ -329,7 +330,6 @@ func ImageDataToImageInspect(ctx context.Context, l *libpodImage.Image) (*ImageI
dockerImageInspect.Parent = d.Parent.String()
}
return &ImageInspect{dockerImageInspect}, nil
-
}
// portsToPortSet converts libpods exposed ports to dockers structs
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
index ab545d882..81f12bff7 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -51,22 +51,22 @@ func (i *Image) Id() string { // nolint
}
type ImageSummary struct {
- ID string `json:"Id"`
- ParentId string // nolint
- RepoTags []string `json:",omitempty"`
+ ID string `json:"Id"`
+ ParentId string // nolint
+ RepoTags []string
+ RepoDigests []string
Created int64
- Size int64 `json:",omitempty"`
- SharedSize int `json:",omitempty"`
- VirtualSize int64 `json:",omitempty"`
- Labels map[string]string `json:",omitempty"`
- Containers int `json:",omitempty"`
- ReadOnly bool `json:",omitempty"`
- Dangling bool `json:",omitempty"`
+ Size int64
+ SharedSize int
+ VirtualSize int64
+ Labels map[string]string
+ Containers int
+ ReadOnly bool `json:",omitempty"`
+ Dangling bool `json:",omitempty"`
// Podman extensions
Names []string `json:",omitempty"`
Digest string `json:",omitempty"`
- Digests []string `json:",omitempty"`
ConfigDigest string `json:",omitempty"`
History []string `json:",omitempty"`
}
diff --git a/pkg/domain/infra/abi/images_list.go b/pkg/domain/infra/abi/images_list.go
index 281b04294..20ae0d0f6 100644
--- a/pkg/domain/infra/abi/images_list.go
+++ b/pkg/domain/infra/abi/images_list.go
@@ -35,7 +35,7 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions)
Created: img.Created().Unix(),
Dangling: img.Dangling(),
Digest: string(img.Digest()),
- Digests: digests,
+ RepoDigests: digests,
History: img.NamesHistory(),
Names: img.Names(),
ParentId: img.Parent,
diff --git a/pkg/util/utils.go b/pkg/util/utils.go
index f6a084c00..e0f631eb4 100644
--- a/pkg/util/utils.go
+++ b/pkg/util/utils.go
@@ -530,6 +530,11 @@ func ParseInputTime(inputTime string) (time.Time, error) {
}
}
+ unix_timestamp, err := strconv.ParseInt(inputTime, 10, 64)
+ if err == nil {
+ return time.Unix(unix_timestamp, 0), nil
+ }
+
// input might be a duration
duration, err := time.ParseDuration(inputTime)
if err != nil {
diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at
index b35c27215..5c35edf2b 100644
--- a/test/apiv2/20-containers.at
+++ b/test/apiv2/20-containers.at
@@ -169,7 +169,7 @@ t GET containers/$cid/json 200 \
.Args[1]="param2"
t DELETE containers/$cid 204
-# test only set the entrpoint, Cmd should be []
+# test only set the entrypoint, Cmd should be []
t POST containers/create '"Image":"'$IMAGE'","Entrypoint":["echo","param1"]' 201 \
.Id~[0-9a-f]\\{64\\}
cid=$(jq -r '.Id' <<<"$output")
diff --git a/test/apiv2/rest_api/test_rest_v2_0_0.py b/test/apiv2/rest_api/test_rest_v2_0_0.py
index 52348d4f4..dc92a2966 100644
--- a/test/apiv2/rest_api/test_rest_v2_0_0.py
+++ b/test/apiv2/rest_api/test_rest_v2_0_0.py
@@ -179,15 +179,19 @@ class TestApi(unittest.TestCase):
"NetworkDisabled": False,
# FIXME adding these 2 lines cause: (This is sampled from docker-py)
# "network already exists","message":"container
- # 01306e499df5441560d70071a54342611e422a94de20865add50a9565fd79fb9 is already connected to CNI network \"TestDefaultNetwork\": network already exists"
+ # 01306e499df5441560d70071a54342611e422a94de20865add50a9565fd79fb9 is already connected to CNI
+ # network \"TestDefaultNetwork\": network already exists"
# "HostConfig": {"NetworkMode": "TestDefaultNetwork"},
# "NetworkingConfig": {"EndpointsConfig": {"TestDefaultNetwork": None}},
# FIXME These two lines cause:
- # CNI network \"TestNetwork\" not found","message":"error configuring network namespace for container 369ddfa7d3211ebf1fbd5ddbff91bd33fa948858cea2985c133d6b6507546dff: CNI network \"TestNetwork\" not found"
+ # CNI network \"TestNetwork\" not found","message":"error configuring network namespace for container
+ # 369ddfa7d3211ebf1fbd5ddbff91bd33fa948858cea2985c133d6b6507546dff: CNI network \"TestNetwork\" not
+ # found"
# "HostConfig": {"NetworkMode": "TestNetwork"},
# "NetworkingConfig": {"EndpointsConfig": {"TestNetwork": None}},
# FIXME no networking defined cause: (note this error is from the container inspect below)
- # "internal libpod error","message":"network inspection mismatch: asked to join 2 CNI network(s) [TestDefaultNetwork podman], but have information on 1 network(s): internal libpod error"
+ # "internal libpod error","message":"network inspection mismatch: asked to join 2 CNI network(s) [
+ # TestDefaultNetwork podman], but have information on 1 network(s): internal libpod error"
},
)
self.assertEqual(create.status_code, 201, create.text)
@@ -255,23 +259,68 @@ class TestApi(unittest.TestCase):
def test_commit(self):
r = requests.post(_url(ctnr("/commit?container={}")))
self.assertEqual(r.status_code, 200, r.text)
- validateObjectFields(r.text)
- def test_images(self):
- r = requests.get(_url("/images/json"))
+ obj = json.loads(r.content)
+ self.assertIsInstance(obj, dict)
+ self.assertIn("Id", obj)
+
+ def test_images_compat(self):
+ r = requests.get(PODMAN_URL + "/v1.40/images/json")
self.assertEqual(r.status_code, 200, r.text)
- validateObjectFields(r.content)
- def test_inspect_image(self):
- r = requests.get(_url("/images/alpine/json"))
+ # See https://docs.docker.com/engine/api/v1.40/#operation/ImageList
+ required_keys = (
+ "Id",
+ "ParentId",
+ "RepoTags",
+ "RepoDigests",
+ "Created",
+ "Size",
+ "SharedSize",
+ "VirtualSize",
+ "Labels",
+ "Containers",
+ )
+ objs = json.loads(r.content)
+ self.assertIn(type(objs), (list,))
+ for o in objs:
+ self.assertIsInstance(o, dict)
+ for k in required_keys:
+ self.assertIn(k, o)
+
+ def test_inspect_image_compat(self):
+ r = requests.get(PODMAN_URL + "/v1.40/images/alpine/json")
self.assertEqual(r.status_code, 200, r.text)
- obj = validateObjectFields(r.content)
+
+ # See https://docs.docker.com/engine/api/v1.40/#operation/ImageInspect
+ required_keys = (
+ "Id",
+ "Parent",
+ "Comment",
+ "Created",
+ "Container",
+ "DockerVersion",
+ "Author",
+ "Architecture",
+ "Os",
+ "Size",
+ "VirtualSize",
+ "GraphDriver",
+ "RootFS",
+ "Metadata",
+ )
+
+ obj = json.loads(r.content)
+ self.assertIn(type(obj), (dict,))
+ for k in required_keys:
+ self.assertIn(k, obj)
_ = parse(obj["Created"])
- def test_delete_image(self):
- r = requests.delete(_url("/images/alpine?force=true"))
+ def test_delete_image_compat(self):
+ r = requests.delete(PODMAN_URL + "/v1.40/images/alpine?force=true")
self.assertEqual(r.status_code, 200, r.text)
- json.loads(r.text)
+ obj = json.loads(r.content)
+ self.assertIn(type(obj), (list,))
def test_pull(self):
r = requests.post(_url("/images/pull?reference=alpine"), timeout=15)
@@ -295,12 +344,13 @@ class TestApi(unittest.TestCase):
self.assertTrue(keys["images"], "Expected to find images stanza")
self.assertTrue(keys["stream"], "Expected to find stream progress stanza's")
- def test_search(self):
+ def test_search_compat(self):
# Had issues with this test hanging when repositories not happy
def do_search():
- r = requests.get(_url("/images/search?term=alpine"), timeout=5)
+ r = requests.get(PODMAN_URL + "/v1.40/images/search?term=alpine", timeout=5)
self.assertEqual(r.status_code, 200, r.text)
- json.loads(r.text)
+ objs = json.loads(r.text)
+ self.assertIn(type(objs), (list,))
search = Process(target=do_search)
search.start()
@@ -320,6 +370,20 @@ class TestApi(unittest.TestCase):
r = requests.get(_url("/_ping"))
self.assertEqual(r.status_code, 200, r.text)
+ def test_history_compat(self):
+ r = requests.get(PODMAN_URL + "/v1.40/images/alpine/history")
+ self.assertEqual(r.status_code, 200, r.text)
+
+ # See https://docs.docker.com/engine/api/v1.40/#operation/ImageHistory
+ required_keys = ("Id", "Created", "CreatedBy", "Tags", "Size", "Comment")
+
+ objs = json.loads(r.content)
+ self.assertIn(type(objs), (list,))
+ for o in objs:
+ self.assertIsInstance(o, dict)
+ for k in required_keys:
+ self.assertIn(k, o)
+
if __name__ == "__main__":
unittest.main()
diff --git a/test/system/030-run.bats b/test/system/030-run.bats
index 37695f205..6db6b76f1 100644
--- a/test/system/030-run.bats
+++ b/test/system/030-run.bats
@@ -536,6 +536,30 @@ json-file | f
run_podman untag $IMAGE $newtag $newtag2
}
+@test "Verify /run/.containerenv exist" {
+ run_podman run --rm $IMAGE ls -1 /run/.containerenv
+ is "$output" "/run/.containerenv"
+
+ run_podman run --privileged --rm $IMAGE sh -c '. /run/.containerenv; echo $engine'
+ is "$output" ".*podman.*" "failed to identify engine"
+
+ run_podman run --privileged --name "testcontainerenv" --rm $IMAGE sh -c '. /run/.containerenv; echo $name'
+ is "$output" ".*testcontainerenv.*"
+
+ run_podman run --privileged --rm $IMAGE sh -c '. /run/.containerenv; echo $image'
+ is "$output" ".*$IMAGE.*" "failed to idenitfy image"
+
+ run_podman run --privileged --rm $IMAGE sh -c '. /run/.containerenv; echo $rootless'
+ # FIXME: on some CI systems, 'run --privileged' emits a spurious
+ # warning line about dup devices. Ignore it.
+ remove_same_dev_warning
+ if is_rootless; then
+ is "$output" "1"
+ else
+ is "$output" "0"
+ fi
+}
+
@test "podman run with --net=host and --port prints warning" {
rand=$(random_string 10)
diff --git a/test/system/035-logs.bats b/test/system/035-logs.bats
index a3d6a5800..a081a7ce1 100644
--- a/test/system/035-logs.bats
+++ b/test/system/035-logs.bats
@@ -21,6 +21,9 @@ load helpers
run_podman logs $cid
is "$output" "$rand_string" "output from podman-logs after container is run"
+ # test --since with Unix timestamps
+ run_podman logs --since 1000 $cid
+
run_podman rm $cid
}
diff --git a/utils/utils_supported.go b/utils/utils_supported.go
index e6978ca6f..6f517dc72 100644
--- a/utils/utils_supported.go
+++ b/utils/utils_supported.go
@@ -184,7 +184,7 @@ func moveUnderCgroup(cgroup, subtree string, processes []uint32) error {
}
for _, pid := range bytes.Split(processesData, []byte("\n")) {
if _, err := f.Write(pid); err != nil {
- logrus.Warnf("Cannot move process %d to cgroup %q", pid, newCgroup)
+ logrus.Warnf("Cannot move process %s to cgroup %q", string(pid), newCgroup)
}
}
}