Compare commits
102 Commits
master
...
release-1.
Author | SHA1 | Date |
---|---|---|
Mrunal Patel | 01826ef50b | |
Antonio Murdaca | a5dfa11498 | |
Antonio Murdaca | 7a3e37e253 | |
Mrunal Patel | eb8df95183 | |
Antonio Murdaca | 762cb4cca5 | |
Antonio Murdaca | cc0f78dfc4 | |
Antonio Murdaca | e4470612a2 | |
Antonio Murdaca | 3a36f553a4 | |
Daniel J Walsh | c30571b431 | |
Antonio Murdaca | 327c673182 | |
Antonio Murdaca | f19b368f70 | |
Antonio Murdaca | dd571523ec | |
Mrunal Patel | 3078f59068 | |
Antonio Murdaca | bfc061b1c4 | |
Antonio Murdaca | 665895770a | |
Antonio Murdaca | 56760df738 | |
Mrunal Patel | 4fa5505a7c | |
Antonio Murdaca | d45e90673f | |
Antonio Murdaca | 4aceedee21 | |
Antonio Murdaca | 8bdb7b912d | |
Antonio Murdaca | bc4319c7a8 | |
Antonio Murdaca | ed34ff3255 | |
Mrunal Patel | ce319adcfe | |
Antonio Murdaca | 12cb424833 | |
Antonio Murdaca | 5488bfeb9e | |
Daniel J Walsh | 8a39d94a0d | |
Antonio Murdaca | dc8d1e9383 | |
Antonio Murdaca | 17bcfb495c | |
Antonio Murdaca | b63b96722c | |
Mrunal Patel | 66586603b9 | |
Mrunal Patel | b0da62fcb0 | |
Daniel J Walsh | 00eca36d8e | |
Antonio Murdaca | 762827be57 | |
Antonio Murdaca | f4afea6480 | |
Mrunal Patel | 06b4946e68 | |
Mrunal Patel | 894c98bde8 | |
Mrunal Patel | 7e1e21f1f4 | |
Mrunal Patel | f6c2df62c9 | |
Mrunal Patel | 0eebfa6671 | |
Antonio Murdaca | 430ff2b01d | |
Daniel J Walsh | 222216256f | |
Daniel J Walsh | d54ad94656 | |
Mrunal Patel | db9d8a691b | |
Antonio Murdaca | 2f95aaf84c | |
Ed Santiago | 1ac7da151b | |
Antonio Murdaca | f8e583fca1 | |
Ed Santiago | e78c44fe25 | |
Ed Santiago | 6dcf27f8cb | |
Mrunal Patel | 532ed47d03 | |
Mrunal Patel | 748bc46605 | |
Daniel J Walsh | 29186a6b2c | |
Antonio Murdaca | e4f8dff230 | |
Daniel J Walsh | c31d1d15f7 | |
Daniel J Walsh | ae6e74731c | |
Daniel J Walsh | fc2457a3f0 | |
Daniel J Walsh | 266e1c10ec | |
Antonio Murdaca | 6a912eb2d3 | |
Antonio Murdaca | 085bdf8ff5 | |
Antonio Murdaca | 1544488a10 | |
Lokesh Mandvekar | 73b6543864 | |
Mrunal Patel | ffe0d51a3c | |
Antonio Murdaca | 98ca0803d7 | |
Antonio Murdaca | 387c1c6bff | |
Mrunal Patel | c1ccd5f295 | |
Mrunal Patel | 64a30e1654 | |
Daniel J Walsh | 155e83d75b | |
Daniel J Walsh | d8aaba71b7 | |
Daniel J Walsh | d2ea9cc2fa | |
Antonio Murdaca | 1461072521 | |
Antonio Murdaca | 0385463de0 | |
Mrunal Patel | 3a504024d5 | |
Antonio Murdaca | a45c16d7fa | |
Mrunal Patel | a90213930b | |
Mrunal Patel | 17db40dcda | |
Antonio Murdaca | a32c3d4b9a | |
Mrunal Patel | a2ab0a5eb0 | |
Antonio Murdaca | ed89aa630e | |
Daniel J Walsh | 76508edc10 | |
Mrunal Patel | 5f826acfaf | |
Antonio Murdaca | 9b797f0cb9 | |
Mrunal Patel | bb737b9121 | |
Mrunal Patel | 70f6306a51 | |
umohnani8 | c0f6f4fb48 | |
Antonio Murdaca | 7efdae80bc | |
Mrunal Patel | a636972c3e | |
Mrunal Patel | 5b62041194 | |
Mrunal Patel | 38c2a34b46 | |
Antonio Murdaca | ab2a4839d7 | |
Daniel J Walsh | c4f7506896 | |
Samuel Ortiz | 29121c8c0c | |
Nalin Dahyabhai | ddb8fb30cc | |
Nalin Dahyabhai | a88f6840d8 | |
Mrunal Patel | 436194290a | |
umohnani8 | d1aea31786 | |
Daniel J Walsh | 5b41729b6c | |
Daniel J Walsh | a8224f8be1 | |
umohnani8 | d5b5028cb9 | |
Mrunal Patel | bb4b2e9fea | |
Daniel J Walsh | d7cbdfce76 | |
baude | 3907e0d346 | |
Daniel J Walsh | 3363064622 | |
Mrunal Patel | 7c2c9a8c85 |
|
@ -0,0 +1,7 @@
|
||||||
|
# GitHub code owners
|
||||||
|
# See https://help.github.com/articles/about-codeowners/
|
||||||
|
#
|
||||||
|
# KEEP THIS FILE SORTED. Order is important. Last match takes precedence.
|
||||||
|
|
||||||
|
* @mrunalp @runcom
|
||||||
|
pkg/storage/** @nalind @runcom @rhatdan
|
|
@ -0,0 +1,58 @@
|
||||||
|
<!--
|
||||||
|
If you are reporting a new issue, make sure that we do not have any duplicates
|
||||||
|
already open. You can ensure this by searching the issue list for this
|
||||||
|
repository. If there is a duplicate, please close your issue and add a comment
|
||||||
|
to the existing issue instead.
|
||||||
|
|
||||||
|
If you suspect your issue is a bug, please edit your issue description to
|
||||||
|
include the BUG REPORT INFORMATION shown below. If you fail to provide this
|
||||||
|
information within 7 days, we cannot debug your issue and will close it. We
|
||||||
|
will, however, reopen it if you later provide the information.
|
||||||
|
|
||||||
|
For more information about reporting issues, see
|
||||||
|
https://github.com/kubernetes-incubator/cri-o/blob/master/CONTRIBUTING.md#reporting-issues
|
||||||
|
|
||||||
|
---------------------------------------------------
|
||||||
|
GENERAL SUPPORT INFORMATION
|
||||||
|
---------------------------------------------------
|
||||||
|
|
||||||
|
The GitHub issue tracker is for bug reports and feature requests.
|
||||||
|
General support for **CRI-O** can be found at the following locations:
|
||||||
|
|
||||||
|
- IRC - #cri-o channel on irc.freenode.org
|
||||||
|
- Slack - kubernetes.slack.com #sig-node channel
|
||||||
|
- Post a question on StackOverflow, using the CRI-O tag
|
||||||
|
|
||||||
|
---------------------------------------------------
|
||||||
|
BUG REPORT INFORMATION
|
||||||
|
---------------------------------------------------
|
||||||
|
Use the commands below to provide key information from your environment:
|
||||||
|
You do NOT have to include this information if this is a FEATURE REQUEST
|
||||||
|
-->
|
||||||
|
|
||||||
|
**Description**
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Briefly describe the problem you are having in a few paragraphs.
|
||||||
|
-->
|
||||||
|
|
||||||
|
**Steps to reproduce the issue:**
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
|
||||||
|
**Describe the results you received:**
|
||||||
|
|
||||||
|
|
||||||
|
**Describe the results you expected:**
|
||||||
|
|
||||||
|
|
||||||
|
**Additional information you deem important (e.g. issue happens only occasionally):**
|
||||||
|
|
||||||
|
**Output of `crio --version`:**
|
||||||
|
|
||||||
|
```
|
||||||
|
(paste your output here)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Additional environment details (AWS, VirtualBox, physical, etc.):**
|
|
@ -0,0 +1,23 @@
|
||||||
|
<!--
|
||||||
|
Please make sure you've read and understood our contributing guidelines;
|
||||||
|
https://github.com/kubernetes-incubator/cri-o/blob/master/CONTRIBUTING.md
|
||||||
|
|
||||||
|
** Make sure all your commits include a signature generated with `git commit -s` **
|
||||||
|
|
||||||
|
If this is a bug fix, make sure your description includes "fixes #xxxx", or
|
||||||
|
"closes #xxxx"
|
||||||
|
|
||||||
|
Please provide the following information:
|
||||||
|
-->
|
||||||
|
|
||||||
|
**- What I did**
|
||||||
|
|
||||||
|
**- How I did it**
|
||||||
|
|
||||||
|
**- How to verify it**
|
||||||
|
|
||||||
|
**- Description for the changelog**
|
||||||
|
<!--
|
||||||
|
Write a short (one line) summary that describes the changes in this
|
||||||
|
pull request for inclusion in the changelog:
|
||||||
|
-->
|
|
@ -1,17 +1,13 @@
|
||||||
/.artifacts/
|
/.artifacts/
|
||||||
/_output/
|
/_output/
|
||||||
/conmon/conmon
|
|
||||||
/conmon/conmon.o
|
/conmon/conmon.o
|
||||||
/docs/*.[158]
|
/docs/*.[158]
|
||||||
/docs/*.[158].gz
|
/docs/*.[158].gz
|
||||||
/kpod
|
|
||||||
/crioctl
|
|
||||||
/crio
|
|
||||||
/crio.conf
|
/crio.conf
|
||||||
*.o
|
*.o
|
||||||
*.orig
|
*.orig
|
||||||
/pause/pause
|
|
||||||
/pause/pause.o
|
/pause/pause.o
|
||||||
|
/bin/
|
||||||
/test/bin2img/bin2img
|
/test/bin2img/bin2img
|
||||||
/test/checkseccomp/checkseccomp
|
/test/checkseccomp/checkseccomp
|
||||||
/test/copyimg/copyimg
|
/test/copyimg/copyimg
|
||||||
|
|
|
@ -47,8 +47,6 @@ jobs:
|
||||||
go: 1.9.x
|
go: 1.9.x
|
||||||
- script:
|
- script:
|
||||||
- make .gitvalidation
|
- make .gitvalidation
|
||||||
- make gofmt
|
|
||||||
- make lint
|
|
||||||
- make testunit
|
- make testunit
|
||||||
- make docs
|
- make docs
|
||||||
- make
|
- make
|
||||||
|
|
|
@ -97,7 +97,7 @@ RUN set -x \
|
||||||
&& rm -rf "$GOPATH"
|
&& rm -rf "$GOPATH"
|
||||||
|
|
||||||
# Install crictl
|
# Install crictl
|
||||||
ENV CRICTL_COMMIT 16e6fe4d7199c5689db4630a9330e6a8a12cecd1
|
ENV CRICTL_COMMIT b42fc3f364dd48f649d55926c34492beeb9b2e99
|
||||||
RUN set -x \
|
RUN set -x \
|
||||||
&& export GOPATH="$(mktemp -d)" \
|
&& export GOPATH="$(mktemp -d)" \
|
||||||
&& git clone https://github.com/kubernetes-incubator/cri-tools.git "$GOPATH/src/github.com/kubernetes-incubator/cri-tools" \
|
&& git clone https://github.com/kubernetes-incubator/cri-tools.git "$GOPATH/src/github.com/kubernetes-incubator/cri-tools" \
|
||||||
|
|
46
Makefile
46
Makefile
|
@ -11,7 +11,9 @@ LIBEXECDIR ?= ${PREFIX}/libexec
|
||||||
MANDIR ?= ${PREFIX}/share/man
|
MANDIR ?= ${PREFIX}/share/man
|
||||||
ETCDIR ?= ${DESTDIR}/etc
|
ETCDIR ?= ${DESTDIR}/etc
|
||||||
ETCDIR_CRIO ?= ${ETCDIR}/crio
|
ETCDIR_CRIO ?= ${ETCDIR}/crio
|
||||||
BUILDTAGS ?= selinux seccomp $(shell hack/btrfs_tag.sh) $(shell hack/libdm_tag.sh) $(shell hack/btrfs_installed_tag.sh)
|
BUILDTAGS ?= seccomp $(shell hack/btrfs_tag.sh) $(shell hack/libdm_tag.sh) $(shell hack/btrfs_installed_tag.sh) $(shell hack/ostree_tag.sh) $(shell hack/selinux_tag.sh)
|
||||||
|
CRICTL_CONFIG_DIR=${DESTDIR}/etc
|
||||||
|
|
||||||
BASHINSTALLDIR=${PREFIX}/share/bash-completion/completions
|
BASHINSTALLDIR=${PREFIX}/share/bash-completion/completions
|
||||||
OCIUMOUNTINSTALLDIR=$(PREFIX)/share/oci-umount/oci-umount.d
|
OCIUMOUNTINSTALLDIR=$(PREFIX)/share/oci-umount/oci-umount.d
|
||||||
|
|
||||||
|
@ -22,7 +24,6 @@ COMMIT_NO := $(shell git rev-parse HEAD 2> /dev/null || true)
|
||||||
GIT_COMMIT := $(if $(shell git status --porcelain --untracked-files=no),"${COMMIT_NO}-dirty","${COMMIT_NO}")
|
GIT_COMMIT := $(if $(shell git status --porcelain --untracked-files=no),"${COMMIT_NO}-dirty","${COMMIT_NO}")
|
||||||
BUILD_INFO := $(shell date +%s)
|
BUILD_INFO := $(shell date +%s)
|
||||||
|
|
||||||
VERSION := ${shell cat ./VERSION}
|
|
||||||
KPOD_VERSION := ${shell cat ./KPOD_VERSION}
|
KPOD_VERSION := ${shell cat ./KPOD_VERSION}
|
||||||
|
|
||||||
# If GOPATH not specified, use one in the local directory
|
# If GOPATH not specified, use one in the local directory
|
||||||
|
@ -35,8 +36,11 @@ GOPKGBASEDIR := $(shell dirname "$(GOPKGDIR)")
|
||||||
|
|
||||||
# Update VPATH so make finds .gopathok
|
# Update VPATH so make finds .gopathok
|
||||||
VPATH := $(VPATH):$(GOPATH)
|
VPATH := $(VPATH):$(GOPATH)
|
||||||
|
SHRINKFLAGS := -s -w
|
||||||
LDFLAGS := -ldflags '-X main.gitCommit=${GIT_COMMIT} -X main.buildInfo=${BUILD_INFO} -X main.version=${VERSION} -X main.kpodVersion=${KPOD_VERSION}'
|
BASE_LDFLAGS := ${SHRINKFLAGS} -X main.gitCommit=${GIT_COMMIT} -X main.buildInfo=${BUILD_INFO}
|
||||||
|
KPOD_LDFLAGS := -X main.kpodVersion=${KPOD_VERSION}
|
||||||
|
LDFLAGS := -ldflags '${BASE_LDFLAGS}'
|
||||||
|
LDFLAGS_KPOD := -ldflags '${BASE_LDFLAGS} ${KPOD_LDFLAGS}'
|
||||||
|
|
||||||
all: binaries crio.conf docs
|
all: binaries crio.conf docs
|
||||||
|
|
||||||
|
@ -46,7 +50,7 @@ help:
|
||||||
@echo "Usage: make <target>"
|
@echo "Usage: make <target>"
|
||||||
@echo
|
@echo
|
||||||
@echo " * 'install' - Install binaries to system locations"
|
@echo " * 'install' - Install binaries to system locations"
|
||||||
@echo " * 'binaries' - Build crio, conmon and crioctl"
|
@echo " * 'binaries' - Build crio, conmon, pause, crioctl and kpod"
|
||||||
@echo " * 'integration' - Execute integration tests"
|
@echo " * 'integration' - Execute integration tests"
|
||||||
@echo " * 'clean' - Clean artifacts"
|
@echo " * 'clean' - Clean artifacts"
|
||||||
@echo " * 'lint' - Execute the source code linter"
|
@echo " * 'lint' - Execute the source code linter"
|
||||||
|
@ -73,25 +77,25 @@ pause:
|
||||||
$(MAKE) -C $@
|
$(MAKE) -C $@
|
||||||
|
|
||||||
test/bin2img/bin2img: .gopathok $(wildcard test/bin2img/*.go)
|
test/bin2img/bin2img: .gopathok $(wildcard test/bin2img/*.go)
|
||||||
$(GO) build $(LDFLAGS) -tags "$(BUILDTAGS)" -o $@ $(PROJECT)/test/bin2img
|
$(GO) build $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/test/bin2img
|
||||||
|
|
||||||
test/copyimg/copyimg: .gopathok $(wildcard test/copyimg/*.go)
|
test/copyimg/copyimg: .gopathok $(wildcard test/copyimg/*.go)
|
||||||
$(GO) build $(LDFLAGS) -tags "$(BUILDTAGS)" -o $@ $(PROJECT)/test/copyimg
|
$(GO) build $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/test/copyimg
|
||||||
|
|
||||||
test/checkseccomp/checkseccomp: .gopathok $(wildcard test/checkseccomp/*.go)
|
test/checkseccomp/checkseccomp: .gopathok $(wildcard test/checkseccomp/*.go)
|
||||||
$(GO) build $(LDFLAGS) -tags "$(BUILDTAGS)" -o $@ $(PROJECT)/test/checkseccomp
|
$(GO) build $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/test/checkseccomp
|
||||||
|
|
||||||
crio: .gopathok $(shell hack/find-godeps.sh $(GOPKGDIR) cmd/crio $(PROJECT))
|
crio: .gopathok $(shell hack/find-godeps.sh $(GOPKGDIR) cmd/crio $(PROJECT))
|
||||||
$(GO) build $(LDFLAGS) -tags "$(BUILDTAGS)" -o $@ $(PROJECT)/cmd/crio
|
$(GO) build $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o bin/$@ $(PROJECT)/cmd/crio
|
||||||
|
|
||||||
crioctl: .gopathok $(shell hack/find-godeps.sh $(GOPKGDIR) cmd/crioctl $(PROJECT))
|
crioctl: .gopathok $(shell hack/find-godeps.sh $(GOPKGDIR) cmd/crioctl $(PROJECT))
|
||||||
$(GO) build $(LDFLAGS) -tags "$(BUILDTAGS)" -o $@ $(PROJECT)/cmd/crioctl
|
$(GO) build $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o bin/$@ $(PROJECT)/cmd/crioctl
|
||||||
|
|
||||||
kpod: .gopathok $(shell hack/find-godeps.sh $(GOPKGDIR) cmd/kpod $(PROJECT))
|
kpod: .gopathok $(shell hack/find-godeps.sh $(GOPKGDIR) cmd/kpod $(PROJECT))
|
||||||
$(GO) build $(LDFLAGS) -tags "$(BUILDTAGS)" -o $@ $(PROJECT)/cmd/kpod
|
$(GO) build $(LDFLAGS_KPOD) -tags "$(BUILDTAGS)" -o bin/$@ $(PROJECT)/cmd/kpod
|
||||||
|
|
||||||
crio.conf: crio
|
crio.conf: crio
|
||||||
./crio --config="" config --default > crio.conf
|
./bin/crio --config="" config --default > crio.conf
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
ifneq ($(GOPATH),)
|
ifneq ($(GOPATH),)
|
||||||
|
@ -102,7 +106,7 @@ endif
|
||||||
rm -fr test/testdata/redis-image
|
rm -fr test/testdata/redis-image
|
||||||
find . -name \*~ -delete
|
find . -name \*~ -delete
|
||||||
find . -name \#\* -delete
|
find . -name \#\* -delete
|
||||||
rm -f crioctl crio kpod
|
rm -f bin/crioctl bin/crio bin/kpod
|
||||||
make -C conmon clean
|
make -C conmon clean
|
||||||
make -C pause clean
|
make -C pause clean
|
||||||
rm -f test/bin2img/bin2img
|
rm -f test/bin2img/bin2img
|
||||||
|
@ -121,10 +125,11 @@ integration: crioimage
|
||||||
testunit:
|
testunit:
|
||||||
$(GO) test -tags "$(BUILDTAGS)" -cover $(PACKAGES)
|
$(GO) test -tags "$(BUILDTAGS)" -cover $(PACKAGES)
|
||||||
|
|
||||||
localintegration: clean binaries
|
localintegration: clean binaries test-binaries
|
||||||
./test/test_runner.sh ${TESTFLAGS}
|
./test/test_runner.sh ${TESTFLAGS}
|
||||||
|
|
||||||
binaries: crio crioctl kpod conmon pause test/bin2img/bin2img test/copyimg/copyimg test/checkseccomp/checkseccomp
|
binaries: crio conmon pause kpod crioctl
|
||||||
|
test-binaries: test/bin2img/bin2img test/copyimg/copyimg test/checkseccomp/checkseccomp
|
||||||
|
|
||||||
MANPAGES_MD := $(wildcard docs/*.md)
|
MANPAGES_MD := $(wildcard docs/*.md)
|
||||||
MANPAGES := $(MANPAGES_MD:%.md=%)
|
MANPAGES := $(MANPAGES_MD:%.md=%)
|
||||||
|
@ -143,11 +148,11 @@ docs: $(MANPAGES)
|
||||||
install: .gopathok install.bin install.man
|
install: .gopathok install.bin install.man
|
||||||
|
|
||||||
install.bin:
|
install.bin:
|
||||||
install ${SELINUXOPT} -D -m 755 crio $(BINDIR)/crio
|
install ${SELINUXOPT} -D -m 755 bin/crio $(BINDIR)/crio
|
||||||
install ${SELINUXOPT} -D -m 755 crioctl $(BINDIR)/crioctl
|
install ${SELINUXOPT} -D -m 755 bin/crioctl $(BINDIR)/crioctl
|
||||||
install ${SELINUXOPT} -D -m 755 kpod $(BINDIR)/kpod
|
install ${SELINUXOPT} -D -m 755 bin/kpod $(BINDIR)/kpod
|
||||||
install ${SELINUXOPT} -D -m 755 conmon/conmon $(LIBEXECDIR)/crio/conmon
|
install ${SELINUXOPT} -D -m 755 bin/conmon $(LIBEXECDIR)/crio/conmon
|
||||||
install ${SELINUXOPT} -D -m 755 pause/pause $(LIBEXECDIR)/crio/pause
|
install ${SELINUXOPT} -D -m 755 bin/pause $(LIBEXECDIR)/crio/pause
|
||||||
|
|
||||||
install.man:
|
install.man:
|
||||||
install ${SELINUXOPT} -d -m 755 $(MANDIR)/man1
|
install ${SELINUXOPT} -d -m 755 $(MANDIR)/man1
|
||||||
|
@ -161,6 +166,7 @@ install.config:
|
||||||
install ${SELINUXOPT} -D -m 644 crio.conf $(ETCDIR_CRIO)/crio.conf
|
install ${SELINUXOPT} -D -m 644 crio.conf $(ETCDIR_CRIO)/crio.conf
|
||||||
install ${SELINUXOPT} -D -m 644 seccomp.json $(ETCDIR_CRIO)/seccomp.json
|
install ${SELINUXOPT} -D -m 644 seccomp.json $(ETCDIR_CRIO)/seccomp.json
|
||||||
install ${SELINUXOPT} -D -m 644 crio-umount.conf $(OCIUMOUNTINSTALLDIR)/crio-umount.conf
|
install ${SELINUXOPT} -D -m 644 crio-umount.conf $(OCIUMOUNTINSTALLDIR)/crio-umount.conf
|
||||||
|
install ${SELINUXOPT} -D -m 644 crictl.yaml $(CRICTL_CONFIG_DIR)
|
||||||
|
|
||||||
install.completions:
|
install.completions:
|
||||||
install ${SELINUXOPT} -d -m 755 ${BASHINSTALLDIR}
|
install ${SELINUXOPT} -d -m 755 ${BASHINSTALLDIR}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
[![Build Status](https://img.shields.io/travis/kubernetes-incubator/cri-o.svg?maxAge=2592000&style=flat-square)](https://travis-ci.org/kubernetes-incubator/cri-o)
|
[![Build Status](https://img.shields.io/travis/kubernetes-incubator/cri-o.svg?maxAge=2592000&style=flat-square)](https://travis-ci.org/kubernetes-incubator/cri-o)
|
||||||
[![Go Report Card](https://goreportcard.com/badge/github.com/kubernetes-incubator/cri-o?style=flat-square)](https://goreportcard.com/report/github.com/kubernetes-incubator/cri-o)
|
[![Go Report Card](https://goreportcard.com/badge/github.com/kubernetes-incubator/cri-o?style=flat-square)](https://goreportcard.com/report/github.com/kubernetes-incubator/cri-o)
|
||||||
|
|
||||||
### Status: Release Candidate 3
|
### Status: Stable
|
||||||
|
|
||||||
## What is the scope of this project?
|
## What is the scope of this project?
|
||||||
|
|
||||||
|
@ -256,5 +256,4 @@ To run a full cluster, see [the instructions](kubernetes.md).
|
||||||
1. Support for log management, networking integration using CNI, pluggable image/storage management (done)
|
1. Support for log management, networking integration using CNI, pluggable image/storage management (done)
|
||||||
1. Support for exec/attach (done)
|
1. Support for exec/attach (done)
|
||||||
1. Target fully automated kubernetes testing without failures [e2e status](https://github.com/kubernetes-incubator/cri-o/issues/533)
|
1. Target fully automated kubernetes testing without failures [e2e status](https://github.com/kubernetes-incubator/cri-o/issues/533)
|
||||||
1. Release 1.0
|
|
||||||
1. Track upstream k8s releases
|
1. Track upstream k8s releases
|
||||||
|
|
|
@ -108,6 +108,10 @@ cgroup_manager = "{{ .CgroupManager }}"
|
||||||
# hooks_dir_path is the oci hooks directory for automatically executed hooks
|
# hooks_dir_path is the oci hooks directory for automatically executed hooks
|
||||||
hooks_dir_path = "{{ .HooksDirPath }}"
|
hooks_dir_path = "{{ .HooksDirPath }}"
|
||||||
|
|
||||||
|
# default_mounts is the mounts list to be mounted for the container when created
|
||||||
|
default_mounts = [
|
||||||
|
{{ range $mount := .DefaultMounts }}{{ printf "\t%q, \n" $mount }}{{ end }}]
|
||||||
|
|
||||||
# pids_limit is the number of processes allowed in a container
|
# pids_limit is the number of processes allowed in a container
|
||||||
pids_limit = {{ .PidsLimit }}
|
pids_limit = {{ .PidsLimit }}
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,12 @@ import (
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/containers/storage/pkg/reexec"
|
"github.com/containers/storage/pkg/reexec"
|
||||||
"github.com/kubernetes-incubator/cri-o/libkpod"
|
"github.com/kubernetes-incubator/cri-o/libkpod"
|
||||||
"github.com/kubernetes-incubator/cri-o/server"
|
"github.com/kubernetes-incubator/cri-o/server"
|
||||||
|
"github.com/kubernetes-incubator/cri-o/version"
|
||||||
"github.com/opencontainers/selinux/go-selinux"
|
"github.com/opencontainers/selinux/go-selinux"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/soheilhy/cmux"
|
"github.com/soheilhy/cmux"
|
||||||
|
@ -23,10 +25,6 @@ import (
|
||||||
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
"k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is populated by the Makefile from the VERSION file
|
|
||||||
// in the repository
|
|
||||||
var version = ""
|
|
||||||
|
|
||||||
// gitCommit is the commit that the binary is being built from.
|
// gitCommit is the commit that the binary is being built from.
|
||||||
// It will be populated by the Makefile.
|
// It will be populated by the Makefile.
|
||||||
var gitCommit = ""
|
var gitCommit = ""
|
||||||
|
@ -127,6 +125,9 @@ func mergeConfig(config *server.Config, ctx *cli.Context) error {
|
||||||
if ctx.GlobalIsSet("hooks-dir-path") {
|
if ctx.GlobalIsSet("hooks-dir-path") {
|
||||||
config.HooksDirPath = ctx.GlobalString("hooks-dir-path")
|
config.HooksDirPath = ctx.GlobalString("hooks-dir-path")
|
||||||
}
|
}
|
||||||
|
if ctx.GlobalIsSet("default-mounts") {
|
||||||
|
config.DefaultMounts = ctx.GlobalStringSlice("default-mounts")
|
||||||
|
}
|
||||||
if ctx.GlobalIsSet("pids-limit") {
|
if ctx.GlobalIsSet("pids-limit") {
|
||||||
config.PidsLimit = ctx.GlobalInt64("pids-limit")
|
config.PidsLimit = ctx.GlobalInt64("pids-limit")
|
||||||
}
|
}
|
||||||
|
@ -179,9 +180,7 @@ func main() {
|
||||||
app := cli.NewApp()
|
app := cli.NewApp()
|
||||||
|
|
||||||
var v []string
|
var v []string
|
||||||
if version != "" {
|
v = append(v, version.Version)
|
||||||
v = append(v, version)
|
|
||||||
}
|
|
||||||
if gitCommit != "" {
|
if gitCommit != "" {
|
||||||
v = append(v, fmt.Sprintf("commit: %s", gitCommit))
|
v = append(v, fmt.Sprintf("commit: %s", gitCommit))
|
||||||
}
|
}
|
||||||
|
@ -322,6 +321,11 @@ func main() {
|
||||||
Value: libkpod.DefaultHooksDirPath,
|
Value: libkpod.DefaultHooksDirPath,
|
||||||
Hidden: true,
|
Hidden: true,
|
||||||
},
|
},
|
||||||
|
cli.StringSliceFlag{
|
||||||
|
Name: "default-mounts",
|
||||||
|
Usage: "add one or more default mount paths in the form host:container",
|
||||||
|
Hidden: true,
|
||||||
|
},
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
Name: "profile",
|
Name: "profile",
|
||||||
Usage: "enable pprof remote profiler on localhost:6060",
|
Usage: "enable pprof remote profiler on localhost:6060",
|
||||||
|
@ -405,6 +409,16 @@ func main() {
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
args := c.Args()
|
||||||
|
if len(args) > 0 {
|
||||||
|
for _, command := range app.Commands {
|
||||||
|
if args[0] == command.Name {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("command %q not supported", args[0])
|
||||||
|
}
|
||||||
|
|
||||||
config := c.App.Metadata["config"].(*server.Config)
|
config := c.App.Metadata["config"].(*server.Config)
|
||||||
|
|
||||||
if !config.SELinux {
|
if !config.SELinux {
|
||||||
|
@ -467,7 +481,8 @@ func main() {
|
||||||
|
|
||||||
infoMux := service.GetInfoMux()
|
infoMux := service.GetInfoMux()
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
Handler: infoMux,
|
Handler: infoMux,
|
||||||
|
ReadTimeout: 5 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
graceful := false
|
graceful := false
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
|
"bytes"
|
||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
@ -59,6 +60,16 @@ func (j JSONStructArray) Out() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JSON returns a byte array with a literal null [110 117 108 108] in it
|
||||||
|
// if it is passed empty data. We used bytes.Compare to see if that is
|
||||||
|
// the case.
|
||||||
|
if diff := bytes.Compare(data, []byte("null")); diff == 0 {
|
||||||
|
data = []byte("[]")
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the we did get NULL back, we should spit out {} which is
|
||||||
|
// at least valid JSON for the consumer.
|
||||||
fmt.Printf("%s\n", data)
|
fmt.Printf("%s\n", data)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -398,7 +398,9 @@ func getJSONOutput(containers []*libkpod.ContainerData, nSpace bool) (psOutput [
|
||||||
|
|
||||||
func generatePsOutput(containers []*libkpod.ContainerData, server *libkpod.ContainerServer, opts psOptions) error {
|
func generatePsOutput(containers []*libkpod.ContainerData, server *libkpod.ContainerServer, opts psOptions) error {
|
||||||
containersOutput := getContainers(containers, opts)
|
containersOutput := getContainers(containers, opts)
|
||||||
if len(containersOutput) == 0 {
|
// In the case of JSON, we want to continue so we at least pass
|
||||||
|
// {} --valid JSON-- to the consumer
|
||||||
|
if len(containersOutput) == 0 && opts.format != formats.JSONString {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"github.com/kubernetes-incubator/cri-o/libkpod"
|
"github.com/kubernetes-incubator/cri-o/libkpod"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -53,7 +54,7 @@ func rmCmd(c *cli.Context) error {
|
||||||
force := c.Bool("force")
|
force := c.Bool("force")
|
||||||
|
|
||||||
for _, container := range c.Args() {
|
for _, container := range c.Args() {
|
||||||
id, err2 := server.Remove(container, force)
|
id, err2 := server.Remove(context.Background(), container, force)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = err2
|
err = err2
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/kubernetes-incubator/cri-o/libkpod"
|
"github.com/kubernetes-incubator/cri-o/libkpod"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -61,7 +62,7 @@ func stopCmd(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
var lastError error
|
var lastError error
|
||||||
for _, container := range c.Args() {
|
for _, container := range c.Args() {
|
||||||
cid, err := server.ContainerStop(container, stopTimeout)
|
cid, err := server.ContainerStop(context.Background(), container, stopTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if lastError != nil {
|
if lastError != nil {
|
||||||
fmt.Fprintln(os.Stderr, lastError)
|
fmt.Fprintln(os.Stderr, lastError)
|
||||||
|
|
|
@ -5,8 +5,8 @@ override LIBS += $(shell pkg-config --libs glib-2.0)
|
||||||
override CFLAGS += -std=c99 -Os -Wall -Wextra $(shell pkg-config --cflags glib-2.0)
|
override CFLAGS += -std=c99 -Os -Wall -Wextra $(shell pkg-config --cflags glib-2.0)
|
||||||
|
|
||||||
conmon: $(obj)
|
conmon: $(obj)
|
||||||
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
|
$(CC) -o ../bin/$@ $^ $(CFLAGS) $(LIBS)
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
rm -f $(obj) conmon
|
rm -f $(obj) ../bin/conmon
|
||||||
|
|
|
@ -12,7 +12,7 @@ ExecStart=/usr/local/bin/crio \
|
||||||
$CRIO_STORAGE_OPTIONS \
|
$CRIO_STORAGE_OPTIONS \
|
||||||
$CRIO_NETWORK_OPTIONS
|
$CRIO_NETWORK_OPTIONS
|
||||||
ExecReload=/bin/kill -s HUP $MAINPID
|
ExecReload=/bin/kill -s HUP $MAINPID
|
||||||
TasksMax=8192
|
TasksMax=infinity
|
||||||
LimitNOFILE=1048576
|
LimitNOFILE=1048576
|
||||||
LimitNPROC=1048576
|
LimitNPROC=1048576
|
||||||
LimitCORE=infinity
|
LimitCORE=infinity
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
git:
|
git:
|
||||||
repo: "https://github.com/kubernetes-incubator/cri-tools.git"
|
repo: "https://github.com/kubernetes-incubator/cri-tools.git"
|
||||||
dest: "{{ ansible_env.GOPATH }}/src/github.com/kubernetes-incubator/cri-tools"
|
dest: "{{ ansible_env.GOPATH }}/src/github.com/kubernetes-incubator/cri-tools"
|
||||||
version: "16e6fe4d7199c5689db4630a9330e6a8a12cecd1"
|
version: "b42fc3f364dd48f649d55926c34492beeb9b2e99"
|
||||||
|
|
||||||
- name: install crictl
|
- name: install crictl
|
||||||
command: "/usr/bin/go install github.com/kubernetes-incubator/cri-tools/cmd/crictl"
|
command: "/usr/bin/go install github.com/kubernetes-incubator/cri-tools/cmd/crictl"
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
git:
|
git:
|
||||||
repo: "https://github.com/runcom/kubernetes.git"
|
repo: "https://github.com/runcom/kubernetes.git"
|
||||||
dest: "{{ ansible_env.GOPATH }}/src/k8s.io/kubernetes"
|
dest: "{{ ansible_env.GOPATH }}/src/k8s.io/kubernetes"
|
||||||
version: "cri-o-node-e2e-patched"
|
version: "cri-o-node-e2e-patched-logs"
|
||||||
|
|
||||||
- name: install etcd
|
- name: install etcd
|
||||||
command: "hack/install-etcd.sh"
|
command: "hack/install-etcd.sh"
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
e2e_shell_cmd: >
|
e2e_shell_cmd: >
|
||||||
/usr/bin/go run hack/e2e.go
|
/usr/bin/go run hack/e2e.go
|
||||||
--test
|
--test
|
||||||
-test_args="-host=https://{{ ansible_default_ipv4.address }}:6443
|
--test_args="-host=https://{{ ansible_default_ipv4.address }}:6443
|
||||||
--ginkgo.focus=\[Conformance\]
|
--ginkgo.focus=\[Conformance\]
|
||||||
--report-dir={{ artifacts }}"
|
--report-dir={{ artifacts }}"
|
||||||
&> {{ artifacts }}/e2e.log
|
&> {{ artifacts }}/e2e.log
|
||||||
|
|
|
@ -108,4 +108,4 @@
|
||||||
|
|
||||||
- name: Update the kernel cmdline to include quota support
|
- name: Update the kernel cmdline to include quota support
|
||||||
command: grubby --update-kernel=ALL --args="rootflags=pquota"
|
command: grubby --update-kernel=ALL --args="rootflags=pquota"
|
||||||
when: ansible_distribution in ['RedHat', 'CentOS']
|
when: ansible_distribution in ['RedHat', 'CentOS']
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
runtime-endpoint: /var/run/crio.sock
|
|
@ -105,6 +105,9 @@ Example:
|
||||||
**no_pivot**=*true*|*false*
|
**no_pivot**=*true*|*false*
|
||||||
Instructs the runtime to not use pivot_root, but instead use MS_MOVE
|
Instructs the runtime to not use pivot_root, but instead use MS_MOVE
|
||||||
|
|
||||||
|
**default_mounts**=[]
|
||||||
|
List of mount points, in the form host:container, to be mounted in every container
|
||||||
|
|
||||||
## CRIO.IMAGE TABLE
|
## CRIO.IMAGE TABLE
|
||||||
|
|
||||||
**default_transport**
|
**default_transport**
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/bash
|
||||||
|
if ! pkg-config ostree-1 2> /dev/null ; then
|
||||||
|
echo containers_image_ostree_stub
|
||||||
|
fi
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/bash
|
||||||
|
if pkg-config libselinux 2> /dev/null ; then
|
||||||
|
echo selinux
|
||||||
|
fi
|
|
@ -145,6 +145,10 @@ type RuntimeConfig struct {
|
||||||
// HooksDirPath location of oci hooks config files
|
// HooksDirPath location of oci hooks config files
|
||||||
HooksDirPath string `toml:"hooks_dir_path"`
|
HooksDirPath string `toml:"hooks_dir_path"`
|
||||||
|
|
||||||
|
// DefaultMounts is the list of mounts to be mounted for each container
|
||||||
|
// The format of each mount is "host-path:container-path"
|
||||||
|
DefaultMounts []string `toml:"default_mounts"`
|
||||||
|
|
||||||
// Hooks List of hooks to run with container
|
// Hooks List of hooks to run with container
|
||||||
Hooks map[string]HookParams
|
Hooks map[string]HookParams
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ import (
|
||||||
"github.com/kubernetes-incubator/cri-o/pkg/storage"
|
"github.com/kubernetes-incubator/cri-o/pkg/storage"
|
||||||
"github.com/opencontainers/runc/libcontainer"
|
"github.com/opencontainers/runc/libcontainer"
|
||||||
rspec "github.com/opencontainers/runtime-spec/specs-go"
|
rspec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
"github.com/opencontainers/selinux/go-selinux"
|
||||||
"github.com/opencontainers/selinux/go-selinux/label"
|
"github.com/opencontainers/selinux/go-selinux/label"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
@ -168,6 +169,7 @@ func New(config *Config) (*ContainerServer, error) {
|
||||||
containers: oci.NewMemoryStore(),
|
containers: oci.NewMemoryStore(),
|
||||||
infraContainers: oci.NewMemoryStore(),
|
infraContainers: oci.NewMemoryStore(),
|
||||||
sandboxes: make(map[string]*sandbox.Sandbox),
|
sandboxes: make(map[string]*sandbox.Sandbox),
|
||||||
|
processLevels: make(map[string]int),
|
||||||
},
|
},
|
||||||
config: config,
|
config: config,
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -388,6 +390,7 @@ func (c *ContainerServer) LoadSandbox(id string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
scontainer.SetSpec(&m)
|
||||||
scontainer.SetMountPoint(m.Annotations[annotations.MountPoint])
|
scontainer.SetMountPoint(m.Annotations[annotations.MountPoint])
|
||||||
|
|
||||||
if m.Annotations[annotations.Volumes] != "" {
|
if m.Annotations[annotations.Volumes] != "" {
|
||||||
|
@ -511,6 +514,7 @@ func (c *ContainerServer) LoadContainer(id string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
ctr.SetSpec(&m)
|
||||||
ctr.SetMountPoint(m.Annotations[annotations.MountPoint])
|
ctr.SetMountPoint(m.Annotations[annotations.MountPoint])
|
||||||
|
|
||||||
c.ContainerStateFromDisk(ctr)
|
c.ContainerStateFromDisk(ctr)
|
||||||
|
@ -609,6 +613,8 @@ type containerServerState struct {
|
||||||
containers oci.ContainerStorer
|
containers oci.ContainerStorer
|
||||||
infraContainers oci.ContainerStorer
|
infraContainers oci.ContainerStorer
|
||||||
sandboxes map[string]*sandbox.Sandbox
|
sandboxes map[string]*sandbox.Sandbox
|
||||||
|
// processLevels The number of sandboxes using the same SELinux MCS level. Need to release MCS Level, when count reaches 0
|
||||||
|
processLevels map[string]int
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddContainer adds a container to the container state store
|
// AddContainer adds a container to the container state store
|
||||||
|
@ -696,6 +702,7 @@ func (c *ContainerServer) AddSandbox(sb *sandbox.Sandbox) {
|
||||||
c.stateLock.Lock()
|
c.stateLock.Lock()
|
||||||
defer c.stateLock.Unlock()
|
defer c.stateLock.Unlock()
|
||||||
c.state.sandboxes[sb.ID()] = sb
|
c.state.sandboxes[sb.ID()] = sb
|
||||||
|
c.state.processLevels[selinux.NewContext(sb.ProcessLabel())["level"]]++
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSandbox returns a sandbox by its ID
|
// GetSandbox returns a sandbox by its ID
|
||||||
|
@ -728,7 +735,14 @@ func (c *ContainerServer) HasSandbox(id string) bool {
|
||||||
func (c *ContainerServer) RemoveSandbox(id string) {
|
func (c *ContainerServer) RemoveSandbox(id string) {
|
||||||
c.stateLock.Lock()
|
c.stateLock.Lock()
|
||||||
defer c.stateLock.Unlock()
|
defer c.stateLock.Unlock()
|
||||||
|
processLabel := c.state.sandboxes[id].ProcessLabel()
|
||||||
delete(c.state.sandboxes, id)
|
delete(c.state.sandboxes, id)
|
||||||
|
level := selinux.NewContext(processLabel)["level"]
|
||||||
|
c.state.processLevels[level]--
|
||||||
|
if c.state.processLevels[level] == 0 {
|
||||||
|
label.ReleaseLabel(processLabel)
|
||||||
|
delete(c.state.processLevels, level)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListSandboxes lists all sandboxes in the state store
|
// ListSandboxes lists all sandboxes in the state store
|
||||||
|
|
|
@ -6,10 +6,11 @@ import (
|
||||||
|
|
||||||
"github.com/kubernetes-incubator/cri-o/oci"
|
"github.com/kubernetes-incubator/cri-o/oci"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Remove removes a container
|
// Remove removes a container
|
||||||
func (c *ContainerServer) Remove(container string, force bool) (string, error) {
|
func (c *ContainerServer) Remove(ctx context.Context, container string, force bool) (string, error) {
|
||||||
ctr, err := c.LookupContainer(container)
|
ctr, err := c.LookupContainer(container)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -22,7 +23,7 @@ func (c *ContainerServer) Remove(container string, force bool) (string, error) {
|
||||||
return "", errors.Errorf("cannot remove paused container %s", ctrID)
|
return "", errors.Errorf("cannot remove paused container %s", ctrID)
|
||||||
case oci.ContainerStateCreated, oci.ContainerStateRunning:
|
case oci.ContainerStateCreated, oci.ContainerStateRunning:
|
||||||
if force {
|
if force {
|
||||||
_, err = c.ContainerStop(container, -1)
|
_, err = c.ContainerStop(ctx, container, 10)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Wrapf(err, "unable to stop container %s", ctrID)
|
return "", errors.Wrapf(err, "unable to stop container %s", ctrID)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,11 @@ package libkpod
|
||||||
import (
|
import (
|
||||||
"github.com/kubernetes-incubator/cri-o/oci"
|
"github.com/kubernetes-incubator/cri-o/oci"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ContainerStop stops a running container with a grace period (i.e., timeout).
|
// ContainerStop stops a running container with a grace period (i.e., timeout).
|
||||||
func (c *ContainerServer) ContainerStop(container string, timeout int64) (string, error) {
|
func (c *ContainerServer) ContainerStop(ctx context.Context, container string, timeout int64) (string, error) {
|
||||||
ctr, err := c.LookupContainer(container)
|
ctr, err := c.LookupContainer(container)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Wrapf(err, "failed to find container %s", container)
|
return "", errors.Wrapf(err, "failed to find container %s", container)
|
||||||
|
@ -20,7 +21,7 @@ func (c *ContainerServer) ContainerStop(container string, timeout int64) (string
|
||||||
return "", errors.Errorf("cannot stop paused container %s", ctrID)
|
return "", errors.Errorf("cannot stop paused container %s", ctrID)
|
||||||
default:
|
default:
|
||||||
if cStatus.Status != oci.ContainerStateStopped {
|
if cStatus.Status != oci.ContainerStateStopped {
|
||||||
if err := c.runtime.StopContainer(ctr, timeout); err != nil {
|
if err := c.runtime.StopContainer(ctx, ctr, timeout); err != nil {
|
||||||
return "", errors.Wrapf(err, "failed to stop container %s", ctrID)
|
return "", errors.Wrapf(err, "failed to stop container %s", ctrID)
|
||||||
}
|
}
|
||||||
if err := c.storageRuntimeServer.StopContainer(ctrID); err != nil {
|
if err := c.storageRuntimeServer.StopContainer(ctrID); err != nil {
|
||||||
|
|
|
@ -48,6 +48,7 @@ type Container struct {
|
||||||
imageRef string
|
imageRef string
|
||||||
volumes []ContainerVolume
|
volumes []ContainerVolume
|
||||||
mountPoint string
|
mountPoint string
|
||||||
|
spec *specs.Spec
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContainerVolume is a bind mount for the container.
|
// ContainerVolume is a bind mount for the container.
|
||||||
|
@ -99,6 +100,16 @@ func NewContainer(id string, name string, bundlePath string, logPath string, net
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetSpec loads the OCI spec in the container struct
|
||||||
|
func (c *Container) SetSpec(s *specs.Spec) {
|
||||||
|
c.spec = s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spec returns a copy of the spec for the container
|
||||||
|
func (c *Container) Spec() specs.Spec {
|
||||||
|
return *c.spec
|
||||||
|
}
|
||||||
|
|
||||||
// GetStopSignal returns the container's own stop signal configured from the
|
// GetStopSignal returns the container's own stop signal configured from the
|
||||||
// image configuration or the default one.
|
// image configuration or the default one.
|
||||||
func (c *Container) GetStopSignal() string {
|
func (c *Container) GetStopSignal() string {
|
||||||
|
|
81
oci/oci.go
81
oci/oci.go
|
@ -17,6 +17,7 @@ import (
|
||||||
"github.com/kubernetes-incubator/cri-o/utils"
|
"github.com/kubernetes-incubator/cri-o/utils"
|
||||||
rspec "github.com/opencontainers/runtime-spec/specs-go"
|
rspec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/net/context"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
kwait "k8s.io/apimachinery/pkg/util/wait"
|
kwait "k8s.io/apimachinery/pkg/util/wait"
|
||||||
)
|
)
|
||||||
|
@ -39,6 +40,10 @@ const (
|
||||||
SystemdCgroupsManager = "systemd"
|
SystemdCgroupsManager = "systemd"
|
||||||
// ContainerExitsDir is the location of container exit dirs
|
// ContainerExitsDir is the location of container exit dirs
|
||||||
ContainerExitsDir = "/var/run/crio/exits"
|
ContainerExitsDir = "/var/run/crio/exits"
|
||||||
|
|
||||||
|
// killContainerTimeout is the timeout that we wait for the container to
|
||||||
|
// be SIGKILLed.
|
||||||
|
killContainerTimeout = 2 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
// New creates a new Runtime with options provided
|
// New creates a new Runtime with options provided
|
||||||
|
@ -224,11 +229,12 @@ func (r *Runtime) CreateContainer(c *Container, cgroupParent string) (err error)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Warnf("Failed to add conmon to cgroupfs sandbox cgroup: %v", err)
|
logrus.Warnf("Failed to add conmon to cgroupfs sandbox cgroup: %v", err)
|
||||||
} else {
|
} else {
|
||||||
// XXX: this defer does nothing as the cgroup can't be deleted cause
|
// Here we should defer a crio-connmon- cgroup hierarchy deletion, but it will
|
||||||
// it contains the conmon pid in tasks
|
// always fail as conmon's pid is still there.
|
||||||
// we need to remove this defer and delete the cgroup once conmon exits
|
// Fortunately, kubelet takes care of deleting this for us, so the leak will
|
||||||
// maybe need a conmon monitor?
|
// only happens in corner case where one does a manual deletion of the container
|
||||||
defer control.Delete()
|
// through e.g. runc. This should be handled by implementing a conmon monitoring
|
||||||
|
// routine that does the cgroup cleanup once conmon is terminated.
|
||||||
if err := control.Add(cgroups.Process{Pid: cmd.Process.Pid}); err != nil {
|
if err := control.Add(cgroups.Process{Pid: cmd.Process.Pid}); err != nil {
|
||||||
logrus.Warnf("Failed to add conmon to cgroupfs sandbox cgroup: %v", err)
|
logrus.Warnf("Failed to add conmon to cgroupfs sandbox cgroup: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -406,7 +412,7 @@ func (r *Runtime) ExecSync(c *Container, command []string, timeout int64) (resp
|
||||||
os.RemoveAll(logPath)
|
os.RemoveAll(logPath)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
f, err := ioutil.TempFile("", "exec-process")
|
f, err := ioutil.TempFile("", "exec-sync-process")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ExecSyncError{
|
return nil, ExecSyncError{
|
||||||
ExitCode: -1,
|
ExitCode: -1,
|
||||||
|
@ -429,11 +435,8 @@ func (r *Runtime) ExecSync(c *Container, command []string, timeout int64) (resp
|
||||||
}
|
}
|
||||||
args = append(args, "-l", logPath)
|
args = append(args, "-l", logPath)
|
||||||
|
|
||||||
pspec := rspec.Process{
|
pspec := c.Spec().Process
|
||||||
Env: r.conmonEnv,
|
pspec.Args = command
|
||||||
Args: command,
|
|
||||||
Cwd: "/",
|
|
||||||
}
|
|
||||||
processJSON, err := json.Marshal(pspec)
|
processJSON, err := json.Marshal(pspec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ExecSyncError{
|
return nil, ExecSyncError{
|
||||||
|
@ -541,25 +544,7 @@ func (r *Runtime) ExecSync(c *Container, command []string, timeout int64) (resp
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// StopContainer stops a container. Timeout is given in seconds.
|
func waitContainerStop(ctx context.Context, c *Container, timeout time.Duration) error {
|
||||||
func (r *Runtime) StopContainer(c *Container, timeout int64) error {
|
|
||||||
c.opLock.Lock()
|
|
||||||
defer c.opLock.Unlock()
|
|
||||||
|
|
||||||
// Check if the process is around before sending a signal
|
|
||||||
err := unix.Kill(c.state.Pid, 0)
|
|
||||||
if err == unix.ESRCH {
|
|
||||||
c.state.Finished = time.Now()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, r.Path(c), "kill", c.id, c.GetStopSignal()); err != nil {
|
|
||||||
return fmt.Errorf("failed to stop container %s, %v", c.id, err)
|
|
||||||
}
|
|
||||||
if timeout == -1 {
|
|
||||||
// default 10 seconds delay
|
|
||||||
timeout = 10
|
|
||||||
}
|
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
// we could potentially re-use "done" channel to exit the loop on timeout
|
// we could potentially re-use "done" channel to exit the loop on timeout
|
||||||
// but we use another channel "chControl" so that we won't never incur in the
|
// but we use another channel "chControl" so that we won't never incur in the
|
||||||
|
@ -587,7 +572,10 @@ func (r *Runtime) StopContainer(c *Container, timeout int64) error {
|
||||||
select {
|
select {
|
||||||
case <-done:
|
case <-done:
|
||||||
return nil
|
return nil
|
||||||
case <-time.After(time.Duration(timeout) * time.Second):
|
case <-ctx.Done():
|
||||||
|
close(chControl)
|
||||||
|
return ctx.Err()
|
||||||
|
case <-time.After(timeout):
|
||||||
close(chControl)
|
close(chControl)
|
||||||
err := unix.Kill(c.state.Pid, unix.SIGKILL)
|
err := unix.Kill(c.state.Pid, unix.SIGKILL)
|
||||||
if err != nil && err != unix.ESRCH {
|
if err != nil && err != unix.ESRCH {
|
||||||
|
@ -596,10 +584,39 @@ func (r *Runtime) StopContainer(c *Container, timeout int64) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
c.state.Finished = time.Now()
|
c.state.Finished = time.Now()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StopContainer stops a container. Timeout is given in seconds.
|
||||||
|
func (r *Runtime) StopContainer(ctx context.Context, c *Container, timeout int64) error {
|
||||||
|
c.opLock.Lock()
|
||||||
|
defer c.opLock.Unlock()
|
||||||
|
|
||||||
|
// Check if the process is around before sending a signal
|
||||||
|
err := unix.Kill(c.state.Pid, 0)
|
||||||
|
if err == unix.ESRCH {
|
||||||
|
c.state.Finished = time.Now()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if timeout > 0 {
|
||||||
|
if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, r.Path(c), "kill", c.id, c.GetStopSignal()); err != nil {
|
||||||
|
return fmt.Errorf("failed to stop container %s, %v", c.id, err)
|
||||||
|
}
|
||||||
|
err = waitContainerStop(ctx, c, time.Duration(timeout)*time.Second)
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
logrus.Warnf("Stop container %q timed out: %v", c.ID(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, r.Path(c), "kill", "--all", c.id, "KILL"); err != nil {
|
||||||
|
return fmt.Errorf("failed to stop container %s, %v", c.id, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return waitContainerStop(ctx, c, killContainerTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteContainer deletes a container.
|
// DeleteContainer deletes a container.
|
||||||
func (r *Runtime) DeleteContainer(c *Container) error {
|
func (r *Runtime) DeleteContainer(c *Container) error {
|
||||||
c.opLock.Lock()
|
c.opLock.Lock()
|
||||||
|
|
|
@ -5,9 +5,9 @@ override LIBS +=
|
||||||
override CFLAGS += -std=c99 -Os -Wall -Wextra -static
|
override CFLAGS += -std=c99 -Os -Wall -Wextra -static
|
||||||
|
|
||||||
pause: $(obj)
|
pause: $(obj)
|
||||||
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
|
$(CC) -o ../bin/$@ $^ $(CFLAGS) $(LIBS)
|
||||||
strip $@
|
strip ../bin/$@
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
rm -f $(obj) pause
|
rm -f $(obj) ../bin/pause
|
||||||
|
|
|
@ -2,10 +2,8 @@ package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containers/image/copy"
|
"github.com/containers/image/copy"
|
||||||
|
@ -16,7 +14,12 @@ import (
|
||||||
"github.com/containers/image/transports/alltransports"
|
"github.com/containers/image/transports/alltransports"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
distreference "github.com/docker/distribution/reference"
|
digest "github.com/opencontainers/go-digest"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrCannotParseImageID is returned when we try to ResolveNames for an image ID
|
||||||
|
ErrCannotParseImageID = errors.New("cannot parse an image ID")
|
||||||
)
|
)
|
||||||
|
|
||||||
// ImageResult wraps a subset of information about an image: its ID, its names,
|
// ImageResult wraps a subset of information about an image: its ID, its names,
|
||||||
|
@ -25,6 +28,10 @@ type ImageResult struct {
|
||||||
ID string
|
ID string
|
||||||
Names []string
|
Names []string
|
||||||
Size *uint64
|
Size *uint64
|
||||||
|
// TODO(runcom): this is an hack for https://github.com/kubernetes-incubator/cri-o/pull/1136
|
||||||
|
// drop this when we have proper image IDs (as in, image IDs should be just
|
||||||
|
// the config blog digest which is stable across same images).
|
||||||
|
ConfigDigest digest.Digest
|
||||||
}
|
}
|
||||||
|
|
||||||
type indexInfo struct {
|
type indexInfo struct {
|
||||||
|
@ -47,6 +54,9 @@ type ImageServer interface {
|
||||||
ListImages(systemContext *types.SystemContext, filter string) ([]ImageResult, error)
|
ListImages(systemContext *types.SystemContext, filter string) ([]ImageResult, error)
|
||||||
// ImageStatus returns status of an image which matches the filter.
|
// ImageStatus returns status of an image which matches the filter.
|
||||||
ImageStatus(systemContext *types.SystemContext, filter string) (*ImageResult, error)
|
ImageStatus(systemContext *types.SystemContext, filter string) (*ImageResult, error)
|
||||||
|
// PrepareImage returns an Image where the config digest can be grabbed
|
||||||
|
// for further analysis. Call Close() on the resulting image.
|
||||||
|
PrepareImage(systemContext *types.SystemContext, imageName string, options *copy.Options) (types.Image, error)
|
||||||
// PullImage imports an image from the specified location.
|
// PullImage imports an image from the specified location.
|
||||||
PullImage(systemContext *types.SystemContext, imageName string, options *copy.Options) (types.ImageReference, error)
|
PullImage(systemContext *types.SystemContext, imageName string, options *copy.Options) (types.ImageReference, error)
|
||||||
// RemoveImage deletes the specified image.
|
// RemoveImage deletes the specified image.
|
||||||
|
@ -146,14 +156,16 @@ func (svc *imageService) ImageStatus(systemContext *types.SystemContext, nameOrI
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
defer img.Close()
|
||||||
size := imageSize(img)
|
size := imageSize(img)
|
||||||
img.Close()
|
|
||||||
|
|
||||||
return &ImageResult{
|
res := &ImageResult{
|
||||||
ID: image.ID,
|
ID: image.ID,
|
||||||
Names: image.Names,
|
Names: image.Names,
|
||||||
Size: size,
|
Size: size,
|
||||||
}, nil
|
ConfigDigest: img.ConfigInfo().Digest,
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func imageSize(img types.Image) *uint64 {
|
func imageSize(img types.Image) *uint64 {
|
||||||
|
@ -165,7 +177,7 @@ func imageSize(img types.Image) *uint64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (svc *imageService) CanPull(imageName string, options *copy.Options) (bool, error) {
|
func (svc *imageService) CanPull(imageName string, options *copy.Options) (bool, error) {
|
||||||
srcRef, err := svc.prepareImage(imageName, options)
|
srcRef, err := svc.prepareReference(imageName, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -182,9 +194,9 @@ func (svc *imageService) CanPull(imageName string, options *copy.Options) (bool,
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepareImage creates an image reference from an image string and set options
|
// prepareReference creates an image reference from an image string and set options
|
||||||
// for the source context
|
// for the source context
|
||||||
func (svc *imageService) prepareImage(imageName string, options *copy.Options) (types.ImageReference, error) {
|
func (svc *imageService) prepareReference(imageName string, options *copy.Options) (types.ImageReference, error) {
|
||||||
if imageName == "" {
|
if imageName == "" {
|
||||||
return nil, storage.ErrNotAnImage
|
return nil, storage.ErrNotAnImage
|
||||||
}
|
}
|
||||||
|
@ -212,6 +224,18 @@ func (svc *imageService) prepareImage(imageName string, options *copy.Options) (
|
||||||
return srcRef, nil
|
return srcRef, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (svc *imageService) PrepareImage(systemContext *types.SystemContext, imageName string, options *copy.Options) (types.Image, error) {
|
||||||
|
if options == nil {
|
||||||
|
options = ©.Options{}
|
||||||
|
}
|
||||||
|
|
||||||
|
srcRef, err := svc.prepareReference(imageName, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return srcRef.NewImage(systemContext)
|
||||||
|
}
|
||||||
|
|
||||||
func (svc *imageService) PullImage(systemContext *types.SystemContext, imageName string, options *copy.Options) (types.ImageReference, error) {
|
func (svc *imageService) PullImage(systemContext *types.SystemContext, imageName string, options *copy.Options) (types.ImageReference, error) {
|
||||||
policy, err := signature.DefaultPolicy(systemContext)
|
policy, err := signature.DefaultPolicy(systemContext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -225,7 +249,7 @@ func (svc *imageService) PullImage(systemContext *types.SystemContext, imageName
|
||||||
options = ©.Options{}
|
options = ©.Options{}
|
||||||
}
|
}
|
||||||
|
|
||||||
srcRef, err := svc.prepareImage(imageName, options)
|
srcRef, err := svc.prepareReference(imageName, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -307,113 +331,27 @@ func (svc *imageService) isSecureIndex(indexName string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func isValidHostname(hostname string) bool {
|
func splitDockerDomain(name string) (domain, remainder string) {
|
||||||
return hostname != "" && !strings.Contains(hostname, "/") &&
|
|
||||||
(strings.Contains(hostname, ".") ||
|
|
||||||
strings.Contains(hostname, ":") || hostname == "localhost")
|
|
||||||
}
|
|
||||||
|
|
||||||
func isReferenceFullyQualified(reposName reference.Named) bool {
|
|
||||||
indexName, _, _ := splitReposName(reposName)
|
|
||||||
return indexName != ""
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// defaultHostname is the default built-in hostname
|
|
||||||
defaultHostname = "docker.io"
|
|
||||||
// legacyDefaultHostname is automatically converted to DefaultHostname
|
|
||||||
legacyDefaultHostname = "index.docker.io"
|
|
||||||
// defaultRepoPrefix is the prefix used for default repositories in default host
|
|
||||||
defaultRepoPrefix = "library/"
|
|
||||||
)
|
|
||||||
|
|
||||||
// splitReposName breaks a reposName into an index name and remote name
|
|
||||||
func splitReposName(reposName reference.Named) (indexName string, remoteName reference.Named, err error) {
|
|
||||||
var remoteNameStr string
|
|
||||||
indexName, remoteNameStr = distreference.SplitHostname(reposName)
|
|
||||||
if !isValidHostname(indexName) {
|
|
||||||
// This is a Docker Index repos (ex: samalba/hipache or ubuntu)
|
|
||||||
// 'docker.io'
|
|
||||||
indexName = ""
|
|
||||||
remoteName = reposName
|
|
||||||
} else {
|
|
||||||
remoteName, err = withName(remoteNameStr)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateName(name string) error {
|
|
||||||
if err := validateID(strings.TrimPrefix(name, defaultHostname+"/")); err == nil {
|
|
||||||
return fmt.Errorf("Invalid repository name (%s), cannot specify 64-byte hexadecimal strings", name)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var validHex = regexp.MustCompile(`^([a-f0-9]{64})$`)
|
|
||||||
|
|
||||||
// validateID checks whether an ID string is a valid image ID.
|
|
||||||
func validateID(id string) error {
|
|
||||||
if ok := validHex.MatchString(id); !ok {
|
|
||||||
return fmt.Errorf("image ID %q is invalid", id)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// withName returns a named object representing the given string. If the input
|
|
||||||
// is invalid ErrReferenceInvalidFormat will be returned.
|
|
||||||
func withName(name string) (reference.Named, error) {
|
|
||||||
name, err := normalize(name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := validateName(name); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
r, err := distreference.WithName(name)
|
|
||||||
return r, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// splitHostname splits a repository name to hostname and remotename string.
|
|
||||||
// If no valid hostname is found, empty string will be returned as a resulting
|
|
||||||
// hostname. Repository name needs to be already validated before.
|
|
||||||
func splitHostname(name string) (hostname, remoteName string) {
|
|
||||||
i := strings.IndexRune(name, '/')
|
i := strings.IndexRune(name, '/')
|
||||||
if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") {
|
if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") {
|
||||||
hostname, remoteName = "", name
|
domain, remainder = "", name
|
||||||
} else {
|
} else {
|
||||||
hostname, remoteName = name[:i], name[i+1:]
|
domain, remainder = name[:i], name[i+1:]
|
||||||
}
|
|
||||||
if hostname == legacyDefaultHostname {
|
|
||||||
hostname = defaultHostname
|
|
||||||
}
|
|
||||||
if hostname == defaultHostname && !strings.ContainsRune(remoteName, '/') {
|
|
||||||
remoteName = defaultRepoPrefix + remoteName
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// normalize returns a repository name in its normalized form, meaning it
|
|
||||||
// will contain library/ prefix for official images.
|
|
||||||
func normalize(name string) (string, error) {
|
|
||||||
host, remoteName := splitHostname(name)
|
|
||||||
if strings.ToLower(remoteName) != remoteName {
|
|
||||||
return "", errors.New("invalid reference format: repository name must be lowercase")
|
|
||||||
}
|
|
||||||
if host == defaultHostname {
|
|
||||||
if strings.HasPrefix(remoteName, defaultRepoPrefix) {
|
|
||||||
remoteName = strings.TrimPrefix(remoteName, defaultRepoPrefix)
|
|
||||||
}
|
|
||||||
return host + "/" + remoteName, nil
|
|
||||||
}
|
|
||||||
return name, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (svc *imageService) ResolveNames(imageName string) ([]string, error) {
|
func (svc *imageService) ResolveNames(imageName string) ([]string, error) {
|
||||||
r, err := reference.ParseNormalizedNamed(imageName)
|
// This to prevent any image ID to go through this routine
|
||||||
|
_, err := reference.ParseNormalizedNamed(imageName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "cannot specify 64-byte hexadecimal strings") {
|
||||||
|
return nil, ErrCannotParseImageID
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if isReferenceFullyQualified(r) {
|
domain, remainder := splitDockerDomain(imageName)
|
||||||
|
if domain != "" {
|
||||||
// this means the image is already fully qualified
|
// this means the image is already fully qualified
|
||||||
return []string{imageName}, nil
|
return []string{imageName}, nil
|
||||||
}
|
}
|
||||||
|
@ -425,10 +363,13 @@ func (svc *imageService) ResolveNames(imageName string) ([]string, error) {
|
||||||
// this means we got an image in the form of "busybox"
|
// this means we got an image in the form of "busybox"
|
||||||
// we need to use additional registries...
|
// we need to use additional registries...
|
||||||
// normalize the unqualified image to be domain/repo/image...
|
// normalize the unqualified image to be domain/repo/image...
|
||||||
_, rest := splitDomain(r.Name())
|
|
||||||
images := []string{}
|
images := []string{}
|
||||||
for _, r := range svc.registries {
|
for _, r := range svc.registries {
|
||||||
images = append(images, filepath.Join(r, rest))
|
rem := remainder
|
||||||
|
if r == "docker.io" && !strings.ContainsRune(remainder, '/') {
|
||||||
|
rem = "library/" + rem
|
||||||
|
}
|
||||||
|
images = append(images, filepath.Join(r, rem))
|
||||||
}
|
}
|
||||||
return images, nil
|
return images, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,125 +0,0 @@
|
||||||
package storage
|
|
||||||
|
|
||||||
// This is a fork of docker/distribution code to be used when manipulating image
|
|
||||||
// references.
|
|
||||||
// DO NOT EDIT THIS FILE.
|
|
||||||
|
|
||||||
import "regexp"
|
|
||||||
|
|
||||||
var (
|
|
||||||
// alphaNumericRegexp defines the alpha numeric atom, typically a
|
|
||||||
// component of names. This only allows lower case characters and digits.
|
|
||||||
alphaNumericRegexp = match(`[a-z0-9]+`)
|
|
||||||
|
|
||||||
// separatorRegexp defines the separators allowed to be embedded in name
|
|
||||||
// components. This allow one period, one or two underscore and multiple
|
|
||||||
// dashes.
|
|
||||||
separatorRegexp = match(`(?:[._]|__|[-]*)`)
|
|
||||||
|
|
||||||
// nameComponentRegexp restricts registry path component names to start
|
|
||||||
// with at least one letter or number, with following parts able to be
|
|
||||||
// separated by one period, one or two underscore and multiple dashes.
|
|
||||||
nameComponentRegexp = expression(
|
|
||||||
alphaNumericRegexp,
|
|
||||||
optional(repeated(separatorRegexp, alphaNumericRegexp)))
|
|
||||||
|
|
||||||
// domainComponentRegexp restricts the registry domain component of a
|
|
||||||
// repository name to start with a component as defined by domainRegexp
|
|
||||||
// and followed by an optional port.
|
|
||||||
domainComponentRegexp = match(`(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])`)
|
|
||||||
|
|
||||||
// domainRegexp defines the structure of potential domain components
|
|
||||||
// that may be part of image names. This is purposely a subset of what is
|
|
||||||
// allowed by DNS to ensure backwards compatibility with Docker image
|
|
||||||
// names.
|
|
||||||
domainRegexp = expression(
|
|
||||||
domainComponentRegexp,
|
|
||||||
optional(repeated(literal(`.`), domainComponentRegexp)),
|
|
||||||
optional(literal(`:`), match(`[0-9]+`)))
|
|
||||||
|
|
||||||
// NameRegexp is the format for the name component of references. The
|
|
||||||
// regexp has capturing groups for the domain and name part omitting
|
|
||||||
// the separating forward slash from either.
|
|
||||||
NameRegexp = expression(
|
|
||||||
optional(domainRegexp, literal(`/`)),
|
|
||||||
nameComponentRegexp,
|
|
||||||
optional(repeated(literal(`/`), nameComponentRegexp)))
|
|
||||||
|
|
||||||
// anchoredNameRegexp is used to parse a name value, capturing the
|
|
||||||
// domain and trailing components.
|
|
||||||
anchoredNameRegexp = anchored(
|
|
||||||
optional(capture(domainRegexp), literal(`/`)),
|
|
||||||
capture(nameComponentRegexp,
|
|
||||||
optional(repeated(literal(`/`), nameComponentRegexp))))
|
|
||||||
|
|
||||||
// IdentifierRegexp is the format for string identifier used as a
|
|
||||||
// content addressable identifier using sha256. These identifiers
|
|
||||||
// are like digests without the algorithm, since sha256 is used.
|
|
||||||
IdentifierRegexp = match(`([a-f0-9]{64})`)
|
|
||||||
|
|
||||||
// ShortIdentifierRegexp is the format used to represent a prefix
|
|
||||||
// of an identifier. A prefix may be used to match a sha256 identifier
|
|
||||||
// within a list of trusted identifiers.
|
|
||||||
ShortIdentifierRegexp = match(`([a-f0-9]{6,64})`)
|
|
||||||
)
|
|
||||||
|
|
||||||
// match compiles the string to a regular expression.
|
|
||||||
var match = regexp.MustCompile
|
|
||||||
|
|
||||||
// literal compiles s into a literal regular expression, escaping any regexp
|
|
||||||
// reserved characters.
|
|
||||||
func literal(s string) *regexp.Regexp {
|
|
||||||
re := match(regexp.QuoteMeta(s))
|
|
||||||
|
|
||||||
if _, complete := re.LiteralPrefix(); !complete {
|
|
||||||
panic("must be a literal")
|
|
||||||
}
|
|
||||||
|
|
||||||
return re
|
|
||||||
}
|
|
||||||
|
|
||||||
func splitDomain(name string) (string, string) {
|
|
||||||
match := anchoredNameRegexp.FindStringSubmatch(name)
|
|
||||||
if len(match) != 3 {
|
|
||||||
return "", name
|
|
||||||
}
|
|
||||||
return match[1], match[2]
|
|
||||||
}
|
|
||||||
|
|
||||||
// expression defines a full expression, where each regular expression must
|
|
||||||
// follow the previous.
|
|
||||||
func expression(res ...*regexp.Regexp) *regexp.Regexp {
|
|
||||||
var s string
|
|
||||||
for _, re := range res {
|
|
||||||
s += re.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
return match(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// optional wraps the expression in a non-capturing group and makes the
|
|
||||||
// production optional.
|
|
||||||
func optional(res ...*regexp.Regexp) *regexp.Regexp {
|
|
||||||
return match(group(expression(res...)).String() + `?`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// repeated wraps the regexp in a non-capturing group to get one or more
|
|
||||||
// matches.
|
|
||||||
func repeated(res ...*regexp.Regexp) *regexp.Regexp {
|
|
||||||
return match(group(expression(res...)).String() + `+`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// group wraps the regexp in a non-capturing group.
|
|
||||||
func group(res ...*regexp.Regexp) *regexp.Regexp {
|
|
||||||
return match(`(?:` + expression(res...).String() + `)`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// capture wraps the expression in a capturing group.
|
|
||||||
func capture(res ...*regexp.Regexp) *regexp.Regexp {
|
|
||||||
return match(`(` + expression(res...).String() + `)`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// anchored anchors the regular expression by adding start and end delimiters.
|
|
||||||
func anchored(res ...*regexp.Regexp) *regexp.Regexp {
|
|
||||||
return match(`^` + expression(res...).String() + `$`)
|
|
||||||
}
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestResolveNames(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
additionalRegistries []string
|
||||||
|
imageName string
|
||||||
|
expected []string
|
||||||
|
err bool
|
||||||
|
errContains string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "test unqualified images get correctly qualified in order and correct tag",
|
||||||
|
additionalRegistries: []string{"testregistry.com", "registry.access.redhat.com", "docker.io"},
|
||||||
|
imageName: "openshift3/ose-deployer:sometag",
|
||||||
|
expected: []string{"testregistry.com/openshift3/ose-deployer:sometag", "registry.access.redhat.com/openshift3/ose-deployer:sometag", "docker.io/openshift3/ose-deployer:sometag"},
|
||||||
|
err: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test unqualified images get correctly qualified in order and correct digest",
|
||||||
|
additionalRegistries: []string{"testregistry.com", "registry.access.redhat.com", "docker.io"},
|
||||||
|
imageName: "openshift3/ose-deployer@sha256:dc5f67a48da730d67bf4bfb8824ea8a51be26711de090d6d5a1ffff2723168a3",
|
||||||
|
expected: []string{"testregistry.com/openshift3/ose-deployer@sha256:dc5f67a48da730d67bf4bfb8824ea8a51be26711de090d6d5a1ffff2723168a3", "registry.access.redhat.com/openshift3/ose-deployer@sha256:dc5f67a48da730d67bf4bfb8824ea8a51be26711de090d6d5a1ffff2723168a3", "docker.io/openshift3/ose-deployer@sha256:dc5f67a48da730d67bf4bfb8824ea8a51be26711de090d6d5a1ffff2723168a3"},
|
||||||
|
err: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test unqualified images get correctly qualified in order",
|
||||||
|
additionalRegistries: []string{"testregistry.com", "registry.access.redhat.com", "docker.io"},
|
||||||
|
imageName: "openshift3/ose-deployer:latest",
|
||||||
|
expected: []string{"testregistry.com/openshift3/ose-deployer:latest", "registry.access.redhat.com/openshift3/ose-deployer:latest", "docker.io/openshift3/ose-deployer:latest"},
|
||||||
|
err: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test unqualified images get correctly qualified from official library",
|
||||||
|
additionalRegistries: []string{"testregistry.com", "registry.access.redhat.com", "docker.io"},
|
||||||
|
imageName: "nginx:latest",
|
||||||
|
expected: []string{"testregistry.com/nginx:latest", "registry.access.redhat.com/nginx:latest", "docker.io/library/nginx:latest"},
|
||||||
|
err: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test qualified images returns just qualified",
|
||||||
|
additionalRegistries: []string{"testregistry.com", "registry.access.redhat.com", "docker.io"},
|
||||||
|
imageName: "mypersonalregistry.com/nginx:latest",
|
||||||
|
expected: []string{"mypersonalregistry.com/nginx:latest"},
|
||||||
|
err: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test we don't have names w/o registries",
|
||||||
|
imageName: "openshift3/ose-deployer:latest",
|
||||||
|
err: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test we cannot resolve names from an image ID",
|
||||||
|
imageName: "6ad733544a6317992a6fac4eb19fe1df577d4dec7529efec28a5bd0edad0fd30",
|
||||||
|
err: true,
|
||||||
|
errContains: "cannot parse an image ID",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
svc := &imageService{
|
||||||
|
registries: c.additionalRegistries,
|
||||||
|
}
|
||||||
|
names, err := svc.ResolveNames(c.imageName)
|
||||||
|
if !c.err {
|
||||||
|
require.NoError(t, err, c.name)
|
||||||
|
if !reflect.DeepEqual(names, c.expected) {
|
||||||
|
t.Fatalf("Exepected: %v, Got: %v: %q", c.expected, names, c.name)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
require.Error(t, err, c.name)
|
||||||
|
if c.errContains != "" {
|
||||||
|
assert.Contains(t, err.Error(), c.errContains)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/kubernetes-incubator/cri-o/oci"
|
"github.com/kubernetes-incubator/cri-o/oci"
|
||||||
"github.com/kubernetes-incubator/cri-o/utils"
|
"github.com/kubernetes-incubator/cri-o/utils"
|
||||||
|
@ -25,10 +26,15 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Attach prepares a streaming endpoint to attach to a running container.
|
// Attach prepares a streaming endpoint to attach to a running container.
|
||||||
func (s *Server) Attach(ctx context.Context, req *pb.AttachRequest) (*pb.AttachResponse, error) {
|
func (s *Server) Attach(ctx context.Context, req *pb.AttachRequest) (resp *pb.AttachResponse, err error) {
|
||||||
|
const operation = "attach"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
logrus.Debugf("AttachRequest %+v", req)
|
logrus.Debugf("AttachRequest %+v", req)
|
||||||
|
|
||||||
resp, err := s.GetAttach(req)
|
resp, err = s.GetAttach(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to prepare attach endpoint")
|
return nil, fmt.Errorf("unable to prepare attach endpoint")
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -46,29 +47,54 @@ const (
|
||||||
defaultSystemdParent = "system.slice"
|
defaultSystemdParent = "system.slice"
|
||||||
)
|
)
|
||||||
|
|
||||||
func addOCIBindMounts(mountLabel string, containerConfig *pb.ContainerConfig, specgen *generate.Generator) ([]oci.ContainerVolume, error) {
|
type orderedMounts []rspec.Mount
|
||||||
|
|
||||||
|
// Len returns the number of mounts. Used in sorting.
|
||||||
|
func (m orderedMounts) Len() int {
|
||||||
|
return len(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less returns true if the number of parts (a/b/c would be 3 parts) in the
|
||||||
|
// mount indexed by parameter 1 is less than that of the mount indexed by
|
||||||
|
// parameter 2. Used in sorting.
|
||||||
|
func (m orderedMounts) Less(i, j int) bool {
|
||||||
|
return m.parts(i) < m.parts(j)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap swaps two items in an array of mounts. Used in sorting
|
||||||
|
func (m orderedMounts) Swap(i, j int) {
|
||||||
|
m[i], m[j] = m[j], m[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// parts returns the number of parts in the destination of a mount. Used in sorting.
|
||||||
|
func (m orderedMounts) parts(i int) int {
|
||||||
|
return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator))
|
||||||
|
}
|
||||||
|
|
||||||
|
func addOCIBindMounts(mountLabel string, containerConfig *pb.ContainerConfig, specgen *generate.Generator) ([]oci.ContainerVolume, []rspec.Mount, error) {
|
||||||
volumes := []oci.ContainerVolume{}
|
volumes := []oci.ContainerVolume{}
|
||||||
|
ociMounts := []rspec.Mount{}
|
||||||
mounts := containerConfig.GetMounts()
|
mounts := containerConfig.GetMounts()
|
||||||
for _, mount := range mounts {
|
for _, mount := range mounts {
|
||||||
dest := mount.ContainerPath
|
dest := mount.ContainerPath
|
||||||
if dest == "" {
|
if dest == "" {
|
||||||
return nil, fmt.Errorf("Mount.ContainerPath is empty")
|
return nil, nil, fmt.Errorf("Mount.ContainerPath is empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
src := mount.HostPath
|
src := mount.HostPath
|
||||||
if src == "" {
|
if src == "" {
|
||||||
return nil, fmt.Errorf("Mount.HostPath is empty")
|
return nil, nil, fmt.Errorf("Mount.HostPath is empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := os.Stat(src); err != nil && os.IsNotExist(err) {
|
if _, err := os.Stat(src); err != nil && os.IsNotExist(err) {
|
||||||
if err1 := os.MkdirAll(src, 0644); err1 != nil {
|
if err1 := os.MkdirAll(src, 0644); err1 != nil {
|
||||||
return nil, fmt.Errorf("Failed to mkdir %s: %s", src, err)
|
return nil, nil, fmt.Errorf("Failed to mkdir %s: %s", src, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
src, err := resolveSymbolicLink(src)
|
src, err := resolveSymbolicLink(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to resolve symlink %q: %v", src, err)
|
return nil, nil, fmt.Errorf("failed to resolve symlink %q: %v", src, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
options := []string{"rw"}
|
options := []string{"rw"}
|
||||||
|
@ -80,7 +106,7 @@ func addOCIBindMounts(mountLabel string, containerConfig *pb.ContainerConfig, sp
|
||||||
if mount.SelinuxRelabel {
|
if mount.SelinuxRelabel {
|
||||||
// Need a way in kubernetes to determine if the volume is shared or private
|
// Need a way in kubernetes to determine if the volume is shared or private
|
||||||
if err := label.Relabel(src, mountLabel, true); err != nil && err != unix.ENOTSUP {
|
if err := label.Relabel(src, mountLabel, true); err != nil && err != unix.ENOTSUP {
|
||||||
return nil, fmt.Errorf("relabel failed %s: %v", src, err)
|
return nil, nil, fmt.Errorf("relabel failed %s: %v", src, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,45 +116,55 @@ func addOCIBindMounts(mountLabel string, containerConfig *pb.ContainerConfig, sp
|
||||||
Readonly: mount.Readonly,
|
Readonly: mount.Readonly,
|
||||||
})
|
})
|
||||||
|
|
||||||
specgen.AddBindMount(src, dest, options)
|
ociMounts = append(ociMounts, rspec.Mount{
|
||||||
|
Source: src,
|
||||||
|
Destination: dest,
|
||||||
|
Options: options,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return volumes, nil
|
return volumes, ociMounts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func addImageVolumes(rootfs string, s *Server, containerInfo *storage.ContainerInfo, specgen *generate.Generator, mountLabel string) error {
|
func addImageVolumes(rootfs string, s *Server, containerInfo *storage.ContainerInfo, specgen *generate.Generator, mountLabel string) ([]rspec.Mount, error) {
|
||||||
|
mounts := []rspec.Mount{}
|
||||||
for dest := range containerInfo.Config.Config.Volumes {
|
for dest := range containerInfo.Config.Config.Volumes {
|
||||||
fp, err := symlink.FollowSymlinkInScope(filepath.Join(rootfs, dest), rootfs)
|
fp, err := symlink.FollowSymlinkInScope(filepath.Join(rootfs, dest), rootfs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
switch s.config.ImageVolumes {
|
switch s.config.ImageVolumes {
|
||||||
case libkpod.ImageVolumesMkdir:
|
case libkpod.ImageVolumesMkdir:
|
||||||
if err1 := os.MkdirAll(fp, 0644); err1 != nil {
|
if err1 := os.MkdirAll(fp, 0644); err1 != nil {
|
||||||
return err1
|
return nil, err1
|
||||||
}
|
}
|
||||||
case libkpod.ImageVolumesBind:
|
case libkpod.ImageVolumesBind:
|
||||||
volumeDirName := stringid.GenerateNonCryptoID()
|
volumeDirName := stringid.GenerateNonCryptoID()
|
||||||
src := filepath.Join(containerInfo.RunDir, "mounts", volumeDirName)
|
src := filepath.Join(containerInfo.RunDir, "mounts", volumeDirName)
|
||||||
if err1 := os.MkdirAll(src, 0644); err1 != nil {
|
if err1 := os.MkdirAll(src, 0644); err1 != nil {
|
||||||
return err1
|
return nil, err1
|
||||||
}
|
}
|
||||||
// Label the source with the sandbox selinux mount label
|
// Label the source with the sandbox selinux mount label
|
||||||
if mountLabel != "" {
|
if mountLabel != "" {
|
||||||
if err1 := label.Relabel(src, mountLabel, true); err1 != nil && err1 != unix.ENOTSUP {
|
if err1 := label.Relabel(src, mountLabel, true); err1 != nil && err1 != unix.ENOTSUP {
|
||||||
return fmt.Errorf("relabel failed %s: %v", src, err1)
|
return nil, fmt.Errorf("relabel failed %s: %v", src, err1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Debugf("Adding bind mounted volume: %s to %s", src, dest)
|
logrus.Debugf("Adding bind mounted volume: %s to %s", src, dest)
|
||||||
specgen.AddBindMount(src, dest, []string{"rw"})
|
mounts = append(mounts, rspec.Mount{
|
||||||
|
Source: src,
|
||||||
|
Destination: dest,
|
||||||
|
Options: []string{"rw"},
|
||||||
|
})
|
||||||
|
|
||||||
case libkpod.ImageVolumesIgnore:
|
case libkpod.ImageVolumesIgnore:
|
||||||
logrus.Debugf("Ignoring volume %v", dest)
|
logrus.Debugf("Ignoring volume %v", dest)
|
||||||
default:
|
default:
|
||||||
logrus.Fatalf("Unrecognized image volumes setting")
|
logrus.Fatalf("Unrecognized image volumes setting")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return mounts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolveSymbolicLink resolves a possbile symlink path. If the path is a symlink, returns resolved
|
// resolveSymbolicLink resolves a possbile symlink path. If the path is a symlink, returns resolved
|
||||||
|
@ -384,8 +420,23 @@ func ensureSaneLogPath(logPath string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// addSecretsBindMounts mounts user defined secrets to the container
|
||||||
|
func addSecretsBindMounts(mountLabel, ctrRunDir string, defaultMounts []string, specgen generate.Generator) ([]rspec.Mount, error) {
|
||||||
|
containerMounts := specgen.Spec().Mounts
|
||||||
|
mounts, err := secretMounts(defaultMounts, mountLabel, ctrRunDir, containerMounts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return mounts, nil
|
||||||
|
}
|
||||||
|
|
||||||
// CreateContainer creates a new container in specified PodSandbox
|
// CreateContainer creates a new container in specified PodSandbox
|
||||||
func (s *Server) CreateContainer(ctx context.Context, req *pb.CreateContainerRequest) (res *pb.CreateContainerResponse, err error) {
|
func (s *Server) CreateContainer(ctx context.Context, req *pb.CreateContainerRequest) (res *pb.CreateContainerResponse, err error) {
|
||||||
|
const operation = "create_container"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
logrus.Debugf("CreateContainerRequest %+v", req)
|
logrus.Debugf("CreateContainerRequest %+v", req)
|
||||||
|
|
||||||
s.updateLock.RLock()
|
s.updateLock.RLock()
|
||||||
|
@ -549,7 +600,7 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
containerVolumes, err := addOCIBindMounts(mountLabel, containerConfig, &specgen)
|
containerVolumes, ociMounts, err := addOCIBindMounts(mountLabel, containerConfig, &specgen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -569,6 +620,10 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
||||||
|
|
||||||
labels := containerConfig.GetLabels()
|
labels := containerConfig.GetLabels()
|
||||||
|
|
||||||
|
if err := validateLabels(labels); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
metadata := containerConfig.GetMetadata()
|
metadata := containerConfig.GetMetadata()
|
||||||
|
|
||||||
kubeAnnotations := containerConfig.GetAnnotations()
|
kubeAnnotations := containerConfig.GetAnnotations()
|
||||||
|
@ -733,6 +788,7 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
||||||
"/proc/timer_list",
|
"/proc/timer_list",
|
||||||
"/proc/timer_stats",
|
"/proc/timer_stats",
|
||||||
"/proc/sched_debug",
|
"/proc/sched_debug",
|
||||||
|
"/proc/scsi",
|
||||||
"/sys/firmware",
|
"/sys/firmware",
|
||||||
} {
|
} {
|
||||||
specgen.AddLinuxMaskedPaths(mp)
|
specgen.AddLinuxMaskedPaths(mp)
|
||||||
|
@ -756,10 +812,20 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
||||||
logrus.Debugf("pod container state %+v", podInfraState)
|
logrus.Debugf("pod container state %+v", podInfraState)
|
||||||
|
|
||||||
ipcNsPath := fmt.Sprintf("/proc/%d/ns/ipc", podInfraState.Pid)
|
ipcNsPath := fmt.Sprintf("/proc/%d/ns/ipc", podInfraState.Pid)
|
||||||
if err := specgen.AddOrReplaceLinuxNamespace("ipc", ipcNsPath); err != nil {
|
if err := specgen.AddOrReplaceLinuxNamespace(string(rspec.IPCNamespace), ipcNsPath); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
utsNsPath := fmt.Sprintf("/proc/%d/ns/uts", podInfraState.Pid)
|
||||||
|
if err := specgen.AddOrReplaceLinuxNamespace(string(rspec.UTSNamespace), utsNsPath); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not share pid ns for now
|
||||||
|
if containerConfig.GetLinux().GetSecurityContext().GetNamespaceOptions().GetHostPid() {
|
||||||
|
specgen.RemoveLinuxNamespace(string(rspec.PIDNamespace))
|
||||||
|
}
|
||||||
|
|
||||||
netNsPath := sb.NetNsPath()
|
netNsPath := sb.NetNsPath()
|
||||||
if netNsPath == "" {
|
if netNsPath == "" {
|
||||||
// The sandbox does not have a permanent namespace,
|
// The sandbox does not have a permanent namespace,
|
||||||
|
@ -767,7 +833,7 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
||||||
netNsPath = fmt.Sprintf("/proc/%d/ns/net", podInfraState.Pid)
|
netNsPath = fmt.Sprintf("/proc/%d/ns/net", podInfraState.Pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := specgen.AddOrReplaceLinuxNamespace("network", netNsPath); err != nil {
|
if err := specgen.AddOrReplaceLinuxNamespace(string(rspec.NetworkNamespace), netNsPath); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -782,8 +848,7 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
||||||
}
|
}
|
||||||
images, err := s.StorageImageServer().ResolveNames(image)
|
images, err := s.StorageImageServer().ResolveNames(image)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This means we got an image ID
|
if err == storage.ErrCannotParseImageID {
|
||||||
if strings.Contains(err.Error(), "cannot specify 64-byte hexadecimal strings") {
|
|
||||||
images = append(images, image)
|
images = append(images, image)
|
||||||
} else {
|
} else {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -856,7 +921,9 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
||||||
specgen.AddBindMount("/etc/hosts", "/etc/hosts", options)
|
specgen.AddBindMount("/etc/hosts", "/etc/hosts", options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set hostname and add env for hostname
|
||||||
specgen.SetHostname(sb.Hostname())
|
specgen.SetHostname(sb.Hostname())
|
||||||
|
specgen.AddProcessEnv("HOSTNAME", sb.Hostname())
|
||||||
|
|
||||||
specgen.AddAnnotation(annotations.Name, containerName)
|
specgen.AddAnnotation(annotations.Name, containerName)
|
||||||
specgen.AddAnnotation(annotations.ContainerID, containerID)
|
specgen.AddAnnotation(annotations.ContainerID, containerID)
|
||||||
|
@ -928,7 +995,8 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add image volumes
|
// Add image volumes
|
||||||
if err := addImageVolumes(mountPoint, s, &containerInfo, &specgen, mountLabel); err != nil {
|
volumeMounts, err := addImageVolumes(mountPoint, s, &containerInfo, &specgen, mountLabel)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -938,30 +1006,46 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
||||||
}
|
}
|
||||||
specgen.SetProcessArgs(processArgs)
|
specgen.SetProcessArgs(processArgs)
|
||||||
|
|
||||||
// Add environment variables from CRI and image config
|
envs := []string{}
|
||||||
envs := containerConfig.GetEnvs()
|
if containerConfig.GetEnvs() == nil && containerImageConfig != nil {
|
||||||
if envs != nil {
|
envs = containerImageConfig.Config.Env
|
||||||
for _, item := range envs {
|
} else {
|
||||||
key := item.Key
|
for _, item := range containerConfig.GetEnvs() {
|
||||||
value := item.Value
|
if item.GetKey() == "" {
|
||||||
if key == "" {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
specgen.AddProcessEnv(key, value)
|
envs = append(envs, item.GetKey()+"="+item.GetValue())
|
||||||
|
}
|
||||||
|
if containerImageConfig != nil {
|
||||||
|
for _, imageEnv := range containerImageConfig.Config.Env {
|
||||||
|
var found bool
|
||||||
|
parts := strings.SplitN(imageEnv, "=", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
imageEnvKey := parts[0]
|
||||||
|
if imageEnvKey == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, kubeEnv := range envs {
|
||||||
|
kubeEnvKey := strings.SplitN(kubeEnv, "=", 2)[0]
|
||||||
|
if kubeEnvKey == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if imageEnvKey == kubeEnvKey {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
envs = append(envs, imageEnv)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if containerImageConfig != nil {
|
for _, e := range envs {
|
||||||
for _, item := range containerImageConfig.Config.Env {
|
parts := strings.SplitN(e, "=", 2)
|
||||||
parts := strings.SplitN(item, "=", 2)
|
specgen.AddProcessEnv(parts[0], parts[1])
|
||||||
if len(parts) != 2 {
|
|
||||||
return nil, fmt.Errorf("invalid env from image: %s", item)
|
|
||||||
}
|
|
||||||
|
|
||||||
if parts[0] == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
specgen.AddProcessEnv(parts[0], parts[1])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set working directory
|
// Set working directory
|
||||||
|
@ -978,6 +1062,32 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
||||||
containerCwd = runtimeCwd
|
containerCwd = runtimeCwd
|
||||||
}
|
}
|
||||||
specgen.SetProcessCwd(containerCwd)
|
specgen.SetProcessCwd(containerCwd)
|
||||||
|
if err := setupWorkingDirectory(mountPoint, mountLabel, containerCwd); err != nil {
|
||||||
|
if err1 := s.StorageRuntimeServer().StopContainer(containerID); err1 != nil {
|
||||||
|
return nil, fmt.Errorf("can't umount container after cwd error %v: %v", err, err1)
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var secretMounts []rspec.Mount
|
||||||
|
if len(s.config.DefaultMounts) > 0 {
|
||||||
|
var err error
|
||||||
|
secretMounts, err = addSecretsBindMounts(mountLabel, containerInfo.RunDir, s.config.DefaultMounts, specgen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to mount secrets: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mounts := []rspec.Mount{}
|
||||||
|
mounts = append(mounts, ociMounts...)
|
||||||
|
mounts = append(mounts, volumeMounts...)
|
||||||
|
mounts = append(mounts, secretMounts...)
|
||||||
|
|
||||||
|
sort.Sort(orderedMounts(mounts))
|
||||||
|
|
||||||
|
for _, m := range mounts {
|
||||||
|
specgen.AddBindMount(m.Source, m.Destination, m.Options)
|
||||||
|
}
|
||||||
|
|
||||||
if err := s.setupOCIHooks(&specgen, sb, containerConfig, processArgs[0]); err != nil {
|
if err := s.setupOCIHooks(&specgen, sb, containerConfig, processArgs[0]); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -1013,6 +1123,7 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
container.SetSpec(specgen.Spec())
|
||||||
container.SetMountPoint(mountPoint)
|
container.SetMountPoint(mountPoint)
|
||||||
|
|
||||||
for _, cv := range containerVolumes {
|
for _, cv := range containerVolumes {
|
||||||
|
@ -1135,3 +1246,19 @@ func clearReadOnly(m *rspec.Mount) {
|
||||||
}
|
}
|
||||||
m.Options = opt
|
m.Options = opt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupWorkingDirectory(rootfs, mountLabel, containerCwd string) error {
|
||||||
|
fp, err := symlink.FollowSymlinkInScope(filepath.Join(rootfs, containerCwd), rootfs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(fp, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if mountLabel != "" {
|
||||||
|
if err1 := label.Relabel(fp, mountLabel, true); err1 != nil && err1 != unix.ENOTSUP {
|
||||||
|
return fmt.Errorf("relabel failed %s: %v", fp, err1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/pools"
|
"github.com/docker/docker/pkg/pools"
|
||||||
"github.com/kubernetes-incubator/cri-o/oci"
|
"github.com/kubernetes-incubator/cri-o/oci"
|
||||||
|
@ -18,10 +21,16 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Exec prepares a streaming endpoint to execute a command in the container.
|
// Exec prepares a streaming endpoint to execute a command in the container.
|
||||||
func (s *Server) Exec(ctx context.Context, req *pb.ExecRequest) (*pb.ExecResponse, error) {
|
func (s *Server) Exec(ctx context.Context, req *pb.ExecRequest) (resp *pb.ExecResponse, err error) {
|
||||||
|
const operation = "exec"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
|
|
||||||
logrus.Debugf("ExecRequest %+v", req)
|
logrus.Debugf("ExecRequest %+v", req)
|
||||||
|
|
||||||
resp, err := s.GetExec(req)
|
resp, err = s.GetExec(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to prepare exec endpoint")
|
return nil, fmt.Errorf("unable to prepare exec endpoint")
|
||||||
}
|
}
|
||||||
|
@ -46,12 +55,29 @@ func (ss streamService) Exec(containerID string, cmd []string, stdin io.Reader,
|
||||||
return fmt.Errorf("container is not created or running")
|
return fmt.Errorf("container is not created or running")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f, err := ioutil.TempFile("", "exec-process")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(f.Name())
|
||||||
|
|
||||||
|
pspec := c.Spec().Process
|
||||||
|
pspec.Args = cmd
|
||||||
|
processJSON, err := json.Marshal(pspec)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ioutil.WriteFile(f.Name(), processJSON, 0644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
args := []string{"exec"}
|
args := []string{"exec"}
|
||||||
if tty {
|
if tty {
|
||||||
args = append(args, "-t")
|
args = append(args, "-t")
|
||||||
}
|
}
|
||||||
|
args = append(args, "-p", f.Name())
|
||||||
args = append(args, c.ID())
|
args = append(args, c.ID())
|
||||||
args = append(args, cmd...)
|
|
||||||
execCmd := exec.Command(ss.runtimeServer.Runtime().Path(c), args...)
|
execCmd := exec.Command(ss.runtimeServer.Runtime().Path(c), args...)
|
||||||
var cmdErr error
|
var cmdErr error
|
||||||
if tty {
|
if tty {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/kubernetes-incubator/cri-o/oci"
|
"github.com/kubernetes-incubator/cri-o/oci"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
@ -10,7 +11,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExecSync runs a command in a container synchronously.
|
// ExecSync runs a command in a container synchronously.
|
||||||
func (s *Server) ExecSync(ctx context.Context, req *pb.ExecSyncRequest) (*pb.ExecSyncResponse, error) {
|
func (s *Server) ExecSync(ctx context.Context, req *pb.ExecSyncRequest) (resp *pb.ExecSyncResponse, err error) {
|
||||||
|
const operation = "exec_sync"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
logrus.Debugf("ExecSyncRequest %+v", req)
|
logrus.Debugf("ExecSyncRequest %+v", req)
|
||||||
c, err := s.GetContainerFromRequest(req.ContainerId)
|
c, err := s.GetContainerFromRequest(req.ContainerId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -35,7 +41,7 @@ func (s *Server) ExecSync(ctx context.Context, req *pb.ExecSyncRequest) (*pb.Exe
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
resp := &pb.ExecSyncResponse{
|
resp = &pb.ExecSyncResponse{
|
||||||
Stdout: execResp.Stdout,
|
Stdout: execResp.Stdout,
|
||||||
Stderr: execResp.Stderr,
|
Stderr: execResp.Stderr,
|
||||||
ExitCode: execResp.ExitCode,
|
ExitCode: execResp.ExitCode,
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/kubernetes-incubator/cri-o/oci"
|
"github.com/kubernetes-incubator/cri-o/oci"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
@ -27,41 +29,49 @@ func filterContainer(c *pb.Container, filter *pb.ContainerFilter) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListContainers lists all containers by filters.
|
// ListContainers lists all containers by filters.
|
||||||
func (s *Server) ListContainers(ctx context.Context, req *pb.ListContainersRequest) (*pb.ListContainersResponse, error) {
|
func (s *Server) ListContainers(ctx context.Context, req *pb.ListContainersRequest) (resp *pb.ListContainersResponse, err error) {
|
||||||
|
const operation = "list_containers"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
logrus.Debugf("ListContainersRequest %+v", req)
|
logrus.Debugf("ListContainersRequest %+v", req)
|
||||||
|
|
||||||
var ctrs []*pb.Container
|
var ctrs []*pb.Container
|
||||||
filter := req.Filter
|
filter := req.GetFilter()
|
||||||
ctrList, err := s.ContainerServer.ListContainers()
|
ctrList, err := s.ContainerServer.ListContainers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter using container id and pod id first.
|
if filter != nil {
|
||||||
if filter.Id != "" {
|
// Filter using container id and pod id first.
|
||||||
id, err := s.CtrIDIndex().Get(filter.Id)
|
if filter.Id != "" {
|
||||||
if err != nil {
|
id, err := s.CtrIDIndex().Get(filter.Id)
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
return nil, err
|
||||||
c := s.ContainerServer.GetContainer(id)
|
|
||||||
if c != nil {
|
|
||||||
if filter.PodSandboxId != "" {
|
|
||||||
if c.Sandbox() == filter.PodSandboxId {
|
|
||||||
ctrList = []*oci.Container{c}
|
|
||||||
} else {
|
|
||||||
ctrList = []*oci.Container{}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
ctrList = []*oci.Container{c}
|
|
||||||
}
|
}
|
||||||
}
|
c := s.ContainerServer.GetContainer(id)
|
||||||
} else {
|
if c != nil {
|
||||||
if filter.PodSandboxId != "" {
|
if filter.PodSandboxId != "" {
|
||||||
pod := s.ContainerServer.GetSandbox(filter.PodSandboxId)
|
if c.Sandbox() == filter.PodSandboxId {
|
||||||
if pod == nil {
|
ctrList = []*oci.Container{c}
|
||||||
ctrList = []*oci.Container{}
|
} else {
|
||||||
} else {
|
ctrList = []*oci.Container{}
|
||||||
ctrList = pod.Containers().List()
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ctrList = []*oci.Container{c}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if filter.PodSandboxId != "" {
|
||||||
|
pod := s.ContainerServer.GetSandbox(filter.PodSandboxId)
|
||||||
|
if pod == nil {
|
||||||
|
ctrList = []*oci.Container{}
|
||||||
|
} else {
|
||||||
|
ctrList = pod.Containers().List()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,7 +111,7 @@ func (s *Server) ListContainers(ctx context.Context, req *pb.ListContainersReque
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := &pb.ListContainersResponse{
|
resp = &pb.ListContainersResponse{
|
||||||
Containers: ctrs,
|
Containers: ctrs,
|
||||||
}
|
}
|
||||||
logrus.Debugf("ListContainersResponse: %+v", resp)
|
logrus.Debugf("ListContainersResponse: %+v", resp)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/pools"
|
"github.com/docker/docker/pkg/pools"
|
||||||
"github.com/kubernetes-incubator/cri-o/oci"
|
"github.com/kubernetes-incubator/cri-o/oci"
|
||||||
|
@ -15,11 +16,15 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// PortForward prepares a streaming endpoint to forward ports from a PodSandbox.
|
// PortForward prepares a streaming endpoint to forward ports from a PodSandbox.
|
||||||
func (s *Server) PortForward(ctx context.Context, req *pb.PortForwardRequest) (*pb.PortForwardResponse, error) {
|
func (s *Server) PortForward(ctx context.Context, req *pb.PortForwardRequest) (resp *pb.PortForwardResponse, err error) {
|
||||||
|
const operation = "port_forward"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
logrus.Debugf("PortForwardRequest %+v", req)
|
logrus.Debugf("PortForwardRequest %+v", req)
|
||||||
|
|
||||||
resp, err := s.GetPortForward(req)
|
resp, err = s.GetPortForward(req)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to prepare portforward endpoint")
|
return nil, fmt.Errorf("unable to prepare portforward endpoint")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||||
|
@ -8,13 +10,20 @@ import (
|
||||||
|
|
||||||
// RemoveContainer removes the container. If the container is running, the container
|
// RemoveContainer removes the container. If the container is running, the container
|
||||||
// should be force removed.
|
// should be force removed.
|
||||||
func (s *Server) RemoveContainer(ctx context.Context, req *pb.RemoveContainerRequest) (*pb.RemoveContainerResponse, error) {
|
func (s *Server) RemoveContainer(ctx context.Context, req *pb.RemoveContainerRequest) (resp *pb.RemoveContainerResponse, err error) {
|
||||||
_, err := s.ContainerServer.Remove(req.ContainerId, true)
|
const operation = "remove_container"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
|
logrus.Debugf("RemoveContainerRequest: %+v", req)
|
||||||
|
|
||||||
|
_, err = s.ContainerServer.Remove(ctx, req.ContainerId, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := &pb.RemoveContainerResponse{}
|
resp = &pb.RemoveContainerResponse{}
|
||||||
logrus.Debugf("RemoveContainerResponse: %+v", resp)
|
logrus.Debugf("RemoveContainerResponse: %+v", resp)
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/kubernetes-incubator/cri-o/oci"
|
"github.com/kubernetes-incubator/cri-o/oci"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
@ -10,7 +11,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// StartContainer starts the container.
|
// StartContainer starts the container.
|
||||||
func (s *Server) StartContainer(ctx context.Context, req *pb.StartContainerRequest) (*pb.StartContainerResponse, error) {
|
func (s *Server) StartContainer(ctx context.Context, req *pb.StartContainerRequest) (resp *pb.StartContainerResponse, err error) {
|
||||||
|
const operation = "start_container"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
logrus.Debugf("StartContainerRequest %+v", req)
|
logrus.Debugf("StartContainerRequest %+v", req)
|
||||||
c, err := s.GetContainerFromRequest(req.ContainerId)
|
c, err := s.GetContainerFromRequest(req.ContainerId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -37,7 +43,7 @@ func (s *Server) StartContainer(ctx context.Context, req *pb.StartContainerReque
|
||||||
return nil, fmt.Errorf("failed to start container %s: %v", c.ID(), err)
|
return nil, fmt.Errorf("failed to start container %s: %v", c.ID(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := &pb.StartContainerResponse{}
|
resp = &pb.StartContainerResponse{}
|
||||||
logrus.Debugf("StartContainerResponse %+v", resp)
|
logrus.Debugf("StartContainerResponse %+v", resp)
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||||
|
@ -9,6 +10,11 @@ import (
|
||||||
|
|
||||||
// ContainerStats returns stats of the container. If the container does not
|
// ContainerStats returns stats of the container. If the container does not
|
||||||
// exist, the call returns an error.
|
// exist, the call returns an error.
|
||||||
func (s *Server) ContainerStats(ctx context.Context, req *pb.ContainerStatsRequest) (*pb.ContainerStatsResponse, error) {
|
func (s *Server) ContainerStats(ctx context.Context, req *pb.ContainerStatsRequest) (resp *pb.ContainerStatsResponse, err error) {
|
||||||
|
const operation = "container_stats"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
return nil, fmt.Errorf("not implemented")
|
return nil, fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,18 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ListContainerStats returns stats of all running containers.
|
// ListContainerStats returns stats of all running containers.
|
||||||
func (s *Server) ListContainerStats(ctx context.Context, req *pb.ListContainerStatsRequest) (*pb.ListContainerStatsResponse, error) {
|
func (s *Server) ListContainerStats(ctx context.Context, req *pb.ListContainerStatsRequest) (resp *pb.ListContainerStatsResponse, err error) {
|
||||||
|
const operation = "list_container_stats"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
return nil, fmt.Errorf("not implemented")
|
return nil, fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/kubernetes-incubator/cri-o/oci"
|
"github.com/kubernetes-incubator/cri-o/oci"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
@ -14,7 +16,12 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// ContainerStatus returns status of the container.
|
// ContainerStatus returns status of the container.
|
||||||
func (s *Server) ContainerStatus(ctx context.Context, req *pb.ContainerStatusRequest) (*pb.ContainerStatusResponse, error) {
|
func (s *Server) ContainerStatus(ctx context.Context, req *pb.ContainerStatusRequest) (resp *pb.ContainerStatusResponse, err error) {
|
||||||
|
const operation = "container_status"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
logrus.Debugf("ContainerStatusRequest %+v", req)
|
logrus.Debugf("ContainerStatusRequest %+v", req)
|
||||||
c, err := s.GetContainerFromRequest(req.ContainerId)
|
c, err := s.GetContainerFromRequest(req.ContainerId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -22,7 +29,7 @@ func (s *Server) ContainerStatus(ctx context.Context, req *pb.ContainerStatusReq
|
||||||
}
|
}
|
||||||
|
|
||||||
containerID := c.ID()
|
containerID := c.ID()
|
||||||
resp := &pb.ContainerStatusResponse{
|
resp = &pb.ContainerStatusResponse{
|
||||||
Status: &pb.ContainerStatus{
|
Status: &pb.ContainerStatus{
|
||||||
Id: containerID,
|
Id: containerID,
|
||||||
Metadata: c.Metadata(),
|
Metadata: c.Metadata(),
|
||||||
|
|
|
@ -1,19 +1,28 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StopContainer stops a running container with a grace period (i.e., timeout).
|
// StopContainer stops a running container with a grace period (i.e., timeout).
|
||||||
func (s *Server) StopContainer(ctx context.Context, req *pb.StopContainerRequest) (*pb.StopContainerResponse, error) {
|
func (s *Server) StopContainer(ctx context.Context, req *pb.StopContainerRequest) (resp *pb.StopContainerResponse, err error) {
|
||||||
_, err := s.ContainerServer.ContainerStop(req.ContainerId, req.Timeout)
|
const operation = "stop_container"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
|
logrus.Debugf("StopContainerRequest %+v", req)
|
||||||
|
|
||||||
|
_, err = s.ContainerServer.ContainerStop(ctx, req.ContainerId, req.Timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := &pb.StopContainerResponse{}
|
resp = &pb.StopContainerResponse{}
|
||||||
logrus.Debugf("StopContainerResponse %s: %+v", req.ContainerId, resp)
|
logrus.Debugf("StopContainerResponse %s: %+v", req.ContainerId, resp)
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,19 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UpdateRuntimeConfig updates the configuration of a running container.
|
// UpdateRuntimeConfig updates the configuration of a running container.
|
||||||
func (s *Server) UpdateRuntimeConfig(ctx context.Context, req *pb.UpdateRuntimeConfigRequest) (*pb.UpdateRuntimeConfigResponse, error) {
|
func (s *Server) UpdateRuntimeConfig(ctx context.Context, req *pb.UpdateRuntimeConfigRequest) (resp *pb.UpdateRuntimeConfigResponse, err error) {
|
||||||
|
const operation = "update_runtime_config"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
|
|
||||||
return &pb.UpdateRuntimeConfigResponse{}, nil
|
return &pb.UpdateRuntimeConfigResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,19 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ImageFsInfo returns information of the filesystem that is used to store images.
|
// ImageFsInfo returns information of the filesystem that is used to store images.
|
||||||
func (s *Server) ImageFsInfo(ctx context.Context, req *pb.ImageFsInfoRequest) (*pb.ImageFsInfoResponse, error) {
|
func (s *Server) ImageFsInfo(ctx context.Context, req *pb.ImageFsInfoRequest) (resp *pb.ImageFsInfoResponse, err error) {
|
||||||
|
const operation = "image_fs_info"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
|
|
||||||
return nil, fmt.Errorf("not implemented")
|
return nil, fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,21 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ListImages lists existing images.
|
// ListImages lists existing images.
|
||||||
func (s *Server) ListImages(ctx context.Context, req *pb.ListImagesRequest) (*pb.ListImagesResponse, error) {
|
func (s *Server) ListImages(ctx context.Context, req *pb.ListImagesRequest) (resp *pb.ListImagesResponse, err error) {
|
||||||
|
const operation = "list_images"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
|
|
||||||
logrus.Debugf("ListImagesRequest: %+v", req)
|
logrus.Debugf("ListImagesRequest: %+v", req)
|
||||||
filter := ""
|
filter := ""
|
||||||
reqFilter := req.GetFilter()
|
reqFilter := req.GetFilter()
|
||||||
|
@ -21,21 +29,21 @@ func (s *Server) ListImages(ctx context.Context, req *pb.ListImagesRequest) (*pb
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
response := pb.ListImagesResponse{}
|
resp = &pb.ListImagesResponse{}
|
||||||
for _, result := range results {
|
for _, result := range results {
|
||||||
if result.Size != nil {
|
if result.Size != nil {
|
||||||
response.Images = append(response.Images, &pb.Image{
|
resp.Images = append(resp.Images, &pb.Image{
|
||||||
Id: result.ID,
|
Id: result.ID,
|
||||||
RepoTags: result.Names,
|
RepoTags: result.Names,
|
||||||
Size_: *result.Size,
|
Size_: *result.Size,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
response.Images = append(response.Images, &pb.Image{
|
resp.Images = append(resp.Images, &pb.Image{
|
||||||
Id: result.ID,
|
Id: result.ID,
|
||||||
RepoTags: result.Names,
|
RepoTags: result.Names,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logrus.Debugf("ListImagesResponse: %+v", response)
|
logrus.Debugf("ListImagesResponse: %+v", resp)
|
||||||
return &response, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,16 +3,24 @@ package server
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/containers/image/copy"
|
"github.com/containers/image/copy"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
|
"github.com/kubernetes-incubator/cri-o/pkg/storage"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PullImage pulls a image with authentication config.
|
// PullImage pulls a image with authentication config.
|
||||||
func (s *Server) PullImage(ctx context.Context, req *pb.PullImageRequest) (*pb.PullImageResponse, error) {
|
func (s *Server) PullImage(ctx context.Context, req *pb.PullImageRequest) (resp *pb.PullImageResponse, err error) {
|
||||||
|
const operation = "pull_image"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
|
|
||||||
logrus.Debugf("PullImageRequest: %+v", req)
|
logrus.Debugf("PullImageRequest: %+v", req)
|
||||||
// TODO: what else do we need here? (Signatures when the story isn't just pulling from docker://)
|
// TODO: what else do we need here? (Signatures when the story isn't just pulling from docker://)
|
||||||
image := ""
|
image := ""
|
||||||
|
@ -24,7 +32,6 @@ func (s *Server) PullImage(ctx context.Context, req *pb.PullImageRequest) (*pb.P
|
||||||
var (
|
var (
|
||||||
images []string
|
images []string
|
||||||
pulled string
|
pulled string
|
||||||
err error
|
|
||||||
)
|
)
|
||||||
images, err = s.StorageImageServer().ResolveNames(image)
|
images, err = s.StorageImageServer().ResolveNames(image)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -67,11 +74,23 @@ func (s *Server) PullImage(ctx context.Context, req *pb.PullImageRequest) (*pb.P
|
||||||
}
|
}
|
||||||
|
|
||||||
// let's be smart, docker doesn't repull if image already exists.
|
// let's be smart, docker doesn't repull if image already exists.
|
||||||
_, err = s.StorageImageServer().ImageStatus(s.ImageContext(), img)
|
var storedImage *storage.ImageResult
|
||||||
|
storedImage, err = s.StorageImageServer().ImageStatus(s.ImageContext(), img)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
logrus.Debugf("image %s already in store, skipping pull", img)
|
tmpImg, err := s.StorageImageServer().PrepareImage(s.ImageContext(), img, options)
|
||||||
pulled = img
|
if err == nil {
|
||||||
break
|
tmpImgConfigDigest := tmpImg.ConfigInfo().Digest
|
||||||
|
if tmpImgConfigDigest.String() == "" {
|
||||||
|
// this means we are playing with a schema1 image, in which
|
||||||
|
// case, we're going to repull the image in any case
|
||||||
|
logrus.Debugf("image config digest is empty, re-pulling image")
|
||||||
|
} else if tmpImgConfigDigest.String() == storedImage.ConfigDigest.String() {
|
||||||
|
logrus.Debugf("image %s already in store, skipping pull", img)
|
||||||
|
pulled = img
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logrus.Debugf("image in store has different ID, re-pulling %s", img)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = s.StorageImageServer().PullImage(s.ImageContext(), img, options)
|
_, err = s.StorageImageServer().PullImage(s.ImageContext(), img, options)
|
||||||
|
@ -85,7 +104,7 @@ func (s *Server) PullImage(ctx context.Context, req *pb.PullImageRequest) (*pb.P
|
||||||
if pulled == "" && err != nil {
|
if pulled == "" && err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
resp := &pb.PullImageResponse{
|
resp = &pb.PullImageResponse{
|
||||||
ImageRef: pulled,
|
ImageRef: pulled,
|
||||||
}
|
}
|
||||||
logrus.Debugf("PullImageResponse: %+v", resp)
|
logrus.Debugf("PullImageResponse: %+v", resp)
|
||||||
|
|
|
@ -2,15 +2,22 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"time"
|
||||||
|
|
||||||
|
"github.com/kubernetes-incubator/cri-o/pkg/storage"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RemoveImage removes the image.
|
// RemoveImage removes the image.
|
||||||
func (s *Server) RemoveImage(ctx context.Context, req *pb.RemoveImageRequest) (*pb.RemoveImageResponse, error) {
|
func (s *Server) RemoveImage(ctx context.Context, req *pb.RemoveImageRequest) (resp *pb.RemoveImageResponse, err error) {
|
||||||
|
const operation = "remove_image"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
|
|
||||||
logrus.Debugf("RemoveImageRequest: %+v", req)
|
logrus.Debugf("RemoveImageRequest: %+v", req)
|
||||||
image := ""
|
image := ""
|
||||||
img := req.GetImage()
|
img := req.GetImage()
|
||||||
|
@ -22,13 +29,11 @@ func (s *Server) RemoveImage(ctx context.Context, req *pb.RemoveImageRequest) (*
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
images []string
|
images []string
|
||||||
err error
|
|
||||||
deleted bool
|
deleted bool
|
||||||
)
|
)
|
||||||
images, err = s.StorageImageServer().ResolveNames(image)
|
images, err = s.StorageImageServer().ResolveNames(image)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This means we got an image ID
|
if err == storage.ErrCannotParseImageID {
|
||||||
if strings.Contains(err.Error(), "cannot specify 64-byte hexadecimal strings") {
|
|
||||||
images = append(images, image)
|
images = append(images, image)
|
||||||
} else {
|
} else {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -46,7 +51,7 @@ func (s *Server) RemoveImage(ctx context.Context, req *pb.RemoveImageRequest) (*
|
||||||
if !deleted && err != nil {
|
if !deleted && err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
resp := &pb.RemoveImageResponse{}
|
resp = &pb.RemoveImageResponse{}
|
||||||
logrus.Debugf("RemoveImageResponse: %+v", resp)
|
logrus.Debugf("RemoveImageResponse: %+v", resp)
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,10 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"time"
|
||||||
|
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
|
pkgstorage "github.com/kubernetes-incubator/cri-o/pkg/storage"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
@ -12,7 +13,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// ImageStatus returns the status of the image.
|
// ImageStatus returns the status of the image.
|
||||||
func (s *Server) ImageStatus(ctx context.Context, req *pb.ImageStatusRequest) (*pb.ImageStatusResponse, error) {
|
func (s *Server) ImageStatus(ctx context.Context, req *pb.ImageStatusRequest) (resp *pb.ImageStatusResponse, err error) {
|
||||||
|
const operation = "image_status"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
|
|
||||||
logrus.Debugf("ImageStatusRequest: %+v", req)
|
logrus.Debugf("ImageStatusRequest: %+v", req)
|
||||||
image := ""
|
image := ""
|
||||||
img := req.GetImage()
|
img := req.GetImage()
|
||||||
|
@ -24,8 +31,7 @@ func (s *Server) ImageStatus(ctx context.Context, req *pb.ImageStatusRequest) (*
|
||||||
}
|
}
|
||||||
images, err := s.StorageImageServer().ResolveNames(image)
|
images, err := s.StorageImageServer().ResolveNames(image)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This means we got an image ID
|
if err == pkgstorage.ErrCannotParseImageID {
|
||||||
if strings.Contains(err.Error(), "cannot specify 64-byte hexadecimal strings") {
|
|
||||||
images = append(images, image)
|
images = append(images, image)
|
||||||
} else {
|
} else {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -40,7 +46,7 @@ func (s *Server) ImageStatus(ctx context.Context, req *pb.ImageStatusRequest) (*
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
resp := &pb.ImageStatusResponse{
|
resp = &pb.ImageStatusResponse{
|
||||||
Image: &pb.Image{
|
Image: &pb.Image{
|
||||||
Id: status.ID,
|
Id: status.ID,
|
||||||
RepoTags: status.Names,
|
RepoTags: status.Names,
|
||||||
|
|
|
@ -48,7 +48,7 @@ func (s *Server) getContainerInfo(id string, getContainerFunc func(id string) *o
|
||||||
return types.ContainerInfo{
|
return types.ContainerInfo{
|
||||||
Name: ctr.Name(),
|
Name: ctr.Name(),
|
||||||
Pid: ctrState.Pid,
|
Pid: ctrState.Pid,
|
||||||
Image: ctr.Image(),
|
Image: ctr.ImageName(),
|
||||||
CreatedTime: ctrState.Created.UnixNano(),
|
CreatedTime: ctrState.Created.UnixNano(),
|
||||||
Labels: ctr.Labels(),
|
Labels: ctr.Labels(),
|
||||||
Annotations: ctr.Annotations(),
|
Annotations: ctr.Annotations(),
|
||||||
|
|
|
@ -96,7 +96,7 @@ func TestGetContainerInfo(t *testing.T) {
|
||||||
t.Fatalf("expected same created time %d, got %d", created.UnixNano(), ci.CreatedTime)
|
t.Fatalf("expected same created time %d, got %d", created.UnixNano(), ci.CreatedTime)
|
||||||
}
|
}
|
||||||
if ci.Pid != 42 {
|
if ci.Pid != 42 {
|
||||||
t.Fatalf("expected pid 42, got %s", ci.Pid)
|
t.Fatalf("expected pid 42, got %v", ci.Pid)
|
||||||
}
|
}
|
||||||
if ci.Name != "testname" {
|
if ci.Name != "testname" {
|
||||||
t.Fatalf("expected name testname, got %s", ci.Name)
|
t.Fatalf("expected name testname, got %s", ci.Name)
|
||||||
|
@ -114,7 +114,7 @@ func TestGetContainerInfo(t *testing.T) {
|
||||||
t.Fatalf("expected sandbox to be testsandboxid, got %s", ci.Sandbox)
|
t.Fatalf("expected sandbox to be testsandboxid, got %s", ci.Sandbox)
|
||||||
}
|
}
|
||||||
if ci.IP != "1.1.1.42" {
|
if ci.IP != "1.1.1.42" {
|
||||||
t.Fatal("expected ip 1.1.1.42, got %s", ci.IP)
|
t.Fatalf("expected ip 1.1.1.42, got %s", ci.IP)
|
||||||
}
|
}
|
||||||
if len(ci.Annotations) == 0 {
|
if len(ci.Annotations) == 0 {
|
||||||
t.Fatal("annotations are empty")
|
t.Fatal("annotations are empty")
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// CRIOOperationsKey is the key for CRI-O operation metrics.
|
||||||
|
CRIOOperationsKey = "crio_operations"
|
||||||
|
// CRIOOperationsLatencyKey is the key for the operation latency metrics.
|
||||||
|
CRIOOperationsLatencyKey = "crio_operations_latency_microseconds"
|
||||||
|
// CRIOOperationsErrorsKey is the key for the operation error metrics.
|
||||||
|
CRIOOperationsErrorsKey = "crio_operations_errors"
|
||||||
|
|
||||||
|
// TODO(runcom):
|
||||||
|
// timeouts
|
||||||
|
|
||||||
|
subsystem = "container_runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// CRIOOperations collects operation counts by operation type.
|
||||||
|
CRIOOperations = prometheus.NewCounterVec(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Subsystem: subsystem,
|
||||||
|
Name: CRIOOperationsKey,
|
||||||
|
Help: "Cumulative number of CRI-O operations by operation type.",
|
||||||
|
},
|
||||||
|
[]string{"operation_type"},
|
||||||
|
)
|
||||||
|
// CRIOOperationsLatency collects operation latency numbers by operation
|
||||||
|
// type.
|
||||||
|
CRIOOperationsLatency = prometheus.NewSummaryVec(
|
||||||
|
prometheus.SummaryOpts{
|
||||||
|
Subsystem: subsystem,
|
||||||
|
Name: CRIOOperationsLatencyKey,
|
||||||
|
Help: "Latency in microseconds of CRI-O operations. Broken down by operation type.",
|
||||||
|
},
|
||||||
|
[]string{"operation_type"},
|
||||||
|
)
|
||||||
|
// CRIOOperationsErrors collects operation errors by operation
|
||||||
|
// type.
|
||||||
|
CRIOOperationsErrors = prometheus.NewCounterVec(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Subsystem: subsystem,
|
||||||
|
Name: CRIOOperationsErrorsKey,
|
||||||
|
Help: "Cumulative number of CRI-O operation errors by operation type.",
|
||||||
|
},
|
||||||
|
[]string{"operation_type"},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
var registerMetrics sync.Once
|
||||||
|
|
||||||
|
// Register all metrics
|
||||||
|
func Register() {
|
||||||
|
registerMetrics.Do(func() {
|
||||||
|
prometheus.MustRegister(CRIOOperations)
|
||||||
|
prometheus.MustRegister(CRIOOperationsLatency)
|
||||||
|
prometheus.MustRegister(CRIOOperationsErrors)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SinceInMicroseconds gets the time since the specified start in microseconds.
|
||||||
|
func SinceInMicroseconds(start time.Time) float64 {
|
||||||
|
return float64(time.Since(start).Nanoseconds() / time.Microsecond.Nanoseconds())
|
||||||
|
}
|
|
@ -1,12 +1,19 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Status returns the status of the runtime
|
// Status returns the status of the runtime
|
||||||
func (s *Server) Status(ctx context.Context, req *pb.StatusRequest) (*pb.StatusResponse, error) {
|
func (s *Server) Status(ctx context.Context, req *pb.StatusRequest) (resp *pb.StatusResponse, err error) {
|
||||||
|
const operation = "status"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
|
|
||||||
// Deal with Runtime conditions
|
// Deal with Runtime conditions
|
||||||
runtimeReady, err := s.Runtime().RuntimeReady()
|
runtimeReady, err := s.Runtime().RuntimeReady()
|
||||||
|
@ -22,7 +29,7 @@ func (s *Server) Status(ctx context.Context, req *pb.StatusRequest) (*pb.StatusR
|
||||||
runtimeReadyConditionString := pb.RuntimeReady
|
runtimeReadyConditionString := pb.RuntimeReady
|
||||||
networkReadyConditionString := pb.NetworkReady
|
networkReadyConditionString := pb.NetworkReady
|
||||||
|
|
||||||
resp := &pb.StatusResponse{
|
resp = &pb.StatusResponse{
|
||||||
Status: &pb.RuntimeStatus{
|
Status: &pb.RuntimeStatus{
|
||||||
Conditions: []*pb.RuntimeCondition{
|
Conditions: []*pb.RuntimeCondition{
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/kubernetes-incubator/cri-o/libkpod/sandbox"
|
"github.com/kubernetes-incubator/cri-o/libkpod/sandbox"
|
||||||
"github.com/kubernetes-incubator/cri-o/oci"
|
"github.com/kubernetes-incubator/cri-o/oci"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
@ -28,7 +30,13 @@ func filterSandbox(p *pb.PodSandbox, filter *pb.PodSandboxFilter) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListPodSandbox returns a list of SandBoxes.
|
// ListPodSandbox returns a list of SandBoxes.
|
||||||
func (s *Server) ListPodSandbox(ctx context.Context, req *pb.ListPodSandboxRequest) (*pb.ListPodSandboxResponse, error) {
|
func (s *Server) ListPodSandbox(ctx context.Context, req *pb.ListPodSandboxRequest) (resp *pb.ListPodSandboxResponse, err error) {
|
||||||
|
const operation = "list_pod_sandbox"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
|
|
||||||
logrus.Debugf("ListPodSandboxRequest %+v", req)
|
logrus.Debugf("ListPodSandboxRequest %+v", req)
|
||||||
var pods []*pb.PodSandbox
|
var pods []*pb.PodSandbox
|
||||||
var podList []*sandbox.Sandbox
|
var podList []*sandbox.Sandbox
|
||||||
|
@ -82,7 +90,7 @@ func (s *Server) ListPodSandbox(ctx context.Context, req *pb.ListPodSandboxReque
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := &pb.ListPodSandboxResponse{
|
resp = &pb.ListPodSandboxResponse{
|
||||||
Items: pods,
|
Items: pods,
|
||||||
}
|
}
|
||||||
logrus.Debugf("ListPodSandboxResponse %+v", resp)
|
logrus.Debugf("ListPodSandboxResponse %+v", resp)
|
||||||
|
|
|
@ -2,6 +2,7 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
"github.com/kubernetes-incubator/cri-o/libkpod/sandbox"
|
"github.com/kubernetes-incubator/cri-o/libkpod/sandbox"
|
||||||
|
@ -15,7 +16,13 @@ import (
|
||||||
|
|
||||||
// RemovePodSandbox deletes the sandbox. If there are any running containers in the
|
// RemovePodSandbox deletes the sandbox. If there are any running containers in the
|
||||||
// sandbox, they should be force deleted.
|
// sandbox, they should be force deleted.
|
||||||
func (s *Server) RemovePodSandbox(ctx context.Context, req *pb.RemovePodSandboxRequest) (*pb.RemovePodSandboxResponse, error) {
|
func (s *Server) RemovePodSandbox(ctx context.Context, req *pb.RemovePodSandboxRequest) (resp *pb.RemovePodSandboxResponse, err error) {
|
||||||
|
const operation = "remove_pod_sandbox"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
|
|
||||||
logrus.Debugf("RemovePodSandboxRequest %+v", req)
|
logrus.Debugf("RemovePodSandboxRequest %+v", req)
|
||||||
sb, err := s.getPodSandboxFromRequest(req.PodSandboxId)
|
sb, err := s.getPodSandboxFromRequest(req.PodSandboxId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -27,7 +34,7 @@ func (s *Server) RemovePodSandbox(ctx context.Context, req *pb.RemovePodSandboxR
|
||||||
// the the CRI interface which expects to not error out in not found
|
// the the CRI interface which expects to not error out in not found
|
||||||
// cases.
|
// cases.
|
||||||
|
|
||||||
resp := &pb.RemovePodSandboxResponse{}
|
resp = &pb.RemovePodSandboxResponse{}
|
||||||
logrus.Warnf("could not get sandbox %s, it's probably been removed already: %v", req.PodSandboxId, err)
|
logrus.Warnf("could not get sandbox %s, it's probably been removed already: %v", req.PodSandboxId, err)
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
@ -41,7 +48,7 @@ func (s *Server) RemovePodSandbox(ctx context.Context, req *pb.RemovePodSandboxR
|
||||||
if !sb.Stopped() {
|
if !sb.Stopped() {
|
||||||
cState := s.Runtime().ContainerStatus(c)
|
cState := s.Runtime().ContainerStatus(c)
|
||||||
if cState.Status == oci.ContainerStateCreated || cState.Status == oci.ContainerStateRunning {
|
if cState.Status == oci.ContainerStateCreated || cState.Status == oci.ContainerStateRunning {
|
||||||
if err := s.Runtime().StopContainer(c, -1); err != nil {
|
if err := s.Runtime().StopContainer(ctx, c, 10); err != nil {
|
||||||
// Assume container is already stopped
|
// Assume container is already stopped
|
||||||
logrus.Warnf("failed to stop container %s: %v", c.Name(), err)
|
logrus.Warnf("failed to stop container %s: %v", c.Name(), err)
|
||||||
}
|
}
|
||||||
|
@ -92,7 +99,7 @@ func (s *Server) RemovePodSandbox(ctx context.Context, req *pb.RemovePodSandboxR
|
||||||
return nil, fmt.Errorf("failed to delete pod sandbox %s from index: %v", sb.ID(), err)
|
return nil, fmt.Errorf("failed to delete pod sandbox %s from index: %v", sb.ID(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := &pb.RemovePodSandboxResponse{}
|
resp = &pb.RemovePodSandboxResponse{}
|
||||||
logrus.Debugf("RemovePodSandboxResponse %+v", resp)
|
logrus.Debugf("RemovePodSandboxResponse %+v", resp)
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"github.com/kubernetes-incubator/cri-o/libkpod/sandbox"
|
"github.com/kubernetes-incubator/cri-o/libkpod/sandbox"
|
||||||
"github.com/kubernetes-incubator/cri-o/oci"
|
"github.com/kubernetes-incubator/cri-o/oci"
|
||||||
"github.com/kubernetes-incubator/cri-o/pkg/annotations"
|
"github.com/kubernetes-incubator/cri-o/pkg/annotations"
|
||||||
|
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/opencontainers/runtime-tools/generate"
|
"github.com/opencontainers/runtime-tools/generate"
|
||||||
"github.com/opencontainers/selinux/go-selinux/label"
|
"github.com/opencontainers/selinux/go-selinux/label"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -94,6 +95,12 @@ var (
|
||||||
|
|
||||||
// RunPodSandbox creates and runs a pod-level sandbox.
|
// RunPodSandbox creates and runs a pod-level sandbox.
|
||||||
func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest) (resp *pb.RunPodSandboxResponse, err error) {
|
func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest) (resp *pb.RunPodSandboxResponse, err error) {
|
||||||
|
const operation = "run_pod_sandbox"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
|
|
||||||
s.updateLock.RLock()
|
s.updateLock.RLock()
|
||||||
defer s.updateLock.RUnlock()
|
defer s.updateLock.RUnlock()
|
||||||
|
|
||||||
|
@ -220,6 +227,10 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest
|
||||||
// add labels
|
// add labels
|
||||||
labels := req.GetConfig().GetLabels()
|
labels := req.GetConfig().GetLabels()
|
||||||
|
|
||||||
|
if err := validateLabels(labels); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Add special container name label for the infra container
|
// Add special container name label for the infra container
|
||||||
labelsJSON := []byte{}
|
labelsJSON := []byte{}
|
||||||
if labels != nil {
|
if labels != nil {
|
||||||
|
@ -424,7 +435,7 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest
|
||||||
|
|
||||||
// set up namespaces
|
// set up namespaces
|
||||||
if hostNetwork {
|
if hostNetwork {
|
||||||
err = g.RemoveLinuxNamespace("network")
|
err = g.RemoveLinuxNamespace(string(runtimespec.NetworkNamespace))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -445,21 +456,21 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Pass the created namespace path to the runtime
|
// Pass the created namespace path to the runtime
|
||||||
err = g.AddOrReplaceLinuxNamespace("network", sb.NetNsPath())
|
err = g.AddOrReplaceLinuxNamespace(string(runtimespec.NetworkNamespace), sb.NetNsPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if namespaceOptions.HostPid {
|
if securityContext.GetNamespaceOptions().GetHostPid() {
|
||||||
err = g.RemoveLinuxNamespace("pid")
|
err = g.RemoveLinuxNamespace(string(runtimespec.PIDNamespace))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if namespaceOptions.HostIpc {
|
if securityContext.GetNamespaceOptions().GetHostIpc() {
|
||||||
err = g.RemoveLinuxNamespace("ipc")
|
err = g.RemoveLinuxNamespace(string(runtimespec.IPCNamespace))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -492,6 +503,7 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
container.SetSpec(g.Spec())
|
||||||
container.SetMountPoint(mountPoint)
|
container.SetMountPoint(mountPoint)
|
||||||
|
|
||||||
sb.SetInfraContainer(container)
|
sb.SetInfraContainer(container)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/kubernetes-incubator/cri-o/oci"
|
"github.com/kubernetes-incubator/cri-o/oci"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
|
@ -8,7 +10,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// PodSandboxStatus returns the Status of the PodSandbox.
|
// PodSandboxStatus returns the Status of the PodSandbox.
|
||||||
func (s *Server) PodSandboxStatus(ctx context.Context, req *pb.PodSandboxStatusRequest) (*pb.PodSandboxStatusResponse, error) {
|
func (s *Server) PodSandboxStatus(ctx context.Context, req *pb.PodSandboxStatusRequest) (resp *pb.PodSandboxStatusResponse, err error) {
|
||||||
|
const operation = "pod_sandbox_status"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
|
|
||||||
logrus.Debugf("PodSandboxStatusRequest %+v", req)
|
logrus.Debugf("PodSandboxStatusRequest %+v", req)
|
||||||
sb, err := s.getPodSandboxFromRequest(req.PodSandboxId)
|
sb, err := s.getPodSandboxFromRequest(req.PodSandboxId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -24,7 +32,7 @@ func (s *Server) PodSandboxStatus(ctx context.Context, req *pb.PodSandboxStatusR
|
||||||
}
|
}
|
||||||
|
|
||||||
sandboxID := sb.ID()
|
sandboxID := sb.ID()
|
||||||
resp := &pb.PodSandboxStatusResponse{
|
resp = &pb.PodSandboxStatusResponse{
|
||||||
Status: &pb.PodSandboxStatus{
|
Status: &pb.PodSandboxStatus{
|
||||||
Id: sandboxID,
|
Id: sandboxID,
|
||||||
CreatedAt: podInfraContainer.CreatedAt().UnixNano(),
|
CreatedAt: podInfraContainer.CreatedAt().UnixNano(),
|
||||||
|
|
|
@ -2,6 +2,7 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
"github.com/docker/docker/pkg/mount"
|
"github.com/docker/docker/pkg/mount"
|
||||||
|
@ -18,7 +19,13 @@ import (
|
||||||
|
|
||||||
// StopPodSandbox stops the sandbox. If there are any running containers in the
|
// StopPodSandbox stops the sandbox. If there are any running containers in the
|
||||||
// sandbox, they should be force terminated.
|
// sandbox, they should be force terminated.
|
||||||
func (s *Server) StopPodSandbox(ctx context.Context, req *pb.StopPodSandboxRequest) (*pb.StopPodSandboxResponse, error) {
|
func (s *Server) StopPodSandbox(ctx context.Context, req *pb.StopPodSandboxRequest) (resp *pb.StopPodSandboxResponse, err error) {
|
||||||
|
const operation = "stop_pod_sandbox"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
|
|
||||||
logrus.Debugf("StopPodSandboxRequest %+v", req)
|
logrus.Debugf("StopPodSandboxRequest %+v", req)
|
||||||
sb, err := s.getPodSandboxFromRequest(req.PodSandboxId)
|
sb, err := s.getPodSandboxFromRequest(req.PodSandboxId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -30,14 +37,14 @@ func (s *Server) StopPodSandbox(ctx context.Context, req *pb.StopPodSandboxReque
|
||||||
// the the CRI interface which expects to not error out in not found
|
// the the CRI interface which expects to not error out in not found
|
||||||
// cases.
|
// cases.
|
||||||
|
|
||||||
resp := &pb.StopPodSandboxResponse{}
|
resp = &pb.StopPodSandboxResponse{}
|
||||||
logrus.Warnf("could not get sandbox %s, it's probably been stopped already: %v", req.PodSandboxId, err)
|
logrus.Warnf("could not get sandbox %s, it's probably been stopped already: %v", req.PodSandboxId, err)
|
||||||
logrus.Debugf("StopPodSandboxResponse %s: %+v", req.PodSandboxId, resp)
|
logrus.Debugf("StopPodSandboxResponse %s: %+v", req.PodSandboxId, resp)
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if sb.Stopped() {
|
if sb.Stopped() {
|
||||||
resp := &pb.StopPodSandboxResponse{}
|
resp = &pb.StopPodSandboxResponse{}
|
||||||
logrus.Debugf("StopPodSandboxResponse %s: %+v", sb.ID(), resp)
|
logrus.Debugf("StopPodSandboxResponse %s: %+v", sb.ID(), resp)
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
@ -56,7 +63,7 @@ func (s *Server) StopPodSandbox(ctx context.Context, req *pb.StopPodSandboxReque
|
||||||
for _, c := range containers {
|
for _, c := range containers {
|
||||||
cStatus := s.Runtime().ContainerStatus(c)
|
cStatus := s.Runtime().ContainerStatus(c)
|
||||||
if cStatus.Status != oci.ContainerStateStopped {
|
if cStatus.Status != oci.ContainerStateStopped {
|
||||||
if err := s.Runtime().StopContainer(c, -1); err != nil {
|
if err := s.Runtime().StopContainer(ctx, c, 10); err != nil {
|
||||||
return nil, fmt.Errorf("failed to stop container %s in pod sandbox %s: %v", c.Name(), sb.ID(), err)
|
return nil, fmt.Errorf("failed to stop container %s in pod sandbox %s: %v", c.Name(), sb.ID(), err)
|
||||||
}
|
}
|
||||||
if c.ID() == podInfraContainer.ID() {
|
if c.ID() == podInfraContainer.ID() {
|
||||||
|
@ -95,7 +102,7 @@ func (s *Server) StopPodSandbox(ctx context.Context, req *pb.StopPodSandboxReque
|
||||||
}
|
}
|
||||||
|
|
||||||
sb.SetStopped()
|
sb.SetStopped()
|
||||||
resp := &pb.StopPodSandboxResponse{}
|
resp = &pb.StopPodSandboxResponse{}
|
||||||
logrus.Debugf("StopPodSandboxResponse %s: %+v", sb.ID(), resp)
|
logrus.Debugf("StopPodSandboxResponse %s: %+v", sb.ID(), resp)
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,162 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
rspec "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
"github.com/opencontainers/selinux/go-selinux/label"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SecretData info
|
||||||
|
type SecretData struct {
|
||||||
|
Name string
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveTo saves secret data to given directory
|
||||||
|
func (s SecretData) SaveTo(dir string) error {
|
||||||
|
path := filepath.Join(dir, s.Name)
|
||||||
|
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil && !os.IsExist(err) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ioutil.WriteFile(path, s.Data, 0700)
|
||||||
|
}
|
||||||
|
|
||||||
|
func readAll(root, prefix string) ([]SecretData, error) {
|
||||||
|
path := filepath.Join(root, prefix)
|
||||||
|
|
||||||
|
data := []SecretData{}
|
||||||
|
|
||||||
|
files, err := ioutil.ReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range files {
|
||||||
|
fileData, err := readFile(root, filepath.Join(prefix, f.Name()))
|
||||||
|
if err != nil {
|
||||||
|
// If the file did not exist, might be a dangling symlink
|
||||||
|
// Ignore the error
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data = append(data, fileData...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readFile(root, name string) ([]SecretData, error) {
|
||||||
|
path := filepath.Join(root, name)
|
||||||
|
|
||||||
|
s, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.IsDir() {
|
||||||
|
dirData, err := readAll(root, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dirData, nil
|
||||||
|
}
|
||||||
|
bytes, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return []SecretData{{Name: name, Data: bytes}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getHostAndCtrDir separates the host:container paths
|
||||||
|
func getMountsMap(path string) (string, string, error) {
|
||||||
|
arr := strings.SplitN(path, ":", 2)
|
||||||
|
if len(arr) == 2 {
|
||||||
|
return arr[0], arr[1], nil
|
||||||
|
}
|
||||||
|
return "", "", errors.Errorf("unable to get host and container dir")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getHostSecretData(hostDir string) ([]SecretData, error) {
|
||||||
|
var allSecrets []SecretData
|
||||||
|
hostSecrets, err := readAll(hostDir, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "failed to read secrets from %q", hostDir)
|
||||||
|
}
|
||||||
|
return append(allSecrets, hostSecrets...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// secretMount copies the contents of host directory to container directory
|
||||||
|
// and returns a list of mounts
|
||||||
|
func secretMounts(defaultMountsPaths []string, mountLabel, containerWorkingDir string, runtimeMounts []rspec.Mount) ([]rspec.Mount, error) {
|
||||||
|
var mounts []rspec.Mount
|
||||||
|
for _, path := range defaultMountsPaths {
|
||||||
|
hostDir, ctrDir, err := getMountsMap(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// skip if the hostDir path doesn't exist
|
||||||
|
if _, err := os.Stat(hostDir); os.IsNotExist(err) {
|
||||||
|
logrus.Warnf("%q doesn't exist, skipping", hostDir)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrDirOnHost := filepath.Join(containerWorkingDir, ctrDir)
|
||||||
|
// skip if ctrDir has already been mounted by caller
|
||||||
|
if isAlreadyMounted(runtimeMounts, ctrDir) {
|
||||||
|
logrus.Warnf("%q has already been mounted; cannot override mount", ctrDir)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.RemoveAll(ctrDirOnHost); err != nil {
|
||||||
|
return nil, fmt.Errorf("remove container directory failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(ctrDirOnHost, 0755); err != nil {
|
||||||
|
return nil, fmt.Errorf("making container directory failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hostDir, err = resolveSymbolicLink(hostDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := getHostSecretData(hostDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "getting host secret data failed")
|
||||||
|
}
|
||||||
|
for _, s := range data {
|
||||||
|
s.SaveTo(ctrDirOnHost)
|
||||||
|
}
|
||||||
|
label.Relabel(ctrDirOnHost, mountLabel, false)
|
||||||
|
|
||||||
|
m := rspec.Mount{
|
||||||
|
Source: ctrDirOnHost,
|
||||||
|
Destination: ctrDir,
|
||||||
|
}
|
||||||
|
|
||||||
|
mounts = append(mounts, m)
|
||||||
|
}
|
||||||
|
return mounts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isAlreadyMounted(mounts []rspec.Mount, mountPath string) bool {
|
||||||
|
for _, mount := range mounts {
|
||||||
|
if mount.Destination == mountPath {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"github.com/kubernetes-incubator/cri-o/oci"
|
"github.com/kubernetes-incubator/cri-o/oci"
|
||||||
"github.com/kubernetes-incubator/cri-o/pkg/storage"
|
"github.com/kubernetes-incubator/cri-o/pkg/storage"
|
||||||
"github.com/kubernetes-incubator/cri-o/server/apparmor"
|
"github.com/kubernetes-incubator/cri-o/server/apparmor"
|
||||||
|
"github.com/kubernetes-incubator/cri-o/server/metrics"
|
||||||
"github.com/kubernetes-incubator/cri-o/server/seccomp"
|
"github.com/kubernetes-incubator/cri-o/server/seccomp"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
@ -35,8 +36,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
runtimeAPIVersion = "v1alpha1"
|
shutdownFile = "/var/lib/crio/crio.shutdown"
|
||||||
shutdownFile = "/var/lib/crio/crio.shutdown"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func isTrue(annotaton string) bool {
|
func isTrue(annotaton string) bool {
|
||||||
|
@ -350,6 +350,7 @@ func (s *Server) getPodSandboxFromRequest(podSandboxID string) (*sandbox.Sandbox
|
||||||
// CreateMetricsEndpoint creates a /metrics endpoint
|
// CreateMetricsEndpoint creates a /metrics endpoint
|
||||||
// for prometheus monitoring
|
// for prometheus monitoring
|
||||||
func (s *Server) CreateMetricsEndpoint() (*http.ServeMux, error) {
|
func (s *Server) CreateMetricsEndpoint() (*http.ServeMux, error) {
|
||||||
|
metrics.Register()
|
||||||
mux := &http.ServeMux{}
|
mux := &http.ServeMux{}
|
||||||
mux.Handle("/metrics", prometheus.Handler())
|
mux.Handle("/metrics", prometheus.Handler())
|
||||||
return mux, nil
|
return mux, nil
|
||||||
|
@ -419,6 +420,7 @@ func (s *Server) StartExitMonitor() {
|
||||||
}()
|
}()
|
||||||
if err := watcher.Add(s.config.ContainerExitsDir); err != nil {
|
if err := watcher.Add(s.config.ContainerExitsDir); err != nil {
|
||||||
logrus.Errorf("watcher.Add(%q) failed: %s", s.config.ContainerExitsDir, err)
|
logrus.Errorf("watcher.Add(%q) failed: %s", s.config.ContainerExitsDir, err)
|
||||||
|
close(done)
|
||||||
}
|
}
|
||||||
<-done
|
<-done
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,11 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/cri-o/ocicni/pkg/ocicni"
|
"github.com/cri-o/ocicni/pkg/ocicni"
|
||||||
"github.com/kubernetes-incubator/cri-o/libkpod/sandbox"
|
"github.com/kubernetes-incubator/cri-o/libkpod/sandbox"
|
||||||
|
"github.com/kubernetes-incubator/cri-o/server/metrics"
|
||||||
"github.com/opencontainers/runtime-tools/validate"
|
"github.com/opencontainers/runtime-tools/validate"
|
||||||
"github.com/syndtr/gocapability/capability"
|
"github.com/syndtr/gocapability/capability"
|
||||||
)
|
)
|
||||||
|
@ -16,6 +18,8 @@ const (
|
||||||
// According to http://man7.org/linux/man-pages/man5/resolv.conf.5.html:
|
// According to http://man7.org/linux/man-pages/man5/resolv.conf.5.html:
|
||||||
// "The search list is currently limited to six domains with a total of 256 characters."
|
// "The search list is currently limited to six domains with a total of 256 characters."
|
||||||
maxDNSSearches = 6
|
maxDNSSearches = 6
|
||||||
|
|
||||||
|
maxLabelSize = 4096
|
||||||
)
|
)
|
||||||
|
|
||||||
func copyFile(src, dest string) error {
|
func copyFile(src, dest string) error {
|
||||||
|
@ -181,3 +185,28 @@ func getOCICapabilitiesList() []string {
|
||||||
}
|
}
|
||||||
return caps
|
return caps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func recordOperation(operation string, start time.Time) {
|
||||||
|
metrics.CRIOOperations.WithLabelValues(operation).Inc()
|
||||||
|
metrics.CRIOOperationsLatency.WithLabelValues(operation).Observe(metrics.SinceInMicroseconds(start))
|
||||||
|
}
|
||||||
|
|
||||||
|
// recordError records error for metric if an error occurred.
|
||||||
|
func recordError(operation string, err error) {
|
||||||
|
if err != nil {
|
||||||
|
// TODO(runcom): handle timeout from ctx as well
|
||||||
|
metrics.CRIOOperationsErrors.WithLabelValues(operation).Inc()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateLabels(labels map[string]string) error {
|
||||||
|
for k, v := range labels {
|
||||||
|
if (len(k) + len(v)) > maxLabelSize {
|
||||||
|
if len(k) > 10 {
|
||||||
|
k = k[:10]
|
||||||
|
}
|
||||||
|
return fmt.Errorf("label key and value greater than maximum size (%d bytes), key: %s", maxLabelSize, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,29 +1,35 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/kubernetes-incubator/cri-o/version"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version returns the runtime name, runtime version and runtime API version
|
const (
|
||||||
func (s *Server) Version(ctx context.Context, req *pb.VersionRequest) (*pb.VersionResponse, error) {
|
// kubeAPIVersion is the api version of kubernetes.
|
||||||
|
|
||||||
runtimeVersion, err := s.Runtime().Version()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Track upstream code. For now it expects 0.1.0
|
// TODO: Track upstream code. For now it expects 0.1.0
|
||||||
version := "0.1.0"
|
kubeAPIVersion = "0.1.0"
|
||||||
|
// containerName is the name prepended in kubectl describe->Container ID:
|
||||||
|
// cri-o://<CONTAINER_ID>
|
||||||
|
containerName = "cri-o"
|
||||||
|
runtimeAPIVersion = "v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
// taking const address
|
// Version returns the runtime name, runtime version and runtime API version
|
||||||
rav := runtimeAPIVersion
|
func (s *Server) Version(ctx context.Context, req *pb.VersionRequest) (resp *pb.VersionResponse, err error) {
|
||||||
runtimeName := s.Runtime().Name()
|
const operation = "version"
|
||||||
|
defer func() {
|
||||||
|
recordOperation(operation, time.Now())
|
||||||
|
recordError(operation, err)
|
||||||
|
}()
|
||||||
|
|
||||||
return &pb.VersionResponse{
|
return &pb.VersionResponse{
|
||||||
Version: version,
|
Version: kubeAPIVersion,
|
||||||
RuntimeName: runtimeName,
|
RuntimeName: containerName,
|
||||||
RuntimeVersion: runtimeVersion,
|
RuntimeVersion: version.Version,
|
||||||
RuntimeApiVersion: rav,
|
RuntimeApiVersion: runtimeAPIVersion,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ function teardown() {
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
ctr_id="$output"
|
ctr_id="$output"
|
||||||
[ "$status" -eq 0 ]
|
|
||||||
run crioctl ctr execsync --id "$ctr_id" touch test.txt
|
run crioctl ctr execsync --id "$ctr_id" touch test.txt
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
|
@ -60,7 +59,6 @@ function teardown() {
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
ctr_id="$output"
|
ctr_id="$output"
|
||||||
[ "$status" -eq 0 ]
|
|
||||||
run crioctl ctr execsync --id "$ctr_id" touch test.txt
|
run crioctl ctr execsync --id "$ctr_id" touch test.txt
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -ne 0 ]
|
[ "$status" -ne 0 ]
|
||||||
|
@ -94,7 +92,6 @@ function teardown() {
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
ctr_id="$output"
|
ctr_id="$output"
|
||||||
[ "$status" -eq 0 ]
|
|
||||||
run crioctl ctr execsync --id "$ctr_id" touch test.txt
|
run crioctl ctr execsync --id "$ctr_id" touch test.txt
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -ne 0 ]
|
[ "$status" -ne 0 ]
|
||||||
|
@ -156,7 +153,6 @@ function teardown() {
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
ctr_id="$output"
|
ctr_id="$output"
|
||||||
[ "$status" -eq 0 ]
|
|
||||||
run crioctl ctr execsync --id "$ctr_id" touch test.txt
|
run crioctl ctr execsync --id "$ctr_id" touch test.txt
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
@test "crio commands" {
|
||||||
|
run ${CRIO_BINARY} --config /dev/null config > /dev/null
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run ${CRIO_BINARY} badoption > /dev/null
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -ne 0 ]
|
||||||
|
}
|
|
@ -560,6 +560,7 @@ function teardown() {
|
||||||
run crioctl ctr execsync --id "$ctr_id" --timeout 1 sleep 10
|
run crioctl ctr execsync --id "$ctr_id" --timeout 1 sleep 10
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[[ "$output" =~ "command timed out" ]]
|
[[ "$output" =~ "command timed out" ]]
|
||||||
|
[ "$status" -ne 0 ]
|
||||||
run crioctl pod stop --id "$pod_id"
|
run crioctl pod stop --id "$pod_id"
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
|
@ -599,6 +600,31 @@ function teardown() {
|
||||||
stop_crio
|
stop_crio
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "ctr hostname env" {
|
||||||
|
start_crio
|
||||||
|
run crioctl pod run --config "$TESTDATA"/sandbox_config.json
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
pod_id="$output"
|
||||||
|
run crioctl ctr create --config "$TESTDATA"/container_config.json --pod "$pod_id"
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
ctr_id="$output"
|
||||||
|
run crioctl ctr execsync --id "$ctr_id" env
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[[ "$output" =~ "HOSTNAME" ]]
|
||||||
|
run crioctl pod stop --id "$pod_id"
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run crioctl pod remove --id "$pod_id"
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
cleanup_ctrs
|
||||||
|
cleanup_pods
|
||||||
|
stop_crio
|
||||||
|
}
|
||||||
|
|
||||||
@test "ctr execsync failure" {
|
@test "ctr execsync failure" {
|
||||||
start_crio
|
start_crio
|
||||||
run crioctl pod run --config "$TESTDATA"/sandbox_config.json
|
run crioctl pod run --config "$TESTDATA"/sandbox_config.json
|
||||||
|
@ -766,10 +792,16 @@ function teardown() {
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
# Wait for container to OOM
|
# Wait for container to OOM
|
||||||
run sleep 100
|
attempt=0
|
||||||
run crioctl ctr status --id "$ctr_id"
|
while [ $attempt -le 100 ]; do
|
||||||
echo "$output"
|
attempt=$((attempt+1))
|
||||||
[ "$status" -eq 0 ]
|
run crioctl ctr status --id "$ctr_id"
|
||||||
|
echo "$output"
|
||||||
|
if [[ "$output" =~ "OOMKilled" ]]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 10
|
||||||
|
done
|
||||||
[[ "$output" =~ "OOMKilled" ]]
|
[[ "$output" =~ "OOMKilled" ]]
|
||||||
run crioctl pod stop --id "$pod_id"
|
run crioctl pod stop --id "$pod_id"
|
||||||
echo "$output"
|
echo "$output"
|
||||||
|
@ -864,3 +896,59 @@ function teardown() {
|
||||||
cleanup_pods
|
cleanup_pods
|
||||||
stop_crio
|
stop_crio
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "ctr correctly setup working directory" {
|
||||||
|
start_crio
|
||||||
|
run crioctl pod run --config "$TESTDATA"/sandbox_config.json
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
pod_id="$output"
|
||||||
|
notexistcwd=$(cat "$TESTDATA"/container_config.json | python -c 'import json,sys;obj=json.load(sys.stdin);obj["working_dir"] = "/thisshouldntexistatall"; json.dump(obj, sys.stdout)')
|
||||||
|
echo "$notexistcwd" > "$TESTDIR"/container_cwd_notexist.json
|
||||||
|
run crioctl ctr create --config "$TESTDIR"/container_cwd_notexist.json --pod "$pod_id"
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
ctr_id="$output"
|
||||||
|
run crioctl ctr start --id "$ctr_id"
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
|
||||||
|
filecwd=$(cat "$TESTDATA"/container_config.json | python -c 'import json,sys;obj=json.load(sys.stdin);obj["working_dir"] = "/etc/passwd"; obj["metadata"]["name"] = "container2"; json.dump(obj, sys.stdout)')
|
||||||
|
echo "$filecwd" > "$TESTDIR"/container_cwd_file.json
|
||||||
|
run crioctl ctr create --config "$TESTDIR"/container_cwd_file.json --pod "$pod_id"
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -ne 0 ]
|
||||||
|
ctr_id="$output"
|
||||||
|
[[ "$output" =~ "not a directory" ]]
|
||||||
|
|
||||||
|
cleanup_ctrs
|
||||||
|
cleanup_pods
|
||||||
|
stop_crio
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "ctr execsync conflicting with conmon env" {
|
||||||
|
start_crio
|
||||||
|
run crictl runs "$TESTDATA"/sandbox_config.json
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
pod_id="$output"
|
||||||
|
run crictl create "$pod_id" "$TESTDATA"/container_redis_env_custom.json "$TESTDATA"/sandbox_config.json
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
ctr_id="$output"
|
||||||
|
run crictl start "$ctr_id"
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run crictl exec "$ctr_id" env
|
||||||
|
echo "$output"
|
||||||
|
echo "$status"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[[ "$output" =~ "acustompathinpath" ]]
|
||||||
|
run crictl exec --sync "$ctr_id" env
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[[ "$output" =~ "acustompathinpath" ]]
|
||||||
|
cleanup_ctrs
|
||||||
|
cleanup_pods
|
||||||
|
stop_crio
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
IMAGE="redis:alpine"
|
||||||
|
|
||||||
|
function teardown() {
|
||||||
|
cleanup_test
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "bind secrets mounts to container" {
|
||||||
|
start_crio
|
||||||
|
run crioctl pod run --config "$TESTDATA"/sandbox_config.json
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
pod_id="$output"
|
||||||
|
run crioctl image pull "$IMAGE"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run crioctl ctr create --config "$TESTDATA"/container_redis.json --pod "$pod_id"
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
ctr_id="$output"
|
||||||
|
run crioctl ctr execsync --id "$ctr_id" cat /proc/mounts
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
mount_info="$output"
|
||||||
|
run grep /container/path1 <<< "$mount_info"
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
cleanup_ctrs
|
||||||
|
cleanup_pods
|
||||||
|
stop_crio
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "default mounts correctly sorted with other mounts" {
|
||||||
|
start_crio
|
||||||
|
run crioctl pod run --config "$TESTDATA"/sandbox_config.json
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
pod_id="$output"
|
||||||
|
run crioctl image pull "$IMAGE"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
host_path="$TESTDIR"/clash
|
||||||
|
mkdir "$host_path"
|
||||||
|
echo "clashing..." > "$host_path"/clashing.txt
|
||||||
|
sed -e "s,%HPATH%,$host_path,g" "$TESTDATA"/container_redis_default_mounts.json > "$TESTDIR"/defmounts_pre.json
|
||||||
|
sed -e 's,%CPATH%,\/container\/path1\/clash,g' "$TESTDIR"/defmounts_pre.json > "$TESTDIR"/defmounts.json
|
||||||
|
run crioctl ctr create --config "$TESTDIR"/defmounts.json --pod "$pod_id"
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
ctr_id="$output"
|
||||||
|
run crioctl ctr execsync --id "$ctr_id" ls -la /container/path1/clash
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run crioctl ctr execsync --id "$ctr_id" cat /container/path1/clash/clashing.txt
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[[ "$output" =~ "clashing..." ]]
|
||||||
|
run crioctl ctr execsync --id "$ctr_id" ls -la /container/path1
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run crioctl ctr execsync --id "$ctr_id" cat /container/path1/test.txt
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
[[ "$output" =~ "Testing secrets mounts!" ]]
|
||||||
|
cleanup_ctrs
|
||||||
|
cleanup_pods
|
||||||
|
stop_crio
|
||||||
|
}
|
|
@ -10,16 +10,16 @@ TESTDATA="${INTEGRATION_ROOT}/testdata"
|
||||||
CRIO_ROOT=${CRIO_ROOT:-$(cd "$INTEGRATION_ROOT/../.."; pwd -P)}
|
CRIO_ROOT=${CRIO_ROOT:-$(cd "$INTEGRATION_ROOT/../.."; pwd -P)}
|
||||||
|
|
||||||
# Path of the crio binary.
|
# Path of the crio binary.
|
||||||
CRIO_BINARY=${CRIO_BINARY:-${CRIO_ROOT}/cri-o/crio}
|
CRIO_BINARY=${CRIO_BINARY:-${CRIO_ROOT}/cri-o/bin/crio}
|
||||||
# Path of the crictl binary.
|
# Path of the crictl binary.
|
||||||
CRICTL_PATH=$(command -v crictl || true)
|
CRICTL_PATH=$(command -v crictl || true)
|
||||||
CRICTL_BINARY=${CRICTL_PATH:-/usr/bin/crictl}
|
CRICTL_BINARY=${CRICTL_PATH:-/usr/bin/crictl}
|
||||||
# Path to kpod binary.
|
# Path to kpod binary.
|
||||||
KPOD_BINARY=${KPOD_BINARY:-${CRIO_ROOT}/cri-o/kpod}
|
KPOD_BINARY=${KPOD_BINARY:-${CRIO_ROOT}/cri-o/bin/kpod}
|
||||||
# Path of the conmon binary.
|
# Path of the conmon binary.
|
||||||
CONMON_BINARY=${CONMON_BINARY:-${CRIO_ROOT}/cri-o/conmon/conmon}
|
CONMON_BINARY=${CONMON_BINARY:-${CRIO_ROOT}/cri-o/bin/conmon}
|
||||||
# Path of the pause binary.
|
# Path of the pause binary.
|
||||||
PAUSE_BINARY=${PAUSE_BINARY:-${CRIO_ROOT}/cri-o/pause/pause}
|
PAUSE_BINARY=${PAUSE_BINARY:-${CRIO_ROOT}/cri-o/bin/pause}
|
||||||
# Path of the default seccomp profile.
|
# Path of the default seccomp profile.
|
||||||
SECCOMP_PROFILE=${SECCOMP_PROFILE:-${CRIO_ROOT}/cri-o/seccomp.json}
|
SECCOMP_PROFILE=${SECCOMP_PROFILE:-${CRIO_ROOT}/cri-o/seccomp.json}
|
||||||
# Name of the default apparmor profile.
|
# Name of the default apparmor profile.
|
||||||
|
@ -69,6 +69,15 @@ HOOKSDIR=$TESTDIR/hooks
|
||||||
mkdir ${HOOKSDIR}
|
mkdir ${HOOKSDIR}
|
||||||
HOOKS_OPTS="--hooks-dir-path=$HOOKSDIR"
|
HOOKS_OPTS="--hooks-dir-path=$HOOKSDIR"
|
||||||
|
|
||||||
|
# Setup default secrets mounts
|
||||||
|
MOUNT_PATH="$TESTDIR/secrets"
|
||||||
|
mkdir ${MOUNT_PATH}
|
||||||
|
MOUNT_FILE="${MOUNT_PATH}/test.txt"
|
||||||
|
touch ${MOUNT_FILE}
|
||||||
|
echo "Testing secrets mounts!" > ${MOUNT_FILE}
|
||||||
|
|
||||||
|
DEFAULT_MOUNTS_OPTS="--default-mounts=${MOUNT_PATH}:/container/path1"
|
||||||
|
|
||||||
# We may need to set some default storage options.
|
# We may need to set some default storage options.
|
||||||
case "$(stat -f -c %T ${TESTDIR})" in
|
case "$(stat -f -c %T ${TESTDIR})" in
|
||||||
aufs)
|
aufs)
|
||||||
|
@ -165,7 +174,7 @@ function crio() {
|
||||||
}
|
}
|
||||||
|
|
||||||
# DEPRECATED
|
# DEPRECATED
|
||||||
OCIC_BINARY=${OCIC_BINARY:-${CRIO_ROOT}/cri-o/crioctl}
|
OCIC_BINARY=${OCIC_BINARY:-${CRIO_ROOT}/cri-o/bin/crioctl}
|
||||||
# Run crioctl using the binary specified by $OCIC_BINARY.
|
# Run crioctl using the binary specified by $OCIC_BINARY.
|
||||||
function crioctl() {
|
function crioctl() {
|
||||||
"$OCIC_BINARY" --connect "$CRIO_SOCKET" "$@"
|
"$OCIC_BINARY" --connect "$CRIO_SOCKET" "$@"
|
||||||
|
@ -202,9 +211,9 @@ function retry() {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
# Waits until the given crio becomes reachable.
|
# Waits until crio becomes reachable.
|
||||||
function wait_until_reachable() {
|
function wait_until_reachable() {
|
||||||
retry 15 1 crictl status
|
retry 15 1 crictl version
|
||||||
}
|
}
|
||||||
|
|
||||||
# Start crio.
|
# Start crio.
|
||||||
|
@ -235,7 +244,7 @@ function start_crio() {
|
||||||
"$COPYIMG_BINARY" --root "$TESTDIR/crio" $STORAGE_OPTS --runroot "$TESTDIR/crio-run" --image-name=mrunalp/image-volume-test --import-from=dir:"$ARTIFACTS_PATH"/image-volume-test-image --add-name=docker.io/library/mrunalp/image-volume-test --signature-policy="$INTEGRATION_ROOT"/policy.json
|
"$COPYIMG_BINARY" --root "$TESTDIR/crio" $STORAGE_OPTS --runroot "$TESTDIR/crio-run" --image-name=mrunalp/image-volume-test --import-from=dir:"$ARTIFACTS_PATH"/image-volume-test-image --add-name=docker.io/library/mrunalp/image-volume-test --signature-policy="$INTEGRATION_ROOT"/policy.json
|
||||||
"$COPYIMG_BINARY" --root "$TESTDIR/crio" $STORAGE_OPTS --runroot "$TESTDIR/crio-run" --image-name=busybox:latest --import-from=dir:"$ARTIFACTS_PATH"/busybox-image --add-name=docker.io/library/busybox:latest --signature-policy="$INTEGRATION_ROOT"/policy.json
|
"$COPYIMG_BINARY" --root "$TESTDIR/crio" $STORAGE_OPTS --runroot "$TESTDIR/crio-run" --image-name=busybox:latest --import-from=dir:"$ARTIFACTS_PATH"/busybox-image --add-name=docker.io/library/busybox:latest --signature-policy="$INTEGRATION_ROOT"/policy.json
|
||||||
"$COPYIMG_BINARY" --root "$TESTDIR/crio" $STORAGE_OPTS --runroot "$TESTDIR/crio-run" --image-name=runcom/stderr-test:latest --import-from=dir:"$ARTIFACTS_PATH"/stderr-test --add-name=docker.io/runcom/stderr-test:latest --signature-policy="$INTEGRATION_ROOT"/policy.json
|
"$COPYIMG_BINARY" --root "$TESTDIR/crio" $STORAGE_OPTS --runroot "$TESTDIR/crio-run" --image-name=runcom/stderr-test:latest --import-from=dir:"$ARTIFACTS_PATH"/stderr-test --add-name=docker.io/runcom/stderr-test:latest --signature-policy="$INTEGRATION_ROOT"/policy.json
|
||||||
"$CRIO_BINARY" ${HOOKS_OPTS} --conmon "$CONMON_BINARY" --listen "$CRIO_SOCKET" --cgroup-manager "$CGROUP_MANAGER" --registry "docker.io" --runtime "$RUNTIME_BINARY" --root "$TESTDIR/crio" --runroot "$TESTDIR/crio-run" $STORAGE_OPTS --seccomp-profile "$seccomp" --apparmor-profile "$apparmor" --cni-config-dir "$CRIO_CNI_CONFIG" --cni-plugin-dir "$CRIO_CNI_PLUGIN" --signature-policy "$INTEGRATION_ROOT"/policy.json --image-volumes "$IMAGE_VOLUMES" --pids-limit "$PIDS_LIMIT" --log-size-max "$LOG_SIZE_MAX_LIMIT" --config /dev/null config >$CRIO_CONFIG
|
"$CRIO_BINARY" ${DEFAULT_MOUNTS_OPTS} ${HOOKS_OPTS} --conmon "$CONMON_BINARY" --listen "$CRIO_SOCKET" --cgroup-manager "$CGROUP_MANAGER" --registry "docker.io" --runtime "$RUNTIME_BINARY" --root "$TESTDIR/crio" --runroot "$TESTDIR/crio-run" $STORAGE_OPTS --seccomp-profile "$seccomp" --apparmor-profile "$apparmor" --cni-config-dir "$CRIO_CNI_CONFIG" --cni-plugin-dir "$CRIO_CNI_PLUGIN" --signature-policy "$INTEGRATION_ROOT"/policy.json --image-volumes "$IMAGE_VOLUMES" --pids-limit "$PIDS_LIMIT" --log-size-max "$LOG_SIZE_MAX_LIMIT" --config /dev/null config >$CRIO_CONFIG
|
||||||
|
|
||||||
# Prepare the CNI configuration files, we're running with non host networking by default
|
# Prepare the CNI configuration files, we're running with non host networking by default
|
||||||
if [[ -n "$4" ]]; then
|
if [[ -n "$4" ]]; then
|
||||||
|
|
|
@ -10,7 +10,7 @@ cp hooks/checkhook.sh ${HOOKSDIR}
|
||||||
sed "s|HOOKSDIR|${HOOKSDIR}|" hooks/checkhook.json > ${HOOKSDIR}/checkhook.json
|
sed "s|HOOKSDIR|${HOOKSDIR}|" hooks/checkhook.json > ${HOOKSDIR}/checkhook.json
|
||||||
|
|
||||||
@test "pod test hooks" {
|
@test "pod test hooks" {
|
||||||
run rm -f /run/hookscheck
|
rm -f /run/hookscheck
|
||||||
start_crio
|
start_crio
|
||||||
run crioctl pod run --config "$TESTDATA"/sandbox_config.json
|
run crioctl pod run --config "$TESTDATA"/sandbox_config.json
|
||||||
echo "$output"
|
echo "$output"
|
||||||
|
|
|
@ -38,6 +38,7 @@ function teardown() {
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
run ${KPOD_BINARY} ${KPOD_OPTIONS} images --format json
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} images --format json
|
||||||
echo "$output"
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
name=$(echo $output | python -c 'import sys; import json; print(json.loads(sys.stdin.read())[0])["names"][0]')
|
name=$(echo $output | python -c 'import sys; import json; print(json.loads(sys.stdin.read())[0])["names"][0]')
|
||||||
[ "$name" = "docker.io/library/${IMAGE}" ]
|
[ "$name" = "docker.io/library/${IMAGE}" ]
|
||||||
run ${KPOD_BINARY} ${KPOD_OPTIONS} rmi ${IMAGE}
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} rmi ${IMAGE}
|
||||||
|
|
|
@ -59,6 +59,7 @@ function teardown() {
|
||||||
ctr_id="$output"
|
ctr_id="$output"
|
||||||
run crioctl ctr start --id "$ctr_id"
|
run crioctl ctr start --id "$ctr_id"
|
||||||
echo "$output"
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
id="$output"
|
id="$output"
|
||||||
run ${KPOD_BINARY} ${KPOD_OPTIONS} pause "$id"
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} pause "$id"
|
||||||
echo "$output"
|
echo "$output"
|
||||||
|
@ -87,6 +88,7 @@ function teardown() {
|
||||||
ctr_id="$output"
|
ctr_id="$output"
|
||||||
run crioctl ctr start --id "$ctr_id"
|
run crioctl ctr start --id "$ctr_id"
|
||||||
echo "$output"
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
run ${KPOD_BINARY} ${KPOD_OPTIONS} pause "k8s_podsandbox1-redis_podsandbox1_redhat.test.crio_redhat-test-crio_0"
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} pause "k8s_podsandbox1-redis_podsandbox1_redhat.test.crio_redhat-test-crio_0"
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
|
@ -115,6 +117,7 @@ function teardown() {
|
||||||
run crioctl ctr start --id "$ctr_id"
|
run crioctl ctr start --id "$ctr_id"
|
||||||
echo "$output"
|
echo "$output"
|
||||||
id="$output"
|
id="$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
run ${KPOD_BINARY} ${KPOD_OPTIONS} pause "$id"
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} pause "$id"
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
|
|
|
@ -167,12 +167,10 @@ IMAGE="redis:alpine"
|
||||||
cleanup_ctrs
|
cleanup_ctrs
|
||||||
cleanup_pods
|
cleanup_pods
|
||||||
stop_crio
|
stop_crio
|
||||||
[ "$status" -eq 0 ]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "kpod ps namespace flag" {
|
@test "kpod ps namespace flag" {
|
||||||
start_crio
|
start_crio
|
||||||
[ "$status" -eq 0 ]
|
|
||||||
run crioctl pod run --config "$TESTDATA"/sandbox_config.json
|
run crioctl pod run --config "$TESTDATA"/sandbox_config.json
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
|
@ -215,7 +213,6 @@ IMAGE="redis:alpine"
|
||||||
|
|
||||||
@test "kpod ps without namespace flag and format flag = json" {
|
@test "kpod ps without namespace flag and format flag = json" {
|
||||||
start_crio
|
start_crio
|
||||||
[ "$status" -eq 0 ]
|
|
||||||
run crioctl pod run --config "$TESTDATA"/sandbox_config.json
|
run crioctl pod run --config "$TESTDATA"/sandbox_config.json
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
|
@ -231,7 +228,6 @@ IMAGE="redis:alpine"
|
||||||
cleanup_ctrs
|
cleanup_ctrs
|
||||||
cleanup_pods
|
cleanup_pods
|
||||||
stop_crio
|
stop_crio
|
||||||
[ "$status" -eq 0 ]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "kpod ps format flag = go template" {
|
@test "kpod ps format flag = go template" {
|
||||||
|
|
|
@ -19,6 +19,7 @@ function teardown() {
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
run crioctl ctr create --config "$TESTDATA"/container_config.json --pod "$pod_id"
|
run crioctl ctr create --config "$TESTDATA"/container_config.json --pod "$pod_id"
|
||||||
ctr_id="$output"
|
ctr_id="$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
run ${KPOD_BINARY} $KPOD_OPTIONS rename "$ctr_id" "$NEW_NAME"
|
run ${KPOD_BINARY} $KPOD_OPTIONS rename "$ctr_id" "$NEW_NAME"
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
|
|
|
@ -30,7 +30,6 @@ function teardown() {
|
||||||
run ${KPOD_BINARY} ${KPOD_OPTIONS} rmi $IMAGE
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} rmi $IMAGE
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
rm -f alpine.tar
|
rm -f alpine.tar
|
||||||
[ "$status" -eq 0 ]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "kpod save using stdout" {
|
@test "kpod save using stdout" {
|
||||||
|
|
|
@ -25,7 +25,10 @@ function teardown() {
|
||||||
run crioctl ctr start --id "$ctr_id"
|
run crioctl ctr start --id "$ctr_id"
|
||||||
echo "$output"
|
echo "$output"
|
||||||
id="$output"
|
id="$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
run ${KPOD_BINARY} ${KPOD_OPTIONS} stop "$id"
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} stop "$id"
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
cleanup_pods
|
cleanup_pods
|
||||||
stop_crio
|
stop_crio
|
||||||
}
|
}
|
||||||
|
@ -41,8 +44,15 @@ function teardown() {
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
ctr_id="$output"
|
ctr_id="$output"
|
||||||
run crioctl ctr start --id "$ctr_id"
|
run crioctl ctr start --id "$ctr_id"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run crioctl ctr inspect --id "$ctr_id"
|
||||||
echo "$output"
|
echo "$output"
|
||||||
run ${KPOD_BINARY} ${KPOD_OPTIONS} stop "k8s_podsandbox1-redis_podsandbox1_redhat.test.crio_redhat-test-crio_0"
|
[ "$status" -eq 0 ]
|
||||||
|
ctr_name=$(python -c 'import json; import sys; print json.load(sys.stdin)["crio_annotations"]["io.kubernetes.cri-o.Name"]' <<< "$output")
|
||||||
|
echo container name is \""$ctr_name"\"
|
||||||
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} stop "$ctr_name"
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
cleanup_pods
|
cleanup_pods
|
||||||
stop_crio
|
stop_crio
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ function container_start() {
|
||||||
@test "wait on a stopped container" {
|
@test "wait on a stopped container" {
|
||||||
run ${KPOD_BINARY} ${KPOD_OPTIONS} pull docker.io/library/busybox:latest
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} pull docker.io/library/busybox:latest
|
||||||
echo $output
|
echo $output
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
start_crio
|
start_crio
|
||||||
pod_id=$( pod_run_from_template "test" "test" "test1-1" )
|
pod_id=$( pod_run_from_template "test" "test" "test1-1" )
|
||||||
echo $pod_id
|
echo $pod_id
|
||||||
|
@ -50,6 +51,7 @@ function container_start() {
|
||||||
@test "wait on a sleeping container" {
|
@test "wait on a sleeping container" {
|
||||||
run ${KPOD_BINARY} ${KPOD_OPTIONS} pull docker.io/library/busybox:latest
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} pull docker.io/library/busybox:latest
|
||||||
echo $output
|
echo $output
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
start_crio
|
start_crio
|
||||||
pod_id=$( pod_run_from_template "test" "test" "test1-1" )
|
pod_id=$( pod_run_from_template "test" "test" "test1-1" )
|
||||||
echo $pod_id
|
echo $pod_id
|
||||||
|
@ -57,6 +59,7 @@ function container_start() {
|
||||||
echo $ctr_id
|
echo $ctr_id
|
||||||
run container_start $ctr_id
|
run container_start $ctr_id
|
||||||
echo $output
|
echo $output
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
run ${KPOD_BINARY} ${KPOD_OPTIONS} wait $ctr_id
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} wait $ctr_id
|
||||||
echo $output
|
echo $output
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
|
|
|
@ -7,6 +7,7 @@ function teardown() {
|
||||||
cleanup_pods
|
cleanup_pods
|
||||||
stop_crio
|
stop_crio
|
||||||
rm -f /var/lib/cni/networks/crionet_test_args/*
|
rm -f /var/lib/cni/networks/crionet_test_args/*
|
||||||
|
chmod 0755 $CONMON_BINARY
|
||||||
cleanup_test
|
cleanup_test
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,10 +122,8 @@ function teardown() {
|
||||||
ctr2_id="$output"
|
ctr2_id="$output"
|
||||||
|
|
||||||
ping_pod_from_pod $ctr1_id $ctr2_id
|
ping_pod_from_pod $ctr1_id $ctr2_id
|
||||||
[ "$status" -eq 0 ]
|
|
||||||
|
|
||||||
ping_pod_from_pod $ctr2_id $ctr1_id
|
ping_pod_from_pod $ctr2_id $ctr1_id
|
||||||
[ "$status" -eq 0 ]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "Ensure correct CNI plugin namespace/name/container-id arguments" {
|
@test "Ensure correct CNI plugin namespace/name/container-id arguments" {
|
||||||
|
@ -165,6 +164,7 @@ function teardown() {
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
run crioctl ctr stop --id "$ctr_id"
|
run crioctl ctr stop --id "$ctr_id"
|
||||||
echo "$output"
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "Clean up network if pod sandbox fails" {
|
@test "Clean up network if pod sandbox fails" {
|
||||||
|
@ -172,9 +172,11 @@ function teardown() {
|
||||||
|
|
||||||
# make conmon non-executable to cause the sandbox setup to fail after
|
# make conmon non-executable to cause the sandbox setup to fail after
|
||||||
# networking has been configured
|
# networking has been configured
|
||||||
chmod 0644 /go/src/github.com/kubernetes-incubator/cri-o/conmon/conmon
|
chmod 0644 $CONMON_BINARY
|
||||||
run crioctl pod run --config "$TESTDATA"/sandbox_config.json
|
run crioctl pod run --config "$TESTDATA"/sandbox_config.json
|
||||||
chmod 0755 /go/src/github.com/kubernetes-incubator/cri-o/conmon/conmon
|
chmod 0755 $CONMON_BINARY
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -ne 0 ]
|
||||||
|
|
||||||
# ensure that the server cleaned up sandbox networking if the sandbox
|
# ensure that the server cleaned up sandbox networking if the sandbox
|
||||||
# failed after network setup
|
# failed after network setup
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"name": "podsandbox1-redis"
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
"image": "redis:alpine"
|
||||||
|
},
|
||||||
|
"args": [
|
||||||
|
"docker-entrypoint.sh",
|
||||||
|
"redis-server"
|
||||||
|
],
|
||||||
|
"mounts": [
|
||||||
|
{
|
||||||
|
"container_path": "%CPATH%",
|
||||||
|
"host_path": "%HPATH%"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"working_dir": "/data",
|
||||||
|
"envs": [
|
||||||
|
{
|
||||||
|
"key": "PATH",
|
||||||
|
"value": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "TERM",
|
||||||
|
"value": "xterm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "REDIS_VERSION",
|
||||||
|
"value": "3.2.3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "REDIS_DOWNLOAD_URL",
|
||||||
|
"value": "http://download.redis.io/releases/redis-3.2.3.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "REDIS_DOWNLOAD_SHA1",
|
||||||
|
"value": "92d6d93ef2efc91e595c8bf578bf72baff397507"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"labels": {
|
||||||
|
"tier": "backend"
|
||||||
|
},
|
||||||
|
"annotations": {
|
||||||
|
"pod": "podsandbox1"
|
||||||
|
},
|
||||||
|
"readonly_rootfs": false,
|
||||||
|
"log_path": "",
|
||||||
|
"stdin": false,
|
||||||
|
"stdin_once": false,
|
||||||
|
"tty": false,
|
||||||
|
"linux": {
|
||||||
|
"resources": {
|
||||||
|
"cpu_period": 10000,
|
||||||
|
"cpu_quota": 20000,
|
||||||
|
"cpu_shares": 512,
|
||||||
|
"oom_score_adj": 30
|
||||||
|
},
|
||||||
|
"security_context": {
|
||||||
|
"capabilities": {
|
||||||
|
"add_capabilities": [
|
||||||
|
"sys_admin"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
{
|
||||||
|
"metadata": {
|
||||||
|
"name": "podsandbox1-redis"
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
"image": "redis:alpine"
|
||||||
|
},
|
||||||
|
"args": [
|
||||||
|
"docker-entrypoint.sh",
|
||||||
|
"redis-server"
|
||||||
|
],
|
||||||
|
"working_dir": "/data",
|
||||||
|
"envs": [
|
||||||
|
{
|
||||||
|
"key": "PATH",
|
||||||
|
"value": "/acustompathinpath:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "TERM",
|
||||||
|
"value": "xterm"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "REDIS_VERSION",
|
||||||
|
"value": "3.2.3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "REDIS_DOWNLOAD_URL",
|
||||||
|
"value": "http://download.redis.io/releases/redis-3.2.3.tar.gz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "REDIS_DOWNLOAD_SHA1",
|
||||||
|
"value": "92d6d93ef2efc91e595c8bf578bf72baff397507"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"labels": {
|
||||||
|
"tier": "backend"
|
||||||
|
},
|
||||||
|
"annotations": {
|
||||||
|
"pod": "podsandbox1"
|
||||||
|
},
|
||||||
|
"readonly_rootfs": false,
|
||||||
|
"log_path": "",
|
||||||
|
"stdin": false,
|
||||||
|
"stdin_once": false,
|
||||||
|
"tty": false,
|
||||||
|
"linux": {
|
||||||
|
"resources": {
|
||||||
|
"memory_limit_in_bytes": 209715200,
|
||||||
|
"cpu_period": 10000,
|
||||||
|
"cpu_quota": 20000,
|
||||||
|
"cpu_shares": 512,
|
||||||
|
"oom_score_adj": 30
|
||||||
|
},
|
||||||
|
"security_context": {
|
||||||
|
"capabilities": {
|
||||||
|
"add_capabilities": [
|
||||||
|
"sys_admin"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ github.com/ostreedev/ostree-go master
|
||||||
github.com/containers/storage 64bf27465d0d1edd89e7a4ce49866fea01145782
|
github.com/containers/storage 64bf27465d0d1edd89e7a4ce49866fea01145782
|
||||||
github.com/containernetworking/cni v0.4.0
|
github.com/containernetworking/cni v0.4.0
|
||||||
google.golang.org/grpc v1.0.4 https://github.com/grpc/grpc-go
|
google.golang.org/grpc v1.0.4 https://github.com/grpc/grpc-go
|
||||||
github.com/opencontainers/selinux v1.0.0-rc1
|
github.com/opencontainers/selinux b29023b86e4a69d1b46b7e7b4e2b6fda03f0b9cd
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc0
|
github.com/opencontainers/go-digest v1.0.0-rc0
|
||||||
github.com/opencontainers/runtime-tools d3f7e9e9e631c7e87552d67dc7c86de33c3fb68a
|
github.com/opencontainers/runtime-tools d3f7e9e9e631c7e87552d67dc7c86de33c3fb68a
|
||||||
github.com/opencontainers/runc 45bde006ca8c90e089894508708bcf0e2cdf9e13
|
github.com/opencontainers/runc 45bde006ca8c90e089894508708bcf0e2cdf9e13
|
||||||
|
@ -71,9 +71,9 @@ github.com/Microsoft/hcsshim 43f9725307998e09f2e3816c2c0c36dc98f0c982
|
||||||
github.com/emicklei/go-restful ff4f55a206334ef123e4f79bbf348980da81ca46
|
github.com/emicklei/go-restful ff4f55a206334ef123e4f79bbf348980da81ca46
|
||||||
github.com/emicklei/go-restful-swagger12 1.0.1
|
github.com/emicklei/go-restful-swagger12 1.0.1
|
||||||
github.com/pkg/errors v0.8.0
|
github.com/pkg/errors v0.8.0
|
||||||
github.com/godbus/dbus v4.0.0
|
github.com/godbus/dbus a389bdde4dd695d414e47b755e95e72b7826432c
|
||||||
github.com/urfave/cli v1.20.0
|
github.com/urfave/cli v1.20.0
|
||||||
github.com/vbatts/tar-split v0.10.1
|
github.com/vbatts/tar-split v0.10.2
|
||||||
github.com/renstrom/dedent v1.0.0
|
github.com/renstrom/dedent v1.0.0
|
||||||
github.com/hpcloud/tail v1.0.0
|
github.com/hpcloud/tail v1.0.0
|
||||||
gopkg.in/fsnotify.v1 v1.4.2
|
gopkg.in/fsnotify.v1 v1.4.2
|
||||||
|
@ -101,3 +101,5 @@ github.com/go-zoo/bone 031b4005dfe248ccba241a0c9de0f9e112fd6b7c
|
||||||
github.com/soheilhy/cmux v0.1.3
|
github.com/soheilhy/cmux v0.1.3
|
||||||
github.com/hashicorp/go-multierror 83588e72410abfbe4df460eeb6f30841ae47d4c4
|
github.com/hashicorp/go-multierror 83588e72410abfbe4df460eeb6f30841ae47d4c4
|
||||||
github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55
|
github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55
|
||||||
|
github.com/stretchr/testify 4d4bfba8f1d1027c4fdbe371823030df51419987
|
||||||
|
github.com/pmezard/go-difflib v1.0.0
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
## About
|
|
||||||
|
|
||||||
This directory contains a collection of scripts used to build and manage this
|
|
||||||
repository. If there are any issues regarding the intention of a particular
|
|
||||||
script (or even part of a certain script), please reach out to us.
|
|
||||||
It may help us either refine our current scripts, or add on new ones
|
|
||||||
that are appropriate for a given use case.
|
|
||||||
|
|
||||||
## DinD (dind.sh)
|
|
||||||
|
|
||||||
DinD is a wrapper script which allows Docker to be run inside a Docker
|
|
||||||
container. DinD requires the container to
|
|
||||||
be run with privileged mode enabled.
|
|
||||||
|
|
||||||
## Generate Authors (generate-authors.sh)
|
|
||||||
|
|
||||||
Generates AUTHORS; a file with all the names and corresponding emails of
|
|
||||||
individual contributors. AUTHORS can be found in the home directory of
|
|
||||||
this repository.
|
|
||||||
|
|
||||||
## Make
|
|
||||||
|
|
||||||
There are two make files, each with different extensions. Neither are supposed
|
|
||||||
to be called directly; only invoke `make`. Both scripts run inside a Docker
|
|
||||||
container.
|
|
||||||
|
|
||||||
### make.ps1
|
|
||||||
|
|
||||||
- The Windows native build script that uses PowerShell semantics; it is limited
|
|
||||||
unlike `hack\make.sh` since it does not provide support for the full set of
|
|
||||||
operations provided by the Linux counterpart, `make.sh`. However, `make.ps1`
|
|
||||||
does provide support for local Windows development and Windows to Windows CI.
|
|
||||||
More information is found within `make.ps1` by the author, @jhowardmsft
|
|
||||||
|
|
||||||
### make.sh
|
|
||||||
|
|
||||||
- Referenced via `make test` when running tests on a local machine,
|
|
||||||
or directly referenced when running tests inside a Docker development container.
|
|
||||||
- When running on a local machine, `make test` to run all tests found in
|
|
||||||
`test`, `test-unit`, `test-integration-cli`, and `test-docker-py` on
|
|
||||||
your local machine. The default timeout is set in `make.sh` to 60 minutes
|
|
||||||
(`${TIMEOUT:=60m}`), since it currently takes up to an hour to run
|
|
||||||
all of the tests.
|
|
||||||
- When running inside a Docker development container, `hack/make.sh` does
|
|
||||||
not have a single target that runs all the tests. You need to provide a
|
|
||||||
single command line with multiple targets that performs the same thing.
|
|
||||||
An example referenced from [Run targets inside a development container](https://docs.docker.com/opensource/project/test-and-docs/#run-targets-inside-a-development-container): `root@5f8630b873fe:/go/src/github.com/moby/moby# hack/make.sh dynbinary binary cross test-unit test-integration-cli test-docker-py`
|
|
||||||
- For more information related to testing outside the scope of this README,
|
|
||||||
refer to
|
|
||||||
[Run tests and test documentation](https://docs.docker.com/opensource/project/test-and-docs/)
|
|
||||||
|
|
||||||
## Release (release.sh)
|
|
||||||
|
|
||||||
Releases any bundles built by `make` on a public AWS S3 bucket.
|
|
||||||
For information regarding configuration, please view `release.sh`.
|
|
||||||
|
|
||||||
## Vendor (vendor.sh)
|
|
||||||
|
|
||||||
A shell script that is a wrapper around Vndr. For information on how to use
|
|
||||||
this, please refer to [vndr's README](https://github.com/LK4D4/vndr/blob/master/README.md)
|
|
|
@ -1,69 +0,0 @@
|
||||||
# Integration Testing on Swarm
|
|
||||||
|
|
||||||
IT on Swarm allows you to execute integration test in parallel across a Docker Swarm cluster
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
### Master service
|
|
||||||
|
|
||||||
- Works as a funker caller
|
|
||||||
- Calls a worker funker (`-worker-service`) with a chunk of `-check.f` filter strings (passed as a file via `-input` flag, typically `/mnt/input`)
|
|
||||||
|
|
||||||
### Worker service
|
|
||||||
|
|
||||||
- Works as a funker callee
|
|
||||||
- Executes an equivalent of `TESTFLAGS=-check.f TestFoo|TestBar|TestBaz ... make test-integration-cli` using the bind-mounted API socket (`docker.sock`)
|
|
||||||
|
|
||||||
### Client
|
|
||||||
|
|
||||||
- Controls master and workers via `docker stack`
|
|
||||||
- No need to have a local daemon
|
|
||||||
|
|
||||||
Typically, the master and workers are supposed to be running on a cloud environment,
|
|
||||||
while the client is supposed to be running on a laptop, e.g. Docker for Mac/Windows.
|
|
||||||
|
|
||||||
## Requirement
|
|
||||||
|
|
||||||
- Docker daemon 1.13 or later
|
|
||||||
- Private registry for distributed execution with multiple nodes
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### Step 1: Prepare images
|
|
||||||
|
|
||||||
$ make build-integration-cli-on-swarm
|
|
||||||
|
|
||||||
Following environment variables are known to work in this step:
|
|
||||||
|
|
||||||
- `BUILDFLAGS`
|
|
||||||
- `DOCKER_INCREMENTAL_BINARY`
|
|
||||||
|
|
||||||
Note: during the transition into Moby Project, you might need to create a symbolic link `$GOPATH/src/github.com/docker/docker` to `$GOPATH/src/github.com/moby/moby`.
|
|
||||||
|
|
||||||
### Step 2: Execute tests
|
|
||||||
|
|
||||||
$ ./hack/integration-cli-on-swarm/integration-cli-on-swarm -replicas 40 -push-worker-image YOUR_REGISTRY.EXAMPLE.COM/integration-cli-worker:latest
|
|
||||||
|
|
||||||
Following environment variables are known to work in this step:
|
|
||||||
|
|
||||||
- `DOCKER_GRAPHDRIVER`
|
|
||||||
- `DOCKER_EXPERIMENTAL`
|
|
||||||
|
|
||||||
#### Flags
|
|
||||||
|
|
||||||
Basic flags:
|
|
||||||
|
|
||||||
- `-replicas N`: the number of worker service replicas. i.e. degree of parallelism.
|
|
||||||
- `-chunks N`: the number of chunks. By default, `chunks` == `replicas`.
|
|
||||||
- `-push-worker-image REGISTRY/IMAGE:TAG`: push the worker image to the registry. Note that if you have only single node and hence you do not need a private registry, you do not need to specify `-push-worker-image`.
|
|
||||||
|
|
||||||
Experimental flags for mitigating makespan nonuniformity:
|
|
||||||
|
|
||||||
- `-shuffle`: Shuffle the test filter strings
|
|
||||||
|
|
||||||
Flags for debugging IT on Swarm itself:
|
|
||||||
|
|
||||||
- `-rand-seed N`: the random seed. This flag is useful for deterministic replaying. By default(0), the timestamp is used.
|
|
||||||
- `-filters-file FILE`: the file contains `-check.f` strings. By default, the file is automatically generated.
|
|
||||||
- `-dry-run`: skip the actual workload
|
|
||||||
- `keep-executor`: do not auto-remove executor containers, which is used for running privileged programs on Swarm
|
|
2
vendor/github.com/docker/docker/hack/integration-cli-on-swarm/agent/vendor.conf
generated
vendored
2
vendor/github.com/docker/docker/hack/integration-cli-on-swarm/agent/vendor.conf
generated
vendored
|
@ -1,2 +0,0 @@
|
||||||
# dependencies specific to worker (i.e. github.com/docker/docker/...) are not vendored here
|
|
||||||
github.com/bfirsh/funker-go eaa0a2e06f30e72c9a0b7f858951e581e26ef773
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
[![Build Status](https://travis-ci.org/godbus/dbus.svg?branch=master)](https://travis-ci.org/godbus/dbus)
|
||||||
|
|
||||||
dbus
|
dbus
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@ -29,6 +31,7 @@ gives a short overview over the basic usage.
|
||||||
|
|
||||||
#### Projects using godbus
|
#### Projects using godbus
|
||||||
- [notify](https://github.com/esiqveland/notify) provides desktop notifications over dbus into a library.
|
- [notify](https://github.com/esiqveland/notify) provides desktop notifications over dbus into a library.
|
||||||
|
- [go-bluetooth](https://github.com/muka/go-bluetooth) provides a bluetooth client over bluez dbus API.
|
||||||
|
|
||||||
Please note that the API is considered unstable for now and may change without
|
Please note that the API is considered unstable for now and may change without
|
||||||
further notice.
|
further notice.
|
||||||
|
|
|
@ -9,8 +9,6 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket"
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
systemBus *Conn
|
systemBus *Conn
|
||||||
systemBusLck sync.Mutex
|
systemBusLck sync.Mutex
|
||||||
|
@ -47,15 +45,13 @@ type Conn struct {
|
||||||
calls map[uint32]*Call
|
calls map[uint32]*Call
|
||||||
callsLck sync.RWMutex
|
callsLck sync.RWMutex
|
||||||
|
|
||||||
handlers map[ObjectPath]map[string]exportedObj
|
handler Handler
|
||||||
handlersLck sync.RWMutex
|
|
||||||
|
|
||||||
out chan *Message
|
out chan *Message
|
||||||
closed bool
|
closed bool
|
||||||
outLck sync.RWMutex
|
outLck sync.RWMutex
|
||||||
|
|
||||||
signals []chan<- *Signal
|
signalHandler SignalHandler
|
||||||
signalsLck sync.Mutex
|
|
||||||
|
|
||||||
eavesdropped chan<- *Message
|
eavesdropped chan<- *Message
|
||||||
eavesdroppedLck sync.Mutex
|
eavesdroppedLck sync.Mutex
|
||||||
|
@ -90,16 +86,33 @@ func SessionBus() (conn *Conn, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// SessionBusPrivate returns a new private connection to the session bus.
|
func getSessionBusAddress() (string, error) {
|
||||||
func SessionBusPrivate() (*Conn, error) {
|
|
||||||
sessionEnvLck.Lock()
|
sessionEnvLck.Lock()
|
||||||
defer sessionEnvLck.Unlock()
|
defer sessionEnvLck.Unlock()
|
||||||
address := os.Getenv("DBUS_SESSION_BUS_ADDRESS")
|
address := os.Getenv("DBUS_SESSION_BUS_ADDRESS")
|
||||||
if address != "" && address != "autolaunch:" {
|
if address != "" && address != "autolaunch:" {
|
||||||
return Dial(address)
|
return address, nil
|
||||||
|
}
|
||||||
|
return getSessionBusPlatformAddress()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionBusPrivate returns a new private connection to the session bus.
|
||||||
|
func SessionBusPrivate() (*Conn, error) {
|
||||||
|
address, err := getSessionBusAddress()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return sessionBusPlatform()
|
return Dial(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionBusPrivate returns a new private connection to the session bus.
|
||||||
|
func SessionBusPrivateHandler(handler Handler, signalHandler SignalHandler) (*Conn, error) {
|
||||||
|
address, err := getSessionBusAddress()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return DialHandler(address, handler, signalHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SystemBus returns a shared connection to the system bus, connecting to it if
|
// SystemBus returns a shared connection to the system bus, connecting to it if
|
||||||
|
@ -133,11 +146,12 @@ func SystemBus() (conn *Conn, err error) {
|
||||||
|
|
||||||
// SystemBusPrivate returns a new private connection to the system bus.
|
// SystemBusPrivate returns a new private connection to the system bus.
|
||||||
func SystemBusPrivate() (*Conn, error) {
|
func SystemBusPrivate() (*Conn, error) {
|
||||||
address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS")
|
return Dial(getSystemBusPlatformAddress())
|
||||||
if address != "" {
|
}
|
||||||
return Dial(address)
|
|
||||||
}
|
// SystemBusPrivateHandler returns a new private connection to the system bus, using the provided handlers.
|
||||||
return Dial(defaultSystemBusAddress)
|
func SystemBusPrivateHandler(handler Handler, signalHandler SignalHandler) (*Conn, error) {
|
||||||
|
return DialHandler(getSystemBusPlatformAddress(), handler, signalHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dial establishes a new private connection to the message bus specified by address.
|
// Dial establishes a new private connection to the message bus specified by address.
|
||||||
|
@ -146,21 +160,36 @@ func Dial(address string) (*Conn, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return newConn(tr)
|
return newConn(tr, NewDefaultHandler(), NewDefaultSignalHandler())
|
||||||
|
}
|
||||||
|
|
||||||
|
// DialHandler establishes a new private connection to the message bus specified by address, using the supplied handlers.
|
||||||
|
func DialHandler(address string, handler Handler, signalHandler SignalHandler) (*Conn, error) {
|
||||||
|
tr, err := getTransport(address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newConn(tr, handler, signalHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConn creates a new private *Conn from an already established connection.
|
// NewConn creates a new private *Conn from an already established connection.
|
||||||
func NewConn(conn io.ReadWriteCloser) (*Conn, error) {
|
func NewConn(conn io.ReadWriteCloser) (*Conn, error) {
|
||||||
return newConn(genericTransport{conn})
|
return NewConnHandler(conn, NewDefaultHandler(), NewDefaultSignalHandler())
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConnHandler creates a new private *Conn from an already established connection, using the supplied handlers.
|
||||||
|
func NewConnHandler(conn io.ReadWriteCloser, handler Handler, signalHandler SignalHandler) (*Conn, error) {
|
||||||
|
return newConn(genericTransport{conn}, handler, signalHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newConn creates a new *Conn from a transport.
|
// newConn creates a new *Conn from a transport.
|
||||||
func newConn(tr transport) (*Conn, error) {
|
func newConn(tr transport, handler Handler, signalHandler SignalHandler) (*Conn, error) {
|
||||||
conn := new(Conn)
|
conn := new(Conn)
|
||||||
conn.transport = tr
|
conn.transport = tr
|
||||||
conn.calls = make(map[uint32]*Call)
|
conn.calls = make(map[uint32]*Call)
|
||||||
conn.out = make(chan *Message, 10)
|
conn.out = make(chan *Message, 10)
|
||||||
conn.handlers = make(map[ObjectPath]map[string]exportedObj)
|
conn.handler = handler
|
||||||
|
conn.signalHandler = signalHandler
|
||||||
conn.nextSerial = 1
|
conn.nextSerial = 1
|
||||||
conn.serialUsed = map[uint32]bool{0: true}
|
conn.serialUsed = map[uint32]bool{0: true}
|
||||||
conn.busObj = conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus")
|
conn.busObj = conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus")
|
||||||
|
@ -188,16 +217,21 @@ func (conn *Conn) Close() error {
|
||||||
close(conn.out)
|
close(conn.out)
|
||||||
conn.closed = true
|
conn.closed = true
|
||||||
conn.outLck.Unlock()
|
conn.outLck.Unlock()
|
||||||
conn.signalsLck.Lock()
|
|
||||||
for _, ch := range conn.signals {
|
if term, ok := conn.signalHandler.(Terminator); ok {
|
||||||
close(ch)
|
term.Terminate()
|
||||||
}
|
}
|
||||||
conn.signalsLck.Unlock()
|
|
||||||
|
if term, ok := conn.handler.(Terminator); ok {
|
||||||
|
term.Terminate()
|
||||||
|
}
|
||||||
|
|
||||||
conn.eavesdroppedLck.Lock()
|
conn.eavesdroppedLck.Lock()
|
||||||
if conn.eavesdropped != nil {
|
if conn.eavesdropped != nil {
|
||||||
close(conn.eavesdropped)
|
close(conn.eavesdropped)
|
||||||
}
|
}
|
||||||
conn.eavesdroppedLck.Unlock()
|
conn.eavesdroppedLck.Unlock()
|
||||||
|
|
||||||
return conn.transport.Close()
|
return conn.transport.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,17 +368,7 @@ func (conn *Conn) inWorker() {
|
||||||
conn.namesLck.Unlock()
|
conn.namesLck.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
signal := &Signal{
|
conn.handleSignal(msg)
|
||||||
Sender: sender,
|
|
||||||
Path: msg.Headers[FieldPath].value.(ObjectPath),
|
|
||||||
Name: iface + "." + member,
|
|
||||||
Body: msg.Body,
|
|
||||||
}
|
|
||||||
conn.signalsLck.Lock()
|
|
||||||
for _, ch := range conn.signals {
|
|
||||||
ch <- signal
|
|
||||||
}
|
|
||||||
conn.signalsLck.Unlock()
|
|
||||||
case TypeMethodCall:
|
case TypeMethodCall:
|
||||||
go conn.handleCall(msg)
|
go conn.handleCall(msg)
|
||||||
}
|
}
|
||||||
|
@ -365,6 +389,21 @@ func (conn *Conn) inWorker() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) handleSignal(msg *Message) {
|
||||||
|
iface := msg.Headers[FieldInterface].value.(string)
|
||||||
|
member := msg.Headers[FieldMember].value.(string)
|
||||||
|
// as per http://dbus.freedesktop.org/doc/dbus-specification.html ,
|
||||||
|
// sender is optional for signals.
|
||||||
|
sender, _ := msg.Headers[FieldSender].value.(string)
|
||||||
|
signal := &Signal{
|
||||||
|
Sender: sender,
|
||||||
|
Path: msg.Headers[FieldPath].value.(ObjectPath),
|
||||||
|
Name: iface + "." + member,
|
||||||
|
Body: msg.Body,
|
||||||
|
}
|
||||||
|
conn.signalHandler.DeliverSignal(iface, member, signal)
|
||||||
|
}
|
||||||
|
|
||||||
// Names returns the list of all names that are currently owned by this
|
// Names returns the list of all names that are currently owned by this
|
||||||
// connection. The slice is always at least one element long, the first element
|
// connection. The slice is always at least one element long, the first element
|
||||||
// being the unique name of the connection.
|
// being the unique name of the connection.
|
||||||
|
@ -455,7 +494,19 @@ func (conn *Conn) Send(msg *Message, ch chan *Call) *Call {
|
||||||
|
|
||||||
// sendError creates an error message corresponding to the parameters and sends
|
// sendError creates an error message corresponding to the parameters and sends
|
||||||
// it to conn.out.
|
// it to conn.out.
|
||||||
func (conn *Conn) sendError(e Error, dest string, serial uint32) {
|
func (conn *Conn) sendError(err error, dest string, serial uint32) {
|
||||||
|
var e *Error
|
||||||
|
switch em := err.(type) {
|
||||||
|
case Error:
|
||||||
|
e = &em
|
||||||
|
case *Error:
|
||||||
|
e = em
|
||||||
|
case DBusError:
|
||||||
|
name, body := em.DBusError()
|
||||||
|
e = NewError(name, body)
|
||||||
|
default:
|
||||||
|
e = MakeFailedError(err)
|
||||||
|
}
|
||||||
msg := new(Message)
|
msg := new(Message)
|
||||||
msg.Type = TypeError
|
msg.Type = TypeError
|
||||||
msg.serial = conn.getSerial()
|
msg.serial = conn.getSerial()
|
||||||
|
@ -498,6 +549,14 @@ func (conn *Conn) sendReply(dest string, serial uint32, values ...interface{}) {
|
||||||
conn.outLck.RUnlock()
|
conn.outLck.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) defaultSignalAction(fn func(h *defaultSignalHandler, ch chan<- *Signal), ch chan<- *Signal) {
|
||||||
|
if !isDefaultSignalHandler(conn.signalHandler) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
handler := conn.signalHandler.(*defaultSignalHandler)
|
||||||
|
fn(handler, ch)
|
||||||
|
}
|
||||||
|
|
||||||
// Signal registers the given channel to be passed all received signal messages.
|
// Signal registers the given channel to be passed all received signal messages.
|
||||||
// The caller has to make sure that ch is sufficiently buffered; if a message
|
// The caller has to make sure that ch is sufficiently buffered; if a message
|
||||||
// arrives when a write to c is not possible, it is discarded.
|
// arrives when a write to c is not possible, it is discarded.
|
||||||
|
@ -508,22 +567,12 @@ func (conn *Conn) sendReply(dest string, serial uint32, values ...interface{}) {
|
||||||
// channel for eavesdropped messages, this channel receives all signals, and
|
// channel for eavesdropped messages, this channel receives all signals, and
|
||||||
// none of the channels passed to Signal will receive any signals.
|
// none of the channels passed to Signal will receive any signals.
|
||||||
func (conn *Conn) Signal(ch chan<- *Signal) {
|
func (conn *Conn) Signal(ch chan<- *Signal) {
|
||||||
conn.signalsLck.Lock()
|
conn.defaultSignalAction((*defaultSignalHandler).addSignal, ch)
|
||||||
conn.signals = append(conn.signals, ch)
|
|
||||||
conn.signalsLck.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveSignal removes the given channel from the list of the registered channels.
|
// RemoveSignal removes the given channel from the list of the registered channels.
|
||||||
func (conn *Conn) RemoveSignal(ch chan<- *Signal) {
|
func (conn *Conn) RemoveSignal(ch chan<- *Signal) {
|
||||||
conn.signalsLck.Lock()
|
conn.defaultSignalAction((*defaultSignalHandler).removeSignal, ch)
|
||||||
for i := len(conn.signals) - 1; i >= 0; i-- {
|
|
||||||
if ch == conn.signals[i] {
|
|
||||||
copy(conn.signals[i:], conn.signals[i+1:])
|
|
||||||
conn.signals[len(conn.signals)-1] = nil
|
|
||||||
conn.signals = conn.signals[:len(conn.signals)-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conn.signalsLck.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SupportsUnixFDs returns whether the underlying transport supports passing of
|
// SupportsUnixFDs returns whether the underlying transport supports passing of
|
||||||
|
|
|
@ -2,20 +2,32 @@ package dbus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
)
|
)
|
||||||
|
|
||||||
func sessionBusPlatform() (*Conn, error) {
|
const defaultSystemBusAddress = "unix:path=/opt/local/var/run/dbus/system_bus_socket"
|
||||||
|
|
||||||
|
func getSessionBusPlatformAddress() (string, error) {
|
||||||
cmd := exec.Command("launchctl", "getenv", "DBUS_LAUNCHD_SESSION_BUS_SOCKET")
|
cmd := exec.Command("launchctl", "getenv", "DBUS_LAUNCHD_SESSION_BUS_SOCKET")
|
||||||
b, err := cmd.CombinedOutput()
|
b, err := cmd.CombinedOutput()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(b) == 0 {
|
if len(b) == 0 {
|
||||||
return nil, errors.New("dbus: couldn't determine address of session bus")
|
return "", errors.New("dbus: couldn't determine address of session bus")
|
||||||
}
|
}
|
||||||
|
|
||||||
return Dial("unix:path=" + string(b[:len(b)-1]))
|
return "unix:path=" + string(b[:len(b)-1]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSystemBusPlatformAddress() string {
|
||||||
|
address := os.Getenv("DBUS_LAUNCHD_SESSION_BUS_SOCKET")
|
||||||
|
if address != "" {
|
||||||
|
return fmt.Sprintf("unix:path=%s", address)
|
||||||
|
}
|
||||||
|
return defaultSystemBusAddress
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,27 +5,38 @@ package dbus
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
)
|
)
|
||||||
|
|
||||||
func sessionBusPlatform() (*Conn, error) {
|
const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket"
|
||||||
|
|
||||||
|
func getSessionBusPlatformAddress() (string, error) {
|
||||||
cmd := exec.Command("dbus-launch")
|
cmd := exec.Command("dbus-launch")
|
||||||
b, err := cmd.CombinedOutput()
|
b, err := cmd.CombinedOutput()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
i := bytes.IndexByte(b, '=')
|
i := bytes.IndexByte(b, '=')
|
||||||
j := bytes.IndexByte(b, '\n')
|
j := bytes.IndexByte(b, '\n')
|
||||||
|
|
||||||
if i == -1 || j == -1 {
|
if i == -1 || j == -1 {
|
||||||
return nil, errors.New("dbus: couldn't determine address of session bus")
|
return "", errors.New("dbus: couldn't determine address of session bus")
|
||||||
}
|
}
|
||||||
|
|
||||||
env, addr := string(b[0:i]), string(b[i+1:j])
|
env, addr := string(b[0:i]), string(b[i+1:j])
|
||||||
os.Setenv(env, addr)
|
os.Setenv(env, addr)
|
||||||
|
|
||||||
return Dial(addr)
|
return addr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSystemBusPlatformAddress() string {
|
||||||
|
address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS")
|
||||||
|
if address != "" {
|
||||||
|
return fmt.Sprintf("unix:path=%s", address)
|
||||||
|
}
|
||||||
|
return defaultSystemBusAddress
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package dbus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
@ -12,6 +13,8 @@ var (
|
||||||
uint8Type = reflect.TypeOf(uint8(0))
|
uint8Type = reflect.TypeOf(uint8(0))
|
||||||
int16Type = reflect.TypeOf(int16(0))
|
int16Type = reflect.TypeOf(int16(0))
|
||||||
uint16Type = reflect.TypeOf(uint16(0))
|
uint16Type = reflect.TypeOf(uint16(0))
|
||||||
|
intType = reflect.TypeOf(int(0))
|
||||||
|
uintType = reflect.TypeOf(uint(0))
|
||||||
int32Type = reflect.TypeOf(int32(0))
|
int32Type = reflect.TypeOf(int32(0))
|
||||||
uint32Type = reflect.TypeOf(uint32(0))
|
uint32Type = reflect.TypeOf(uint32(0))
|
||||||
int64Type = reflect.TypeOf(int64(0))
|
int64Type = reflect.TypeOf(int64(0))
|
||||||
|
@ -22,6 +25,7 @@ var (
|
||||||
objectPathType = reflect.TypeOf(ObjectPath(""))
|
objectPathType = reflect.TypeOf(ObjectPath(""))
|
||||||
variantType = reflect.TypeOf(Variant{Signature{""}, nil})
|
variantType = reflect.TypeOf(Variant{Signature{""}, nil})
|
||||||
interfacesType = reflect.TypeOf([]interface{}{})
|
interfacesType = reflect.TypeOf([]interface{}{})
|
||||||
|
interfaceType = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||||
unixFDType = reflect.TypeOf(UnixFD(0))
|
unixFDType = reflect.TypeOf(UnixFD(0))
|
||||||
unixFDIndexType = reflect.TypeOf(UnixFDIndex(0))
|
unixFDIndexType = reflect.TypeOf(UnixFDIndex(0))
|
||||||
)
|
)
|
||||||
|
@ -46,86 +50,251 @@ func Store(src []interface{}, dest ...interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range src {
|
for i := range src {
|
||||||
if err := store(src[i], dest[i]); err != nil {
|
if err := storeInterfaces(src[i], dest[i]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func store(src, dest interface{}) error {
|
func storeInterfaces(src, dest interface{}) error {
|
||||||
if reflect.TypeOf(dest).Elem() == reflect.TypeOf(src) {
|
return store(reflect.ValueOf(dest), reflect.ValueOf(src))
|
||||||
reflect.ValueOf(dest).Elem().Set(reflect.ValueOf(src))
|
}
|
||||||
return nil
|
|
||||||
} else if hasStruct(dest) {
|
func store(dest, src reflect.Value) error {
|
||||||
rv := reflect.ValueOf(dest).Elem()
|
if dest.Kind() == reflect.Ptr {
|
||||||
switch rv.Kind() {
|
return store(dest.Elem(), src)
|
||||||
case reflect.Struct:
|
}
|
||||||
vs, ok := src.([]interface{})
|
switch src.Kind() {
|
||||||
if !ok {
|
case reflect.Slice:
|
||||||
return errors.New("dbus.Store: type mismatch")
|
return storeSlice(dest, src)
|
||||||
}
|
case reflect.Map:
|
||||||
t := rv.Type()
|
return storeMap(dest, src)
|
||||||
ndest := make([]interface{}, 0, rv.NumField())
|
default:
|
||||||
for i := 0; i < rv.NumField(); i++ {
|
return storeBase(dest, src)
|
||||||
field := t.Field(i)
|
|
||||||
if field.PkgPath == "" && field.Tag.Get("dbus") != "-" {
|
|
||||||
ndest = append(ndest, rv.Field(i).Addr().Interface())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(vs) != len(ndest) {
|
|
||||||
return errors.New("dbus.Store: type mismatch")
|
|
||||||
}
|
|
||||||
err := Store(vs, ndest...)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("dbus.Store: type mismatch")
|
|
||||||
}
|
|
||||||
case reflect.Slice:
|
|
||||||
sv := reflect.ValueOf(src)
|
|
||||||
if sv.Kind() != reflect.Slice {
|
|
||||||
return errors.New("dbus.Store: type mismatch")
|
|
||||||
}
|
|
||||||
rv.Set(reflect.MakeSlice(rv.Type(), sv.Len(), sv.Len()))
|
|
||||||
for i := 0; i < sv.Len(); i++ {
|
|
||||||
if err := store(sv.Index(i).Interface(), rv.Index(i).Addr().Interface()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Map:
|
|
||||||
sv := reflect.ValueOf(src)
|
|
||||||
if sv.Kind() != reflect.Map {
|
|
||||||
return errors.New("dbus.Store: type mismatch")
|
|
||||||
}
|
|
||||||
keys := sv.MapKeys()
|
|
||||||
rv.Set(reflect.MakeMap(sv.Type()))
|
|
||||||
for _, key := range keys {
|
|
||||||
v := reflect.New(sv.Type().Elem())
|
|
||||||
if err := store(v, sv.MapIndex(key).Interface()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rv.SetMapIndex(key, v.Elem())
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return errors.New("dbus.Store: type mismatch")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
return errors.New("dbus.Store: type mismatch")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func hasStruct(v interface{}) bool {
|
func storeBase(dest, src reflect.Value) error {
|
||||||
t := reflect.TypeOf(v)
|
return setDest(dest, src)
|
||||||
for {
|
}
|
||||||
switch t.Kind() {
|
|
||||||
case reflect.Struct:
|
func setDest(dest, src reflect.Value) error {
|
||||||
return true
|
if !isVariant(src.Type()) && isVariant(dest.Type()) {
|
||||||
case reflect.Slice, reflect.Ptr, reflect.Map:
|
//special conversion for dbus.Variant
|
||||||
t = t.Elem()
|
dest.Set(reflect.ValueOf(MakeVariant(src.Interface())))
|
||||||
default:
|
return nil
|
||||||
return false
|
}
|
||||||
|
if isVariant(src.Type()) && !isVariant(dest.Type()) {
|
||||||
|
src = getVariantValue(src)
|
||||||
|
}
|
||||||
|
if !src.Type().ConvertibleTo(dest.Type()) {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"dbus.Store: type mismatch: cannot convert %s to %s",
|
||||||
|
src.Type(), dest.Type())
|
||||||
|
}
|
||||||
|
dest.Set(src.Convert(dest.Type()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func kindsAreCompatible(dest, src reflect.Type) bool {
|
||||||
|
switch {
|
||||||
|
case isVariant(dest):
|
||||||
|
return true
|
||||||
|
case dest.Kind() == reflect.Interface:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return dest.Kind() == src.Kind()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isConvertibleTo(dest, src reflect.Type) bool {
|
||||||
|
switch {
|
||||||
|
case isVariant(dest):
|
||||||
|
return true
|
||||||
|
case dest.Kind() == reflect.Interface:
|
||||||
|
return true
|
||||||
|
case dest.Kind() == reflect.Slice:
|
||||||
|
return src.Kind() == reflect.Slice &&
|
||||||
|
isConvertibleTo(dest.Elem(), src.Elem())
|
||||||
|
case dest.Kind() == reflect.Struct:
|
||||||
|
return src == interfacesType
|
||||||
|
default:
|
||||||
|
return src.ConvertibleTo(dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeMap(dest, src reflect.Value) error {
|
||||||
|
switch {
|
||||||
|
case !kindsAreCompatible(dest.Type(), src.Type()):
|
||||||
|
return fmt.Errorf(
|
||||||
|
"dbus.Store: type mismatch: "+
|
||||||
|
"map: cannot store a value of %s into %s",
|
||||||
|
src.Type(), dest.Type())
|
||||||
|
case isVariant(dest.Type()):
|
||||||
|
return storeMapIntoVariant(dest, src)
|
||||||
|
case dest.Kind() == reflect.Interface:
|
||||||
|
return storeMapIntoInterface(dest, src)
|
||||||
|
case isConvertibleTo(dest.Type().Key(), src.Type().Key()) &&
|
||||||
|
isConvertibleTo(dest.Type().Elem(), src.Type().Elem()):
|
||||||
|
return storeMapIntoMap(dest, src)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf(
|
||||||
|
"dbus.Store: type mismatch: "+
|
||||||
|
"map: cannot convert a value of %s into %s",
|
||||||
|
src.Type(), dest.Type())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeMapIntoVariant(dest, src reflect.Value) error {
|
||||||
|
dv := reflect.MakeMap(src.Type())
|
||||||
|
err := store(dv, src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return storeBase(dest, dv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeMapIntoInterface(dest, src reflect.Value) error {
|
||||||
|
var dv reflect.Value
|
||||||
|
if isVariant(src.Type().Elem()) {
|
||||||
|
//Convert variants to interface{} recursively when converting
|
||||||
|
//to interface{}
|
||||||
|
dv = reflect.MakeMap(
|
||||||
|
reflect.MapOf(src.Type().Key(), interfaceType))
|
||||||
|
} else {
|
||||||
|
dv = reflect.MakeMap(src.Type())
|
||||||
|
}
|
||||||
|
err := store(dv, src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return storeBase(dest, dv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeMapIntoMap(dest, src reflect.Value) error {
|
||||||
|
if dest.IsNil() {
|
||||||
|
dest.Set(reflect.MakeMap(dest.Type()))
|
||||||
|
}
|
||||||
|
keys := src.MapKeys()
|
||||||
|
for _, key := range keys {
|
||||||
|
dkey := key.Convert(dest.Type().Key())
|
||||||
|
dval := reflect.New(dest.Type().Elem()).Elem()
|
||||||
|
err := store(dval, getVariantValue(src.MapIndex(key)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dest.SetMapIndex(dkey, dval)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeSlice(dest, src reflect.Value) error {
|
||||||
|
switch {
|
||||||
|
case src.Type() == interfacesType && dest.Kind() == reflect.Struct:
|
||||||
|
//The decoder always decodes structs as slices of interface{}
|
||||||
|
return storeStruct(dest, src)
|
||||||
|
case !kindsAreCompatible(dest.Type(), src.Type()):
|
||||||
|
return fmt.Errorf(
|
||||||
|
"dbus.Store: type mismatch: "+
|
||||||
|
"slice: cannot store a value of %s into %s",
|
||||||
|
src.Type(), dest.Type())
|
||||||
|
case isVariant(dest.Type()):
|
||||||
|
return storeSliceIntoVariant(dest, src)
|
||||||
|
case dest.Kind() == reflect.Interface:
|
||||||
|
return storeSliceIntoInterface(dest, src)
|
||||||
|
case isConvertibleTo(dest.Type().Elem(), src.Type().Elem()):
|
||||||
|
return storeSliceIntoSlice(dest, src)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf(
|
||||||
|
"dbus.Store: type mismatch: "+
|
||||||
|
"slice: cannot convert a value of %s into %s",
|
||||||
|
src.Type(), dest.Type())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeStruct(dest, src reflect.Value) error {
|
||||||
|
if isVariant(dest.Type()) {
|
||||||
|
return storeBase(dest, src)
|
||||||
|
}
|
||||||
|
dval := make([]interface{}, 0, dest.NumField())
|
||||||
|
dtype := dest.Type()
|
||||||
|
for i := 0; i < dest.NumField(); i++ {
|
||||||
|
field := dest.Field(i)
|
||||||
|
ftype := dtype.Field(i)
|
||||||
|
if ftype.PkgPath != "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ftype.Tag.Get("dbus") == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dval = append(dval, field.Addr().Interface())
|
||||||
|
}
|
||||||
|
if src.Len() != len(dval) {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"dbus.Store: type mismatch: "+
|
||||||
|
"destination struct does not have "+
|
||||||
|
"enough fields need: %d have: %d",
|
||||||
|
src.Len(), len(dval))
|
||||||
|
}
|
||||||
|
return Store(src.Interface().([]interface{}), dval...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeSliceIntoVariant(dest, src reflect.Value) error {
|
||||||
|
dv := reflect.MakeSlice(src.Type(), src.Len(), src.Cap())
|
||||||
|
err := store(dv, src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return storeBase(dest, dv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeSliceIntoInterface(dest, src reflect.Value) error {
|
||||||
|
var dv reflect.Value
|
||||||
|
if isVariant(src.Type().Elem()) {
|
||||||
|
//Convert variants to interface{} recursively when converting
|
||||||
|
//to interface{}
|
||||||
|
dv = reflect.MakeSlice(reflect.SliceOf(interfaceType),
|
||||||
|
src.Len(), src.Cap())
|
||||||
|
} else {
|
||||||
|
dv = reflect.MakeSlice(src.Type(), src.Len(), src.Cap())
|
||||||
|
}
|
||||||
|
err := store(dv, src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return storeBase(dest, dv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeSliceIntoSlice(dest, src reflect.Value) error {
|
||||||
|
if dest.IsNil() || dest.Len() < src.Len() {
|
||||||
|
dest.Set(reflect.MakeSlice(dest.Type(), src.Len(), src.Cap()))
|
||||||
|
}
|
||||||
|
if dest.Len() != src.Len() {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"dbus.Store: type mismatch: "+
|
||||||
|
"slices are different lengths "+
|
||||||
|
"need: %d have: %d",
|
||||||
|
src.Len(), dest.Len())
|
||||||
|
}
|
||||||
|
for i := 0; i < src.Len(); i++ {
|
||||||
|
err := store(dest.Index(i), getVariantValue(src.Index(i)))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getVariantValue(in reflect.Value) reflect.Value {
|
||||||
|
if isVariant(in.Type()) {
|
||||||
|
return reflect.ValueOf(in.Interface().(Variant).Value())
|
||||||
|
}
|
||||||
|
return in
|
||||||
|
}
|
||||||
|
|
||||||
|
func isVariant(t reflect.Type) bool {
|
||||||
|
return t == variantType
|
||||||
}
|
}
|
||||||
|
|
||||||
// An ObjectPath is an object path as defined by the D-Bus spec.
|
// An ObjectPath is an object path as defined by the D-Bus spec.
|
||||||
|
@ -177,15 +346,15 @@ func alignment(t reflect.Type) int {
|
||||||
return 4
|
return 4
|
||||||
case signatureType:
|
case signatureType:
|
||||||
return 1
|
return 1
|
||||||
case interfacesType: // sometimes used for structs
|
case interfacesType:
|
||||||
return 8
|
return 4
|
||||||
}
|
}
|
||||||
switch t.Kind() {
|
switch t.Kind() {
|
||||||
case reflect.Uint8:
|
case reflect.Uint8:
|
||||||
return 1
|
return 1
|
||||||
case reflect.Uint16, reflect.Int16:
|
case reflect.Uint16, reflect.Int16:
|
||||||
return 2
|
return 2
|
||||||
case reflect.Uint32, reflect.Int32, reflect.String, reflect.Array, reflect.Slice, reflect.Map:
|
case reflect.Uint, reflect.Int, reflect.Uint32, reflect.Int32, reflect.String, reflect.Array, reflect.Slice, reflect.Map:
|
||||||
return 4
|
return 4
|
||||||
case reflect.Uint64, reflect.Int64, reflect.Float64, reflect.Struct:
|
case reflect.Uint64, reflect.Int64, reflect.Float64, reflect.Struct:
|
||||||
return 8
|
return 8
|
||||||
|
@ -200,7 +369,7 @@ func isKeyType(t reflect.Type) bool {
|
||||||
switch t.Kind() {
|
switch t.Kind() {
|
||||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||||
reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float64,
|
reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float64,
|
||||||
reflect.String:
|
reflect.String, reflect.Uint, reflect.Int:
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,291 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newIntrospectIntf(h *defaultHandler) *exportedIntf {
|
||||||
|
methods := make(map[string]Method)
|
||||||
|
methods["Introspect"] = exportedMethod{
|
||||||
|
reflect.ValueOf(func(msg Message) (string, *Error) {
|
||||||
|
path := msg.Headers[FieldPath].value.(ObjectPath)
|
||||||
|
return h.introspectPath(path), nil
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
return newExportedIntf(methods, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewDefaultHandler returns an instance of the default
|
||||||
|
//call handler. This is useful if you want to implement only
|
||||||
|
//one of the two handlers but not both.
|
||||||
|
func NewDefaultHandler() *defaultHandler {
|
||||||
|
h := &defaultHandler{
|
||||||
|
objects: make(map[ObjectPath]*exportedObj),
|
||||||
|
defaultIntf: make(map[string]*exportedIntf),
|
||||||
|
}
|
||||||
|
h.defaultIntf["org.freedesktop.DBus.Introspectable"] = newIntrospectIntf(h)
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultHandler struct {
|
||||||
|
sync.RWMutex
|
||||||
|
objects map[ObjectPath]*exportedObj
|
||||||
|
defaultIntf map[string]*exportedIntf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *defaultHandler) PathExists(path ObjectPath) bool {
|
||||||
|
_, ok := h.objects[path]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *defaultHandler) introspectPath(path ObjectPath) string {
|
||||||
|
subpath := make(map[string]struct{})
|
||||||
|
var xml bytes.Buffer
|
||||||
|
xml.WriteString("<node>")
|
||||||
|
for obj, _ := range h.objects {
|
||||||
|
p := string(path)
|
||||||
|
if p != "/" {
|
||||||
|
p += "/"
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(string(obj), p) {
|
||||||
|
node_name := strings.Split(string(obj[len(p):]), "/")[0]
|
||||||
|
subpath[node_name] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for s, _ := range subpath {
|
||||||
|
xml.WriteString("\n\t<node name=\"" + s + "\"/>")
|
||||||
|
}
|
||||||
|
xml.WriteString("\n</node>")
|
||||||
|
return xml.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *defaultHandler) LookupObject(path ObjectPath) (ServerObject, bool) {
|
||||||
|
h.RLock()
|
||||||
|
defer h.RUnlock()
|
||||||
|
object, ok := h.objects[path]
|
||||||
|
if ok {
|
||||||
|
return object, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an object wasn't found for this exact path,
|
||||||
|
// look for a matching subtree registration
|
||||||
|
subtreeObject := newExportedObject()
|
||||||
|
path = path[:strings.LastIndex(string(path), "/")]
|
||||||
|
for len(path) > 0 {
|
||||||
|
object, ok = h.objects[path]
|
||||||
|
if ok {
|
||||||
|
for name, iface := range object.interfaces {
|
||||||
|
// Only include this handler if it registered for the subtree
|
||||||
|
if iface.isFallbackInterface() {
|
||||||
|
subtreeObject.interfaces[name] = iface
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
path = path[:strings.LastIndex(string(path), "/")]
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, intf := range h.defaultIntf {
|
||||||
|
if _, exists := subtreeObject.interfaces[name]; exists {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
subtreeObject.interfaces[name] = intf
|
||||||
|
}
|
||||||
|
|
||||||
|
return subtreeObject, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *defaultHandler) AddObject(path ObjectPath, object *exportedObj) {
|
||||||
|
h.Lock()
|
||||||
|
h.objects[path] = object
|
||||||
|
h.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *defaultHandler) DeleteObject(path ObjectPath) {
|
||||||
|
h.Lock()
|
||||||
|
delete(h.objects, path)
|
||||||
|
h.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
type exportedMethod struct {
|
||||||
|
reflect.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m exportedMethod) Call(args ...interface{}) ([]interface{}, error) {
|
||||||
|
t := m.Type()
|
||||||
|
|
||||||
|
params := make([]reflect.Value, len(args))
|
||||||
|
for i := 0; i < len(args); i++ {
|
||||||
|
params[i] = reflect.ValueOf(args[i]).Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := m.Value.Call(params)
|
||||||
|
|
||||||
|
err := ret[t.NumOut()-1].Interface().(*Error)
|
||||||
|
ret = ret[:t.NumOut()-1]
|
||||||
|
out := make([]interface{}, len(ret))
|
||||||
|
for i, val := range ret {
|
||||||
|
out[i] = val.Interface()
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
//concrete type to interface nil is a special case
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m exportedMethod) NumArguments() int {
|
||||||
|
return m.Value.Type().NumIn()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m exportedMethod) ArgumentValue(i int) interface{} {
|
||||||
|
return reflect.Zero(m.Type().In(i)).Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m exportedMethod) NumReturns() int {
|
||||||
|
return m.Value.Type().NumOut()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m exportedMethod) ReturnValue(i int) interface{} {
|
||||||
|
return reflect.Zero(m.Type().Out(i)).Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newExportedObject() *exportedObj {
|
||||||
|
return &exportedObj{
|
||||||
|
interfaces: make(map[string]*exportedIntf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type exportedObj struct {
|
||||||
|
interfaces map[string]*exportedIntf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *exportedObj) LookupInterface(name string) (Interface, bool) {
|
||||||
|
if name == "" {
|
||||||
|
return obj, true
|
||||||
|
}
|
||||||
|
intf, exists := obj.interfaces[name]
|
||||||
|
return intf, exists
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *exportedObj) AddInterface(name string, iface *exportedIntf) {
|
||||||
|
obj.interfaces[name] = iface
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *exportedObj) DeleteInterface(name string) {
|
||||||
|
delete(obj.interfaces, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *exportedObj) LookupMethod(name string) (Method, bool) {
|
||||||
|
for _, intf := range obj.interfaces {
|
||||||
|
method, exists := intf.LookupMethod(name)
|
||||||
|
if exists {
|
||||||
|
return method, exists
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *exportedObj) isFallbackInterface() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func newExportedIntf(methods map[string]Method, includeSubtree bool) *exportedIntf {
|
||||||
|
return &exportedIntf{
|
||||||
|
methods: methods,
|
||||||
|
includeSubtree: includeSubtree,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type exportedIntf struct {
|
||||||
|
methods map[string]Method
|
||||||
|
|
||||||
|
// Whether or not this export is for the entire subtree
|
||||||
|
includeSubtree bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *exportedIntf) LookupMethod(name string) (Method, bool) {
|
||||||
|
out, exists := obj.methods[name]
|
||||||
|
return out, exists
|
||||||
|
}
|
||||||
|
|
||||||
|
func (obj *exportedIntf) isFallbackInterface() bool {
|
||||||
|
return obj.includeSubtree
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewDefaultSignalHandler returns an instance of the default
|
||||||
|
//signal handler. This is useful if you want to implement only
|
||||||
|
//one of the two handlers but not both.
|
||||||
|
func NewDefaultSignalHandler() *defaultSignalHandler {
|
||||||
|
return &defaultSignalHandler{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDefaultSignalHandler(handler SignalHandler) bool {
|
||||||
|
_, ok := handler.(*defaultSignalHandler)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultSignalHandler struct {
|
||||||
|
sync.RWMutex
|
||||||
|
closed bool
|
||||||
|
signals []chan<- *Signal
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sh *defaultSignalHandler) DeliverSignal(intf, name string, signal *Signal) {
|
||||||
|
go func() {
|
||||||
|
sh.RLock()
|
||||||
|
defer sh.RUnlock()
|
||||||
|
if sh.closed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, ch := range sh.signals {
|
||||||
|
ch <- signal
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sh *defaultSignalHandler) Init() error {
|
||||||
|
sh.Lock()
|
||||||
|
sh.signals = make([]chan<- *Signal, 0)
|
||||||
|
sh.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sh *defaultSignalHandler) Terminate() {
|
||||||
|
sh.Lock()
|
||||||
|
sh.closed = true
|
||||||
|
for _, ch := range sh.signals {
|
||||||
|
close(ch)
|
||||||
|
}
|
||||||
|
sh.signals = nil
|
||||||
|
sh.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sh *defaultSignalHandler) addSignal(ch chan<- *Signal) {
|
||||||
|
sh.Lock()
|
||||||
|
defer sh.Unlock()
|
||||||
|
if sh.closed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sh.signals = append(sh.signals, ch)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sh *defaultSignalHandler) removeSignal(ch chan<- *Signal) {
|
||||||
|
sh.Lock()
|
||||||
|
defer sh.Unlock()
|
||||||
|
if sh.closed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := len(sh.signals) - 1; i >= 0; i-- {
|
||||||
|
if ch == sh.signals[i] {
|
||||||
|
copy(sh.signals[i:], sh.signals[i+1:])
|
||||||
|
sh.signals[len(sh.signals)-1] = nil
|
||||||
|
sh.signals = sh.signals[:len(sh.signals)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,8 @@ respective D-Bus equivalents:
|
||||||
bool | BOOLEAN
|
bool | BOOLEAN
|
||||||
int16 | INT16
|
int16 | INT16
|
||||||
uint16 | UINT16
|
uint16 | UINT16
|
||||||
|
int | INT32
|
||||||
|
uint | UINT32
|
||||||
int32 | INT32
|
int32 | INT32
|
||||||
uint32 | UINT32
|
uint32 | UINT32
|
||||||
int64 | INT64
|
int64 | INT64
|
||||||
|
@ -28,6 +30,7 @@ respective D-Bus equivalents:
|
||||||
ObjectPath | OBJECT_PATH
|
ObjectPath | OBJECT_PATH
|
||||||
Signature | SIGNATURE
|
Signature | SIGNATURE
|
||||||
Variant | VARIANT
|
Variant | VARIANT
|
||||||
|
interface{} | VARIANT
|
||||||
UnixFDIndex | UNIX_FD
|
UnixFDIndex | UNIX_FD
|
||||||
|
|
||||||
Slices and arrays encode as ARRAYs of their element type.
|
Slices and arrays encode as ARRAYs of their element type.
|
||||||
|
@ -41,6 +44,9 @@ be skipped.
|
||||||
|
|
||||||
Pointers encode as the value they're pointed to.
|
Pointers encode as the value they're pointed to.
|
||||||
|
|
||||||
|
Types convertible to one of the base types above will be mapped as the
|
||||||
|
base type.
|
||||||
|
|
||||||
Trying to encode any other type or a slice, map or struct containing an
|
Trying to encode any other type or a slice, map or struct containing an
|
||||||
unsupported type will result in an InvalidTypeError.
|
unsupported type will result in an InvalidTypeError.
|
||||||
|
|
||||||
|
|
|
@ -96,10 +96,10 @@ func (enc *encoder) encode(v reflect.Value, depth int) {
|
||||||
case reflect.Uint16:
|
case reflect.Uint16:
|
||||||
enc.binwrite(uint16(v.Uint()))
|
enc.binwrite(uint16(v.Uint()))
|
||||||
enc.pos += 2
|
enc.pos += 2
|
||||||
case reflect.Int32:
|
case reflect.Int, reflect.Int32:
|
||||||
enc.binwrite(int32(v.Int()))
|
enc.binwrite(int32(v.Int()))
|
||||||
enc.pos += 4
|
enc.pos += 4
|
||||||
case reflect.Uint32:
|
case reflect.Uint, reflect.Uint32:
|
||||||
enc.binwrite(uint32(v.Uint()))
|
enc.binwrite(uint32(v.Uint()))
|
||||||
enc.pos += 4
|
enc.pos += 4
|
||||||
case reflect.Int64:
|
case reflect.Int64:
|
||||||
|
@ -202,6 +202,8 @@ func (enc *encoder) encode(v reflect.Value, depth int) {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
enc.pos += length
|
enc.pos += length
|
||||||
|
case reflect.Interface:
|
||||||
|
enc.encode(reflect.ValueOf(MakeVariant(v.Interface())), depth)
|
||||||
default:
|
default:
|
||||||
panic(InvalidTypeError{v.Type()})
|
panic(InvalidTypeError{v.Type()})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package dbus
|
package dbus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -9,32 +8,29 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errmsgInvalidArg = Error{
|
ErrMsgInvalidArg = Error{
|
||||||
"org.freedesktop.DBus.Error.InvalidArgs",
|
"org.freedesktop.DBus.Error.InvalidArgs",
|
||||||
[]interface{}{"Invalid type / number of args"},
|
[]interface{}{"Invalid type / number of args"},
|
||||||
}
|
}
|
||||||
errmsgNoObject = Error{
|
ErrMsgNoObject = Error{
|
||||||
"org.freedesktop.DBus.Error.NoSuchObject",
|
"org.freedesktop.DBus.Error.NoSuchObject",
|
||||||
[]interface{}{"No such object"},
|
[]interface{}{"No such object"},
|
||||||
}
|
}
|
||||||
errmsgUnknownMethod = Error{
|
ErrMsgUnknownMethod = Error{
|
||||||
"org.freedesktop.DBus.Error.UnknownMethod",
|
"org.freedesktop.DBus.Error.UnknownMethod",
|
||||||
[]interface{}{"Unknown / invalid method"},
|
[]interface{}{"Unknown / invalid method"},
|
||||||
}
|
}
|
||||||
|
ErrMsgUnknownInterface = Error{
|
||||||
|
"org.freedesktop.DBus.Error.UnknownInterface",
|
||||||
|
[]interface{}{"Object does not implement the interface"},
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// exportedObj represents an exported object. It stores a precomputed
|
func MakeFailedError(err error) *Error {
|
||||||
// method table that represents the methods exported on the bus.
|
return &Error{
|
||||||
type exportedObj struct {
|
"org.freedesktop.DBus.Error.Failed",
|
||||||
methods map[string]reflect.Value
|
[]interface{}{err.Error()},
|
||||||
|
}
|
||||||
// Whether or not this export is for the entire subtree
|
|
||||||
includeSubtree bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (obj exportedObj) Method(name string) (reflect.Value, bool) {
|
|
||||||
out, exists := obj.methods[name]
|
|
||||||
return out, exists
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sender is a type which can be used in exported methods to receive the message
|
// Sender is a type which can be used in exported methods to receive the message
|
||||||
|
@ -63,7 +59,7 @@ func getMethods(in interface{}, mapping map[string]string) map[string]reflect.Va
|
||||||
// only track valid methods must return *Error as last arg
|
// only track valid methods must return *Error as last arg
|
||||||
// and must be exported
|
// and must be exported
|
||||||
if t.NumOut() == 0 ||
|
if t.NumOut() == 0 ||
|
||||||
t.Out(t.NumOut()-1) != reflect.TypeOf(&errmsgInvalidArg) ||
|
t.Out(t.NumOut()-1) != reflect.TypeOf(&ErrMsgInvalidArg) ||
|
||||||
methtype.PkgPath != "" {
|
methtype.PkgPath != "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -73,119 +69,12 @@ func getMethods(in interface{}, mapping map[string]string) map[string]reflect.Va
|
||||||
return methods
|
return methods
|
||||||
}
|
}
|
||||||
|
|
||||||
// searchHandlers will look through all registered handlers looking for one
|
func standardMethodArgumentDecode(m Method, sender string, msg *Message, body []interface{}) ([]interface{}, error) {
|
||||||
// to handle the given path. If a verbatim one isn't found, it will check for
|
pointers := make([]interface{}, m.NumArguments())
|
||||||
// a subtree registration for the path as well.
|
decode := make([]interface{}, 0, len(body))
|
||||||
func (conn *Conn) searchHandlers(path ObjectPath) (map[string]exportedObj, bool) {
|
|
||||||
conn.handlersLck.RLock()
|
|
||||||
defer conn.handlersLck.RUnlock()
|
|
||||||
|
|
||||||
handlers, ok := conn.handlers[path]
|
for i := 0; i < m.NumArguments(); i++ {
|
||||||
if ok {
|
tp := reflect.TypeOf(m.ArgumentValue(i))
|
||||||
return handlers, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// If handlers weren't found for this exact path, look for a matching subtree
|
|
||||||
// registration
|
|
||||||
handlers = make(map[string]exportedObj)
|
|
||||||
path = path[:strings.LastIndex(string(path), "/")]
|
|
||||||
for len(path) > 0 {
|
|
||||||
var subtreeHandlers map[string]exportedObj
|
|
||||||
subtreeHandlers, ok = conn.handlers[path]
|
|
||||||
if ok {
|
|
||||||
for iface, handler := range subtreeHandlers {
|
|
||||||
// Only include this handler if it registered for the subtree
|
|
||||||
if handler.includeSubtree {
|
|
||||||
handlers[iface] = handler
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
path = path[:strings.LastIndex(string(path), "/")]
|
|
||||||
}
|
|
||||||
|
|
||||||
return handlers, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// handleCall handles the given method call (i.e. looks if it's one of the
|
|
||||||
// pre-implemented ones and searches for a corresponding handler if not).
|
|
||||||
func (conn *Conn) handleCall(msg *Message) {
|
|
||||||
name := msg.Headers[FieldMember].value.(string)
|
|
||||||
path := msg.Headers[FieldPath].value.(ObjectPath)
|
|
||||||
ifaceName, hasIface := msg.Headers[FieldInterface].value.(string)
|
|
||||||
sender, hasSender := msg.Headers[FieldSender].value.(string)
|
|
||||||
serial := msg.serial
|
|
||||||
if ifaceName == "org.freedesktop.DBus.Peer" {
|
|
||||||
switch name {
|
|
||||||
case "Ping":
|
|
||||||
conn.sendReply(sender, serial)
|
|
||||||
case "GetMachineId":
|
|
||||||
conn.sendReply(sender, serial, conn.uuid)
|
|
||||||
default:
|
|
||||||
conn.sendError(errmsgUnknownMethod, sender, serial)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
} else if ifaceName == "org.freedesktop.DBus.Introspectable" && name == "Introspect" {
|
|
||||||
if _, ok := conn.handlers[path]; !ok {
|
|
||||||
subpath := make(map[string]struct{})
|
|
||||||
var xml bytes.Buffer
|
|
||||||
xml.WriteString("<node>")
|
|
||||||
for h, _ := range conn.handlers {
|
|
||||||
p := string(path)
|
|
||||||
if p != "/" {
|
|
||||||
p += "/"
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(string(h), p) {
|
|
||||||
node_name := strings.Split(string(h[len(p):]), "/")[0]
|
|
||||||
subpath[node_name] = struct{}{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for s, _ := range subpath {
|
|
||||||
xml.WriteString("\n\t<node name=\"" + s + "\"/>")
|
|
||||||
}
|
|
||||||
xml.WriteString("\n</node>")
|
|
||||||
conn.sendReply(sender, serial, xml.String())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(name) == 0 {
|
|
||||||
conn.sendError(errmsgUnknownMethod, sender, serial)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the exported handler (if any) for this path
|
|
||||||
handlers, ok := conn.searchHandlers(path)
|
|
||||||
if !ok {
|
|
||||||
conn.sendError(errmsgNoObject, sender, serial)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var m reflect.Value
|
|
||||||
var exists bool
|
|
||||||
if hasIface {
|
|
||||||
iface := handlers[ifaceName]
|
|
||||||
m, exists = iface.Method(name)
|
|
||||||
} else {
|
|
||||||
for _, v := range handlers {
|
|
||||||
m, exists = v.Method(name)
|
|
||||||
if exists {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !exists {
|
|
||||||
conn.sendError(errmsgUnknownMethod, sender, serial)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
t := m.Type()
|
|
||||||
vs := msg.Body
|
|
||||||
pointers := make([]interface{}, t.NumIn())
|
|
||||||
decode := make([]interface{}, 0, len(vs))
|
|
||||||
for i := 0; i < t.NumIn(); i++ {
|
|
||||||
tp := t.In(i)
|
|
||||||
val := reflect.New(tp)
|
val := reflect.New(tp)
|
||||||
pointers[i] = val.Interface()
|
pointers[i] = val.Interface()
|
||||||
if tp == reflect.TypeOf((*Sender)(nil)).Elem() {
|
if tp == reflect.TypeOf((*Sender)(nil)).Elem() {
|
||||||
|
@ -197,26 +86,73 @@ func (conn *Conn) handleCall(msg *Message) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(decode) != len(vs) {
|
if len(decode) != len(body) {
|
||||||
conn.sendError(errmsgInvalidArg, sender, serial)
|
return nil, ErrMsgInvalidArg
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := Store(body, decode...); err != nil {
|
||||||
|
return nil, ErrMsgInvalidArg
|
||||||
|
}
|
||||||
|
|
||||||
|
return pointers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) decodeArguments(m Method, sender string, msg *Message) ([]interface{}, error) {
|
||||||
|
if decoder, ok := m.(ArgumentDecoder); ok {
|
||||||
|
return decoder.DecodeArguments(conn, sender, msg, msg.Body)
|
||||||
|
}
|
||||||
|
return standardMethodArgumentDecode(m, sender, msg, msg.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleCall handles the given method call (i.e. looks if it's one of the
|
||||||
|
// pre-implemented ones and searches for a corresponding handler if not).
|
||||||
|
func (conn *Conn) handleCall(msg *Message) {
|
||||||
|
name := msg.Headers[FieldMember].value.(string)
|
||||||
|
path := msg.Headers[FieldPath].value.(ObjectPath)
|
||||||
|
ifaceName, _ := msg.Headers[FieldInterface].value.(string)
|
||||||
|
sender, hasSender := msg.Headers[FieldSender].value.(string)
|
||||||
|
serial := msg.serial
|
||||||
|
if ifaceName == "org.freedesktop.DBus.Peer" {
|
||||||
|
switch name {
|
||||||
|
case "Ping":
|
||||||
|
conn.sendReply(sender, serial)
|
||||||
|
case "GetMachineId":
|
||||||
|
conn.sendReply(sender, serial, conn.uuid)
|
||||||
|
default:
|
||||||
|
conn.sendError(ErrMsgUnknownMethod, sender, serial)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(name) == 0 {
|
||||||
|
conn.sendError(ErrMsgUnknownMethod, sender, serial)
|
||||||
|
}
|
||||||
|
|
||||||
|
object, ok := conn.handler.LookupObject(path)
|
||||||
|
if !ok {
|
||||||
|
conn.sendError(ErrMsgNoObject, sender, serial)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := Store(vs, decode...); err != nil {
|
iface, exists := object.LookupInterface(ifaceName)
|
||||||
conn.sendError(errmsgInvalidArg, sender, serial)
|
if !exists {
|
||||||
|
conn.sendError(ErrMsgUnknownInterface, sender, serial)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract parameters
|
m, exists := iface.LookupMethod(name)
|
||||||
params := make([]reflect.Value, len(pointers))
|
if !exists {
|
||||||
for i := 0; i < len(pointers); i++ {
|
conn.sendError(ErrMsgUnknownMethod, sender, serial)
|
||||||
params[i] = reflect.ValueOf(pointers[i]).Elem()
|
return
|
||||||
|
}
|
||||||
|
args, err := conn.decodeArguments(m, sender, msg)
|
||||||
|
if err != nil {
|
||||||
|
conn.sendError(err, sender, serial)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call method
|
ret, err := m.Call(args...)
|
||||||
ret := m.Call(params)
|
if err != nil {
|
||||||
if em := ret[t.NumOut()-1].Interface().(*Error); em != nil {
|
conn.sendError(err, sender, serial)
|
||||||
conn.sendError(*em, sender, serial)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,13 +165,11 @@ func (conn *Conn) handleCall(msg *Message) {
|
||||||
reply.Headers[FieldDestination] = msg.Headers[FieldSender]
|
reply.Headers[FieldDestination] = msg.Headers[FieldSender]
|
||||||
}
|
}
|
||||||
reply.Headers[FieldReplySerial] = MakeVariant(msg.serial)
|
reply.Headers[FieldReplySerial] = MakeVariant(msg.serial)
|
||||||
reply.Body = make([]interface{}, len(ret)-1)
|
reply.Body = make([]interface{}, len(ret))
|
||||||
for i := 0; i < len(ret)-1; i++ {
|
for i := 0; i < len(ret); i++ {
|
||||||
reply.Body[i] = ret[i].Interface()
|
reply.Body[i] = ret[i]
|
||||||
}
|
|
||||||
if len(ret) != 1 {
|
|
||||||
reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...))
|
|
||||||
}
|
}
|
||||||
|
reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...))
|
||||||
conn.outLck.RLock()
|
conn.outLck.RLock()
|
||||||
if !conn.closed {
|
if !conn.closed {
|
||||||
conn.out <- reply
|
conn.out <- reply
|
||||||
|
@ -375,7 +309,7 @@ func (conn *Conn) exportMethodTable(methods map[string]interface{}, path ObjectP
|
||||||
t := rval.Type()
|
t := rval.Type()
|
||||||
// only track valid methods must return *Error as last arg
|
// only track valid methods must return *Error as last arg
|
||||||
if t.NumOut() == 0 ||
|
if t.NumOut() == 0 ||
|
||||||
t.Out(t.NumOut()-1) != reflect.TypeOf(&errmsgInvalidArg) {
|
t.Out(t.NumOut()-1) != reflect.TypeOf(&ErrMsgInvalidArg) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
out[name] = rval
|
out[name] = rval
|
||||||
|
@ -383,38 +317,49 @@ func (conn *Conn) exportMethodTable(methods map[string]interface{}, path ObjectP
|
||||||
return conn.export(out, path, iface, includeSubtree)
|
return conn.export(out, path, iface, includeSubtree)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) unexport(h *defaultHandler, path ObjectPath, iface string) error {
|
||||||
|
if h.PathExists(path) {
|
||||||
|
obj := h.objects[path]
|
||||||
|
obj.DeleteInterface(iface)
|
||||||
|
if len(obj.interfaces) == 0 {
|
||||||
|
h.DeleteObject(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// exportWithMap is the worker function for all exports/registrations.
|
// exportWithMap is the worker function for all exports/registrations.
|
||||||
func (conn *Conn) export(methods map[string]reflect.Value, path ObjectPath, iface string, includeSubtree bool) error {
|
func (conn *Conn) export(methods map[string]reflect.Value, path ObjectPath, iface string, includeSubtree bool) error {
|
||||||
|
h, ok := conn.handler.(*defaultHandler)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf(
|
||||||
|
`dbus: export only allowed on the default hander handler have %T"`,
|
||||||
|
conn.handler)
|
||||||
|
}
|
||||||
|
|
||||||
if !path.IsValid() {
|
if !path.IsValid() {
|
||||||
return fmt.Errorf(`dbus: Invalid path name: "%s"`, path)
|
return fmt.Errorf(`dbus: Invalid path name: "%s"`, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.handlersLck.Lock()
|
|
||||||
defer conn.handlersLck.Unlock()
|
|
||||||
|
|
||||||
// Remove a previous export if the interface is nil
|
// Remove a previous export if the interface is nil
|
||||||
if methods == nil {
|
if methods == nil {
|
||||||
if _, ok := conn.handlers[path]; ok {
|
return conn.unexport(h, path, iface)
|
||||||
delete(conn.handlers[path], iface)
|
|
||||||
if len(conn.handlers[path]) == 0 {
|
|
||||||
delete(conn.handlers, path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is the first handler for this path, make a new map to hold all
|
// If this is the first handler for this path, make a new map to hold all
|
||||||
// handlers for this path.
|
// handlers for this path.
|
||||||
if _, ok := conn.handlers[path]; !ok {
|
if !h.PathExists(path) {
|
||||||
conn.handlers[path] = make(map[string]exportedObj)
|
h.AddObject(path, newExportedObject())
|
||||||
|
}
|
||||||
|
|
||||||
|
exportedMethods := make(map[string]Method)
|
||||||
|
for name, method := range methods {
|
||||||
|
exportedMethods[name] = exportedMethod{method}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, save this handler
|
// Finally, save this handler
|
||||||
conn.handlers[path][iface] = exportedObj{
|
obj := h.objects[path]
|
||||||
methods: methods,
|
obj.AddInterface(iface, newExportedIntf(exportedMethods, includeSubtree))
|
||||||
includeSubtree: includeSubtree,
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,8 @@ func (o *Object) AddMatchSignal(iface, member string) *Call {
|
||||||
// will be allocated. Otherwise, ch has to be buffered or Go will panic.
|
// will be allocated. Otherwise, ch has to be buffered or Go will panic.
|
||||||
//
|
//
|
||||||
// If the flags include FlagNoReplyExpected, ch is ignored and a Call structure
|
// If the flags include FlagNoReplyExpected, ch is ignored and a Call structure
|
||||||
// is returned of which only the Err member is valid.
|
// is returned with any error in Err and a closed channel in Done containing
|
||||||
|
// the returned Call as it's one entry.
|
||||||
//
|
//
|
||||||
// If the method parameter contains a dot ('.'), the part before the last dot
|
// If the method parameter contains a dot ('.'), the part before the last dot
|
||||||
// specifies the interface on which the method is called.
|
// specifies the interface on which the method is called.
|
||||||
|
@ -97,11 +98,21 @@ func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...interface
|
||||||
}
|
}
|
||||||
o.conn.outLck.RLock()
|
o.conn.outLck.RLock()
|
||||||
defer o.conn.outLck.RUnlock()
|
defer o.conn.outLck.RUnlock()
|
||||||
|
done := make(chan *Call, 1)
|
||||||
|
call := &Call{
|
||||||
|
Err: nil,
|
||||||
|
Done: done,
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
call.Done <- call
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
if o.conn.closed {
|
if o.conn.closed {
|
||||||
return &Call{Err: ErrClosed}
|
call.Err = ErrClosed
|
||||||
|
return call
|
||||||
}
|
}
|
||||||
o.conn.out <- msg
|
o.conn.out <- msg
|
||||||
return &Call{Err: nil}
|
return call
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetProperty calls org.freedesktop.DBus.Properties.GetProperty on the given
|
// GetProperty calls org.freedesktop.DBus.Properties.GetProperty on the given
|
||||||
|
@ -125,12 +136,12 @@ func (o *Object) GetProperty(p string) (Variant, error) {
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destination returns the destination that calls on o are sent to.
|
// Destination returns the destination that calls on (o *Object) are sent to.
|
||||||
func (o *Object) Destination() string {
|
func (o *Object) Destination() string {
|
||||||
return o.dest
|
return o.dest
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path returns the path that calls on o are sent to.
|
// Path returns the path that calls on (o *Object") are sent to.
|
||||||
func (o *Object) Path() ObjectPath {
|
func (o *Object) Path() ObjectPath {
|
||||||
return o.path
|
return o.path
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
package dbus
|
||||||
|
|
||||||
|
// Terminator allows a handler to implement a shutdown mechanism that
|
||||||
|
// is called when the connection terminates.
|
||||||
|
type Terminator interface {
|
||||||
|
Terminate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handler is the representation of a D-Bus Application.
|
||||||
|
//
|
||||||
|
// The Handler must have a way to lookup objects given
|
||||||
|
// an ObjectPath. The returned object must implement the
|
||||||
|
// ServerObject interface.
|
||||||
|
type Handler interface {
|
||||||
|
LookupObject(path ObjectPath) (ServerObject, bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerObject is the representation of an D-Bus Object.
|
||||||
|
//
|
||||||
|
// Objects are registered at a path for a given Handler.
|
||||||
|
// The Objects implement D-Bus interfaces. The semantics
|
||||||
|
// of Interface lookup is up to the implementation of
|
||||||
|
// the ServerObject. The ServerObject implementation may
|
||||||
|
// choose to implement empty string as a valid interface
|
||||||
|
// represeting all methods or not per the D-Bus specification.
|
||||||
|
type ServerObject interface {
|
||||||
|
LookupInterface(name string) (Interface, bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Interface is the representation of a D-Bus Interface.
|
||||||
|
//
|
||||||
|
// Interfaces are a grouping of methods implemented by the Objects.
|
||||||
|
// Interfaces are responsible for routing method calls.
|
||||||
|
type Interface interface {
|
||||||
|
LookupMethod(name string) (Method, bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Method represents the exposed methods on D-Bus.
|
||||||
|
type Method interface {
|
||||||
|
// Call requires that all arguments are decoded before being passed to it.
|
||||||
|
Call(args ...interface{}) ([]interface{}, error)
|
||||||
|
NumArguments() int
|
||||||
|
NumReturns() int
|
||||||
|
// ArgumentValue returns a representative value for the argument at position
|
||||||
|
// it should be of the proper type. reflect.Zero would be a good mechanism
|
||||||
|
// to use for this Value.
|
||||||
|
ArgumentValue(position int) interface{}
|
||||||
|
// ReturnValue returns a representative value for the return at position
|
||||||
|
// it should be of the proper type. reflect.Zero would be a good mechanism
|
||||||
|
// to use for this Value.
|
||||||
|
ReturnValue(position int) interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An Argument Decoder can decode arguments using the non-standard mechanism
|
||||||
|
//
|
||||||
|
// If a method implements this interface then the non-standard
|
||||||
|
// decoder will be used.
|
||||||
|
//
|
||||||
|
// Method arguments must be decoded from the message.
|
||||||
|
// The mechanism for doing this will vary based on the
|
||||||
|
// implementation of the method. A normal approach is provided
|
||||||
|
// as part of this library, but may be replaced with
|
||||||
|
// any other decoding scheme.
|
||||||
|
type ArgumentDecoder interface {
|
||||||
|
// To decode the arguments of a method the sender and message are
|
||||||
|
// provided incase the semantics of the implementer provides access
|
||||||
|
// to these as part of the method invocation.
|
||||||
|
DecodeArguments(conn *Conn, sender string, msg *Message, args []interface{}) ([]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A SignalHandler is responsible for delivering a signal.
|
||||||
|
//
|
||||||
|
// Signal delivery may be changed from the default channel
|
||||||
|
// based approach by Handlers implementing the SignalHandler
|
||||||
|
// interface.
|
||||||
|
type SignalHandler interface {
|
||||||
|
DeliverSignal(iface, name string, signal *Signal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A DBusError is used to convert a generic object to a D-Bus error.
|
||||||
|
//
|
||||||
|
// Any custom error mechanism may implement this interface to provide
|
||||||
|
// a custom encoding of the error on D-Bus. By default if a normal
|
||||||
|
// error is returned, it will be encoded as the generic
|
||||||
|
// "org.freedesktop.DBus.Error.Failed" error. By implementing this
|
||||||
|
// interface as well a custom encoding may be provided.
|
||||||
|
type DBusError interface {
|
||||||
|
DBusError() (string, []interface{})
|
||||||
|
}
|
|
@ -57,12 +57,12 @@ func getSignature(t reflect.Type) string {
|
||||||
return "n"
|
return "n"
|
||||||
case reflect.Uint16:
|
case reflect.Uint16:
|
||||||
return "q"
|
return "q"
|
||||||
case reflect.Int32:
|
case reflect.Int, reflect.Int32:
|
||||||
if t == unixFDType {
|
if t == unixFDType {
|
||||||
return "h"
|
return "h"
|
||||||
}
|
}
|
||||||
return "i"
|
return "i"
|
||||||
case reflect.Uint32:
|
case reflect.Uint, reflect.Uint32:
|
||||||
if t == unixFDIndexType {
|
if t == unixFDIndexType {
|
||||||
return "h"
|
return "h"
|
||||||
}
|
}
|
||||||
|
@ -101,6 +101,8 @@ func getSignature(t reflect.Type) string {
|
||||||
panic(InvalidTypeError{t})
|
panic(InvalidTypeError{t})
|
||||||
}
|
}
|
||||||
return "a{" + getSignature(t.Key()) + getSignature(t.Elem()) + "}"
|
return "a{" + getSignature(t.Key()) + getSignature(t.Elem()) + "}"
|
||||||
|
case reflect.Interface:
|
||||||
|
return "v"
|
||||||
}
|
}
|
||||||
panic(InvalidTypeError{t})
|
panic(InvalidTypeError{t})
|
||||||
}
|
}
|
||||||
|
@ -162,7 +164,7 @@ func (e SignatureError) Error() string {
|
||||||
return fmt.Sprintf("dbus: invalid signature: %q (%s)", e.Sig, e.Reason)
|
return fmt.Sprintf("dbus: invalid signature: %q (%s)", e.Sig, e.Reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to read a single type from this string. If it was successfull, err is nil
|
// Try to read a single type from this string. If it was successful, err is nil
|
||||||
// and rem is the remaining unparsed part. Otherwise, err is a non-nil
|
// and rem is the remaining unparsed part. Otherwise, err is a non-nil
|
||||||
// SignatureError and rem is "". depth is the current recursion depth which may
|
// SignatureError and rem is "". depth is the current recursion depth which may
|
||||||
// not be greater than 64 and should be given as 0 on the first call.
|
// not be greater than 64 and should be given as 0 on the first call.
|
||||||
|
|
|
@ -4,8 +4,23 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var nativeEndian binary.ByteOrder
|
||||||
|
|
||||||
|
func detectEndianness() binary.ByteOrder {
|
||||||
|
var x uint32 = 0x01020304
|
||||||
|
if *(*byte)(unsafe.Pointer(&x)) == 0x01 {
|
||||||
|
return binary.BigEndian
|
||||||
|
}
|
||||||
|
return binary.LittleEndian
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
nativeEndian = detectEndianness()
|
||||||
|
}
|
||||||
|
|
||||||
type genericTransport struct {
|
type genericTransport struct {
|
||||||
io.ReadWriteCloser
|
io.ReadWriteCloser
|
||||||
}
|
}
|
||||||
|
@ -31,5 +46,5 @@ func (t genericTransport) SendMessage(msg *Message) error {
|
||||||
return errors.New("dbus: unix fd passing not enabled")
|
return errors.New("dbus: unix fd passing not enabled")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return msg.EncodeTo(t, binary.LittleEndian)
|
return msg.EncodeTo(t, nativeEndian)
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,7 +175,7 @@ func (t *unixTransport) SendMessage(msg *Message) error {
|
||||||
msg.Headers[FieldUnixFDs] = MakeVariant(uint32(len(fds)))
|
msg.Headers[FieldUnixFDs] = MakeVariant(uint32(len(fds)))
|
||||||
oob := syscall.UnixRights(fds...)
|
oob := syscall.UnixRights(fds...)
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
msg.EncodeTo(buf, binary.LittleEndian)
|
msg.EncodeTo(buf, nativeEndian)
|
||||||
n, oobn, err := t.UnixConn.WriteMsgUnix(buf.Bytes(), oob, nil)
|
n, oobn, err := t.UnixConn.WriteMsgUnix(buf.Bytes(), oob, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -184,7 +184,7 @@ func (t *unixTransport) SendMessage(msg *Message) error {
|
||||||
return io.ErrShortWrite
|
return io.ErrShortWrite
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := msg.EncodeTo(t, binary.LittleEndian); err != nil {
|
if err := msg.EncodeTo(t, nativeEndian); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue