Remove varlink support from Podman

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
pull/8400/head
Daniel J Walsh 2020-11-18 16:12:33 -05:00
parent ad2439264d
commit f62a356515
No known key found for this signature in database
GPG Key ID: A2DF901DABE2C028
101 changed files with 26 additions and 17765 deletions

View File

@ -270,24 +270,6 @@ swagger_task:
always: *binary_artifacts
endpoint_task:
name: "Test Endpoint"
alias: endpoint
only_if: *not_docs
depends_on:
- build
container: *smallcontainer
env:
<<: *stdenvars
TEST_FLAVOR: endpoint
TEST_ENVIRON: container
CTR_FQIN: ${FEDORA_CONTAINER_FQIN}
clone_script: *full_clone # build-cache not available to container tasks
setup_script: *setup
main_script: *main
always: *runner_stats
# Check that all included go modules from other sources match
# what is expected in `vendor/modules.txt` vs `go.mod`.
vendor_task:
@ -327,12 +309,8 @@ alt_build_task:
ALT_NAME: 'Windows Cross'
- env:
ALT_NAME: 'Build Without CGO'
- env:
ALT_NAME: 'Build varlink-API'
- env:
ALT_NAME: 'Test build RPM'
- env:
ALT_NAME: 'Build varlink-binaries'
setup_script: *setup
main_script: *main
always: *binary_artifacts
@ -633,7 +611,6 @@ success_task:
- build
- validate
- bindings
- endpoint
- swagger
- vendor
- alt_build

3
.gitignore vendored
View File

@ -2,8 +2,6 @@
/bin/
/brew
/build/
/cmd/podman/varlink/iopodman.go
/cmd/podman/varlink/ioprojectatomicpodman.go
/conmon/
contrib/spec/podman.spec
*.coverprofile
@ -21,7 +19,6 @@ coverprofile
/_output/
/pause/pause.o
pkg/api/swagger.yaml
/pkg/varlink/iopodman.go
podman-remote*.zip
podman*.tar.gz
__pycache__

View File

@ -8,9 +8,6 @@ run:
- dependencies
- test
- pkg/spec
- pkg/varlink
- pkg/varlinkapi
- docs/varlink
- vendor
skip-files:
- iopodman.go

2211
API.md

File diff suppressed because it is too large Load Diff

View File

@ -40,9 +40,6 @@ SOURCES = $(shell find . -path './.*' -prune -o -name "*.go")
BUILDFLAGS := -mod=vendor $(BUILDFLAGS)
BUILDTAGS_CROSS ?= containers_image_openpgp exclude_graphdriver_btrfs exclude_graphdriver_devicemapper exclude_graphdriver_overlay
ifneq (,$(findstring varlink,$(BUILDTAGS)))
PODMAN_VARLINK_DEPENDENCIES = pkg/varlink/iopodman.go
endif
CONTAINER_RUNTIME := $(shell command -v podman 2> /dev/null || echo docker)
OCI_RUNTIME ?= ""
@ -149,10 +146,6 @@ ifeq ("$(wildcard $(GOPKGDIR))","")
mkdir -p "$(GOPKGBASEDIR)"
ln -sfn "$(CURDIR)" "$(GOPKGDIR)"
endif
ifneq (,$(findstring varlink,$(BUILDTAGS)))
ln -sfn "$(CURDIR)/vendor/github.com/varlink" "$(FIRST_GOPATH)/src/github.com/varlink"
endif
touch $@
.PHONY: lint
@ -165,7 +158,7 @@ endif
$(PRE_COMMIT) run -a
.PHONY: golangci-lint
golangci-lint: .gopathok varlink_generate .install.golangci-lint
golangci-lint: .gopathok .install.golangci-lint
hack/golangci-lint.sh run
.PHONY: gofmt
@ -189,7 +182,7 @@ test/goecho/goecho: .gopathok $(wildcard test/goecho/*.go)
.PHONY: bin/podman
bin/podman: .gopathok $(SOURCES) go.mod go.sum $(PODMAN_VARLINK_DEPENDENCIES) ## Build with podman
bin/podman: .gopathok $(SOURCES) go.mod go.sum ## Build with podman
# Make sure to warn in case we're building without the systemd buildtag.
ifeq (,$(findstring systemd,$(BUILDTAGS)))
@echo "Podman is being compiled without the systemd build tag. Install libsystemd on \
@ -201,7 +194,7 @@ endif
podman: bin/podman
.PHONY: bin/podman-remote
bin/podman-remote: .gopathok $(SOURCES) go.mod go.sum $(PODMAN_VARLINK_DEPENDENCIES) ## Build with podman on remote environment
bin/podman-remote: .gopathok $(SOURCES) go.mod go.sum ## Build with podman on remote environment
$(GO) build $(BUILDFLAGS) -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "${REMOTETAGS}" -o $@ ./cmd/podman
.PHONY: bin/podman-remote-static
@ -218,7 +211,7 @@ podman.msi: podman-remote podman-remote-windows install-podman-remote-windows-do
|wixl-heat --var var.ManSourceDir --component-group ManFiles --directory-ref INSTALLDIR --prefix $(DOCFILE)/ >$(DOCFILE)/pages.wsx
wixl -D VERSION=$(RELEASE_NUMBER) -D ManSourceDir=$(DOCFILE) -o podman-v$(RELEASE_NUMBER).msi contrib/msi/podman.wxs $(DOCFILE)/pages.wsx
podman-remote-%: .gopathok $(PODMAN_VARLINK_DEPENDENCIES) ## Build podman for a specific GOOS
podman-remote-%: .gopathok ## Build podman for a specific GOOS
$(eval BINSFX := $(shell test "$*" != "windows" || echo ".exe"))
CGO_ENABLED=0 GOOS=$* $(GO) build $(BUILDFLAGS) -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "${REMOTETAGS}" -o bin/$@$(BINSFX) ./cmd/podman
@ -267,7 +260,6 @@ clean: ## Clean artifacts
test/checkseccomp/checkseccomp \
test/goecho/goecho \
test/testdata/redis-image \
pkg/varlink/iopodman.go \
libpod/container_ffjson.go \
libpod/pod_ffjson.go \
libpod/container_easyjson.go \
@ -276,13 +268,13 @@ clean: ## Clean artifacts
make -C docs clean
.PHONY: localunit
localunit: test/goecho/goecho varlink_generate
localunit: test/goecho/goecho
hack/check_root.sh make localunit
rm -rf ${COVERAGE_PATH} && mkdir -p ${COVERAGE_PATH}
$(GOBIN)/ginkgo \
-r \
$(TESTFLAGS) \
--skipPackage test/e2e,pkg/apparmor,test/endpoint,pkg/bindings,hack \
--skipPackage test/e2e,pkg/apparmor,pkg/bindings,hack \
--cover \
--covermode atomic \
--coverprofile coverprofile \
@ -304,18 +296,11 @@ ginkgo:
ginkgo-remote:
$(GOBIN)/ginkgo -v $(TESTFLAGS) -tags "$(REMOTETAGS)" $(GINKGOTIMEOUT) -cover -flakeAttempts 3 -progress -trace -noColor test/e2e/.
.PHONY: endpoint
ifneq (,$(findstring varlink,$(BUILDTAGS)))
endpoint:
$(GOBIN)/ginkgo -v $(TESTFLAGS) -tags "$(BUILDTAGS)" $(GINKGOTIMEOUT) -cover -flakeAttempts 3 -progress -trace -noColor -debug test/endpoint/.
endpoint:
endif
.PHONY: localintegration
localintegration: varlink_generate test-binaries ginkgo
localintegration: test-binaries ginkgo
.PHONY: remoteintegration
remoteintegration: varlink_generate test-binaries ginkgo-remote
remoteintegration: test-binaries ginkgo-remote
.PHONY: localsystem
localsystem:
@ -370,7 +355,7 @@ system.test-binary: .install.ginkgo
$(GO) test -c ./test/system
.PHONY: binaries
binaries: varlink_generate podman podman-remote ## Build podman
binaries: podman podman-remote ## Build podman
.PHONY: install.catatonit
install.catatonit:
@ -543,23 +528,8 @@ install.docker: docker-docs
install ${SELINUXOPT} -m 755 -d ${DESTDIR}${SYSTEMDDIR} ${DESTDIR}${USERSYSTEMDDIR} ${DESTDIR}${TMPFILESDIR}
install ${SELINUXOPT} -m 644 contrib/systemd/system/podman-docker.conf -t ${DESTDIR}${TMPFILESDIR}
.PHONY: install.varlink
ifneq (,$(findstring varlink,$(BUILDTAGS)))
install.varlink:
install ${SELINUXOPT} -m 755 -d ${DESTDIR}${SYSTEMDDIR} ${DESTDIR}${USERSYSTEMDDIR}
install ${SELINUXOPT} -m 644 contrib/varlink/io.podman.socket ${DESTDIR}${SYSTEMDDIR}/io.podman.socket
install ${SELINUXOPT} -m 644 contrib/varlink/io.podman.socket ${DESTDIR}${USERSYSTEMDDIR}/io.podman.socket
install ${SELINUXOPT} -m 644 contrib/varlink/io.podman.service ${DESTDIR}${SYSTEMDDIR}/io.podman.service
# User units are ordered differently, we can't make the *system* multi-user.target depend on a user unit.
# For user units the default.target that's the default is fine.
sed -e 's,^WantedBy=.*,WantedBy=default.target,' < contrib/varlink/io.podman.service > ${DESTDIR}${USERSYSTEMDDIR}/io.podman.service
else
install.varlink:
endif
.PHONY: install.systemd
install.systemd: install.varlink
install.systemd:
install ${SELINUXOPT} -m 755 -d ${DESTDIR}${SYSTEMDDIR} ${DESTDIR}${USERSYSTEMDDIR}
# User services
install ${SELINUXOPT} -m 644 contrib/systemd/auto-update/podman-auto-update.service ${DESTDIR}${USERSYSTEMDDIR}/podman-auto-update.service
@ -640,20 +610,6 @@ endef
fi
# $BUILD_TAGS variable is used in hack/golangci-lint.sh
.PHONY: varlink_generate
ifneq (or $(findstring varlink,$(BUILDTAGS)),$(findstring varlink,$(BUILD_TAGS)))
varlink_generate: .gopathok pkg/varlink/iopodman.go ## Generate varlink
else
varlink_generate:
endif
.PHONY: varlink_api_generate
ifneq (,$(findstring varlink,$(BUILDTAGS)))
varlink_api_generate: .gopathok API.md
else
varlink_api_generate:
endif
.PHONY: install.libseccomp.sudo
install.libseccomp.sudo:
rm -rf ../../seccomp/libseccomp
@ -661,15 +617,6 @@ install.libseccomp.sudo:
cd ../../seccomp/libseccomp && git checkout --detach $(LIBSECCOMP_COMMIT) && ./autogen.sh && ./configure --prefix=/usr && make all && make install
pkg/varlink/iopodman.go: .gopathok pkg/varlink/io.podman.varlink
ifneq (,$(findstring Linux,$(shell uname -s)))
# Only generate the varlink code on Linux (see issue #4814).
$(GO) generate ./pkg/varlink/...
endif
API.md: pkg/varlink/io.podman.varlink
$(GO) generate ./docs/...
.PHONY: validate.completions
validate.completions: SHELL:=/usr/bin/env bash # Set shell to bash for this target
validate.completions:

View File

@ -176,10 +176,10 @@ you to manage and maintain those images and containers in a production environme
familiar container cli commands. For more details, see the
[Container Tools Guide](https://github.com/containers/buildah/tree/master/docs/containertools).
## Podman Legacy API (Varlink)
Podman offers a [Varlink-based API](https://github.com/containers/podman/blob/master/docs/tutorials/varlink_remote_client.md)
for remote management of containers. However, this API has been deprecated by the REST API.
Varlink support is in maintenance mode, and will be removed in a future release.
## Podman Former API (Varlink)
Podman formerly offered a [Varlink-based API](https://github.com/containers/podman/blob/master/docs/tutorials/varlink_remote_client.md)
for remote management of containers. However, this API was replaced by the REST API.
Varlink support has been removed as of the 3.0 release.
For more details, you can see [this blog](https://podman.io/blogs/2020/01/17/podman-new-api.html).
## Static Binary Builds

View File

@ -18,9 +18,6 @@ const DefaultRootAPIPath = "/run/podman/podman.sock"
// DefaultRootAPIAddress is the default address of the REST socket with unix: prefix
const DefaultRootAPIAddress = "unix:" + DefaultRootAPIPath
// DefaultVarlinkAddress is the default address of the varlink socket
const DefaultVarlinkAddress = "unix:/run/podman/io.podman"
type CliCommand struct {
Mode []entities.EngineMode
Command *cobra.Command

View File

@ -38,7 +38,6 @@ Enable a listening service for API access to Podman commands.
srvArgs = struct {
Timeout int64
Varlink bool
}{}
)
@ -55,9 +54,6 @@ func init() {
flags.Int64VarP(&srvArgs.Timeout, timeFlagName, "t", 5, "Time until the service session expires in seconds. Use 0 to disable the timeout")
_ = srvCmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone)
flags.BoolVar(&srvArgs.Varlink, "varlink", false, "Use legacy varlink service instead of REST. Unit of --time changes from seconds to milliseconds.")
_ = flags.MarkDeprecated("varlink", "valink API is deprecated.")
flags.SetNormalizeFunc(aliasTimeoutFlag)
}
@ -97,11 +93,6 @@ func service(cmd *cobra.Command, args []string) error {
Command: cmd,
}
if srvArgs.Varlink {
opts.Timeout = time.Duration(srvArgs.Timeout) * time.Millisecond
return registry.ContainerEngine().VarlinkService(registry.GetContext(), opts)
}
opts.Timeout = time.Duration(srvArgs.Timeout) * time.Second
return restService(opts, cmd.Flags(), registry.PodmanConfig())
}
@ -111,8 +102,7 @@ func resolveAPIURI(_url []string) (string, error) {
// 1) User input wins always
// 2) systemd socket activation
// 3) rootless honors XDG_RUNTIME_DIR
// 4) if varlink -- adapter.DefaultVarlinkAddress
// 5) lastly adapter.DefaultAPIAddress
// 4) lastly adapter.DefaultAPIAddress
if len(_url) == 0 {
if v, found := os.LookupEnv("PODMAN_SOCKET"); found {
@ -134,16 +124,11 @@ func resolveAPIURI(_url []string) (string, error) {
}
socketName := "podman.sock"
if srvArgs.Varlink {
socketName = "io.podman"
}
socketPath := filepath.Join(xdg, "podman", socketName)
if err := os.MkdirAll(filepath.Dir(socketPath), 0700); err != nil {
return "", err
}
return "unix:" + socketPath, nil
case srvArgs.Varlink:
return registry.DefaultVarlinkAddress, nil
default:
if err := os.MkdirAll(filepath.Dir(registry.DefaultRootAPIPath), 0700); err != nil {
return "", err

View File

@ -1,61 +0,0 @@
// +build linux,!remote
package system
import (
"time"
"github.com/containers/common/pkg/completion"
"github.com/containers/podman/v2/cmd/podman/registry"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/spf13/cobra"
)
var (
varlinkDescription = `Run varlink interface. Podman varlink listens on the specified unix domain socket for incoming connects.
Tools speaking varlink protocol can remotely manage pods, containers and images.
`
varlinkCmd = &cobra.Command{
Use: "varlink [options] [URI]",
Args: cobra.MinimumNArgs(1),
Short: "Run varlink interface",
Long: varlinkDescription,
RunE: varlinkE,
ValidArgsFunction: completion.AutocompleteDefault,
Deprecated: "Please see 'podman system service' for RESTful APIs",
Hidden: true,
Example: `podman varlink unix:/run/podman/io.podman
podman varlink --time 5000 unix:/run/podman/io.podman`,
}
varlinkArgs = struct {
Timeout int64
}{}
)
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
Command: varlinkCmd,
})
flags := varlinkCmd.Flags()
timeFlagName := "time"
flags.Int64VarP(&varlinkArgs.Timeout, timeFlagName, "t", 1000, "Time until the varlink session expires in milliseconds. Use 0 to disable the timeout")
_ = varlinkCmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone)
flags.SetNormalizeFunc(aliasTimeoutFlag)
}
func varlinkE(cmd *cobra.Command, args []string) error {
uri := registry.DefaultVarlinkAddress
if len(args) > 0 {
uri = args[0]
}
opts := entities.ServiceOptions{
URI: uri,
Timeout: time.Duration(varlinkArgs.Timeout) * time.Second,
Command: cmd,
}
return registry.ContainerEngine().VarlinkService(registry.GetContext(), opts)
}

View File

@ -94,7 +94,6 @@
| [podman-umount(1)](https://podman.readthedocs.io/en/latest/markdown/podman-umount.1.html) | Unmount a working container's root filesystem |
| [podman-unpause(1)](https://podman.readthedocs.io/en/latest/markdown/podman-unpause.1.html) | Unpause one or more containers | [![...](/docs/source/markdown/play.png)](https://podman.io/asciinema/podman/pause_unpause/) | [Here](https://github.com/containers/Demos/blob/master/podman_cli/podman_pause_unpause.sh) |
| [podman-unshare(1)](https://podman.readthedocs.io/en/latest/markdown/podman-unshare.1.html) | Run a command inside of a modified user namespace |
| [podman-varlink(1)](https://podman.readthedocs.io/en/latest/markdown/podman-varlink.1.html) | Runs the varlink backend interface |
| [podman-version(1)](https://podman.readthedocs.io/en/latest/markdown/podman-version.1.html) | Display the Podman version information |
| [podman-volume(1)](https://podman.readthedocs.io/en/latest/volume.html) | Manage Volumes |
| [podman-volume-create(1)](https://podman.readthedocs.io/en/latest/markdown/podman-volume-create.1.html) | Create a new volume |

View File

@ -49,7 +49,6 @@ if [[ $pkg_manager == *dnf ]]; then
fi
PKGS+=(python3-devel \
python3-varlink \
)
fi

View File

@ -71,8 +71,8 @@ SCRIPT_BASE=${SCRIPT_BASE:-./contrib/cirrus}
# Downloaded, but not installed packages.
PACKAGE_DOWNLOAD_DIR=/var/cache/download
# Log remote-client system test varlink output here
PODMAN_SERVER_LOG=$CIRRUS_WORKING_DIR/varlink.log
# Log remote-client system test server output here
PODMAN_SERVER_LOG=$CIRRUS_WORKING_DIR/server.log
# Defaults when not running under CI
export CI="${CI:-false}"

View File

@ -176,14 +176,6 @@ function _run_altbuild() {
*Without*)
make build-no-cgo
;;
*varlink-API)
export SUGGESTION='remove API.md, then "make varlink_api_generate" and commit changes.'
make varlink_api_generate BUILDTAGS="varlink"
./hack/tree_status.sh
;;
*varlink-binaries)
make clean BUILDTAGS="varlink" binaries
;;
*RPM*)
make -f ./.copr/Makefile
rpmbuild --rebuild ./podman-*.src.rpm

View File

@ -24,7 +24,6 @@ python3-pip
python3-psutil
python3-pytoml
python3-pyyaml
python3-varlink
rsync
slirp4netns
unzip

View File

@ -166,7 +166,6 @@ Provides: bundled(golang(github.com/tchap/go-patricia)) = v2.2.6
Provides: bundled(golang(github.com/ulikunitz/xz)) = v0.5.4
# "-" are not accepted in version strings, so comment out below line
#Provides: bundled(golang(github.com/urfave/cli)) = fix-short-opts-parsing
Provides: bundled(golang(github.com/varlink/go)) = master
Provides: bundled(golang(github.com/vbatts/tar-split)) = v0.10.2
Provides: bundled(golang(github.com/vishvananda/netlink)) = master
Provides: bundled(golang(github.com/vishvananda/netns)) = master

View File

@ -36,20 +36,13 @@ BuildArch: noarch
BuildRequires: git
BuildRequires: python3-devel
BuildRequires: python3-setuptools
BuildRequires: python3-varlink
Requires: python3-humanize
Requires: python3-pytoml
Requires: python3-setuptools
Requires: python3-varlink
Requires: python3-psutil
Requires: podman
%if 0%{?fedora}
# 2018-07-20 RHEL8 doesn't have varlink RPM yet
Requires: python3-varlink
%endif
Provides: %{name} = %{version}-%{release}
%description

View File

@ -1,15 +0,0 @@
[Unit]
Description=Podman Remote API Service
Requires=io.podman.socket
After=io.podman.socket
Documentation=man:podman-varlink(1)
[Service]
Type=simple
ExecStart=/usr/bin/podman varlink unix:%t/podman/io.podman --timeout=60000
TimeoutStopSec=30
KillMode=process
[Install]
WantedBy=multi-user.target
Also=io.podman.socket

View File

@ -1,10 +0,0 @@
[Unit]
Description=Podman Remote API Socket
Documentation=man:podman-varlink(1)
[Socket]
ListenStream=%t/podman/io.podman
SocketMode=0600
[Install]
WantedBy=sockets.target

View File

@ -29,7 +29,6 @@ WORK=/tmp/go-build794287815
2.7M github.com/containers/podman/vendor/github.com/gogo/protobuf/proto
2.5M github.com/containers/podman/vendor/k8s.io/apimachinery/pkg/apis/meta/v1
2.3M github.com/containers/podman/vendor/github.com/vishvananda/netlink
2.1M github.com/containers/podman/cmd/podman/varlink
```
The output of the `go-archive-analysis.sh` script is a sorted table with the size in bytes followed by the package.

View File

@ -2,5 +2,4 @@ package dependencies
import (
_ "github.com/onsi/ginkgo/ginkgo"
_ "github.com/varlink/go/cmd/varlink-go-interface-generator" // Note: this file is used to trick `go mod` into vendoring the command below.
)

View File

@ -1,3 +0,0 @@
package docs
//go:generate go run varlink/apidoc.go ../pkg/varlink/io.podman.varlink ../API.md

View File

@ -52,10 +52,6 @@ Potential skew between multiple libpod versions operating on the same storage ca
.RE
.SH Varlink
.PP
Some code exists for this; splits the difference. Future uncertain.
.SH Making the choice
.PP
A good question to ask first is: Do you want users to be able to use \fB\fCpodman\fR to manipulate the containers created by your project?

View File

@ -46,11 +46,6 @@ Disadvantages:
- Binary size
- Potential skew between multiple libpod versions operating on the same storage can cause problems
Varlink
---
The Varlink API is presently deprecated. We do not recommend adopting it for new projects.
Making the choice
---

View File

@ -1,89 +0,0 @@
# Podman varlink remote-client tutorial [DEPRECATED]
## What is the varlink client
This API has been deprecated by the [REST API](https://docs.podman.io/en/latest/_static/api.html).
For usage on Windows and Mac, please reference the [Podman Mac/Windows tutorial](https://github.com/containers/podman/blob/master/docs/tutorials/mac_win_client.md)
Varlink support is in maintenance mode, and will be removed in a future release.
For more details, you can see [this blog](https://podman.io/blogs/2020/01/17/podman-new-api.html).
The purpose of the Podman remote-client is to allow users to interact with a Podman "backend"
while on a separate client. The command line interface of the remote client is exactly the
same as the regular Podman commands with the exception of some flags being removed as they
do not apply to the remote-client.
## What you need
To use the remote-client, you will need a binary for your client and a Podman "backend"; hereafter
referred to as the Podman node. In this context, a Podman node is a Linux system with Podman
installed on it and the varlink service activated. You will also need to be able to ssh into this
system as a user with privileges to the varlink socket (more on this later).
## Building the remote client
At this time, the Podman remote-client is not being packaged for any distribution. It must be built from
source. To set up your build environment, see [Installation notes](https://github.com/containers/podman/blob/master/install.md) and follow the
section [Building from scratch](https://github.com/containers/podman/blob/master/install.md#building-from-scratch). Once you can successfully
build the regular Podman binary, you can now build the remote-client.
```
$ make podman-remote
```
Like building the regular Podman, the resulting binary will be in the *bin* directory. This is the binary
you will run on the remote node later in the instructions.
## Setting up the remote and Podman nodes
To use the remote-client, you must perform some setup on both the remote and Podman nodes. In this case,
the remote node refers to where the remote-client is being run; and the Podman node refers to where
Podman and its storage reside.
### Podman node setup
Varlink bridge support is provided by the varlink cli command and installed using:
```
$ sudo dnf install varlink-cli
```
The Podman node must have Podman (not the remote-client) installed as normal. If your system uses systemd,
then simply start the Podman varlink socket.
```
$ sudo systemctl start io.podman.socket
```
If your system cannot use systemd, then you can manually establish the varlink socket with the Podman
command:
```
$ sudo podman --log-level debug varlink --timeout 0 unix://run/podman/io.podman
```
### Required permissions
For now, the remote-client requires that you be able to run a privileged Podman and have privileged ssh
access to the remote system. This limitation is being worked on.
### Remote node setup
#### Initiate an ssh session to the Podman node
To use the remote client, an ssh connection to the Podman server must be established.
Using the varlink bridge, an ssh tunnel must be initiated to connect to the server. Podman must then be informed of the location of the sshd server on the targeted server
```
$ export PODMAN_VARLINK_BRIDGE=$'ssh -T -p22 root@remotehost -- "varlink -A \'podman varlink \$VARLINK_ADDRESS\' bridge"'
$ bin/podman-remote images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/library/ubuntu latest 47b19964fb50 2 weeks ago 90.7 MB
docker.io/library/alpine latest caf27325b298 3 weeks ago 5.8 MB
quay.io/cevich/gcloud_centos latest 641dad61989a 5 weeks ago 489 MB
k8s.gcr.io/pause 3.1 da86e6ba6ca1 14 months ago 747 kB
```
The PODMAN_VARLINK_BRIDGE variable may be added to your log in settings. It does not change per connection.
If coming from a Windows machine, the PODMAN_VARLINK_BRIDGE is formatted as:
```
set PODMAN_VARLINK_BRIDGE=C:\Windows\System32\OpenSSH\ssh.exe -T -p22 root@remotehost -- varlink -A "podman varlink $VARLINK_ADDRESS" bridge
```
The arguments before the `--` are presented to ssh while the arguments after are for the varlink cli. The varlink arguments should be copied verbatim.
- `-p` is the port on the remote host for the ssh tunnel. `22` is the default.
- `root` is the currently supported user, while `remotehost` is the name or IP address of the host providing the Podman service.
- `-i` may be added to select an identity file.

View File

@ -1,279 +0,0 @@
package main
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"sort"
"strings"
"github.com/varlink/go/varlink/idl"
)
func readFileToString(path string) (string, error) {
content, err := ioutil.ReadFile(path)
if err != nil {
return "", err
}
return string(content), nil
}
func exit(err error) {
fmt.Println(err.Error())
os.Exit(1)
}
func typeToString(input *idl.Type) string {
switch input.Kind {
case idl.TypeString:
return "string"
case idl.TypeBool:
return "bool"
case idl.TypeFloat:
return "float"
case idl.TypeArray:
result := input.ElementType.Alias
if result == "" {
return fmt.Sprintf("[]%s", typeToString(input.ElementType))
}
return result
case idl.TypeAlias:
return input.Alias
case idl.TypeMap:
return "map[string]"
case idl.TypeInt:
return "int"
case idl.TypeMaybe:
return fmt.Sprintf("?%s", typeToString(input.ElementType))
}
return ""
}
func typeToLink(input string) string {
switch input {
case "string":
return "https://godoc.org/builtin#string"
case "int":
return "https://godoc.org/builtin#int"
case "bool":
return "https://godoc.org/builtin#bool"
case "float":
return "https://golang.org/src/builtin/builtin.go#L58"
default:
return fmt.Sprintf("#%s", input)
}
}
type funcArgs struct {
paramName string
paramKind string
}
type funcDescriber struct {
Name string
inputParams []funcArgs
returnParams []funcArgs
doc string
}
type funcSorter []funcDescriber
func (a funcSorter) Len() int { return len(a) }
func (a funcSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a funcSorter) Less(i, j int) bool { return a[i].Name < a[j].Name }
type typeAttrs struct {
Name string
AttrType string
}
type typeDescriber struct {
Name string
doc string
Attrs []typeAttrs
}
type typeSorter []typeDescriber
func (a typeSorter) Len() int { return len(a) }
func (a typeSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a typeSorter) Less(i, j int) bool { return a[i].Name < a[j].Name }
type err struct {
Name string
doc string
}
type errorSorter []err
func (a errorSorter) Len() int { return len(a) }
func (a errorSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a errorSorter) Less(i, j int) bool { return a[i].Name < a[j].Name }
// collects defined types in the idl
func getTypes(tidl *idl.IDL) []typeDescriber {
var types []typeDescriber
for _, x := range tidl.Aliases {
i := typeDescriber{
Name: x.Name,
doc: x.Doc,
}
ta := []typeAttrs{}
for _, y := range x.Type.Fields {
result := typeToString(y.Type)
ta = append(ta, typeAttrs{Name: y.Name, AttrType: result})
}
i.Attrs = ta
types = append(types, i)
}
return types
}
// collects defined methods in the idl
func getMethods(midl *idl.IDL) []funcDescriber {
var methods []funcDescriber
for _, t := range midl.Methods {
m := funcDescriber{
Name: t.Name,
doc: t.Doc,
}
fa := []funcArgs{}
fo := []funcArgs{}
for _, i := range t.In.Fields {
fa = append(fa, funcArgs{paramName: i.Name, paramKind: typeToString(i.Type)})
}
for _, f := range t.Out.Fields {
fo = append(fo, funcArgs{paramName: f.Name, paramKind: typeToString(f.Type)})
}
m.inputParams = fa
m.returnParams = fo
methods = append(methods, m)
}
return methods
}
// collects defined errors in the idl
func getErrors(midl *idl.IDL) []err {
var errors []err
for _, e := range midl.Errors {
myError := err{
Name: e.Name,
doc: e.Doc,
}
errors = append(errors, myError)
}
return errors
}
// generates the index for the top of the markdown page
func generateIndex(methods []funcDescriber, types []typeDescriber, errors []err, b bytes.Buffer) bytes.Buffer {
// Sort the methods, types, and errors by alphabetical order
sort.Sort(funcSorter(methods))
sort.Sort(typeSorter(types))
sort.Sort(errorSorter(errors))
for _, method := range methods {
var inArgs []string
var outArgs []string
for _, inArg := range method.inputParams {
inArgs = append(inArgs, fmt.Sprintf("%s: %s", inArg.paramName, inArg.paramKind))
}
for _, outArg := range method.returnParams {
outArgs = append(outArgs, outArg.paramKind)
}
b.WriteString(fmt.Sprintf("\n[func %s(%s) %s](#%s)\n", method.Name, strings.Join(inArgs, ", "), strings.Join(outArgs, ", "), method.Name))
}
b.WriteString("\n")
for _, t := range types {
b.WriteString(fmt.Sprintf("[type %s](#%s)\n\n", t.Name, t.Name))
}
for _, e := range errors {
b.WriteString(fmt.Sprintf("[error %s](#%s)\n\n", e.Name, e.Name))
}
return b
}
// performs the output for defined methods
func generateFuncDescriptions(methods []funcDescriber, b bytes.Buffer) bytes.Buffer {
for _, method := range methods {
b.WriteString(fmt.Sprintf("### <a name=\"%s\"></a>func %s\n", method.Name, method.Name))
var inArgs []string
var outArgs []string
for _, inArg := range method.inputParams {
inArgs = append(inArgs, fmt.Sprintf("%s: [%s](%s)", inArg.paramName, inArg.paramKind, typeToLink(inArg.paramKind)))
}
for _, outArg := range method.returnParams {
outArgs = append(outArgs, fmt.Sprintf("[%s](%s)", outArg.paramKind, typeToLink(outArg.paramKind)))
}
b.WriteString(fmt.Sprintf("<div style=\"background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;\">\n\nmethod %s(%s) %s</div>", method.Name, strings.Join(inArgs, ", "), strings.Join(outArgs, ", ")))
b.WriteString("\n")
b.WriteString(method.doc)
b.WriteString("\n")
}
return b
}
// performs the output for defined types/structs
func generateTypeDescriptions(types []typeDescriber, b bytes.Buffer) bytes.Buffer {
for _, t := range types {
b.WriteString(fmt.Sprintf("### <a name=\"%s\"></a>type %s\n", t.Name, t.Name))
b.WriteString(fmt.Sprintf("\n%s\n", t.doc))
for _, i := range t.Attrs {
b.WriteString(fmt.Sprintf("\n%s [%s](%s)\n", i.Name, i.AttrType, typeToLink(i.AttrType)))
}
}
return b
}
// performs the output for defined errors
func generateErrorDescriptions(errors []err, b bytes.Buffer) bytes.Buffer {
for _, e := range errors {
b.WriteString(fmt.Sprintf("### <a name=\"%s\"></a>type %s\n", e.Name, e.Name))
b.WriteString(fmt.Sprintf("\n%s\n", e.doc))
}
return b
}
func main() {
args := os.Args
if len(args) < 2 {
exit(fmt.Errorf("you must provide an input and output path"))
}
varlinkFile := args[1]
mdFile := args[2]
varlinkInput, err := readFileToString(varlinkFile)
if err != nil {
exit(err)
}
varlinkInput = strings.TrimRight(varlinkInput, "\n")
// Run the idl parser
midl, err := idl.New(varlinkInput)
if err != nil {
exit(err)
}
// Collect up the info from the idl
methods := getMethods(midl)
types := getTypes(midl)
errors := getErrors(midl)
out := bytes.Buffer{}
out.WriteString(fmt.Sprintf("# %s\n", midl.Name))
out.WriteString(fmt.Sprintf("%s\n", midl.Doc))
out.WriteString("## Index\n")
out = generateIndex(methods, types, errors, out)
out.WriteString("## Methods\n")
out = generateFuncDescriptions(methods, out)
out.WriteString("## Types\n")
out = generateTypeDescriptions(types, out)
out.WriteString("## Errors\n")
out = generateErrorDescriptions(errors, out)
if err := ioutil.WriteFile(mdFile, out.Bytes(), 0755); err != nil {
fmt.Fprintf(os.Stderr, "Error writing file: %v\n", err)
os.Exit(1)
}
}

1
go.mod
View File

@ -57,7 +57,6 @@ require (
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2
github.com/uber/jaeger-client-go v2.25.0+incompatible
github.com/uber/jaeger-lib v2.2.0+incompatible // indirect
github.com/varlink/go v0.0.0-20190502142041-0f1d566d194b
github.com/vishvananda/netlink v1.1.0
go.etcd.io/bbolt v1.3.5
go.uber.org/atomic v1.7.0 // indirect

2
go.sum
View File

@ -544,8 +544,6 @@ github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oW
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/varlink/go v0.0.0-20190502142041-0f1d566d194b h1:hdDRrn9OP/roL8a/e/5Zu85ldrcdndu9IeBj2OEvQm0=
github.com/varlink/go v0.0.0-20190502142041-0f1d566d194b/go.mod h1:YHaw8N660ESgMgLOZfLQqT1htFItynAUxMesFBho52s=
github.com/vbatts/tar-split v0.11.1 h1:0Odu65rhcZ3JZaPHxl7tCI3V/C/Q9Zf82UFravl02dE=
github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g=
github.com/vbauerster/mpb/v5 v5.3.0 h1:vgrEJjUzHaSZKDRRxul5Oh4C72Yy/5VEMb0em+9M0mQ=

View File

@ -6,8 +6,8 @@ set -e
declare -A BUILD_TAGS
# TODO: add systemd tag
BUILD_TAGS[default]="apparmor,seccomp,selinux"
BUILD_TAGS[abi]="${BUILD_TAGS[default]},varlink,!remoteclient"
BUILD_TAGS[tunnel]="${BUILD_TAGS[default]},remote,varlink,remoteclient"
BUILD_TAGS[abi]="${BUILD_TAGS[default]},!remoteclient"
BUILD_TAGS[tunnel]="${BUILD_TAGS[default]},remote,remoteclient"
declare -A SKIP_DIRS
SKIP_DIRS[abi]=""

View File

@ -176,9 +176,6 @@ sub xref_by_man {
# Special case: weirdness with Cobra and global/local options
next if $k eq '--namespace' && $man =~ /-ps/;
# Special case: these require compiling with 'varlink' tag,
# which doesn't happen in CI gating task.
next if $k eq 'varlink';
next if "@subcommand" eq 'system' && $k eq 'service';
# Special case: podman completion is a hidden command

View File

@ -80,8 +80,8 @@ func (c *Container) Top(descriptors []string) ([]string, error) {
func (c *Container) GetContainerPidInformation(descriptors []string) ([]string, error) {
pid := strconv.Itoa(c.state.PID)
// TODO: psgo returns a [][]string to give users the ability to apply
// filters on the data. We need to change the API here and the
// varlink API to return a [][]string if we want to make use of
// filters on the data. We need to change the API here
// to return a [][]string if we want to make use of
// filtering.
opts := psgo.JoinNamespaceOpts{FillMappings: rootless.IsRootless()}

View File

@ -95,10 +95,6 @@ type Type string
type Status string
const (
// If you add or subtract any values to the following lists, make sure you also update
// the switch statements below and the enums for EventType or EventStatus in the
// varlink description file.
// Container - event is related to containers
Container Type = "container"
// Image - event is related to images

View File

@ -53,9 +53,8 @@ func (p *Pod) GetPodPidInformation(descriptors []string) ([]string, error) {
}
// TODO: psgo returns a [][]string to give users the ability to apply
// filters on the data. We need to change the API here and the
// varlink API to return a [][]string if we want to make use of
// filtering.
// filters on the data. We need to change the API here to return
// a [][]string if we want to make use of filtering.
opts := psgo.JoinNamespaceOpts{FillMappings: rootless.IsRootless()}
output, err := psgo.JoinNamespaceAndProcessInfoByPidsWithOptions(pids, psgoDescriptors, &opts)
if err != nil {

View File

@ -1,14 +1,11 @@
package utils
import (
"context"
"net/http"
"time"
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/domain/entities"
createconfig "github.com/containers/podman/v2/pkg/spec"
"github.com/gorilla/schema"
"github.com/pkg/errors"
)
@ -59,18 +56,3 @@ func WaitContainer(w http.ResponseWriter, r *http.Request) (int32, error) {
}
return con.WaitForConditionWithInterval(interval, condition)
}
func CreateContainer(ctx context.Context, w http.ResponseWriter, runtime *libpod.Runtime, cc *createconfig.CreateConfig) {
var pod *libpod.Pod
ctr, err := createconfig.CreateContainerFromCreateConfig(ctx, runtime, cc, pod)
if err != nil {
Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "CreateContainerFromCreateConfig()"))
return
}
response := entities.ContainerCreateResponse{
ID: ctr.ID(),
Warnings: []string{}}
WriteResponse(w, http.StatusCreated, response)
}

View File

@ -75,7 +75,6 @@ type ContainerEngine interface {
Shutdown(ctx context.Context)
SystemDf(ctx context.Context, options SystemDfOptions) (*SystemDfReport, error)
Unshare(ctx context.Context, args []string) error
VarlinkService(ctx context.Context, opts ServiceOptions) error
Version(ctx context.Context) (*SystemVersionReport, error)
VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IDOrNameResponse, error)
VolumeInspect(ctx context.Context, namesOrIds []string, opts InspectOptions) ([]*VolumeInspectReport, []error, error)

View File

@ -1,14 +0,0 @@
// +build !varlink
package abi
import (
"context"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/pkg/errors"
)
func (ic *ContainerEngine) VarlinkService(_ context.Context, opts entities.ServiceOptions) error {
return errors.Errorf("varlink is not supported")
}

View File

@ -1,49 +0,0 @@
// +build varlink
package abi
import (
"context"
"github.com/containers/podman/v2/pkg/domain/entities"
iopodman "github.com/containers/podman/v2/pkg/varlink"
iopodmanAPI "github.com/containers/podman/v2/pkg/varlinkapi"
"github.com/containers/podman/v2/version"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/varlink/go/varlink"
)
func (ic *ContainerEngine) VarlinkService(_ context.Context, opts entities.ServiceOptions) error {
var varlinkInterfaces = []*iopodman.VarlinkInterface{
iopodmanAPI.New(opts.Command, ic.Libpod),
}
service, err := varlink.NewService(
"Atomic",
"podman",
version.Version.String(),
"https://github.com/containers/podman",
)
if err != nil {
return errors.Wrapf(err, "unable to create new varlink service")
}
for _, i := range varlinkInterfaces {
if err := service.RegisterInterface(i); err != nil {
return errors.Errorf("unable to register varlink interface %v", i)
}
}
// Run the varlink server at the given address
if err = service.Listen(opts.URI, opts.Timeout); err != nil {
switch err.(type) {
case varlink.ServiceTimeoutError:
logrus.Infof("varlink service expired (use --time to increase session time beyond %s ms, 0 means never timeout)", opts.Timeout.String())
return nil
default:
return errors.Wrapf(err, "unable to start varlink service")
}
}
return nil
}

View File

@ -14,10 +14,6 @@ func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) {
return system.Info(ic.ClientCxt)
}
func (ic *ContainerEngine) VarlinkService(_ context.Context, _ entities.ServiceOptions) error {
panic(errors.New("varlink service is not supported when tunneling"))
}
func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) error {
panic(errors.New("rootless engine mode is not supported when tunneling"))
}

View File

@ -1,371 +0,0 @@
// +build linux
package createconfig
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/containers/podman/v2/pkg/rootless"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/devices"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
// Device transforms a libcontainer configs.Device to a specs.LinuxDevice object.
func Device(d *configs.Device) spec.LinuxDevice {
return spec.LinuxDevice{
Type: string(d.Type),
Path: d.Path,
Major: d.Major,
Minor: d.Minor,
FileMode: fmPtr(int64(d.FileMode)),
UID: u32Ptr(int64(d.Uid)),
GID: u32Ptr(int64(d.Gid)),
}
}
// DevicesFromPath computes a list of devices
func DevicesFromPath(g *generate.Generator, devicePath string) error {
devs := strings.Split(devicePath, ":")
resolvedDevicePath := devs[0]
// check if it is a symbolic link
if src, err := os.Lstat(resolvedDevicePath); err == nil && src.Mode()&os.ModeSymlink == os.ModeSymlink {
if linkedPathOnHost, err := filepath.EvalSymlinks(resolvedDevicePath); err == nil {
resolvedDevicePath = linkedPathOnHost
}
}
st, err := os.Stat(resolvedDevicePath)
if err != nil {
return errors.Wrapf(err, "cannot stat %s", devicePath)
}
if st.IsDir() {
found := false
src := resolvedDevicePath
dest := src
var devmode string
if len(devs) > 1 {
if len(devs[1]) > 0 && devs[1][0] == '/' {
dest = devs[1]
} else {
devmode = devs[1]
}
}
if len(devs) > 2 {
if devmode != "" {
return errors.Wrapf(unix.EINVAL, "invalid device specification %s", devicePath)
}
devmode = devs[2]
}
// mount the internal devices recursively
if err := filepath.Walk(resolvedDevicePath, func(dpath string, f os.FileInfo, e error) error {
if f.Mode()&os.ModeDevice == os.ModeDevice {
found = true
device := fmt.Sprintf("%s:%s", dpath, filepath.Join(dest, strings.TrimPrefix(dpath, src)))
if devmode != "" {
device = fmt.Sprintf("%s:%s", device, devmode)
}
if err := addDevice(g, device); err != nil {
return errors.Wrapf(err, "failed to add %s device", dpath)
}
}
return nil
}); err != nil {
return err
}
if !found {
return errors.Wrapf(unix.EINVAL, "no devices found in %s", devicePath)
}
return nil
}
return addDevice(g, strings.Join(append([]string{resolvedDevicePath}, devs[1:]...), ":"))
}
func deviceCgroupRules(g *generate.Generator, deviceCgroupRules []string) error {
for _, deviceCgroupRule := range deviceCgroupRules {
if err := validateDeviceCgroupRule(deviceCgroupRule); err != nil {
return err
}
ss := parseDeviceCgroupRule(deviceCgroupRule)
if len(ss[0]) != 5 {
return errors.Errorf("invalid device cgroup rule format: '%s'", deviceCgroupRule)
}
matches := ss[0]
var major, minor *int64
if matches[2] == "*" {
majorDev := int64(-1)
major = &majorDev
} else {
majorDev, err := strconv.ParseInt(matches[2], 10, 64)
if err != nil {
return errors.Errorf("invalid major value in device cgroup rule format: '%s'", deviceCgroupRule)
}
major = &majorDev
}
if matches[3] == "*" {
minorDev := int64(-1)
minor = &minorDev
} else {
minorDev, err := strconv.ParseInt(matches[2], 10, 64)
if err != nil {
return errors.Errorf("invalid major value in device cgroup rule format: '%s'", deviceCgroupRule)
}
minor = &minorDev
}
g.AddLinuxResourcesDevice(true, matches[1], major, minor, matches[4])
}
return nil
}
func addDevice(g *generate.Generator, device string) error {
src, dst, permissions, err := ParseDevice(device)
if err != nil {
return err
}
dev, err := devices.DeviceFromPath(src, permissions)
if err != nil {
return errors.Wrapf(err, "%s is not a valid device", src)
}
if rootless.IsRootless() {
if _, err := os.Stat(src); err != nil {
if os.IsNotExist(err) {
return errors.Wrapf(err, "the specified device %s doesn't exist", src)
}
return errors.Wrapf(err, "stat device %s exist", src)
}
perm := "ro"
if strings.Contains(permissions, "w") {
perm = "rw"
}
devMnt := spec.Mount{
Destination: dst,
Type: TypeBind,
Source: src,
Options: []string{"slave", "nosuid", "noexec", perm, "rbind"},
}
g.Config.Mounts = append(g.Config.Mounts, devMnt)
return nil
}
dev.Path = dst
linuxdev := spec.LinuxDevice{
Path: dev.Path,
Type: string(dev.Type),
Major: dev.Major,
Minor: dev.Minor,
FileMode: &dev.FileMode,
UID: &dev.Uid,
GID: &dev.Gid,
}
g.AddDevice(linuxdev)
g.AddLinuxResourcesDevice(true, string(dev.Type), &dev.Major, &dev.Minor, string(dev.Permissions))
return nil
}
// based on getDevices from runc (libcontainer/devices/devices.go)
func getDevices(path string) ([]*configs.Device, error) {
files, err := ioutil.ReadDir(path)
if err != nil {
if rootless.IsRootless() && os.IsPermission(err) {
return nil, nil
}
return nil, err
}
out := []*configs.Device{}
for _, f := range files {
switch {
case f.IsDir():
switch f.Name() {
// ".lxc" & ".lxd-mounts" added to address https://github.com/lxc/lxd/issues/2825
case "pts", "shm", "fd", "mqueue", ".lxc", ".lxd-mounts":
continue
default:
sub, err := getDevices(filepath.Join(path, f.Name()))
if err != nil {
return nil, err
}
if sub != nil {
out = append(out, sub...)
}
continue
}
case f.Name() == "console":
continue
case f.Mode()&os.ModeSymlink != 0:
// do not add symlink'd devices to privileged devices
continue
}
device, err := devices.DeviceFromPath(filepath.Join(path, f.Name()), "rwm")
if err != nil {
if err == devices.ErrNotADevice {
continue
}
if os.IsNotExist(err) {
continue
}
return nil, err
}
out = append(out, device)
}
return out, nil
}
func addPrivilegedDevices(g *generate.Generator) error {
hostDevices, err := getDevices("/dev")
if err != nil {
return err
}
g.ClearLinuxDevices()
if rootless.IsRootless() {
mounts := make(map[string]interface{})
for _, m := range g.Mounts() {
mounts[m.Destination] = true
}
newMounts := []spec.Mount{}
for _, d := range hostDevices {
devMnt := spec.Mount{
Destination: d.Path,
Type: TypeBind,
Source: d.Path,
Options: []string{"slave", "nosuid", "noexec", "rw", "rbind"},
}
if d.Path == "/dev/ptmx" || strings.HasPrefix(d.Path, "/dev/tty") {
continue
}
if _, found := mounts[d.Path]; found {
continue
}
st, err := os.Stat(d.Path)
if err != nil {
if err == unix.EPERM {
continue
}
return errors.Wrapf(err, "stat %s", d.Path)
}
// Skip devices that the user has not access to.
if st.Mode()&0007 == 0 {
continue
}
newMounts = append(newMounts, devMnt)
}
g.Config.Mounts = append(newMounts, g.Config.Mounts...)
g.Config.Linux.Resources.Devices = nil
} else {
for _, d := range hostDevices {
g.AddDevice(Device(d))
}
// Add resources device - need to clear the existing one first.
g.Config.Linux.Resources.Devices = nil
g.AddLinuxResourcesDevice(true, "", nil, nil, "rwm")
}
return nil
}
func (c *CreateConfig) createBlockIO() (*spec.LinuxBlockIO, error) {
var ret *spec.LinuxBlockIO
bio := &spec.LinuxBlockIO{}
if c.Resources.BlkioWeight > 0 {
ret = bio
bio.Weight = &c.Resources.BlkioWeight
}
if len(c.Resources.BlkioWeightDevice) > 0 {
var lwds []spec.LinuxWeightDevice
ret = bio
for _, i := range c.Resources.BlkioWeightDevice {
wd, err := ValidateweightDevice(i)
if err != nil {
return ret, errors.Wrapf(err, "invalid values for blkio-weight-device")
}
wdStat, err := GetStatFromPath(wd.Path)
if err != nil {
return ret, errors.Wrapf(err, "error getting stat from path %q", wd.Path)
}
lwd := spec.LinuxWeightDevice{
Weight: &wd.Weight,
}
lwd.Major = int64(unix.Major(wdStat.Rdev))
lwd.Minor = int64(unix.Minor(wdStat.Rdev))
lwds = append(lwds, lwd)
}
bio.WeightDevice = lwds
}
if len(c.Resources.DeviceReadBps) > 0 {
ret = bio
readBps, err := makeThrottleArray(c.Resources.DeviceReadBps, bps)
if err != nil {
return ret, err
}
bio.ThrottleReadBpsDevice = readBps
}
if len(c.Resources.DeviceWriteBps) > 0 {
ret = bio
writeBpds, err := makeThrottleArray(c.Resources.DeviceWriteBps, bps)
if err != nil {
return ret, err
}
bio.ThrottleWriteBpsDevice = writeBpds
}
if len(c.Resources.DeviceReadIOps) > 0 {
ret = bio
readIOps, err := makeThrottleArray(c.Resources.DeviceReadIOps, iops)
if err != nil {
return ret, err
}
bio.ThrottleReadIOPSDevice = readIOps
}
if len(c.Resources.DeviceWriteIOps) > 0 {
ret = bio
writeIOps, err := makeThrottleArray(c.Resources.DeviceWriteIOps, iops)
if err != nil {
return ret, err
}
bio.ThrottleWriteIOPSDevice = writeIOps
}
return ret, nil
}
func makeThrottleArray(throttleInput []string, rateType int) ([]spec.LinuxThrottleDevice, error) {
var (
ltds []spec.LinuxThrottleDevice
t *throttleDevice
err error
)
for _, i := range throttleInput {
if rateType == bps {
t, err = validateBpsDevice(i)
} else {
t, err = validateIOpsDevice(i)
}
if err != nil {
return []spec.LinuxThrottleDevice{}, err
}
ltdStat, err := GetStatFromPath(t.path)
if err != nil {
return ltds, errors.Wrapf(err, "error getting stat from path %q", t.path)
}
ltd := spec.LinuxThrottleDevice{
Rate: t.rate,
}
ltd.Major = int64(unix.Major(ltdStat.Rdev))
ltd.Minor = int64(unix.Minor(ltdStat.Rdev))
ltds = append(ltds, ltd)
}
return ltds, nil
}
func GetStatFromPath(path string) (unix.Stat_t, error) {
s := unix.Stat_t{}
err := unix.Stat(path, &s)
return s, err
}

View File

@ -1,47 +0,0 @@
// +build linux,cgo
package createconfig
import (
"io/ioutil"
goSeccomp "github.com/containers/common/pkg/seccomp"
"github.com/containers/podman/v2/pkg/seccomp"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
func getSeccompConfig(config *SecurityConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) {
var seccompConfig *spec.LinuxSeccomp
var err error
if config.SeccompPolicy == seccomp.PolicyImage && config.SeccompProfileFromImage != "" {
logrus.Debug("Loading seccomp profile from the security config")
seccompConfig, err = goSeccomp.LoadProfile(config.SeccompProfileFromImage, configSpec)
if err != nil {
return nil, errors.Wrap(err, "loading seccomp profile failed")
}
return seccompConfig, nil
}
if config.SeccompProfilePath != "" {
logrus.Debugf("Loading seccomp profile from %q", config.SeccompProfilePath)
seccompProfile, err := ioutil.ReadFile(config.SeccompProfilePath)
if err != nil {
return nil, errors.Wrap(err, "opening seccomp profile failed")
}
seccompConfig, err = goSeccomp.LoadProfile(string(seccompProfile), configSpec)
if err != nil {
return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", config.SeccompProfilePath)
}
} else {
logrus.Debug("Loading default seccomp profile")
seccompConfig, err = goSeccomp.GetDefaultProfile(configSpec)
if err != nil {
return nil, errors.Wrapf(err, "loading default seccomp profile failed")
}
}
return seccompConfig, nil
}

View File

@ -1,11 +0,0 @@
// +build linux,!cgo
package createconfig
import (
spec "github.com/opencontainers/runtime-spec/specs-go"
)
func getSeccompConfig(config *SecurityConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) {
return nil, nil
}

View File

@ -1,36 +0,0 @@
// +build !linux
package createconfig
import (
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/pkg/errors"
)
func getSeccompConfig(config *SecurityConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) {
return nil, errors.New("function not supported on non-linux OS's")
}
func addDevice(g *generate.Generator, device string) error {
return errors.New("function not implemented")
}
func addPrivilegedDevices(g *generate.Generator) error {
return errors.New("function not implemented")
}
func (c *CreateConfig) createBlockIO() (*spec.LinuxBlockIO, error) {
return nil, errors.New("function not implemented")
}
func makeThrottleArray(throttleInput []string, rateType int) ([]spec.LinuxThrottleDevice, error) {
return nil, errors.New("function not implemented")
}
func DevicesFromPath(g *generate.Generator, devicePath string) error {
return errors.New("function not implemented")
}
func deviceCgroupRules(g *generate.Generator, deviceCgroupRules []string) error {
return errors.New("function not implemented")
}

View File

@ -1,41 +0,0 @@
package createconfig
import (
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// MakeContainerConfig generates all configuration necessary to start a
// container with libpod from a completed CreateConfig struct.
func (config *CreateConfig) MakeContainerConfig(runtime *libpod.Runtime, pod *libpod.Pod) (*spec.Spec, []libpod.CtrCreateOption, error) {
if config.Pod != "" && pod == nil {
return nil, nil, errors.Wrapf(define.ErrInvalidArg, "pod was specified but no pod passed")
} else if config.Pod == "" && pod != nil {
return nil, nil, errors.Wrapf(define.ErrInvalidArg, "pod was given but no pod is specified")
}
// Parse volumes flag into OCI spec mounts and libpod Named Volumes.
// If there is an identical mount in the OCI spec, we will replace it
// with a mount generated here.
mounts, namedVolumes, err := config.parseVolumes(runtime)
if err != nil {
return nil, nil, err
}
runtimeSpec, err := config.createConfigToOCISpec(runtime, mounts)
if err != nil {
return nil, nil, err
}
options, err := config.getContainerCreateOptions(runtime, pod, mounts, namedVolumes)
if err != nil {
return nil, nil, err
}
logrus.Debugf("created OCI spec and options for new container")
return runtimeSpec, options, nil
}

View File

@ -1,426 +0,0 @@
package createconfig
import (
"context"
"os"
"strconv"
"strings"
"syscall"
"github.com/containers/image/v5/manifest"
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/namespaces"
"github.com/containers/podman/v2/pkg/seccomp"
"github.com/containers/storage"
"github.com/docker/go-connections/nat"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// Type constants
const (
bps = iota
iops
)
// CreateResourceConfig represents resource elements in CreateConfig
// structures
type CreateResourceConfig struct {
BlkioWeight uint16 // blkio-weight
BlkioWeightDevice []string // blkio-weight-device
CgroupConf map[string]string
CPUPeriod uint64 // cpu-period
CPUQuota int64 // cpu-quota
CPURtPeriod uint64 // cpu-rt-period
CPURtRuntime int64 // cpu-rt-runtime
CPUShares uint64 // cpu-shares
CPUs float64 // cpus
CPUsetCPUs string
CPUsetMems string // cpuset-mems
DeviceCgroupRules []string //device-cgroup-rule
DeviceReadBps []string // device-read-bps
DeviceReadIOps []string // device-read-iops
DeviceWriteBps []string // device-write-bps
DeviceWriteIOps []string // device-write-iops
DisableOomKiller bool // oom-kill-disable
KernelMemory int64 // kernel-memory
Memory int64 //memory
MemoryReservation int64 // memory-reservation
MemorySwap int64 //memory-swap
MemorySwappiness int // memory-swappiness
OomScoreAdj int //oom-score-adj
PidsLimit int64 // pids-limit
ShmSize int64
Ulimit []string //ulimit
}
// PidConfig configures the pid namespace for the container
type PidConfig struct {
PidMode namespaces.PidMode //pid
}
// IpcConfig configures the ipc namespace for the container
type IpcConfig struct {
IpcMode namespaces.IpcMode //ipc
}
// CgroupConfig configures the cgroup namespace for the container
type CgroupConfig struct {
Cgroups string
Cgroupns string
CgroupParent string // cgroup-parent
CgroupMode namespaces.CgroupMode //cgroup
}
// UserConfig configures the user namespace for the container
type UserConfig struct {
GroupAdd []string // group-add
IDMappings *storage.IDMappingOptions
UsernsMode namespaces.UsernsMode //userns
User string //user
}
// UtsConfig configures the uts namespace for the container
type UtsConfig struct {
UtsMode namespaces.UTSMode //uts
NoHosts bool
HostAdd []string //add-host
Hostname string
}
// NetworkConfig configures the network namespace for the container
type NetworkConfig struct {
DNSOpt []string //dns-opt
DNSSearch []string //dns-search
DNSServers []string //dns
ExposedPorts map[nat.Port]struct{}
HTTPProxy bool
IP6Address string //ipv6
IPAddress string //ip
LinkLocalIP []string // link-local-ip
MacAddress string //mac-address
NetMode namespaces.NetworkMode //net
Network string //network
NetworkAlias []string //network-alias
PortBindings nat.PortMap
Publish []string //publish
PublishAll bool //publish-all
}
// SecurityConfig configures the security features for the container
type SecurityConfig struct {
CapAdd []string // cap-add
CapDrop []string // cap-drop
CapRequired []string // cap-required
LabelOpts []string //SecurityOpts
NoNewPrivs bool //SecurityOpts
ApparmorProfile string //SecurityOpts
SeccompProfilePath string //SecurityOpts
SeccompProfileFromImage string // seccomp profile from the container image
SeccompPolicy seccomp.Policy
SecurityOpts []string
Privileged bool //privileged
ReadOnlyRootfs bool //read-only
ReadOnlyTmpfs bool //read-only-tmpfs
Sysctl map[string]string //sysctl
ProcOpts []string
}
// CreateConfig is a pre OCI spec structure. It represents user input from varlink or the CLI
// swagger:model CreateConfig
type CreateConfig struct {
Annotations map[string]string
Args []string
CidFile string
ConmonPidFile string
Command []string // Full command that will be used
UserCommand []string // User-entered command (or image CMD)
Detach bool // detach
Devices []string // device
Entrypoint []string //entrypoint
Env map[string]string //env
HealthCheck *manifest.Schema2HealthConfig
Init bool // init
InitPath string //init-path
Image string
ImageID string
RawImageName string
BuiltinImgVolumes map[string]struct{} // volumes defined in the image config
ImageVolumeType string // how to handle the image volume, either bind, tmpfs, or ignore
Interactive bool //interactive
Labels map[string]string //label
LogDriver string // log-driver
LogDriverOpt []string // log-opt
Name string //name
PodmanPath string
Pod string //pod
Quiet bool //quiet
Resources CreateResourceConfig
RestartPolicy string
Rm bool //rm
Rmi bool //rmi
StopSignal syscall.Signal // stop-signal
StopTimeout uint // stop-timeout
Systemd bool
Tmpfs []string // tmpfs
Tty bool //tty
Mounts []spec.Mount
MountsFlag []string // mounts
NamedVolumes []*libpod.ContainerNamedVolume
Volumes []string //volume
VolumesFrom []string
WorkDir string //workdir
Rootfs string
Security SecurityConfig
Syslog bool // Whether to enable syslog on exit commands
// Namespaces
Pid PidConfig
Ipc IpcConfig
Cgroup CgroupConfig
User UserConfig
Uts UtsConfig
Network NetworkConfig
}
func u32Ptr(i int64) *uint32 { u := uint32(i); return &u }
func fmPtr(i int64) *os.FileMode { fm := os.FileMode(i); return &fm }
// CreateBlockIO returns a LinuxBlockIO struct from a CreateConfig
func (c *CreateConfig) CreateBlockIO() (*spec.LinuxBlockIO, error) {
return c.createBlockIO()
}
func (c *CreateConfig) createExitCommand(runtime *libpod.Runtime) ([]string, error) {
config, err := runtime.GetConfig()
if err != nil {
return nil, err
}
storageConfig := runtime.StorageConfig()
// We need a cleanup process for containers in the current model.
// But we can't assume that the caller is Podman - it could be another
// user of the API.
// As such, provide a way to specify a path to Podman, so we can
// still invoke a cleanup process.
cmd := c.PodmanPath
if cmd == "" {
cmd, _ = os.Executable()
}
command := []string{cmd,
"--root", storageConfig.GraphRoot,
"--runroot", storageConfig.RunRoot,
"--log-level", logrus.GetLevel().String(),
"--cgroup-manager", config.Engine.CgroupManager,
"--tmpdir", config.Engine.TmpDir,
}
if config.Engine.OCIRuntime != "" {
command = append(command, []string{"--runtime", config.Engine.OCIRuntime}...)
}
if storageConfig.GraphDriverName != "" {
command = append(command, []string{"--storage-driver", storageConfig.GraphDriverName}...)
}
for _, opt := range storageConfig.GraphDriverOptions {
command = append(command, []string{"--storage-opt", opt}...)
}
if config.Engine.EventsLogger != "" {
command = append(command, []string{"--events-backend", config.Engine.EventsLogger}...)
}
if c.Syslog {
command = append(command, "--syslog", "true")
}
command = append(command, []string{"container", "cleanup"}...)
if c.Rm {
command = append(command, "--rm")
}
if c.Rmi {
command = append(command, "--rmi")
}
return command, nil
}
// GetContainerCreateOptions takes a CreateConfig and returns a slice of CtrCreateOptions
func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *libpod.Pod, mounts []spec.Mount, namedVolumes []*libpod.ContainerNamedVolume) ([]libpod.CtrCreateOption, error) {
var options []libpod.CtrCreateOption
var err error
if c.Interactive {
options = append(options, libpod.WithStdin())
}
if c.Systemd {
options = append(options, libpod.WithSystemd())
}
if c.Name != "" {
logrus.Debugf("setting container name %s", c.Name)
options = append(options, libpod.WithName(c.Name))
}
if c.Pod != "" {
logrus.Debugf("adding container to pod %s", c.Pod)
options = append(options, runtime.WithPod(pod))
}
// handle some spec from the InfraContainer when it's a pod
if pod != nil && pod.HasInfraContainer() {
InfraCtr, err := pod.InfraContainer()
if err != nil {
return nil, err
}
// handle the pod.spec.hostAliases
options = append(options, libpod.WithHosts(InfraCtr.HostsAdd()))
}
if len(mounts) != 0 || len(namedVolumes) != 0 {
destinations := []string{}
// Take all mount and named volume destinations.
for _, mount := range mounts {
destinations = append(destinations, mount.Destination)
}
for _, volume := range namedVolumes {
destinations = append(destinations, volume.Dest)
}
options = append(options, libpod.WithUserVolumes(destinations))
}
if len(namedVolumes) != 0 {
options = append(options, libpod.WithNamedVolumes(namedVolumes))
}
if len(c.UserCommand) != 0 {
options = append(options, libpod.WithCommand(c.UserCommand))
}
// Add entrypoint if it was set
// If it's empty it's because it was explicitly set to ""
if c.Entrypoint != nil {
options = append(options, libpod.WithEntrypoint(c.Entrypoint))
}
// TODO: MNT, USER, CGROUP
options = append(options, libpod.WithStopSignal(c.StopSignal))
options = append(options, libpod.WithStopTimeout(c.StopTimeout))
logPath, logTag := getLoggingOpts(c.LogDriverOpt)
if logPath != "" {
options = append(options, libpod.WithLogPath(logPath))
}
if logTag != "" {
options = append(options, libpod.WithLogTag(logTag))
}
if c.LogDriver != "" {
options = append(options, libpod.WithLogDriver(c.LogDriver))
}
secOpts, err := c.Security.ToCreateOptions()
if err != nil {
return nil, err
}
options = append(options, secOpts...)
nsOpts, err := c.Cgroup.ToCreateOptions(runtime)
if err != nil {
return nil, err
}
options = append(options, nsOpts...)
nsOpts, err = c.Ipc.ToCreateOptions(runtime)
if err != nil {
return nil, err
}
options = append(options, nsOpts...)
nsOpts, err = c.Pid.ToCreateOptions(runtime)
if err != nil {
return nil, err
}
options = append(options, nsOpts...)
nsOpts, err = c.Network.ToCreateOptions(runtime, &c.User)
if err != nil {
return nil, err
}
options = append(options, nsOpts...)
nsOpts, err = c.Uts.ToCreateOptions(runtime, pod)
if err != nil {
return nil, err
}
options = append(options, nsOpts...)
nsOpts, err = c.User.ToCreateOptions(runtime)
if err != nil {
return nil, err
}
options = append(options, nsOpts...)
// Gather up the options for NewContainer which consist of With... funcs
options = append(options, libpod.WithRootFSFromImage(c.ImageID, c.Image, c.RawImageName))
options = append(options, libpod.WithConmonPidFile(c.ConmonPidFile))
options = append(options, libpod.WithLabels(c.Labels))
options = append(options, libpod.WithShmSize(c.Resources.ShmSize))
if c.Rootfs != "" {
options = append(options, libpod.WithRootFS(c.Rootfs))
}
// Default used if not overridden on command line
if c.RestartPolicy != "" {
if c.RestartPolicy == "unless-stopped" {
return nil, errors.Wrapf(define.ErrInvalidArg, "the unless-stopped restart policy is not supported")
}
split := strings.Split(c.RestartPolicy, ":")
if len(split) > 1 {
numTries, err := strconv.Atoi(split[1])
if err != nil {
return nil, errors.Wrapf(err, "%s is not a valid number of retries for restart policy", split[1])
}
if numTries < 0 {
return nil, errors.Wrapf(define.ErrInvalidArg, "restart policy requires a positive number of retries")
}
options = append(options, libpod.WithRestartRetries(uint(numTries)))
}
options = append(options, libpod.WithRestartPolicy(split[0]))
}
// Always use a cleanup process to clean up Podman after termination
exitCmd, err := c.createExitCommand(runtime)
if err != nil {
return nil, err
}
options = append(options, libpod.WithExitCommand(exitCmd))
if c.HealthCheck != nil {
options = append(options, libpod.WithHealthCheck(c.HealthCheck))
logrus.Debugf("New container has a health check")
}
return options, nil
}
// AddPrivilegedDevices iterates through host devices and adds all
// host devices to the spec
func AddPrivilegedDevices(g *generate.Generator) error {
return addPrivilegedDevices(g)
}
func CreateContainerFromCreateConfig(ctx context.Context, r *libpod.Runtime, createConfig *CreateConfig, pod *libpod.Pod) (*libpod.Container, error) {
runtimeSpec, options, err := createConfig.MakeContainerConfig(r, pod)
if err != nil {
return nil, err
}
ctr, err := r.NewContainer(ctx, runtimeSpec, options...)
if err != nil {
return nil, err
}
return ctr, nil
}

View File

@ -1,463 +0,0 @@
package createconfig
import (
"net"
"os"
"strconv"
"strings"
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/cgroups"
"github.com/cri-o/ocicni/pkg/ocicni"
"github.com/docker/go-connections/nat"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// DefaultKernelNamespaces is a comma-separated list of default kernel
// namespaces.
const DefaultKernelNamespaces = "cgroup,ipc,net,uts"
// ToCreateOptions converts the input to a slice of container create options.
func (c *NetworkConfig) ToCreateOptions(runtime *libpod.Runtime, userns *UserConfig) ([]libpod.CtrCreateOption, error) {
var portBindings []ocicni.PortMapping
var err error
if len(c.PortBindings) > 0 {
portBindings, err = NatToOCIPortBindings(c.PortBindings)
if err != nil {
return nil, errors.Wrapf(err, "unable to create port bindings")
}
}
options := make([]libpod.CtrCreateOption, 0)
userNetworks := c.NetMode.UserDefined()
networks := make([]string, 0)
if IsPod(userNetworks) {
userNetworks = ""
}
if userNetworks != "" {
for _, netName := range strings.Split(userNetworks, ",") {
if netName == "" {
return nil, errors.Errorf("container networks %q invalid", userNetworks)
}
networks = append(networks, netName)
}
}
switch {
case c.NetMode.IsNS():
ns := c.NetMode.NS()
if ns == "" {
return nil, errors.Errorf("invalid empty user-defined network namespace")
}
_, err := os.Stat(ns)
if err != nil {
return nil, err
}
case c.NetMode.IsContainer():
connectedCtr, err := runtime.LookupContainer(c.NetMode.Container())
if err != nil {
return nil, errors.Wrapf(err, "container %q not found", c.NetMode.Container())
}
options = append(options, libpod.WithNetNSFrom(connectedCtr))
case !c.NetMode.IsHost() && !c.NetMode.IsNone():
postConfigureNetNS := userns.getPostConfigureNetNS()
options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, string(c.NetMode), networks))
}
if len(c.DNSSearch) > 0 {
options = append(options, libpod.WithDNSSearch(c.DNSSearch))
}
if len(c.DNSServers) > 0 {
if len(c.DNSServers) == 1 && strings.ToLower(c.DNSServers[0]) == "none" {
options = append(options, libpod.WithUseImageResolvConf())
} else {
options = append(options, libpod.WithDNS(c.DNSServers))
}
}
if len(c.DNSOpt) > 0 {
options = append(options, libpod.WithDNSOption(c.DNSOpt))
}
if c.IPAddress != "" {
ip := net.ParseIP(c.IPAddress)
if ip == nil {
return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as IP address", c.IPAddress)
} else if ip.To4() == nil {
return nil, errors.Wrapf(define.ErrInvalidArg, "%s is not an IPv4 address", c.IPAddress)
}
options = append(options, libpod.WithStaticIP(ip))
}
if c.MacAddress != "" {
mac, err := net.ParseMAC(c.MacAddress)
if err != nil {
return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as MAC address: %v", c.MacAddress, err)
}
options = append(options, libpod.WithStaticMAC(mac))
}
return options, nil
}
// ConfigureGenerator configures the generator based according to the current
// state of the NetworkConfig.
func (c *NetworkConfig) ConfigureGenerator(g *generate.Generator) error {
netMode := c.NetMode
netCtr := netMode.Container()
switch {
case netMode.IsHost():
logrus.Debug("Using host netmode")
if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil {
return err
}
case netMode.IsNone():
logrus.Debug("Using none netmode")
case netMode.IsBridge():
logrus.Debug("Using bridge netmode")
case netCtr != "":
logrus.Debugf("using container %s netmode", netCtr)
case IsNS(string(netMode)):
logrus.Debug("Using ns netmode")
if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), NS(string(netMode))); err != nil {
return err
}
case IsPod(string(netMode)):
logrus.Debug("Using pod netmode, unless pod is not sharing")
case netMode.IsSlirp4netns():
logrus.Debug("Using slirp4netns netmode")
case netMode.IsUserDefined():
logrus.Debug("Using user defined netmode")
default:
return errors.Errorf("unknown network mode")
}
if c.HTTPProxy {
for _, envSpec := range []string{
"http_proxy",
"HTTP_PROXY",
"https_proxy",
"HTTPS_PROXY",
"ftp_proxy",
"FTP_PROXY",
"no_proxy",
"NO_PROXY",
} {
envVal := os.Getenv(envSpec)
if envVal != "" {
g.AddProcessEnv(envSpec, envVal)
}
}
}
if g.Config.Annotations == nil {
g.Config.Annotations = make(map[string]string)
}
if c.PublishAll {
g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseTrue
} else {
g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseFalse
}
return nil
}
// NatToOCIPortBindings iterates a nat.portmap slice and creates []ocicni portmapping slice
func NatToOCIPortBindings(ports nat.PortMap) ([]ocicni.PortMapping, error) {
var portBindings []ocicni.PortMapping
for containerPb, hostPb := range ports {
var pm ocicni.PortMapping
pm.ContainerPort = int32(containerPb.Int())
for _, i := range hostPb {
var hostPort int
var err error
pm.HostIP = i.HostIP
if i.HostPort == "" {
hostPort = containerPb.Int()
} else {
hostPort, err = strconv.Atoi(i.HostPort)
if err != nil {
return nil, errors.Wrapf(err, "unable to convert host port to integer")
}
}
pm.HostPort = int32(hostPort)
pm.Protocol = containerPb.Proto()
portBindings = append(portBindings, pm)
}
}
return portBindings, nil
}
// ToCreateOptions converts the input to container create options.
func (c *CgroupConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
options := make([]libpod.CtrCreateOption, 0)
if c.CgroupMode.IsNS() {
ns := c.CgroupMode.NS()
if ns == "" {
return nil, errors.Errorf("invalid empty user-defined network namespace")
}
_, err := os.Stat(ns)
if err != nil {
return nil, err
}
} else if c.CgroupMode.IsContainer() {
connectedCtr, err := runtime.LookupContainer(c.CgroupMode.Container())
if err != nil {
return nil, errors.Wrapf(err, "container %q not found", c.CgroupMode.Container())
}
options = append(options, libpod.WithCgroupNSFrom(connectedCtr))
}
if c.CgroupParent != "" {
options = append(options, libpod.WithCgroupParent(c.CgroupParent))
}
if c.Cgroups != "" {
options = append(options, libpod.WithCgroupsMode(c.Cgroups))
}
return options, nil
}
// ToCreateOptions converts the input to container create options.
func (c *UserConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
options := make([]libpod.CtrCreateOption, 0)
switch {
case c.UsernsMode.IsNS():
ns := c.UsernsMode.NS()
if ns == "" {
return nil, errors.Errorf("invalid empty user-defined user namespace")
}
_, err := os.Stat(ns)
if err != nil {
return nil, err
}
options = append(options, libpod.WithIDMappings(*c.IDMappings))
case c.UsernsMode.IsContainer():
connectedCtr, err := runtime.LookupContainer(c.UsernsMode.Container())
if err != nil {
return nil, errors.Wrapf(err, "container %q not found", c.UsernsMode.Container())
}
options = append(options, libpod.WithUserNSFrom(connectedCtr))
default:
options = append(options, libpod.WithIDMappings(*c.IDMappings))
}
options = append(options, libpod.WithUser(c.User))
options = append(options, libpod.WithGroups(c.GroupAdd))
return options, nil
}
// ConfigureGenerator configures the generator according to the current state
// of the UserConfig.
func (c *UserConfig) ConfigureGenerator(g *generate.Generator) error {
if IsNS(string(c.UsernsMode)) {
if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), NS(string(c.UsernsMode))); err != nil {
return err
}
// runc complains if no mapping is specified, even if we join another ns. So provide a dummy mapping
g.AddLinuxUIDMapping(uint32(0), uint32(0), uint32(1))
g.AddLinuxGIDMapping(uint32(0), uint32(0), uint32(1))
}
if (len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0) && !c.UsernsMode.IsHost() {
if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil {
return err
}
}
for _, uidmap := range c.IDMappings.UIDMap {
g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size))
}
for _, gidmap := range c.IDMappings.GIDMap {
g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size))
}
return nil
}
func (c *UserConfig) getPostConfigureNetNS() bool {
hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || c.UsernsMode.IsAuto() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
postConfigureNetNS := hasUserns && !c.UsernsMode.IsHost()
return postConfigureNetNS
}
// InNS returns true if the UserConfig indicates to be in a dedicated user
// namespace.
func (c *UserConfig) InNS(isRootless bool) bool {
hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || c.UsernsMode.IsAuto() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
return isRootless || (hasUserns && !c.UsernsMode.IsHost())
}
// ToCreateOptions converts the input to container create options.
func (c *IpcConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
options := make([]libpod.CtrCreateOption, 0)
if c.IpcMode.IsHost() {
options = append(options, libpod.WithShmDir("/dev/shm"))
} else if c.IpcMode.IsContainer() {
connectedCtr, err := runtime.LookupContainer(c.IpcMode.Container())
if err != nil {
return nil, errors.Wrapf(err, "container %q not found", c.IpcMode.Container())
}
options = append(options, libpod.WithIPCNSFrom(connectedCtr))
options = append(options, libpod.WithShmDir(connectedCtr.ShmDir()))
}
return options, nil
}
// ConfigureGenerator configures the generator according to the current state
// of the IpcConfig.
func (c *IpcConfig) ConfigureGenerator(g *generate.Generator) error {
ipcMode := c.IpcMode
if IsNS(string(ipcMode)) {
return g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), NS(string(ipcMode)))
}
if ipcMode.IsHost() {
return g.RemoveLinuxNamespace(string(spec.IPCNamespace))
}
if ipcCtr := ipcMode.Container(); ipcCtr != "" {
logrus.Debugf("Using container %s ipcmode", ipcCtr)
}
return nil
}
// ConfigureGenerator configures the generator according to the current state
// of the CgroupConfig.
func (c *CgroupConfig) ConfigureGenerator(g *generate.Generator) error {
cgroupMode := c.CgroupMode
if cgroupMode.IsDefaultValue() {
// If the value is not specified, default to "private" on cgroups v2 and "host" on cgroups v1.
unified, err := cgroups.IsCgroup2UnifiedMode()
if err != nil {
return err
}
if unified {
cgroupMode = "private"
} else {
cgroupMode = "host"
}
}
if cgroupMode.IsNS() {
return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), NS(string(cgroupMode)))
}
if cgroupMode.IsHost() {
return g.RemoveLinuxNamespace(string(spec.CgroupNamespace))
}
if cgroupMode.IsPrivate() {
return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), "")
}
if cgCtr := cgroupMode.Container(); cgCtr != "" {
logrus.Debugf("Using container %s cgroup mode", cgCtr)
}
return nil
}
// ToCreateOptions converts the input to container create options.
func (c *PidConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
options := make([]libpod.CtrCreateOption, 0)
if c.PidMode.IsContainer() {
connectedCtr, err := runtime.LookupContainer(c.PidMode.Container())
if err != nil {
return nil, errors.Wrapf(err, "container %q not found", c.PidMode.Container())
}
options = append(options, libpod.WithPIDNSFrom(connectedCtr))
}
return options, nil
}
// ConfigureGenerator configures the generator according to the current state
// of the PidConfig.
func (c *PidConfig) ConfigureGenerator(g *generate.Generator) error {
pidMode := c.PidMode
if IsNS(string(pidMode)) {
return g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), NS(string(pidMode)))
}
if pidMode.IsHost() {
return g.RemoveLinuxNamespace(string(spec.PIDNamespace))
}
if pidCtr := pidMode.Container(); pidCtr != "" {
logrus.Debugf("using container %s pidmode", pidCtr)
}
if IsPod(string(pidMode)) {
logrus.Debug("using pod pidmode")
}
return nil
}
// ToCreateOptions converts the input to container create options.
func (c *UtsConfig) ToCreateOptions(runtime *libpod.Runtime, pod *libpod.Pod) ([]libpod.CtrCreateOption, error) {
options := make([]libpod.CtrCreateOption, 0)
if IsPod(string(c.UtsMode)) {
options = append(options, libpod.WithUTSNSFromPod(pod))
}
if c.UtsMode.IsContainer() {
connectedCtr, err := runtime.LookupContainer(c.UtsMode.Container())
if err != nil {
return nil, errors.Wrapf(err, "container %q not found", c.UtsMode.Container())
}
options = append(options, libpod.WithUTSNSFrom(connectedCtr))
}
if c.NoHosts {
options = append(options, libpod.WithUseImageHosts())
}
if len(c.HostAdd) > 0 && !c.NoHosts {
options = append(options, libpod.WithHosts(c.HostAdd))
}
return options, nil
}
// ConfigureGenerator configures the generator according to the current state
// of the UtsConfig.
func (c *UtsConfig) ConfigureGenerator(g *generate.Generator, net *NetworkConfig, runtime *libpod.Runtime) error {
hostname := c.Hostname
utsCtrID := c.UtsMode.Container()
var err error
if hostname == "" {
switch {
case utsCtrID != "":
utsCtr, err := runtime.LookupContainer(utsCtrID)
if err != nil {
return errors.Wrapf(err, "unable to retrieve hostname from dependency container %s", utsCtrID)
}
hostname = utsCtr.Hostname()
case net.NetMode.IsHost() || c.UtsMode.IsHost():
hostname, err = os.Hostname()
if err != nil {
return errors.Wrap(err, "unable to retrieve hostname of the host")
}
default:
logrus.Debug("No hostname set; container's hostname will default to runtime default")
}
}
g.RemoveHostname()
if c.Hostname != "" || !c.UtsMode.IsHost() {
// Set the hostname in the OCI configuration only
// if specified by the user or if we are creating
// a new UTS namespace.
g.SetHostname(hostname)
}
g.AddProcessEnv("HOSTNAME", hostname)
utsMode := c.UtsMode
if IsNS(string(utsMode)) {
return g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), NS(string(utsMode)))
}
if utsMode.IsHost() {
return g.RemoveLinuxNamespace(string(spec.UTSNamespace))
}
if utsCtr := utsMode.Container(); utsCtr != "" {
logrus.Debugf("using container %s utsmode", utsCtr)
}
return nil
}

View File

@ -1,225 +0,0 @@
package createconfig
import (
"fmt"
"regexp"
"strconv"
"strings"
"github.com/docker/go-units"
"github.com/pkg/errors"
)
// deviceCgroupRulegex defines the valid format of device-cgroup-rule
var deviceCgroupRuleRegex = regexp.MustCompile(`^([acb]) ([0-9]+|\*):([0-9]+|\*) ([rwm]{1,3})$`)
// Pod signifies a kernel namespace is being shared
// by a container with the pod it is associated with
const Pod = "pod"
// weightDevice is a structure that holds device:weight pair
type weightDevice struct {
Path string
Weight uint16
}
func (w *weightDevice) String() string {
return fmt.Sprintf("%s:%d", w.Path, w.Weight)
}
// LinuxNS is a struct that contains namespace information
// It implemented Valid to show it is a valid namespace
type LinuxNS interface {
Valid() bool
}
// IsNS returns if the specified string has a ns: prefix
func IsNS(s string) bool {
parts := strings.SplitN(s, ":", 2)
return len(parts) > 1 && parts[0] == "ns"
}
// IsPod returns if the specified string is pod
func IsPod(s string) bool {
return s == Pod
}
// Valid checks the validity of a linux namespace
// s should be the string representation of ns
func Valid(s string, ns LinuxNS) bool {
return IsPod(s) || IsNS(s) || ns.Valid()
}
// NS is the path to the namespace to join.
func NS(s string) string {
parts := strings.SplitN(s, ":", 2)
if len(parts) > 1 {
return parts[1]
}
return ""
}
// ValidateweightDevice validates that the specified string has a valid device-weight format
// for blkio-weight-device flag
func ValidateweightDevice(val string) (*weightDevice, error) {
split := strings.SplitN(val, ":", 2)
if len(split) != 2 {
return nil, fmt.Errorf("bad format: %s", val)
}
if !strings.HasPrefix(split[0], "/dev/") {
return nil, fmt.Errorf("bad format for device path: %s", val)
}
weight, err := strconv.ParseUint(split[1], 10, 0)
if err != nil {
return nil, fmt.Errorf("invalid weight for device: %s", val)
}
if weight > 0 && (weight < 10 || weight > 1000) {
return nil, fmt.Errorf("invalid weight for device: %s", val)
}
return &weightDevice{
Path: split[0],
Weight: uint16(weight),
}, nil
}
// throttleDevice is a structure that holds device:rate_per_second pair
type throttleDevice struct {
path string
rate uint64
}
func (t *throttleDevice) String() string {
return fmt.Sprintf("%s:%d", t.path, t.rate)
}
// validateBpsDevice validates that the specified string has a valid device-rate format
// for device-read-bps and device-write-bps flags
func validateBpsDevice(val string) (*throttleDevice, error) {
split := strings.SplitN(val, ":", 2)
if len(split) != 2 {
return nil, fmt.Errorf("bad format: %s", val)
}
if !strings.HasPrefix(split[0], "/dev/") {
return nil, fmt.Errorf("bad format for device path: %s", val)
}
rate, err := units.RAMInBytes(split[1])
if err != nil {
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val)
}
if rate < 0 {
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>[<unit>]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val)
}
return &throttleDevice{
path: split[0],
rate: uint64(rate),
}, nil
}
// validateIOpsDevice validates that the specified string has a valid device-rate format
// for device-write-iops and device-read-iops flags
func validateIOpsDevice(val string) (*throttleDevice, error) { //nolint
split := strings.SplitN(val, ":", 2)
if len(split) != 2 {
return nil, fmt.Errorf("bad format: %s", val)
}
if !strings.HasPrefix(split[0], "/dev/") {
return nil, fmt.Errorf("bad format for device path: %s", val)
}
rate, err := strconv.ParseUint(split[1], 10, 64)
if err != nil {
return nil, fmt.Errorf("invalid rate for device: %s. The correct format is <device-path>:<number>. Number must be a positive integer", val)
}
return &throttleDevice{
path: split[0],
rate: rate,
}, nil
}
// getLoggingOpts splits the path= and tag= options provided to --log-opt.
func getLoggingOpts(opts []string) (string, string) {
var path, tag string
for _, opt := range opts {
arr := strings.SplitN(opt, "=", 2)
if len(arr) == 2 {
if strings.TrimSpace(arr[0]) == "path" {
path = strings.TrimSpace(arr[1])
} else if strings.TrimSpace(arr[0]) == "tag" {
tag = strings.TrimSpace(arr[1])
}
}
if path != "" && tag != "" {
break
}
}
return path, tag
}
// ParseDevice parses device mapping string to a src, dest & permissions string
func ParseDevice(device string) (string, string, string, error) { //nolint
src := ""
dst := ""
permissions := "rwm"
arr := strings.Split(device, ":")
switch len(arr) {
case 3:
if !IsValidDeviceMode(arr[2]) {
return "", "", "", fmt.Errorf("invalid device mode: %s", arr[2])
}
permissions = arr[2]
fallthrough
case 2:
if IsValidDeviceMode(arr[1]) {
permissions = arr[1]
} else {
if len(arr[1]) == 0 || arr[1][0] != '/' {
return "", "", "", fmt.Errorf("invalid device mode: %s", arr[1])
}
dst = arr[1]
}
fallthrough
case 1:
src = arr[0]
default:
return "", "", "", fmt.Errorf("invalid device specification: %s", device)
}
if dst == "" {
dst = src
}
return src, dst, permissions, nil
}
// IsValidDeviceMode checks if the mode for device is valid or not.
// IsValid mode is a composition of r (read), w (write), and m (mknod).
func IsValidDeviceMode(mode string) bool {
var legalDeviceMode = map[rune]bool{
'r': true,
'w': true,
'm': true,
}
if mode == "" {
return false
}
for _, c := range mode {
if !legalDeviceMode[c] {
return false
}
legalDeviceMode[c] = false
}
return true
}
// validateDeviceCgroupRule validates the format of deviceCgroupRule
func validateDeviceCgroupRule(deviceCgroupRule string) error {
if !deviceCgroupRuleRegex.MatchString(deviceCgroupRule) {
return errors.Errorf("invalid device cgroup rule format: '%s'", deviceCgroupRule)
}
return nil
}
// parseDeviceCgroupRule matches and parses the deviceCgroupRule into slice
func parseDeviceCgroupRule(deviceCgroupRule string) [][]string {
return deviceCgroupRuleRegex.FindAllStringSubmatch(deviceCgroupRule, -1)
}

View File

@ -1,107 +0,0 @@
package createconfig
import (
"fmt"
"net"
"strconv"
"github.com/docker/go-connections/nat"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// ExposedPorts parses user and image ports and returns binding information
func ExposedPorts(expose, publish []string, publishAll bool, imageExposedPorts map[string]struct{}) (map[nat.Port][]nat.PortBinding, error) {
containerPorts := make(map[string]string)
// add expose ports from the image itself
for expose := range imageExposedPorts {
_, port := nat.SplitProtoPort(expose)
containerPorts[port] = ""
}
// add the expose ports from the user (--expose)
// can be single or a range
for _, expose := range expose {
//support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
_, port := nat.SplitProtoPort(expose)
//parse the start and end port and create a sequence of ports to expose
//if expose a port, the start and end port are the same
start, end, err := nat.ParsePortRange(port)
if err != nil {
return nil, fmt.Errorf("invalid range format for --expose: %s, error: %s", expose, err)
}
for i := start; i <= end; i++ {
containerPorts[strconv.Itoa(int(i))] = ""
}
}
// parse user inputted port bindings
pbPorts, portBindings, err := nat.ParsePortSpecs(publish)
if err != nil {
return nil, err
}
// delete exposed container ports if being used by -p
for i := range pbPorts {
delete(containerPorts, i.Port())
}
// iterate container ports and make port bindings from them
if publishAll {
for e := range containerPorts {
//support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>]
//proto, port := nat.SplitProtoPort(e)
p, err := nat.NewPort("tcp", e)
if err != nil {
return nil, err
}
rp, err := getRandomPort()
if err != nil {
return nil, err
}
logrus.Debug(fmt.Sprintf("Using random host port %d with container port %d", rp, p.Int()))
portBindings[p] = CreatePortBinding(rp, "")
}
}
// We need to see if any host ports are not populated and if so, we need to assign a
// random port to them.
for k, pb := range portBindings {
if pb[0].HostPort == "" {
hostPort, err := getRandomPort()
if err != nil {
return nil, err
}
logrus.Debug(fmt.Sprintf("Using random host port %d with container port %s", hostPort, k.Port()))
pb[0].HostPort = strconv.Itoa(hostPort)
}
}
return portBindings, nil
}
func getRandomPort() (int, error) {
l, err := net.Listen("tcp", ":0")
if err != nil {
return 0, errors.Wrapf(err, "unable to get free port")
}
defer l.Close()
_, randomPort, err := net.SplitHostPort(l.Addr().String())
if err != nil {
return 0, errors.Wrapf(err, "unable to determine free port")
}
rp, err := strconv.Atoi(randomPort)
if err != nil {
return 0, errors.Wrapf(err, "unable to convert random port to int")
}
return rp, nil
}
//CreatePortBinding takes port (int) and IP (string) and creates an array of portbinding structs
func CreatePortBinding(hostPort int, hostIP string) []nat.PortBinding {
pb := nat.PortBinding{
HostPort: strconv.Itoa(hostPort),
}
pb.HostIP = hostIP
return []nat.PortBinding{pb}
}

View File

@ -1,204 +0,0 @@
package createconfig
import (
"fmt"
"strings"
"github.com/containers/common/pkg/capabilities"
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/util"
"github.com/opencontainers/runtime-tools/generate"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// ToCreateOptions convert the SecurityConfig to a slice of container create
// options.
func (c *SecurityConfig) ToCreateOptions() ([]libpod.CtrCreateOption, error) {
options := make([]libpod.CtrCreateOption, 0)
options = append(options, libpod.WithSecLabels(c.LabelOpts))
options = append(options, libpod.WithPrivileged(c.Privileged))
return options, nil
}
// SetLabelOpts sets the label options of the SecurityConfig according to the
// input.
func (c *SecurityConfig) SetLabelOpts(runtime *libpod.Runtime, pidConfig *PidConfig, ipcConfig *IpcConfig) error {
if c.Privileged {
c.LabelOpts = label.DisableSecOpt()
return nil
}
var labelOpts []string
if pidConfig.PidMode.IsHost() {
labelOpts = append(labelOpts, label.DisableSecOpt()...)
} else if pidConfig.PidMode.IsContainer() {
ctr, err := runtime.LookupContainer(pidConfig.PidMode.Container())
if err != nil {
return errors.Wrapf(err, "container %q not found", pidConfig.PidMode.Container())
}
secopts, err := label.DupSecOpt(ctr.ProcessLabel())
if err != nil {
return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel())
}
labelOpts = append(labelOpts, secopts...)
}
if ipcConfig.IpcMode.IsHost() {
labelOpts = append(labelOpts, label.DisableSecOpt()...)
} else if ipcConfig.IpcMode.IsContainer() {
ctr, err := runtime.LookupContainer(ipcConfig.IpcMode.Container())
if err != nil {
return errors.Wrapf(err, "container %q not found", ipcConfig.IpcMode.Container())
}
secopts, err := label.DupSecOpt(ctr.ProcessLabel())
if err != nil {
return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel())
}
labelOpts = append(labelOpts, secopts...)
}
c.LabelOpts = append(c.LabelOpts, labelOpts...)
return nil
}
// SetSecurityOpts the the security options (labels, apparmor, seccomp, etc.).
func (c *SecurityConfig) SetSecurityOpts(runtime *libpod.Runtime, securityOpts []string) error {
for _, opt := range securityOpts {
if opt == "no-new-privileges" {
c.NoNewPrivs = true
} else {
con := strings.SplitN(opt, "=", 2)
if len(con) != 2 {
return fmt.Errorf("invalid --security-opt 1: %q", opt)
}
switch con[0] {
case "proc-opts":
c.ProcOpts = strings.Split(con[1], ",")
case "label":
c.LabelOpts = append(c.LabelOpts, con[1])
case "apparmor":
c.ApparmorProfile = con[1]
case "seccomp":
c.SeccompProfilePath = con[1]
default:
return fmt.Errorf("invalid --security-opt 2: %q", opt)
}
}
}
if c.SeccompProfilePath == "" {
var err error
c.SeccompProfilePath, err = libpod.DefaultSeccompPath()
if err != nil {
return err
}
}
c.SecurityOpts = securityOpts
return nil
}
// ConfigureGenerator configures the generator according to the input.
func (c *SecurityConfig) ConfigureGenerator(g *generate.Generator, user *UserConfig) error {
// HANDLE CAPABILITIES
// NOTE: Must happen before SECCOMP
if c.Privileged {
g.SetupPrivileged(true)
}
useNotRoot := func(user string) bool {
if user == "" || user == "root" || user == "0" {
return false
}
return true
}
configSpec := g.Config
var err error
var defaultCaplist []string
bounding := configSpec.Process.Capabilities.Bounding
if useNotRoot(user.User) {
configSpec.Process.Capabilities.Bounding = defaultCaplist
}
defaultCaplist, err = capabilities.MergeCapabilities(configSpec.Process.Capabilities.Bounding, c.CapAdd, c.CapDrop)
if err != nil {
return err
}
privCapRequired := []string{}
if !c.Privileged && len(c.CapRequired) > 0 {
// Pass CapRequired in CapAdd field to normalize capabilities names
capRequired, err := capabilities.MergeCapabilities(nil, c.CapRequired, nil)
if err != nil {
logrus.Errorf("capabilities requested by user or image are not valid: %q", strings.Join(c.CapRequired, ","))
} else {
// Verify all capRequiered are in the defaultCapList
for _, cap := range capRequired {
if !util.StringInSlice(cap, defaultCaplist) {
privCapRequired = append(privCapRequired, cap)
}
}
}
if len(privCapRequired) == 0 {
defaultCaplist = capRequired
} else {
logrus.Errorf("capabilities requested by user or image are not allowed by default: %q", strings.Join(privCapRequired, ","))
}
}
configSpec.Process.Capabilities.Bounding = defaultCaplist
configSpec.Process.Capabilities.Permitted = defaultCaplist
configSpec.Process.Capabilities.Inheritable = defaultCaplist
configSpec.Process.Capabilities.Effective = defaultCaplist
configSpec.Process.Capabilities.Ambient = defaultCaplist
if useNotRoot(user.User) {
defaultCaplist, err = capabilities.MergeCapabilities(bounding, c.CapAdd, c.CapDrop)
if err != nil {
return err
}
}
configSpec.Process.Capabilities.Bounding = defaultCaplist
// HANDLE SECCOMP
if c.SeccompProfilePath != "unconfined" {
seccompConfig, err := getSeccompConfig(c, configSpec)
if err != nil {
return err
}
configSpec.Linux.Seccomp = seccompConfig
}
// Clear default Seccomp profile from Generator for privileged containers
if c.SeccompProfilePath == "unconfined" || c.Privileged {
configSpec.Linux.Seccomp = nil
}
for _, opt := range c.SecurityOpts {
// Split on both : and =
splitOpt := strings.SplitN(opt, "=", 2)
if len(splitOpt) == 1 {
splitOpt = strings.Split(opt, ":")
}
if len(splitOpt) < 2 {
continue
}
switch splitOpt[0] {
case "label":
configSpec.Annotations[define.InspectAnnotationLabel] = splitOpt[1]
case "seccomp":
configSpec.Annotations[define.InspectAnnotationSeccomp] = splitOpt[1]
case "apparmor":
configSpec.Annotations[define.InspectAnnotationApparmor] = splitOpt[1]
}
}
g.SetRootReadonly(c.ReadOnlyRootfs)
for sysctlKey, sysctlVal := range c.Sysctl {
g.AddLinuxSysctl(sysctlKey, sysctlVal)
}
return nil
}

View File

@ -1,593 +0,0 @@
package createconfig
import (
"strings"
"github.com/containers/common/pkg/capabilities"
cconfig "github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/sysinfo"
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/cgroups"
"github.com/containers/podman/v2/pkg/env"
"github.com/containers/podman/v2/pkg/rootless"
"github.com/containers/podman/v2/pkg/util"
"github.com/docker/go-units"
"github.com/opencontainers/runc/libcontainer/user"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/generate"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"golang.org/x/sys/unix"
)
const CpuPeriod = 100000
func GetAvailableGids() (int64, error) {
idMap, err := user.ParseIDMapFile("/proc/self/gid_map")
if err != nil {
return 0, err
}
count := int64(0)
for _, r := range idMap {
count += r.Count
}
return count, nil
}
// CreateConfigToOCISpec parses information needed to create a container into an OCI runtime spec
func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userMounts []spec.Mount) (*spec.Spec, error) {
cgroupPerm := "ro"
g, err := generate.New("linux")
if err != nil {
return nil, err
}
// Remove the default /dev/shm mount to ensure we overwrite it
g.RemoveMount("/dev/shm")
g.HostSpecific = true
addCgroup := true
canMountSys := true
isRootless := rootless.IsRootless()
inUserNS := config.User.InNS(isRootless)
if inUserNS && config.Network.NetMode.IsHost() {
canMountSys = false
}
if config.Security.Privileged && canMountSys {
cgroupPerm = "rw"
g.RemoveMount("/sys")
sysMnt := spec.Mount{
Destination: "/sys",
Type: "sysfs",
Source: "sysfs",
Options: []string{"rprivate", "nosuid", "noexec", "nodev", "rw"},
}
g.AddMount(sysMnt)
} else if !canMountSys {
addCgroup = false
g.RemoveMount("/sys")
r := "ro"
if config.Security.Privileged {
r = "rw"
}
sysMnt := spec.Mount{
Destination: "/sys",
Type: TypeBind,
Source: "/sys",
Options: []string{"rprivate", "nosuid", "noexec", "nodev", r, "rbind"},
}
g.AddMount(sysMnt)
if !config.Security.Privileged && isRootless {
g.AddLinuxMaskedPaths("/sys/kernel")
}
}
var runtimeConfig *cconfig.Config
if runtime != nil {
runtimeConfig, err = runtime.GetConfig()
if err != nil {
return nil, err
}
g.Config.Process.Capabilities.Bounding = runtimeConfig.Containers.DefaultCapabilities
sysctls, err := util.ValidateSysctls(runtimeConfig.Containers.DefaultSysctls)
if err != nil {
return nil, err
}
for name, val := range config.Security.Sysctl {
sysctls[name] = val
}
config.Security.Sysctl = sysctls
if !util.StringInSlice("host", config.Resources.Ulimit) {
config.Resources.Ulimit = append(runtimeConfig.Containers.DefaultUlimits, config.Resources.Ulimit...)
}
if config.Resources.PidsLimit < 0 && !config.cgroupDisabled() {
config.Resources.PidsLimit = runtimeConfig.Containers.PidsLimit
}
} else {
g.Config.Process.Capabilities.Bounding = cconfig.DefaultCapabilities
if config.Resources.PidsLimit < 0 && !config.cgroupDisabled() {
config.Resources.PidsLimit = cconfig.DefaultPidsLimit
}
}
gid5Available := true
if isRootless {
nGids, err := GetAvailableGids()
if err != nil {
return nil, err
}
gid5Available = nGids >= 5
}
// When using a different user namespace, check that the GID 5 is mapped inside
// the container.
if gid5Available && len(config.User.IDMappings.GIDMap) > 0 {
mappingFound := false
for _, r := range config.User.IDMappings.GIDMap {
if r.ContainerID <= 5 && 5 < r.ContainerID+r.Size {
mappingFound = true
break
}
}
if !mappingFound {
gid5Available = false
}
}
if !gid5Available {
// If we have no GID mappings, the gid=5 default option would fail, so drop it.
g.RemoveMount("/dev/pts")
devPts := spec.Mount{
Destination: "/dev/pts",
Type: "devpts",
Source: "devpts",
Options: []string{"rprivate", "nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620"},
}
g.AddMount(devPts)
}
if inUserNS && config.Ipc.IpcMode.IsHost() {
g.RemoveMount("/dev/mqueue")
devMqueue := spec.Mount{
Destination: "/dev/mqueue",
Type: TypeBind,
Source: "/dev/mqueue",
Options: []string{"bind", "nosuid", "noexec", "nodev"},
}
g.AddMount(devMqueue)
}
if inUserNS && config.Pid.PidMode.IsHost() {
g.RemoveMount("/proc")
procMount := spec.Mount{
Destination: "/proc",
Type: TypeBind,
Source: "/proc",
Options: []string{"rbind", "nosuid", "noexec", "nodev"},
}
g.AddMount(procMount)
}
if addCgroup {
cgroupMnt := spec.Mount{
Destination: "/sys/fs/cgroup",
Type: "cgroup",
Source: "cgroup",
Options: []string{"rprivate", "nosuid", "noexec", "nodev", "relatime", cgroupPerm},
}
g.AddMount(cgroupMnt)
}
g.SetProcessCwd(config.WorkDir)
ProcessArgs := make([]string, 0)
// We need to iterate the input for entrypoint because it is a []string
// but "" is a legit json input, which translates into a []string with an
// empty position. This messes up the eventual command being executed
// in the container
for _, a := range config.Entrypoint {
if len(a) > 0 {
ProcessArgs = append(ProcessArgs, a)
}
}
// Same issue as explained above for config.Entrypoint.
for _, a := range config.Command {
if len(a) > 0 {
ProcessArgs = append(ProcessArgs, a)
}
}
g.SetProcessArgs(ProcessArgs)
g.SetProcessTerminal(config.Tty)
for key, val := range config.Annotations {
g.AddAnnotation(key, val)
}
addedResources := false
// RESOURCES - MEMORY
if config.Resources.Memory != 0 {
g.SetLinuxResourcesMemoryLimit(config.Resources.Memory)
// If a swap limit is not explicitly set, also set a swap limit
// Default to double the memory limit
if config.Resources.MemorySwap == 0 {
g.SetLinuxResourcesMemorySwap(2 * config.Resources.Memory)
}
addedResources = true
}
if config.Resources.MemoryReservation != 0 {
g.SetLinuxResourcesMemoryReservation(config.Resources.MemoryReservation)
addedResources = true
}
if config.Resources.MemorySwap != 0 {
g.SetLinuxResourcesMemorySwap(config.Resources.MemorySwap)
addedResources = true
}
if config.Resources.KernelMemory != 0 {
g.SetLinuxResourcesMemoryKernel(config.Resources.KernelMemory)
addedResources = true
}
if config.Resources.MemorySwappiness != -1 {
g.SetLinuxResourcesMemorySwappiness(uint64(config.Resources.MemorySwappiness))
addedResources = true
}
g.SetLinuxResourcesMemoryDisableOOMKiller(config.Resources.DisableOomKiller)
g.SetProcessOOMScoreAdj(config.Resources.OomScoreAdj)
// RESOURCES - CPU
if config.Resources.CPUShares != 0 {
g.SetLinuxResourcesCPUShares(config.Resources.CPUShares)
addedResources = true
}
if config.Resources.CPUQuota != 0 {
g.SetLinuxResourcesCPUQuota(config.Resources.CPUQuota)
addedResources = true
}
if config.Resources.CPUPeriod != 0 {
g.SetLinuxResourcesCPUPeriod(config.Resources.CPUPeriod)
addedResources = true
}
if config.Resources.CPUs != 0 {
g.SetLinuxResourcesCPUPeriod(CpuPeriod)
g.SetLinuxResourcesCPUQuota(int64(config.Resources.CPUs * CpuPeriod))
addedResources = true
}
if config.Resources.CPURtRuntime != 0 {
g.SetLinuxResourcesCPURealtimeRuntime(config.Resources.CPURtRuntime)
addedResources = true
}
if config.Resources.CPURtPeriod != 0 {
g.SetLinuxResourcesCPURealtimePeriod(config.Resources.CPURtPeriod)
addedResources = true
}
if config.Resources.CPUsetCPUs != "" {
g.SetLinuxResourcesCPUCpus(config.Resources.CPUsetCPUs)
addedResources = true
}
if config.Resources.CPUsetMems != "" {
g.SetLinuxResourcesCPUMems(config.Resources.CPUsetMems)
addedResources = true
}
// Devices
if config.Security.Privileged {
// If privileged, we need to add all the host devices to the
// spec. We do not add the user provided ones because we are
// already adding them all.
if err := AddPrivilegedDevices(&g); err != nil {
return nil, err
}
} else {
for _, devicePath := range config.Devices {
if err := DevicesFromPath(&g, devicePath); err != nil {
return nil, err
}
}
if len(config.Resources.DeviceCgroupRules) != 0 {
if err := deviceCgroupRules(&g, config.Resources.DeviceCgroupRules); err != nil {
return nil, err
}
addedResources = true
}
}
g.SetProcessNoNewPrivileges(config.Security.NoNewPrivs)
if !config.Security.Privileged {
g.SetProcessApparmorProfile(config.Security.ApparmorProfile)
}
// Unless already set via the CLI, check if we need to disable process
// labels or set the defaults.
if len(config.Security.LabelOpts) == 0 && runtimeConfig != nil {
if !runtimeConfig.Containers.EnableLabeling {
// Disabled in the config.
config.Security.LabelOpts = append(config.Security.LabelOpts, "disable")
} else if err := config.Security.SetLabelOpts(runtime, &config.Pid, &config.Ipc); err != nil {
// Defaults!
return nil, err
}
}
BlockAccessToKernelFilesystems(config.Security.Privileged, config.Pid.PidMode.IsHost(), &g)
// RESOURCES - PIDS
if config.Resources.PidsLimit > 0 {
// if running on rootless on a cgroupv1 machine or using the cgroupfs manager, pids
// limit is not supported. If the value is still the default
// then ignore the settings. If the caller asked for a
// non-default, then try to use it.
setPidLimit := true
if rootless.IsRootless() {
cgroup2, err := cgroups.IsCgroup2UnifiedMode()
if err != nil {
return nil, err
}
if (!cgroup2 || (runtimeConfig != nil && runtimeConfig.Engine.CgroupManager != cconfig.SystemdCgroupsManager)) && config.Resources.PidsLimit == sysinfo.GetDefaultPidsLimit() {
setPidLimit = false
}
}
if setPidLimit {
g.SetLinuxResourcesPidsLimit(config.Resources.PidsLimit)
addedResources = true
}
}
// Make sure to always set the default variables unless overridden in the
// config.
var defaultEnv map[string]string
if runtimeConfig == nil {
defaultEnv = env.DefaultEnvVariables()
} else {
defaultEnv, err = env.ParseSlice(runtimeConfig.Containers.Env)
if err != nil {
return nil, errors.Wrap(err, "Env fields in containers.conf failed to parse")
}
defaultEnv = env.Join(env.DefaultEnvVariables(), defaultEnv)
}
if err := addRlimits(config, &g); err != nil {
return nil, err
}
// NAMESPACES
if err := config.Pid.ConfigureGenerator(&g); err != nil {
return nil, err
}
if err := config.User.ConfigureGenerator(&g); err != nil {
return nil, err
}
if err := config.Network.ConfigureGenerator(&g); err != nil {
return nil, err
}
if err := config.Uts.ConfigureGenerator(&g, &config.Network, runtime); err != nil {
return nil, err
}
if err := config.Ipc.ConfigureGenerator(&g); err != nil {
return nil, err
}
if err := config.Cgroup.ConfigureGenerator(&g); err != nil {
return nil, err
}
config.Env = env.Join(defaultEnv, config.Env)
for name, val := range config.Env {
g.AddProcessEnv(name, val)
}
configSpec := g.Config
// If the container image specifies an label with a
// capabilities.ContainerImageLabel then split the comma separated list
// of capabilities and record them. This list indicates the only
// capabilities, required to run the container.
var capRequired []string
for key, val := range config.Labels {
if util.StringInSlice(key, capabilities.ContainerImageLabels) {
capRequired = strings.Split(val, ",")
}
}
config.Security.CapRequired = capRequired
if err := config.Security.ConfigureGenerator(&g, &config.User); err != nil {
return nil, err
}
// BIND MOUNTS
configSpec.Mounts = SupercedeUserMounts(userMounts, configSpec.Mounts)
// Process mounts to ensure correct options
if err := InitFSMounts(configSpec.Mounts); err != nil {
return nil, err
}
// BLOCK IO
blkio, err := config.CreateBlockIO()
if err != nil {
return nil, errors.Wrapf(err, "error creating block io")
}
if blkio != nil {
configSpec.Linux.Resources.BlockIO = blkio
addedResources = true
}
if rootless.IsRootless() {
cgroup2, err := cgroups.IsCgroup2UnifiedMode()
if err != nil {
return nil, err
}
if !addedResources {
configSpec.Linux.Resources = &spec.LinuxResources{}
}
canUseResources := cgroup2 && runtimeConfig != nil && (runtimeConfig.Engine.CgroupManager == cconfig.SystemdCgroupsManager)
if addedResources && !canUseResources {
return nil, errors.New("invalid configuration, cannot specify resource limits without cgroups v2 and --cgroup-manager=systemd")
}
if !canUseResources {
// Force the resources block to be empty instead of having default values.
configSpec.Linux.Resources = &spec.LinuxResources{}
}
}
switch config.Cgroup.Cgroups {
case "disabled":
if addedResources {
return nil, errors.New("cannot specify resource limits when cgroups are disabled is specified")
}
configSpec.Linux.Resources = &spec.LinuxResources{}
case "enabled", "no-conmon", "":
// Do nothing
default:
return nil, errors.New("unrecognized option for cgroups; supported are 'default', 'disabled', 'no-conmon'")
}
// Add annotations
if configSpec.Annotations == nil {
configSpec.Annotations = make(map[string]string)
}
if config.CidFile != "" {
configSpec.Annotations[define.InspectAnnotationCIDFile] = config.CidFile
}
if config.Rm {
configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseTrue
} else {
configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseFalse
}
if len(config.VolumesFrom) > 0 {
configSpec.Annotations[define.InspectAnnotationVolumesFrom] = strings.Join(config.VolumesFrom, ",")
}
if config.Security.Privileged {
configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseTrue
} else {
configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseFalse
}
if config.Init {
configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseTrue
} else {
configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseFalse
}
return configSpec, nil
}
func (config *CreateConfig) cgroupDisabled() bool {
return config.Cgroup.Cgroups == "disabled"
}
func BlockAccessToKernelFilesystems(privileged, pidModeIsHost bool, g *generate.Generator) {
if !privileged {
for _, mp := range []string{
"/proc/acpi",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware",
"/sys/fs/selinux",
} {
g.AddLinuxMaskedPaths(mp)
}
if pidModeIsHost && rootless.IsRootless() {
return
}
for _, rp := range []string{
"/proc/asound",
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger",
} {
g.AddLinuxReadonlyPaths(rp)
}
}
}
func addRlimits(config *CreateConfig, g *generate.Generator) error {
var (
isRootless = rootless.IsRootless()
nofileSet = false
nprocSet = false
)
for _, u := range config.Resources.Ulimit {
if u == "host" {
if len(config.Resources.Ulimit) != 1 {
return errors.New("ulimit can use host only once")
}
g.Config.Process.Rlimits = nil
break
}
ul, err := units.ParseUlimit(u)
if err != nil {
return errors.Wrapf(err, "ulimit option %q requires name=SOFT:HARD, failed to be parsed", u)
}
if ul.Name == "nofile" {
nofileSet = true
} else if ul.Name == "nproc" {
nprocSet = true
}
g.AddProcessRlimits("RLIMIT_"+strings.ToUpper(ul.Name), uint64(ul.Hard), uint64(ul.Soft))
}
// If not explicitly overridden by the user, default number of open
// files and number of processes to the maximum they can be set to
// (without overriding a sysctl)
if !nofileSet {
max := define.RLimitDefaultValue
current := define.RLimitDefaultValue
if isRootless {
var rlimit unix.Rlimit
if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &rlimit); err != nil {
logrus.Warnf("failed to return RLIMIT_NOFILE ulimit %q", err)
}
if rlimit.Cur < current {
current = rlimit.Cur
}
if rlimit.Max < max {
max = rlimit.Max
}
}
g.AddProcessRlimits("RLIMIT_NOFILE", max, current)
}
if !nprocSet {
max := define.RLimitDefaultValue
current := define.RLimitDefaultValue
if isRootless {
var rlimit unix.Rlimit
if err := unix.Getrlimit(unix.RLIMIT_NPROC, &rlimit); err != nil {
logrus.Warnf("failed to return RLIMIT_NPROC ulimit %q", err)
}
if rlimit.Cur < current {
current = rlimit.Cur
}
if rlimit.Max < max {
max = rlimit.Max
}
}
g.AddProcessRlimits("RLIMIT_NPROC", max, current)
}
return nil
}

View File

@ -1,108 +0,0 @@
package createconfig
import (
"runtime"
"testing"
"github.com/containers/common/pkg/sysinfo"
"github.com/containers/podman/v2/pkg/cgroups"
"github.com/containers/podman/v2/pkg/rootless"
"github.com/containers/storage"
"github.com/containers/storage/pkg/idtools"
"github.com/docker/go-units"
"github.com/stretchr/testify/assert"
)
var (
sysInfo = sysinfo.New(true)
)
// Make createconfig to test with
func makeTestCreateConfig() *CreateConfig {
cc := new(CreateConfig)
cc.Resources = CreateResourceConfig{}
cc.User.IDMappings = new(storage.IDMappingOptions)
cc.User.IDMappings.UIDMap = []idtools.IDMap{}
cc.User.IDMappings.GIDMap = []idtools.IDMap{}
return cc
}
func doCommonSkipChecks(t *testing.T) {
// The default configuration of podman enables seccomp, which is not available on non-Linux systems.
// Thus, any tests that use the default seccomp setting would fail.
// Skip the tests on non-Linux platforms rather than explicitly disable seccomp in the test and possibly affect the test result.
if runtime.GOOS != "linux" {
t.Skip("seccomp, which is enabled by default, is only supported on Linux")
}
if rootless.IsRootless() {
isCgroupV2, err := cgroups.IsCgroup2UnifiedMode()
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if !isCgroupV2 {
t.Skip("cgroups v1 cannot be used when rootless")
}
}
}
// TestPIDsLimit verifies the given pid-limit is correctly defined in the spec
func TestPIDsLimit(t *testing.T) {
doCommonSkipChecks(t)
if !sysInfo.PidsLimit {
t.Skip("running test not supported by the host system")
}
cc := makeTestCreateConfig()
cc.Resources.PidsLimit = 22
spec, err := cc.createConfigToOCISpec(nil, nil)
assert.NoError(t, err)
assert.Equal(t, spec.Linux.Resources.Pids.Limit, int64(22))
}
// TestBLKIOWeightDevice verifies the given blkio weight is correctly set in the
// spec.
func TestBLKIOWeightDevice(t *testing.T) {
doCommonSkipChecks(t)
if !sysInfo.BlkioWeightDevice {
t.Skip("running test not supported by the host system")
}
cc := makeTestCreateConfig()
cc.Resources.BlkioWeightDevice = []string{"/dev/zero:100"}
spec, err := cc.createConfigToOCISpec(nil, nil)
assert.NoError(t, err)
// /dev/zero is guaranteed 1,5 by the Linux kernel
assert.Equal(t, spec.Linux.Resources.BlockIO.WeightDevice[0].Major, int64(1))
assert.Equal(t, spec.Linux.Resources.BlockIO.WeightDevice[0].Minor, int64(5))
assert.Equal(t, *(spec.Linux.Resources.BlockIO.WeightDevice[0].Weight), uint16(100))
}
// TestMemorySwap verifies that the given swap memory limit is correctly set in
// the spec.
func TestMemorySwap(t *testing.T) {
doCommonSkipChecks(t)
if !sysInfo.SwapLimit {
t.Skip("running test not supported by the host system")
}
swapLimit, err := units.RAMInBytes("45m")
assert.NoError(t, err)
cc := makeTestCreateConfig()
cc.Resources.MemorySwap = swapLimit
spec, err := cc.createConfigToOCISpec(nil, nil)
assert.NoError(t, err)
assert.Equal(t, *(spec.Linux.Resources.Memory.Swap), swapLimit)
}

View File

@ -1,875 +0,0 @@
package createconfig
import (
"fmt"
"os"
"path"
"path/filepath"
"strings"
"github.com/containers/buildah/pkg/parse"
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/pkg/util"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
const (
// TypeBind is the type for mounting host dir
TypeBind = "bind"
// TypeVolume is the type for named volumes
TypeVolume = "volume"
// TypeTmpfs is the type for mounting tmpfs
TypeTmpfs = "tmpfs"
)
var (
errDuplicateDest = errors.Errorf("duplicate mount destination")
optionArgError = errors.Errorf("must provide an argument for option")
noDestError = errors.Errorf("must set volume destination")
)
// Parse all volume-related options in the create config into a set of mounts
// and named volumes to add to the container.
// Handles --volumes-from, --volumes, --tmpfs, --init, and --init-path flags.
// TODO: Named volume options - should we default to rprivate? It bakes into a
// bind mount under the hood...
// TODO: handle options parsing/processing via containers/storage/pkg/mount
func (config *CreateConfig) parseVolumes(runtime *libpod.Runtime) ([]spec.Mount, []*libpod.ContainerNamedVolume, error) {
// Add image volumes.
baseMounts, baseVolumes, err := config.getImageVolumes()
if err != nil {
return nil, nil, err
}
// Add --volumes-from.
// Overrides image volumes unconditionally.
vFromMounts, vFromVolumes, err := config.getVolumesFrom(runtime)
if err != nil {
return nil, nil, err
}
for dest, mount := range vFromMounts {
baseMounts[dest] = mount
}
for dest, volume := range vFromVolumes {
baseVolumes[dest] = volume
}
// Next mounts from the --mounts flag.
// Do not override yet.
unifiedMounts, unifiedVolumes, err := config.getMounts()
if err != nil {
return nil, nil, err
}
// Next --volumes flag.
// Do not override yet.
volumeMounts, volumeVolumes, err := config.getVolumeMounts()
if err != nil {
return nil, nil, err
}
// Next --tmpfs flag.
// Do not override yet.
tmpfsMounts, err := config.getTmpfsMounts()
if err != nil {
return nil, nil, err
}
// Unify mounts from --mount, --volume, --tmpfs.
// Also add mounts + volumes directly from createconfig.
// Start with --volume.
for dest, mount := range volumeMounts {
if _, ok := unifiedMounts[dest]; ok {
return nil, nil, errors.Wrapf(errDuplicateDest, dest)
}
unifiedMounts[dest] = mount
}
for dest, volume := range volumeVolumes {
if _, ok := unifiedVolumes[dest]; ok {
return nil, nil, errors.Wrapf(errDuplicateDest, dest)
}
unifiedVolumes[dest] = volume
}
// Now --tmpfs
for dest, tmpfs := range tmpfsMounts {
if _, ok := unifiedMounts[dest]; ok {
return nil, nil, errors.Wrapf(errDuplicateDest, dest)
}
unifiedMounts[dest] = tmpfs
}
// Now spec mounts and volumes
for _, mount := range config.Mounts {
dest := mount.Destination
if _, ok := unifiedMounts[dest]; ok {
return nil, nil, errors.Wrapf(errDuplicateDest, dest)
}
unifiedMounts[dest] = mount
}
for _, volume := range config.NamedVolumes {
dest := volume.Dest
if _, ok := unifiedVolumes[dest]; ok {
return nil, nil, errors.Wrapf(errDuplicateDest, dest)
}
unifiedVolumes[dest] = volume
}
// If requested, add container init binary
if config.Init {
initPath := config.InitPath
if initPath == "" {
rtc, err := runtime.GetConfig()
if err != nil {
return nil, nil, err
}
initPath = rtc.Engine.InitPath
}
initMount, err := config.addContainerInitBinary(initPath)
if err != nil {
return nil, nil, err
}
if _, ok := unifiedMounts[initMount.Destination]; ok {
return nil, nil, errors.Wrapf(errDuplicateDest, "conflict with mount added by --init to %q", initMount.Destination)
}
unifiedMounts[initMount.Destination] = initMount
}
// Before superseding, we need to find volume mounts which conflict with
// named volumes, and vice versa.
// We'll delete the conflicts here as we supersede.
for dest := range unifiedMounts {
if _, ok := baseVolumes[dest]; ok {
delete(baseVolumes, dest)
}
}
for dest := range unifiedVolumes {
if _, ok := baseMounts[dest]; ok {
delete(baseMounts, dest)
}
}
// Supersede volumes-from/image volumes with unified volumes from above.
// This is an unconditional replacement.
for dest, mount := range unifiedMounts {
baseMounts[dest] = mount
}
for dest, volume := range unifiedVolumes {
baseVolumes[dest] = volume
}
// If requested, add tmpfs filesystems for read-only containers.
if config.Security.ReadOnlyRootfs && config.Security.ReadOnlyTmpfs {
readonlyTmpfs := []string{"/tmp", "/var/tmp", "/run"}
options := []string{"rw", "rprivate", "nosuid", "nodev", "tmpcopyup"}
for _, dest := range readonlyTmpfs {
if _, ok := baseMounts[dest]; ok {
continue
}
if _, ok := baseVolumes[dest]; ok {
continue
}
localOpts := options
if dest == "/run" {
localOpts = append(localOpts, "noexec", "size=65536k")
} else {
localOpts = append(localOpts, "exec")
}
baseMounts[dest] = spec.Mount{
Destination: dest,
Type: "tmpfs",
Source: "tmpfs",
Options: localOpts,
}
}
}
// Check for conflicts between named volumes and mounts
for dest := range baseMounts {
if _, ok := baseVolumes[dest]; ok {
return nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
}
}
for dest := range baseVolumes {
if _, ok := baseMounts[dest]; ok {
return nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
}
}
// Final step: maps to arrays
finalMounts := make([]spec.Mount, 0, len(baseMounts))
for _, mount := range baseMounts {
if mount.Type == TypeBind {
absSrc, err := filepath.Abs(mount.Source)
if err != nil {
return nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source)
}
mount.Source = absSrc
}
finalMounts = append(finalMounts, mount)
}
finalVolumes := make([]*libpod.ContainerNamedVolume, 0, len(baseVolumes))
for _, volume := range baseVolumes {
finalVolumes = append(finalVolumes, volume)
}
return finalMounts, finalVolumes, nil
}
// Parse volumes from - a set of containers whose volumes we will mount in.
// Grab the containers, retrieve any user-created spec mounts and all named
// volumes, and return a list of them.
// Conflicts are resolved simply - the last container specified wins.
// Container names may be suffixed by mount options after a colon.
// TODO: We should clean these paths if possible
func (config *CreateConfig) getVolumesFrom(runtime *libpod.Runtime) (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) {
// Both of these are maps of mount destination to mount type.
// We ensure that each destination is only mounted to once in this way.
finalMounts := make(map[string]spec.Mount)
finalNamedVolumes := make(map[string]*libpod.ContainerNamedVolume)
for _, vol := range config.VolumesFrom {
var (
options = []string{}
err error
splitVol = strings.SplitN(vol, ":", 2)
)
if len(splitVol) == 2 {
splitOpts := strings.Split(splitVol[1], ",")
for _, checkOpt := range splitOpts {
switch checkOpt {
case "z", "ro", "rw":
// Do nothing, these are valid options
default:
return nil, nil, errors.Errorf("invalid options %q, can only specify 'ro', 'rw', and 'z'", splitVol[1])
}
}
if options, err = parse.ValidateVolumeOpts(splitOpts); err != nil {
return nil, nil, err
}
}
ctr, err := runtime.LookupContainer(splitVol[0])
if err != nil {
return nil, nil, errors.Wrapf(err, "error looking up container %q for volumes-from", splitVol[0])
}
logrus.Debugf("Adding volumes from container %s", ctr.ID())
// Look up the container's user volumes. This gets us the
// destinations of all mounts the user added to the container.
userVolumesArr := ctr.UserVolumes()
// We're going to need to access them a lot, so convert to a map
// to reduce looping.
// We'll also use the map to indicate if we missed any volumes along the way.
userVolumes := make(map[string]bool)
for _, dest := range userVolumesArr {
userVolumes[dest] = false
}
// Now we get the container's spec and loop through its volumes
// and append them in if we can find them.
spec := ctr.Spec()
if spec == nil {
return nil, nil, errors.Errorf("error retrieving container %s spec for volumes-from", ctr.ID())
}
for _, mnt := range spec.Mounts {
if mnt.Type != TypeBind {
continue
}
if _, exists := userVolumes[mnt.Destination]; exists {
userVolumes[mnt.Destination] = true
if len(options) != 0 {
mnt.Options = options
}
if _, ok := finalMounts[mnt.Destination]; ok {
logrus.Debugf("Overriding mount to %s with new mount from container %s", mnt.Destination, ctr.ID())
}
finalMounts[mnt.Destination] = mnt
}
}
// We're done with the spec mounts. Add named volumes.
// Add these unconditionally - none of them are automatically
// part of the container, as some spec mounts are.
namedVolumes := ctr.NamedVolumes()
for _, namedVol := range namedVolumes {
if _, exists := userVolumes[namedVol.Dest]; exists {
userVolumes[namedVol.Dest] = true
}
if len(options) != 0 {
namedVol.Options = options
}
if _, ok := finalMounts[namedVol.Dest]; ok {
logrus.Debugf("Overriding named volume mount to %s with new named volume from container %s", namedVol.Dest, ctr.ID())
}
finalNamedVolumes[namedVol.Dest] = namedVol
}
// Check if we missed any volumes
for volDest, found := range userVolumes {
if !found {
logrus.Warnf("Unable to match volume %s from container %s for volumes-from", volDest, ctr.ID())
}
}
}
return finalMounts, finalNamedVolumes, nil
}
// getMounts takes user-provided input from the --mount flag and creates OCI
// spec mounts and Libpod named volumes.
// podman run --mount type=bind,src=/etc/resolv.conf,target=/etc/resolv.conf ...
// podman run --mount type=tmpfs,target=/dev/shm ...
// podman run --mount type=volume,source=test-volume, ...
func (config *CreateConfig) getMounts() (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) {
finalMounts := make(map[string]spec.Mount)
finalNamedVolumes := make(map[string]*libpod.ContainerNamedVolume)
errInvalidSyntax := errors.Errorf("incorrect mount format: should be --mount type=<bind|tmpfs|volume>,[src=<host-dir|volume-name>,]target=<ctr-dir>[,options]")
// TODO(vrothberg): the manual parsing can be replaced with a regular expression
// to allow a more robust parsing of the mount format and to give
// precise errors regarding supported format versus supported options.
for _, mount := range config.MountsFlag {
arr := strings.SplitN(mount, ",", 2)
if len(arr) < 2 {
return nil, nil, errors.Wrapf(errInvalidSyntax, "%q", mount)
}
kv := strings.Split(arr[0], "=")
// TODO: type is not explicitly required in Docker.
// If not specified, it defaults to "volume".
if len(kv) != 2 || kv[0] != "type" {
return nil, nil, errors.Wrapf(errInvalidSyntax, "%q", mount)
}
tokens := strings.Split(arr[1], ",")
switch kv[1] {
case TypeBind:
mount, err := getBindMount(tokens)
if err != nil {
return nil, nil, err
}
if _, ok := finalMounts[mount.Destination]; ok {
return nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination)
}
finalMounts[mount.Destination] = mount
case TypeTmpfs:
mount, err := getTmpfsMount(tokens)
if err != nil {
return nil, nil, err
}
if _, ok := finalMounts[mount.Destination]; ok {
return nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination)
}
finalMounts[mount.Destination] = mount
case "volume":
volume, err := getNamedVolume(tokens)
if err != nil {
return nil, nil, err
}
if _, ok := finalNamedVolumes[volume.Dest]; ok {
return nil, nil, errors.Wrapf(errDuplicateDest, volume.Dest)
}
finalNamedVolumes[volume.Dest] = volume
default:
return nil, nil, errors.Errorf("invalid filesystem type %q", kv[1])
}
}
return finalMounts, finalNamedVolumes, nil
}
// Parse a single bind mount entry from the --mount flag.
func getBindMount(args []string) (spec.Mount, error) {
newMount := spec.Mount{
Type: TypeBind,
}
var setSource, setDest, setRORW, setSuid, setDev, setExec, setRelabel bool
for _, val := range args {
kv := strings.SplitN(val, "=", 2)
switch kv[0] {
case "bind-nonrecursive":
newMount.Options = append(newMount.Options, "bind")
case "ro", "rw":
if setRORW {
return newMount, errors.Wrapf(optionArgError, "cannot pass 'ro' or 'rw' options more than once")
}
setRORW = true
// Can be formatted as one of:
// ro
// ro=[true|false]
// rw
// rw=[true|false]
switch len(kv) {
case 1:
newMount.Options = append(newMount.Options, kv[0])
case 2:
switch strings.ToLower(kv[1]) {
case "true":
newMount.Options = append(newMount.Options, kv[0])
case "false":
// Set the opposite only for rw
// ro's opposite is the default
if kv[0] == "rw" {
newMount.Options = append(newMount.Options, "ro")
}
default:
return newMount, errors.Wrapf(optionArgError, "%s must be set to true or false, instead received %q", kv[0], kv[1])
}
default:
return newMount, errors.Wrapf(optionArgError, "badly formatted option %q", val)
}
case "nosuid", "suid":
if setSuid {
return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once")
}
setSuid = true
newMount.Options = append(newMount.Options, kv[0])
case "nodev", "dev":
if setDev {
return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once")
}
setDev = true
newMount.Options = append(newMount.Options, kv[0])
case "noexec", "exec":
if setExec {
return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once")
}
setExec = true
newMount.Options = append(newMount.Options, kv[0])
case "shared", "rshared", "private", "rprivate", "slave", "rslave", "unbindable", "runbindable", "Z", "z":
newMount.Options = append(newMount.Options, kv[0])
case "bind-propagation":
if len(kv) == 1 {
return newMount, errors.Wrapf(optionArgError, kv[0])
}
newMount.Options = append(newMount.Options, kv[1])
case "src", "source":
if len(kv) == 1 {
return newMount, errors.Wrapf(optionArgError, kv[0])
}
if err := parse.ValidateVolumeHostDir(kv[1]); err != nil {
return newMount, err
}
newMount.Source = kv[1]
setSource = true
case "target", "dst", "destination":
if len(kv) == 1 {
return newMount, errors.Wrapf(optionArgError, kv[0])
}
if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
return newMount, err
}
newMount.Destination = filepath.Clean(kv[1])
setDest = true
case "relabel":
if setRelabel {
return newMount, errors.Wrapf(optionArgError, "cannot pass 'relabel' option more than once")
}
setRelabel = true
if len(kv) != 2 {
return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0])
}
switch kv[1] {
case "private":
newMount.Options = append(newMount.Options, "z")
case "shared":
newMount.Options = append(newMount.Options, "Z")
default:
return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0])
}
default:
return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0])
}
}
if !setDest {
return newMount, noDestError
}
if !setSource {
newMount.Source = newMount.Destination
}
options, err := parse.ValidateVolumeOpts(newMount.Options)
if err != nil {
return newMount, err
}
newMount.Options = options
return newMount, nil
}
// Parse a single tmpfs mount entry from the --mount flag
func getTmpfsMount(args []string) (spec.Mount, error) {
newMount := spec.Mount{
Type: TypeTmpfs,
Source: TypeTmpfs,
}
var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup bool
for _, val := range args {
kv := strings.SplitN(val, "=", 2)
switch kv[0] {
case "tmpcopyup", "notmpcopyup":
if setTmpcopyup {
return newMount, errors.Wrapf(optionArgError, "cannot pass 'tmpcopyup' and 'notmpcopyup' options more than once")
}
setTmpcopyup = true
newMount.Options = append(newMount.Options, kv[0])
case "ro", "rw":
if setRORW {
return newMount, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once")
}
setRORW = true
newMount.Options = append(newMount.Options, kv[0])
case "nosuid", "suid":
if setSuid {
return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once")
}
setSuid = true
newMount.Options = append(newMount.Options, kv[0])
case "nodev", "dev":
if setDev {
return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once")
}
setDev = true
newMount.Options = append(newMount.Options, kv[0])
case "noexec", "exec":
if setExec {
return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once")
}
setExec = true
newMount.Options = append(newMount.Options, kv[0])
case "tmpfs-mode":
if len(kv) == 1 {
return newMount, errors.Wrapf(optionArgError, kv[0])
}
newMount.Options = append(newMount.Options, fmt.Sprintf("mode=%s", kv[1]))
case "tmpfs-size":
if len(kv) == 1 {
return newMount, errors.Wrapf(optionArgError, kv[0])
}
newMount.Options = append(newMount.Options, fmt.Sprintf("size=%s", kv[1]))
case "src", "source":
return newMount, errors.Errorf("source is not supported with tmpfs mounts")
case "target", "dst", "destination":
if len(kv) == 1 {
return newMount, errors.Wrapf(optionArgError, kv[0])
}
if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
return newMount, err
}
newMount.Destination = filepath.Clean(kv[1])
setDest = true
default:
return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0])
}
}
if !setDest {
return newMount, noDestError
}
return newMount, nil
}
// Parse a single volume mount entry from the --mount flag.
// Note that the volume-label option for named volumes is currently NOT supported.
// TODO: add support for --volume-label
func getNamedVolume(args []string) (*libpod.ContainerNamedVolume, error) {
newVolume := new(libpod.ContainerNamedVolume)
var setSource, setDest, setRORW, setSuid, setDev, setExec bool
for _, val := range args {
kv := strings.SplitN(val, "=", 2)
switch kv[0] {
case "ro", "rw":
if setRORW {
return nil, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once")
}
setRORW = true
newVolume.Options = append(newVolume.Options, kv[0])
case "nosuid", "suid":
if setSuid {
return nil, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once")
}
setSuid = true
newVolume.Options = append(newVolume.Options, kv[0])
case "nodev", "dev":
if setDev {
return nil, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once")
}
setDev = true
newVolume.Options = append(newVolume.Options, kv[0])
case "noexec", "exec":
if setExec {
return nil, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once")
}
setExec = true
newVolume.Options = append(newVolume.Options, kv[0])
case "volume-label":
return nil, errors.Errorf("the --volume-label option is not presently implemented")
case "src", "source":
if len(kv) == 1 {
return nil, errors.Wrapf(optionArgError, kv[0])
}
newVolume.Name = kv[1]
setSource = true
case "target", "dst", "destination":
if len(kv) == 1 {
return nil, errors.Wrapf(optionArgError, kv[0])
}
if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil {
return nil, err
}
newVolume.Dest = filepath.Clean(kv[1])
setDest = true
default:
return nil, errors.Wrapf(util.ErrBadMntOption, kv[0])
}
}
if !setSource {
return nil, errors.Errorf("must set source volume")
}
if !setDest {
return nil, noDestError
}
return newVolume, nil
}
func (config *CreateConfig) getVolumeMounts() (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) {
mounts := make(map[string]spec.Mount)
volumes := make(map[string]*libpod.ContainerNamedVolume)
volumeFormatErr := errors.Errorf("incorrect volume format, should be [host-dir:]ctr-dir[:option]")
for _, vol := range config.Volumes {
var (
options []string
src string
dest string
err error
)
splitVol := strings.Split(vol, ":")
if len(splitVol) > 3 {
return nil, nil, errors.Wrapf(volumeFormatErr, vol)
}
src = splitVol[0]
if len(splitVol) == 1 {
// This is an anonymous named volume. Only thing given
// is destination.
// Name/source will be blank, and populated by libpod.
src = ""
dest = splitVol[0]
} else if len(splitVol) > 1 {
dest = splitVol[1]
}
if len(splitVol) > 2 {
if options, err = parse.ValidateVolumeOpts(strings.Split(splitVol[2], ",")); err != nil {
return nil, nil, err
}
}
// Do not check source dir for anonymous volumes
if len(splitVol) > 1 {
if err := parse.ValidateVolumeHostDir(src); err != nil {
return nil, nil, err
}
}
if err := parse.ValidateVolumeCtrDir(dest); err != nil {
return nil, nil, err
}
cleanDest := filepath.Clean(dest)
if strings.HasPrefix(src, "/") || strings.HasPrefix(src, ".") {
// This is not a named volume
newMount := spec.Mount{
Destination: cleanDest,
Type: string(TypeBind),
Source: src,
Options: options,
}
if _, ok := mounts[newMount.Destination]; ok {
return nil, nil, errors.Wrapf(errDuplicateDest, newMount.Destination)
}
mounts[newMount.Destination] = newMount
} else {
// This is a named volume
newNamedVol := new(libpod.ContainerNamedVolume)
newNamedVol.Name = src
newNamedVol.Dest = cleanDest
newNamedVol.Options = options
if _, ok := volumes[newNamedVol.Dest]; ok {
return nil, nil, errors.Wrapf(errDuplicateDest, newNamedVol.Dest)
}
volumes[newNamedVol.Dest] = newNamedVol
}
logrus.Debugf("User mount %s:%s options %v", src, dest, options)
}
return mounts, volumes, nil
}
// Get mounts for container's image volumes
func (config *CreateConfig) getImageVolumes() (map[string]spec.Mount, map[string]*libpod.ContainerNamedVolume, error) {
mounts := make(map[string]spec.Mount)
volumes := make(map[string]*libpod.ContainerNamedVolume)
if config.ImageVolumeType == "ignore" {
return mounts, volumes, nil
}
for vol := range config.BuiltinImgVolumes {
cleanDest := filepath.Clean(vol)
logrus.Debugf("Adding image volume at %s", cleanDest)
if config.ImageVolumeType == "tmpfs" {
// Tmpfs image volumes are handled as mounts
mount := spec.Mount{
Destination: cleanDest,
Source: TypeTmpfs,
Type: TypeTmpfs,
Options: []string{"rprivate", "rw", "nodev", "exec"},
}
mounts[cleanDest] = mount
} else {
// Anonymous volumes have no name.
namedVolume := new(libpod.ContainerNamedVolume)
namedVolume.Options = []string{"rprivate", "rw", "nodev", "exec"}
namedVolume.Dest = cleanDest
volumes[cleanDest] = namedVolume
}
}
return mounts, volumes, nil
}
// GetTmpfsMounts creates spec.Mount structs for user-requested tmpfs mounts
func (config *CreateConfig) getTmpfsMounts() (map[string]spec.Mount, error) {
m := make(map[string]spec.Mount)
for _, i := range config.Tmpfs {
// Default options if nothing passed
var options []string
spliti := strings.Split(i, ":")
destPath := spliti[0]
if err := parse.ValidateVolumeCtrDir(spliti[0]); err != nil {
return nil, err
}
if len(spliti) > 1 {
options = strings.Split(spliti[1], ",")
}
if _, ok := m[destPath]; ok {
return nil, errors.Wrapf(errDuplicateDest, destPath)
}
mount := spec.Mount{
Destination: filepath.Clean(destPath),
Type: string(TypeTmpfs),
Options: options,
Source: string(TypeTmpfs),
}
m[destPath] = mount
}
return m, nil
}
// AddContainerInitBinary adds the init binary specified by path iff the
// container will run in a private PID namespace that is not shared with the
// host or another pre-existing container, where an init-like process is
// already running.
//
// Note that AddContainerInitBinary prepends "/dev/init" "--" to the command
// to execute the bind-mounted binary as PID 1.
func (config *CreateConfig) addContainerInitBinary(path string) (spec.Mount, error) {
mount := spec.Mount{
Destination: "/dev/init",
Type: TypeBind,
Source: path,
Options: []string{TypeBind, "ro"},
}
if path == "" {
return mount, fmt.Errorf("please specify a path to the container-init binary")
}
if !config.Pid.PidMode.IsPrivate() {
return mount, fmt.Errorf("cannot add init binary as PID 1 (PID namespace isn't private)")
}
if config.Systemd {
return mount, fmt.Errorf("cannot use container-init binary with systemd")
}
if _, err := os.Stat(path); os.IsNotExist(err) {
return mount, errors.Wrap(err, "container-init binary not found on the host")
}
config.Command = append([]string{"/dev/init", "--"}, config.Command...)
return mount, nil
}
// Supersede existing mounts in the spec with new, user-specified mounts.
// TODO: Should we unmount subtree mounts? E.g., if /tmp/ is mounted by
// one mount, and we already have /tmp/a and /tmp/b, should we remove
// the /tmp/a and /tmp/b mounts in favor of the more general /tmp?
func SupercedeUserMounts(mounts []spec.Mount, configMount []spec.Mount) []spec.Mount {
if len(mounts) > 0 {
// If we have overlappings mounts, remove them from the spec in favor of
// the user-added volume mounts
destinations := make(map[string]bool)
for _, mount := range mounts {
destinations[path.Clean(mount.Destination)] = true
}
// Copy all mounts from spec to defaultMounts, except for
// - mounts overridden by a user supplied mount;
// - all mounts under /dev if a user supplied /dev is present;
mountDev := destinations["/dev"]
for _, mount := range configMount {
if _, ok := destinations[path.Clean(mount.Destination)]; !ok {
if mountDev && strings.HasPrefix(mount.Destination, "/dev/") {
// filter out everything under /dev if /dev is user-mounted
continue
}
logrus.Debugf("Adding mount %s", mount.Destination)
mounts = append(mounts, mount)
}
}
return mounts
}
return configMount
}
// Ensure mount options on all mounts are correct
func InitFSMounts(mounts []spec.Mount) error {
for i, m := range mounts {
switch {
case m.Type == TypeBind:
opts, err := util.ProcessOptions(m.Options, false, m.Source)
if err != nil {
return err
}
mounts[i].Options = opts
case m.Type == TypeTmpfs && filepath.Clean(m.Destination) != "/dev":
opts, err := util.ProcessOptions(m.Options, true, "")
if err != nil {
return err
}
mounts[i].Options = opts
}
}
return nil
}

View File

@ -1,38 +0,0 @@
package createconfig
import (
"testing"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/assert"
)
func TestGetVolumeMountsOneVolume(t *testing.T) {
data := spec.Mount{
Destination: "/foobar",
Type: "bind",
Source: "/tmp",
Options: []string{"ro"},
}
config := CreateConfig{
Volumes: []string{"/tmp:/foobar:ro"},
}
specMount, _, err := config.getVolumeMounts()
assert.NoError(t, err)
assert.EqualValues(t, data, specMount[data.Destination])
}
func TestGetTmpfsMounts(t *testing.T) {
data := spec.Mount{
Destination: "/homer",
Type: "tmpfs",
Source: "tmpfs",
Options: []string{"rw", "size=787448k", "mode=1777"},
}
config := CreateConfig{
Tmpfs: []string{"/homer:rw,size=787448k,mode=1777"},
}
tmpfsMount, err := config.getTmpfsMounts()
assert.NoError(t, err)
assert.EqualValues(t, data, tmpfsMount[data.Destination])
}

View File

@ -1,3 +0,0 @@
package iopodman
//go:generate go run ../../vendor/github.com/varlink/go/cmd/varlink-go-interface-generator/main.go io.podman.varlink

File diff suppressed because it is too large Load Diff

View File

@ -1,144 +0,0 @@
// +build varlink
package varlinkapi
import (
"bufio"
"context"
"io"
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/libpod/events"
iopodman "github.com/containers/podman/v2/pkg/varlink"
"github.com/containers/podman/v2/pkg/varlinkapi/virtwriter"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"k8s.io/client-go/tools/remotecommand"
)
func setupStreams(call iopodman.VarlinkCall) (*bufio.Reader, *bufio.Writer, *io.PipeReader, *io.PipeWriter, *define.AttachStreams) {
// These are the varlink sockets
reader := call.Call.Reader
writer := call.Call.Writer
// This pipe is used to pass stdin from the client to the input stream
// once the msg has been "decoded"
pr, pw := io.Pipe()
stdoutWriter := virtwriter.NewVirtWriteCloser(writer, virtwriter.ToStdout)
// TODO if runc ever starts passing stderr, we can too
// stderrWriter := NewVirtWriteCloser(writer, ToStderr)
streams := define.AttachStreams{
OutputStream: stdoutWriter,
InputStream: bufio.NewReader(pr),
// Runc eats the error stream
ErrorStream: stdoutWriter,
AttachInput: true,
AttachOutput: true,
// Runc eats the error stream
AttachError: true,
}
return reader, writer, pr, pw, &streams
}
// Attach connects to a containers console
func (i *VarlinkAPI) Attach(call iopodman.VarlinkCall, name string, detachKeys string, start bool) error {
var finalErr error
resize := make(chan remotecommand.TerminalSize)
errChan := make(chan error)
if !call.WantsUpgrade() {
return call.ReplyErrorOccurred("client must use upgraded connection to attach")
}
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
state, err := ctr.State()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
if !start && state != define.ContainerStateRunning {
return call.ReplyErrorOccurred("container must be running to attach")
}
// ACK the client upgrade request
if err := call.ReplyAttach(); err != nil {
return call.ReplyErrorOccurred(err.Error())
}
reader, writer, _, pw, streams := setupStreams(call)
go func() {
if err := virtwriter.Reader(reader, nil, nil, pw, resize, nil); err != nil {
errChan <- err
}
}()
if state == define.ContainerStateRunning {
finalErr = attach(ctr, streams, detachKeys, resize, errChan)
} else {
finalErr = startAndAttach(ctr, streams, detachKeys, resize, errChan)
}
exitCode := define.ExitCode(finalErr)
if finalErr != define.ErrDetach && finalErr != nil {
logrus.Error(finalErr)
} else {
if ecode, err := ctr.Wait(); err != nil {
if errors.Cause(err) == define.ErrNoSuchCtr {
// Check events
event, err := i.Runtime.GetLastContainerEvent(context.Background(), ctr.ID(), events.Exited)
if err != nil {
logrus.Errorf("Cannot get exit code: %v", err)
exitCode = define.ExecErrorCodeNotFound
} else {
exitCode = event.ContainerExitCode
}
} else {
exitCode = define.ExitCode(err)
}
} else {
exitCode = int(ecode)
}
}
if ctr.AutoRemove() {
err := i.Runtime.RemoveContainer(getContext(), ctr, false, false)
if err != nil {
logrus.Errorf("Failed to remove container %s: %s", ctr.ID(), err.Error())
}
}
if err = virtwriter.HangUp(writer, uint32(exitCode)); err != nil {
logrus.Errorf("Failed to HANG-UP attach to %s: %s", ctr.ID(), err.Error())
}
return call.Writer.Flush()
}
func attach(ctr *libpod.Container, streams *define.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error {
go func() {
if err := ctr.Attach(streams, detachKeys, resize); err != nil {
errChan <- err
}
}()
attachError := <-errChan
return attachError
}
func startAndAttach(ctr *libpod.Container, streams *define.AttachStreams, detachKeys string, resize chan remotecommand.TerminalSize, errChan chan error) error {
var finalErr error
attachChan, err := ctr.StartAndAttach(getContext(), streams, detachKeys, resize, false)
if err != nil {
return err
}
select {
case attachChanErr := <-attachChan:
finalErr = attachChanErr
case chanError := <-errChan:
finalErr = chanError
}
return finalErr
}

View File

@ -1,22 +0,0 @@
// +build varlink
package varlinkapi
import (
"github.com/containers/podman/v2/libpod"
iopodman "github.com/containers/podman/v2/pkg/varlink"
"github.com/spf13/cobra"
)
// VarlinkAPI is the basic varlink struct for libpod
type VarlinkAPI struct {
Cli *cobra.Command
iopodman.VarlinkInterface
Runtime *libpod.Runtime
}
// New creates a new varlink client
func New(cli *cobra.Command, runtime *libpod.Runtime) *iopodman.VarlinkInterface {
lp := VarlinkAPI{Cli: cli, Runtime: runtime}
return iopodman.VarlinkNew(&lp)
}

View File

@ -1,928 +0,0 @@
package varlinkapi
import (
"context"
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"runtime"
"sort"
"strconv"
"strings"
"sync"
"time"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/libpod/image"
"github.com/containers/podman/v2/pkg/timetype"
"github.com/containers/podman/v2/pkg/util"
"github.com/cri-o/ocicni/pkg/ocicni"
"github.com/docker/go-units"
"github.com/google/shlex"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
v1 "k8s.io/api/core/v1"
)
const (
cidTruncLength = 12
podTruncLength = 12
iidTruncLength = 12
cmdTruncLength = 17
)
// PsOptions describes the struct being formed for ps.
type PsOptions struct {
All bool
Format string
Last int
Latest bool
NoTrunc bool
Pod bool
Quiet bool
Size bool
Sort string
Namespace bool
Sync bool
}
// BatchContainerStruct is the return object from BatchContainer and contains
// container related information.
type BatchContainerStruct struct {
ConConfig *libpod.ContainerConfig
ConState define.ContainerStatus
ExitCode int32
Exited bool
Pid int
StartedTime time.Time
ExitedTime time.Time
Size *ContainerSize
}
// PsContainerOutput is the struct being returned from a parallel
// batch operation.
type PsContainerOutput struct {
ID string
Image string
ImageID string
Command string
Created string
Ports string
Names string
IsInfra bool
Status string
State define.ContainerStatus
Pid int
Size *ContainerSize
Pod string
PodName string
CreatedAt time.Time
ExitedAt time.Time
StartedAt time.Time
Labels map[string]string
PID string
Cgroup string
IPC string
MNT string
NET string
PIDNS string
User string
UTS string
Mounts string
}
// Namespace describes output for ps namespace.
type Namespace struct {
PID string `json:"pid,omitempty"`
Cgroup string `json:"cgroup,omitempty"`
IPC string `json:"ipc,omitempty"`
MNT string `json:"mnt,omitempty"`
NET string `json:"net,omitempty"`
PIDNS string `json:"pidns,omitempty"`
User string `json:"user,omitempty"`
UTS string `json:"uts,omitempty"`
}
// ContainerSize holds the size of the container's root filesystem and top
// read-write layer.
type ContainerSize struct {
RootFsSize int64 `json:"rootFsSize"`
RwSize int64 `json:"rwSize"`
}
// NewBatchContainer runs a batch process under one lock to get container information and only
// be called in PBatch.
func NewBatchContainer(r *libpod.Runtime, ctr *libpod.Container, opts PsOptions) (PsContainerOutput, error) {
var (
conState define.ContainerStatus
command string
created string
status string
exitedAt time.Time
startedAt time.Time
exitCode int32
err error
pid int
size *ContainerSize
ns *Namespace
pso PsContainerOutput
)
batchErr := ctr.Batch(func(c *libpod.Container) error {
if opts.Sync {
if err := c.Sync(); err != nil {
return err
}
}
conState, err = c.State()
if err != nil {
return errors.Wrapf(err, "unable to obtain container state")
}
command = strings.Join(c.Command(), " ")
created = units.HumanDuration(time.Since(c.CreatedTime())) + " ago"
exitCode, _, err = c.ExitCode()
if err != nil {
return errors.Wrapf(err, "unable to obtain container exit code")
}
startedAt, err = c.StartedTime()
if err != nil {
logrus.Errorf("error getting started time for %q: %v", c.ID(), err)
}
exitedAt, err = c.FinishedTime()
if err != nil {
logrus.Errorf("error getting exited time for %q: %v", c.ID(), err)
}
if opts.Namespace {
pid, err = c.PID()
if err != nil {
return errors.Wrapf(err, "unable to obtain container pid")
}
ns = GetNamespaces(pid)
}
if opts.Size {
size = new(ContainerSize)
rootFsSize, err := c.RootFsSize()
if err != nil {
logrus.Errorf("error getting root fs size for %q: %v", c.ID(), err)
}
rwSize, err := c.RWSize()
if err != nil {
logrus.Errorf("error getting rw size for %q: %v", c.ID(), err)
}
size.RootFsSize = rootFsSize
size.RwSize = rwSize
}
return nil
})
if batchErr != nil {
return pso, batchErr
}
switch conState.String() {
case define.ContainerStateExited.String():
fallthrough
case define.ContainerStateStopped.String():
exitedSince := units.HumanDuration(time.Since(exitedAt))
status = fmt.Sprintf("Exited (%d) %s ago", exitCode, exitedSince)
case define.ContainerStateRunning.String():
status = "Up " + units.HumanDuration(time.Since(startedAt)) + " ago"
case define.ContainerStatePaused.String():
status = "Paused"
case define.ContainerStateCreated.String(), define.ContainerStateConfigured.String():
status = "Created"
case define.ContainerStateRemoving.String():
status = "Removing"
default:
status = "Error"
}
imageID, imageName := ctr.Image()
cid := ctr.ID()
podID := ctr.PodID()
if !opts.NoTrunc {
cid = cid[0:cidTruncLength]
if len(podID) > podTruncLength {
podID = podID[0:podTruncLength]
}
if len(command) > cmdTruncLength {
command = command[0:cmdTruncLength] + "..."
}
if len(imageID) > iidTruncLength {
imageID = imageID[0:iidTruncLength]
}
}
ports, err := ctr.PortMappings()
if err != nil {
logrus.Errorf("unable to lookup namespace container for %s", ctr.ID())
}
pso.ID = cid
pso.Image = imageName
pso.ImageID = imageID
pso.Command = command
pso.Created = created
pso.Ports = portsToString(ports)
pso.Names = ctr.Name()
pso.IsInfra = ctr.IsInfra()
pso.Status = status
pso.State = conState
pso.Pid = pid
pso.Size = size
pso.ExitedAt = exitedAt
pso.CreatedAt = ctr.CreatedTime()
pso.StartedAt = startedAt
pso.Labels = ctr.Labels()
pso.Mounts = strings.Join(ctr.UserVolumes(), " ")
// Add pod name and pod ID if requested by user.
// No need to look up the pod if its ID is empty.
if opts.Pod && len(podID) > 0 {
// The pod name is not in the container definition
// so we need to retrieve it using the pod ID.
var podName string
pod, err := r.LookupPod(podID)
if err != nil {
logrus.Errorf("unable to lookup pod for container %s", ctr.ID())
} else {
podName = pod.Name()
}
pso.Pod = podID
pso.PodName = podName
}
if opts.Namespace {
pso.Cgroup = ns.Cgroup
pso.IPC = ns.IPC
pso.MNT = ns.MNT
pso.NET = ns.NET
pso.User = ns.User
pso.UTS = ns.UTS
pso.PIDNS = ns.PIDNS
}
return pso, nil
}
type batchFunc func() (PsContainerOutput, error)
type workerInput struct {
parallelFunc batchFunc
opts PsOptions
cid string
job int
}
// worker is a "threaded" worker that takes jobs from the channel "queue".
func worker(wg *sync.WaitGroup, jobs <-chan workerInput, results chan<- PsContainerOutput, errors chan<- error) {
for j := range jobs {
r, err := j.parallelFunc()
// If we find an error, we return just the error.
if err != nil {
errors <- err
} else {
// Return the result.
results <- r
}
wg.Done()
}
}
// GenerateContainerFilterFuncs return ContainerFilter functions based of filter.
func GenerateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) (func(container *libpod.Container) bool, error) {
switch filter {
case "id":
return func(c *libpod.Container) bool {
return strings.Contains(c.ID(), filterValue)
}, nil
case "label":
var filterArray = strings.SplitN(filterValue, "=", 2)
var filterKey = filterArray[0]
if len(filterArray) > 1 {
filterValue = filterArray[1]
} else {
filterValue = ""
}
return func(c *libpod.Container) bool {
for labelKey, labelValue := range c.Labels() {
if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) {
return true
}
}
return false
}, nil
case "name":
return func(c *libpod.Container) bool {
match, err := regexp.MatchString(filterValue, c.Name())
if err != nil {
return false
}
return match
}, nil
case "exited":
exitCode, err := strconv.ParseInt(filterValue, 10, 32)
if err != nil {
return nil, errors.Wrapf(err, "exited code out of range %q", filterValue)
}
return func(c *libpod.Container) bool {
ec, exited, err := c.ExitCode()
if ec == int32(exitCode) && err == nil && exited {
return true
}
return false
}, nil
case "status":
if !util.StringInSlice(filterValue, []string{"created", "running", "paused", "stopped", "exited", "unknown"}) {
return nil, errors.Errorf("%s is not a valid status", filterValue)
}
return func(c *libpod.Container) bool {
status, err := c.State()
if err != nil {
return false
}
if filterValue == "stopped" {
filterValue = "exited"
}
state := status.String()
if status == define.ContainerStateConfigured {
state = "created"
} else if status == define.ContainerStateStopped {
state = "exited"
}
return state == filterValue
}, nil
case "ancestor":
// This needs to refine to match docker
// - ancestor=(<image-name>[:tag]|<image-id>| ⟨image@digest⟩) - containers created from an image or a descendant.
return func(c *libpod.Container) bool {
containerConfig := c.Config()
if strings.Contains(containerConfig.RootfsImageID, filterValue) || strings.Contains(containerConfig.RootfsImageName, filterValue) {
return true
}
return false
}, nil
case "before":
ctr, err := r.LookupContainer(filterValue)
if err != nil {
return nil, errors.Errorf("unable to find container by name or id of %s", filterValue)
}
containerConfig := ctr.Config()
createTime := containerConfig.CreatedTime
return func(c *libpod.Container) bool {
cc := c.Config()
return createTime.After(cc.CreatedTime)
}, nil
case "since":
ctr, err := r.LookupContainer(filterValue)
if err != nil {
return nil, errors.Errorf("unable to find container by name or id of %s", filterValue)
}
containerConfig := ctr.Config()
createTime := containerConfig.CreatedTime
return func(c *libpod.Container) bool {
cc := c.Config()
return createTime.Before(cc.CreatedTime)
}, nil
case "volume":
//- volume=(<volume-name>|<mount-point-destination>)
return func(c *libpod.Container) bool {
containerConfig := c.Config()
var dest string
arr := strings.Split(filterValue, ":")
source := arr[0]
if len(arr) == 2 {
dest = arr[1]
}
for _, mount := range containerConfig.Spec.Mounts {
if dest != "" && (mount.Source == source && mount.Destination == dest) {
return true
}
if dest == "" && mount.Source == source {
return true
}
}
return false
}, nil
case "health":
return func(c *libpod.Container) bool {
hcStatus, err := c.HealthCheckStatus()
if err != nil {
return false
}
return hcStatus == filterValue
}, nil
case "until":
ts, err := timetype.GetTimestamp(filterValue, time.Now())
if err != nil {
return nil, err
}
seconds, nanoseconds, err := timetype.ParseTimestamps(ts, 0)
if err != nil {
return nil, err
}
until := time.Unix(seconds, nanoseconds)
return func(c *libpod.Container) bool {
if !until.IsZero() && c.CreatedTime().After((until)) {
return true
}
return false
}, nil
}
return nil, errors.Errorf("%s is an invalid filter", filter)
}
// GetPsContainerOutput returns a slice of containers specifically for ps output.
func GetPsContainerOutput(r *libpod.Runtime, opts PsOptions, filters []string, maxWorkers int) ([]PsContainerOutput, error) {
var (
filterFuncs []libpod.ContainerFilter
outputContainers []*libpod.Container
)
if len(filters) > 0 {
for _, f := range filters {
filterSplit := strings.SplitN(f, "=", 2)
if len(filterSplit) < 2 {
return nil, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
}
generatedFunc, err := GenerateContainerFilterFuncs(filterSplit[0], filterSplit[1], r)
if err != nil {
return nil, errors.Wrapf(err, "invalid filter")
}
filterFuncs = append(filterFuncs, generatedFunc)
}
}
if !opts.Latest {
// Get all containers.
containers, err := r.GetContainers(filterFuncs...)
if err != nil {
return nil, err
}
// We only want the last few containers.
if opts.Last > 0 && opts.Last <= len(containers) {
return nil, errors.Errorf("--last not yet supported")
} else {
outputContainers = containers
}
} else {
// Get just the latest container.
// Ignore filters.
latestCtr, err := r.GetLatestContainer()
if err != nil {
return nil, err
}
outputContainers = []*libpod.Container{latestCtr}
}
pss := PBatch(r, outputContainers, maxWorkers, opts)
return pss, nil
}
// PBatch performs batch operations on a container in parallel. It spawns the
// number of workers relative to the number of parallel operations desired.
func PBatch(r *libpod.Runtime, containers []*libpod.Container, workers int, opts PsOptions) []PsContainerOutput {
var wg sync.WaitGroup
psResults := []PsContainerOutput{}
// If the number of containers in question is less than the number of
// proposed parallel operations, we shouldn't spawn so many workers.
if workers > len(containers) {
workers = len(containers)
}
jobs := make(chan workerInput, len(containers))
results := make(chan PsContainerOutput, len(containers))
batchErrors := make(chan error, len(containers))
// Create the workers.
for w := 1; w <= workers; w++ {
go worker(&wg, jobs, results, batchErrors)
}
// Add jobs to the workers.
for i, j := range containers {
j := j
wg.Add(1)
f := func() (PsContainerOutput, error) {
return NewBatchContainer(r, j, opts)
}
jobs <- workerInput{
parallelFunc: f,
opts: opts,
cid: j.ID(),
job: i,
}
}
close(jobs)
wg.Wait()
close(results)
close(batchErrors)
for err := range batchErrors {
logrus.Errorf("unable to get container info: %q", err)
}
for res := range results {
// We sort out running vs non-running here to save lots of copying
// later.
if !opts.All && !opts.Latest && opts.Last < 1 {
if !res.IsInfra && res.State == define.ContainerStateRunning {
psResults = append(psResults, res)
}
} else {
psResults = append(psResults, res)
}
}
return psResults
}
// BatchContainerOp is used in ps to reduce performance hits by "batching"
// locks.
func BatchContainerOp(ctr *libpod.Container, opts PsOptions) (BatchContainerStruct, error) {
var (
conConfig *libpod.ContainerConfig
conState define.ContainerStatus
err error
exitCode int32
exited bool
pid int
size *ContainerSize
startedTime time.Time
exitedTime time.Time
)
batchErr := ctr.Batch(func(c *libpod.Container) error {
conConfig = c.Config()
conState, err = c.State()
if err != nil {
return errors.Wrapf(err, "unable to obtain container state")
}
exitCode, exited, err = c.ExitCode()
if err != nil {
return errors.Wrapf(err, "unable to obtain container exit code")
}
startedTime, err = c.StartedTime()
if err != nil {
logrus.Errorf("error getting started time for %q: %v", c.ID(), err)
}
exitedTime, err = c.FinishedTime()
if err != nil {
logrus.Errorf("error getting exited time for %q: %v", c.ID(), err)
}
if !opts.Size && !opts.Namespace {
return nil
}
if opts.Namespace {
pid, err = c.PID()
if err != nil {
return errors.Wrapf(err, "unable to obtain container pid")
}
}
if opts.Size {
size = new(ContainerSize)
rootFsSize, err := c.RootFsSize()
if err != nil {
logrus.Errorf("error getting root fs size for %q: %v", c.ID(), err)
}
rwSize, err := c.RWSize()
if err != nil {
logrus.Errorf("error getting rw size for %q: %v", c.ID(), err)
}
size.RootFsSize = rootFsSize
size.RwSize = rwSize
}
return nil
})
if batchErr != nil {
return BatchContainerStruct{}, batchErr
}
return BatchContainerStruct{
ConConfig: conConfig,
ConState: conState,
ExitCode: exitCode,
Exited: exited,
Pid: pid,
StartedTime: startedTime,
ExitedTime: exitedTime,
Size: size,
}, nil
}
// GetNamespaces returns a populated namespace struct.
func GetNamespaces(pid int) *Namespace {
ctrPID := strconv.Itoa(pid)
cgroup, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "cgroup"))
ipc, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "ipc"))
mnt, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "mnt"))
net, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "net"))
pidns, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "pid"))
user, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "user"))
uts, _ := getNamespaceInfo(filepath.Join("/proc", ctrPID, "ns", "uts"))
return &Namespace{
PID: ctrPID,
Cgroup: cgroup,
IPC: ipc,
MNT: mnt,
NET: net,
PIDNS: pidns,
User: user,
UTS: uts,
}
}
// GetNamespaceInfo is an exported wrapper for getNamespaceInfo
func GetNamespaceInfo(path string) (string, error) {
return getNamespaceInfo(path)
}
func getNamespaceInfo(path string) (string, error) {
val, err := os.Readlink(path)
if err != nil {
return "", errors.Wrapf(err, "error getting info from %q", path)
}
return getStrFromSquareBrackets(val), nil
}
// getStrFromSquareBrackets gets the string inside [] from a string.
func getStrFromSquareBrackets(cmd string) string {
reg := regexp.MustCompile(`.*\[|\].*`)
arr := strings.Split(reg.ReplaceAllLiteralString(cmd, ""), ",")
return strings.Join(arr, ",")
}
func comparePorts(i, j ocicni.PortMapping) bool {
if i.ContainerPort != j.ContainerPort {
return i.ContainerPort < j.ContainerPort
}
if i.HostIP != j.HostIP {
return i.HostIP < j.HostIP
}
if i.HostPort != j.HostPort {
return i.HostPort < j.HostPort
}
return i.Protocol < j.Protocol
}
// formatGroup returns the group as <IP:startPort:lastPort->startPort:lastPort/Proto>
// e.g 0.0.0.0:1000-1006->1000-1006/tcp.
func formatGroup(key string, start, last int32) string {
parts := strings.Split(key, "/")
groupType := parts[0]
var ip string
if len(parts) > 1 {
ip = parts[0]
groupType = parts[1]
}
group := strconv.Itoa(int(start))
if start != last {
group = fmt.Sprintf("%s-%d", group, last)
}
if ip != "" {
group = fmt.Sprintf("%s:%s->%s", ip, group, group)
}
return fmt.Sprintf("%s/%s", group, groupType)
}
// portsToString converts the ports used to a string of the from "port1, port2"
// and also groups a continuous list of ports into a readable format.
func portsToString(ports []ocicni.PortMapping) string {
type portGroup struct {
first int32
last int32
}
var portDisplay []string
if len(ports) == 0 {
return ""
}
//Sort the ports, so grouping continuous ports become easy.
sort.Slice(ports, func(i, j int) bool {
return comparePorts(ports[i], ports[j])
})
// portGroupMap is used for grouping continuous ports.
portGroupMap := make(map[string]*portGroup)
var groupKeyList []string
for _, v := range ports {
hostIP := v.HostIP
if hostIP == "" {
hostIP = "0.0.0.0"
}
// If hostPort and containerPort are not same, consider as individual port.
if v.ContainerPort != v.HostPort {
portDisplay = append(portDisplay, fmt.Sprintf("%s:%d->%d/%s", hostIP, v.HostPort, v.ContainerPort, v.Protocol))
continue
}
portMapKey := fmt.Sprintf("%s/%s", hostIP, v.Protocol)
portgroup, ok := portGroupMap[portMapKey]
if !ok {
portGroupMap[portMapKey] = &portGroup{first: v.ContainerPort, last: v.ContainerPort}
// This list is required to traverse portGroupMap.
groupKeyList = append(groupKeyList, portMapKey)
continue
}
if portgroup.last == (v.ContainerPort - 1) {
portgroup.last = v.ContainerPort
continue
}
}
// For each portMapKey, format group list and append to output string.
for _, portKey := range groupKeyList {
group := portGroupMap[portKey]
portDisplay = append(portDisplay, formatGroup(portKey, group.first, group.last))
}
return strings.Join(portDisplay, ", ")
}
// GetRunlabel is a helper function for runlabel; it gets the image if needed and begins the
// construction of the runlabel output and environment variables.
func GetRunlabel(label string, runlabelImage string, ctx context.Context, runtime *libpod.Runtime, pull bool, inputCreds string, dockerRegistryOptions image.DockerRegistryOptions, authfile string, signaturePolicyPath string, output io.Writer) (string, string, error) {
var (
newImage *image.Image
err error
imageName string
)
if pull {
var registryCreds *types.DockerAuthConfig
if inputCreds != "" {
creds, err := util.ParseRegistryCreds(inputCreds)
if err != nil {
return "", "", err
}
registryCreds = creds
}
dockerRegistryOptions.DockerRegistryCreds = registryCreds
newImage, err = runtime.ImageRuntime().New(ctx, runlabelImage, signaturePolicyPath, authfile, output, &dockerRegistryOptions, image.SigningOptions{}, &label, util.PullImageMissing)
} else {
newImage, err = runtime.ImageRuntime().NewFromLocal(runlabelImage)
}
if err != nil {
return "", "", errors.Wrapf(err, "unable to find image")
}
if len(newImage.Names()) < 1 {
imageName = newImage.ID()
} else {
imageName = newImage.Names()[0]
}
runLabel, err := newImage.GetLabel(ctx, label)
return runLabel, imageName, err
}
// GenerateRunlabelCommand generates the command that will eventually be executed by Podman.
func GenerateRunlabelCommand(runLabel, imageName, name string, opts map[string]string, extraArgs []string, globalOpts string) ([]string, []string, error) {
// If no name is provided, we use the image's basename instead.
if name == "" {
baseName, err := image.GetImageBaseName(imageName)
if err != nil {
return nil, nil, err
}
name = baseName
}
// The user provided extra arguments that need to be tacked onto the label's command.
if len(extraArgs) > 0 {
runLabel = fmt.Sprintf("%s %s", runLabel, strings.Join(extraArgs, " "))
}
cmd, err := GenerateCommand(runLabel, imageName, name, globalOpts)
if err != nil {
return nil, nil, errors.Wrapf(err, "unable to generate command")
}
env := GenerateRunEnvironment(name, imageName, opts)
env = append(env, "PODMAN_RUNLABEL_NESTED=1")
envmap := envSliceToMap(env)
envmapper := func(k string) string {
switch k {
case "OPT1":
return envmap["OPT1"]
case "OPT2":
return envmap["OPT2"]
case "OPT3":
return envmap["OPT3"]
case "PWD":
// I would prefer to use os.getenv but it appears PWD is not in the os env list.
d, err := os.Getwd()
if err != nil {
logrus.Error("unable to determine current working directory")
return ""
}
return d
}
return ""
}
newS := os.Expand(strings.Join(cmd, " "), envmapper)
cmd, err = shlex.Split(newS)
if err != nil {
return nil, nil, err
}
return cmd, env, nil
}
func envSliceToMap(env []string) map[string]string {
m := make(map[string]string)
for _, i := range env {
split := strings.Split(i, "=")
m[split[0]] = strings.Join(split[1:], " ")
}
return m
}
// GenerateKube generates kubernetes yaml based on a pod or container.
func GenerateKube(name string, service bool, r *libpod.Runtime) (*v1.Pod, *v1.Service, error) {
var (
pod *libpod.Pod
podYAML *v1.Pod
err error
container *libpod.Container
servicePorts []v1.ServicePort
serviceYAML v1.Service
)
// Get the container in question.
container, err = r.LookupContainer(name)
if err != nil {
pod, err = r.LookupPod(name)
if err != nil {
return nil, nil, err
}
podYAML, servicePorts, err = pod.GenerateForKube()
} else {
if len(container.Dependencies()) > 0 {
return nil, nil, errors.Wrapf(define.ErrNotImplemented, "containers with dependencies")
}
podYAML, err = container.GenerateForKube()
}
if err != nil {
return nil, nil, err
}
if service {
serviceYAML = libpod.GenerateKubeServiceFromV1Pod(podYAML, servicePorts)
}
return podYAML, &serviceYAML, nil
}
// Parallelize provides the maximum number of parallel workers (int) as calculated by a basic
// heuristic. This can be overridden by the --max-workers primary switch to podman.
func Parallelize(job string) int {
numCpus := runtime.NumCPU()
switch job {
case "kill":
if numCpus <= 3 {
return numCpus * 3
}
return numCpus * 4
case "pause":
if numCpus <= 3 {
return numCpus * 3
}
return numCpus * 4
case "ps":
return 8
case "restart":
return numCpus * 2
case "rm":
if numCpus <= 3 {
return numCpus * 3
} else {
return numCpus * 4
}
case "stop":
if numCpus <= 2 {
return 4
} else {
return numCpus * 3
}
case "unpause":
if numCpus <= 3 {
return numCpus * 3
}
return numCpus * 4
}
return 3
}

View File

@ -1,912 +0,0 @@
// +build varlink
package varlinkapi
import (
"bufio"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"strings"
"sync"
"syscall"
"time"
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/libpod/logs"
"github.com/containers/podman/v2/pkg/cgroups"
"github.com/containers/podman/v2/pkg/rootless"
iopodman "github.com/containers/podman/v2/pkg/varlink"
"github.com/containers/podman/v2/pkg/varlinkapi/virtwriter"
"github.com/containers/storage/pkg/archive"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"k8s.io/client-go/tools/remotecommand"
)
// ListContainers ...
func (i *VarlinkAPI) ListContainers(call iopodman.VarlinkCall) error {
var (
listContainers []iopodman.Container
)
containers, err := i.Runtime.GetAllContainers()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
opts := PsOptions{
Namespace: true,
Size: true,
}
for _, ctr := range containers {
batchInfo, err := BatchContainerOp(ctr, opts)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
listContainers = append(listContainers, makeListContainer(ctr.ID(), batchInfo))
}
return call.ReplyListContainers(listContainers)
}
func (i *VarlinkAPI) Ps(call iopodman.VarlinkCall, opts iopodman.PsOpts) error {
var (
containers []iopodman.PsContainer
)
maxWorkers := Parallelize("ps")
psOpts := makePsOpts(opts)
filters := []string{}
if opts.Filters != nil {
filters = *opts.Filters
}
psContainerOutputs, err := GetPsContainerOutput(i.Runtime, psOpts, filters, maxWorkers)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
for _, ctr := range psContainerOutputs {
container := iopodman.PsContainer{
Id: ctr.ID,
Image: ctr.Image,
Command: ctr.Command,
Created: ctr.Created,
Ports: ctr.Ports,
Names: ctr.Names,
IsInfra: ctr.IsInfra,
Status: ctr.Status,
State: ctr.State.String(),
PidNum: int64(ctr.Pid),
Pod: ctr.Pod,
CreatedAt: ctr.CreatedAt.Format(time.RFC3339Nano),
ExitedAt: ctr.ExitedAt.Format(time.RFC3339Nano),
StartedAt: ctr.StartedAt.Format(time.RFC3339Nano),
Labels: ctr.Labels,
NsPid: ctr.PID,
Cgroup: ctr.Cgroup,
Ipc: ctr.Cgroup,
Mnt: ctr.MNT,
Net: ctr.NET,
PidNs: ctr.PIDNS,
User: ctr.User,
Uts: ctr.UTS,
Mounts: ctr.Mounts,
}
if ctr.Size != nil {
container.RootFsSize = ctr.Size.RootFsSize
container.RwSize = ctr.Size.RwSize
}
containers = append(containers, container)
}
return call.ReplyPs(containers)
}
// GetContainer ...
func (i *VarlinkAPI) GetContainer(call iopodman.VarlinkCall, id string) error {
ctr, err := i.Runtime.LookupContainer(id)
if err != nil {
return call.ReplyContainerNotFound(id, err.Error())
}
opts := PsOptions{
Namespace: true,
Size: true,
}
batchInfo, err := BatchContainerOp(ctr, opts)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyGetContainer(makeListContainer(ctr.ID(), batchInfo))
}
// getContainersByContext returns a slice of container ids based on all, latest, or a list
func (i *VarlinkAPI) GetContainersByContext(call iopodman.VarlinkCall, all, latest bool, input []string) error {
var ids []string
ctrs, err := getContainersByContext(all, latest, input, i.Runtime)
if err != nil {
if errors.Cause(err) == define.ErrNoSuchCtr {
return call.ReplyContainerNotFound("", err.Error())
}
return call.ReplyErrorOccurred(err.Error())
}
for _, c := range ctrs {
ids = append(ids, c.ID())
}
return call.ReplyGetContainersByContext(ids)
}
// GetContainersByStatus returns a slice of containers filtered by a libpod status
func (i *VarlinkAPI) GetContainersByStatus(call iopodman.VarlinkCall, statuses []string) error {
var (
filterFuncs []libpod.ContainerFilter
containers []iopodman.Container
)
for _, status := range statuses {
lpstatus, err := define.StringToContainerStatus(status)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
filterFuncs = append(filterFuncs, func(c *libpod.Container) bool {
state, _ := c.State()
return state == lpstatus
})
}
filteredContainers, err := i.Runtime.GetContainers(filterFuncs...)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
opts := PsOptions{Size: true, Namespace: true}
for _, ctr := range filteredContainers {
batchInfo, err := BatchContainerOp(ctr, opts)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
containers = append(containers, makeListContainer(ctr.ID(), batchInfo))
}
return call.ReplyGetContainersByStatus(containers)
}
// InspectContainer ...
func (i *VarlinkAPI) InspectContainer(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name, err.Error())
}
data, err := ctr.Inspect(true)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
b, err := json.Marshal(data)
if err != nil {
return call.ReplyErrorOccurred(fmt.Sprintf("unable to serialize"))
}
return call.ReplyInspectContainer(string(b))
}
// ListContainerProcesses ...
func (i *VarlinkAPI) ListContainerProcesses(call iopodman.VarlinkCall, name string, opts []string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name, err.Error())
}
containerState, err := ctr.State()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
if containerState != define.ContainerStateRunning {
return call.ReplyErrorOccurred(fmt.Sprintf("container %s is not running", name))
}
var psArgs []string
psOpts := []string{"user", "pid", "ppid", "pcpu", "etime", "tty", "time", "comm"}
if len(opts) > 1 {
psOpts = opts
}
psArgs = append(psArgs, psOpts...)
psOutput, err := ctr.GetContainerPidInformation(psArgs)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyListContainerProcesses(psOutput)
}
// GetContainerLogs ...
func (i *VarlinkAPI) GetContainerLogs(call iopodman.VarlinkCall, name string) error {
var logs []string
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name, err.Error())
}
logPath := ctr.LogPath()
containerState, err := ctr.State()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
if _, err := os.Stat(logPath); err != nil {
if containerState == define.ContainerStateConfigured {
return call.ReplyGetContainerLogs(logs)
}
}
file, err := os.Open(logPath)
if err != nil {
return errors.Wrapf(err, "unable to read container log file")
}
defer file.Close()
reader := bufio.NewReader(file)
if call.WantsMore() {
call.Continues = true
}
for {
line, err := reader.ReadString('\n')
// We've read the entire file
if err == io.EOF {
if !call.WantsMore() {
// If this is a non-following log request, we return what we have
break
} else {
// If we want to follow, return what we have, wipe the slice, and make
// sure the container is still running before iterating.
call.ReplyGetContainerLogs(logs)
logs = []string{}
time.Sleep(1 * time.Second)
state, err := ctr.State()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
if state != define.ContainerStateRunning && state != define.ContainerStatePaused {
return call.ReplyErrorOccurred(fmt.Sprintf("%s is no longer running", ctr.ID()))
}
}
} else if err != nil {
return call.ReplyErrorOccurred(err.Error())
} else {
logs = append(logs, line)
}
}
call.Continues = false
return call.ReplyGetContainerLogs(logs)
}
// ListContainerChanges ...
func (i *VarlinkAPI) ListContainerChanges(call iopodman.VarlinkCall, name string) error {
changes, err := i.Runtime.GetDiff("", name)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
result := iopodman.ContainerChanges{}
for _, change := range changes {
switch change.Kind {
case archive.ChangeModify:
result.Changed = append(result.Changed, change.Path)
case archive.ChangeDelete:
result.Deleted = append(result.Deleted, change.Path)
case archive.ChangeAdd:
result.Added = append(result.Added, change.Path)
}
}
return call.ReplyListContainerChanges(result)
}
// ExportContainer ...
func (i *VarlinkAPI) ExportContainer(call iopodman.VarlinkCall, name, outPath string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name, err.Error())
}
outputFile, err := ioutil.TempFile("", "varlink_recv")
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
defer outputFile.Close()
if outPath == "" {
outPath = outputFile.Name()
}
if err := ctr.Export(outPath); err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyExportContainer(outPath)
}
// GetContainerStats ...
func (i *VarlinkAPI) GetContainerStats(call iopodman.VarlinkCall, name string) error {
if rootless.IsRootless() {
cgroupv2, err := cgroups.IsCgroup2UnifiedMode()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
if !cgroupv2 {
return call.ReplyErrRequiresCgroupsV2ForRootless("rootless containers cannot report container stats")
}
}
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name, err.Error())
}
containerStats, err := ctr.GetContainerStats(&define.ContainerStats{})
if err != nil {
if errors.Cause(err) == define.ErrCtrStateInvalid {
return call.ReplyNoContainerRunning()
}
return call.ReplyErrorOccurred(err.Error())
}
cs := iopodman.ContainerStats{
Id: ctr.ID(),
Name: ctr.Name(),
Cpu: containerStats.CPU,
Cpu_nano: int64(containerStats.CPUNano),
System_nano: int64(containerStats.SystemNano),
Mem_usage: int64(containerStats.MemUsage),
Mem_limit: int64(containerStats.MemLimit),
Mem_perc: containerStats.MemPerc,
Net_input: int64(containerStats.NetInput),
Net_output: int64(containerStats.NetOutput),
Block_input: int64(containerStats.BlockInput),
Block_output: int64(containerStats.BlockOutput),
Pids: int64(containerStats.PIDs),
}
return call.ReplyGetContainerStats(cs)
}
// StartContainer ...
func (i *VarlinkAPI) StartContainer(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name, err.Error())
}
state, err := ctr.State()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
if state == define.ContainerStateRunning || state == define.ContainerStatePaused {
return call.ReplyErrorOccurred("container is already running or paused")
}
recursive := false
if ctr.PodID() != "" {
recursive = true
}
if err := ctr.Start(getContext(), recursive); err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyStartContainer(ctr.ID())
}
// InitContainer initializes the container given by Varlink.
func (i *VarlinkAPI) InitContainer(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name, err.Error())
}
if err := ctr.Init(getContext(), false); err != nil {
if errors.Cause(err) == define.ErrCtrStateInvalid {
return call.ReplyInvalidState(ctr.ID(), err.Error())
}
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyInitContainer(ctr.ID())
}
// StopContainer ...
func (i *VarlinkAPI) StopContainer(call iopodman.VarlinkCall, name string, timeout int64) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name, err.Error())
}
if err := ctr.StopWithTimeout(uint(timeout)); err != nil {
if errors.Cause(err) == define.ErrCtrStopped {
return call.ReplyErrCtrStopped(ctr.ID())
}
if errors.Cause(err) == define.ErrCtrStateInvalid {
return call.ReplyInvalidState(ctr.ID(), err.Error())
}
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyStopContainer(ctr.ID())
}
// RestartContainer ...
func (i *VarlinkAPI) RestartContainer(call iopodman.VarlinkCall, name string, timeout int64) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name, err.Error())
}
if err := ctr.RestartWithTimeout(getContext(), uint(timeout)); err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyRestartContainer(ctr.ID())
}
// ContainerExists looks in local storage for the existence of a container
func (i *VarlinkAPI) ContainerExists(call iopodman.VarlinkCall, name string) error {
_, err := i.Runtime.LookupContainer(name)
if errors.Cause(err) == define.ErrNoSuchCtr {
return call.ReplyContainerExists(1)
}
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyContainerExists(0)
}
// KillContainer kills a running container. If you want to use the default SIGTERM signal, just send a -1
// for the signal arg.
func (i *VarlinkAPI) KillContainer(call iopodman.VarlinkCall, name string, signal int64) error {
killSignal := uint(syscall.SIGTERM)
if signal != -1 {
killSignal = uint(signal)
}
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name, err.Error())
}
if err := ctr.Kill(killSignal); err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyKillContainer(ctr.ID())
}
// PauseContainer ...
func (i *VarlinkAPI) PauseContainer(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name, err.Error())
}
if err := ctr.Pause(); err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyPauseContainer(ctr.ID())
}
// UnpauseContainer ...
func (i *VarlinkAPI) UnpauseContainer(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name, err.Error())
}
if err := ctr.Unpause(); err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyUnpauseContainer(ctr.ID())
}
// WaitContainer ...
func (i *VarlinkAPI) WaitContainer(call iopodman.VarlinkCall, name string, interval int64) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name, err.Error())
}
exitCode, err := ctr.WaitWithInterval(time.Duration(interval))
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyWaitContainer(int64(exitCode))
}
// RemoveContainer ...
func (i *VarlinkAPI) RemoveContainer(call iopodman.VarlinkCall, name string, force bool, removeVolumes bool) error {
ctx := getContext()
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name, err.Error())
}
if err := i.Runtime.RemoveContainer(ctx, ctr, force, removeVolumes); err != nil {
if errors.Cause(err) == define.ErrNoSuchCtr {
return call.ReplyContainerExists(1)
}
if errors.Cause(err) == define.ErrCtrStateInvalid {
return call.ReplyInvalidState(ctr.ID(), err.Error())
}
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyRemoveContainer(ctr.ID())
}
// EvictContainer ...
func (i *VarlinkAPI) EvictContainer(call iopodman.VarlinkCall, name string, removeVolumes bool) error {
ctx := getContext()
id, err := i.Runtime.EvictContainer(ctx, name, removeVolumes)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyEvictContainer(id)
}
// DeleteStoppedContainers ...
func (i *VarlinkAPI) DeleteStoppedContainers(call iopodman.VarlinkCall) error {
ctx := getContext()
var deletedContainers []string
containers, err := i.Runtime.GetAllContainers()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
for _, ctr := range containers {
state, err := ctr.State()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
if state != define.ContainerStateRunning {
if err := i.Runtime.RemoveContainer(ctx, ctr, false, false); err != nil {
return call.ReplyErrorOccurred(err.Error())
}
deletedContainers = append(deletedContainers, ctr.ID())
}
}
return call.ReplyDeleteStoppedContainers(deletedContainers)
}
// GetAttachSockets ...
func (i *VarlinkAPI) GetAttachSockets(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name, err.Error())
}
status, err := ctr.State()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
// If the container hasn't been run, we need to run init
// so the conmon sockets get created.
if status == define.ContainerStateConfigured || status == define.ContainerStateStopped {
if err := ctr.Init(getContext(), false); err != nil {
return call.ReplyErrorOccurred(err.Error())
}
}
sockPath, err := ctr.AttachSocketPath()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
s := iopodman.Sockets{
Container_id: ctr.ID(),
Io_socket: sockPath,
Control_socket: ctr.ControlSocketPath(),
}
return call.ReplyGetAttachSockets(s)
}
// ContainerCheckpoint ...
func (i *VarlinkAPI) ContainerCheckpoint(call iopodman.VarlinkCall, name string, keep, leaveRunning, tcpEstablished bool) error {
ctx := getContext()
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name, err.Error())
}
options := libpod.ContainerCheckpointOptions{
Keep: keep,
TCPEstablished: tcpEstablished,
KeepRunning: leaveRunning,
}
if err := ctr.Checkpoint(ctx, options); err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyContainerCheckpoint(ctr.ID())
}
// ContainerRestore ...
func (i *VarlinkAPI) ContainerRestore(call iopodman.VarlinkCall, name string, keep, tcpEstablished bool) error {
ctx := getContext()
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name, err.Error())
}
options := libpod.ContainerCheckpointOptions{
Keep: keep,
TCPEstablished: tcpEstablished,
}
if err := ctr.Restore(ctx, options); err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyContainerRestore(ctr.ID())
}
// ContainerConfig returns just the container.config struct
func (i *VarlinkAPI) ContainerConfig(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name, err.Error())
}
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 *VarlinkAPI) ContainerArtifacts(call iopodman.VarlinkCall, name, artifactName string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name, 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 *VarlinkAPI) ContainerInspectData(call iopodman.VarlinkCall, name string, size bool) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name, err.Error())
}
data, err := ctr.Inspect(size)
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 *VarlinkAPI) ContainerStateData(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyContainerNotFound(name, 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))
}
// GetContainerStatsWithHistory is a varlink endpoint that returns container stats based on current and
// previous statistics
func (i *VarlinkAPI) GetContainerStatsWithHistory(call iopodman.VarlinkCall, prevStats iopodman.ContainerStats) error {
con, err := i.Runtime.LookupContainer(prevStats.Id)
if err != nil {
return call.ReplyContainerNotFound(prevStats.Id, err.Error())
}
previousStats := ContainerStatsToLibpodContainerStats(prevStats)
stats, err := con.GetContainerStats(&previousStats)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
cStats := iopodman.ContainerStats{
Id: stats.ContainerID,
Name: stats.Name,
Cpu: stats.CPU,
Cpu_nano: int64(stats.CPUNano),
System_nano: int64(stats.SystemNano),
Mem_usage: int64(stats.MemUsage),
Mem_limit: int64(stats.MemLimit),
Mem_perc: stats.MemPerc,
Net_input: int64(stats.NetInput),
Net_output: int64(stats.NetOutput),
Block_input: int64(stats.BlockInput),
Block_output: int64(stats.BlockOutput),
Pids: int64(stats.PIDs),
}
return call.ReplyGetContainerStatsWithHistory(cStats)
}
// Spec ...
func (i *VarlinkAPI) Spec(call iopodman.VarlinkCall, name string) error {
ctr, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
spec := ctr.Spec()
b, err := json.Marshal(spec)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplySpec(string(b))
}
// GetContainersLogs is the varlink endpoint to obtain one or more container logs
func (i *VarlinkAPI) GetContainersLogs(call iopodman.VarlinkCall, names []string, follow, latest bool, since string, tail int64, timestamps bool) error {
var wg sync.WaitGroup
if call.WantsMore() {
call.Continues = true
}
sinceTime, err := time.Parse(time.RFC3339Nano, since)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
options := logs.LogOptions{
Follow: follow,
Since: sinceTime,
Tail: tail,
Timestamps: timestamps,
}
options.WaitGroup = &wg
if len(names) > 1 {
options.Multi = true
}
tailLen := int(tail)
if tailLen < 0 {
tailLen = 0
}
logChannel := make(chan *logs.LogLine, tailLen*len(names)+1)
containers, err := getContainersByContext(false, latest, names, i.Runtime)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
if err := i.Runtime.Log(getContext(), containers, &options, logChannel); err != nil {
return err
}
go func() {
wg.Wait()
close(logChannel)
}()
for line := range logChannel {
call.ReplyGetContainersLogs(newPodmanLogLine(line))
if !call.Continues {
break
}
}
return call.ReplyGetContainersLogs(iopodman.LogLine{})
}
func newPodmanLogLine(line *logs.LogLine) iopodman.LogLine {
return iopodman.LogLine{
Device: line.Device,
ParseLogType: line.ParseLogType,
Time: line.Time.Format(time.RFC3339Nano),
Msg: line.Msg,
Cid: line.CID,
}
}
// Top displays information about a container's running processes
func (i *VarlinkAPI) Top(call iopodman.VarlinkCall, nameOrID string, descriptors []string) error {
ctr, err := i.Runtime.LookupContainer(nameOrID)
if err != nil {
return call.ReplyContainerNotFound(ctr.ID(), err.Error())
}
topInfo, err := ctr.Top(descriptors)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyTop(topInfo)
}
// ExecContainer is the varlink endpoint to execute a command in a container
func (i *VarlinkAPI) ExecContainer(call iopodman.VarlinkCall, opts iopodman.ExecOpts) error {
if !call.WantsUpgrade() {
return call.ReplyErrorOccurred("client must use upgraded connection to exec")
}
ctr, err := i.Runtime.LookupContainer(opts.Name)
if err != nil {
return call.ReplyContainerNotFound(opts.Name, err.Error())
}
state, err := ctr.State()
if err != nil {
return call.ReplyErrorOccurred(
fmt.Sprintf("exec failed to obtain container %s state: %s", ctr.ID(), err.Error()))
}
if state != define.ContainerStateRunning {
return call.ReplyErrorOccurred(
fmt.Sprintf("exec requires a running container, %s is %s", ctr.ID(), state.String()))
}
// ACK the client upgrade request
call.ReplyExecContainer()
envs := make(map[string]string)
if opts.Env != nil {
// HACK: The Varlink API uses the old []string format for env,
// storage as "k=v". Split on the = and turn into the new map
// format.
for _, env := range *opts.Env {
splitEnv := strings.SplitN(env, "=", 2)
if len(splitEnv) == 1 {
logrus.Errorf("Got badly-formatted environment variable %q in exec", env)
continue
}
envs[splitEnv[0]] = splitEnv[1]
}
}
var user string
if opts.User != nil {
user = *opts.User
}
var workDir string
if opts.Workdir != nil {
workDir = *opts.Workdir
}
resizeChan := make(chan remotecommand.TerminalSize)
reader, writer, _, pipeWriter, streams := setupStreams(call)
type ExitCodeError struct {
ExitCode uint32
Error error
}
ecErrChan := make(chan ExitCodeError, 1)
go func() {
if err := virtwriter.Reader(reader, nil, nil, pipeWriter, resizeChan, nil); err != nil {
ecErrChan <- ExitCodeError{
define.ExecErrorCodeGeneric,
err,
}
}
}()
execConfig := new(libpod.ExecConfig)
execConfig.Command = opts.Cmd
execConfig.Terminal = opts.Tty
execConfig.Privileged = opts.Privileged
execConfig.Environment = envs
execConfig.User = user
execConfig.WorkDir = workDir
execConfig.DetachKeys = opts.DetachKeys
go func() {
ec, err := ctr.Exec(execConfig, streams, resizeChan)
if err != nil {
logrus.Errorf(err.Error())
}
ecErrChan <- ExitCodeError{
uint32(ec),
err,
}
}()
ecErr := <-ecErrChan
exitCode := define.TranslateExecErrorToExitCode(int(ecErr.ExitCode), ecErr.Error)
if err = virtwriter.HangUp(writer, uint32(exitCode)); err != nil {
logrus.Errorf("ExecContainer failed to HANG-UP on %s: %s", ctr.ID(), err.Error())
}
if err := call.Writer.Flush(); err != nil {
logrus.Errorf("Exec Container err: %s", err.Error())
}
return ecErr.Error
}
// HealthCheckRun executes defined container's healthcheck command and returns the container's health status.
func (i *VarlinkAPI) HealthCheckRun(call iopodman.VarlinkCall, nameOrID string) error {
hcStatus, err := i.Runtime.HealthCheck(nameOrID)
if err != nil && hcStatus != define.HealthCheckFailure {
return call.ReplyErrorOccurred(err.Error())
}
status := define.HealthCheckUnhealthy
if hcStatus == define.HealthCheckSuccess {
status = define.HealthCheckHealthy
}
return call.ReplyHealthCheckRun(status)
}

View File

@ -1,17 +0,0 @@
// +build varlink
package varlinkapi
import (
iopodman "github.com/containers/podman/v2/pkg/varlink"
)
// CreateContainer ...
func (i *VarlinkAPI) CreateContainer(call iopodman.VarlinkCall, config iopodman.Create) error {
generic := VarlinkCreateToGeneric(config)
ctr, _, err := CreateContainer(getContext(), &generic, i.Runtime)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyCreateContainer(ctr.ID())
}

File diff suppressed because it is too large Load Diff

View File

@ -1,56 +0,0 @@
// +build varlink
package varlinkapi
import (
"context"
"time"
"github.com/containers/podman/v2/libpod/events"
iopodman "github.com/containers/podman/v2/pkg/varlink"
)
// GetEvents is a remote endpoint to get events from the event log
func (i *VarlinkAPI) GetEvents(call iopodman.VarlinkCall, filter []string, since string, until string) error {
var (
fromStart bool
eventsError error
event *events.Event
stream bool
)
if call.WantsMore() {
stream = true
call.Continues = true
}
if len(since) > 0 || len(until) > 0 {
fromStart = true
}
eventChannel := make(chan *events.Event)
go func() {
readOpts := events.ReadOptions{FromStart: fromStart, Stream: stream, Filters: filter, EventChannel: eventChannel}
eventsError = i.Runtime.Events(context.Background(), readOpts)
}()
if eventsError != nil {
return call.ReplyErrorOccurred(eventsError.Error())
}
for {
event = <-eventChannel
if event == nil {
call.Continues = false
break
}
call.ReplyGetEvents(iopodman.Event{
Id: event.ID,
Image: event.Image,
Name: event.Name,
Status: string(event.Status),
Time: event.Time.Format(time.RFC3339Nano),
Type: string(event.Type),
})
if !call.Continues {
// For a one-shot on events, we break out here
break
}
}
return nil
}

View File

@ -1,121 +0,0 @@
package varlinkapi
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v2/libpod/image"
"github.com/google/shlex"
"github.com/pkg/errors"
)
func GetSystemContext(authfile string) (*types.SystemContext, error) {
if authfile != "" {
if _, err := os.Stat(authfile); err != nil {
return nil, errors.Wrapf(err, "error checking authfile path %s", authfile)
}
}
return image.GetSystemContext("", authfile, false), nil
}
func substituteCommand(cmd string) (string, error) {
var (
newCommand string
)
// Replace cmd with "/proc/self/exe" if "podman" or "docker" is being
// used. If "/usr/bin/docker" is provided, we also sub in podman.
// Otherwise, leave the command unchanged.
if cmd == "podman" || filepath.Base(cmd) == "docker" {
newCommand = "/proc/self/exe"
} else {
newCommand = cmd
}
// If cmd is an absolute or relative path, check if the file exists.
// Throw an error if it doesn't exist.
if strings.Contains(newCommand, "/") || strings.HasPrefix(newCommand, ".") {
res, err := filepath.Abs(newCommand)
if err != nil {
return "", err
}
if _, err := os.Stat(res); !os.IsNotExist(err) {
return res, nil
} else if err != nil {
return "", err
}
}
return newCommand, nil
}
// GenerateCommand takes a label (string) and converts it to an executable command
func GenerateCommand(command, imageName, name, globalOpts string) ([]string, error) {
var (
newCommand []string
)
if name == "" {
name = imageName
}
cmd, err := shlex.Split(command)
if err != nil {
return nil, err
}
prog, err := substituteCommand(cmd[0])
if err != nil {
return nil, err
}
newCommand = append(newCommand, prog)
for _, arg := range cmd[1:] {
var newArg string
switch arg {
case "IMAGE":
newArg = imageName
case "$IMAGE":
newArg = imageName
case "IMAGE=IMAGE":
newArg = fmt.Sprintf("IMAGE=%s", imageName)
case "IMAGE=$IMAGE":
newArg = fmt.Sprintf("IMAGE=%s", imageName)
case "NAME":
newArg = name
case "NAME=NAME":
newArg = fmt.Sprintf("NAME=%s", name)
case "NAME=$NAME":
newArg = fmt.Sprintf("NAME=%s", name)
case "$NAME":
newArg = name
case "$GLOBAL_OPTS":
newArg = globalOpts
default:
newArg = arg
}
newCommand = append(newCommand, newArg)
}
return newCommand, nil
}
// GenerateRunEnvironment merges the current environment variables with optional
// environment variables provided by the user
func GenerateRunEnvironment(name, imageName string, opts map[string]string) []string {
newEnv := os.Environ()
newEnv = append(newEnv, fmt.Sprintf("NAME=%s", name))
newEnv = append(newEnv, fmt.Sprintf("IMAGE=%s", imageName))
if opts["opt1"] != "" {
newEnv = append(newEnv, fmt.Sprintf("OPT1=%s", opts["opt1"]))
}
if opts["opt2"] != "" {
newEnv = append(newEnv, fmt.Sprintf("OPT2=%s", opts["opt2"]))
}
if opts["opt3"] != "" {
newEnv = append(newEnv, fmt.Sprintf("OPT3=%s", opts["opt3"]))
}
return newEnv
}

View File

@ -1,30 +0,0 @@
// +build varlink
package varlinkapi
import (
"encoding/json"
iopodman "github.com/containers/podman/v2/pkg/varlink"
)
// GenerateKube ...
func (i *VarlinkAPI) GenerateKube(call iopodman.VarlinkCall, name string, service bool) error {
pod, serv, err := GenerateKube(name, service, i.Runtime)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
podB, err := json.Marshal(pod)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
servB, err := json.Marshal(serv)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyGenerateKube(iopodman.KubePodService{
Pod: string(podB),
Service: string(servB),
})
}

File diff suppressed because it is too large Load Diff

View File

@ -1,289 +0,0 @@
package varlinkapi
import (
"github.com/sirupsen/logrus"
)
/*
attention
in this file you will see a lot of struct duplication. this was done because people wanted a strongly typed
varlink mechanism. this resulted in us creating this intermediate layer that allows us to take the input
from the cli and make an intermediate layer which can be transferred as strongly typed structures over a varlink
interface.
we intentionally avoided heavy use of reflection here because we were concerned about performance impacts to the
non-varlink intermediate layer generation.
*/
// GenericCLIResult describes the overall interface for dealing with
// the create command cli in both local and remote uses
type GenericCLIResult interface {
IsSet() bool
Name() string
Value() interface{}
}
// CRStringSlice describes a string slice cli struct
type CRStringSlice struct {
Val []string
createResult
}
// CRString describes a string cli struct
type CRString struct {
Val string
createResult
}
// CRUint64 describes a uint64 cli struct
type CRUint64 struct {
Val uint64
createResult
}
// CRFloat64 describes a float64 cli struct
type CRFloat64 struct {
Val float64
createResult
}
//CRBool describes a bool cli struct
type CRBool struct {
Val bool
createResult
}
// CRInt64 describes an int64 cli struct
type CRInt64 struct {
Val int64
createResult
}
// CRUint describes a uint cli struct
type CRUint struct {
Val uint
createResult
}
// CRInt describes an int cli struct
type CRInt struct {
Val int
createResult
}
// CRStringArray describes a stringarray cli struct
type CRStringArray struct {
Val []string
createResult
}
type createResult struct {
Flag string
Changed bool
}
// GenericCLIResults in the intermediate object between the cobra cli
// and createconfig
type GenericCLIResults struct {
results map[string]GenericCLIResult
InputArgs []string
}
// IsSet returns a bool if the flag was changed
func (f GenericCLIResults) IsSet(flag string) bool {
r := f.findResult(flag)
if r == nil {
return false
}
return r.IsSet()
}
// Value returns the value of the cli flag
func (f GenericCLIResults) Value(flag string) interface{} {
r := f.findResult(flag)
if r == nil {
return ""
}
return r.Value()
}
func (f GenericCLIResults) findResult(flag string) GenericCLIResult {
val, ok := f.results[flag]
if ok {
return val
}
logrus.Debugf("unable to find flag %s", flag)
return nil
}
// Bool is a wrapper to get a bool value from GenericCLIResults
func (f GenericCLIResults) Bool(flag string) bool {
r := f.findResult(flag)
if r == nil {
return false
}
return r.Value().(bool)
}
// String is a wrapper to get a string value from GenericCLIResults
func (f GenericCLIResults) String(flag string) string {
r := f.findResult(flag)
if r == nil {
return ""
}
return r.Value().(string)
}
// Uint is a wrapper to get an uint value from GenericCLIResults
func (f GenericCLIResults) Uint(flag string) uint {
r := f.findResult(flag)
if r == nil {
return 0
}
return r.Value().(uint)
}
// StringSlice is a wrapper to get a stringslice value from GenericCLIResults
func (f GenericCLIResults) StringSlice(flag string) []string {
r := f.findResult(flag)
if r == nil {
return []string{}
}
return r.Value().([]string)
}
// StringArray is a wrapper to get a stringslice value from GenericCLIResults
func (f GenericCLIResults) StringArray(flag string) []string {
r := f.findResult(flag)
if r == nil {
return []string{}
}
return r.Value().([]string)
}
// Uint64 is a wrapper to get an uint64 value from GenericCLIResults
func (f GenericCLIResults) Uint64(flag string) uint64 {
r := f.findResult(flag)
if r == nil {
return 0
}
return r.Value().(uint64)
}
// Int64 is a wrapper to get an int64 value from GenericCLIResults
func (f GenericCLIResults) Int64(flag string) int64 {
r := f.findResult(flag)
if r == nil {
return 0
}
return r.Value().(int64)
}
// Int is a wrapper to get an int value from GenericCLIResults
func (f GenericCLIResults) Int(flag string) int {
r := f.findResult(flag)
if r == nil {
return 0
}
return r.Value().(int)
}
// Float64 is a wrapper to get an float64 value from GenericCLIResults
func (f GenericCLIResults) Float64(flag string) float64 {
r := f.findResult(flag)
if r == nil {
return 0
}
return r.Value().(float64)
}
// Float64 is a wrapper to get an float64 value from GenericCLIResults
func (f GenericCLIResults) Changed(flag string) bool {
r := f.findResult(flag)
if r == nil {
return false
}
return r.IsSet()
}
// IsSet ...
func (c CRStringSlice) IsSet() bool { return c.Changed }
// Name ...
func (c CRStringSlice) Name() string { return c.Flag }
// Value ...
func (c CRStringSlice) Value() interface{} { return c.Val }
// IsSet ...
func (c CRString) IsSet() bool { return c.Changed }
// Name ...
func (c CRString) Name() string { return c.Flag }
// Value ...
func (c CRString) Value() interface{} { return c.Val }
// IsSet ...
func (c CRUint64) IsSet() bool { return c.Changed }
// Name ...
func (c CRUint64) Name() string { return c.Flag }
// Value ...
func (c CRUint64) Value() interface{} { return c.Val }
// IsSet ...
func (c CRFloat64) IsSet() bool { return c.Changed }
// Name ...
func (c CRFloat64) Name() string { return c.Flag }
// Value ...
func (c CRFloat64) Value() interface{} { return c.Val }
// IsSet ...
func (c CRBool) IsSet() bool { return c.Changed }
// Name ...
func (c CRBool) Name() string { return c.Flag }
// Value ...
func (c CRBool) Value() interface{} { return c.Val }
// IsSet ...
func (c CRInt64) IsSet() bool { return c.Changed }
// Name ...
func (c CRInt64) Name() string { return c.Flag }
// Value ...
func (c CRInt64) Value() interface{} { return c.Val }
// IsSet ...
func (c CRUint) IsSet() bool { return c.Changed }
// Name ...
func (c CRUint) Name() string { return c.Flag }
// Value ...
func (c CRUint) Value() interface{} { return c.Val }
// IsSet ...
func (c CRInt) IsSet() bool { return c.Changed }
// Name ...
func (c CRInt) Name() string { return c.Flag }
// Value ...
func (c CRInt) Value() interface{} { return c.Val }
// IsSet ...
func (c CRStringArray) IsSet() bool { return c.Changed }
// Name ...
func (c CRStringArray) Name() string { return c.Flag }
// Value ...
func (c CRStringArray) Value() interface{} { return c.Val }

View File

@ -1,457 +0,0 @@
// +build varlink remoteclient
package varlinkapi
import (
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v2/pkg/rootless"
iopodman "github.com/containers/podman/v2/pkg/varlink"
"github.com/pkg/errors"
)
//FIXME these are duplicated here to resolve a circular
//import with cmd/podman/common.
var (
// DefaultHealthCheckInterval default value
DefaultHealthCheckInterval = "30s"
// DefaultHealthCheckRetries default value
DefaultHealthCheckRetries uint = 3
// DefaultHealthCheckStartPeriod default value
DefaultHealthCheckStartPeriod = "0s"
// DefaultHealthCheckTimeout default value
DefaultHealthCheckTimeout = "30s"
// DefaultImageVolume default value
DefaultImageVolume = "bind"
)
// StringSliceToPtr converts a genericcliresult value into a *[]string
func StringSliceToPtr(g GenericCLIResult) *[]string {
if !g.IsSet() {
return nil
}
newT := g.Value().([]string)
return &newT
}
// StringToPtr converts a genericcliresult value into a *string
func StringToPtr(g GenericCLIResult) *string {
if !g.IsSet() {
return nil
}
newT := g.Value().(string)
return &newT
}
// BoolToPtr converts a genericcliresult value into a *bool
func BoolToPtr(g GenericCLIResult) *bool {
if !g.IsSet() {
return nil
}
newT := g.Value().(bool)
return &newT
}
// AnyIntToInt64Ptr converts a genericcliresult value into an *int64
func AnyIntToInt64Ptr(g GenericCLIResult) *int64 {
if !g.IsSet() {
return nil
}
var newT int64
switch g.Value().(type) {
case int:
newT = int64(g.Value().(int))
case int64:
newT = g.Value().(int64)
case uint64:
newT = int64(g.Value().(uint64))
case uint:
newT = int64(g.Value().(uint))
default:
panic(errors.Errorf("invalid int type"))
}
return &newT
}
// Float64ToPtr converts a genericcliresult into a *float64
func Float64ToPtr(g GenericCLIResult) *float64 {
if !g.IsSet() {
return nil
}
newT := g.Value().(float64)
return &newT
}
// MakeVarlink creates a varlink transportable struct from GenericCLIResults
func (g GenericCLIResults) MakeVarlink() iopodman.Create {
v := iopodman.Create{
Args: g.InputArgs,
AddHost: StringSliceToPtr(g.Find("add-host")),
Annotation: StringSliceToPtr(g.Find("annotation")),
Attach: StringSliceToPtr(g.Find("attach")),
BlkioWeight: StringToPtr(g.Find("blkio-weight")),
BlkioWeightDevice: StringSliceToPtr(g.Find("blkio-weight-device")),
CapAdd: StringSliceToPtr(g.Find("cap-add")),
CapDrop: StringSliceToPtr(g.Find("cap-drop")),
CgroupParent: StringToPtr(g.Find("cgroup-parent")),
CidFile: StringToPtr(g.Find("cidfile")),
ConmonPidfile: StringToPtr(g.Find("conmon-pidfile")),
CpuPeriod: AnyIntToInt64Ptr(g.Find("cpu-period")),
CpuQuota: AnyIntToInt64Ptr(g.Find("cpu-quota")),
CpuRtPeriod: AnyIntToInt64Ptr(g.Find("cpu-rt-period")),
CpuRtRuntime: AnyIntToInt64Ptr(g.Find("cpu-rt-runtime")),
CpuShares: AnyIntToInt64Ptr(g.Find("cpu-shares")),
Cpus: Float64ToPtr(g.Find("cpus")),
CpuSetCpus: StringToPtr(g.Find("cpuset-cpus")),
CpuSetMems: StringToPtr(g.Find("cpuset-mems")),
Detach: BoolToPtr(g.Find("detach")),
DetachKeys: StringToPtr(g.Find("detach-keys")),
Device: StringSliceToPtr(g.Find("device")),
DeviceReadBps: StringSliceToPtr(g.Find("device-read-bps")),
DeviceReadIops: StringSliceToPtr(g.Find("device-read-iops")),
DeviceWriteBps: StringSliceToPtr(g.Find("device-write-bps")),
DeviceWriteIops: StringSliceToPtr(g.Find("device-write-iops")),
Dns: StringSliceToPtr(g.Find("dns")),
DnsOpt: StringSliceToPtr(g.Find("dns-opt")),
DnsSearch: StringSliceToPtr(g.Find("dns-search")),
Entrypoint: StringToPtr(g.Find("entrypoint")),
Env: StringSliceToPtr(g.Find("env")),
EnvFile: StringSliceToPtr(g.Find("env-file")),
Expose: StringSliceToPtr(g.Find("expose")),
Gidmap: StringSliceToPtr(g.Find("gidmap")),
Groupadd: StringSliceToPtr(g.Find("group-add")),
HealthcheckCommand: StringToPtr(g.Find("healthcheck-command")),
HealthcheckInterval: StringToPtr(g.Find("healthcheck-interval")),
HealthcheckRetries: AnyIntToInt64Ptr(g.Find("healthcheck-retries")),
HealthcheckStartPeriod: StringToPtr(g.Find("healthcheck-start-period")),
HealthcheckTimeout: StringToPtr(g.Find("healthcheck-timeout")),
Hostname: StringToPtr(g.Find("hostname")),
ImageVolume: StringToPtr(g.Find("image-volume")),
Init: BoolToPtr(g.Find("init")),
InitPath: StringToPtr(g.Find("init-path")),
Interactive: BoolToPtr(g.Find("interactive")),
Ip: StringToPtr(g.Find("ip")),
Ipc: StringToPtr(g.Find("ipc")),
KernelMemory: StringToPtr(g.Find("kernel-memory")),
Label: StringSliceToPtr(g.Find("label")),
LabelFile: StringSliceToPtr(g.Find("label-file")),
LogDriver: StringToPtr(g.Find("log-driver")),
LogOpt: StringSliceToPtr(g.Find("log-opt")),
MacAddress: StringToPtr(g.Find("mac-address")),
Memory: StringToPtr(g.Find("memory")),
MemoryReservation: StringToPtr(g.Find("memory-reservation")),
MemorySwap: StringToPtr(g.Find("memory-swap")),
MemorySwappiness: AnyIntToInt64Ptr(g.Find("memory-swappiness")),
Name: StringToPtr(g.Find("name")),
Network: StringToPtr(g.Find("network")),
OomKillDisable: BoolToPtr(g.Find("oom-kill-disable")),
OomScoreAdj: AnyIntToInt64Ptr(g.Find("oom-score-adj")),
OverrideOS: StringToPtr(g.Find("override-os")),
OverrideArch: StringToPtr(g.Find("override-arch")),
Pid: StringToPtr(g.Find("pid")),
PidsLimit: AnyIntToInt64Ptr(g.Find("pids-limit")),
Pod: StringToPtr(g.Find("pod")),
Privileged: BoolToPtr(g.Find("privileged")),
Publish: StringSliceToPtr(g.Find("publish")),
PublishAll: BoolToPtr(g.Find("publish-all")),
Pull: StringToPtr(g.Find("pull")),
Quiet: BoolToPtr(g.Find("quiet")),
Readonly: BoolToPtr(g.Find("read-only")),
Readonlytmpfs: BoolToPtr(g.Find("read-only-tmpfs")),
Restart: StringToPtr(g.Find("restart")),
Rm: BoolToPtr(g.Find("rm")),
Rootfs: BoolToPtr(g.Find("rootfs")),
SecurityOpt: StringSliceToPtr(g.Find("security-opt")),
ShmSize: StringToPtr(g.Find("shm-size")),
StopSignal: StringToPtr(g.Find("stop-signal")),
StopTimeout: AnyIntToInt64Ptr(g.Find("stop-timeout")),
StorageOpt: StringSliceToPtr(g.Find("storage-opt")),
Subuidname: StringToPtr(g.Find("subuidname")),
Subgidname: StringToPtr(g.Find("subgidname")),
Sysctl: StringSliceToPtr(g.Find("sysctl")),
Systemd: StringToPtr(g.Find("systemd")),
Tmpfs: StringSliceToPtr(g.Find("tmpfs")),
Tty: BoolToPtr(g.Find("tty")),
Uidmap: StringSliceToPtr(g.Find("uidmap")),
Ulimit: StringSliceToPtr(g.Find("ulimit")),
User: StringToPtr(g.Find("user")),
Userns: StringToPtr(g.Find("userns")),
Uts: StringToPtr(g.Find("uts")),
Mount: StringSliceToPtr(g.Find("mount")),
Volume: StringSliceToPtr(g.Find("volume")),
VolumesFrom: StringSliceToPtr(g.Find("volumes-from")),
WorkDir: StringToPtr(g.Find("workdir")),
}
return v
}
func stringSliceFromVarlink(v *[]string, flagName string, defaultValue *[]string) CRStringSlice {
cr := CRStringSlice{}
if v == nil {
cr.Val = []string{}
if defaultValue != nil {
cr.Val = *defaultValue
}
cr.Changed = false
} else {
cr.Val = *v
cr.Changed = true
}
cr.Flag = flagName
return cr
}
func stringFromVarlink(v *string, flagName string, defaultValue *string) CRString {
cr := CRString{}
if v == nil {
cr.Val = ""
if defaultValue != nil {
cr.Val = *defaultValue
}
cr.Changed = false
} else {
cr.Val = *v
cr.Changed = true
}
cr.Flag = flagName
return cr
}
func boolFromVarlink(v *bool, flagName string, defaultValue bool) CRBool {
cr := CRBool{}
if v == nil {
// In case a cli bool default value is true
cr.Val = defaultValue
cr.Changed = false
} else {
cr.Val = *v
cr.Changed = true
}
cr.Flag = flagName
return cr
}
func uint64FromVarlink(v *int64, flagName string, defaultValue *uint64) CRUint64 {
cr := CRUint64{}
if v == nil {
cr.Val = 0
if defaultValue != nil {
cr.Val = *defaultValue
}
cr.Changed = false
} else {
cr.Val = uint64(*v)
cr.Changed = true
}
cr.Flag = flagName
return cr
}
func int64FromVarlink(v *int64, flagName string, defaultValue *int64) CRInt64 {
cr := CRInt64{}
if v == nil {
cr.Val = 0
if defaultValue != nil {
cr.Val = *defaultValue
}
cr.Changed = false
} else {
cr.Val = *v
cr.Changed = true
}
cr.Flag = flagName
return cr
}
func float64FromVarlink(v *float64, flagName string, defaultValue *float64) CRFloat64 {
cr := CRFloat64{}
if v == nil {
cr.Val = 0
if defaultValue != nil {
cr.Val = *defaultValue
}
cr.Changed = false
} else {
cr.Val = *v
cr.Changed = true
}
cr.Flag = flagName
return cr
}
func uintFromVarlink(v *int64, flagName string, defaultValue *uint) CRUint {
cr := CRUint{}
if v == nil {
cr.Val = 0
if defaultValue != nil {
cr.Val = *defaultValue
}
cr.Changed = false
} else {
cr.Val = uint(*v)
cr.Changed = true
}
cr.Flag = flagName
return cr
}
func stringArrayFromVarlink(v *[]string, flagName string, defaultValue *[]string) CRStringArray {
cr := CRStringArray{}
if v == nil {
cr.Val = []string{}
if defaultValue != nil {
cr.Val = *defaultValue
}
cr.Changed = false
} else {
cr.Val = *v
cr.Changed = true
}
cr.Flag = flagName
return cr
}
func intFromVarlink(v *int64, flagName string, defaultValue *int) CRInt {
cr := CRInt{}
if v == nil {
if defaultValue != nil {
cr.Val = *defaultValue
}
cr.Val = 0
cr.Changed = false
} else {
cr.Val = int(*v)
cr.Changed = true
}
cr.Flag = flagName
return cr
}
// VarlinkCreateToGeneric creates a GenericCLIResults from the varlink create
// structure.
func VarlinkCreateToGeneric(opts iopodman.Create) GenericCLIResults {
// FIXME this will need to be fixed!!!!! With containers conf
//containerConfig := cliconfig.GetDefaultConfig()
// TODO | WARN
// We do not get a default network over varlink. Unlike the other default values for some cli
// elements, it seems it gets set to the default anyway.
var memSwapDefault int64 = -1
netModeDefault := "bridge"
systemdDefault := "true"
if rootless.IsRootless() {
netModeDefault = "slirp4netns"
}
shmSize := config.DefaultShmSize
m := make(map[string]GenericCLIResult)
m["add-host"] = stringSliceFromVarlink(opts.AddHost, "add-host", nil)
m["annotation"] = stringSliceFromVarlink(opts.Annotation, "annotation", nil)
m["attach"] = stringSliceFromVarlink(opts.Attach, "attach", nil)
m["blkio-weight"] = stringFromVarlink(opts.BlkioWeight, "blkio-weight", nil)
m["blkio-weight-device"] = stringSliceFromVarlink(opts.BlkioWeightDevice, "blkio-weight-device", nil)
m["cap-add"] = stringSliceFromVarlink(opts.CapAdd, "cap-add", nil)
m["cap-drop"] = stringSliceFromVarlink(opts.CapDrop, "cap-drop", nil)
m["cgroup-parent"] = stringFromVarlink(opts.CgroupParent, "cgroup-parent", nil)
m["cidfile"] = stringFromVarlink(opts.CidFile, "cidfile", nil)
m["conmon-pidfile"] = stringFromVarlink(opts.ConmonPidfile, "conmon-file", nil)
m["cpu-period"] = uint64FromVarlink(opts.CpuPeriod, "cpu-period", nil)
m["cpu-quota"] = int64FromVarlink(opts.CpuQuota, "quota", nil)
m["cpu-rt-period"] = uint64FromVarlink(opts.CpuRtPeriod, "cpu-rt-period", nil)
m["cpu-rt-runtime"] = int64FromVarlink(opts.CpuRtRuntime, "cpu-rt-quota", nil)
m["cpu-shares"] = uint64FromVarlink(opts.CpuShares, "cpu-shares", nil)
m["cpus"] = float64FromVarlink(opts.Cpus, "cpus", nil)
m["cpuset-cpus"] = stringFromVarlink(opts.CpuSetCpus, "cpuset-cpus", nil)
m["cpuset-mems"] = stringFromVarlink(opts.CpuSetMems, "cpuset-mems", nil)
m["detach"] = boolFromVarlink(opts.Detach, "detach", false)
m["detach-keys"] = stringFromVarlink(opts.DetachKeys, "detach-keys", nil)
m["device"] = stringSliceFromVarlink(opts.Device, "device", nil)
m["device-read-bps"] = stringSliceFromVarlink(opts.DeviceReadBps, "device-read-bps", nil)
m["device-read-iops"] = stringSliceFromVarlink(opts.DeviceReadIops, "device-read-iops", nil)
m["device-write-bps"] = stringSliceFromVarlink(opts.DeviceWriteBps, "write-device-bps", nil)
m["device-write-iops"] = stringSliceFromVarlink(opts.DeviceWriteIops, "write-device-iops", nil)
m["dns"] = stringSliceFromVarlink(opts.Dns, "dns", nil)
m["dns-opt"] = stringSliceFromVarlink(opts.DnsOpt, "dns-opt", nil)
m["dns-search"] = stringSliceFromVarlink(opts.DnsSearch, "dns-search", nil)
m["entrypoint"] = stringFromVarlink(opts.Entrypoint, "entrypoint", nil)
m["env"] = stringArrayFromVarlink(opts.Env, "env", nil)
m["env-file"] = stringSliceFromVarlink(opts.EnvFile, "env-file", nil)
m["expose"] = stringSliceFromVarlink(opts.Expose, "expose", nil)
m["gidmap"] = stringSliceFromVarlink(opts.Gidmap, "gidmap", nil)
m["group-add"] = stringSliceFromVarlink(opts.Groupadd, "group-add", nil)
m["healthcheck-command"] = stringFromVarlink(opts.HealthcheckCommand, "healthcheck-command", nil)
m["healthcheck-interval"] = stringFromVarlink(opts.HealthcheckInterval, "healthcheck-interval", &DefaultHealthCheckInterval)
m["healthcheck-retries"] = uintFromVarlink(opts.HealthcheckRetries, "healthcheck-retries", &DefaultHealthCheckRetries)
m["healthcheck-start-period"] = stringFromVarlink(opts.HealthcheckStartPeriod, "healthcheck-start-period", &DefaultHealthCheckStartPeriod)
m["healthcheck-timeout"] = stringFromVarlink(opts.HealthcheckTimeout, "healthcheck-timeout", &DefaultHealthCheckTimeout)
m["hostname"] = stringFromVarlink(opts.Hostname, "hostname", nil)
m["image-volume"] = stringFromVarlink(opts.ImageVolume, "image-volume", &DefaultImageVolume)
m["init"] = boolFromVarlink(opts.Init, "init", false)
m["init-path"] = stringFromVarlink(opts.InitPath, "init-path", nil)
m["interactive"] = boolFromVarlink(opts.Interactive, "interactive", false)
m["ip"] = stringFromVarlink(opts.Ip, "ip", nil)
m["ipc"] = stringFromVarlink(opts.Ipc, "ipc", nil)
m["kernel-memory"] = stringFromVarlink(opts.KernelMemory, "kernel-memory", nil)
m["label"] = stringArrayFromVarlink(opts.Label, "label", nil)
m["label-file"] = stringSliceFromVarlink(opts.LabelFile, "label-file", nil)
m["log-driver"] = stringFromVarlink(opts.LogDriver, "log-driver", nil)
m["log-opt"] = stringSliceFromVarlink(opts.LogOpt, "log-opt", nil)
m["mac-address"] = stringFromVarlink(opts.MacAddress, "mac-address", nil)
m["memory"] = stringFromVarlink(opts.Memory, "memory", nil)
m["memory-reservation"] = stringFromVarlink(opts.MemoryReservation, "memory-reservation", nil)
m["memory-swap"] = stringFromVarlink(opts.MemorySwap, "memory-swap", nil)
m["memory-swappiness"] = int64FromVarlink(opts.MemorySwappiness, "memory-swappiness", &memSwapDefault)
m["name"] = stringFromVarlink(opts.Name, "name", nil)
m["network"] = stringFromVarlink(opts.Network, "network", &netModeDefault)
m["no-hosts"] = boolFromVarlink(opts.NoHosts, "no-hosts", false)
m["oom-kill-disable"] = boolFromVarlink(opts.OomKillDisable, "oon-kill-disable", false)
m["oom-score-adj"] = intFromVarlink(opts.OomScoreAdj, "oom-score-adj", nil)
m["override-os"] = stringFromVarlink(opts.OverrideOS, "override-os", nil)
m["override-arch"] = stringFromVarlink(opts.OverrideArch, "override-arch", nil)
m["pid"] = stringFromVarlink(opts.Pid, "pid", nil)
m["pids-limit"] = int64FromVarlink(opts.PidsLimit, "pids-limit", nil)
m["pod"] = stringFromVarlink(opts.Pod, "pod", nil)
m["privileged"] = boolFromVarlink(opts.Privileged, "privileged", false)
m["publish"] = stringSliceFromVarlink(opts.Publish, "publish", nil)
m["publish-all"] = boolFromVarlink(opts.PublishAll, "publish-all", false)
m["pull"] = stringFromVarlink(opts.Pull, "missing", nil)
m["quiet"] = boolFromVarlink(opts.Quiet, "quiet", false)
m["read-only"] = boolFromVarlink(opts.Readonly, "read-only", false)
m["read-only-tmpfs"] = boolFromVarlink(opts.Readonlytmpfs, "read-only-tmpfs", true)
m["restart"] = stringFromVarlink(opts.Restart, "restart", nil)
m["rm"] = boolFromVarlink(opts.Rm, "rm", false)
m["rootfs"] = boolFromVarlink(opts.Rootfs, "rootfs", false)
m["security-opt"] = stringArrayFromVarlink(opts.SecurityOpt, "security-opt", nil)
m["shm-size"] = stringFromVarlink(opts.ShmSize, "shm-size", &shmSize)
m["stop-signal"] = stringFromVarlink(opts.StopSignal, "stop-signal", nil)
m["stop-timeout"] = uintFromVarlink(opts.StopTimeout, "stop-timeout", nil)
m["storage-opt"] = stringSliceFromVarlink(opts.StorageOpt, "storage-opt", nil)
m["subgidname"] = stringFromVarlink(opts.Subgidname, "subgidname", nil)
m["subuidname"] = stringFromVarlink(opts.Subuidname, "subuidname", nil)
m["sysctl"] = stringSliceFromVarlink(opts.Sysctl, "sysctl", nil)
m["systemd"] = stringFromVarlink(opts.Systemd, "systemd", &systemdDefault)
m["tmpfs"] = stringSliceFromVarlink(opts.Tmpfs, "tmpfs", nil)
m["tty"] = boolFromVarlink(opts.Tty, "tty", false)
m["uidmap"] = stringSliceFromVarlink(opts.Uidmap, "uidmap", nil)
m["ulimit"] = stringSliceFromVarlink(opts.Ulimit, "ulimit", nil)
m["user"] = stringFromVarlink(opts.User, "user", nil)
m["userns"] = stringFromVarlink(opts.Userns, "userns", nil)
m["uts"] = stringFromVarlink(opts.Uts, "uts", nil)
m["mount"] = stringArrayFromVarlink(opts.Mount, "mount", nil)
m["volume"] = stringArrayFromVarlink(opts.Volume, "volume", nil)
m["volumes-from"] = stringSliceFromVarlink(opts.VolumesFrom, "volumes-from", nil)
m["workdir"] = stringFromVarlink(opts.WorkDir, "workdir", nil)
gcli := GenericCLIResults{m, opts.Args}
return gcli
}
// Find returns a flag from a GenericCLIResults by name
func (g GenericCLIResults) Find(name string) GenericCLIResult {
result, ok := g.results[name]
if ok {
return result
}
panic(errors.Errorf("unable to find generic flag for varlink %s", name))
}

View File

@ -1,49 +0,0 @@
// +build varlink
package varlinkapi
import iopodman "github.com/containers/podman/v2/pkg/varlink"
// ListContainerMounts ...
func (i *VarlinkAPI) ListContainerMounts(call iopodman.VarlinkCall) error {
mounts := make(map[string]string)
allContainers, err := i.Runtime.GetAllContainers()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
for _, container := range allContainers {
mounted, mountPoint, err := container.Mounted()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
if mounted {
mounts[container.ID()] = mountPoint
}
}
return call.ReplyListContainerMounts(mounts)
}
// MountContainer ...
func (i *VarlinkAPI) MountContainer(call iopodman.VarlinkCall, name string) error {
container, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
path, err := container.Mount()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyMountContainer(path)
}
// UnmountContainer ...
func (i *VarlinkAPI) UnmountContainer(call iopodman.VarlinkCall, name string, force bool) error {
container, err := i.Runtime.LookupContainer(name)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
if err := container.Unmount(force); err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyUnmountContainer()
}

View File

@ -1,389 +0,0 @@
// +build varlink
package varlinkapi
import (
"context"
"encoding/json"
"fmt"
"strconv"
"syscall"
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
iopodman "github.com/containers/podman/v2/pkg/varlink"
"github.com/cri-o/ocicni/pkg/ocicni"
"github.com/docker/go-connections/nat"
"github.com/pkg/errors"
)
// CreatePod ...
func (i *VarlinkAPI) CreatePod(call iopodman.VarlinkCall, create iopodman.PodCreate) error {
var options []libpod.PodCreateOption
if create.Infra {
options = append(options, libpod.WithInfraContainer())
nsOptions, err := GetNamespaceOptions(create.Share)
if err != nil {
return err
}
options = append(options, nsOptions...)
}
if create.CgroupParent != "" {
options = append(options, libpod.WithPodCgroupParent(create.CgroupParent))
}
if len(create.Labels) > 0 {
options = append(options, libpod.WithPodLabels(create.Labels))
}
if create.Name != "" {
options = append(options, libpod.WithPodName(create.Name))
}
if len(create.Share) > 0 && !create.Infra {
return call.ReplyErrorOccurred("You cannot share kernel namespaces on the pod level without an infra container")
}
if len(create.Share) == 0 && create.Infra {
return call.ReplyErrorOccurred("You must share kernel namespaces to run an infra container")
}
if len(create.Publish) > 0 {
if !create.Infra {
return call.ReplyErrorOccurred("you must have an infra container to publish port bindings to the host")
}
portBindings, err := CreatePortBindings(create.Publish)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
options = append(options, libpod.WithInfraContainerPorts(portBindings))
}
options = append(options, libpod.WithPodCgroups())
pod, err := i.Runtime.NewPod(getContext(), options...)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyCreatePod(pod.ID())
}
// ListPods ...
func (i *VarlinkAPI) ListPods(call iopodman.VarlinkCall) error {
var (
listPods []iopodman.ListPodData
)
pods, err := i.Runtime.GetAllPods()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
opts := PsOptions{}
for _, pod := range pods {
listPod, err := makeListPod(pod, opts)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
listPods = append(listPods, listPod)
}
return call.ReplyListPods(listPods)
}
// GetPod ...
func (i *VarlinkAPI) GetPod(call iopodman.VarlinkCall, name string) error {
pod, err := i.Runtime.LookupPod(name)
if err != nil {
return call.ReplyPodNotFound(name, err.Error())
}
opts := PsOptions{}
listPod, err := makeListPod(pod, opts)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyGetPod(listPod)
}
// GetPodsByStatus returns a slice of pods filtered by a libpod status
func (i *VarlinkAPI) GetPodsByStatus(call iopodman.VarlinkCall, statuses []string) error {
filterFuncs := func(p *libpod.Pod) bool {
state, _ := p.GetPodStatus()
for _, status := range statuses {
if state == status {
return true
}
}
return false
}
filteredPods, err := i.Runtime.Pods(filterFuncs)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
podIDs := make([]string, 0, len(filteredPods))
for _, p := range filteredPods {
podIDs = append(podIDs, p.ID())
}
return call.ReplyGetPodsByStatus(podIDs)
}
// InspectPod ...
func (i *VarlinkAPI) InspectPod(call iopodman.VarlinkCall, name string) error {
pod, err := i.Runtime.LookupPod(name)
if err != nil {
return call.ReplyPodNotFound(name, err.Error())
}
inspectData, err := pod.Inspect()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
b, err := json.Marshal(&inspectData)
if err != nil {
return call.ReplyErrorOccurred("unable to serialize")
}
return call.ReplyInspectPod(string(b))
}
// StartPod ...
func (i *VarlinkAPI) StartPod(call iopodman.VarlinkCall, name string) error {
pod, err := i.Runtime.LookupPod(name)
if err != nil {
return call.ReplyPodNotFound(name, err.Error())
}
ctnrs, err := pod.AllContainers()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
if 0 == len(ctnrs) {
return call.ReplyNoContainersInPod(name)
}
ctrErrs, err := pod.Start(getContext())
callErr := handlePodCall(call, pod, ctrErrs, err)
if callErr != nil {
return err
}
return call.ReplyStartPod(pod.ID())
}
// StopPod ...
func (i *VarlinkAPI) StopPod(call iopodman.VarlinkCall, name string, timeout int64) error {
pod, err := i.Runtime.LookupPod(name)
if err != nil {
return call.ReplyPodNotFound(name, err.Error())
}
ctrErrs, err := pod.StopWithTimeout(getContext(), true, int(timeout))
callErr := handlePodCall(call, pod, ctrErrs, err)
if callErr != nil {
return err
}
return call.ReplyStopPod(pod.ID())
}
// RestartPod ...
func (i *VarlinkAPI) RestartPod(call iopodman.VarlinkCall, name string) error {
pod, err := i.Runtime.LookupPod(name)
if err != nil {
return call.ReplyPodNotFound(name, err.Error())
}
ctnrs, err := pod.AllContainers()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
if 0 == len(ctnrs) {
return call.ReplyNoContainersInPod(name)
}
ctrErrs, err := pod.Restart(getContext())
callErr := handlePodCall(call, pod, ctrErrs, err)
if callErr != nil {
return err
}
return call.ReplyRestartPod(pod.ID())
}
// KillPod kills the running containers in a pod. If you want to use the default SIGTERM signal,
// just send a -1 for the signal arg.
func (i *VarlinkAPI) KillPod(call iopodman.VarlinkCall, name string, signal int64) error {
killSignal := uint(syscall.SIGTERM)
if signal != -1 {
killSignal = uint(signal)
}
pod, err := i.Runtime.LookupPod(name)
if err != nil {
return call.ReplyPodNotFound(name, err.Error())
}
ctrErrs, err := pod.Kill(context.TODO(), killSignal)
callErr := handlePodCall(call, pod, ctrErrs, err)
if callErr != nil {
return err
}
return call.ReplyKillPod(pod.ID())
}
// PausePod ...
func (i *VarlinkAPI) PausePod(call iopodman.VarlinkCall, name string) error {
pod, err := i.Runtime.LookupPod(name)
if err != nil {
return call.ReplyPodNotFound(name, err.Error())
}
ctrErrs, err := pod.Pause(context.TODO())
callErr := handlePodCall(call, pod, ctrErrs, err)
if callErr != nil {
return err
}
return call.ReplyPausePod(pod.ID())
}
// UnpausePod ...
func (i *VarlinkAPI) UnpausePod(call iopodman.VarlinkCall, name string) error {
pod, err := i.Runtime.LookupPod(name)
if err != nil {
return call.ReplyPodNotFound(name, err.Error())
}
ctrErrs, err := pod.Unpause(context.TODO())
callErr := handlePodCall(call, pod, ctrErrs, err)
if callErr != nil {
return err
}
return call.ReplyUnpausePod(pod.ID())
}
// RemovePod ...
func (i *VarlinkAPI) RemovePod(call iopodman.VarlinkCall, name string, force bool) error {
ctx := getContext()
pod, err := i.Runtime.LookupPod(name)
if err != nil {
return call.ReplyPodNotFound(name, err.Error())
}
if err = i.Runtime.RemovePod(ctx, pod, true, force); err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyRemovePod(pod.ID())
}
// GetPodStats ...
func (i *VarlinkAPI) GetPodStats(call iopodman.VarlinkCall, name string) error {
pod, err := i.Runtime.LookupPod(name)
if err != nil {
return call.ReplyPodNotFound(name, err.Error())
}
prevStats := make(map[string]*define.ContainerStats)
podStats, err := pod.GetPodStats(prevStats)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
if len(podStats) == 0 {
return call.ReplyNoContainerRunning()
}
containersStats := make([]iopodman.ContainerStats, 0)
for ctrID, containerStats := range podStats {
cs := iopodman.ContainerStats{
Id: ctrID,
Name: containerStats.Name,
Cpu: containerStats.CPU,
Cpu_nano: int64(containerStats.CPUNano),
System_nano: int64(containerStats.SystemNano),
Mem_usage: int64(containerStats.MemUsage),
Mem_limit: int64(containerStats.MemLimit),
Mem_perc: containerStats.MemPerc,
Net_input: int64(containerStats.NetInput),
Net_output: int64(containerStats.NetOutput),
Block_input: int64(containerStats.BlockInput),
Block_output: int64(containerStats.BlockOutput),
Pids: int64(containerStats.PIDs),
}
containersStats = append(containersStats, cs)
}
return call.ReplyGetPodStats(pod.ID(), containersStats)
}
// getPodsByContext returns a slice of pod ids based on all, latest, or a list
func (i *VarlinkAPI) GetPodsByContext(call iopodman.VarlinkCall, all, latest bool, input []string) error {
var podids []string
pods, err := getPodsByContext(all, latest, input, i.Runtime)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
for _, p := range pods {
podids = append(podids, p.ID())
}
return call.ReplyGetPodsByContext(podids)
}
// PodStateData returns a container's state data in string format
func (i *VarlinkAPI) PodStateData(call iopodman.VarlinkCall, name string) error {
pod, err := i.Runtime.LookupPod(name)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
data, err := pod.Inspect()
if err != nil {
return call.ReplyErrorOccurred("unable to obtain pod state")
}
b, err := json.Marshal(data)
if err != nil {
return call.ReplyErrorOccurred("unable to serialize pod inspect data")
}
return call.ReplyPodStateData(string(b))
}
// TopPod provides the top stats for a given or latest pod
func (i *VarlinkAPI) TopPod(call iopodman.VarlinkCall, name string, latest bool, descriptors []string) error {
var (
pod *libpod.Pod
err error
)
if latest {
name = "latest"
pod, err = i.Runtime.GetLatestPod()
} else {
pod, err = i.Runtime.LookupPod(name)
}
if err != nil {
return call.ReplyPodNotFound(name, err.Error())
}
podStatus, err := pod.GetPodStatus()
if err != nil {
return call.ReplyErrorOccurred(fmt.Sprintf("unable to get status for pod %s", pod.ID()))
}
if podStatus != "Running" {
return call.ReplyErrorOccurred("pod top can only be used on pods with at least one running container")
}
reply, err := pod.GetPodPidInformation(descriptors)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyTopPod(reply)
}
// CreatePortBindings iterates ports mappings and exposed ports into a format CNI understands
func CreatePortBindings(ports []string) ([]ocicni.PortMapping, error) {
var portBindings []ocicni.PortMapping
// The conversion from []string to natBindings is temporary while mheon reworks the port
// deduplication code. Eventually that step will not be required.
_, natBindings, err := nat.ParsePortSpecs(ports)
if err != nil {
return nil, err
}
for containerPb, hostPb := range natBindings {
var pm ocicni.PortMapping
pm.ContainerPort = int32(containerPb.Int())
for _, i := range hostPb {
var hostPort int
var err error
pm.HostIP = i.HostIP
if i.HostPort == "" {
hostPort = containerPb.Int()
} else {
hostPort, err = strconv.Atoi(i.HostPort)
if err != nil {
return nil, errors.Wrapf(err, "unable to convert host port to integer")
}
}
pm.HostPort = int32(hostPort)
pm.Protocol = containerPb.Proto()
portBindings = append(portBindings, pm)
}
}
return portBindings, nil
}

View File

@ -1,29 +0,0 @@
// +build varlink remoteclient
package varlinkapi
import (
"github.com/containers/podman/v2/libpod/define"
iopodman "github.com/containers/podman/v2/pkg/varlink"
)
// ContainerStatsToLibpodContainerStats converts the varlink containerstats to a libpod
// container stats
func ContainerStatsToLibpodContainerStats(stats iopodman.ContainerStats) define.ContainerStats {
cstats := define.ContainerStats{
ContainerID: stats.Id,
Name: stats.Name,
CPU: stats.Cpu,
CPUNano: uint64(stats.Cpu_nano),
SystemNano: uint64(stats.System_nano),
MemUsage: uint64(stats.Mem_usage),
MemLimit: uint64(stats.Mem_limit),
MemPerc: stats.Mem_perc,
NetInput: uint64(stats.Net_input),
NetOutput: uint64(stats.Net_output),
BlockInput: uint64(stats.Block_input),
BlockOutput: uint64(stats.Block_output),
PIDs: uint64(stats.Pids),
}
return cstats
}

View File

@ -1,66 +0,0 @@
package varlinkapi
import (
"github.com/containers/podman/v2/libpod"
"github.com/sirupsen/logrus"
)
// getPodsByContext returns a slice of pods. Note that all, latest and pods are
// mutually exclusive arguments.
func getPodsByContext(all, latest bool, pods []string, runtime *libpod.Runtime) ([]*libpod.Pod, error) {
var outpods []*libpod.Pod
if all {
return runtime.GetAllPods()
}
if latest {
p, err := runtime.GetLatestPod()
if err != nil {
return nil, err
}
outpods = append(outpods, p)
return outpods, nil
}
var err error
for _, p := range pods {
pod, e := runtime.LookupPod(p)
if e != nil {
// Log all errors here, so callers don't need to.
logrus.Debugf("Error looking up pod %q: %v", p, e)
if err == nil {
err = e
}
} else {
outpods = append(outpods, pod)
}
}
return outpods, err
}
// getContainersByContext gets pods whether all, latest, or a slice of names/ids
// is specified.
func getContainersByContext(all, latest bool, names []string, runtime *libpod.Runtime) (ctrs []*libpod.Container, err error) {
var ctr *libpod.Container
ctrs = []*libpod.Container{}
switch {
case all:
ctrs, err = runtime.GetAllContainers()
case latest:
ctr, err = runtime.GetLatestContainer()
ctrs = append(ctrs, ctr)
default:
for _, n := range names {
ctr, e := runtime.LookupContainer(n)
if e != nil {
// Log all errors here, so callers don't need to.
logrus.Debugf("Error looking up container %q: %v", n, e)
if err == nil {
err = e
}
} else {
ctrs = append(ctrs, ctr)
}
}
}
return
}

View File

@ -1,129 +0,0 @@
// +build varlink
package varlinkapi
import (
"context"
"fmt"
"os"
goruntime "runtime"
"strconv"
"time"
"github.com/containers/image/v5/pkg/sysregistriesv2"
"github.com/containers/podman/v2/libpod/define"
iopodman "github.com/containers/podman/v2/pkg/varlink"
"github.com/sirupsen/logrus"
)
// GetVersion ...
func (i *VarlinkAPI) GetVersion(call iopodman.VarlinkCall) error {
versionInfo, err := define.GetVersion()
if err != nil {
return err
}
int64APIVersion, err := strconv.ParseInt(versionInfo.APIVersion, 10, 64)
if err != nil {
return err
}
return call.ReplyGetVersion(
versionInfo.Version,
versionInfo.GoVersion,
versionInfo.GitCommit,
time.Unix(versionInfo.Built, 0).Format(time.RFC3339),
versionInfo.OsArch,
int64APIVersion,
)
}
// GetInfo returns details about the podman host and its stores
func (i *VarlinkAPI) GetInfo(call iopodman.VarlinkCall) error {
versionInfo, err := define.GetVersion()
if err != nil {
return err
}
podmanInfo := iopodman.PodmanInfo{}
info, err := i.Runtime.Info()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
distribution := iopodman.InfoDistribution{
Distribution: info.Host.Distribution.Distribution,
Version: info.Host.Distribution.Version,
}
infoHost := iopodman.InfoHost{
Buildah_version: info.Host.BuildahVersion,
Distribution: distribution,
Mem_free: info.Host.MemFree,
Mem_total: info.Host.MemTotal,
Swap_free: info.Host.SwapFree,
Swap_total: info.Host.SwapTotal,
Arch: info.Host.Arch,
Cpus: int64(info.Host.CPUs),
Hostname: info.Host.Hostname,
Kernel: info.Host.Kernel,
Os: info.Host.OS,
Uptime: info.Host.Uptime,
Eventlogger: info.Host.EventLogger,
}
podmanInfo.Host = infoHost
pmaninfo := iopodman.InfoPodmanBinary{
Compiler: goruntime.Compiler,
Go_version: goruntime.Version(),
Podman_version: versionInfo.Version,
Git_commit: versionInfo.GitCommit,
}
graphStatus := iopodman.InfoGraphStatus{
Backing_filesystem: info.Store.GraphStatus["Backing Filesystem"],
Native_overlay_diff: info.Store.GraphStatus["Native Overlay Diff"],
Supports_d_type: info.Store.GraphStatus["Supports d_type"],
}
infoStore := iopodman.InfoStore{
Graph_driver_name: info.Store.GraphDriverName,
Containers: int64(info.Store.ContainerStore.Number),
Images: int64(info.Store.ImageStore.Number),
Run_root: info.Store.RunRoot,
Graph_root: info.Store.GraphRoot,
Graph_driver_options: fmt.Sprintf("%v", info.Store.GraphOptions),
Graph_status: graphStatus,
}
// Registry information if any is stored as the second list item
for key, val := range info.Registries {
if key == "search" {
podmanInfo.Registries.Search = val.([]string)
continue
}
regData := val.(sysregistriesv2.Registry)
if regData.Insecure {
podmanInfo.Registries.Insecure = append(podmanInfo.Registries.Insecure, key)
}
if regData.Blocked {
podmanInfo.Registries.Blocked = append(podmanInfo.Registries.Blocked, key)
}
}
podmanInfo.Store = infoStore
podmanInfo.Podman = pmaninfo
return call.ReplyGetInfo(podmanInfo)
}
// GetVersion ...
func (i *VarlinkAPI) Reset(call iopodman.VarlinkCall) error {
if err := i.Runtime.Reset(context.TODO()); err != nil {
logrus.Errorf("Reset Failed: %v", err)
if err := call.ReplyErrorOccurred(err.Error()); err != nil {
logrus.Errorf("Failed to send ReplyErrorOccurred: %v", err)
}
os.Exit(define.ExecErrorCodeGeneric)
}
if err := call.ReplyReset(); err != nil {
logrus.Errorf("Failed to send ReplyReset: %v", err)
os.Exit(define.ExecErrorCodeGeneric)
}
os.Exit(0)
return nil
}

View File

@ -1,80 +0,0 @@
// +build varlink
package varlinkapi
import (
"bufio"
"io"
"io/ioutil"
"os"
iopodman "github.com/containers/podman/v2/pkg/varlink"
"github.com/sirupsen/logrus"
)
// SendFile allows a client to send a file to the varlink server
func (i *VarlinkAPI) SendFile(call iopodman.VarlinkCall, ftype string, length int64) error {
if !call.WantsUpgrade() {
return call.ReplyErrorOccurred("client must use upgraded connection to send files")
}
outputFile, err := ioutil.TempFile("", "varlink_send")
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
defer outputFile.Close()
if err = call.ReplySendFile(outputFile.Name()); err != nil {
// If an error occurs while sending the reply, return the error
return err
}
writer := bufio.NewWriter(outputFile)
defer writer.Flush()
reader := call.Call.Reader
if _, err := io.CopyN(writer, reader, length); err != nil {
return err
}
logrus.Debugf("successfully received %s", outputFile.Name())
// Send an ACK to the client
call.Call.Writer.WriteString(outputFile.Name() + ":")
call.Call.Writer.Flush()
return nil
}
// ReceiveFile allows the varlink server to send a file to a client
func (i *VarlinkAPI) ReceiveFile(call iopodman.VarlinkCall, filepath string, delete bool) error {
if !call.WantsUpgrade() {
return call.ReplyErrorOccurred("client must use upgraded connection to send files")
}
fs, err := os.Open(filepath)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
fileInfo, err := fs.Stat()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
// Send the file length down to client
// Varlink connection upgraded
if err = call.ReplyReceiveFile(fileInfo.Size()); err != nil {
// If an error occurs while sending the reply, return the error
return err
}
reader := bufio.NewReader(fs)
_, err = reader.WriteTo(call.Writer)
if err != nil {
return err
}
if delete {
if err := os.Remove(filepath); err != nil {
return err
}
}
return call.Writer.Flush()
}

View File

@ -1,237 +0,0 @@
// +build varlink
package varlinkapi
import (
"context"
"strconv"
"strings"
"time"
"github.com/containers/buildah"
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/pkg/channel"
iopodman "github.com/containers/podman/v2/pkg/varlink"
"github.com/containers/storage/pkg/archive"
)
// getContext returns a non-nil, empty context
func getContext() context.Context {
return context.TODO()
}
func makeListContainer(containerID string, batchInfo BatchContainerStruct) iopodman.Container {
var (
mounts []iopodman.ContainerMount
ports []iopodman.ContainerPortMappings
)
ns := GetNamespaces(batchInfo.Pid)
for _, mount := range batchInfo.ConConfig.Spec.Mounts {
m := iopodman.ContainerMount{
Destination: mount.Destination,
Type: mount.Type,
Source: mount.Source,
Options: mount.Options,
}
mounts = append(mounts, m)
}
for _, pm := range batchInfo.ConConfig.PortMappings {
p := iopodman.ContainerPortMappings{
Host_port: strconv.Itoa(int(pm.HostPort)),
Host_ip: pm.HostIP,
Protocol: pm.Protocol,
Container_port: strconv.Itoa(int(pm.ContainerPort)),
}
ports = append(ports, p)
}
// If we find this needs to be done for other container endpoints, we should
// convert this to a separate function or a generic map from struct function.
namespace := iopodman.ContainerNameSpace{
User: ns.User,
Uts: ns.UTS,
Pidns: ns.PIDNS,
Pid: ns.PID,
Cgroup: ns.Cgroup,
Net: ns.NET,
Mnt: ns.MNT,
Ipc: ns.IPC,
}
lc := iopodman.Container{
Id: containerID,
Image: batchInfo.ConConfig.RootfsImageName,
Imageid: batchInfo.ConConfig.RootfsImageID,
Command: batchInfo.ConConfig.Spec.Process.Args,
Createdat: batchInfo.ConConfig.CreatedTime.Format(time.RFC3339),
Runningfor: time.Since(batchInfo.ConConfig.CreatedTime).String(),
Status: batchInfo.ConState.String(),
Ports: ports,
Names: batchInfo.ConConfig.Name,
Labels: batchInfo.ConConfig.Labels,
Mounts: mounts,
Containerrunning: batchInfo.ConState == define.ContainerStateRunning,
Namespaces: namespace,
}
if batchInfo.Size != nil {
lc.Rootfssize = batchInfo.Size.RootFsSize
lc.Rwsize = batchInfo.Size.RwSize
}
return lc
}
func makeListPodContainers(containerID string, batchInfo BatchContainerStruct) iopodman.ListPodContainerInfo {
lc := iopodman.ListPodContainerInfo{
Id: containerID,
Status: batchInfo.ConState.String(),
Name: batchInfo.ConConfig.Name,
}
return lc
}
func makeListPod(pod *libpod.Pod, batchInfo PsOptions) (iopodman.ListPodData, error) {
var listPodsContainers []iopodman.ListPodContainerInfo
var errPodData = iopodman.ListPodData{}
status, err := pod.GetPodStatus()
if err != nil {
return errPodData, err
}
containers, err := pod.AllContainers()
if err != nil {
return errPodData, err
}
for _, ctr := range containers {
batchInfo, err := BatchContainerOp(ctr, batchInfo)
if err != nil {
return errPodData, err
}
listPodsContainers = append(listPodsContainers, makeListPodContainers(ctr.ID(), batchInfo))
}
listPod := iopodman.ListPodData{
Createdat: pod.CreatedTime().Format(time.RFC3339),
Id: pod.ID(),
Name: pod.Name(),
Status: status,
Cgroup: pod.CgroupParent(),
Numberofcontainers: strconv.Itoa(len(listPodsContainers)),
Containersinfo: listPodsContainers,
}
return listPod, nil
}
func handlePodCall(call iopodman.VarlinkCall, pod *libpod.Pod, ctrErrs map[string]error, err error) error {
if err != nil && ctrErrs == nil {
return call.ReplyErrorOccurred(err.Error())
}
if ctrErrs != nil {
containerErrs := make([]iopodman.PodContainerErrorData, len(ctrErrs))
for ctr, reason := range ctrErrs {
ctrErr := iopodman.PodContainerErrorData{Containerid: ctr, Reason: reason.Error()}
containerErrs = append(containerErrs, ctrErr)
}
return call.ReplyPodContainerError(pod.ID(), containerErrs)
}
return nil
}
func stringCompressionToArchiveType(s string) archive.Compression {
switch strings.ToUpper(s) {
case "BZIP2":
return archive.Bzip2
case "GZIP":
return archive.Gzip
case "XZ":
return archive.Xz
}
return archive.Uncompressed
}
func stringPullPolicyToType(s string) buildah.PullPolicy {
switch strings.ToUpper(s) {
case "PULLIFMISSING":
return buildah.PullIfMissing
case "PULLALWAYS":
return buildah.PullAlways
case "PULLNEVER":
return buildah.PullNever
}
return buildah.PullIfMissing
}
func derefBool(inBool *bool) bool {
if inBool == nil {
return false
}
return *inBool
}
func derefString(in *string) string {
if in == nil {
return ""
}
return *in
}
func makePsOpts(inOpts iopodman.PsOpts) PsOptions {
last := 0
if inOpts.Last != nil {
lastT := *inOpts.Last
last = int(lastT)
}
return PsOptions{
All: inOpts.All,
Last: last,
Latest: derefBool(inOpts.Latest),
NoTrunc: derefBool(inOpts.NoTrunc),
Pod: derefBool(inOpts.Pod),
Size: derefBool(inOpts.Size),
Sort: derefString(inOpts.Sort),
Namespace: true,
Sync: derefBool(inOpts.Sync),
}
}
// forwardOutput is a helper method for varlink endpoints that employ both more and without
// more. it is capable of sending updates as the output writer gets them or append them
// all to a log. the chan error is the error from the libpod call so we can honor
// and error event in that case.
func forwardOutput(log []string, c chan error, wantsMore bool, output channel.WriteCloser, reply func(br iopodman.MoreResponse) error) ([]string, error) {
done := false
for {
select {
// We need to check if the libpod func being called has returned an
// error yet
case err := <-c:
if err != nil {
return nil, err
}
done = true
// if no error is found, we pull what we can from the log writer and
// append it to log string slice
case line := <-output.Chan():
log = append(log, string(line))
// If the end point is being used in more mode, send what we have
if wantsMore {
br := iopodman.MoreResponse{
Logs: log,
}
if err := reply(br); err != nil {
return nil, err
}
// "reset" the log to empty because we are sending what we
// get as we get it
log = []string{}
}
}
if done {
break
}
}
return log, nil
}

View File

@ -1,204 +0,0 @@
package virtwriter
import (
"bufio"
"encoding/binary"
"encoding/json"
"io"
"time"
"github.com/pkg/errors"
"k8s.io/client-go/tools/remotecommand"
)
// SocketDest is the "key" to where IO should go on the varlink
// multiplexed socket
type SocketDest int
const (
// ToStdout indicates traffic should go stdout
ToStdout SocketDest = iota
// ToStdin indicates traffic came from stdin
ToStdin SocketDest = iota
// ToStderr indicates traffuc should go to stderr
ToStderr SocketDest = iota
// TerminalResize indicates a terminal resize event has occurred
// and data should be passed to resizer
TerminalResize SocketDest = iota
// Quit and detach
Quit SocketDest = iota
// HangUpFromClient hangs up from the client
HangUpFromClient SocketDest = iota
)
// ErrClientHangup signifies that the client wants to drop its connection from
// the server.
var ErrClientHangup = errors.New("client hangup")
// IntToSocketDest returns a socketdest based on integer input
func IntToSocketDest(i int) SocketDest {
switch i {
case ToStdout.Int():
return ToStdout
case ToStderr.Int():
return ToStderr
case ToStdin.Int():
return ToStdin
case TerminalResize.Int():
return TerminalResize
case Quit.Int():
return Quit
case HangUpFromClient.Int():
return HangUpFromClient
default:
return ToStderr
}
}
// Int returns the integer representation of the socket dest
func (sd SocketDest) Int() int {
return int(sd)
}
// VirtWriteCloser are writers for attach which include the dest
// of the data
type VirtWriteCloser struct {
writer *bufio.Writer
dest SocketDest
}
// NewVirtWriteCloser is a constructor
func NewVirtWriteCloser(w *bufio.Writer, dest SocketDest) VirtWriteCloser {
return VirtWriteCloser{w, dest}
}
// Close is a required method for a writecloser
func (v VirtWriteCloser) Close() error {
return v.writer.Flush()
}
// Write prepends a header to the input message. The header is
// 8bytes. Position one contains the destination. Positions
// 5,6,7,8 are a big-endian encoded uint32 for len of the message.
func (v VirtWriteCloser) Write(input []byte) (int, error) {
header := []byte{byte(v.dest), 0, 0, 0}
// Go makes us define the byte for big endian
mlen := make([]byte, 4)
binary.BigEndian.PutUint32(mlen, uint32(len(input)))
// append the message len to the header
msg := append(header, mlen...)
// append the message to the header
msg = append(msg, input...)
_, err := v.writer.Write(msg)
if err != nil {
return 0, err
}
err = v.writer.Flush()
return len(input), err
}
// Reader decodes the content that comes over the wire and directs it to the proper destination.
func Reader(r *bufio.Reader, output, errput, input io.Writer, resize chan remotecommand.TerminalSize, execEcChan chan int) error {
var messageSize int64
headerBytes := make([]byte, 8)
if r == nil {
return errors.Errorf("Reader must not be nil")
}
for {
n, err := io.ReadFull(r, headerBytes)
if err != nil {
return errors.Wrapf(err, "Virtual Read failed, %d", n)
}
if n < 8 {
return errors.New("short read and no full header read")
}
messageSize = int64(binary.BigEndian.Uint32(headerBytes[4:8]))
switch IntToSocketDest(int(headerBytes[0])) {
case ToStdout:
if output != nil {
_, err := io.CopyN(output, r, messageSize)
if err != nil {
return err
}
}
case ToStderr:
if errput != nil {
_, err := io.CopyN(errput, r, messageSize)
if err != nil {
return err
}
}
case ToStdin:
if input != nil {
_, err := io.CopyN(input, r, messageSize)
if err != nil {
return err
}
}
case TerminalResize:
if resize != nil {
out := make([]byte, messageSize)
if messageSize > 0 {
_, err = io.ReadFull(r, out)
if err != nil {
return err
}
}
// Resize events come over in bytes, need to be reserialized
resizeEvent := remotecommand.TerminalSize{}
if err := json.Unmarshal(out, &resizeEvent); err != nil {
return err
}
resize <- resizeEvent
}
case Quit:
out := make([]byte, messageSize)
if messageSize > 0 {
_, err = io.ReadFull(r, out)
if err != nil {
return err
}
}
if execEcChan != nil {
ecInt := binary.BigEndian.Uint32(out)
execEcChan <- int(ecInt)
}
return nil
case HangUpFromClient:
// This sleep allows the pipes to flush themselves before tearing everything down.
// It makes me sick to do it but after a full day I cannot put my finger on the race
// that occurs when closing things up. It would require a significant rewrite of code
// to make the pipes close down properly. Given that we are currently discussing a
// rewrite of all things remote, this hardly seems worth resolving.
//
// reproducer: echo hello | (podman-remote run -i alpine cat)
time.Sleep(1 * time.Second)
return ErrClientHangup
default:
// Something really went wrong
return errors.New("unknown multiplex destination")
}
}
}
// HangUp sends message to peer to close connection
func HangUp(writer *bufio.Writer, ec uint32) (err error) {
n := 0
msg := make([]byte, 4)
binary.BigEndian.PutUint32(msg, ec)
writeQuit := NewVirtWriteCloser(writer, Quit)
if n, err = writeQuit.Write(msg); err != nil {
return
}
if n != len(msg) {
return errors.Errorf("Failed to send complete %s message", string(msg))
}
return
}

View File

@ -1,165 +0,0 @@
// +build varlink
package varlinkapi
import (
"context"
"encoding/json"
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/pkg/domain/infra/abi/parse"
iopodman "github.com/containers/podman/v2/pkg/varlink"
)
// VolumeCreate creates a libpod volume based on input from a varlink connection
func (i *VarlinkAPI) VolumeCreate(call iopodman.VarlinkCall, options iopodman.VolumeCreateOpts) error {
var volumeOptions []libpod.VolumeCreateOption
if len(options.VolumeName) > 0 {
volumeOptions = append(volumeOptions, libpod.WithVolumeName(options.VolumeName))
}
if len(options.Driver) > 0 {
volumeOptions = append(volumeOptions, libpod.WithVolumeDriver(options.Driver))
}
if len(options.Labels) > 0 {
volumeOptions = append(volumeOptions, libpod.WithVolumeLabels(options.Labels))
}
if len(options.Options) > 0 {
parsedOptions, err := parse.VolumeOptions(options.Options)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
volumeOptions = append(volumeOptions, parsedOptions...)
}
newVolume, err := i.Runtime.NewVolume(getContext(), volumeOptions...)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyVolumeCreate(newVolume.Name())
}
// VolumeRemove removes volumes by options.All or options.Volumes
func (i *VarlinkAPI) VolumeRemove(call iopodman.VarlinkCall, options iopodman.VolumeRemoveOpts) error {
success, failed, err := SharedRemoveVolumes(getContext(), i.Runtime, options.Volumes, options.All, options.Force)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
// Convert map[string]string to map[string]error
errStrings := make(map[string]string)
for k, v := range failed {
errStrings[k] = v.Error()
}
return call.ReplyVolumeRemove(success, errStrings)
}
// GetVolumes returns all the volumes known to the remote system
func (i *VarlinkAPI) GetVolumes(call iopodman.VarlinkCall, args []string, all bool) error {
var (
err error
reply []*libpod.Volume
volumes []iopodman.Volume
)
if all {
reply, err = i.Runtime.GetAllVolumes()
} else {
for _, v := range args {
vol, err := i.Runtime.GetVolume(v)
if err != nil {
return err
}
reply = append(reply, vol)
}
}
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
// Build the iopodman.volume struct for the return
for _, v := range reply {
newVol := iopodman.Volume{
Driver: v.Driver(),
Labels: v.Labels(),
MountPoint: v.MountPoint(),
Name: v.Name(),
Options: v.Options(),
}
volumes = append(volumes, newVol)
}
return call.ReplyGetVolumes(volumes)
}
// InspectVolume inspects a single volume, returning its JSON as a string.
func (i *VarlinkAPI) InspectVolume(call iopodman.VarlinkCall, name string) error {
vol, err := i.Runtime.LookupVolume(name)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
inspectOut, err := vol.Inspect()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
inspectJSON, err := json.Marshal(inspectOut)
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
return call.ReplyInspectVolume(string(inspectJSON))
}
// VolumesPrune removes unused images via a varlink call
func (i *VarlinkAPI) VolumesPrune(call iopodman.VarlinkCall) error {
var (
prunedErrors []string
prunedNames []string
)
responses, err := i.Runtime.PruneVolumes(getContext())
if err != nil {
return call.ReplyVolumesPrune([]string{}, []string{err.Error()})
}
for k, v := range responses {
if v == nil {
prunedNames = append(prunedNames, k)
} else {
prunedErrors = append(prunedErrors, v.Error())
}
}
return call.ReplyVolumesPrune(prunedNames, prunedErrors)
}
// Remove given set of volumes
func SharedRemoveVolumes(ctx context.Context, runtime *libpod.Runtime, vols []string, all, force bool) ([]string, map[string]error, error) {
var (
toRemove []*libpod.Volume
success []string
failed map[string]error
)
failed = make(map[string]error)
if all {
vols, err := runtime.Volumes()
if err != nil {
return nil, nil, err
}
toRemove = vols
} else {
for _, v := range vols {
vol, err := runtime.LookupVolume(v)
if err != nil {
failed[v] = err
continue
}
toRemove = append(toRemove, vol)
}
}
// We could parallelize this, but I haven't heard anyone complain about
// performance here yet, so hold off.
for _, vol := range toRemove {
if err := runtime.RemoveVolume(ctx, vol, force); err != nil {
failed[vol.Name()] = err
continue
}
success = append(success, vol.Name())
}
return success, failed, nil
}

View File

@ -661,7 +661,7 @@ func (p *PodmanTestIntegration) PodmanAsUser(args []string, uid, gid uint32, cwd
return &PodmanSessionIntegration{podmanSession}
}
// We don't support running Varlink when local
// RestartRemoteService stop and start API Server, usually to change config
func (p *PodmanTestIntegration) RestartRemoteService() {
p.StopRemoteService()
p.StartRemoteService()

View File

@ -107,7 +107,6 @@ func (p *PodmanTestIntegration) StopRemoteService() {
}
} else {
//p.ResetVarlinkAddress()
parentPid := fmt.Sprintf("%d", p.RemoteSession.Pid)
pgrep := exec.Command("pgrep", "-P", parentPid)
fmt.Printf("running: pgrep %s\n", parentPid)

View File

@ -59,13 +59,12 @@ func (p *PodmanTestIntegration) RestoreArtifact(image string) error {
}
func (p *PodmanTestIntegration) StopRemoteService() {}
func (p *PodmanTestIntegration) DelayForVarlink() {}
// SeedImages is a no-op for localized testing
func (p *PodmanTestIntegration) SeedImages() error {
return nil
}
// We don't support running Varlink when local
// We don't support running API service when local
func (p *PodmanTestIntegration) StartRemoteService() {
}

View File

@ -1,207 +0,0 @@
// +build remoteclientvarlink
package integration
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"syscall"
"time"
"github.com/containers/podman/v2/pkg/rootless"
"github.com/onsi/ginkgo"
)
func IsRemote() bool {
return true
}
func SkipIfRemote(reason string) {
ginkgo.Skip("[remote]: " + reason)
}
// Podman is the exec call to podman on the filesystem
func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration {
podmanSession := p.PodmanBase(args, false, false)
return &PodmanSessionIntegration{podmanSession}
}
// PodmanExtraFiles is the exec call to podman on the filesystem and passes down extra files
func (p *PodmanTestIntegration) PodmanExtraFiles(args []string, extraFiles []*os.File) *PodmanSessionIntegration {
podmanSession := p.PodmanAsUserBase(args, 0, 0, "", nil, false, false, nil, extraFiles)
return &PodmanSessionIntegration{podmanSession}
}
// PodmanNoCache calls podman with out adding the imagecache
func (p *PodmanTestIntegration) PodmanNoCache(args []string) *PodmanSessionIntegration {
podmanSession := p.PodmanBase(args, false, true)
return &PodmanSessionIntegration{podmanSession}
}
// PodmanNoEvents calls the Podman command without an imagecache and without an
// events backend. It is used mostly for caching and uncaching images.
func (p *PodmanTestIntegration) PodmanNoEvents(args []string) *PodmanSessionIntegration {
podmanSession := p.PodmanBase(args, true, true)
return &PodmanSessionIntegration{podmanSession}
}
func (p *PodmanTestIntegration) setDefaultRegistriesConfigEnv() {
defaultFile := filepath.Join(INTEGRATION_ROOT, "test/registries.conf")
os.Setenv("REGISTRIES_CONFIG_PATH", defaultFile)
}
func (p *PodmanTestIntegration) setRegistriesConfigEnv(b []byte) {
outfile := filepath.Join(p.TempDir, "registries.conf")
os.Setenv("REGISTRIES_CONFIG_PATH", outfile)
ioutil.WriteFile(outfile, b, 0644)
}
func resetRegistriesConfigEnv() {
os.Setenv("REGISTRIES_CONFIG_PATH", "")
}
func PodmanTestCreate(tempDir string) *PodmanTestIntegration {
pti := PodmanTestCreateUtil(tempDir, true)
pti.StartRemoteService()
return pti
}
func (p *PodmanTestIntegration) ResetVarlinkAddress() {
//os.Unsetenv("PODMAN_VARLINK_ADDRESS")
}
func (p *PodmanTestIntegration) SetVarlinkAddress(addr string) {
//os.Setenv("PODMAN_VARLINK_ADDRESS", addr)
}
func (p *PodmanTestIntegration) StartVarlink() {
if os.Geteuid() == 0 {
os.MkdirAll("/run/podman", 0755)
}
varlinkEndpoint := p.RemoteSocket
p.SetVarlinkAddress(p.RemoteSocket)
args := []string{"varlink", "--time", "0", varlinkEndpoint}
podmanOptions := getVarlinkOptions(p, args)
command := exec.Command(p.PodmanBinary, podmanOptions...)
fmt.Printf("Running: %s %s\n", p.PodmanBinary, strings.Join(podmanOptions, " "))
command.Start()
command.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
p.RemoteCommand = command
p.RemoteSession = command.Process
p.DelayForService()
}
func (p *PodmanTestIntegration) StopVarlink() {
var out bytes.Buffer
var pids []int
varlinkSession := p.RemoteSession
if !rootless.IsRootless() {
if err := varlinkSession.Kill(); err != nil {
fmt.Fprintf(os.Stderr, "error on varlink stop-kill %q", err)
}
if _, err := varlinkSession.Wait(); err != nil {
fmt.Fprintf(os.Stderr, "error on varlink stop-wait %q", err)
}
} else {
p.ResetVarlinkAddress()
parentPid := fmt.Sprintf("%d", p.RemoteSession.Pid)
pgrep := exec.Command("pgrep", "-P", parentPid)
fmt.Printf("running: pgrep %s\n", parentPid)
pgrep.Stdout = &out
err := pgrep.Run()
if err != nil {
fmt.Fprint(os.Stderr, "unable to find varlink pid")
}
for _, s := range strings.Split(out.String(), "\n") {
if len(s) == 0 {
continue
}
p, err := strconv.Atoi(s)
if err != nil {
fmt.Fprintf(os.Stderr, "unable to convert %s to int", s)
}
if p != 0 {
pids = append(pids, p)
}
}
pids = append(pids, p.RemoteSession.Pid)
for _, pid := range pids {
syscall.Kill(pid, syscall.SIGKILL)
}
}
socket := strings.Split(p.RemoteSocket, ":")[1]
if err := os.Remove(socket); err != nil {
fmt.Println(err)
}
}
//MakeOptions assembles all the podman main options
func (p *PodmanTestIntegration) makeOptions(args []string, noEvents, noCache bool) []string {
return args
}
//MakeOptions assembles all the podman main options
func getVarlinkOptions(p *PodmanTestIntegration, args []string) []string {
podmanOptions := strings.Split(fmt.Sprintf("--root %s --runroot %s --runtime %s --conmon %s --cni-config-dir %s --cgroup-manager %s",
p.CrioRoot, p.RunRoot, p.OCIRuntime, p.ConmonBinary, p.CNIConfigDir, p.CgroupManager), " ")
if os.Getenv("HOOK_OPTION") != "" {
podmanOptions = append(podmanOptions, os.Getenv("HOOK_OPTION"))
}
podmanOptions = append(podmanOptions, strings.Split(p.StorageOptions, " ")...)
podmanOptions = append(podmanOptions, args...)
return podmanOptions
}
func (p *PodmanTestIntegration) RestoreArtifactToCache(image string) error {
fmt.Printf("Restoring %s...\n", image)
dest := strings.Split(image, "/")
destName := fmt.Sprintf("/tmp/%s.tar", strings.Replace(strings.Join(strings.Split(dest[len(dest)-1], "/"), ""), ":", "-", -1))
p.CrioRoot = p.ImageCacheDir
restore := p.PodmanNoEvents([]string{"load", "-q", "-i", destName})
restore.WaitWithDefaultTimeout()
return nil
}
// SeedImages restores all the artifacts into the main store for remote tests
func (p *PodmanTestIntegration) SeedImages() error {
return p.RestoreAllArtifacts()
}
// RestoreArtifact puts the cached image into our test store
func (p *PodmanTestIntegration) RestoreArtifact(image string) error {
fmt.Printf("Restoring %s...\n", image)
dest := strings.Split(image, "/")
destName := fmt.Sprintf("/tmp/%s.tar", strings.Replace(strings.Join(strings.Split(dest[len(dest)-1], "/"), ""), ":", "-", -1))
args := []string{"load", "-q", "-i", destName}
podmanOptions := getVarlinkOptions(p, args)
command := exec.Command(p.PodmanBinary, podmanOptions...)
fmt.Printf("Running: %s %s\n", p.PodmanBinary, strings.Join(podmanOptions, " "))
command.Start()
command.Wait()
return nil
}
func (p *PodmanTestIntegration) DelayForVarlink() {
for i := 0; i < 5; i++ {
session := p.Podman([]string{"info"})
session.WaitWithDefaultTimeout()
if session.ExitCode() == 0 || i == 4 {
break
}
time.Sleep(1 * time.Second)
}
}
func populateCache(podman *PodmanTestIntegration) {}
func removeCache() {}

View File

@ -59,7 +59,7 @@ var _ = Describe("podman system reset", func() {
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
// If remote then the varlink service should have exited
// If remote then the API service should have exited
// On local tests this is a noop
podmanTest.StartRemoteService()

View File

@ -1,49 +0,0 @@
// +build varlink
package endpoint
import (
"encoding/json"
"os"
. "github.com/containers/podman/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Podman commit", func() {
var (
tempdir string
err error
endpointTest *EndpointTestIntegration
)
BeforeEach(func() {
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
}
endpointTest = Setup(tempdir)
endpointTest.StartVarlinkWithCache()
})
AfterEach(func() {
endpointTest.Cleanup()
})
It("ensure commit with uppercase image name does not panic", func() {
body := make(map[string]string)
body["image_name"] = "FOO"
body["format"] = "oci"
body["name"] = "top"
b, err := json.Marshal(body)
Expect(err).To(BeNil())
// run the container to be committed
_ = endpointTest.startTopContainer("top")
result := endpointTest.Varlink("Commit", string(b), false)
// This indicates an error occurred
Expect(len(result.StdErrToString())).To(BeNumerically(">", 0))
})
})

View File

@ -1,24 +0,0 @@
// +build varlink
package endpoint
import "encoding/json"
var (
STORAGE_FS = "vfs"
STORAGE_OPTIONS = "--storage-driver vfs"
ROOTLESS_STORAGE_FS = "vfs"
ROOTLESS_STORAGE_OPTIONS = "--storage-driver vfs"
CACHE_IMAGES = []string{ALPINE, BB, fedoraMinimal, nginx, redis, registry, infra, labels}
nginx = "quay.io/libpod/alpine_nginx:latest"
BB_GLIBC = "docker.io/library/busybox:glibc"
registry = "docker.io/library/registry:2.6"
labels = "quay.io/libpod/alpine_labels:latest"
)
func makeNameMessage(name string) string {
n := make(map[string]string)
n["name"] = name
b, _ := json.Marshal(n)
return string(b)
}

View File

@ -1,225 +0,0 @@
// +build varlink
package endpoint
import (
"bytes"
"encoding/json"
"fmt"
"os"
"os/exec"
"strconv"
"strings"
"syscall"
"time"
"github.com/containers/podman/v2/pkg/rootless"
iopodman "github.com/containers/podman/v2/pkg/varlink"
. "github.com/onsi/ginkgo"
"github.com/onsi/gomega/gexec"
)
var (
ARTIFACT_DIR = "/tmp/.artifacts"
CGROUP_MANAGER = "systemd"
defaultWaitTimeout = 90
//RESTORE_IMAGES = []string{ALPINE, BB}
INTEGRATION_ROOT string
ImageCacheDir = "/tmp/podman/imagecachedir"
VarlinkBinary = "/usr/bin/varlink"
ALPINE = "docker.io/library/alpine:latest"
infra = "k8s.gcr.io/pause:3.2"
BB = "docker.io/library/busybox:latest"
redis = "docker.io/library/redis:alpine"
fedoraMinimal = "quay.io/libpod/fedora-minimal:latest"
)
type EndpointTestIntegration struct {
ArtifactPath string
CNIConfigDir string
CgroupManager string
ConmonBinary string
CrioRoot string
//Host HostOS
ImageCacheDir string
ImageCacheFS string
OCIRuntime string
PodmanBinary string
RemoteTest bool
RunRoot string
SignaturePolicyPath string
StorageOptions string
TmpDir string
Timings []string
VarlinkBinary string
VarlinkCommand *exec.Cmd
VarlinkEndpoint string
VarlinkSession *os.Process
}
func (p *EndpointTestIntegration) StartVarlink() {
p.startVarlink(false)
}
func (p *EndpointTestIntegration) StartVarlinkWithCache() {
p.startVarlink(true)
}
func (p *EndpointTestIntegration) startVarlink(useImageCache bool) {
var (
counter int
)
if os.Geteuid() == 0 {
os.MkdirAll("/run/podman", 0755)
}
varlinkEndpoint := p.VarlinkEndpoint
//p.SetVarlinkAddress(p.RemoteSocket)
args := []string{"varlink", "--time", "0", varlinkEndpoint}
podmanOptions := getVarlinkOptions(p, args)
if useImageCache {
cacheOptions := []string{"--storage-opt", fmt.Sprintf("%s.imagestore=%s", p.ImageCacheFS, p.ImageCacheDir)}
podmanOptions = append(cacheOptions, podmanOptions...)
}
command := exec.Command(p.PodmanBinary, podmanOptions...)
fmt.Printf("Running: %s %s\n", p.PodmanBinary, strings.Join(podmanOptions, " "))
command.Start()
command.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
p.VarlinkCommand = command
p.VarlinkSession = command.Process
for {
if result := p.endpointReady(); result == 0 {
break
}
fmt.Println("Waiting for varlink connection to become active", counter)
time.Sleep(250 * time.Millisecond)
counter++
if counter > 40 {
Fail("varlink endpoint never became ready")
}
}
}
func (p *EndpointTestIntegration) endpointReady() int {
session := p.Varlink("GetVersion", "", false)
return session.ExitCode()
}
func (p *EndpointTestIntegration) StopVarlink() {
var out bytes.Buffer
var pids []int
varlinkSession := p.VarlinkSession
if !rootless.IsRootless() {
if err := varlinkSession.Kill(); err != nil {
fmt.Fprintf(os.Stderr, "error on varlink stop-kill %q", err)
}
if _, err := varlinkSession.Wait(); err != nil {
fmt.Fprintf(os.Stderr, "error on varlink stop-wait %q", err)
}
} else {
//p.ResetVarlinkAddress()
parentPid := fmt.Sprintf("%d", p.VarlinkSession.Pid)
pgrep := exec.Command("pgrep", "-P", parentPid)
fmt.Printf("running: pgrep %s\n", parentPid)
pgrep.Stdout = &out
err := pgrep.Run()
if err != nil {
fmt.Fprint(os.Stderr, "unable to find varlink pid")
}
for _, s := range strings.Split(out.String(), "\n") {
if len(s) == 0 {
continue
}
p, err := strconv.Atoi(s)
if err != nil {
fmt.Fprintf(os.Stderr, "unable to convert %s to int", s)
}
if p != 0 {
pids = append(pids, p)
}
}
pids = append(pids, p.VarlinkSession.Pid)
for _, pid := range pids {
syscall.Kill(pid, syscall.SIGKILL)
}
}
socket := strings.Split(p.VarlinkEndpoint, ":")[1]
if err := os.Remove(socket); err != nil {
fmt.Println(err)
}
}
type EndpointSession struct {
*gexec.Session
}
func getVarlinkOptions(p *EndpointTestIntegration, args []string) []string {
podmanOptions := strings.Split(fmt.Sprintf("--root %s --runroot %s --runtime %s --conmon %s --cni-config-dir %s --cgroup-manager %s",
p.CrioRoot, p.RunRoot, p.OCIRuntime, p.ConmonBinary, p.CNIConfigDir, p.CgroupManager), " ")
if os.Getenv("HOOK_OPTION") != "" {
podmanOptions = append(podmanOptions, os.Getenv("HOOK_OPTION"))
}
podmanOptions = append(podmanOptions, strings.Split(p.StorageOptions, " ")...)
podmanOptions = append(podmanOptions, args...)
return podmanOptions
}
func (p *EndpointTestIntegration) Varlink(endpoint, message string, more bool) *EndpointSession {
//call unix:/run/user/1000/podman/io.podman/io.podman.GetContainerStats '{"name": "foobar" }'
var (
command *exec.Cmd
)
args := []string{"call"}
if more {
args = append(args, "-m")
}
args = append(args, []string{fmt.Sprintf("%s/io.podman.%s", p.VarlinkEndpoint, endpoint)}...)
if len(message) > 0 {
args = append(args, message)
}
command = exec.Command(p.VarlinkBinary, args...)
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
if err != nil {
Fail(fmt.Sprintf("unable to run varlink command: %s\n%v", strings.Join(args, " "), err))
}
session.Wait(defaultWaitTimeout)
return &EndpointSession{session}
}
func (s *EndpointSession) StdErrToString() string {
fields := strings.Fields(string(s.Err.Contents()))
return strings.Join(fields, " ")
}
func (s *EndpointSession) OutputToString() string {
fields := strings.Fields(string(s.Out.Contents()))
return strings.Join(fields, " ")
}
func (s *EndpointSession) OutputToBytes() []byte {
out := s.OutputToString()
return []byte(out)
}
func (s *EndpointSession) OutputToStringMap() map[string]string {
var out map[string]string
json.Unmarshal(s.OutputToBytes(), &out)
return out
}
func (s *EndpointSession) OutputToMapToInt() map[string]int {
var out map[string]int
json.Unmarshal(s.OutputToBytes(), &out)
return out
}
func (s *EndpointSession) OutputToMoreResponse() iopodman.MoreResponse {
out := make(map[string]iopodman.MoreResponse)
json.Unmarshal(s.OutputToBytes(), &out)
return out["reply"]
}

View File

@ -1,72 +0,0 @@
// +build varlink
package endpoint
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestEndpoint(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Endpoint Suite")
}
var LockTmpDir string
var _ = SynchronizedBeforeSuite(func() []byte {
// Cache images
cwd, _ := os.Getwd()
INTEGRATION_ROOT = filepath.Join(cwd, "../../")
podman := Setup("/tmp")
podman.ArtifactPath = ARTIFACT_DIR
if _, err := os.Stat(ARTIFACT_DIR); os.IsNotExist(err) {
if err = os.Mkdir(ARTIFACT_DIR, 0777); err != nil {
fmt.Printf("%q\n", err)
os.Exit(1)
}
}
// make cache dir
if err := os.MkdirAll(ImageCacheDir, 0777); err != nil {
fmt.Printf("%q\n", err)
os.Exit(1)
}
podman.StartVarlink()
for _, image := range CACHE_IMAGES {
podman.createArtifact(image)
}
podman.StopVarlink()
// If running localized tests, the cache dir is created and populated. if the
// tests are remote, this is a no-op
populateCache(podman)
path, err := ioutil.TempDir("", "libpodlock")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
return []byte(path)
}, func(data []byte) {
LockTmpDir = string(data)
})
var _ = SynchronizedAfterSuite(func() {},
func() {
podman := Setup("/tmp")
if err := os.RemoveAll(podman.CrioRoot); err != nil {
fmt.Printf("%q\n", err)
os.Exit(1)
}
if err := os.RemoveAll(podman.ImageCacheDir); err != nil {
fmt.Printf("%q\n", err)
os.Exit(1)
}
})

View File

@ -1,68 +0,0 @@
// +build varlink
package endpoint
import (
"os"
. "github.com/containers/podman/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Podman exists", func() {
var (
tempdir string
err error
endpointTest *EndpointTestIntegration
)
BeforeEach(func() {
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
}
endpointTest = Setup(tempdir)
endpointTest.StartVarlinkWithCache()
})
AfterEach(func() {
endpointTest.Cleanup()
//f := CurrentGinkgoTestDescription()
//processTestResult(f)
})
It("image exists in local storage", func() {
result := endpointTest.Varlink("ImageExists", makeNameMessage(ALPINE), false)
Expect(result.ExitCode()).To(BeZero())
output := result.OutputToMapToInt()
Expect(output["exists"]).To(BeZero())
})
It("image exists in local storage by shortname", func() {
result := endpointTest.Varlink("ImageExists", makeNameMessage("alpine"), false)
Expect(result.ExitCode()).To(BeZero())
output := result.OutputToMapToInt()
Expect(output["exists"]).To(BeZero())
})
It("image does not exist in local storage", func() {
result := endpointTest.Varlink("ImageExists", makeNameMessage("alpineforest"), false)
Expect(result.ExitCode()).To(BeZero())
output := result.OutputToMapToInt()
Expect(output["exists"]).To(Equal(1))
})
It("container exists in local storage by name", func() {
_ = endpointTest.startTopContainer("top")
result := endpointTest.Varlink("ContainerExists", makeNameMessage("top"), false)
Expect(result.ExitCode()).To(BeZero())
output := result.OutputToMapToInt()
Expect(output["exists"]).To(BeZero())
})
})

View File

@ -1,46 +0,0 @@
// +build varlink
package endpoint
import (
"os"
. "github.com/containers/podman/v2/test/utils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Podman pull", func() {
var (
tempdir string
err error
endpointTest *EndpointTestIntegration
)
BeforeEach(func() {
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
}
endpointTest = Setup(tempdir)
endpointTest.StartVarlink()
})
AfterEach(func() {
endpointTest.Cleanup()
//f := CurrentGinkgoTestDescription()
//processTestResult(f)
})
It("podman pull", func() {
session := endpointTest.Varlink("PullImage", makeNameMessage(ALPINE), false)
Expect(session.ExitCode()).To(BeZero())
result := endpointTest.Varlink("ImageExists", makeNameMessage(ALPINE), false)
Expect(result.ExitCode()).To(BeZero())
output := result.OutputToMapToInt()
Expect(output["exists"]).To(BeZero())
})
})

View File

@ -1,214 +0,0 @@
// +build varlink
package endpoint
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/containers/podman/v2/pkg/rootless"
iopodman "github.com/containers/podman/v2/pkg/varlink"
"github.com/containers/storage/pkg/stringid"
"github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/sirupsen/logrus"
)
func Setup(tempDir string) *EndpointTestIntegration {
var (
endpoint string
)
cwd, _ := os.Getwd()
INTEGRATION_ROOT = filepath.Join(cwd, "../../")
podmanBinary := filepath.Join(cwd, "../../bin/podman")
if os.Getenv("PODMAN_BINARY") != "" {
podmanBinary = os.Getenv("PODMAN_BINARY")
}
conmonBinary := filepath.Join("/usr/libexec/podman/conmon")
altConmonBinary := "/usr/bin/conmon"
if _, err := os.Stat(conmonBinary); os.IsNotExist(err) {
conmonBinary = altConmonBinary
}
if os.Getenv("CONMON_BINARY") != "" {
conmonBinary = os.Getenv("CONMON_BINARY")
}
storageOptions := STORAGE_OPTIONS
if os.Getenv("STORAGE_OPTIONS") != "" {
storageOptions = os.Getenv("STORAGE_OPTIONS")
}
cgroupManager := CGROUP_MANAGER
if rootless.IsRootless() {
cgroupManager = "cgroupfs"
}
if os.Getenv("CGROUP_MANAGER") != "" {
cgroupManager = os.Getenv("CGROUP_MANAGER")
}
ociRuntime := os.Getenv("OCI_RUNTIME")
if ociRuntime == "" {
ociRuntime = "runc"
}
os.Setenv("DISABLE_HC_SYSTEMD", "true")
CNIConfigDir := "/etc/cni/net.d"
storageFs := STORAGE_FS
if rootless.IsRootless() {
storageFs = ROOTLESS_STORAGE_FS
}
uuid := stringid.GenerateNonCryptoID()
if !rootless.IsRootless() {
endpoint = fmt.Sprintf("unix:/run/podman/io.podman-%s", uuid)
} else {
runtimeDir := os.Getenv("XDG_RUNTIME_DIR")
socket := fmt.Sprintf("io.podman-%s", uuid)
fqpath := filepath.Join(runtimeDir, socket)
endpoint = fmt.Sprintf("unix:%s", fqpath)
}
eti := EndpointTestIntegration{
ArtifactPath: ARTIFACT_DIR,
CNIConfigDir: CNIConfigDir,
CgroupManager: cgroupManager,
ConmonBinary: conmonBinary,
CrioRoot: filepath.Join(tempDir, "crio"),
ImageCacheDir: ImageCacheDir,
ImageCacheFS: storageFs,
OCIRuntime: ociRuntime,
PodmanBinary: podmanBinary,
RunRoot: filepath.Join(tempDir, "crio-run"),
SignaturePolicyPath: filepath.Join(INTEGRATION_ROOT, "test/policy.json"),
StorageOptions: storageOptions,
TmpDir: tempDir,
// Timings: nil,
VarlinkBinary: VarlinkBinary,
VarlinkCommand: nil,
VarlinkEndpoint: endpoint,
VarlinkSession: nil,
}
return &eti
}
func (p *EndpointTestIntegration) Cleanup() {
// Remove all containers
// TODO Make methods to do all this?
p.stopAllContainers()
// TODO need to make stop all pods
p.StopVarlink()
// Nuke tempdir
if err := os.RemoveAll(p.TmpDir); err != nil {
fmt.Printf("%q\n", err)
}
// Clean up the registries configuration file ENV variable set in Create
resetRegistriesConfigEnv()
}
func (p *EndpointTestIntegration) listContainers() []iopodman.Container {
containers := p.Varlink("ListContainers", "", false)
var varlinkContainers map[string][]iopodman.Container
if err := json.Unmarshal(containers.OutputToBytes(), &varlinkContainers); err != nil {
logrus.Error("failed to unmarshal containers")
}
return varlinkContainers["containers"]
}
func (p *EndpointTestIntegration) stopAllContainers() {
containers := p.listContainers()
for _, container := range containers {
p.stopContainer(container.Id)
}
}
func (p *EndpointTestIntegration) stopContainer(cid string) {
p.Varlink("StopContainer", fmt.Sprintf("{\"name\":\"%s\", \"timeout\":0}", cid), false)
}
func resetRegistriesConfigEnv() {
os.Setenv("REGISTRIES_CONFIG_PATH", "")
}
func (p *EndpointTestIntegration) createArtifact(image string) {
if os.Getenv("NO_TEST_CACHE") != "" {
return
}
dest := strings.Split(image, "/")
destName := fmt.Sprintf("/tmp/%s.tar", strings.Replace(strings.Join(strings.Split(dest[len(dest)-1], "/"), ""), ":", "-", -1))
fmt.Printf("Caching %s at %s...", image, destName)
if _, err := os.Stat(destName); os.IsNotExist(err) {
pull := p.Varlink("PullImage", fmt.Sprintf("{\"name\":\"%s\"}", image), false)
Expect(pull.ExitCode()).To(Equal(0))
imageSave := iopodman.ImageSaveOptions{
// Name:image,
// Output: destName,
// Format: "oci-archive",
}
imageSave.Name = image
imageSave.Output = destName
imageSave.Format = "oci-archive"
foo := make(map[string]iopodman.ImageSaveOptions)
foo["options"] = imageSave
f, _ := json.Marshal(foo)
save := p.Varlink("ImageSave", string(f), false)
result := save.OutputToMoreResponse()
Expect(save.ExitCode()).To(Equal(0))
Expect(os.Rename(result.Id, destName)).To(BeNil())
fmt.Printf("\n")
} else {
fmt.Printf(" already exists.\n")
}
}
func populateCache(p *EndpointTestIntegration) {
p.CrioRoot = p.ImageCacheDir
p.StartVarlink()
for _, image := range CACHE_IMAGES {
p.RestoreArtifactToCache(image)
}
p.StopVarlink()
}
func (p *EndpointTestIntegration) RestoreArtifactToCache(image string) error {
fmt.Printf("Restoring %s...\n", image)
dest := strings.Split(image, "/")
destName := fmt.Sprintf("/tmp/%s.tar", strings.Replace(strings.Join(strings.Split(dest[len(dest)-1], "/"), ""), ":", "-", -1))
// fmt.Println(destName, p.ImageCacheDir)
load := p.Varlink("LoadImage", fmt.Sprintf("{\"name\": \"%s\", \"inputFile\": \"%s\"}", image, destName), false)
Expect(load.ExitCode()).To(BeZero())
return nil
}
func (p *EndpointTestIntegration) startTopContainer(name string) string {
t := true
args := iopodman.Create{
Args: []string{"docker.io/library/alpine:latest", "top"},
Tty: &t,
Detach: &t,
}
if len(name) > 0 {
args.Name = &name
}
b, err := json.Marshal(args)
if err != nil {
ginkgo.Fail("failed to marshal data for top container")
}
input := fmt.Sprintf("{\"create\":%s}", string(b))
top := p.Varlink("CreateContainer", input, false)
if top.ExitCode() != 0 {
ginkgo.Fail("failed to start top container")
}
start := p.Varlink("StartContainer", fmt.Sprintf("{\"name\":\"%s\"}", name), false)
if start.ExitCode() != 0 {
ginkgo.Fail("failed to start top container")
}
return start.OutputToString()
}

View File

@ -1,43 +0,0 @@
// +build varlink
package endpoint
import (
"os"
. "github.com/containers/podman/v2/test/utils"
"github.com/containers/podman/v2/version"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Podman version", func() {
var (
tempdir string
err error
endpointTest *EndpointTestIntegration
)
BeforeEach(func() {
tempdir, err = CreateTempDirInTempDir()
if err != nil {
os.Exit(1)
}
endpointTest = Setup(tempdir)
endpointTest.StartVarlink()
})
AfterEach(func() {
endpointTest.Cleanup()
//f := CurrentGinkgoTestDescription()
//processTestResult(f)
})
It("podman version", func() {
session := endpointTest.Varlink("GetVersion", "", false)
result := session.OutputToStringMap()
Expect(result["version"]).To(Equal(version.Version))
Expect(session.ExitCode()).To(Equal(0))
})
})

201
vendor/github.com/varlink/go/LICENSE generated vendored
View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,445 +0,0 @@
package main
import (
"bytes"
"fmt"
"go/format"
"io/ioutil"
"os"
"path"
"strings"
"github.com/varlink/go/varlink/idl"
)
func writeType(b *bytes.Buffer, t *idl.Type, json bool, ident int) {
switch t.Kind {
case idl.TypeBool:
b.WriteString("bool")
case idl.TypeInt:
b.WriteString("int64")
case idl.TypeFloat:
b.WriteString("float64")
case idl.TypeString, idl.TypeEnum:
b.WriteString("string")
case idl.TypeObject:
b.WriteString("json.RawMessage")
case idl.TypeArray:
b.WriteString("[]")
writeType(b, t.ElementType, json, ident)
case idl.TypeMap:
b.WriteString("map[string]")
writeType(b, t.ElementType, json, ident)
case idl.TypeMaybe:
b.WriteString("*")
writeType(b, t.ElementType, json, ident)
case idl.TypeAlias:
b.WriteString(t.Alias)
case idl.TypeStruct:
if len(t.Fields) == 0 {
b.WriteString("struct{}")
} else {
b.WriteString("struct {\n")
for _, field := range t.Fields {
for i := 0; i < ident+1; i++ {
b.WriteString("\t")
}
b.WriteString(strings.Title(field.Name) + " ")
writeType(b, field.Type, json, ident+1)
if json {
b.WriteString(" `json:\"" + field.Name)
if field.Type.Kind == idl.TypeMaybe {
b.WriteString(",omitempty")
}
b.WriteString("\"`")
}
b.WriteString("\n")
}
for i := 0; i < ident; i++ {
b.WriteString("\t")
}
b.WriteString("}")
}
}
}
func writeDocString(b *bytes.Buffer, s string) {
if s == "" {
return
}
// Quote multi-line docstrings
b.WriteString("// " + strings.Replace(s, "\n", "\n// ", -1))
b.WriteString("\n")
}
func generateTemplate(description string) (string, []byte, error) {
description = strings.TrimRight(description, "\n")
midl, err := idl.New(description)
if err != nil {
return "", nil, err
}
pkgname := strings.Replace(midl.Name, ".", "", -1)
var b bytes.Buffer
b.WriteString("// Generated with github.com/varlink/go/cmd/varlink-go-interface-generator\n\n")
writeDocString(&b, midl.Doc)
b.WriteString("package " + pkgname + "\n\n")
b.WriteString("@IMPORTS@\n\n")
b.WriteString("// Generated type declarations\n\n")
for _, a := range midl.Aliases {
writeDocString(&b, a.Doc)
b.WriteString("type " + a.Name + " ")
writeType(&b, a.Type, true, 0)
b.WriteString("\n\n")
}
for _, a := range midl.Errors {
writeDocString(&b, a.Doc)
b.WriteString("type " + a.Name + " ")
writeType(&b, a.Type, true, 0)
b.WriteString("\nfunc (e " + a.Name + ") Error() string {\n")
b.WriteString("\treturn \"" + midl.Name + "." + a.Name + "\"\n")
b.WriteString("}\n\n")
}
b.WriteString("func Dispatch_Error(err error) error {\n")
b.WriteString("\tif e, ok := err.(*varlink.Error); ok {\n")
b.WriteString("\t\tswitch e.Name {\n")
for _, a := range midl.Errors {
b.WriteString("\t\tcase \"" + midl.Name + "." + a.Name + "\":\n")
b.WriteString("\t\t\terrorRawParameters := e.Parameters.(*json.RawMessage)\n")
b.WriteString("\t\t\tif errorRawParameters == nil {\n")
b.WriteString("\t\t\t\treturn e\n")
b.WriteString("\t\t\t}\n")
b.WriteString("\t\t\tvar param " + a.Name + "\n")
b.WriteString("\t\t\terr := json.Unmarshal(*errorRawParameters, &param)\n")
b.WriteString("\t\t\tif err != nil {\n")
b.WriteString("\t\t\t\treturn e\n")
b.WriteString("\t\t\t}\n")
b.WriteString("\t\t\treturn &param\n")
}
b.WriteString("\t\t}\n")
b.WriteString("\t}\n")
b.WriteString("\treturn err\n")
b.WriteString("}\n\n")
b.WriteString("// Generated client method calls\n\n")
for _, m := range midl.Methods {
writeDocString(&b, m.Doc)
b.WriteString("type " + m.Name + "_methods struct{}\n")
b.WriteString("func " + m.Name + "() " + m.Name + "_methods { return " + m.Name + "_methods{} }\n\n")
b.WriteString("func (m " + m.Name + "_methods) Call(c *varlink.Connection")
for _, field := range m.In.Fields {
b.WriteString(", " + field.Name + "_in_ ")
writeType(&b, field.Type, false, 1)
}
b.WriteString(") (")
for _, field := range m.Out.Fields {
b.WriteString(field.Name + "_out_ ")
writeType(&b, field.Type, false, 1)
b.WriteString(", ")
}
b.WriteString("err_ error) {\n")
b.WriteString("receive, err_ := m.Send(c, 0")
for _, field := range m.In.Fields {
b.WriteString(", " + field.Name + "_in_ ")
}
b.WriteString(")\n")
b.WriteString("if err_ != nil {\n" +
"\treturn\n" +
"}\n")
b.WriteString("\t")
for _, field := range m.Out.Fields {
b.WriteString(field.Name + "_out_ ")
b.WriteString(", ")
}
b.WriteString("_, err_ = receive()\n")
b.WriteString("\treturn\n" +
"}\n\n")
b.WriteString("func (m " + m.Name + "_methods) Send(c *varlink.Connection, flags uint64")
for _, field := range m.In.Fields {
b.WriteString(", " + field.Name + "_in_ ")
writeType(&b, field.Type, false, 1)
}
b.WriteString(") (func() (")
for _, field := range m.Out.Fields {
writeType(&b, field.Type, false, 1)
b.WriteString(", ")
}
b.WriteString("uint64, error), error) {\n")
if len(m.In.Fields) > 0 {
b.WriteString("\tvar in ")
writeType(&b, m.In, true, 1)
b.WriteString("\n")
for _, field := range m.In.Fields {
switch field.Type.Kind {
case idl.TypeStruct, idl.TypeArray, idl.TypeMap:
b.WriteString("\tin." + strings.Title(field.Name) + " = ")
writeType(&b, field.Type, true, 1)
b.WriteString("(" + field.Name + "_in_)\n")
default:
b.WriteString("\tin." + strings.Title(field.Name) + " = " + field.Name + "_in_\n")
}
}
b.WriteString("\treceive, err := c.Send(\"" + midl.Name + "." + m.Name + "\", in, flags)\n")
} else {
b.WriteString("\treceive, err := c.Send(\"" + midl.Name + "." + m.Name + "\", nil, flags)\n")
}
b.WriteString("\tif err != nil {\n" +
"\t\treturn nil, err\n" +
"\t}\n")
b.WriteString("\treturn func() (")
for _, field := range m.Out.Fields {
b.WriteString(field.Name + "_out_ ")
writeType(&b, field.Type, false, 3)
b.WriteString(", ")
}
b.WriteString("flags uint64, err error) {\n")
if len(m.Out.Fields) > 0 {
b.WriteString("\t\tvar out ")
writeType(&b, m.Out, true, 2)
b.WriteString("\n")
b.WriteString("\t\tflags, err = receive(&out)\n")
} else {
b.WriteString("\t\tflags, err = receive(nil)\n")
}
b.WriteString("\t\tif err != nil {\n" +
"\t\t\terr = Dispatch_Error(err)\n" +
"\t\t\treturn\n" +
"\t\t}\n")
for _, field := range m.Out.Fields {
b.WriteString("\t\t" + field.Name + "_out_ = ")
switch field.Type.Kind {
case idl.TypeStruct, idl.TypeArray, idl.TypeMap:
writeType(&b, field.Type, false, 2)
b.WriteString("(out." + strings.Title(field.Name) + ")\n")
default:
b.WriteString("out." + strings.Title(field.Name) + "\n")
}
}
b.WriteString("\t\treturn\n" +
"\t}, nil\n")
b.WriteString("}\n\n")
}
b.WriteString("// Generated service interface with all methods\n\n")
b.WriteString("type " + pkgname + "Interface interface {\n")
for _, m := range midl.Methods {
b.WriteString("\t" + m.Name + "(c VarlinkCall")
for _, field := range m.In.Fields {
b.WriteString(", " + field.Name + "_ ")
writeType(&b, field.Type, false, 1)
}
b.WriteString(") error\n")
}
b.WriteString("}\n\n")
b.WriteString("// Generated service object with all methods\n\n")
b.WriteString("type VarlinkCall struct{ varlink.Call }\n\n")
b.WriteString("// Generated reply methods for all varlink errors\n\n")
for _, e := range midl.Errors {
writeDocString(&b, e.Doc)
b.WriteString("func (c *VarlinkCall) Reply" + e.Name + "(")
for i, field := range e.Type.Fields {
if i > 0 {
b.WriteString(", ")
}
b.WriteString(field.Name + "_ ")
writeType(&b, field.Type, false, 1)
}
b.WriteString(") error {\n")
b.WriteString("\tvar out " + e.Name + "\n")
if len(e.Type.Fields) > 0 {
for _, field := range e.Type.Fields {
switch field.Type.Kind {
case idl.TypeStruct, idl.TypeArray, idl.TypeMap:
b.WriteString("\tout." + strings.Title(field.Name) + " = ")
writeType(&b, field.Type, true, 1)
b.WriteString("(" + field.Name + "_)\n")
default:
b.WriteString("\tout." + strings.Title(field.Name) + " = " + field.Name + "_\n")
}
}
}
b.WriteString("\treturn c.ReplyError(\"" + midl.Name + "." + e.Name + "\", &out)\n")
b.WriteString("}\n\n")
}
b.WriteString("// Generated reply methods for all varlink methods\n\n")
for _, m := range midl.Methods {
b.WriteString("func (c *VarlinkCall) Reply" + m.Name + "(")
for i, field := range m.Out.Fields {
if i > 0 {
b.WriteString(", ")
}
b.WriteString(field.Name + "_ ")
writeType(&b, field.Type, false, 1)
}
b.WriteString(") error {\n")
if len(m.Out.Fields) > 0 {
b.WriteString("\tvar out ")
writeType(&b, m.Out, true, 1)
b.WriteString("\n")
for _, field := range m.Out.Fields {
switch field.Type.Kind {
case idl.TypeStruct, idl.TypeArray, idl.TypeMap:
b.WriteString("\tout." + strings.Title(field.Name) + " = ")
writeType(&b, field.Type, true, 1)
b.WriteString("(" + field.Name + "_)\n")
default:
b.WriteString("\tout." + strings.Title(field.Name) + " = " + field.Name + "_\n")
}
}
b.WriteString("\treturn c.Reply(&out)\n")
} else {
b.WriteString("\treturn c.Reply(nil)\n")
}
b.WriteString("}\n\n")
}
b.WriteString("// Generated dummy implementations for all varlink methods\n\n")
for _, m := range midl.Methods {
writeDocString(&b, m.Doc)
b.WriteString("func (s *VarlinkInterface) " + m.Name + "(c VarlinkCall")
for _, field := range m.In.Fields {
b.WriteString(", " + field.Name + "_ ")
writeType(&b, field.Type, false, 1)
}
b.WriteString(") error {\n" +
"\treturn c.ReplyMethodNotImplemented(\"" + midl.Name + "." + m.Name + "\")\n" +
"}\n\n")
}
b.WriteString("// Generated method call dispatcher\n\n")
b.WriteString("func (s *VarlinkInterface) VarlinkDispatch(call varlink.Call, methodname string) error {\n" +
"\tswitch methodname {\n")
for _, m := range midl.Methods {
b.WriteString("\tcase \"" + m.Name + "\":\n")
if len(m.In.Fields) > 0 {
b.WriteString("\t\tvar in ")
writeType(&b, m.In, true, 2)
b.WriteString("\n")
b.WriteString("\t\terr := call.GetParameters(&in)\n" +
"\t\tif err != nil {\n" +
"\t\t\treturn call.ReplyInvalidParameter(\"parameters\")\n" +
"\t\t}\n")
b.WriteString("\t\treturn s." + pkgname + "Interface." + m.Name + "(VarlinkCall{call}")
if len(m.In.Fields) > 0 {
for _, field := range m.In.Fields {
switch field.Type.Kind {
case idl.TypeStruct, idl.TypeArray, idl.TypeMap:
b.WriteString(", ")
writeType(&b, field.Type, false, 2)
b.WriteString("(in." + strings.Title(field.Name) + ")")
default:
b.WriteString(", in." + strings.Title(field.Name))
}
}
}
b.WriteString(")\n")
} else {
b.WriteString("\t\treturn s." + pkgname + "Interface." + m.Name + "(VarlinkCall{call})\n")
}
b.WriteString("\n")
}
b.WriteString("\tdefault:\n" +
"\t\treturn call.ReplyMethodNotFound(methodname)\n" +
"\t}\n" +
"}\n\n")
b.WriteString("// Generated varlink interface name\n\n")
b.WriteString("func (s *VarlinkInterface) VarlinkGetName() string {\n" +
"\treturn `" + midl.Name + "`\n" + "}\n\n")
b.WriteString("// Generated varlink interface description\n\n")
// Special-quote backtick, it cannot be part of a backtick-quoted string
b.WriteString("func (s *VarlinkInterface) VarlinkGetDescription() string {\n" +
"\treturn `" + strings.Replace(midl.Description, "`", "` + \"`\" + `", -1) + "\n`\n}\n\n")
b.WriteString("// Generated service interface\n\n")
b.WriteString("type VarlinkInterface struct {\n" +
"\t" + pkgname + "Interface\n" +
"}\n\n")
b.WriteString("func VarlinkNew(m " + pkgname + "Interface) *VarlinkInterface {\n" +
"\treturn &VarlinkInterface{m}\n" +
"}\n")
ret_string := b.String()
if strings.Contains(ret_string, "json.RawMessage") {
ret_string = strings.Replace(ret_string, "@IMPORTS@", "import (\n\t\"github.com/varlink/go/varlink\"\n\t\"encoding/json\"\n)", 1)
} else {
ret_string = strings.Replace(ret_string, "@IMPORTS@", `import "github.com/varlink/go/varlink"`, 1)
}
pretty, err := format.Source([]byte(ret_string))
if err != nil {
return "", nil, err
}
return pkgname, pretty, nil
}
func generateFile(varlinkFile string) {
file, err := ioutil.ReadFile(varlinkFile)
if err != nil {
fmt.Fprintf(os.Stderr, "Error reading file '%s': %s\n", varlinkFile, err)
os.Exit(1)
}
pkgname, b, err := generateTemplate(string(file))
if err != nil {
fmt.Fprintf(os.Stderr, "Error parsing file '%s': %s\n", varlinkFile, err)
os.Exit(1)
}
filename := path.Dir(varlinkFile) + "/" + pkgname + ".go"
err = ioutil.WriteFile(filename, b, 0660)
if err != nil {
fmt.Fprintf(os.Stderr, "Error writing file '%s': %s\n", filename, err)
os.Exit(1)
}
}
func main() {
if len(os.Args) != 2 {
fmt.Printf("Usage: %s <file>\n", os.Args[0])
os.Exit(1)
}
generateFile(os.Args[1])
}

View File

@ -1,65 +0,0 @@
// +build !windows
package varlink
import (
"bufio"
"io"
"net"
"os"
"os/exec"
)
type PipeCon struct {
net.Conn
cmd *exec.Cmd
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
}
p.cmd.Wait()
return nil
}
// NewBridgeWithStderr returns a new connection with the given bridge.
func NewBridgeWithStderr(bridge string, stderr io.Writer) (*Connection, error) {
//var err error
c := Connection{}
cmd := exec.Command("sh", "-c", bridge)
cmd.Stderr = stderr
r, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}
w, err := cmd.StdinPipe()
if err != nil {
return nil, err
}
c.conn = PipeCon{nil, cmd, &r, &w}
c.address = ""
c.Reader = bufio.NewReader(r)
c.Writer = bufio.NewWriter(w)
err = cmd.Start()
if err != nil {
return nil, err
}
return &c, nil
}
// NewBridge returns a new connection with the given bridge.
func NewBridge(bridge string) (*Connection, error) {
return NewBridgeWithStderr(bridge, os.Stderr)
}

View File

@ -1,63 +0,0 @@
package varlink
import (
"bufio"
"io"
"net"
"os"
"os/exec"
)
type PipeCon struct {
net.Conn
cmd *exec.Cmd
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
}
p.cmd.Wait()
return nil
}
// NewBridgeWithStderr returns a new connection with the given bridge.
func NewBridgeWithStderr(bridge string, stderr io.Writer) (*Connection, error) {
//var err error
c := Connection{}
cmd := exec.Command("cmd", "/C", bridge)
cmd.Stderr = stderr
r, err := cmd.StdoutPipe()
if err != nil {
return nil, err
}
w, err := cmd.StdinPipe()
if err != nil {
return nil, err
}
c.conn = PipeCon{nil, cmd, &r, &w}
c.address = ""
c.Reader = bufio.NewReader(r)
c.Writer = bufio.NewWriter(w)
err = cmd.Start()
if err != nil {
return nil, err
}
return &c, nil
}
// NewBridge returns a new connection with the given bridge.
func NewBridge(bridge string) (*Connection, error) {
return NewBridgeWithStderr(bridge, os.Stderr)
}

View File

@ -1,104 +0,0 @@
package varlink
import (
"bufio"
"encoding/json"
"fmt"
"io"
"net"
"strings"
)
// Call is a method call retrieved by a Service. The connection from the
// client can be terminated by returning an error from the call instead
// of sending a reply or error reply.
type Call struct {
*bufio.Reader
*bufio.Writer
Conn *net.Conn
Request *[]byte
In *serviceCall
Continues bool
Upgrade bool
}
// WantsMore indicates if the calling client accepts more than one reply to this method call.
func (c *Call) WantsMore() bool {
return c.In.More
}
// WantsUpgrade indicates that the calling client wants the connection to be upgraded.
func (c *Call) WantsUpgrade() bool {
return c.In.Upgrade
}
// IsOneway indicate that the calling client does not expect a reply.
func (c *Call) IsOneway() bool {
return c.In.Oneway
}
// GetParameters retrieves the method call parameters.
func (c *Call) GetParameters(p interface{}) error {
if c.In.Parameters == nil {
return fmt.Errorf("empty parameters")
}
return json.Unmarshal(*c.In.Parameters, p)
}
func (c *Call) sendMessage(r *serviceReply) error {
if c.In.Oneway {
return nil
}
b, e := json.Marshal(r)
if e != nil {
return e
}
b = append(b, 0)
_, e = c.Writer.Write(b)
if e != nil {
if e == io.EOF {
return io.ErrUnexpectedEOF
}
return e
}
e = c.Writer.Flush()
if e == io.EOF {
return io.ErrUnexpectedEOF
}
return e
}
// Reply sends a reply to this method call.
func (c *Call) Reply(parameters interface{}) error {
if !c.Continues {
return c.sendMessage(&serviceReply{
Parameters: parameters,
})
}
if !c.In.More {
return fmt.Errorf("call did not set more, it does not expect continues")
}
return c.sendMessage(&serviceReply{
Continues: true,
Parameters: parameters,
})
}
// ReplyError sends an error reply to this method call.
func (c *Call) ReplyError(name string, parameters interface{}) error {
r := strings.LastIndex(name, ".")
if r <= 0 {
return fmt.Errorf("invalid error name")
}
if name[:r] == "org.varlink.service" {
return fmt.Errorf("refused to send org.varlink.service errors")
}
return c.sendMessage(&serviceReply{
Error: name,
Parameters: parameters,
})
}

View File

@ -1,291 +0,0 @@
package varlink
import (
"bufio"
"encoding/json"
"fmt"
"io"
"net"
"strings"
)
// Message flags for Send(). More indicates that the client accepts more than one method
// reply to this call. Oneway requests, that the service must not send a method reply to
// this call. Continues indicates that the service will send more than one reply.
const (
More = 1 << iota
Oneway = 1 << iota
Continues = 1 << iota
Upgrade = 1 << iota
)
// Error is a varlink error returned from a method call.
type Error struct {
Name string
Parameters interface{}
}
func (e *Error) DispatchError() error {
errorRawParameters := e.Parameters.(*json.RawMessage)
switch e.Name {
case "org.varlink.service.InterfaceNotFound":
var param InterfaceNotFound
if errorRawParameters != nil {
err := json.Unmarshal(*errorRawParameters, &param)
if err != nil {
return e
}
}
return &param
case "org.varlink.service.MethodNotFound":
var param MethodNotFound
if errorRawParameters != nil {
err := json.Unmarshal(*errorRawParameters, &param)
if err != nil {
return e
}
}
return &param
case "org.varlink.service.MethodNotImplemented":
var param MethodNotImplemented
if errorRawParameters != nil {
err := json.Unmarshal(*errorRawParameters, &param)
if err != nil {
return e
}
}
return &param
case "org.varlink.service.InvalidParameter":
var param InvalidParameter
if errorRawParameters != nil {
err := json.Unmarshal(*errorRawParameters, &param)
if err != nil {
return e
}
}
return &param
}
return e
}
// Error returns the fully-qualified varlink error name.
func (e *Error) Error() string {
return e.Name
}
// Connection is a connection from a client to a service.
type Connection struct {
io.Closer
address string
conn net.Conn
Reader *bufio.Reader
Writer *bufio.Writer
}
// Send sends a method call. It returns a receive() function which is called to retrieve the method reply.
// If Send() is called with the `More`flag and the receive() function carries the `Continues` flag, receive()
// can be called multiple times to retrieve multiple replies.
func (c *Connection) Send(method string, parameters interface{}, flags uint64) (func(interface{}) (uint64, error), error) {
type call struct {
Method string `json:"method"`
Parameters interface{} `json:"parameters,omitempty"`
More bool `json:"more,omitempty"`
Oneway bool `json:"oneway,omitempty"`
Upgrade bool `json:"upgrade,omitempty"`
}
if (flags&More != 0) && (flags&Oneway != 0) {
return nil, &Error{
Name: "org.varlink.InvalidParameter",
Parameters: "oneway",
}
}
if (flags&More != 0) && (flags&Upgrade != 0) {
return nil, &Error{
Name: "org.varlink.InvalidParameter",
Parameters: "more",
}
}
m := call{
Method: method,
Parameters: parameters,
More: flags&More != 0,
Oneway: flags&Oneway != 0,
Upgrade: flags&Upgrade != 0,
}
b, err := json.Marshal(m)
if err != nil {
return nil, err
}
b = append(b, 0)
_, err = c.Writer.Write(b)
if err != nil {
if err == io.EOF {
return nil, io.ErrUnexpectedEOF
}
return nil, err
}
err = c.Writer.Flush()
if err != nil {
if err == io.EOF {
return nil, io.ErrUnexpectedEOF
}
return nil, err
}
receive := func(out_parameters interface{}) (uint64, error) {
type reply struct {
Parameters *json.RawMessage `json:"parameters"`
Continues bool `json:"continues"`
Error string `json:"error"`
}
out, err := c.Reader.ReadBytes('\x00')
if err != nil {
if err == io.EOF {
return 0, io.ErrUnexpectedEOF
}
return 0, err
}
var m reply
err = json.Unmarshal(out[:len(out)-1], &m)
if err != nil {
return 0, err
}
if m.Error != "" {
e := &Error{
Name: m.Error,
Parameters: m.Parameters,
}
return 0, e.DispatchError()
}
if m.Parameters != nil {
json.Unmarshal(*m.Parameters, out_parameters)
}
if m.Continues {
return Continues, nil
}
return 0, nil
}
return receive, nil
}
// Call sends a method call and returns the method reply.
func (c *Connection) Call(method string, parameters interface{}, out_parameters interface{}) error {
receive, err := c.Send(method, &parameters, 0)
if err != nil {
return err
}
_, err = receive(out_parameters)
return err
}
// GetInterfaceDescription requests the interface description string from the service.
func (c *Connection) GetInterfaceDescription(name string) (string, error) {
type request struct {
Interface string `json:"interface"`
}
type reply struct {
Description string `json:"description"`
}
var r reply
err := c.Call("org.varlink.service.GetInterfaceDescription", request{Interface: name}, &r)
if err != nil {
return "", err
}
return r.Description, nil
}
// GetInfo requests information about the service.
func (c *Connection) GetInfo(vendor *string, product *string, version *string, url *string, interfaces *[]string) error {
type reply struct {
Vendor string `json:"vendor"`
Product string `json:"product"`
Version string `json:"version"`
URL string `json:"url"`
Interfaces []string `json:"interfaces"`
}
var r reply
err := c.Call("org.varlink.service.GetInfo", nil, &r)
if err != nil {
return err
}
if vendor != nil {
*vendor = r.Vendor
}
if product != nil {
*product = r.Product
}
if version != nil {
*version = r.Version
}
if url != nil {
*url = r.URL
}
if interfaces != nil {
*interfaces = r.Interfaces
}
return nil
}
// Close terminates the connection.
func (c *Connection) Close() error {
return c.conn.Close()
}
// NewConnection returns a new connection to the given address.
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]
// Ignore parameters after ';'
words = strings.SplitN(addr, ";", 2)
if words != nil {
addr = words[0]
}
switch protocol {
case "unix":
break
case "tcp":
break
}
c := Connection{}
c.conn, err = net.Dial(protocol, addr)
if err != nil {
return nil, err
}
c.address = address
c.Reader = bufio.NewReader(c.conn)
c.Writer = bufio.NewWriter(c.conn)
return &c, nil
}

View File

@ -1,63 +0,0 @@
/*
Package varlink provides varlink client and server implementations. See http://varlink.org
for more information about varlink.
Example varlink interface definition in a org.example.this.varlink file:
interface org.example.this
method Ping(in: string) -> (out: string)
Generated Go module in a orgexamplethis/orgexamplethis.go file. The generated module
provides reply methods for all methods specified in the varlink interface description.
The stub implementations return a MethodNotImplemented error; the service implementation
using this module will override the methods with its own implementation.
// Generated with github.com/varlink/go/cmd/varlink-go-interface-generator
package orgexamplethis
import "github.com/varlink/go/varlink"
type orgexamplethisInterface interface {
Ping(c VarlinkCall, in string) error
}
type VarlinkCall struct{ varlink.Call }
func (c *VarlinkCall) ReplyPing(out string) error {
var out struct {
Out string `json:"out,omitempty"`
}
out.Out = out
return c.Reply(&out)
}
func (s *VarlinkInterface) Ping(c VarlinkCall, in string) error {
return c.ReplyMethodNotImplemented("Ping")
}
[...]
Service implementing the interface and its method:
import ("orgexamplethis")
type Data struct {
orgexamplethis.VarlinkInterface
data string
}
data := Data{data: "test"}
func (d *Data) Ping(call orgexamplethis.VarlinkCall, ping string) error {
return call.ReplyPing(ping)
}
service, _ = varlink.NewService(
"Example",
"This",
"1",
"https://example.org/this",
)
service.RegisterInterface(orgexamplethis.VarlinkNew(&data))
err := service.Listen("unix:/run/org.example.this", 0)
*/
package varlink

View File

@ -1,497 +0,0 @@
// Package idl provides a varlink interface description parser.
package idl
import (
"bytes"
"fmt"
"regexp"
)
// Valid TypeKind values.
const (
TypeBool = iota
TypeInt
TypeFloat
TypeString
TypeObject
TypeArray
TypeMaybe
TypeMap
TypeStruct
TypeEnum
TypeAlias
)
// TypeKind specifies the type of an Type.
type TypeKind uint
// Type represents a varlink type. Types are method input and output parameters,
// error output parameters, or custom defined types in the interface description.
type Type struct {
Kind TypeKind
ElementType *Type
Alias string
Fields []TypeField
}
// TypeField is a named member of a TypeStruct.
type TypeField struct {
Name string
Type *Type
}
// Alias represents a named Type in the interface description.
type Alias struct {
Name string
Doc string
Type *Type
}
// Method represents a method defined in the interface description.
type Method struct {
Name string
Doc string
In *Type
Out *Type
}
// Error represents an error defined in the interface description.
type Error struct {
Name string
Doc string
Type *Type
}
// IDL represents a parsed varlink interface description with types, methods, errors and
// documentation.
type IDL struct {
Name string
Doc string
Description string
Members []interface{}
Aliases []*Alias
Methods []*Method
Errors []*Error
}
type parser struct {
input string
position int
lineStart int
lastComment bytes.Buffer
}
func (p *parser) next() int {
r := -1
if p.position < len(p.input) {
r = int(p.input[p.position])
}
p.position++
return r
}
func (p *parser) backup() {
p.position--
}
func (p *parser) advance() bool {
for {
char := p.next()
if char == '\n' {
p.lineStart = p.position
p.lastComment.Reset()
} else if char == ' ' || char == '\t' {
// ignore
} else if char == '#' {
p.next()
start := p.position
for {
c := p.next()
if c < 0 || c == '\n' {
p.backup()
break
}
}
if p.lastComment.Len() > 0 {
p.lastComment.WriteByte('\n')
}
p.lastComment.WriteString(p.input[start:p.position])
p.next()
} else {
p.backup()
break
}
}
return p.position < len(p.input)
}
func (p *parser) advanceOnLine() {
for {
char := p.next()
if char != ' ' {
p.backup()
return
}
}
}
func (p *parser) readKeyword() string {
start := p.position
for {
char := p.next()
if char < 'a' || char > 'z' {
p.backup()
break
}
}
return p.input[start:p.position]
}
func (p *parser) readInterfaceName() string {
start := p.position
dnrx := regexp.MustCompile(`^[a-z]+(\.[a-z0-9]+([-][a-z0-9]+)*)+`)
name := dnrx.FindString(p.input[start:])
if name != "" {
if len(name) > 255 {
return ""
}
p.position += len(name)
return name
}
xdnrx := regexp.MustCompile(`^xn--[a-z0-9]+(\.[a-z0-9]+([-][a-z0-9]+)*)+`)
name = xdnrx.FindString(p.input[start:])
if name != "" {
if len(name) > 255 {
return ""
}
p.position += len(name)
return name
}
return ""
}
func (p *parser) readFieldName() string {
start := p.position
char := p.next()
if char < 'a' || char > 'z' {
p.backup()
return ""
}
for {
char := p.next()
if (char < 'A' || char > 'Z') && (char < 'a' || char > 'z') && (char < '0' || char > '9') && char != '_' {
p.backup()
break
}
}
return p.input[start:p.position]
}
func (p *parser) readTypeName() string {
start := p.position
for {
char := p.next()
if (char < 'A' || char > 'Z') && (char < 'a' || char > 'z') && (char < '0' || char > '9') {
p.backup()
break
}
}
return p.input[start:p.position]
}
func (p *parser) readStructType() *Type {
if p.next() != '(' {
p.backup()
return nil
}
t := &Type{Kind: TypeStruct}
t.Fields = make([]TypeField, 0)
char := p.next()
if char != ')' {
p.backup()
for {
field := TypeField{}
p.advance()
field.Name = p.readFieldName()
if field.Name == "" {
return nil
}
p.advance()
// Enums have no types, they are just a list of names
if p.next() == ':' {
if t.Kind == TypeEnum {
return nil
}
p.advance()
field.Type = p.readType()
if field.Type == nil {
return nil
}
} else {
t.Kind = TypeEnum
p.backup()
}
t.Fields = append(t.Fields, field)
p.advance()
char = p.next()
if char != ',' {
break
}
}
if char != ')' {
return nil
}
}
return t
}
func (p *parser) readType() *Type {
var t *Type
switch p.next() {
case '?':
e := p.readType()
if e == nil {
return nil
}
if e.Kind == TypeMaybe {
return nil
}
t = &Type{Kind: TypeMaybe, ElementType: e}
case '[':
var kind TypeKind
switch p.readKeyword() {
case "string":
kind = TypeMap
case "":
kind = TypeArray
default:
return nil
}
if p.next() != ']' {
return nil
}
e := p.readType()
if e == nil {
return nil
}
t = &Type{Kind: kind, ElementType: e}
default:
p.backup()
if keyword := p.readKeyword(); keyword != "" {
switch keyword {
case "bool":
t = &Type{Kind: TypeBool}
case "int":
t = &Type{Kind: TypeInt}
case "float":
t = &Type{Kind: TypeFloat}
case "string":
t = &Type{Kind: TypeString}
case "object":
t = &Type{Kind: TypeObject}
}
} else if name := p.readTypeName(); name != "" {
t = &Type{Kind: TypeAlias, Alias: name}
} else if t = p.readStructType(); t == nil {
return nil
}
}
return t
}
func (p *parser) readAlias(idl *IDL) (*Alias, error) {
a := &Alias{}
p.advance()
a.Doc = p.lastComment.String()
a.Name = p.readTypeName()
if a.Name == "" {
return nil, fmt.Errorf("missing type name")
}
p.advance()
a.Type = p.readType()
if a.Type == nil {
return nil, fmt.Errorf("missing type declaration")
}
return a, nil
}
func (p *parser) readMethod(idl *IDL) (*Method, error) {
m := &Method{}
p.advance()
m.Doc = p.lastComment.String()
m.Name = p.readTypeName()
if m.Name == "" {
return nil, fmt.Errorf("missing method type")
}
p.advance()
m.In = p.readType()
if m.In == nil {
return nil, fmt.Errorf("missing method input")
}
p.advance()
one := p.next()
two := p.next()
if (one != '-') || two != '>' {
return nil, fmt.Errorf("missing method '->' operator")
}
p.advance()
m.Out = p.readType()
if m.Out == nil {
return nil, fmt.Errorf("missing method output")
}
return m, nil
}
func (p *parser) readError(idl *IDL) (*Error, error) {
e := &Error{}
p.advance()
e.Doc = p.lastComment.String()
e.Name = p.readTypeName()
if e.Name == "" {
return nil, fmt.Errorf("missing error name")
}
p.advanceOnLine()
e.Type = p.readType()
return e, nil
}
func (p *parser) readIDL() (*IDL, error) {
if keyword := p.readKeyword(); keyword != "interface" {
return nil, fmt.Errorf("missing interface keyword")
}
idl := &IDL{
Members: make([]interface{}, 0),
Aliases: make([]*Alias, 0),
Methods: make([]*Method, 0),
Errors: make([]*Error, 0),
}
p.advance()
idl.Doc = p.lastComment.String()
idl.Name = p.readInterfaceName()
if idl.Name == "" {
return nil, fmt.Errorf("interface name")
}
// Check for duplicates
members := make(map[string]struct{}, 0)
for {
if !p.advance() {
break
}
switch keyword := p.readKeyword(); keyword {
case "type":
a, err := p.readAlias(idl)
if err != nil {
return nil, err
}
if _, ok := members[a.Name]; ok {
return nil, fmt.Errorf("type `%s` already defined", a.Name)
}
members[a.Name] = struct{}{}
idl.Aliases = append(idl.Aliases, a)
idl.Members = append(idl.Members, a)
case "method":
m, err := p.readMethod(idl)
if err != nil {
return nil, err
}
if _, ok := members[m.Name]; ok {
return nil, fmt.Errorf("method `%s` already defined", m.Name)
}
members[m.Name] = struct{}{}
idl.Methods = append(idl.Methods, m)
idl.Members = append(idl.Members, m)
case "error":
e, err := p.readError(idl)
if err != nil {
return nil, err
}
if _, ok := members[e.Name]; ok {
return nil, fmt.Errorf("error `%s` already defined", e.Name)
}
members[e.Name] = struct{}{}
idl.Errors = append(idl.Errors, e)
idl.Members = append(idl.Members, e)
default:
return nil, fmt.Errorf("unknown keyword '%s'", keyword)
}
}
return idl, nil
}
// New parses a varlink interface description.
func New(description string) (*IDL, error) {
p := &parser{input: description}
p.advance()
idl, err := p.readIDL()
if err != nil {
return nil, err
}
if len(idl.Methods) == 0 {
return nil, fmt.Errorf("no methods defined")
}
idl.Description = description
return idl, nil
}

View File

@ -1,162 +0,0 @@
package varlink
// The requested interface was not found.
type InterfaceNotFound struct {
Interface string `json:"interface"`
}
func (e InterfaceNotFound) Error() string {
return "org.varlink.service.InterfaceNotFound"
}
// The requested method was not found
type MethodNotFound struct {
Method string `json:"method"`
}
func (e MethodNotFound) Error() string {
return "org.varlink.service.MethodNotFound"
}
// The interface defines the requested method, but the service does not
// implement it.
type MethodNotImplemented struct {
Method string `json:"method"`
}
func (e MethodNotImplemented) Error() string {
return "org.varlink.service.MethodNotImplemented"
}
// One of the passed parameters is invalid.
type InvalidParameter struct {
Parameter string `json:"parameter"`
}
func (e InvalidParameter) Error() string {
return "org.varlink.service.InvalidParameter"
}
func doReplyError(c *Call, name string, parameters interface{}) error {
return c.sendMessage(&serviceReply{
Error: name,
Parameters: parameters,
})
}
// ReplyInterfaceNotFound sends a org.varlink.service errror reply to this method call
func (c *Call) ReplyInterfaceNotFound(interfaceA string) error {
var out InterfaceNotFound
out.Interface = interfaceA
return doReplyError(c, "org.varlink.service.InterfaceNotFound", &out)
}
// ReplyMethodNotFound sends a org.varlink.service errror reply to this method call
func (c *Call) ReplyMethodNotFound(method string) error {
var out MethodNotFound
out.Method = method
return doReplyError(c, "org.varlink.service.MethodNotFound", &out)
}
// ReplyMethodNotImplemented sends a org.varlink.service errror reply to this method call
func (c *Call) ReplyMethodNotImplemented(method string) error {
var out MethodNotImplemented
out.Method = method
return doReplyError(c, "org.varlink.service.MethodNotImplemented", &out)
}
// ReplyInvalidParameter sends a org.varlink.service errror reply to this method call
func (c *Call) ReplyInvalidParameter(parameter string) error {
var out InvalidParameter
out.Parameter = parameter
return doReplyError(c, "org.varlink.service.InvalidParameter", &out)
}
func (c *Call) replyGetInfo(vendor string, product string, version string, url string, interfaces []string) error {
var out struct {
Vendor string `json:"vendor,omitempty"`
Product string `json:"product,omitempty"`
Version string `json:"version,omitempty"`
URL string `json:"url,omitempty"`
Interfaces []string `json:"interfaces,omitempty"`
}
out.Vendor = vendor
out.Product = product
out.Version = version
out.URL = url
out.Interfaces = interfaces
return c.Reply(&out)
}
func (c *Call) replyGetInterfaceDescription(description string) error {
var out struct {
Description string `json:"description,omitempty"`
}
out.Description = description
return c.Reply(&out)
}
func (s *Service) orgvarlinkserviceDispatch(c Call, methodname string) error {
switch methodname {
case "GetInfo":
return s.getInfo(c)
case "GetInterfaceDescription":
var in struct {
Interface string `json:"interface"`
}
err := c.GetParameters(&in)
if err != nil {
return c.ReplyInvalidParameter("parameters")
}
return s.getInterfaceDescription(c, in.Interface)
default:
return c.ReplyMethodNotFound(methodname)
}
}
func (s *orgvarlinkserviceInterface) VarlinkDispatch(call Call, methodname string) error {
return nil
}
func (s *orgvarlinkserviceInterface) VarlinkGetName() string {
return `org.varlink.service`
}
func (s *orgvarlinkserviceInterface) VarlinkGetDescription() string {
return `# The Varlink Service Interface is provided by every varlink service. It
# describes the service and the interfaces it implements.
interface org.varlink.service
# Get a list of all the interfaces a service provides and information
# about the implementation.
method GetInfo() -> (
vendor: string,
product: string,
version: string,
url: string,
interfaces: []string
)
# Get the description of an interface that is implemented by this service.
method GetInterfaceDescription(interface: string) -> (description: string)
# The requested interface was not found.
error InterfaceNotFound (interface: string)
# The requested method was not found
error MethodNotFound (method: string)
# The interface defines the requested method, but the service does not
# implement it.
error MethodNotImplemented (method: string)
# One of the passed parameters is invalid.
error InvalidParameter (parameter: string)`
}
type orgvarlinkserviceInterface struct{}
func orgvarlinkserviceNew() *orgvarlinkserviceInterface {
return &orgvarlinkserviceInterface{}
}

View File

@ -1,92 +0,0 @@
package varlink
// ResolverAddress is the well-known address of the varlink interface resolver,
// it translates varlink interface names to varlink service addresses.
const ResolverAddress = "unix:/run/org.varlink.resolver"
// Resolver resolves varlink interface names to varlink addresses
type Resolver struct {
address string
conn *Connection
}
// Resolve resolves a varlink interface name to a varlink address.
func (r *Resolver) Resolve(iface string) (string, error) {
type request struct {
Interface string `json:"interface"`
}
type reply struct {
Address string `json:"address"`
}
/* don't ask the resolver for itself */
if iface == "org.varlink.resolver" {
return r.address, nil
}
var rep reply
err := r.conn.Call("org.varlink.resolver.Resolve", &request{Interface: iface}, &rep)
if err != nil {
return "", err
}
return rep.Address, nil
}
// GetInfo requests information about the resolver.
func (r *Resolver) GetInfo(vendor *string, product *string, version *string, url *string, interfaces *[]string) error {
type reply struct {
Vendor string
Product string
Version string
URL string
Interfaces []string
}
var rep reply
err := r.conn.Call("org.varlink.resolver.GetInfo", nil, &rep)
if err != nil {
return err
}
if vendor != nil {
*vendor = rep.Vendor
}
if product != nil {
*product = rep.Product
}
if version != nil {
*version = rep.Version
}
if url != nil {
*url = rep.URL
}
if interfaces != nil {
*interfaces = rep.Interfaces
}
return nil
}
// Close terminates the resolver.
func (r *Resolver) Close() error {
return r.conn.Close()
}
// NewResolver returns a new resolver connected to the given address.
func NewResolver(address string) (*Resolver, error) {
if address == "" {
address = ResolverAddress
}
c, err := NewConnection(address)
if err != nil {
return nil, err
}
r := Resolver{
address: address,
conn: c,
}
return &r, nil
}

View File

@ -1,375 +0,0 @@
package varlink
import (
"bufio"
"encoding/json"
"fmt"
"net"
"os"
"strings"
"sync"
"time"
)
type dispatcher interface {
VarlinkDispatch(c Call, methodname string) error
VarlinkGetName() string
VarlinkGetDescription() string
}
type serviceCall struct {
Method string `json:"method"`
Parameters *json.RawMessage `json:"parameters,omitempty"`
More bool `json:"more,omitempty"`
Oneway bool `json:"oneway,omitempty"`
Upgrade bool `json:"upgrade,omitempty"`
}
type serviceReply struct {
Parameters interface{} `json:"parameters,omitempty"`
Continues bool `json:"continues,omitempty"`
Error string `json:"error,omitempty"`
}
// Service represents an active varlink service. In addition to the registered custom varlink Interfaces, every service
// implements the org.varlink.service interface which allows clients to retrieve information about the
// running service.
type Service struct {
vendor string
product string
version string
url string
interfaces map[string]dispatcher
names []string
descriptions map[string]string
running bool
listener net.Listener
conncounter int64
mutex sync.Mutex
protocol string
address string
}
// ServiceTimoutError helps API users to special-case timeouts.
type ServiceTimeoutError struct{}
func (ServiceTimeoutError) Error() string {
return "service timeout"
}
func (s *Service) getInfo(c Call) error {
return c.replyGetInfo(s.vendor, s.product, s.version, s.url, s.names)
}
func (s *Service) getInterfaceDescription(c Call, name string) error {
if name == "" {
return c.ReplyInvalidParameter("interface")
}
description, ok := s.descriptions[name]
if !ok {
return c.ReplyInvalidParameter("interface")
}
return c.replyGetInterfaceDescription(description)
}
func (s *Service) HandleMessage(conn *net.Conn, reader *bufio.Reader, writer *bufio.Writer, request []byte) error {
var in serviceCall
err := json.Unmarshal(request, &in)
if err != nil {
return err
}
c := Call{
Conn: conn,
Reader: reader,
Writer: writer,
In: &in,
Request: &request,
}
r := strings.LastIndex(in.Method, ".")
if r <= 0 {
return c.ReplyInvalidParameter("method")
}
interfacename := in.Method[:r]
methodname := in.Method[r+1:]
if interfacename == "org.varlink.service" {
return s.orgvarlinkserviceDispatch(c, methodname)
}
// Find the interface and method in our service
iface, ok := s.interfaces[interfacename]
if !ok {
return c.ReplyInterfaceNotFound(interfacename)
}
return iface.VarlinkDispatch(c, methodname)
}
// Shutdown shuts down the listener of a running service.
func (s *Service) Shutdown() {
s.running = false
s.mutex.Lock()
if s.listener != nil {
s.listener.Close()
}
s.mutex.Unlock()
}
func (s *Service) handleConnection(conn net.Conn, wg *sync.WaitGroup) {
defer func() { s.mutex.Lock(); s.conncounter--; s.mutex.Unlock(); wg.Done() }()
reader := bufio.NewReader(conn)
writer := bufio.NewWriter(conn)
for {
request, err := reader.ReadBytes('\x00')
if err != nil {
break
}
err = s.HandleMessage(&conn, reader, writer, request[:len(request)-1])
if err != nil {
// FIXME: report error
//fmt.Fprintf(os.Stderr, "handleMessage: %v", err)
break
}
}
conn.Close()
}
func (s *Service) teardown() {
s.mutex.Lock()
s.listener = nil
s.running = false
s.protocol = ""
s.address = ""
s.mutex.Unlock()
}
func (s *Service) parseAddress(address string) error {
words := strings.SplitN(address, ":", 2)
if len(words) != 2 {
return fmt.Errorf("Unknown protocol")
}
s.protocol = words[0]
s.address = words[1]
// Ignore parameters after ';'
words = strings.SplitN(s.address, ";", 2)
if words != nil {
s.address = words[0]
}
switch s.protocol {
case "unix":
break
case "tcp":
break
default:
return fmt.Errorf("Unknown protocol")
}
return nil
}
func (s *Service) GetListener() (*net.Listener, error) {
s.mutex.Lock()
l := s.listener
s.mutex.Unlock()
return &l, nil
}
func (s *Service) setListener() error {
l := activationListener()
if l == nil {
if s.protocol == "unix" && s.address[0] != '@' {
os.Remove(s.address)
}
var err error
l, err = net.Listen(s.protocol, s.address)
if err != nil {
return err
}
if s.protocol == "unix" && s.address[0] != '@' {
l.(*net.UnixListener).SetUnlinkOnClose(true)
}
}
s.mutex.Lock()
s.listener = l
s.mutex.Unlock()
return nil
}
func (s *Service) refreshTimeout(timeout time.Duration) error {
switch l := s.listener.(type) {
case *net.UnixListener:
if err := l.SetDeadline(time.Now().Add(timeout)); err != nil {
return err
}
case *net.TCPListener:
if err := l.SetDeadline(time.Now().Add(timeout)); err != nil {
return err
}
}
return nil
}
// Listen starts a Service.
func (s *Service) Bind(address string) error {
s.mutex.Lock()
if s.running {
s.mutex.Unlock()
return fmt.Errorf("Init(): already running")
}
s.mutex.Unlock()
s.parseAddress(address)
err := s.setListener()
if err != nil {
return err
}
return nil
}
// Listen starts a Service.
func (s *Service) Listen(address string, timeout time.Duration) error {
var wg sync.WaitGroup
defer func() { s.teardown(); wg.Wait() }()
err := s.Bind(address)
if err != nil {
return err
}
s.mutex.Lock()
s.running = true
l := s.listener
s.mutex.Unlock()
for s.running {
if timeout != 0 {
if err := s.refreshTimeout(timeout); err != nil {
return err
}
}
conn, err := l.Accept()
if err != nil {
if err.(net.Error).Timeout() {
s.mutex.Lock()
if s.conncounter == 0 {
s.mutex.Unlock()
return ServiceTimeoutError{}
}
s.mutex.Unlock()
continue
}
if !s.running {
return nil
}
return err
}
s.mutex.Lock()
s.conncounter++
s.mutex.Unlock()
wg.Add(1)
go s.handleConnection(conn, &wg)
}
return nil
}
// Listen starts a Service.
func (s *Service) DoListen(timeout time.Duration) error {
var wg sync.WaitGroup
defer func() { s.teardown(); wg.Wait() }()
s.mutex.Lock()
l := s.listener
s.mutex.Unlock()
if l == nil {
return fmt.Errorf("No listener set")
}
s.mutex.Lock()
s.running = true
s.mutex.Unlock()
for s.running {
if timeout != 0 {
if err := s.refreshTimeout(timeout); err != nil {
return err
}
}
conn, err := l.Accept()
if err != nil {
if err.(net.Error).Timeout() {
s.mutex.Lock()
if s.conncounter == 0 {
s.mutex.Unlock()
return ServiceTimeoutError{}
}
s.mutex.Unlock()
continue
}
if !s.running {
return nil
}
return err
}
s.mutex.Lock()
s.conncounter++
s.mutex.Unlock()
wg.Add(1)
go s.handleConnection(conn, &wg)
}
return nil
}
// RegisterInterface registers a varlink.Interface containing struct to the Service
func (s *Service) RegisterInterface(iface dispatcher) error {
name := iface.VarlinkGetName()
if _, ok := s.interfaces[name]; ok {
return fmt.Errorf("interface '%s' already registered", name)
}
if s.running {
return fmt.Errorf("service is already running")
}
s.interfaces[name] = iface
s.descriptions[name] = iface.VarlinkGetDescription()
s.names = append(s.names, name)
return nil
}
// NewService creates a new Service which implements the list of given varlink interfaces.
func NewService(vendor string, product string, version string, url string) (*Service, error) {
s := Service{
vendor: vendor,
product: product,
version: version,
url: url,
interfaces: make(map[string]dispatcher),
descriptions: make(map[string]string),
}
err := s.RegisterInterface(orgvarlinkserviceNew())
return &s, err
}

View File

@ -1,63 +0,0 @@
// +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
}

View File

@ -1,7 +0,0 @@
package varlink
import "net"
func activationListener() net.Listener {
return nil
}

Some files were not shown because too many files have changed in this diff Show More