summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml39
-rwxr-xr-xAPI.md36
-rw-r--r--cmd/podman/formats/formats.go13
-rw-r--r--cmd/podman/inspect.go45
-rw-r--r--cmd/podman/shared/container.go16
-rw-r--r--cmd/podman/varlink/io.podman.varlink16
-rwxr-xr-xhack/get_ci_vm.sh12
-rw-r--r--libpod/adapter/client.go22
-rw-r--r--libpod/adapter/containers_remote.go50
-rw-r--r--libpod/adapter/images_remote.go19
-rw-r--r--libpod/adapter/runtime_remote.go98
-rw-r--r--libpod/boltdb_state.go12
-rw-r--r--libpod/boltdb_state_linux.go2
-rw-r--r--libpod/boltdb_state_unsupported.go2
-rw-r--r--libpod/common_test.go8
-rw-r--r--libpod/container.go28
-rw-r--r--libpod/container_internal.go2
-rw-r--r--libpod/container_internal_test.go2
-rw-r--r--libpod/networking_linux.go107
-rw-r--r--libpod/oci.go2
-rw-r--r--libpod/runtime_ctr.go2
-rw-r--r--pkg/spec/createconfig.go4
-rw-r--r--pkg/spec/spec.go4
-rw-r--r--pkg/varlinkapi/containers.go86
-rw-r--r--test/e2e/common_test.go19
-rw-r--r--test/e2e/inspect_test.go6
-rw-r--r--test/e2e/libpod_suite_remoteclient_test.go8
-rw-r--r--test/e2e/libpod_suite_test.go9
-rw-r--r--test/e2e/rootless_test.go4
-rw-r--r--vendor.conf4
-rw-r--r--vendor/github.com/containers/storage/containers_ffjson.go2
-rw-r--r--vendor/github.com/containers/storage/drivers/aufs/aufs.go5
-rw-r--r--vendor/github.com/containers/storage/drivers/btrfs/btrfs.go5
-rw-r--r--vendor/github.com/containers/storage/drivers/devmapper/driver.go5
-rw-r--r--vendor/github.com/containers/storage/drivers/driver.go3
-rw-r--r--vendor/github.com/containers/storage/drivers/overlay/check.go66
-rw-r--r--vendor/github.com/containers/storage/drivers/overlay/overlay.go44
-rw-r--r--vendor/github.com/containers/storage/drivers/template.go45
-rw-r--r--vendor/github.com/containers/storage/drivers/vfs/driver.go8
-rw-r--r--vendor/github.com/containers/storage/drivers/windows/windows.go5
-rw-r--r--vendor/github.com/containers/storage/drivers/zfs/zfs.go5
-rw-r--r--vendor/github.com/containers/storage/images.go4
-rw-r--r--vendor/github.com/containers/storage/images_ffjson.go2
-rw-r--r--vendor/github.com/containers/storage/layers.go44
-rw-r--r--vendor/github.com/containers/storage/layers_ffjson.go2
-rw-r--r--vendor/github.com/containers/storage/pkg/archive/example_changes.go97
-rw-r--r--vendor/github.com/containers/storage/store.go60
-rw-r--r--vendor/github.com/varlink/go/.gitignore1
-rw-r--r--vendor/github.com/varlink/go/.travis.yml6
-rw-r--r--vendor/github.com/varlink/go/cmd/varlink/main.go295
-rw-r--r--vendor/github.com/varlink/go/varlink/bridge.go59
-rw-r--r--vendor/github.com/varlink/go/varlink/bridge_windows.go57
-rw-r--r--vendor/github.com/varlink/go/varlink/connection.go7
-rw-r--r--vendor/github.com/varlink/go/varlink/service.go67
-rw-r--r--vendor/github.com/varlink/go/varlink/socketactivation.go63
-rw-r--r--vendor/github.com/varlink/go/varlink/socketactivation_windows.go7
56 files changed, 1369 insertions, 272 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 199c2a533..3192d15ae 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -25,6 +25,16 @@ env:
CIRRUS_CLONE_DEPTH: 200
####
+ #### Cache-image names to test with
+ ###
+ FEDORA_CACHE_IMAGE_NAME: "fedora-29-libpod-9afa57a9"
+ PRIOR_FEDORA_CACHE_IMAGE_NAME: "fedora-28-libpod-9afa57a9"
+ UBUNTU_CACHE_IMAGE_NAME: "ubuntu-18-libpod-9afa57a9"
+ RHEL_CACHE_IMAGE_NAME: "rhel-8-notready"
+ PRIOR_RHEL_CACHE_IMAGE_NAME: "rhel-7-notready"
+ CENTOS_CACHE_IMAGE_NAME: "centos-7-notready"
+
+ ####
#### Variables for composing new cache-images (used in PR testing) from
#### base-images (pre-existing in GCE)
####
@@ -38,6 +48,8 @@ env:
PACKER_BUILDS: "ubuntu-18,fedora-29,fedora-28" # TODO: fah-29,rhel-7,centos-7
# Version of packer to use
PACKER_VER: "1.3.1"
+ # Special image w/ nested-libvirt + tools for creating new cache and base images
+ IMAGE_BUILDER_CACHE_IMAGE_NAME: "image-builder-image-1541772081"
# Google-maintained base-image names
UBUNTU_BASE_IMAGE: "ubuntu-1804-bionic-v20181203a"
CENTOS_BASE_IMAGE: "centos-7-v20181113"
@@ -113,7 +125,7 @@ build_each_commit_task:
memory: "4Gb"
disk: 40
matrix:
- image_name: "fedora-29-libpod-9afa57a9"
+ image_name: "${FEDORA_CACHE_IMAGE_NAME}"
timeout_in: 30m
@@ -139,14 +151,14 @@ testing_task:
# 'matrix' combinations.
matrix:
# Images are generated separately, from build_images_task (below)
- image_name: fedora-29-libpod-9afa57a9
- image_name: fedora-28-libpod-9afa57a9
- image_name: ubuntu-18-libpod-9afa57a9
+ image_name: "${FEDORA_CACHE_IMAGE_NAME}"
+ image_name: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}"
+ image_name: "${UBUNTU_CACHE_IMAGE_NAME}"
# TODO: tests fail
- # image_name: "rhel-7-something-something"
- # image_name: "centos-7-something-something"
- # image_name: "fah-29-libpod-5070733157859328"
+ # image_name: "${RHEL_CACHE_IMAGE_NAME}
+ # image_name: "${PRIOR_RHEL_CACHE_IMAGE_NAME}
+ # image_name: "${CENTOS_CACHE_IMAGE_NAME}"
timeout_in: 120m
@@ -176,12 +188,13 @@ optional_testing_task:
gce_instance:
image_project: "libpod-218412"
matrix:
- image_name: fedora-29-libpod-9afa57a9
- image_name: fedora-28-libpod-9afa57a9
- image_name: ubuntu-18-libpod-9afa57a9
+ image_name: "${FEDORA_CACHE_IMAGE_NAME}"
+ image_name: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}"
+ image_name: "${UBUNTU_CACHE_IMAGE_NAME}"
# TODO: Make these work (also build_images_task below)
- #image_name: "rhel-server-ec2-7-5-165-1-libpod-fce09afe"
- #image_name: "centos-7-v20180911-libpod-fce09afe"
+ # image_name: "${RHEL_CACHE_IMAGE_NAME}
+ # image_name: "${PRIOR_RHEL_CACHE_IMAGE_NAME}
+ # image_name: "${CENTOS_CACHE_IMAGE_NAME}"
timeout_in: 60m
@@ -214,7 +227,7 @@ cache_images_task:
cpu: 4
memory: "4Gb"
disk: 200
- image_name: "image-builder-image-1541772081" # Simply CentOS 7 + packer dependencies
+ image_name: "${IMAGE_BUILDER_CACHE_IMAGE_NAME}"
# Additional permissions for building GCE images, within a GCE VM
scopes:
- compute
diff --git a/API.md b/API.md
index 0cbdffea4..872d3ee07 100755
--- a/API.md
+++ b/API.md
@@ -9,14 +9,22 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func Commit(name: string, image_name: string, changes: []string, author: string, message: string, pause: bool, manifestType: string) string](#Commit)
+[func ContainerArtifacts(name: string, artifactName: string) string](#ContainerArtifacts)
+
[func ContainerCheckpoint(name: string, keep: bool, leaveRunning: bool, tcpEstablished: bool) string](#ContainerCheckpoint)
+[func ContainerConfig(name: string) string](#ContainerConfig)
+
[func ContainerExists(name: string) int](#ContainerExists)
+[func ContainerInspectData(name: string) string](#ContainerInspectData)
+
[func ContainerRestore(name: string, keep: bool, tcpEstablished: bool) string](#ContainerRestore)
[func ContainerRunlabel(runlabel: Runlabel) ](#ContainerRunlabel)
+[func ContainerStateData(name: string) string](#ContainerStateData)
+
[func CreateContainer(create: Create) string](#CreateContainer)
[func CreateImage() NotImplemented](#CreateImage)
@@ -239,12 +247,24 @@ attributes: _CMD, ENTRYPOINT, ENV, EXPOSE, LABEL, ONBUILD, STOPSIGNAL, USER, VOL
container while it is being committed, pass a _true_ bool for the pause argument. If the container cannot
be found by the ID or name provided, a (ContainerNotFound)[#ContainerNotFound] error will be returned; otherwise,
the resulting image's ID will be returned as a string.
+### <a name="ContainerArtifacts"></a>func ContainerArtifacts
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method ContainerArtifacts(name: [string](https://godoc.org/builtin#string), artifactName: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div>
+ContainerArtifacts returns a container's artifacts in string form. This call is for
+development of Podman only and generally should not be used.
### <a name="ContainerCheckpoint"></a>func ContainerCheckpoint
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
method ContainerCheckpoint(name: [string](https://godoc.org/builtin#string), keep: [bool](https://godoc.org/builtin#bool), leaveRunning: [bool](https://godoc.org/builtin#bool), tcpEstablished: [bool](https://godoc.org/builtin#bool)) [string](https://godoc.org/builtin#string)</div>
ContainerCheckPoint performs a checkpopint on a container by its name or full/partial container
ID. On successful checkpoint, the id of the checkpointed container is returned.
+### <a name="ContainerConfig"></a>func ContainerConfig
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method ContainerConfig(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div>
+ContainerConfig returns a container's config in string form. This call is for
+development of Podman only and generally should not be used.
### <a name="ContainerExists"></a>func ContainerExists
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -258,6 +278,12 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.ContainerExists '{"name":
"exists": 0
}
~~~
+### <a name="ContainerInspectData"></a>func ContainerInspectData
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method ContainerInspectData(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div>
+ContainerInspectData returns a container's inspect data in string form. This call is for
+development of Podman only and generally should not be used.
### <a name="ContainerRestore"></a>func ContainerRestore
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -270,6 +296,12 @@ of the container's ID.
method ContainerRunlabel(runlabel: [Runlabel](#Runlabel)) </div>
ContainerRunlabel runs executes a command as described by a given container image label.
+### <a name="ContainerStateData"></a>func ContainerStateData
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method ContainerStateData(name: [string](https://godoc.org/builtin#string)) [string](https://godoc.org/builtin#string)</div>
+ContainerStateData returns a container's state config in string form. This call is for
+development of Podman only and generally should not be used.
### <a name="CreateContainer"></a>func CreateContainer
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -1373,6 +1405,8 @@ virtualSize [int](https://godoc.org/builtin#int)
containers [int](https://godoc.org/builtin#int)
labels [map[string]](#map[string])
+
+isParent [bool](https://godoc.org/builtin#bool)
### <a name="ImageSearch"></a>type ImageSearch
ImageSearch is the returned structure for SearchImage. It is returned
@@ -1460,7 +1494,7 @@ graph_status [InfoGraphStatus](#InfoGraphStatus)
run_root [string](https://godoc.org/builtin#string)
### <a name="ListContainerData"></a>type ListContainerData
-ListContainer is the returned struct for an individual container
+ListContainerData is the returned struct for an individual container
id [string](https://godoc.org/builtin#string)
diff --git a/cmd/podman/formats/formats.go b/cmd/podman/formats/formats.go
index 3da0ea385..c454c39bd 100644
--- a/cmd/podman/formats/formats.go
+++ b/cmd/podman/formats/formats.go
@@ -20,6 +20,8 @@ const (
JSONString = "json"
// IDString const to save on duplicates for Go templates
IDString = "{{.ID}}"
+
+ parsingErrorStr = "Template parsing error"
)
// Writer interface for outputs
@@ -96,7 +98,7 @@ func (t StdoutTemplateArray) Out() error {
t.Template = strings.Replace(strings.TrimSpace(t.Template[5:]), " ", "\t", -1)
headerTmpl, err := template.New("header").Funcs(headerFunctions).Parse(t.Template)
if err != nil {
- return errors.Wrapf(err, "Template parsing error")
+ return errors.Wrapf(err, parsingErrorStr)
}
err = headerTmpl.Execute(w, t.Fields)
if err != nil {
@@ -107,13 +109,12 @@ func (t StdoutTemplateArray) Out() error {
t.Template = strings.Replace(t.Template, " ", "\t", -1)
tmpl, err := template.New("image").Funcs(basicFunctions).Parse(t.Template)
if err != nil {
- return errors.Wrapf(err, "Template parsing error")
+ return errors.Wrapf(err, parsingErrorStr)
}
- for i, img := range t.Output {
+ for i, raw := range t.Output {
basicTmpl := tmpl.Funcs(basicFunctions)
- err = basicTmpl.Execute(w, img)
- if err != nil {
- return err
+ if err := basicTmpl.Execute(w, raw); err != nil {
+ return errors.Wrapf(err, parsingErrorStr)
}
if i != len(t.Output)-1 {
fmt.Fprintln(w, "")
diff --git a/cmd/podman/inspect.go b/cmd/podman/inspect.go
index 6ffcde55f..3ef740463 100644
--- a/cmd/podman/inspect.go
+++ b/cmd/podman/inspect.go
@@ -2,12 +2,13 @@ package main
import (
"context"
+ "encoding/json"
"strings"
"github.com/containers/libpod/cmd/podman/formats"
- "github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/cmd/podman/shared"
- "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/libpod/adapter"
+ cc "github.com/containers/libpod/pkg/spec"
"github.com/containers/libpod/pkg/util"
"github.com/pkg/errors"
"github.com/urfave/cli"
@@ -63,7 +64,7 @@ func inspectCmd(c *cli.Context) error {
return err
}
- runtime, err := libpodruntime.GetRuntime(c)
+ runtime, err := adapter.GetRuntime(c)
if err != nil {
return errors.Wrapf(err, "error creating libpod runtime")
}
@@ -87,6 +88,9 @@ func inspectCmd(c *cli.Context) error {
}
inspectedObjects, iterateErr := iterateInput(getContext(), c, args, runtime, inspectType)
+ if iterateErr != nil {
+ return iterateErr
+ }
var out formats.Writer
if outputFormat != "" && outputFormat != formats.JSONString {
@@ -97,12 +101,11 @@ func inspectCmd(c *cli.Context) error {
out = formats.JSONStructArray{Output: inspectedObjects}
}
- formats.Writer(out).Out()
- return iterateErr
+ return formats.Writer(out).Out()
}
// func iterateInput iterates the images|containers the user has requested and returns the inspect data and error
-func iterateInput(ctx context.Context, c *cli.Context, args []string, runtime *libpod.Runtime, inspectType string) ([]interface{}, error) {
+func iterateInput(ctx context.Context, c *cli.Context, args []string, runtime *adapter.LocalRuntime, inspectType string) ([]interface{}, error) {
var (
data interface{}
inspectedItems []interface{}
@@ -122,13 +125,18 @@ func iterateInput(ctx context.Context, c *cli.Context, args []string, runtime *l
inspectError = errors.Wrapf(err, "error getting libpod container inspect data %s", ctr.ID())
break
}
- data, err = shared.GetCtrInspectInfo(ctr, libpodInspectData)
+ artifact, err := getArtifact(ctr)
+ if inspectError != nil {
+ inspectError = err
+ break
+ }
+ data, err = shared.GetCtrInspectInfo(ctr.Config(), libpodInspectData, artifact)
if err != nil {
inspectError = errors.Wrapf(err, "error parsing container data %q", ctr.ID())
break
}
case inspectTypeImage:
- image, err := runtime.ImageRuntime().NewFromLocal(input)
+ image, err := runtime.NewImageFromLocal(input)
if err != nil {
inspectError = errors.Wrapf(err, "error getting image %q", input)
break
@@ -141,7 +149,7 @@ func iterateInput(ctx context.Context, c *cli.Context, args []string, runtime *l
case inspectAll:
ctr, err := runtime.LookupContainer(input)
if err != nil {
- image, err := runtime.ImageRuntime().NewFromLocal(input)
+ image, err := runtime.NewImageFromLocal(input)
if err != nil {
inspectError = errors.Wrapf(err, "error getting image %q", input)
break
@@ -157,7 +165,12 @@ func iterateInput(ctx context.Context, c *cli.Context, args []string, runtime *l
inspectError = errors.Wrapf(err, "error getting libpod container inspect data %s", ctr.ID())
break
}
- data, err = shared.GetCtrInspectInfo(ctr, libpodInspectData)
+ artifact, inspectError := getArtifact(ctr)
+ if inspectError != nil {
+ inspectError = err
+ break
+ }
+ data, err = shared.GetCtrInspectInfo(ctr.Config(), libpodInspectData, artifact)
if err != nil {
inspectError = errors.Wrapf(err, "error parsing container data %s", ctr.ID())
break
@@ -170,3 +183,15 @@ func iterateInput(ctx context.Context, c *cli.Context, args []string, runtime *l
}
return inspectedItems, inspectError
}
+
+func getArtifact(ctr *adapter.Container) (*cc.CreateConfig, error) {
+ var createArtifact cc.CreateConfig
+ artifact, err := ctr.GetArtifact("create-config")
+ if err != nil {
+ return nil, err
+ }
+ if err := json.Unmarshal(artifact, &createArtifact); err != nil {
+ return nil, err
+ }
+ return &createArtifact, nil
+}
diff --git a/cmd/podman/shared/container.go b/cmd/podman/shared/container.go
index a904ef75a..9040c4a5c 100644
--- a/cmd/podman/shared/container.go
+++ b/cmd/podman/shared/container.go
@@ -2,7 +2,6 @@ package shared
import (
"context"
- "encoding/json"
"fmt"
"github.com/google/shlex"
"io"
@@ -446,8 +445,7 @@ func getStrFromSquareBrackets(cmd string) string {
// GetCtrInspectInfo takes container inspect data and collects all its info into a ContainerData
// structure for inspection related methods
-func GetCtrInspectInfo(ctr *libpod.Container, ctrInspectData *inspect.ContainerInspectData) (*inspect.ContainerData, error) {
- config := ctr.Config()
+func GetCtrInspectInfo(config *libpod.ContainerConfig, ctrInspectData *inspect.ContainerInspectData, createArtifact *cc.CreateConfig) (*inspect.ContainerData, error) {
spec := config.Spec
cpus, mems, period, quota, realtimePeriod, realtimeRuntime, shares := getCPUInfo(spec)
@@ -456,16 +454,6 @@ func GetCtrInspectInfo(ctr *libpod.Container, ctrInspectData *inspect.ContainerI
pidsLimit := getPidsInfo(spec)
cgroup := getCgroup(spec)
- var createArtifact cc.CreateConfig
- artifact, err := ctr.GetArtifact("create-config")
- if err == nil {
- if err := json.Unmarshal(artifact, &createArtifact); err != nil {
- return nil, err
- }
- } else {
- logrus.Errorf("couldn't get some inspect information, error getting artifact %q: %v", ctr.ID(), err)
- }
-
data := &inspect.ContainerData{
ctrInspectData,
&inspect.HostConfig{
@@ -493,7 +481,7 @@ func GetCtrInspectInfo(ctr *libpod.Container, ctrInspectData *inspect.ContainerI
PidsLimit: pidsLimit,
Privileged: config.Privileged,
ReadonlyRootfs: spec.Root.Readonly,
- Runtime: ctr.RuntimeName(),
+ Runtime: config.OCIRuntime,
NetworkMode: string(createArtifact.NetMode),
IpcMode: string(createArtifact.IpcMode),
Cgroup: cgroup,
diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink
index c6f1d3f1b..79300f9bc 100644
--- a/cmd/podman/varlink/io.podman.varlink
+++ b/cmd/podman/varlink/io.podman.varlink
@@ -1035,6 +1035,22 @@ method GenerateKubeService() -> (notimplemented: NotImplemented)
# like that created by GenerateKube. See also [GenerateKube](GenerateKube).
method ReplayKube() -> (notimplemented: NotImplemented)
+# ContainerConfig returns a container's config in string form. This call is for
+# development of Podman only and generally should not be used.
+method ContainerConfig(name: string) -> (config: string)
+
+# ContainerArtifacts returns a container's artifacts in string form. This call is for
+# development of Podman only and generally should not be used.
+method ContainerArtifacts(name: string, artifactName: string) -> (config: string)
+
+# ContainerInspectData returns a container's inspect data in string form. This call is for
+# development of Podman only and generally should not be used.
+method ContainerInspectData(name: string) -> (config: string)
+
+# ContainerStateData returns a container's state config in string form. This call is for
+# development of Podman only and generally should not be used.
+method ContainerStateData(name: string) -> (config: string)
+
# ImageNotFound means the image could not be found by the provided name or ID in local storage.
error ImageNotFound (name: string)
diff --git a/hack/get_ci_vm.sh b/hack/get_ci_vm.sh
index e1d8e42b9..b058b4273 100755
--- a/hack/get_ci_vm.sh
+++ b/hack/get_ci_vm.sh
@@ -54,15 +54,19 @@ delvm() {
showrun $CLEANUP_CMD # prompts for Yes/No
}
-show_usage(){
+image_hints() {
+ egrep '[[:space:]]+[[:alnum:]].+_CACHE_IMAGE_NAME:[[:space:]+"[[:print:]]+"' \
+ "$LIBPODROOT/.cirrus.yml" | cut -d: -f 2 | tr -d '"[:blank:]' | \
+ grep -v 'notready' | grep -v 'image-builder' | sort -u
+}
+
+show_usage() {
echo -e "\n${RED}ERROR: $1${NOR}"
echo -e "${YEL}Usage: $(basename $0) [-s | -p] <image_name>${NOR}\n"
if [[ -r ".cirrus.yml" ]]
then
- egrep 'image_name' ".cirrus.yml" | grep -v '#' | cut -d: -f 2 | \
- tr -d [:blank:] | sort -u > "$TEMPFILE"
echo -e "${YEL}Some possible image_name values (from .cirrus.yml):${NOR}"
- cat $TEMPFILE
+ image_hints
echo ""
fi
exit 1
diff --git a/libpod/adapter/client.go b/libpod/adapter/client.go
index 383c242c9..b3bb9acae 100644
--- a/libpod/adapter/client.go
+++ b/libpod/adapter/client.go
@@ -3,12 +3,32 @@
package adapter
import (
+ "os"
+
+ "github.com/sirupsen/logrus"
"github.com/varlink/go/varlink"
)
+// DefaultAddress is the default address of the varlink socket
+const DefaultAddress = "unix:/run/podman/io.podman"
+
// Connect provides a varlink connection
func (r RemoteRuntime) Connect() (*varlink.Connection, error) {
- connection, err := varlink.NewConnection("unix:/run/podman/io.podman")
+ var err error
+ var connection *varlink.Connection
+ if bridge := os.Getenv("PODMAN_VARLINK_BRIDGE"); bridge != "" {
+ logrus.Infof("Connecting with varlink bridge")
+ logrus.Debugf("%s", bridge)
+ connection, err = varlink.NewBridge(bridge)
+ } else {
+ address := os.Getenv("PODMAN_VARLINK_ADDRESS")
+ if address == "" {
+ address = DefaultAddress
+ }
+ logrus.Infof("Connecting with varlink address")
+ logrus.Debugf("%s", address)
+ connection, err = varlink.NewConnection(address)
+ }
if err != nil {
return nil, err
}
diff --git a/libpod/adapter/containers_remote.go b/libpod/adapter/containers_remote.go
new file mode 100644
index 000000000..9623304e5
--- /dev/null
+++ b/libpod/adapter/containers_remote.go
@@ -0,0 +1,50 @@
+// +build remoteclient
+
+package adapter
+
+import (
+ "encoding/json"
+
+ iopodman "github.com/containers/libpod/cmd/podman/varlink"
+ "github.com/containers/libpod/libpod"
+ "github.com/containers/libpod/pkg/inspect"
+)
+
+// Inspect returns an inspect struct from varlink
+func (c *Container) Inspect(size bool) (*inspect.ContainerInspectData, error) {
+ reply, err := iopodman.ContainerInspectData().Call(c.Runtime.Conn, c.ID())
+ if err != nil {
+ return nil, err
+ }
+ data := inspect.ContainerInspectData{}
+ if err := json.Unmarshal([]byte(reply), &data); err != nil {
+ return nil, err
+ }
+ return &data, err
+}
+
+// ID returns the ID of the container
+func (c *Container) ID() string {
+ return c.config.ID
+}
+
+// GetArtifact returns a container's artifacts
+func (c *Container) GetArtifact(name string) ([]byte, error) {
+ var data []byte
+ reply, err := iopodman.ContainerArtifacts().Call(c.Runtime.Conn, c.ID(), name)
+ if err != nil {
+ return nil, err
+ }
+ if err := json.Unmarshal([]byte(reply), &data); err != nil {
+ return nil, err
+ }
+ return data, err
+}
+
+// Config returns a container's Config ... same as ctr.Config()
+func (c *Container) Config() *libpod.ContainerConfig {
+ if c.config != nil {
+ return c.config
+ }
+ return c.Runtime.Config(c.ID())
+}
diff --git a/libpod/adapter/images_remote.go b/libpod/adapter/images_remote.go
index 77b0629a7..e7b38dccc 100644
--- a/libpod/adapter/images_remote.go
+++ b/libpod/adapter/images_remote.go
@@ -3,15 +3,22 @@
package adapter
import (
- "github.com/containers/libpod/libpod"
+ "context"
+ "encoding/json"
+
+ iopodman "github.com/containers/libpod/cmd/podman/varlink"
+ "github.com/containers/libpod/pkg/inspect"
)
-// Images returns information for the host system and its components
-func (r RemoteRuntime) Images() ([]libpod.InfoData, error) {
- conn, err := r.Connect()
+// Inspect returns returns an ImageData struct from over a varlink connection
+func (i *ContainerImage) Inspect(ctx context.Context) (*inspect.ImageData, error) {
+ reply, err := iopodman.InspectImage().Call(i.Runtime.Conn, i.ID())
if err != nil {
return nil, err
}
- _ = conn
- return nil, nil
+ data := inspect.ImageData{}
+ if err := json.Unmarshal([]byte(reply), &data); err != nil {
+ return nil, err
+ }
+ return &data, nil
}
diff --git a/libpod/adapter/runtime_remote.go b/libpod/adapter/runtime_remote.go
index 8ef8fe167..7189348bc 100644
--- a/libpod/adapter/runtime_remote.go
+++ b/libpod/adapter/runtime_remote.go
@@ -4,15 +4,18 @@ package adapter
import (
"context"
+ "encoding/json"
"fmt"
"io"
"strings"
"time"
"github.com/containers/image/types"
- iopodman "github.com/containers/libpod/cmd/podman/varlink"
+ "github.com/containers/libpod/cmd/podman/varlink"
+ "github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
- digest "github.com/opencontainers/go-digest"
+ "github.com/opencontainers/go-digest"
+ "github.com/sirupsen/logrus"
"github.com/urfave/cli"
"github.com/varlink/go/varlink"
)
@@ -80,21 +83,9 @@ type Container struct {
// remoteContainer ....
type remoteContainer struct {
- ID string
- Image string
- ImageID string
- Command []string
- Created time.Time
- RunningFor string
- Status string
- //Ports []ocicni.PortMapping
- RootFsSize int64
- RWSize int64
- Names string
- Labels []map[string]string
- // Mounts []string
- // ContainerRunning bool
- //Namespaces []LinuxNameSpace
+ Runtime *LocalRuntime
+ config *libpod.ContainerConfig
+ state *libpod.ContainerState
}
// GetImages returns a slice of containerimages over a varlink connection
@@ -272,39 +263,60 @@ func (ci *ContainerImage) History(ctx context.Context) ([]*image.History, error)
// LookupContainer gets basic information about container over a varlink
// connection and then translates it to a *Container
-func (r *RemoteRuntime) LookupContainer(idOrName string) (*Container, error) {
- container, err := iopodman.GetContainer().Call(r.Conn, idOrName)
+func (r *LocalRuntime) LookupContainer(idOrName string) (*Container, error) {
+ state, err := r.ContainerState(idOrName)
if err != nil {
return nil, err
}
- return listContainerDataToContainer(container)
+ config := r.Config(idOrName)
+ if err != nil {
+ return nil, err
+ }
+
+ rc := remoteContainer{
+ r,
+ config,
+ state,
+ }
+
+ c := Container{
+ rc,
+ }
+ return &c, nil
+}
+
+func (r *LocalRuntime) GetLatestContainer() (*Container, error) {
+ return nil, libpod.ErrNotImplemented
}
-// listContainerDataToContainer takes a varlink listcontainerData struct and makes
-// an "adapted" Container
-func listContainerDataToContainer(listData iopodman.ListContainerData) (*Container, error) {
- created, err := splitStringDate(listData.Createdat)
+// ContainerState returns the "state" of the container.
+func (r *LocalRuntime) ContainerState(name string) (*libpod.ContainerState, error) { //no-lint
+ reply, err := iopodman.ContainerStateData().Call(r.Conn, name)
if err != nil {
return nil, err
}
- rc := remoteContainer{
- // TODO commented out attributes will be populated when podman-remote ps
- // is implemented. They are not needed yet for basic container operations.
- ID: listData.Id,
- Image: listData.Image,
- ImageID: listData.Imageid,
- Command: listData.Command,
- Created: created,
- RunningFor: listData.Runningfor,
- Status: listData.Status,
- //ports:
- RootFsSize: listData.Rootfssize,
- RWSize: listData.Rwsize,
- Names: listData.Names,
- //Labels:
- //Mounts
- //ContainerRunning:
- //namespaces:
+ data := libpod.ContainerState{}
+ if err := json.Unmarshal([]byte(reply), &data); err != nil {
+ return nil, err
}
- return &Container{rc}, nil
+ return &data, err
+
+}
+
+// Config returns a container config
+func (r *LocalRuntime) Config(name string) *libpod.ContainerConfig {
+ // TODO the Spec being returned is not populated. Matt and I could not figure out why. Will defer
+ // further looking into it for after devconf.
+ // The libpod function for this has no errors so we are kind of in a tough
+ // spot here. Logging the errors for now.
+ reply, err := iopodman.ContainerConfig().Call(r.Conn, name)
+ if err != nil {
+ logrus.Error("call to container.config failed")
+ }
+ data := libpod.ContainerConfig{}
+ if err := json.Unmarshal([]byte(reply), &data); err != nil {
+ logrus.Error("failed to unmarshal container inspect data")
+ }
+ return &data
+
}
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go
index e7a07a9a8..5bc15dd7f 100644
--- a/libpod/boltdb_state.go
+++ b/libpod/boltdb_state.go
@@ -205,7 +205,7 @@ func (s *BoltState) Refresh() error {
return errors.Wrapf(ErrInternal, "container %s missing state in DB", string(id))
}
- state := new(containerState)
+ state := new(ContainerState)
if err := json.Unmarshal(stateBytes, state); err != nil {
return errors.Wrapf(err, "error unmarshalling state for container %s", string(id))
@@ -325,7 +325,7 @@ func (s *BoltState) Container(id string) (*Container, error) {
ctr := new(Container)
ctr.config = new(ContainerConfig)
- ctr.state = new(containerState)
+ ctr.state = new(ContainerState)
db, err := s.getDBCon()
if err != nil {
@@ -361,7 +361,7 @@ func (s *BoltState) LookupContainer(idOrName string) (*Container, error) {
ctr := new(Container)
ctr.config = new(ContainerConfig)
- ctr.state = new(containerState)
+ ctr.state = new(ContainerState)
db, err := s.getDBCon()
if err != nil {
@@ -542,7 +542,7 @@ func (s *BoltState) UpdateContainer(ctr *Container) error {
return errors.Wrapf(ErrNSMismatch, "container %s is in namespace %q, does not match our namespace %q", ctr.ID(), ctr.config.Namespace, s.namespace)
}
- newState := new(containerState)
+ newState := new(ContainerState)
netNSPath := ""
ctrID := []byte(ctr.ID())
@@ -754,7 +754,7 @@ func (s *BoltState) AllContainers() ([]*Container, error) {
ctr := new(Container)
ctr.config = new(ContainerConfig)
- ctr.state = new(containerState)
+ ctr.state = new(ContainerState)
if err := s.getContainerFromDB(id, ctr, ctrBucket); err != nil {
// If the error is a namespace mismatch, we can
@@ -1140,7 +1140,7 @@ func (s *BoltState) PodContainers(pod *Pod) ([]*Container, error) {
err = podCtrs.ForEach(func(id, val []byte) error {
newCtr := new(Container)
newCtr.config = new(ContainerConfig)
- newCtr.state = new(containerState)
+ newCtr.state = new(ContainerState)
ctrs = append(ctrs, newCtr)
return s.getContainerFromDB(id, newCtr, ctrBkt)
diff --git a/libpod/boltdb_state_linux.go b/libpod/boltdb_state_linux.go
index d91f311e5..09a9be606 100644
--- a/libpod/boltdb_state_linux.go
+++ b/libpod/boltdb_state_linux.go
@@ -8,7 +8,7 @@ import (
// replaceNetNS handle network namespace transitions after updating a
// container's state.
-func replaceNetNS(netNSPath string, ctr *Container, newState *containerState) error {
+func replaceNetNS(netNSPath string, ctr *Container, newState *ContainerState) error {
if netNSPath != "" {
// Check if the container's old state has a good netns
if ctr.state.NetNS != nil && netNSPath == ctr.state.NetNS.Path() {
diff --git a/libpod/boltdb_state_unsupported.go b/libpod/boltdb_state_unsupported.go
index 64610d304..244dc51a0 100644
--- a/libpod/boltdb_state_unsupported.go
+++ b/libpod/boltdb_state_unsupported.go
@@ -3,7 +3,7 @@
package libpod
// replaceNetNS is exclusive to the Linux platform and is a no-op elsewhere
-func replaceNetNS(netNSPath string, ctr *Container, newState *containerState) error {
+func replaceNetNS(netNSPath string, ctr *Container, newState *ContainerState) error {
return nil
}
diff --git a/libpod/common_test.go b/libpod/common_test.go
index 4af68a040..df730098e 100644
--- a/libpod/common_test.go
+++ b/libpod/common_test.go
@@ -48,7 +48,7 @@ func getTestContainer(id, name string, manager lock.Manager) (*Container, error)
},
},
},
- state: &containerState{
+ state: &ContainerState{
State: ContainerStateRunning,
ConfigPath: "/does/not/exist/specs/" + id,
RunDir: "/does/not/exist/tmp/",
@@ -166,10 +166,10 @@ func testContainersEqual(t *testing.T, a, b *Container, allowedEmpty bool) {
aConfig := new(ContainerConfig)
bConfig := new(ContainerConfig)
- aState := new(containerState)
- bState := new(containerState)
+ aState := new(ContainerState)
+ bState := new(ContainerState)
- blankState := new(containerState)
+ blankState := new(ContainerState)
assert.Equal(t, a.valid, b.valid)
diff --git a/libpod/container.go b/libpod/container.go
index 95f7a2972..b0589be3b 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -116,7 +116,7 @@ func (ns LinuxNS) String() string {
type Container struct {
config *ContainerConfig
- state *containerState
+ state *ContainerState
// Batched indicates that a container has been locked as part of a
// Batch() operation
@@ -136,10 +136,10 @@ type Container struct {
requestedIP net.IP
}
-// containerState contains the current state of the container
+// ContainerState contains the current state of the container
// It is stored on disk in a tmpfs and recreated on reboot
// easyjson:json
-type containerState struct {
+type ContainerState struct {
// The current state of the running container
State ContainerStatus `json:"state"`
// The path to the JSON OCI runtime spec for this container
@@ -415,14 +415,15 @@ func (c *Container) Spec() *spec.Spec {
// config does not exist (e.g., because the container was never started) return
// the spec from the config.
func (c *Container) specFromState() (*spec.Spec, error) {
- spec := c.config.Spec
+ returnSpec := c.config.Spec
if f, err := os.Open(c.state.ConfigPath); err == nil {
+ returnSpec = new(spec.Spec)
content, err := ioutil.ReadAll(f)
if err != nil {
return nil, errors.Wrapf(err, "error reading container config")
}
- if err := json.Unmarshal([]byte(content), &spec); err != nil {
+ if err := json.Unmarshal([]byte(content), &returnSpec); err != nil {
return nil, errors.Wrapf(err, "error unmarshalling container config")
}
} else {
@@ -432,7 +433,7 @@ func (c *Container) specFromState() (*spec.Spec, error) {
}
}
- return spec, nil
+ return returnSpec, nil
}
// ID returns the container's ID
@@ -1062,3 +1063,18 @@ func networkDisabled(c *Container) (bool, error) {
}
return false, nil
}
+
+// ContainerState returns containerstate struct
+func (c *Container) ContainerState() (*ContainerState, error) {
+ if !c.batched {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ if err := c.syncContainer(); err != nil {
+ return nil, err
+ }
+ }
+ returnConfig := new(ContainerState)
+ deepcopier.Copy(c.state).To(returnConfig)
+ return c.state, nil
+}
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index ce8791f08..39c1501da 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -388,7 +388,7 @@ func (c *Container) teardownStorage() error {
// Reset resets state fields to default values
// It is performed before a refresh and clears the state after a reboot
// It does not save the results - assumes the database will do that for us
-func resetState(state *containerState) error {
+func resetState(state *ContainerState) error {
state.PID = 0
state.Mountpoint = ""
state.Mounted = false
diff --git a/libpod/container_internal_test.go b/libpod/container_internal_test.go
index 124f1d20e..f1e2b70a7 100644
--- a/libpod/container_internal_test.go
+++ b/libpod/container_internal_test.go
@@ -37,7 +37,7 @@ func TestPostDeleteHooks(t *testing.T) {
},
StaticDir: dir, // not the bundle, but good enough for this test
},
- state: &containerState{
+ state: &ContainerState{
ExtensionStageHooks: map[string][]rspec.Hook{
"poststop": {
rspec.Hook{
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index a343bee6a..f9caf26d1 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -121,6 +121,19 @@ func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q []*cnitypes.Result,
return ctrNS, networkStatus, err
}
+type slirp4netnsCmdArg struct {
+ Proto string `json:"proto,omitempty"`
+ HostAddr string `json:"host_addr"`
+ HostPort int32 `json:"host_port"`
+ GuestAddr string `json:"guest_addr"`
+ GuestPort int32 `json:"guest_port"`
+}
+
+type slirp4netnsCmd struct {
+ Execute string `json:"execute"`
+ Args slirp4netnsCmdArg `json:"arguments"`
+}
+
// Configure the network namespace for a rootless container
func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) {
defer ctr.rootlessSlirpSyncR.Close()
@@ -139,7 +152,15 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) {
defer syncR.Close()
defer syncW.Close()
- cmd := exec.Command(path, "-c", "-e", "3", "-r", "4", fmt.Sprintf("%d", ctr.state.PID), "tap0")
+ havePortMapping := len(ctr.Config().PortMappings) > 0
+ apiSocket := filepath.Join(r.ociRuntime.tmpDir, fmt.Sprintf("%s.net", ctr.config.ID))
+ var cmd *exec.Cmd
+ if havePortMapping {
+ // if we need ports to be mapped from the host, create a API socket to use for communicating with slirp4netns.
+ cmd = exec.Command(path, "-c", "-e", "3", "-r", "4", "--api-socket", apiSocket, fmt.Sprintf("%d", ctr.state.PID), "tap0")
+ } else {
+ cmd = exec.Command(path, "-c", "-e", "3", "-r", "4", fmt.Sprintf("%d", ctr.state.PID), "tap0")
+ }
cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
@@ -162,19 +183,99 @@ func (r *Runtime) setupRootlessNetNS(ctr *Container) (err error) {
if os.IsTimeout(err) {
// Check if the process is still running.
var status syscall.WaitStatus
- _, err := syscall.Wait4(cmd.Process.Pid, &status, syscall.WNOHANG, nil)
+ pid, err := syscall.Wait4(cmd.Process.Pid, &status, syscall.WNOHANG, nil)
if err != nil {
return errors.Wrapf(err, "failed to read slirp4netns process status")
}
+ if pid != cmd.Process.Pid {
+ continue
+ }
if status.Exited() || status.Signaled() {
return errors.New("slirp4netns failed")
}
-
continue
}
return errors.Wrapf(err, "failed to read from slirp4netns sync pipe")
}
}
+
+ if havePortMapping {
+ const pidWaitTimeout = 60 * time.Second
+ chWait := make(chan error)
+ go func() {
+ interval := 25 * time.Millisecond
+ for i := time.Duration(0); i < pidWaitTimeout; i += interval {
+ // Check if the process is still running.
+ var status syscall.WaitStatus
+ pid, err := syscall.Wait4(cmd.Process.Pid, &status, syscall.WNOHANG, nil)
+ if err != nil {
+ break
+ }
+ if pid != cmd.Process.Pid {
+ continue
+ }
+ if status.Exited() || status.Signaled() {
+ chWait <- fmt.Errorf("slirp4netns exited with status %d", status.ExitStatus())
+ }
+ time.Sleep(interval)
+ }
+ }()
+ defer close(chWait)
+
+ // wait that API socket file appears before trying to use it.
+ if _, err := WaitForFile(apiSocket, chWait, pidWaitTimeout*time.Millisecond); err != nil {
+ return errors.Wrapf(err, "waiting for slirp4nets to create the api socket file %s", apiSocket)
+ }
+
+ // for each port we want to add we need to open a connection to the slirp4netns control socket
+ // and send the add_hostfwd command.
+ for _, i := range ctr.config.PortMappings {
+ conn, err := net.Dial("unix", apiSocket)
+ if err != nil {
+ return errors.Wrapf(err, "cannot open connection to %s", apiSocket)
+ }
+ defer conn.Close()
+ hostIP := i.HostIP
+ if hostIP == "" {
+ hostIP = "0.0.0.0"
+ }
+ cmd := slirp4netnsCmd{
+ Execute: "add_hostfwd",
+ Args: slirp4netnsCmdArg{
+ Proto: i.Protocol,
+ HostAddr: hostIP,
+ HostPort: i.HostPort,
+ GuestPort: i.ContainerPort,
+ },
+ }
+ // create the JSON payload and send it. Mark the end of request shutting down writes
+ // to the socket, as requested by slirp4netns.
+ data, err := json.Marshal(&cmd)
+ if err != nil {
+ return errors.Wrapf(err, "cannot marshal JSON for slirp4netns")
+ }
+ if _, err := conn.Write([]byte(fmt.Sprintf("%s\n", data))); err != nil {
+ return errors.Wrapf(err, "cannot write to control socket %s", apiSocket)
+ }
+ if err := conn.(*net.UnixConn).CloseWrite(); err != nil {
+ return errors.Wrapf(err, "cannot shutdown the socket %s", apiSocket)
+ }
+ buf := make([]byte, 2048)
+ len, err := conn.Read(buf)
+ if err != nil {
+ return errors.Wrapf(err, "cannot read from control socket %s", apiSocket)
+ }
+ // if there is no 'error' key in the received JSON data, then the operation was
+ // successful.
+ var y map[string]interface{}
+ if err := json.Unmarshal(buf[0:len], &y); err != nil {
+ return errors.Wrapf(err, "error parsing error status from slirp4netns")
+ }
+ if e, found := y["error"]; found {
+ return errors.Errorf("error from slirp4netns while setting up port redirection: %v", e)
+ }
+ }
+ }
return nil
}
diff --git a/libpod/oci.go b/libpod/oci.go
index 4092657f8..3451ddaa8 100644
--- a/libpod/oci.go
+++ b/libpod/oci.go
@@ -323,7 +323,7 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string, res
cmd.Env = append(cmd.Env, fmt.Sprintf("HOME=%s", os.Getenv("HOME")))
cmd.Env = append(cmd.Env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir))
- if r.reservePorts {
+ if r.reservePorts && !ctr.config.NetMode.IsSlirp4netns() {
ports, err := bindPorts(ctr.config.PortMappings)
if err != nil {
return err
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 68599fe6d..6d5ce5a7e 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -48,7 +48,7 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ..
ctr := new(Container)
ctr.config = new(ContainerConfig)
- ctr.state = new(containerState)
+ ctr.state = new(ContainerState)
ctr.config.ID = stringid.GenerateNonCryptoID()
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index 632d60b55..344f4afb9 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -422,11 +422,7 @@ func (c *CreateConfig) GetContainerCreateOptions(runtime *libpod.Runtime, pod *l
}
options = append(options, libpod.WithNetNSFrom(connectedCtr))
} else if !c.NetMode.IsHost() && !c.NetMode.IsNone() {
- isRootless := rootless.IsRootless()
postConfigureNetNS := c.NetMode.IsSlirp4netns() || (len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0) && !c.UsernsMode.IsHost()
- if isRootless && len(portBindings) > 0 {
- return nil, errors.New("port bindings are not yet supported by rootless containers")
- }
options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, string(c.NetMode), networks))
}
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index 9ef0223f2..46105af4a 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -376,6 +376,10 @@ func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint
}
func blockAccessToKernelFilesystems(config *CreateConfig, g *generate.Generator) {
+ if config.PidMode.IsHost() && rootless.IsRootless() {
+ return
+ }
+
if !config.Privileged {
for _, mp := range []string{
"/proc/acpi",
diff --git a/pkg/varlinkapi/containers.go b/pkg/varlinkapi/containers.go
index 07d981786..a01e3cc2b 100644
--- a/pkg/varlinkapi/containers.go
+++ b/pkg/varlinkapi/containers.go
@@ -12,6 +12,7 @@ import (
"github.com/containers/libpod/cmd/podman/shared"
"github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
+ cc "github.com/containers/libpod/pkg/spec"
"github.com/containers/storage/pkg/archive"
"github.com/pkg/errors"
)
@@ -68,7 +69,12 @@ func (i *LibpodAPI) InspectContainer(call iopodman.VarlinkCall, name string) err
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
- data, err := shared.GetCtrInspectInfo(ctr, inspectInfo)
+ artifact, err := getArtifact(ctr)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+
+ data, err := shared.GetCtrInspectInfo(ctr.Config(), inspectInfo, artifact)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
@@ -462,3 +468,81 @@ func (i *LibpodAPI) ContainerRestore(call iopodman.VarlinkCall, name string, kee
}
return call.ReplyContainerRestore(ctr.ID())
}
+
+func getArtifact(ctr *libpod.Container) (*cc.CreateConfig, error) {
+ var createArtifact cc.CreateConfig
+ artifact, err := ctr.GetArtifact("create-config")
+ if err != nil {
+ return nil, err
+ }
+ if err := json.Unmarshal(artifact, &createArtifact); err != nil {
+ return nil, err
+ }
+ return &createArtifact, nil
+}
+
+// ContainerConfig returns just the container.config struct
+func (i *LibpodAPI) ContainerConfig(call iopodman.VarlinkCall, name string) error {
+ ctr, err := i.Runtime.LookupContainer(name)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ config := ctr.Config()
+ b, err := json.Marshal(config)
+ if err != nil {
+ return call.ReplyErrorOccurred("unable to serialize container config")
+ }
+ return call.ReplyContainerConfig(string(b))
+}
+
+// ContainerArtifacts returns an untouched container's artifact in string format
+func (i *LibpodAPI) ContainerArtifacts(call iopodman.VarlinkCall, name, artifactName string) error {
+ ctr, err := i.Runtime.LookupContainer(name)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ artifacts, err := ctr.GetArtifact(artifactName)
+ if err != nil {
+ return call.ReplyErrorOccurred("unable to get container artifacts")
+ }
+ b, err := json.Marshal(artifacts)
+ if err != nil {
+ return call.ReplyErrorOccurred("unable to serialize container artifacts")
+ }
+ return call.ReplyContainerArtifacts(string(b))
+}
+
+// ContainerInspectData returns the inspect data of a container in string format
+func (i *LibpodAPI) ContainerInspectData(call iopodman.VarlinkCall, name string) error {
+ ctr, err := i.Runtime.LookupContainer(name)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ data, err := ctr.Inspect(true)
+ if err != nil {
+ return call.ReplyErrorOccurred("unable to inspect container")
+ }
+ b, err := json.Marshal(data)
+ if err != nil {
+ return call.ReplyErrorOccurred("unable to serialize container inspect data")
+ }
+ return call.ReplyContainerInspectData(string(b))
+
+}
+
+// ContainerStateData returns a container's state data in string format
+func (i *LibpodAPI) ContainerStateData(call iopodman.VarlinkCall, name string) error {
+ ctr, err := i.Runtime.LookupContainer(name)
+ if err != nil {
+ return call.ReplyErrorOccurred(err.Error())
+ }
+ data, err := ctr.ContainerState()
+ if err != nil {
+ return call.ReplyErrorOccurred("unable to obtain container state")
+ }
+ b, err := json.Marshal(data)
+ if err != nil {
+ return call.ReplyErrorOccurred("unable to serialize container inspect data")
+ }
+ return call.ReplyContainerStateData(string(b))
+}
diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go
index ed009b530..308a6bf29 100644
--- a/test/e2e/common_test.go
+++ b/test/e2e/common_test.go
@@ -1,17 +1,19 @@
package integration
import (
+ "encoding/json"
"fmt"
- . "github.com/containers/libpod/test/utils"
"os"
"os/exec"
+ "path/filepath"
+ "strings"
+ "testing"
+ "github.com/containers/libpod/pkg/inspect"
+ . "github.com/containers/libpod/test/utils"
"github.com/containers/storage/pkg/reexec"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
- "path/filepath"
- "strings"
- "testing"
)
var (
@@ -205,3 +207,12 @@ func (p *PodmanTestIntegration) CreateArtifact(image string) error {
}
return nil
}
+
+// InspectImageJSON takes the session output of an inspect
+// image and returns json
+func (s *PodmanSessionIntegration) InspectImageJSON() []inspect.ImageData {
+ var i []inspect.ImageData
+ err := json.Unmarshal(s.Out.Contents(), &i)
+ Expect(err).To(BeNil())
+ return i
+}
diff --git a/test/e2e/inspect_test.go b/test/e2e/inspect_test.go
index 38c50da14..e5c471bf9 100644
--- a/test/e2e/inspect_test.go
+++ b/test/e2e/inspect_test.go
@@ -1,5 +1,3 @@
-// +build !remoteclient
-
package integration
import (
@@ -45,6 +43,7 @@ var _ = Describe("Podman inspect", func() {
})
It("podman inspect bogus container", func() {
+ SkipIfRemote()
session := podmanTest.Podman([]string{"inspect", "foobar4321"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Not(Equal(0)))
@@ -68,6 +67,7 @@ var _ = Describe("Podman inspect", func() {
})
It("podman inspect container with size", func() {
+ SkipIfRemote()
_, ec, _ := podmanTest.RunLsContainer("")
Expect(ec).To(Equal(0))
@@ -79,6 +79,7 @@ var _ = Describe("Podman inspect", func() {
})
It("podman inspect container and image", func() {
+ SkipIfRemote()
ls, ec, _ := podmanTest.RunLsContainer("")
Expect(ec).To(Equal(0))
cid := ls.OutputToString()
@@ -90,6 +91,7 @@ var _ = Describe("Podman inspect", func() {
})
It("podman inspect -l with additional input should fail", func() {
+ SkipIfRemote()
result := podmanTest.Podman([]string{"inspect", "-l", "1234foobar"})
result.WaitWithDefaultTimeout()
Expect(result.ExitCode()).To(Equal(125))
diff --git a/test/e2e/libpod_suite_remoteclient_test.go b/test/e2e/libpod_suite_remoteclient_test.go
index c1ad72ff3..4b769a574 100644
--- a/test/e2e/libpod_suite_remoteclient_test.go
+++ b/test/e2e/libpod_suite_remoteclient_test.go
@@ -55,10 +55,10 @@ func (p *PodmanTestIntegration) RunLsContainer(name string) (*PodmanSessionInteg
// InspectImageJSON takes the session output of an inspect
// image and returns json
-func (s *PodmanSessionIntegration) InspectImageJSON() []inspect.ImageData {
- // TODO
- return nil
-}
+//func (s *PodmanSessionIntegration) InspectImageJSON() []inspect.ImageData {
+// // TODO
+// return nil
+//}
func (p *PodmanTestIntegration) setDefaultRegistriesConfigEnv() {
defaultFile := filepath.Join(INTEGRATION_ROOT, "test/registries.conf")
diff --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go
index c519567c4..1f218cbdf 100644
--- a/test/e2e/libpod_suite_test.go
+++ b/test/e2e/libpod_suite_test.go
@@ -118,15 +118,6 @@ func (s *PodmanSessionIntegration) InspectPodToJSON() libpod.PodInspect {
return i
}
-// InspectImageJSON takes the session output of an inspect
-// image and returns json
-func (s *PodmanSessionIntegration) InspectImageJSON() []inspect.ImageData {
- var i []inspect.ImageData
- err := json.Unmarshal(s.Out.Contents(), &i)
- Expect(err).To(BeNil())
- return i
-}
-
// CreatePod creates a pod with no infra container
// it optionally takes a pod name
func (p *PodmanTestIntegration) CreatePod(name string) (*PodmanSessionIntegration, int, string) {
diff --git a/test/e2e/rootless_test.go b/test/e2e/rootless_test.go
index daf8b8c32..2b84d34c9 100644
--- a/test/e2e/rootless_test.go
+++ b/test/e2e/rootless_test.go
@@ -276,6 +276,10 @@ var _ = Describe("Podman rootless", func() {
runRootlessHelper([]string{"--net", "host"})
})
+ It("podman rootless rootfs --pid host", func() {
+ runRootlessHelper([]string{"--pid", "host"})
+ })
+
It("podman rootless rootfs --privileged", func() {
runRootlessHelper([]string{"--privileged"})
})
diff --git a/vendor.conf b/vendor.conf
index 476150a53..9c9d41a0d 100644
--- a/vendor.conf
+++ b/vendor.conf
@@ -16,7 +16,7 @@ github.com/containerd/continuity 004b46473808b3e7a4a3049c20e4376c91eb966d
github.com/containernetworking/cni v0.7.0-alpha1
github.com/containernetworking/plugins v0.7.4
github.com/containers/image v1.3
-github.com/containers/storage v1.6
+github.com/containers/storage v1.7
github.com/containers/psgo v1.1
github.com/coreos/go-systemd v14
github.com/cri-o/ocicni 2d2983e40c242322a56c22a903785e7f83eb378c
@@ -90,7 +90,7 @@ k8s.io/api kubernetes-1.10.13-beta.0 https://github.com/kubernetes/api
k8s.io/apimachinery kubernetes-1.10.13-beta.0 https://github.com/kubernetes/apimachinery
k8s.io/client-go kubernetes-1.10.13-beta.0 https://github.com/kubernetes/client-go
github.com/mrunalp/fileutils 7d4729fb36185a7c1719923406c9d40e54fb93c7
-github.com/varlink/go e9fdc57f40123518ac513eb3443e50625ad6b434
+github.com/varlink/go 92687ab4eb68d99e43b1f5b93477ad76bb54f811
github.com/containers/buildah e7ca330f923701dba8859f5c014d0a9a3f7f0a49
# TODO: Gotty has not been updated since 2012. Can we find replacement?
github.com/Nvveen/Gotty cd527374f1e5bff4938207604a14f2e38a9cf512
diff --git a/vendor/github.com/containers/storage/containers_ffjson.go b/vendor/github.com/containers/storage/containers_ffjson.go
index aef6becfe..40b912bb3 100644
--- a/vendor/github.com/containers/storage/containers_ffjson.go
+++ b/vendor/github.com/containers/storage/containers_ffjson.go
@@ -1,5 +1,5 @@
// Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT.
-// source: containers.go
+// source: ./containers.go
package storage
diff --git a/vendor/github.com/containers/storage/drivers/aufs/aufs.go b/vendor/github.com/containers/storage/drivers/aufs/aufs.go
index ca69816be..e821bc0c5 100644
--- a/vendor/github.com/containers/storage/drivers/aufs/aufs.go
+++ b/vendor/github.com/containers/storage/drivers/aufs/aufs.go
@@ -253,6 +253,11 @@ func (a *Driver) AdditionalImageStores() []string {
return nil
}
+// CreateFromTemplate creates a layer with the same contents and parent as another layer.
+func (a *Driver) CreateFromTemplate(id, template string, templateIDMappings *idtools.IDMappings, parent string, parentIDMappings *idtools.IDMappings, opts *graphdriver.CreateOpts, readWrite bool) error {
+ return graphdriver.NaiveCreateFromTemplate(a, id, template, templateIDMappings, parent, parentIDMappings, opts, readWrite)
+}
+
// CreateReadWrite creates a layer that is writable for use as a container
// file system.
func (a *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
diff --git a/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go b/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go
index 567cda9d3..30254d9fb 100644
--- a/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go
+++ b/vendor/github.com/containers/storage/drivers/btrfs/btrfs.go
@@ -490,6 +490,11 @@ func (d *Driver) quotasDirID(id string) string {
return path.Join(d.quotasDir(), id)
}
+// CreateFromTemplate creates a layer with the same contents and parent as another layer.
+func (d *Driver) CreateFromTemplate(id, template string, templateIDMappings *idtools.IDMappings, parent string, parentIDMappings *idtools.IDMappings, opts *graphdriver.CreateOpts, readWrite bool) error {
+ return d.Create(id, template, opts)
+}
+
// CreateReadWrite creates a layer that is writable for use as a container
// file system.
func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
diff --git a/vendor/github.com/containers/storage/drivers/devmapper/driver.go b/vendor/github.com/containers/storage/drivers/devmapper/driver.go
index 39a4fbe2c..13677c93a 100644
--- a/vendor/github.com/containers/storage/drivers/devmapper/driver.go
+++ b/vendor/github.com/containers/storage/drivers/devmapper/driver.go
@@ -123,6 +123,11 @@ func (d *Driver) Cleanup() error {
return err
}
+// CreateFromTemplate creates a layer with the same contents and parent as another layer.
+func (d *Driver) CreateFromTemplate(id, template string, templateIDMappings *idtools.IDMappings, parent string, parentIDMappings *idtools.IDMappings, opts *graphdriver.CreateOpts, readWrite bool) error {
+ return d.Create(id, template, opts)
+}
+
// CreateReadWrite creates a layer that is writable for use as a container
// file system.
func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
diff --git a/vendor/github.com/containers/storage/drivers/driver.go b/vendor/github.com/containers/storage/drivers/driver.go
index 476b55160..dda172574 100644
--- a/vendor/github.com/containers/storage/drivers/driver.go
+++ b/vendor/github.com/containers/storage/drivers/driver.go
@@ -72,6 +72,9 @@ type ProtoDriver interface {
// specified id and parent and options passed in opts. Parent
// may be "" and opts may be nil.
Create(id, parent string, opts *CreateOpts) error
+ // CreateFromTemplate creates a new filesystem layer with the specified id
+ // and parent, with contents identical to the specified template layer.
+ CreateFromTemplate(id, template string, templateIDMappings *idtools.IDMappings, parent string, parentIDMappings *idtools.IDMappings, opts *CreateOpts, readWrite bool) error
// Remove attempts to remove the filesystem layer with this id.
Remove(id string) error
// Get returns the mountpoint for the layered filesystem referred
diff --git a/vendor/github.com/containers/storage/drivers/overlay/check.go b/vendor/github.com/containers/storage/drivers/overlay/check.go
index 590d517fa..a566e4afd 100644
--- a/vendor/github.com/containers/storage/drivers/overlay/check.go
+++ b/vendor/github.com/containers/storage/drivers/overlay/check.go
@@ -10,6 +10,8 @@ import (
"path/filepath"
"syscall"
+ "github.com/containers/storage/pkg/ioutils"
+ "github.com/containers/storage/pkg/mount"
"github.com/containers/storage/pkg/system"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -57,10 +59,11 @@ func doesSupportNativeDiff(d, mountOpts string) error {
}
opts := fmt.Sprintf("lowerdir=%s:%s,upperdir=%s,workdir=%s", path.Join(td, "l2"), path.Join(td, "l1"), path.Join(td, "l3"), path.Join(td, "work"))
- if mountOpts != "" {
- opts = fmt.Sprintf("%s,%s", opts, mountOpts)
+ flags, data := mount.ParseOptions(mountOpts)
+ if data != "" {
+ opts = fmt.Sprintf("%s,%s", opts, data)
}
- if err := unix.Mount("overlay", filepath.Join(td, "merged"), "overlay", 0, opts); err != nil {
+ if err := unix.Mount("overlay", filepath.Join(td, "merged"), "overlay", uintptr(flags), opts); err != nil {
return errors.Wrap(err, "failed to mount overlay")
}
defer func() {
@@ -103,3 +106,60 @@ func doesSupportNativeDiff(d, mountOpts string) error {
return nil
}
+
+// doesMetacopy checks if the filesystem is going to optimize changes to
+// metadata by using nodes marked with an "overlay.metacopy" attribute to avoid
+// copying up a file from a lower layer unless/until its contents are being
+// modified
+func doesMetacopy(d, mountOpts string) (bool, error) {
+ td, err := ioutil.TempDir(d, "metacopy-check")
+ if err != nil {
+ return false, err
+ }
+ defer func() {
+ if err := os.RemoveAll(td); err != nil {
+ logrus.Warnf("Failed to remove check directory %v: %v", td, err)
+ }
+ }()
+
+ // Make directories l1, l2, work, merged
+ if err := os.MkdirAll(filepath.Join(td, "l1"), 0755); err != nil {
+ return false, err
+ }
+ if err := ioutils.AtomicWriteFile(filepath.Join(td, "l1", "f"), []byte{0xff}, 0700); err != nil {
+ return false, err
+ }
+ if err := os.MkdirAll(filepath.Join(td, "l2"), 0755); err != nil {
+ return false, err
+ }
+ if err := os.Mkdir(filepath.Join(td, "work"), 0755); err != nil {
+ return false, err
+ }
+ if err := os.Mkdir(filepath.Join(td, "merged"), 0755); err != nil {
+ return false, err
+ }
+ // Mount using the mandatory options and configured options
+ opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", path.Join(td, "l1"), path.Join(td, "l2"), path.Join(td, "work"))
+ flags, data := mount.ParseOptions(mountOpts)
+ if data != "" {
+ opts = fmt.Sprintf("%s,%s", opts, data)
+ }
+ if err := unix.Mount("overlay", filepath.Join(td, "merged"), "overlay", uintptr(flags), opts); err != nil {
+ return false, errors.Wrap(err, "failed to mount overlay for metacopy check")
+ }
+ defer func() {
+ if err := unix.Unmount(filepath.Join(td, "merged"), 0); err != nil {
+ logrus.Warnf("Failed to unmount check directory %v: %v", filepath.Join(td, "merged"), err)
+ }
+ }()
+ // Make a change that only impacts the inode, and check if the pulled-up copy is marked
+ // as a metadata-only copy
+ if err := os.Chmod(filepath.Join(td, "merged", "f"), 0600); err != nil {
+ return false, errors.Wrap(err, "error changing permissions on file for metacopy check")
+ }
+ metacopy, err := system.Lgetxattr(filepath.Join(td, "l2", "f"), "trusted.overlay.metacopy")
+ if err != nil {
+ return false, errors.Wrap(err, "metacopy flag was not set on file in upper layer")
+ }
+ return metacopy != nil, nil
+}
diff --git a/vendor/github.com/containers/storage/drivers/overlay/overlay.go b/vendor/github.com/containers/storage/drivers/overlay/overlay.go
index 06d58db38..3e8daf23c 100644
--- a/vendor/github.com/containers/storage/drivers/overlay/overlay.go
+++ b/vendor/github.com/containers/storage/drivers/overlay/overlay.go
@@ -85,13 +85,12 @@ const (
)
type overlayOptions struct {
- overrideKernelCheck bool
- imageStores []string
- quota quota.Quota
- mountProgram string
- ostreeRepo string
- skipMountHome bool
- mountOptions string
+ imageStores []string
+ quota quota.Quota
+ mountProgram string
+ ostreeRepo string
+ skipMountHome bool
+ mountOptions string
}
// Driver contains information about the home directory and the list of active mounts that are created using this driver.
@@ -105,6 +104,7 @@ type Driver struct {
options overlayOptions
naiveDiff graphdriver.DiffDriver
supportsDType bool
+ usingMetacopy bool
locker *locker.Locker
convert map[string]bool
}
@@ -174,6 +174,18 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
}
}
+ usingMetacopy, err := doesMetacopy(home, opts.mountOptions)
+ if err == nil {
+ if usingMetacopy {
+ logrus.Debugf("overlay test mount indicated that metacopy is being used")
+ } else {
+ logrus.Debugf("overlay test mount indicated that metacopy is not being used")
+ }
+ } else {
+ logrus.Warnf("overlay test mount did not indicate whether or not metacopy is being used: %v", err)
+ return nil, err
+ }
+
if !opts.skipMountHome {
if err := mount.MakePrivate(home); err != nil {
return nil, err
@@ -193,6 +205,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
gidMaps: gidMaps,
ctr: graphdriver.NewRefCounter(graphdriver.NewFsChecker(graphdriver.FsMagicOverlay)),
supportsDType: supportsDType,
+ usingMetacopy: usingMetacopy,
locker: locker.New(),
options: *opts,
convert: make(map[string]bool),
@@ -212,7 +225,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
return nil, fmt.Errorf("Storage option overlay.size only supported for backingFS XFS. Found %v", backingFs)
}
- logrus.Debugf("backingFs=%s, projectQuotaSupported=%v, useNativeDiff=%v", backingFs, projectQuotaSupported, !d.useNaiveDiff())
+ logrus.Debugf("backingFs=%s, projectQuotaSupported=%v, useNativeDiff=%v, usingMetacopy=%v", backingFs, projectQuotaSupported, !d.useNaiveDiff(), d.usingMetacopy)
return d, nil
}
@@ -226,12 +239,6 @@ func parseOptions(options []string) (*overlayOptions, error) {
}
key = strings.ToLower(key)
switch key {
- case ".override_kernel_check", "overlay.override_kernel_check", "overlay2.override_kernel_check":
- logrus.Debugf("overlay: override_kernelcheck=%s", val)
- o.overrideKernelCheck, err = strconv.ParseBool(val)
- if err != nil {
- return nil, err
- }
case ".mountopt", "overlay.mountopt", "overlay2.mountopt":
o.mountOptions = val
case ".size", "overlay.size", "overlay2.size":
@@ -375,6 +382,7 @@ func (d *Driver) Status() [][2]string {
{"Backing Filesystem", backingFs},
{"Supports d_type", strconv.FormatBool(d.supportsDType)},
{"Native Overlay Diff", strconv.FormatBool(!d.useNaiveDiff())},
+ {"Using metacopy", strconv.FormatBool(d.usingMetacopy)},
}
}
@@ -410,6 +418,14 @@ func (d *Driver) Cleanup() error {
return mount.Unmount(d.home)
}
+// CreateFromTemplate creates a layer with the same contents and parent as another layer.
+func (d *Driver) CreateFromTemplate(id, template string, templateIDMappings *idtools.IDMappings, parent string, parentIDMappings *idtools.IDMappings, opts *graphdriver.CreateOpts, readWrite bool) error {
+ if readWrite {
+ return d.CreateReadWrite(id, template, opts)
+ }
+ return d.Create(id, template, opts)
+}
+
// CreateReadWrite creates a layer that is writable for use as a container
// file system.
func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
diff --git a/vendor/github.com/containers/storage/drivers/template.go b/vendor/github.com/containers/storage/drivers/template.go
new file mode 100644
index 000000000..dfcbffb83
--- /dev/null
+++ b/vendor/github.com/containers/storage/drivers/template.go
@@ -0,0 +1,45 @@
+package graphdriver
+
+import (
+ "github.com/sirupsen/logrus"
+
+ "github.com/containers/storage/pkg/idtools"
+)
+
+// TemplateDriver is just barely enough of a driver that we can implement a
+// naive version of CreateFromTemplate on top of it.
+type TemplateDriver interface {
+ DiffDriver
+ CreateReadWrite(id, parent string, opts *CreateOpts) error
+ Create(id, parent string, opts *CreateOpts) error
+ Remove(id string) error
+}
+
+// CreateFromTemplate creates a layer with the same contents and parent as
+// another layer. Internally, it may even depend on that other layer
+// continuing to exist, as if it were actually a child of the child layer.
+func NaiveCreateFromTemplate(d TemplateDriver, id, template string, templateIDMappings *idtools.IDMappings, parent string, parentIDMappings *idtools.IDMappings, opts *CreateOpts, readWrite bool) error {
+ var err error
+ if readWrite {
+ err = d.CreateReadWrite(id, parent, opts)
+ } else {
+ err = d.Create(id, parent, opts)
+ }
+ if err != nil {
+ return err
+ }
+ diff, err := d.Diff(template, templateIDMappings, parent, parentIDMappings, opts.MountLabel)
+ if err != nil {
+ if err2 := d.Remove(id); err2 != nil {
+ logrus.Errorf("error removing layer %q: %v", id, err2)
+ }
+ return err
+ }
+ if _, err = d.ApplyDiff(id, templateIDMappings, parent, opts.MountLabel, diff); err != nil {
+ if err2 := d.Remove(id); err2 != nil {
+ logrus.Errorf("error removing layer %q: %v", id, err2)
+ }
+ return err
+ }
+ return nil
+}
diff --git a/vendor/github.com/containers/storage/drivers/vfs/driver.go b/vendor/github.com/containers/storage/drivers/vfs/driver.go
index f7f3c75ba..5941ccc17 100644
--- a/vendor/github.com/containers/storage/drivers/vfs/driver.go
+++ b/vendor/github.com/containers/storage/drivers/vfs/driver.go
@@ -99,6 +99,14 @@ func (d *Driver) Cleanup() error {
return nil
}
+// CreateFromTemplate creates a layer with the same contents and parent as another layer.
+func (d *Driver) CreateFromTemplate(id, template string, templateIDMappings *idtools.IDMappings, parent string, parentIDMappings *idtools.IDMappings, opts *graphdriver.CreateOpts, readWrite bool) error {
+ if readWrite {
+ return d.CreateReadWrite(id, template, opts)
+ }
+ return d.Create(id, template, opts)
+}
+
// CreateReadWrite creates a layer that is writable for use as a container
// file system.
func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
diff --git a/vendor/github.com/containers/storage/drivers/windows/windows.go b/vendor/github.com/containers/storage/drivers/windows/windows.go
index c6d86a4ab..c7df1c1fe 100644
--- a/vendor/github.com/containers/storage/drivers/windows/windows.go
+++ b/vendor/github.com/containers/storage/drivers/windows/windows.go
@@ -185,6 +185,11 @@ func (d *Driver) Exists(id string) bool {
return result
}
+// CreateFromTemplate creates a layer with the same contents and parent as another layer.
+func (d *Driver) CreateFromTemplate(id, template string, templateIDMappings *idtools.IDMappings, parent string, parentIDMappings *idtools.IDMappings, opts *graphdriver.CreateOpts, readWrite bool) error {
+ return graphdriver.NaiveCreateFromTemplate(d, id, template, templateIDMappings, parent, parentIDMappings, opts, readWrite)
+}
+
// CreateReadWrite creates a layer that is writable for use as a container
// file system.
func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
diff --git a/vendor/github.com/containers/storage/drivers/zfs/zfs.go b/vendor/github.com/containers/storage/drivers/zfs/zfs.go
index 69b143c54..eaa9e8bc5 100644
--- a/vendor/github.com/containers/storage/drivers/zfs/zfs.go
+++ b/vendor/github.com/containers/storage/drivers/zfs/zfs.go
@@ -259,6 +259,11 @@ func (d *Driver) mountPath(id string) string {
return path.Join(d.options.mountPath, "graph", getMountpoint(id))
}
+// CreateFromTemplate creates a layer with the same contents and parent as another layer.
+func (d *Driver) CreateFromTemplate(id, template string, templateIDMappings *idtools.IDMappings, parent string, parentIDMappings *idtools.IDMappings, opts *graphdriver.CreateOpts, readWrite bool) error {
+ return d.Create(id, template, opts)
+}
+
// CreateReadWrite creates a layer that is writable for use as a container
// file system.
func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
diff --git a/vendor/github.com/containers/storage/images.go b/vendor/github.com/containers/storage/images.go
index b10501b08..d99842534 100644
--- a/vendor/github.com/containers/storage/images.go
+++ b/vendor/github.com/containers/storage/images.go
@@ -42,7 +42,9 @@ type Image struct {
// MappedTopLayers are the IDs of alternate versions of the top layer
// which have the same contents and parent, and which differ from
- // TopLayer only in which ID mappings they use.
+ // TopLayer only in which ID mappings they use. When the image is
+ // to be removed, they should be removed before the TopLayer, as the
+ // graph driver may depend on that.
MappedTopLayers []string `json:"mapped-layers,omitempty"`
// Metadata is data we keep for the convenience of the caller. It is not
diff --git a/vendor/github.com/containers/storage/images_ffjson.go b/vendor/github.com/containers/storage/images_ffjson.go
index 6b40ebd59..539acfe93 100644
--- a/vendor/github.com/containers/storage/images_ffjson.go
+++ b/vendor/github.com/containers/storage/images_ffjson.go
@@ -1,5 +1,5 @@
// Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT.
-// source: images.go
+// source: ./images.go
package storage
diff --git a/vendor/github.com/containers/storage/layers.go b/vendor/github.com/containers/storage/layers.go
index 299d2f818..cdc3cbba9 100644
--- a/vendor/github.com/containers/storage/layers.go
+++ b/vendor/github.com/containers/storage/layers.go
@@ -551,9 +551,20 @@ func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLab
}
}
parent := ""
- var parentMappings *idtools.IDMappings
if parentLayer != nil {
parent = parentLayer.ID
+ }
+ var parentMappings, templateIDMappings, oldMappings *idtools.IDMappings
+ if moreOptions.TemplateLayer != "" {
+ templateLayer, ok := r.lookup(moreOptions.TemplateLayer)
+ if !ok {
+ return nil, -1, ErrLayerUnknown
+ }
+ templateIDMappings = idtools.NewIDMappingsFromMaps(templateLayer.UIDMap, templateLayer.GIDMap)
+ } else {
+ templateIDMappings = &idtools.IDMappings{}
+ }
+ if parentLayer != nil {
parentMappings = idtools.NewIDMappingsFromMaps(parentLayer.UIDMap, parentLayer.GIDMap)
} else {
parentMappings = &idtools.IDMappings{}
@@ -566,23 +577,34 @@ func (r *layerStore) Put(id string, parentLayer *Layer, names []string, mountLab
MountLabel: mountLabel,
StorageOpt: options,
}
- if writeable {
- if err = r.driver.CreateReadWrite(id, parent, &opts); err != nil {
+ if moreOptions.TemplateLayer != "" {
+ if err = r.driver.CreateFromTemplate(id, moreOptions.TemplateLayer, templateIDMappings, parent, parentMappings, &opts, writeable); err != nil {
if id != "" {
- return nil, -1, errors.Wrapf(err, "error creating read-write layer with ID %q", id)
+ return nil, -1, errors.Wrapf(err, "error creating copy of template layer %q with ID %q", moreOptions.TemplateLayer, id)
}
- return nil, -1, errors.Wrapf(err, "error creating read-write layer")
+ return nil, -1, errors.Wrapf(err, "error creating copy of template layer %q", moreOptions.TemplateLayer)
}
+ oldMappings = templateIDMappings
} else {
- if err = r.driver.Create(id, parent, &opts); err != nil {
- if id != "" {
- return nil, -1, errors.Wrapf(err, "error creating layer with ID %q", id)
+ if writeable {
+ if err = r.driver.CreateReadWrite(id, parent, &opts); err != nil {
+ if id != "" {
+ return nil, -1, errors.Wrapf(err, "error creating read-write layer with ID %q", id)
+ }
+ return nil, -1, errors.Wrapf(err, "error creating read-write layer")
+ }
+ } else {
+ if err = r.driver.Create(id, parent, &opts); err != nil {
+ if id != "" {
+ return nil, -1, errors.Wrapf(err, "error creating layer with ID %q", id)
+ }
+ return nil, -1, errors.Wrapf(err, "error creating layer")
}
- return nil, -1, errors.Wrapf(err, "error creating layer")
}
+ oldMappings = parentMappings
}
- if !reflect.DeepEqual(parentMappings.UIDs(), idMappings.UIDs()) || !reflect.DeepEqual(parentMappings.GIDs(), idMappings.GIDs()) {
- if err = r.driver.UpdateLayerIDMap(id, parentMappings, idMappings, mountLabel); err != nil {
+ if !reflect.DeepEqual(oldMappings.UIDs(), idMappings.UIDs()) || !reflect.DeepEqual(oldMappings.GIDs(), idMappings.GIDs()) {
+ if err = r.driver.UpdateLayerIDMap(id, oldMappings, idMappings, mountLabel); err != nil {
// We don't have a record of this layer, but at least
// try to clean it up underneath us.
r.driver.Remove(id)
diff --git a/vendor/github.com/containers/storage/layers_ffjson.go b/vendor/github.com/containers/storage/layers_ffjson.go
index 125b5d8c9..09b5d0f33 100644
--- a/vendor/github.com/containers/storage/layers_ffjson.go
+++ b/vendor/github.com/containers/storage/layers_ffjson.go
@@ -1,5 +1,5 @@
// Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT.
-// source: layers.go
+// source: ./layers.go
package storage
diff --git a/vendor/github.com/containers/storage/pkg/archive/example_changes.go b/vendor/github.com/containers/storage/pkg/archive/example_changes.go
new file mode 100644
index 000000000..70f9c5564
--- /dev/null
+++ b/vendor/github.com/containers/storage/pkg/archive/example_changes.go
@@ -0,0 +1,97 @@
+// +build ignore
+
+// Simple tool to create an archive stream from an old and new directory
+//
+// By default it will stream the comparison of two temporary directories with junk files
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path"
+
+ "github.com/containers/storage/pkg/archive"
+ "github.com/sirupsen/logrus"
+)
+
+var (
+ flDebug = flag.Bool("D", false, "debugging output")
+ flNewDir = flag.String("newdir", "", "")
+ flOldDir = flag.String("olddir", "", "")
+ log = logrus.New()
+)
+
+func main() {
+ flag.Usage = func() {
+ fmt.Println("Produce a tar from comparing two directory paths. By default a demo tar is created of around 200 files (including hardlinks)")
+ fmt.Printf("%s [OPTIONS]\n", os.Args[0])
+ flag.PrintDefaults()
+ }
+ flag.Parse()
+ log.Out = os.Stderr
+ if (len(os.Getenv("DEBUG")) > 0) || *flDebug {
+ logrus.SetLevel(logrus.DebugLevel)
+ }
+ var newDir, oldDir string
+
+ if len(*flNewDir) == 0 {
+ var err error
+ newDir, err = ioutil.TempDir("", "storage-test-newDir")
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer os.RemoveAll(newDir)
+ if _, err := prepareUntarSourceDirectory(100, newDir, true); err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ newDir = *flNewDir
+ }
+
+ if len(*flOldDir) == 0 {
+ oldDir, err := ioutil.TempDir("", "storage-test-oldDir")
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer os.RemoveAll(oldDir)
+ } else {
+ oldDir = *flOldDir
+ }
+
+ changes, err := archive.ChangesDirs(newDir, oldDir)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ a, err := archive.ExportChanges(newDir, changes)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer a.Close()
+
+ i, err := io.Copy(os.Stdout, a)
+ if err != nil && err != io.EOF {
+ log.Fatal(err)
+ }
+ fmt.Fprintf(os.Stderr, "wrote archive of %d bytes", i)
+}
+
+func prepareUntarSourceDirectory(numberOfFiles int, targetPath string, makeLinks bool) (int, error) {
+ fileData := []byte("fooo")
+ for n := 0; n < numberOfFiles; n++ {
+ fileName := fmt.Sprintf("file-%d", n)
+ if err := ioutil.WriteFile(path.Join(targetPath, fileName), fileData, 0700); err != nil {
+ return 0, err
+ }
+ if makeLinks {
+ if err := os.Link(path.Join(targetPath, fileName), path.Join(targetPath, fileName+"-link")); err != nil {
+ return 0, err
+ }
+ }
+ }
+ totalSize := numberOfFiles * len(fileData)
+ return totalSize, nil
+}
diff --git a/vendor/github.com/containers/storage/store.go b/vendor/github.com/containers/storage/store.go
index 5877c3b06..3fe305cc1 100644
--- a/vendor/github.com/containers/storage/store.go
+++ b/vendor/github.com/containers/storage/store.go
@@ -482,6 +482,10 @@ type LayerOptions struct {
// inherit settings from its parent layer or, if it has no parent
// layer, the Store object.
IDMappingOptions
+ // TemplateLayer is the ID of a layer whose contents will be used to
+ // initialize this layer. If set, it should be a child of the layer
+ // which we want to use as the parent of the new layer.
+ TemplateLayer string
}
// ImageOptions is used for passing options to a Store's CreateImage() method.
@@ -973,7 +977,7 @@ func (s *store) CreateImage(id string, names []string, layer, metadata string, o
return ristore.Create(id, names, layer, metadata, creationDate, options.Digest)
}
-func (s *store) imageTopLayerForMapping(image *Image, ristore ROImageStore, readWrite bool, rlstore LayerStore, lstores []ROLayerStore, options IDMappingOptions) (*Layer, error) {
+func (s *store) imageTopLayerForMapping(image *Image, ristore ROImageStore, createMappedLayer bool, rlstore LayerStore, lstores []ROLayerStore, options IDMappingOptions) (*Layer, error) {
layerMatchesMappingOptions := func(layer *Layer, options IDMappingOptions) bool {
// If the driver supports shifting and the layer has no mappings, we can use it.
if s.graphDriver.SupportsShifting() && len(layer.UIDMap) == 0 && len(layer.GIDMap) == 0 {
@@ -994,7 +998,6 @@ func (s *store) imageTopLayerForMapping(image *Image, ristore ROImageStore, read
return reflect.DeepEqual(layer.UIDMap, options.UIDMap) && reflect.DeepEqual(layer.GIDMap, options.GIDMap)
}
var layer, parentLayer *Layer
- var layerHomeStore ROLayerStore
// Locate the image's top layer and its parent, if it has one.
for _, store := range append([]ROLayerStore{rlstore}, lstores...) {
if store != rlstore {
@@ -1027,7 +1030,6 @@ func (s *store) imageTopLayerForMapping(image *Image, ristore ROImageStore, read
if layer == nil {
layer = cLayer
parentLayer = cParentLayer
- layerHomeStore = store
}
}
}
@@ -1037,27 +1039,25 @@ func (s *store) imageTopLayerForMapping(image *Image, ristore ROImageStore, read
}
// The top layer's mappings don't match the ones we want, but it's in a read-only
// image store, so we can't create and add a mapped copy of the layer to the image.
- if !readWrite {
+ // We'll have to do the mapping for the container itself, elsewhere.
+ if !createMappedLayer {
return layer, nil
}
// The top layer's mappings don't match the ones we want, and it's in an image store
// that lets us edit image metadata...
if istore, ok := ristore.(*imageStore); ok {
- // ... so extract the layer's contents, create a new copy of it with the
- // desired mappings, and register it as an alternate top layer in the image.
- noCompression := archive.Uncompressed
- diffOptions := DiffOptions{
- Compression: &noCompression,
- }
- rc, err := layerHomeStore.Diff("", layer.ID, &diffOptions)
- if err != nil {
- return nil, errors.Wrapf(err, "error reading layer %q to create an ID-mapped version of it", layer.ID)
- }
- defer rc.Close()
-
+ // ... so create a duplicate of the layer with the desired mappings, and
+ // register it as an alternate top layer in the image.
var layerOptions LayerOptions
if s.graphDriver.SupportsShifting() {
- layerOptions = LayerOptions{IDMappingOptions: IDMappingOptions{HostUIDMapping: true, HostGIDMapping: true, UIDMap: nil, GIDMap: nil}}
+ layerOptions = LayerOptions{
+ IDMappingOptions: IDMappingOptions{
+ HostUIDMapping: true,
+ HostGIDMapping: true,
+ UIDMap: nil,
+ GIDMap: nil,
+ },
+ }
} else {
layerOptions = LayerOptions{
IDMappingOptions: IDMappingOptions{
@@ -1068,9 +1068,10 @@ func (s *store) imageTopLayerForMapping(image *Image, ristore ROImageStore, read
},
}
}
- mappedLayer, _, err := rlstore.Put("", parentLayer, nil, layer.MountLabel, nil, &layerOptions, false, nil, rc)
+ layerOptions.TemplateLayer = layer.ID
+ mappedLayer, _, err := rlstore.Put("", parentLayer, nil, layer.MountLabel, nil, &layerOptions, false, nil, nil)
if err != nil {
- return nil, errors.Wrapf(err, "error creating ID-mapped copy of layer %q", layer.ID)
+ return nil, errors.Wrapf(err, "error creating an ID-mapped copy of layer %q", layer.ID)
}
if err = istore.addMappedTopLayer(image.ID, mappedLayer.ID); err != nil {
if err2 := rlstore.Delete(mappedLayer.ID); err2 != nil {
@@ -1144,7 +1145,9 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat
}
imageID = cimage.ID
- ilayer, err := s.imageTopLayerForMapping(cimage, imageHomeStore, imageHomeStore == istore, rlstore, lstores, idMappingsOptions)
+ createMappedLayer := imageHomeStore == istore
+
+ ilayer, err := s.imageTopLayerForMapping(cimage, imageHomeStore, createMappedLayer, rlstore, lstores, idMappingsOptions)
if err != nil {
return nil, err
}
@@ -1170,7 +1173,14 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat
}
var layerOptions *LayerOptions
if s.graphDriver.SupportsShifting() {
- layerOptions = &LayerOptions{IDMappingOptions: IDMappingOptions{HostUIDMapping: true, HostGIDMapping: true, UIDMap: nil, GIDMap: nil}}
+ layerOptions = &LayerOptions{
+ IDMappingOptions: IDMappingOptions{
+ HostUIDMapping: true,
+ HostGIDMapping: true,
+ UIDMap: nil,
+ GIDMap: nil,
+ },
+ }
} else {
layerOptions = &LayerOptions{
IDMappingOptions: IDMappingOptions{
@@ -2091,10 +2101,10 @@ func (s *store) DeleteImage(id string, commit bool) (layers []string, err error)
break
}
lastRemoved = layer
- layersToRemove = append(layersToRemove, lastRemoved)
if layer == image.TopLayer {
layersToRemove = append(layersToRemove, image.MappedTopLayers...)
}
+ layersToRemove = append(layersToRemove, lastRemoved)
layer = parent
}
} else {
@@ -3064,9 +3074,6 @@ type OptionsConfig struct {
// Size
Size string `toml:"size"`
- // OverrideKernelCheck
- OverrideKernelCheck string `toml:"override_kernel_check"`
-
// RemapUIDs is a list of default UID mappings to use for layers.
RemapUIDs string `toml:"remap-uids"`
// RemapGIDs is a list of default GID mappings to use for layers.
@@ -3191,9 +3198,6 @@ func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions) {
if config.Storage.Options.MountOpt != "" {
storeOptions.GraphDriverOptions = append(storeOptions.GraphDriverOptions, fmt.Sprintf("%s.mountopt=%s", config.Storage.Driver, config.Storage.Options.MountOpt))
}
- if config.Storage.Options.OverrideKernelCheck != "" {
- storeOptions.GraphDriverOptions = append(storeOptions.GraphDriverOptions, fmt.Sprintf("%s.override_kernel_check=%s", config.Storage.Driver, config.Storage.Options.OverrideKernelCheck))
- }
if config.Storage.Options.RemapUser != "" && config.Storage.Options.RemapGroup == "" {
config.Storage.Options.RemapGroup = config.Storage.Options.RemapUser
}
diff --git a/vendor/github.com/varlink/go/.gitignore b/vendor/github.com/varlink/go/.gitignore
index 69e30cff1..a43b0e2d3 100644
--- a/vendor/github.com/varlink/go/.gitignore
+++ b/vendor/github.com/varlink/go/.gitignore
@@ -1 +1,2 @@
/cmd/varlink-go-certification/orgvarlinkcertification/orgvarlinkcertification.go
+/.idea
diff --git a/vendor/github.com/varlink/go/.travis.yml b/vendor/github.com/varlink/go/.travis.yml
index c090a924f..fa9963500 100644
--- a/vendor/github.com/varlink/go/.travis.yml
+++ b/vendor/github.com/varlink/go/.travis.yml
@@ -1,11 +1,13 @@
language: go
sudo: false
go:
-- '1.9'
-- 1.10.x
+- '1.10'
+- 1.11.x
install:
- go get golang.org/x/tools/cmd/cover
- go get github.com/mattn/goveralls
+- go get github.com/TylerBrock/colorjson
+- go get github.com/fatih/color
script:
- go generate ./...
- '"$HOME/gopath/bin/goveralls" -v -show -service=travis-ci -repotoken "$COVERALLS_TOKEN"'
diff --git a/vendor/github.com/varlink/go/cmd/varlink/main.go b/vendor/github.com/varlink/go/cmd/varlink/main.go
new file mode 100644
index 000000000..1de4e1a45
--- /dev/null
+++ b/vendor/github.com/varlink/go/cmd/varlink/main.go
@@ -0,0 +1,295 @@
+package main
+
+import (
+ "encoding/json"
+ "flag"
+ "fmt"
+ "github.com/TylerBrock/colorjson"
+ "github.com/fatih/color"
+ "github.com/varlink/go/varlink"
+ "os"
+ "strings"
+)
+
+var bold = color.New(color.Bold)
+var errorBoldRed = bold.Sprint(color.New(color.FgRed).Sprint("Error:"))
+var bridge string
+
+func ErrPrintf(format string, a ...interface{}) {
+ fmt.Fprintf(os.Stderr, "%s ", errorBoldRed)
+ fmt.Fprintf(os.Stderr, format, a...)
+}
+
+func print_usage(set *flag.FlagSet, arg_help string) {
+ if set == nil {
+ fmt.Fprintf(os.Stderr, "Usage: %s [GLOBAL OPTIONS] COMMAND ...\n", os.Args[0])
+ } else {
+ fmt.Fprintf(os.Stderr, "Usage: %s [GLOBAL OPTIONS] %s [OPTIONS] %s\n", os.Args[0], set.Name(), arg_help)
+ }
+
+ fmt.Fprintln(os.Stderr, "\nGlobal Options:")
+ flag.PrintDefaults()
+
+ if set == nil {
+ fmt.Fprintln(os.Stderr, "\nCommands:")
+ fmt.Fprintln(os.Stderr, " info\tPrint information about a service")
+ fmt.Fprintln(os.Stderr, " help\tPrint interface description or service information")
+ fmt.Fprintln(os.Stderr, " call\tCall a method")
+ } else {
+ fmt.Fprintln(os.Stderr, "\nOptions:")
+ set.PrintDefaults()
+ }
+ os.Exit(1)
+}
+
+func varlink_call(args []string) {
+ var err error
+ var oneway bool
+
+ callFlags := flag.NewFlagSet("help", flag.ExitOnError)
+ callFlags.BoolVar(&oneway, "-oneway", false, "Use bridge for connection")
+ var help bool
+ callFlags.BoolVar(&help, "help", false, "Prints help information")
+ var usage = func() { print_usage(callFlags, "<[ADDRESS/]INTERFACE.METHOD> [ARGUMENTS]") }
+ callFlags.Usage = usage
+
+ _ = callFlags.Parse(args)
+
+ if help {
+ usage()
+ }
+
+ var con *varlink.Connection
+ var address string
+ var methodName string
+
+ if len(bridge) != 0 {
+ con, err = varlink.NewBridge(bridge)
+
+ if err != nil {
+ ErrPrintf("Cannot connect with bridge '%s': %v\n", bridge, err)
+ os.Exit(2)
+ }
+ address = "bridge:" + bridge
+ methodName = callFlags.Arg(0)
+ } else {
+ uri := callFlags.Arg(0)
+ if uri == "" {
+ usage()
+ }
+
+ li := strings.LastIndex(uri, "/")
+
+ if li == -1 {
+ ErrPrintf("Invalid address '%s'\n", uri)
+ os.Exit(2)
+ }
+
+ address = uri[:li]
+ methodName = uri[li+1:]
+
+ con, err = varlink.NewConnection(address)
+
+ if err != nil {
+ ErrPrintf("Cannot connect to '%s': %v\n", address, err)
+ os.Exit(2)
+ }
+ }
+ var parameters string
+ var params json.RawMessage
+
+ parameters = callFlags.Arg(1)
+ if parameters == "" {
+ params = nil
+ } else {
+ json.Unmarshal([]byte(parameters), &params)
+ }
+
+ var flags uint64
+ flags = 0
+ if oneway {
+ flags |= varlink.Oneway
+ }
+ recv, err := con.Send(methodName, params, flags)
+
+ var retval map[string]interface{}
+
+ // FIXME: Use cont
+ _, err = recv(&retval)
+
+ f := colorjson.NewFormatter()
+ f.Indent = 2
+ f.KeyColor = color.New(color.FgCyan)
+ f.StringColor = color.New(color.FgMagenta)
+ f.NumberColor = color.New(color.FgMagenta)
+ f.BoolColor = color.New(color.FgMagenta)
+ f.NullColor = color.New(color.FgMagenta)
+
+ if err != nil {
+ ErrPrintf("Error calling '%s': %v\n", methodName, err)
+ switch e := err.(type) {
+ case *varlink.Error:
+ println(e.Name)
+ errorRawParameters := e.Parameters.(*json.RawMessage)
+
+ if errorRawParameters == nil {
+ break
+ }
+ var param map[string]interface{}
+ _ = json.Unmarshal(*errorRawParameters, &param)
+ c, _ := f.Marshal(param)
+ ErrPrintf("%v\n", string(c))
+ }
+ os.Exit(2)
+ }
+ c, _ := f.Marshal(retval)
+ fmt.Println(string(c))
+}
+
+func varlink_help(args []string) {
+ var err error
+
+ helpFlags := flag.NewFlagSet("help", flag.ExitOnError)
+ var help bool
+ helpFlags.BoolVar(&help, "help", false, "Prints help information")
+ var usage = func() { print_usage(helpFlags, "<[ADDRESS/]INTERFACE>") }
+ helpFlags.Usage = usage
+
+ _ = helpFlags.Parse(args)
+
+ if help {
+ usage()
+ }
+
+ var con *varlink.Connection
+ var address string
+ var interfaceName string
+
+ if len(bridge) != 0 {
+ con, err = varlink.NewBridge(bridge)
+
+ if err != nil {
+ ErrPrintf("Cannot connect with bridge '%s': %v\n", bridge, err)
+ os.Exit(2)
+ }
+ address = "bridge:" + bridge
+ interfaceName = helpFlags.Arg(0)
+ } else {
+ uri := helpFlags.Arg(0)
+ if uri == "" && bridge == "" {
+ ErrPrintf("No ADDRESS or activation or bridge\n\n")
+ usage()
+ }
+
+ li := strings.LastIndex(uri, "/")
+
+ if li == -1 {
+ ErrPrintf("Invalid address '%s'\n", uri)
+ os.Exit(2)
+ }
+
+ address = uri[:li]
+
+ con, err = varlink.NewConnection(address)
+
+ if err != nil {
+ ErrPrintf("Cannot connect to '%s': %v\n", address, err)
+ os.Exit(2)
+ }
+
+ interfaceName = uri[li+1:]
+ }
+ description, err := con.GetInterfaceDescription(interfaceName)
+
+ if err != nil {
+ ErrPrintf("Cannot get interface description for '%s': %v\n", interfaceName, err)
+ os.Exit(2)
+ }
+
+ fmt.Println(description)
+}
+
+func varlink_info(args []string) {
+ var err error
+ infoFlags := flag.NewFlagSet("info", flag.ExitOnError)
+ var help bool
+ infoFlags.BoolVar(&help, "help", false, "Prints help information")
+ var usage = func() { print_usage(infoFlags, "[ADDRESS]") }
+ infoFlags.Usage = usage
+
+ _ = infoFlags.Parse(args)
+
+ if help {
+ usage()
+ }
+
+ var con *varlink.Connection
+ var address string
+
+ if len(bridge) != 0 {
+ con, err = varlink.NewBridge(bridge)
+
+ if err != nil {
+ ErrPrintf("Cannot connect with bridge '%s': %v\n", bridge, err)
+ os.Exit(2)
+ }
+ address = "bridge:" + bridge
+ } else {
+ address = infoFlags.Arg(0)
+
+ if address == "" && bridge == "" {
+ ErrPrintf("No ADDRESS or activation or bridge\n\n")
+ usage()
+ }
+
+ con, err = varlink.NewConnection(address)
+
+ if err != nil {
+ ErrPrintf("Cannot connect to '%s': %v\n", address, err)
+ os.Exit(2)
+ }
+ }
+
+ var vendor, product, version, url string
+ var interfaces []string
+
+ err = con.GetInfo(&vendor, &product, &version, &url, &interfaces)
+
+ if err != nil {
+ ErrPrintf("Cannot get info for '%s': %v\n", address, err)
+ os.Exit(2)
+ }
+
+ fmt.Printf("%s %s\n", bold.Sprint("Vendor:"), vendor)
+ fmt.Printf("%s %s\n", bold.Sprint("Product:"), product)
+ fmt.Printf("%s %s\n", bold.Sprint("Version:"), version)
+ fmt.Printf("%s %s\n", bold.Sprint("URL:"), url)
+ fmt.Printf("%s\n %s\n\n", bold.Sprint("Interfaces:"), strings.Join(interfaces[:], "\n "))
+}
+
+func main() {
+ var debug bool
+ var colorMode string
+
+ flag.CommandLine.Usage = func() { print_usage(nil, "") }
+ flag.BoolVar(&debug, "debug", false, "Enable debug output")
+ flag.StringVar(&bridge, "bridge", "", "Use bridge for connection")
+ flag.StringVar(&colorMode, "color", "auto", "colorize output [default: auto] [possible values: on, off, auto]")
+
+ flag.Parse()
+
+ if colorMode != "on" && (os.Getenv("TERM") == "" || colorMode == "off") {
+ color.NoColor = true // disables colorized output
+ }
+
+ switch flag.Arg(0) {
+ case "info":
+ varlink_info(flag.Args()[1:])
+ case "help":
+ varlink_help(flag.Args()[1:])
+ case "call":
+ varlink_call(flag.Args()[1:])
+ default:
+ print_usage(nil, "")
+ }
+}
diff --git a/vendor/github.com/varlink/go/varlink/bridge.go b/vendor/github.com/varlink/go/varlink/bridge.go
new file mode 100644
index 000000000..b20c0925a
--- /dev/null
+++ b/vendor/github.com/varlink/go/varlink/bridge.go
@@ -0,0 +1,59 @@
+// +build !windows
+
+package varlink
+
+import (
+ "bufio"
+ "io"
+ "log"
+ "net"
+ "os/exec"
+)
+
+type PipeCon struct {
+ net.Conn
+ reader *io.ReadCloser
+ writer *io.WriteCloser
+}
+
+func (p PipeCon) Close() error {
+ err1 := (*p.reader).Close()
+ err2 := (*p.writer).Close()
+ if err1 != nil {
+ return err1
+ }
+ if err2 != nil {
+ return err2
+ }
+ return nil
+}
+
+// NewConnection returns a new connection to the given address.
+func NewBridge(bridge string) (*Connection, error) {
+ //var err error
+
+ c := Connection{}
+ cmd := exec.Command("sh", "-c", bridge)
+ r, err := cmd.StdoutPipe()
+ if err != nil {
+ return nil, err
+ }
+ w, err := cmd.StdinPipe()
+ if err != nil {
+ return nil, err
+ }
+ c.conn = PipeCon{nil, &r, &w}
+ c.address = ""
+ c.reader = bufio.NewReader(r)
+ c.writer = bufio.NewWriter(w)
+
+ go func() {
+ err := cmd.Run()
+ if err != nil {
+ log.Fatal(err)
+ }
+ }()
+
+ return &c, nil
+}
+
diff --git a/vendor/github.com/varlink/go/varlink/bridge_windows.go b/vendor/github.com/varlink/go/varlink/bridge_windows.go
new file mode 100644
index 000000000..692367a1a
--- /dev/null
+++ b/vendor/github.com/varlink/go/varlink/bridge_windows.go
@@ -0,0 +1,57 @@
+package varlink
+
+import (
+ "bufio"
+ "io"
+ "log"
+ "net"
+ "os/exec"
+)
+
+type PipeCon struct {
+ net.Conn
+ reader *io.ReadCloser
+ writer *io.WriteCloser
+}
+
+func (p PipeCon) Close() error {
+ err1 := (*p.reader).Close()
+ err2 := (*p.writer).Close()
+ if err1 != nil {
+ return err1
+ }
+ if err2 != nil {
+ return err2
+ }
+ return nil
+}
+
+// NewConnection returns a new connection to the given address.
+func NewBridge(bridge string) (*Connection, error) {
+ //var err error
+
+ c := Connection{}
+ cmd := exec.Command("cmd", "/C", bridge)
+ r, err := cmd.StdoutPipe()
+ if err != nil {
+ return nil, err
+ }
+ w, err := cmd.StdinPipe()
+ if err != nil {
+ return nil, err
+ }
+ c.conn = PipeCon{nil, &r, &w}
+ c.address = ""
+ c.reader = bufio.NewReader(r)
+ c.writer = bufio.NewWriter(w)
+
+ go func() {
+ err := cmd.Run()
+ if err != nil {
+ log.Fatal(err)
+ }
+ }()
+
+ return &c, nil
+}
+
diff --git a/vendor/github.com/varlink/go/varlink/connection.go b/vendor/github.com/varlink/go/varlink/connection.go
index 43bec6393..ac9542408 100644
--- a/vendor/github.com/varlink/go/varlink/connection.go
+++ b/vendor/github.com/varlink/go/varlink/connection.go
@@ -3,6 +3,7 @@ package varlink
import (
"bufio"
"encoding/json"
+ "fmt"
"net"
"strings"
)
@@ -18,6 +19,7 @@ const (
// Error is a varlink error returned from a method call.
type Error struct {
+ error
Name string
Parameters interface{}
}
@@ -189,6 +191,11 @@ func NewConnection(address string) (*Connection, error) {
var err error
words := strings.SplitN(address, ":", 2)
+
+ if len(words) != 2 {
+ return nil, fmt.Errorf("Protocol missing")
+ }
+
protocol := words[0]
addr := words[1]
diff --git a/vendor/github.com/varlink/go/varlink/service.go b/vendor/github.com/varlink/go/varlink/service.go
index 551ba4e53..cb461f917 100644
--- a/vendor/github.com/varlink/go/varlink/service.go
+++ b/vendor/github.com/varlink/go/varlink/service.go
@@ -6,10 +6,8 @@ import (
"fmt"
"net"
"os"
- "strconv"
"strings"
"sync"
- "syscall"
"time"
)
@@ -110,58 +108,6 @@ func (s *Service) handleMessage(writer *bufio.Writer, request []byte) error {
return iface.VarlinkDispatch(c, methodname)
}
-func activationListener() net.Listener {
- pid, err := strconv.Atoi(os.Getenv("LISTEN_PID"))
- if err != nil || pid != os.Getpid() {
- return nil
- }
-
- nfds, err := strconv.Atoi(os.Getenv("LISTEN_FDS"))
- if err != nil || nfds < 1 {
- return nil
- }
-
- fd := -1
-
- // If more than one file descriptor is passed, find the
- // "varlink" tag. The first file descriptor is always 3.
- if nfds > 1 {
- fdnames, set := os.LookupEnv("LISTEN_FDNAMES")
- if !set {
- return nil
- }
-
- names := strings.Split(fdnames, ":")
- if len(names) != nfds {
- return nil
- }
-
- for i, name := range names {
- if name == "varlink" {
- fd = 3 + i
- break
- }
- }
-
- if fd < 0 {
- return nil
- }
-
- } else {
- fd = 3
- }
-
- syscall.CloseOnExec(fd)
-
- file := os.NewFile(uintptr(fd), "varlink")
- listener, err := net.FileListener(file)
- if err != nil {
- return nil
- }
-
- return listener
-}
-
// Shutdown shuts down the listener of a running service.
func (s *Service) Shutdown() {
s.running = false
@@ -253,18 +199,17 @@ func getListener(protocol string, address string) (net.Listener, error) {
}
func (s *Service) refreshTimeout(timeout time.Duration) error {
- switch s.protocol {
- case "unix":
- if err := s.listener.(*net.UnixListener).SetDeadline(time.Now().Add(timeout)); err != nil {
+ switch l := s.listener.(type) {
+ case *net.UnixListener:
+ if err:= l.SetDeadline(time.Now().Add(timeout)); err != nil {
return err
}
-
- case "tcp":
- if err := s.listener.(*net.TCPListener).SetDeadline(time.Now().Add(timeout)); err != nil {
+ case *net.TCPListener:
+ if err:= l.SetDeadline(time.Now().Add(timeout)); err != nil {
return err
}
- }
+ }
return nil
}
diff --git a/vendor/github.com/varlink/go/varlink/socketactivation.go b/vendor/github.com/varlink/go/varlink/socketactivation.go
new file mode 100644
index 000000000..a64c0dc8e
--- /dev/null
+++ b/vendor/github.com/varlink/go/varlink/socketactivation.go
@@ -0,0 +1,63 @@
+// +build !windows
+
+package varlink
+
+import (
+ "net"
+ "os"
+ "strconv"
+ "strings"
+ "syscall"
+)
+
+func activationListener() net.Listener {
+ pid, err := strconv.Atoi(os.Getenv("LISTEN_PID"))
+ if err != nil || pid != os.Getpid() {
+ return nil
+ }
+
+ nfds, err := strconv.Atoi(os.Getenv("LISTEN_FDS"))
+ if err != nil || nfds < 1 {
+ return nil
+ }
+
+ fd := -1
+
+ // If more than one file descriptor is passed, find the
+ // "varlink" tag. The first file descriptor is always 3.
+ if nfds > 1 {
+ fdnames, set := os.LookupEnv("LISTEN_FDNAMES")
+ if !set {
+ return nil
+ }
+
+ names := strings.Split(fdnames, ":")
+ if len(names) != nfds {
+ return nil
+ }
+
+ for i, name := range names {
+ if name == "varlink" {
+ fd = 3 + i
+ break
+ }
+ }
+
+ if fd < 0 {
+ return nil
+ }
+
+ } else {
+ fd = 3
+ }
+
+ syscall.CloseOnExec(fd)
+
+ file := os.NewFile(uintptr(fd), "varlink")
+ listener, err := net.FileListener(file)
+ if err != nil {
+ return nil
+ }
+
+ return listener
+}
diff --git a/vendor/github.com/varlink/go/varlink/socketactivation_windows.go b/vendor/github.com/varlink/go/varlink/socketactivation_windows.go
new file mode 100644
index 000000000..fb0894531
--- /dev/null
+++ b/vendor/github.com/varlink/go/varlink/socketactivation_windows.go
@@ -0,0 +1,7 @@
+package varlink
+
+import "net"
+
+func activationListener() net.Listener {
+ return nil
+}