Compare commits
150 Commits
Author | SHA1 | Date |
---|---|---|
Antonio Murdaca | 54e76afc03 | |
Daniel J Walsh | 4fee97abe3 | |
Kei Sawada | a50f352eb4 | |
Mrunal Patel | ed40d645cd | |
W. Trevor King | 8dbc2d1fff | |
Mrunal Patel | ddb14b7303 | |
Mrunal Patel | 924821e4bf | |
W. Trevor King | 080b84dfcd | |
Mrunal Patel | 214096b7ed | |
Antonio Murdaca | 8c87b6104f | |
Mrunal Patel | b7995aa526 | |
W. Trevor King | 822a6516cf | |
W. Trevor King | 523326b7ba | |
W. Trevor King | 826298483a | |
Mrunal Patel | 7851115693 | |
Antonio Murdaca | 77561e95cf | |
Antonio Murdaca | cbfdda868a | |
W. Trevor King | 282b900433 | |
Mrunal Patel | 1bb5846d7d | |
W. Trevor King | 15d839ea0d | |
W. Trevor King | bf8a99c085 | |
W. Trevor King | 2bf750c871 | |
Mrunal Patel | ba2b4a03d0 | |
W. Trevor King | 8c7c70c2db | |
W. Trevor King | e124834b0d | |
Antonio Murdaca | cb8033cd19 | |
Antonio Murdaca | 8c190a683c | |
Mrunal Patel | d0e0303921 | |
Antonio Murdaca | 8d2a572ead | |
Daniel J Walsh | 22e25158ca | |
Vincent Batts | 27c2eda635 | |
Daniel J Walsh | 23d20c9db5 | |
Mrunal Patel | 41aaf4e3d8 | |
Giuseppe Scrivano | 2cb22eba49 | |
Giuseppe Scrivano | 6bb1b7e17d | |
Giuseppe Scrivano | b1b380d67b | |
Antonio Murdaca | 6f4d7c1ae0 | |
Mrunal Patel | c351bc81e1 | |
Giuseppe Scrivano | b5167d4e8f | |
Giuseppe Scrivano | 1f75ec82e1 | |
Giuseppe Scrivano | 3881f375b9 | |
Mrunal Patel | ad46c581fa | |
Daniel J Walsh | 3c1c6d047e | |
Mrunal Patel | a34038350c | |
Aaron Crickenberger | a28eb8374e | |
Mrunal Patel | 295a11eb17 | |
Mrunal Patel | 28976738de | |
Haoran Wang | 88b13dfddf | |
Jianyong Wu | 8b1fefad71 | |
Mrunal Patel | 6b91df3da7 | |
Antonio Murdaca | de0be63495 | |
Daniel J Walsh | a85f3127d8 | |
Daniel J Walsh | 6c0b79b706 | |
Mrunal Patel | aee7dea272 | |
Antonio Murdaca | e344ad105a | |
Antonio Murdaca | 43119a7b13 | |
Antonio Murdaca | ecc572e7cf | |
Antonio Murdaca | 455245e65b | |
Antonio Murdaca | 7d2bde110a | |
Nalin Dahyabhai | fa90249c59 | |
Nalin Dahyabhai | 72442d0957 | |
Nalin Dahyabhai | 0ab8c507f4 | |
Nalin Dahyabhai | 492f758176 | |
Nalin Dahyabhai | 893aa4e8c7 | |
Nalin Dahyabhai | 6a456d1502 | |
Nalin Dahyabhai | 5ea050fc12 | |
Nalin Dahyabhai | ff7bbb4f0d | |
Nalin Dahyabhai | f3b7065bd8 | |
Nalin Dahyabhai | 553979e1fc | |
Nalin Dahyabhai | 0651d3a8de | |
Mrunal Patel | 2fa1f3f74a | |
Antonio Murdaca | d91df68638 | |
Mrunal Patel | da50e6ca11 | |
Mrunal Patel | ebc249cad8 | |
Antonio Murdaca | f317ffce5b | |
Mrunal Patel | a85ea609db | |
Antonio Murdaca | afeab27a36 | |
Mrunal Patel | 1f3fbdc987 | |
Nicolas Lacasse | 1138af9e59 | |
Antonio Murdaca | 06904d4dbb | |
Mrunal Patel | 85f303f3ff | |
Mrunal Patel | 989d275e76 | |
Antonio Murdaca | d168fc5fec | |
Daniel J Walsh | b9ffd277b9 | |
Antonio Murdaca | 910cfab6e9 | |
Samuel Ortiz | a2e08d5dc4 | |
Antonio Murdaca | 0eaa52c356 | |
Antonio Murdaca | 0ab5e80c38 | |
Mrunal Patel | 2cae11ba35 | |
Mrunal Patel | 40da5c2c16 | |
Antonio Murdaca | b8bba70f99 | |
Mrunal Patel | 32d2c2d57c | |
Antonio Murdaca | c8aad704dd | |
Antonio Murdaca | ea0bf448fe | |
Antonio Murdaca | 902acca4af | |
Antonio Murdaca | b59f31a2d5 | |
Antonio Murdaca | c6f68f1bf1 | |
Oleksandr Stepanov | a71948e9e7 | |
Mrunal Patel | 6faef13293 | |
Mrunal Patel | b2a78eba2b | |
Antonio Murdaca | bae4d2241f | |
Wei Wei | 3006a2159a | |
Antonio Murdaca | 5f5a7a3648 | |
Liu Chang | c0ad5277e6 | |
Mrunal Patel | d10490bccf | |
Mrunal Patel | 4cf4137be0 | |
Mrunal Patel | 4a32d0ff33 | |
Antonio Murdaca | 87f1ae214f | |
Mrunal Patel | 63371009ae | |
Antonio Murdaca | 03fb727f2b | |
Antonio Murdaca | 851759c73b | |
Daniel J Walsh | 8518e06e81 | |
Liu Chang | 42800cc96b | |
Liu Chang | bf515de94d | |
Mrunal Patel | 070b8bfdc5 | |
Mrunal Patel | 7508cdeace | |
Antonio Murdaca | 67e2d28c86 | |
Antonio Murdaca | 2f344c7533 | |
Antonio Murdaca | a75362dca0 | |
z00280905 | 2fbbf49541 | |
Mrunal Patel | 73c1a9823f | |
Antonio Murdaca | 6da7193ff5 | |
Antonio Murdaca | b3f59f31ad | |
Wei Wei | 25dfde9044 | |
Daniel J Walsh | 6c8ab88e9e | |
Mrunal Patel | 946307e5c2 | |
Daniel J Walsh | e23723d62e | |
Mrunal Patel | d68da8929a | |
Chris Evich | bbd9a6528c | |
Wei Wei | 702ab3ee3a | |
Mrunal Patel | 7b837b5a1e | |
Daniel J Walsh | 31111ba651 | |
Wei Wei | b0b6611bdf | |
Antonio Murdaca | 429a687ced | |
Mrunal Patel | 3596aa0155 | |
Mrunal Patel | 8fe6dd36a4 | |
Daniel J Walsh | e7471600f8 | |
Antonio Murdaca | 21252ed22d | |
Antonio Murdaca | 692af73b0b | |
DeShuai Ma | e90e0c7062 | |
Mrunal Patel | a447b2985c | |
Mrunal Patel | 6ed8fbeea2 | |
Antonio Murdaca | 7a675ccd92 | |
Mrunal Patel | 7076c73172 | |
Antonio Murdaca | 8ae0aee7e5 | |
Álex González | c3f86cd016 | |
Mrunal Patel | 25ac83196f | |
Antonio Murdaca | 586eda8245 | |
Daniel J Walsh | edf2300205 | |
Antonio Murdaca | 4d1e77ff9d |
|
@ -11,3 +11,8 @@
|
|||
/test/bin2img/bin2img
|
||||
/test/checkseccomp/checkseccomp
|
||||
/test/copyimg/copyimg
|
||||
|
||||
Vagrantfile
|
||||
.vagrant/
|
||||
|
||||
.vscode/
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
Aleksa Sarai <asarai@suse.de> <asarai@suse.com>
|
||||
Antonio Murdaca <runcom@redhat.com> <runcom@users.noreply.github.com>
|
||||
CuiHaozhi <cuihaozhi@chinacloud.com.cn> <cuihz@wise2c.com>
|
||||
Daniel J Walsh <dwalsh@redhat.com>
|
||||
Haiyan Meng <hmeng@redhat.com> <haiyanalady@gmail.com>
|
||||
Lorenzo Fontana <lo@linux.com> <fontanalorenz@gmail.com>
|
||||
Mrunal Patel <mrunalp@gmail.com> <mpatel@redhat.com>
|
||||
Mrunal Patel <mrunalp@gmail.com> <mrunal@me.com>
|
||||
Pengfei Ni <feiskyer@gmail.com> <feiskyer@users.noreply.github.com>
|
||||
Tobias Klauser <tklauser@distanz.ch> <tobias.klauser@gmail.com>
|
|
@ -38,6 +38,7 @@ RUN apt-get update && apt-get install -y \
|
|||
netcat \
|
||||
socat \
|
||||
--no-install-recommends \
|
||||
bsdmainutils \
|
||||
&& apt-get clean
|
||||
|
||||
# install bats
|
||||
|
@ -56,7 +57,7 @@ RUN mkdir -p /usr/src/criu \
|
|||
&& rm -rf /usr/src/criu
|
||||
|
||||
# Install runc
|
||||
ENV RUNC_COMMIT 84a082bfef6f932de921437815355186db37aeb1
|
||||
ENV RUNC_COMMIT c6e4a1ebeb1a72b529c6f1b6ee2b1ae5b868b14f
|
||||
RUN set -x \
|
||||
&& export GOPATH="$(mktemp -d)" \
|
||||
&& git clone https://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
|
||||
|
|
26
Makefile
26
Makefile
|
@ -11,7 +11,7 @@ LIBEXECDIR ?= ${PREFIX}/libexec
|
|||
MANDIR ?= ${PREFIX}/share/man
|
||||
ETCDIR ?= ${DESTDIR}/etc
|
||||
ETCDIR_CRIO ?= ${ETCDIR}/crio
|
||||
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)
|
||||
BUILDTAGS ?= seccomp $(shell hack/btrfs_tag.sh) $(shell hack/libdm_installed.sh) $(shell hack/libdm_no_deferred_remove_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
|
||||
|
@ -46,7 +46,7 @@ help:
|
|||
@echo "Usage: make <target>"
|
||||
@echo
|
||||
@echo " * 'install' - Install binaries to system locations"
|
||||
@echo " * 'binaries' - Build crio, conmon, pause, and crioctl"
|
||||
@echo " * 'binaries' - Build crio, conmon and pause"
|
||||
@echo " * 'integration' - Execute integration tests"
|
||||
@echo " * 'clean' - Clean artifacts"
|
||||
@echo " * 'lint' - Execute the source code linter"
|
||||
|
@ -64,7 +64,8 @@ lint: .gopathok
|
|||
@./.tool/lint
|
||||
|
||||
gofmt:
|
||||
@./hack/verify-gofmt.sh
|
||||
find . -name '*.go' ! -path './vendor/*' -exec gofmt -s -w {} \+
|
||||
git diff --exit-code
|
||||
|
||||
conmon:
|
||||
$(MAKE) -C $@
|
||||
|
@ -73,19 +74,16 @@ pause:
|
|||
$(MAKE) -C $@
|
||||
|
||||
test/bin2img/bin2img: .gopathok $(wildcard test/bin2img/*.go)
|
||||
$(GO) build $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/test/bin2img
|
||||
$(GO) build -i $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/test/bin2img
|
||||
|
||||
test/copyimg/copyimg: .gopathok $(wildcard test/copyimg/*.go)
|
||||
$(GO) build $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/test/copyimg
|
||||
$(GO) build -i $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/test/copyimg
|
||||
|
||||
test/checkseccomp/checkseccomp: .gopathok $(wildcard test/checkseccomp/*.go)
|
||||
$(GO) build $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/test/checkseccomp
|
||||
$(GO) build -i $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/test/checkseccomp
|
||||
|
||||
crio: .gopathok $(shell hack/find-godeps.sh $(GOPKGDIR) cmd/crio $(PROJECT))
|
||||
$(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))
|
||||
$(GO) build $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o bin/$@ $(PROJECT)/cmd/crioctl
|
||||
$(GO) build -i $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o bin/$@ $(PROJECT)/cmd/crio
|
||||
|
||||
crio.conf: crio
|
||||
./bin/crio --config="" config --default > crio.conf
|
||||
|
@ -99,7 +97,7 @@ endif
|
|||
rm -fr test/testdata/redis-image
|
||||
find . -name \*~ -delete
|
||||
find . -name \#\* -delete
|
||||
rm -f bin/crioctl bin/crio
|
||||
rm -f bin/crio
|
||||
make -C conmon clean
|
||||
make -C pause clean
|
||||
rm -f test/bin2img/bin2img
|
||||
|
@ -121,7 +119,7 @@ testunit:
|
|||
localintegration: clean binaries test-binaries
|
||||
./test/test_runner.sh ${TESTFLAGS}
|
||||
|
||||
binaries: crio conmon pause crioctl
|
||||
binaries: crio conmon pause
|
||||
test-binaries: test/bin2img/bin2img test/copyimg/copyimg test/checkseccomp/checkseccomp
|
||||
|
||||
MANPAGES_MD := $(wildcard docs/*.md)
|
||||
|
@ -139,7 +137,6 @@ install: .gopathok install.bin install.man
|
|||
|
||||
install.bin:
|
||||
install ${SELINUXOPT} -D -m 755 bin/crio $(BINDIR)/crio
|
||||
install ${SELINUXOPT} -D -m 755 bin/crioctl $(BINDIR)/crioctl
|
||||
install ${SELINUXOPT} -D -m 755 bin/conmon $(LIBEXECDIR)/crio/conmon
|
||||
install ${SELINUXOPT} -D -m 755 bin/pause $(LIBEXECDIR)/crio/pause
|
||||
|
||||
|
@ -149,7 +146,7 @@ install.man:
|
|||
install ${SELINUXOPT} -m 644 $(filter %.5,$(MANPAGES)) -t $(MANDIR)/man5
|
||||
install ${SELINUXOPT} -m 644 $(filter %.8,$(MANPAGES)) -t $(MANDIR)/man8
|
||||
|
||||
install.config:
|
||||
install.config: 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 crio-umount.conf $(OCIUMOUNTINSTALLDIR)/crio-umount.conf
|
||||
|
@ -165,7 +162,6 @@ install.systemd:
|
|||
|
||||
uninstall:
|
||||
rm -f $(BINDIR)/crio
|
||||
rm -f $(BINDIR)/crioctl
|
||||
rm -f $(LIBEXECDIR)/crio/conmon
|
||||
rm -f $(LIBEXECDIR)/crio/pause
|
||||
for i in $(filter %.1,$(MANPAGES)); do \
|
||||
|
|
23
README.md
23
README.md
|
@ -6,6 +6,20 @@
|
|||
|
||||
### Status: Stable
|
||||
|
||||
## Compatibility matrix: CRI-O <-> Kubernetes clusters
|
||||
|
||||
| Version - Branch | Kubernetes branch/version | Maintenance status |
|
||||
|----------------------------|-------------------------------|--------------------|
|
||||
| CRI-O 1.0.x - release-1.0 | Kubernetes 1.7 branch, v1.7.x | = |
|
||||
| CRI-O 1.8.x - release-1.8 | Kubernetes 1.8 branch, v1.8.x | = |
|
||||
| CRI-O 1.9.x - release-1.9 | Kubernetes 1.9 branch, v1.9.x | = |
|
||||
| CRI-O HEAD - master | Kubernetes master branch | ✓ |
|
||||
|
||||
Key:
|
||||
|
||||
* `✓` Changes in main Kubernetes repo about CRI are actively implemented in CRI-O
|
||||
* `=` Maintenance is manual, only bugs will be patched.
|
||||
|
||||
## What is the scope of this project?
|
||||
|
||||
CRI-O is meant to provide an integration path between OCI conformant runtimes and the kubelet.
|
||||
|
@ -64,7 +78,12 @@ For sync communication we have an IRC channel #CRI-O, on chat.freenode.net, that
|
|||
|
||||
## Getting started
|
||||
|
||||
### Prerequisites
|
||||
### Runtime dependencies
|
||||
|
||||
- runc, Clear Containers runtime, or any other OCI compatible runtime
|
||||
- socat
|
||||
- iproute
|
||||
- iptables
|
||||
|
||||
Latest version of `runc` is expected to be installed on the system. It is picked up as the default runtime by CRI-O.
|
||||
|
||||
|
@ -216,7 +235,7 @@ You can run a local version of kubernetes with CRI-O using `local-up-cluster.sh`
|
|||
```shell
|
||||
CGROUP_DRIVER=systemd \
|
||||
CONTAINER_RUNTIME=remote \
|
||||
CONTAINER_RUNTIME_ENDPOINT='/var/run/crio.sock --runtime-request-timeout=15m' \
|
||||
CONTAINER_RUNTIME_ENDPOINT='/var/run/crio/crio.sock --runtime-request-timeout=15m' \
|
||||
./hack/local-up-cluster.sh
|
||||
```
|
||||
|
||||
|
|
|
@ -28,8 +28,7 @@ storage_driver = "{{ .Storage }}"
|
|||
storage_option = [
|
||||
{{ range $opt := .StorageOptions }}{{ printf "\t%q,\n" $opt }}{{ end }}]
|
||||
|
||||
# The "crio.api" table contains settings for the kubelet/gRPC
|
||||
# interface (which is also used by crioctl).
|
||||
# The "crio.api" table contains settings for the kubelet/gRPC interface.
|
||||
[crio.api]
|
||||
|
||||
# listen is the path to the AF_LOCAL socket on which crio will listen.
|
||||
|
@ -115,6 +114,9 @@ default_mounts = [
|
|||
# pids_limit is the number of processes allowed in a container
|
||||
pids_limit = {{ .PidsLimit }}
|
||||
|
||||
# enable using a shared PID namespace for containers in a pod
|
||||
enable_shared_pid_namespace = {{ .EnableSharedPIDNamespace }}
|
||||
|
||||
# log_size_max is the max limit for the container log size in bytes.
|
||||
# Negative values indicate that no limit is imposed.
|
||||
log_size_max = {{ .LogSizeMax }}
|
||||
|
|
|
@ -8,12 +8,13 @@ import (
|
|||
_ "net/http/pprof"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containers/storage/pkg/reexec"
|
||||
"github.com/kubernetes-incubator/cri-o/libkpod"
|
||||
"github.com/kubernetes-incubator/cri-o/lib"
|
||||
"github.com/kubernetes-incubator/cri-o/server"
|
||||
"github.com/kubernetes-incubator/cri-o/version"
|
||||
"github.com/opencontainers/selinux/go-selinux"
|
||||
|
@ -31,9 +32,9 @@ var gitCommit = ""
|
|||
|
||||
func validateConfig(config *server.Config) error {
|
||||
switch config.ImageVolumes {
|
||||
case libkpod.ImageVolumesMkdir:
|
||||
case libkpod.ImageVolumesIgnore:
|
||||
case libkpod.ImageVolumesBind:
|
||||
case lib.ImageVolumesMkdir:
|
||||
case lib.ImageVolumesIgnore:
|
||||
case lib.ImageVolumesBind:
|
||||
default:
|
||||
return fmt.Errorf("Unrecognized image volume type specified")
|
||||
|
||||
|
@ -131,6 +132,9 @@ func mergeConfig(config *server.Config, ctx *cli.Context) error {
|
|||
if ctx.GlobalIsSet("pids-limit") {
|
||||
config.PidsLimit = ctx.GlobalInt64("pids-limit")
|
||||
}
|
||||
if ctx.GlobalIsSet("enable-shared-pid-namespace") {
|
||||
config.EnableSharedPIDNamespace = ctx.GlobalBool("enable-shared-pid-namespace")
|
||||
}
|
||||
if ctx.GlobalIsSet("log-size-max") {
|
||||
config.LogSizeMax = ctx.GlobalInt64("log-size-max")
|
||||
}
|
||||
|
@ -141,7 +145,7 @@ func mergeConfig(config *server.Config, ctx *cli.Context) error {
|
|||
config.PluginDir = ctx.GlobalString("cni-plugin-dir")
|
||||
}
|
||||
if ctx.GlobalIsSet("image-volumes") {
|
||||
config.ImageVolumes = libkpod.ImageVolumesType(ctx.GlobalString("image-volumes"))
|
||||
config.ImageVolumes = lib.ImageVolumesType(ctx.GlobalString("image-volumes"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -293,12 +297,16 @@ func main() {
|
|||
},
|
||||
cli.Int64Flag{
|
||||
Name: "pids-limit",
|
||||
Value: libkpod.DefaultPidsLimit,
|
||||
Value: lib.DefaultPidsLimit,
|
||||
Usage: "maximum number of processes allowed in a container",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "enable-shared-pid-namespace",
|
||||
Usage: "enable using a shared PID namespace for containers in a pod",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "log-size-max",
|
||||
Value: libkpod.DefaultLogSizeMax,
|
||||
Value: lib.DefaultLogSizeMax,
|
||||
Usage: "maximum log size in bytes for a container",
|
||||
},
|
||||
cli.StringFlag{
|
||||
|
@ -311,13 +319,13 @@ func main() {
|
|||
},
|
||||
cli.StringFlag{
|
||||
Name: "image-volumes",
|
||||
Value: string(libkpod.ImageVolumesMkdir),
|
||||
Value: string(lib.ImageVolumesMkdir),
|
||||
Usage: "image volume handling ('mkdir', 'bind', or 'ignore')",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "hooks-dir-path",
|
||||
Usage: "set the OCI hooks directory path",
|
||||
Value: libkpod.DefaultHooksDirPath,
|
||||
Value: lib.DefaultHooksDirPath,
|
||||
Hidden: true,
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
|
@ -429,6 +437,10 @@ func main() {
|
|||
return fmt.Errorf("invalid --runtime value %q", err)
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(config.Listen), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove the socket if it already exists
|
||||
if _, err := os.Stat(config.Listen); err == nil {
|
||||
if err := os.Remove(config.Listen); err != nil {
|
||||
|
@ -497,7 +509,7 @@ func main() {
|
|||
if graceful && strings.Contains(strings.ToLower(err.Error()), "use of closed network connection") {
|
||||
err = nil
|
||||
} else {
|
||||
logrus.Errorf("Failed to serve grpc grpc request: %v", err)
|
||||
logrus.Errorf("Failed to serve grpc request: %v", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
|
|
@ -1,654 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/kubernetes-incubator/cri-o/client"
|
||||
"github.com/urfave/cli"
|
||||
"golang.org/x/net/context"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/remotecommand"
|
||||
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||
)
|
||||
|
||||
var containerCommand = cli.Command{
|
||||
Name: "container",
|
||||
Aliases: []string{"ctr"},
|
||||
Subcommands: []cli.Command{
|
||||
createContainerCommand,
|
||||
inspectContainerCommand,
|
||||
startContainerCommand,
|
||||
stopContainerCommand,
|
||||
removeContainerCommand,
|
||||
containerStatusCommand,
|
||||
listContainersCommand,
|
||||
execSyncCommand,
|
||||
execCommand,
|
||||
},
|
||||
}
|
||||
|
||||
type createOptions struct {
|
||||
// configPath is path to the config for container
|
||||
configPath string
|
||||
// name sets the container name
|
||||
name string
|
||||
// podID of the container
|
||||
podID string
|
||||
// labels for the container
|
||||
labels map[string]string
|
||||
}
|
||||
|
||||
var createContainerCommand = cli.Command{
|
||||
Name: "create",
|
||||
Usage: "create a container",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "pod",
|
||||
Usage: "the id of the pod sandbox to which the container belongs",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "config",
|
||||
Value: "config.json",
|
||||
Usage: "the path of a container config file",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "name",
|
||||
Value: "",
|
||||
Usage: "the name of the container",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "label",
|
||||
Usage: "add key=value labels to the container",
|
||||
},
|
||||
},
|
||||
Action: func(context *cli.Context) error {
|
||||
// Set up a connection to the server.
|
||||
conn, err := getClientConnection(context)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
client := pb.NewRuntimeServiceClient(conn)
|
||||
|
||||
if !context.IsSet("pod") {
|
||||
return fmt.Errorf("Please specify the id of the pod sandbox to which the container belongs via the --pod option")
|
||||
}
|
||||
|
||||
opts := createOptions{
|
||||
configPath: context.String("config"),
|
||||
name: context.String("name"),
|
||||
podID: context.String("pod"),
|
||||
labels: make(map[string]string),
|
||||
}
|
||||
|
||||
for _, l := range context.StringSlice("label") {
|
||||
pair := strings.Split(l, "=")
|
||||
if len(pair) != 2 {
|
||||
return fmt.Errorf("incorrectly specified label: %v", l)
|
||||
}
|
||||
opts.labels[pair[0]] = pair[1]
|
||||
}
|
||||
|
||||
// Test RuntimeServiceClient.CreateContainer
|
||||
err = CreateContainer(client, opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Creating container failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var startContainerCommand = cli.Command{
|
||||
Name: "start",
|
||||
Usage: "start a container",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "id",
|
||||
Value: "",
|
||||
Usage: "id of the container",
|
||||
},
|
||||
},
|
||||
Action: func(context *cli.Context) error {
|
||||
// Set up a connection to the server.
|
||||
conn, err := getClientConnection(context)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
client := pb.NewRuntimeServiceClient(conn)
|
||||
|
||||
err = StartContainer(client, context.String("id"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Starting the container failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var stopContainerCommand = cli.Command{
|
||||
Name: "stop",
|
||||
Usage: "stop a container",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "id",
|
||||
Value: "",
|
||||
Usage: "id of the container",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "timeout",
|
||||
Value: 10,
|
||||
Usage: "seconds to wait to kill the container after a graceful stop is requested",
|
||||
},
|
||||
},
|
||||
Action: func(context *cli.Context) error {
|
||||
// Set up a connection to the server.
|
||||
conn, err := getClientConnection(context)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
client := pb.NewRuntimeServiceClient(conn)
|
||||
|
||||
err = StopContainer(client, context.String("id"), context.Int64("timeout"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Stopping the container failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var removeContainerCommand = cli.Command{
|
||||
Name: "remove",
|
||||
Usage: "remove a container",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "id",
|
||||
Value: "",
|
||||
Usage: "id of the container",
|
||||
},
|
||||
},
|
||||
Action: func(context *cli.Context) error {
|
||||
// Set up a connection to the server.
|
||||
conn, err := getClientConnection(context)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
client := pb.NewRuntimeServiceClient(conn)
|
||||
|
||||
err = RemoveContainer(client, context.String("id"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Removing the container failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var containerStatusCommand = cli.Command{
|
||||
Name: "status",
|
||||
Usage: "get the status of a container",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "id",
|
||||
Value: "",
|
||||
Usage: "id of the container",
|
||||
},
|
||||
},
|
||||
Action: func(context *cli.Context) error {
|
||||
// Set up a connection to the server.
|
||||
conn, err := getClientConnection(context)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
client := pb.NewRuntimeServiceClient(conn)
|
||||
|
||||
err = ContainerStatus(client, context.String("id"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Getting the status of the container failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var execSyncCommand = cli.Command{
|
||||
Name: "execsync",
|
||||
Usage: "exec a command synchronously in a container",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "id",
|
||||
Value: "",
|
||||
Usage: "id of the container",
|
||||
},
|
||||
cli.Int64Flag{
|
||||
Name: "timeout",
|
||||
Value: 0,
|
||||
Usage: "timeout for the command",
|
||||
},
|
||||
},
|
||||
Action: func(context *cli.Context) error {
|
||||
// Set up a connection to the server.
|
||||
conn, err := getClientConnection(context)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
client := pb.NewRuntimeServiceClient(conn)
|
||||
|
||||
err = ExecSync(client, context.String("id"), context.Args(), context.Int64("timeout"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("execing command in container failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var execCommand = cli.Command{
|
||||
Name: "exec",
|
||||
Usage: "prepare a streaming endpoint to execute a command in the container",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "id",
|
||||
Value: "",
|
||||
Usage: "id of the container",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "tty",
|
||||
Usage: "whether to use tty",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "stdin",
|
||||
Usage: "whether to stream to stdin",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "url",
|
||||
Usage: "do not exec command, just prepare streaming endpoint",
|
||||
},
|
||||
},
|
||||
Action: func(context *cli.Context) error {
|
||||
// Set up a connection to the server.
|
||||
conn, err := getClientConnection(context)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
client := pb.NewRuntimeServiceClient(conn)
|
||||
|
||||
err = Exec(client, context.String("id"), context.Bool("tty"), context.Bool("stdin"), context.Bool("url"), context.Args())
|
||||
if err != nil {
|
||||
return fmt.Errorf("execing command in container failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
type listOptions struct {
|
||||
// id of the container
|
||||
id string
|
||||
// podID of the container
|
||||
podID string
|
||||
// state of the container
|
||||
state string
|
||||
// quiet is for listing just container IDs
|
||||
quiet bool
|
||||
// labels are selectors for the container
|
||||
labels map[string]string
|
||||
}
|
||||
|
||||
var listContainersCommand = cli.Command{
|
||||
Name: "list",
|
||||
Usage: "list containers",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "quiet",
|
||||
Usage: "list only container IDs",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "id",
|
||||
Value: "",
|
||||
Usage: "filter by container id",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "pod",
|
||||
Value: "",
|
||||
Usage: "filter by container pod id",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "state",
|
||||
Value: "",
|
||||
Usage: "filter by container state",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "label",
|
||||
Usage: "filter by key=value label",
|
||||
},
|
||||
},
|
||||
Action: func(context *cli.Context) error {
|
||||
// Set up a connection to the server.
|
||||
conn, err := getClientConnection(context)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
client := pb.NewRuntimeServiceClient(conn)
|
||||
opts := listOptions{
|
||||
id: context.String("id"),
|
||||
podID: context.String("pod"),
|
||||
state: context.String("state"),
|
||||
quiet: context.Bool("quiet"),
|
||||
labels: make(map[string]string),
|
||||
}
|
||||
|
||||
for _, l := range context.StringSlice("label") {
|
||||
pair := strings.Split(l, "=")
|
||||
if len(pair) != 2 {
|
||||
return fmt.Errorf("incorrectly specified label: %v", l)
|
||||
}
|
||||
opts.labels[pair[0]] = pair[1]
|
||||
}
|
||||
|
||||
err = ListContainers(client, opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("listing containers failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// CreateContainer sends a CreateContainerRequest to the server, and parses
|
||||
// the returned CreateContainerResponse.
|
||||
func CreateContainer(client pb.RuntimeServiceClient, opts createOptions) error {
|
||||
config, err := loadContainerConfig(opts.configPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Override the name by the one specified through CLI
|
||||
if opts.name != "" {
|
||||
config.Metadata.Name = opts.name
|
||||
}
|
||||
|
||||
for k, v := range opts.labels {
|
||||
config.Labels[k] = v
|
||||
}
|
||||
|
||||
r, err := client.CreateContainer(context.Background(), &pb.CreateContainerRequest{
|
||||
PodSandboxId: opts.podID,
|
||||
Config: config,
|
||||
// TODO(runcom): this is missing PodSandboxConfig!!!
|
||||
// we should/could find a way to retrieve it from the fs and set it here
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(r.ContainerId)
|
||||
return nil
|
||||
}
|
||||
|
||||
// StartContainer sends a StartContainerRequest to the server, and parses
|
||||
// the returned StartContainerResponse.
|
||||
func StartContainer(client pb.RuntimeServiceClient, ID string) error {
|
||||
if ID == "" {
|
||||
return fmt.Errorf("ID cannot be empty")
|
||||
}
|
||||
_, err := client.StartContainer(context.Background(), &pb.StartContainerRequest{
|
||||
ContainerId: ID,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// StopContainer sends a StopContainerRequest to the server, and parses
|
||||
// the returned StopContainerResponse.
|
||||
func StopContainer(client pb.RuntimeServiceClient, ID string, timeout int64) error {
|
||||
if ID == "" {
|
||||
return fmt.Errorf("ID cannot be empty")
|
||||
}
|
||||
_, err := client.StopContainer(context.Background(), &pb.StopContainerRequest{
|
||||
ContainerId: ID,
|
||||
Timeout: timeout,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveContainer sends a RemoveContainerRequest to the server, and parses
|
||||
// the returned RemoveContainerResponse.
|
||||
func RemoveContainer(client pb.RuntimeServiceClient, ID string) error {
|
||||
if ID == "" {
|
||||
return fmt.Errorf("ID cannot be empty")
|
||||
}
|
||||
_, err := client.RemoveContainer(context.Background(), &pb.RemoveContainerRequest{
|
||||
ContainerId: ID,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContainerStatus sends a ContainerStatusRequest to the server, and parses
|
||||
// the returned ContainerStatusResponse.
|
||||
func ContainerStatus(client pb.RuntimeServiceClient, ID string) error {
|
||||
if ID == "" {
|
||||
return fmt.Errorf("ID cannot be empty")
|
||||
}
|
||||
r, err := client.ContainerStatus(context.Background(), &pb.ContainerStatusRequest{
|
||||
ContainerId: ID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("ID: %s\n", r.Status.Id)
|
||||
if r.Status.Metadata != nil {
|
||||
if r.Status.Metadata.Name != "" {
|
||||
fmt.Printf("Name: %s\n", r.Status.Metadata.Name)
|
||||
}
|
||||
fmt.Printf("Attempt: %v\n", r.Status.Metadata.Attempt)
|
||||
}
|
||||
// TODO(mzylowski): print it prettier
|
||||
fmt.Printf("Status: %s\n", r.Status.State)
|
||||
ctm := time.Unix(0, r.Status.CreatedAt)
|
||||
fmt.Printf("Created: %v\n", ctm)
|
||||
stm := time.Unix(0, r.Status.StartedAt)
|
||||
fmt.Printf("Started: %v\n", stm)
|
||||
ftm := time.Unix(0, r.Status.FinishedAt)
|
||||
fmt.Printf("Finished: %v\n", ftm)
|
||||
fmt.Printf("Exit Code: %v\n", r.Status.ExitCode)
|
||||
fmt.Printf("Reason: %v\n", r.Status.Reason)
|
||||
if r.Status.Image != nil {
|
||||
fmt.Printf("Image: %v\n", r.Status.Image.Image)
|
||||
}
|
||||
//
|
||||
// TODO: https://github.com/kubernetes-incubator/cri-o/issues/531
|
||||
//
|
||||
//fmt.Printf("ImageRef: %v\n", r.Status.ImageRef)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExecSync sends an ExecSyncRequest to the server, and parses
|
||||
// the returned ExecSyncResponse.
|
||||
func ExecSync(client pb.RuntimeServiceClient, ID string, cmd []string, timeout int64) error {
|
||||
if ID == "" {
|
||||
return fmt.Errorf("ID cannot be empty")
|
||||
}
|
||||
r, err := client.ExecSync(context.Background(), &pb.ExecSyncRequest{
|
||||
ContainerId: ID,
|
||||
Cmd: cmd,
|
||||
Timeout: timeout,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Stdout:")
|
||||
fmt.Println(string(r.Stdout))
|
||||
fmt.Println("Stderr:")
|
||||
fmt.Println(string(r.Stderr))
|
||||
fmt.Printf("Exit code: %v\n", r.ExitCode)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Exec sends an ExecRequest to the server, and parses
|
||||
// the returned ExecResponse.
|
||||
func Exec(client pb.RuntimeServiceClient, ID string, tty bool, stdin bool, urlOnly bool, cmd []string) error {
|
||||
if ID == "" {
|
||||
return fmt.Errorf("ID cannot be empty")
|
||||
}
|
||||
r, err := client.Exec(context.Background(), &pb.ExecRequest{
|
||||
ContainerId: ID,
|
||||
Cmd: cmd,
|
||||
Tty: tty,
|
||||
Stdin: stdin,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if urlOnly {
|
||||
fmt.Println("URL:")
|
||||
fmt.Println(r.Url)
|
||||
return nil
|
||||
}
|
||||
|
||||
execURL, err := url.Parse(r.Url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
streamExec, err := remotecommand.NewSPDYExecutor(&restclient.Config{}, "GET", execURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
options := remotecommand.StreamOptions{
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
Tty: tty,
|
||||
}
|
||||
|
||||
if stdin {
|
||||
options.Stdin = os.Stdin
|
||||
}
|
||||
|
||||
return streamExec.Stream(options)
|
||||
}
|
||||
|
||||
// ListContainers sends a ListContainerRequest to the server, and parses
|
||||
// the returned ListContainerResponse.
|
||||
func ListContainers(client pb.RuntimeServiceClient, opts listOptions) error {
|
||||
filter := &pb.ContainerFilter{}
|
||||
if opts.id != "" {
|
||||
filter.Id = opts.id
|
||||
}
|
||||
if opts.podID != "" {
|
||||
filter.PodSandboxId = opts.podID
|
||||
}
|
||||
if opts.state != "" {
|
||||
st := &pb.ContainerStateValue{}
|
||||
st.State = pb.ContainerState_CONTAINER_UNKNOWN
|
||||
switch opts.state {
|
||||
case "created":
|
||||
st.State = pb.ContainerState_CONTAINER_CREATED
|
||||
filter.State = st
|
||||
case "running":
|
||||
st.State = pb.ContainerState_CONTAINER_RUNNING
|
||||
filter.State = st
|
||||
case "stopped":
|
||||
st.State = pb.ContainerState_CONTAINER_EXITED
|
||||
filter.State = st
|
||||
default:
|
||||
log.Fatalf("--state should be one of created, running or stopped")
|
||||
}
|
||||
}
|
||||
if opts.labels != nil {
|
||||
filter.LabelSelector = opts.labels
|
||||
}
|
||||
r, err := client.ListContainers(context.Background(), &pb.ListContainersRequest{
|
||||
Filter: filter,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, c := range r.GetContainers() {
|
||||
if opts.quiet {
|
||||
fmt.Println(c.Id)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("ID: %s\n", c.Id)
|
||||
fmt.Printf("Pod: %s\n", c.PodSandboxId)
|
||||
if c.Metadata != nil {
|
||||
if c.Metadata.Name != "" {
|
||||
fmt.Printf("Name: %s\n", c.Metadata.Name)
|
||||
}
|
||||
fmt.Printf("Attempt: %v\n", c.Metadata.Attempt)
|
||||
}
|
||||
fmt.Printf("Status: %s\n", c.State)
|
||||
if c.Image != nil {
|
||||
fmt.Printf("Image: %s\n", c.Image.Image)
|
||||
}
|
||||
ctm := time.Unix(0, c.CreatedAt)
|
||||
fmt.Printf("Created: %v\n", ctm)
|
||||
if c.Labels != nil {
|
||||
fmt.Println("Labels:")
|
||||
for _, k := range getSortedKeys(c.Labels) {
|
||||
fmt.Printf("\t%s -> %s\n", k, c.Labels[k])
|
||||
}
|
||||
}
|
||||
if c.Annotations != nil {
|
||||
fmt.Println("Annotations:")
|
||||
for _, k := range getSortedKeys(c.Annotations) {
|
||||
fmt.Printf("\t%s -> %s\n", k, c.Annotations[k])
|
||||
}
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var inspectContainerCommand = cli.Command{
|
||||
Name: "inspect",
|
||||
Usage: "get container info from crio daemon",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "id",
|
||||
Value: "",
|
||||
Usage: "id of the container",
|
||||
},
|
||||
},
|
||||
Action: func(context *cli.Context) error {
|
||||
ID := context.String("id")
|
||||
if ID == "" {
|
||||
return fmt.Errorf("ID cannot be empty")
|
||||
}
|
||||
c, err := client.New(context.GlobalString("connect"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cInfo, err := c.ContainerInfo(ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jsonBytes, err := json.MarshalIndent(cInfo, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(jsonBytes))
|
||||
return nil
|
||||
},
|
||||
}
|
|
@ -1,173 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
"golang.org/x/net/context"
|
||||
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||
)
|
||||
|
||||
var imageCommand = cli.Command{
|
||||
Name: "image",
|
||||
Subcommands: []cli.Command{
|
||||
pullImageCommand,
|
||||
listImageCommand,
|
||||
imageStatusCommand,
|
||||
removeImageCommand,
|
||||
},
|
||||
}
|
||||
|
||||
var pullImageCommand = cli.Command{
|
||||
Name: "pull",
|
||||
Usage: "pull an image",
|
||||
Action: func(context *cli.Context) error {
|
||||
// Set up a connection to the server.
|
||||
conn, err := getClientConnection(context)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
client := pb.NewImageServiceClient(conn)
|
||||
|
||||
_, err = PullImage(client, context.Args().Get(0))
|
||||
if err != nil {
|
||||
return fmt.Errorf("pulling image failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var listImageCommand = cli.Command{
|
||||
Name: "list",
|
||||
Usage: "list images",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "quiet",
|
||||
Usage: "list only image IDs",
|
||||
},
|
||||
},
|
||||
Action: func(context *cli.Context) error {
|
||||
// Set up a connection to the server.
|
||||
conn, err := getClientConnection(context)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
client := pb.NewImageServiceClient(conn)
|
||||
|
||||
r, err := ListImages(client, context.Args().Get(0))
|
||||
if err != nil {
|
||||
return fmt.Errorf("listing images failed: %v", err)
|
||||
}
|
||||
quiet := context.Bool("quiet")
|
||||
for _, image := range r.Images {
|
||||
if quiet {
|
||||
fmt.Printf("%s\n", image.Id)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("ID: %s\n", image.Id)
|
||||
for _, tag := range image.RepoTags {
|
||||
fmt.Printf("Tag: %s\n", tag)
|
||||
}
|
||||
for _, digest := range image.RepoDigests {
|
||||
fmt.Printf("Digest: %s\n", digest)
|
||||
}
|
||||
if image.Size_ != 0 {
|
||||
fmt.Printf("Size: %d\n", image.Size_)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var imageStatusCommand = cli.Command{
|
||||
Name: "status",
|
||||
Usage: "return the status of an image",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "id",
|
||||
Usage: "id of the image",
|
||||
},
|
||||
},
|
||||
Action: func(context *cli.Context) error {
|
||||
// Set up a connection to the server.
|
||||
conn, err := getClientConnection(context)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
client := pb.NewImageServiceClient(conn)
|
||||
|
||||
r, err := ImageStatus(client, context.String("id"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("image status request failed: %v", err)
|
||||
}
|
||||
image := r.Image
|
||||
if image == nil {
|
||||
return fmt.Errorf("no such image present")
|
||||
}
|
||||
fmt.Printf("ID: %s\n", image.Id)
|
||||
for _, tag := range image.RepoTags {
|
||||
fmt.Printf("Tag: %s\n", tag)
|
||||
}
|
||||
for _, digest := range image.RepoDigests {
|
||||
fmt.Printf("Digest: %s\n", digest)
|
||||
}
|
||||
fmt.Printf("Size: %d\n", image.Size_)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
var removeImageCommand = cli.Command{
|
||||
Name: "remove",
|
||||
Usage: "remove an image",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "id",
|
||||
Value: "",
|
||||
Usage: "id of the image",
|
||||
},
|
||||
},
|
||||
Action: func(context *cli.Context) error {
|
||||
// Set up a connection to the server.
|
||||
conn, err := getClientConnection(context)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
client := pb.NewImageServiceClient(conn)
|
||||
|
||||
_, err = RemoveImage(client, context.String("id"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("removing the image failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// PullImage sends a PullImageRequest to the server, and parses
|
||||
// the returned PullImageResponse.
|
||||
func PullImage(client pb.ImageServiceClient, image string) (*pb.PullImageResponse, error) {
|
||||
return client.PullImage(context.Background(), &pb.PullImageRequest{Image: &pb.ImageSpec{Image: image}})
|
||||
}
|
||||
|
||||
// ListImages sends a ListImagesRequest to the server, and parses
|
||||
// the returned ListImagesResponse.
|
||||
func ListImages(client pb.ImageServiceClient, image string) (*pb.ListImagesResponse, error) {
|
||||
return client.ListImages(context.Background(), &pb.ListImagesRequest{Filter: &pb.ImageFilter{Image: &pb.ImageSpec{Image: image}}})
|
||||
}
|
||||
|
||||
// ImageStatus sends an ImageStatusRequest to the server, and parses
|
||||
// the returned ImageStatusResponse.
|
||||
func ImageStatus(client pb.ImageServiceClient, image string) (*pb.ImageStatusResponse, error) {
|
||||
return client.ImageStatus(context.Background(), &pb.ImageStatusRequest{Image: &pb.ImageSpec{Image: image}})
|
||||
}
|
||||
|
||||
// RemoveImage sends a RemoveImageRequest to the server, and parses
|
||||
// the returned RemoveImageResponse.
|
||||
func RemoveImage(client pb.ImageServiceClient, image string) (*pb.RemoveImageResponse, error) {
|
||||
if image == "" {
|
||||
return nil, fmt.Errorf("ID cannot be empty")
|
||||
}
|
||||
return client.RemoveImage(context.Background(), &pb.RemoveImageRequest{Image: &pb.ImageSpec{Image: image}})
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/kubernetes-incubator/cri-o/client"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var infoCommand = cli.Command{
|
||||
Name: "info",
|
||||
Usage: "get crio daemon info",
|
||||
Action: func(context *cli.Context) error {
|
||||
c, err := client.New(context.GlobalString("connect"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
di, err := c.DaemonInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
jsonBytes, err := json.MarshalIndent(di, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(jsonBytes))
|
||||
return nil
|
||||
},
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
"google.golang.org/grpc"
|
||||
pb "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.
|
||||
// It will be populated by the Makefile.
|
||||
var gitCommit = ""
|
||||
|
||||
func getClientConnection(context *cli.Context) (*grpc.ClientConn, error) {
|
||||
conn, err := grpc.Dial(context.GlobalString("connect"), grpc.WithInsecure(), grpc.WithTimeout(context.GlobalDuration("timeout")),
|
||||
grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {
|
||||
return net.DialTimeout("unix", addr, timeout)
|
||||
}))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to connect: %v", err)
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func openFile(path string) (*os.File, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("config at %s not found", path)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func loadPodSandboxConfig(path string) (*pb.PodSandboxConfig, error) {
|
||||
f, err := openFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var config pb.PodSandboxConfig
|
||||
if err := json.NewDecoder(f).Decode(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
func loadContainerConfig(path string) (*pb.ContainerConfig, error) {
|
||||
f, err := openFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var config pb.ContainerConfig
|
||||
if err := json.NewDecoder(f).Decode(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
var v []string
|
||||
if version != "" {
|
||||
v = append(v, version)
|
||||
}
|
||||
if gitCommit != "" {
|
||||
v = append(v, fmt.Sprintf("commit: %s", gitCommit))
|
||||
}
|
||||
|
||||
app.Name = "crioctl"
|
||||
app.Usage = "client for crio"
|
||||
app.Version = strings.Join(v, "\n")
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
podSandboxCommand,
|
||||
containerCommand,
|
||||
runtimeVersionCommand,
|
||||
imageCommand,
|
||||
infoCommand,
|
||||
}
|
||||
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "connect",
|
||||
Value: "/var/run/crio.sock",
|
||||
Usage: "Socket to connect to",
|
||||
},
|
||||
cli.DurationFlag{
|
||||
Name: "timeout",
|
||||
Value: 10 * time.Second,
|
||||
Usage: "Timeout of connecting to server",
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
}
|
|
@ -1,386 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
"golang.org/x/net/context"
|
||||
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||
)
|
||||
|
||||
var podSandboxCommand = cli.Command{
|
||||
Name: "pod",
|
||||
Subcommands: []cli.Command{
|
||||
runPodSandboxCommand,
|
||||
stopPodSandboxCommand,
|
||||
removePodSandboxCommand,
|
||||
podSandboxStatusCommand,
|
||||
listPodSandboxCommand,
|
||||
},
|
||||
}
|
||||
|
||||
var runPodSandboxCommand = cli.Command{
|
||||
Name: "run",
|
||||
Usage: "run a pod",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "config",
|
||||
Value: "",
|
||||
Usage: "the path of a pod sandbox config file",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "name",
|
||||
Value: "",
|
||||
Usage: "the name of the pod sandbox",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "label",
|
||||
Usage: "add key=value labels to the container",
|
||||
},
|
||||
},
|
||||
Action: func(context *cli.Context) error {
|
||||
// Set up a connection to the server.
|
||||
conn, err := getClientConnection(context)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
client := pb.NewRuntimeServiceClient(conn)
|
||||
|
||||
opts := createOptions{
|
||||
configPath: context.String("config"),
|
||||
name: context.String("name"),
|
||||
labels: make(map[string]string),
|
||||
}
|
||||
|
||||
for _, l := range context.StringSlice("label") {
|
||||
pair := strings.Split(l, "=")
|
||||
if len(pair) != 2 {
|
||||
return fmt.Errorf("incorrectly specified label: %v", l)
|
||||
}
|
||||
opts.labels[pair[0]] = pair[1]
|
||||
}
|
||||
|
||||
// Test RuntimeServiceClient.RunPodSandbox
|
||||
err = RunPodSandbox(client, opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Creating the pod sandbox failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var stopPodSandboxCommand = cli.Command{
|
||||
Name: "stop",
|
||||
Usage: "stop a pod sandbox",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "id",
|
||||
Value: "",
|
||||
Usage: "id of the pod sandbox",
|
||||
},
|
||||
},
|
||||
Action: func(context *cli.Context) error {
|
||||
// Set up a connection to the server.
|
||||
conn, err := getClientConnection(context)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
client := pb.NewRuntimeServiceClient(conn)
|
||||
|
||||
err = StopPodSandbox(client, context.String("id"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("stopping the pod sandbox failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var removePodSandboxCommand = cli.Command{
|
||||
Name: "remove",
|
||||
Usage: "remove a pod sandbox",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "id",
|
||||
Value: "",
|
||||
Usage: "id of the pod sandbox",
|
||||
},
|
||||
},
|
||||
Action: func(context *cli.Context) error {
|
||||
// Set up a connection to the server.
|
||||
conn, err := getClientConnection(context)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
client := pb.NewRuntimeServiceClient(conn)
|
||||
|
||||
err = RemovePodSandbox(client, context.String("id"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("removing the pod sandbox failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var podSandboxStatusCommand = cli.Command{
|
||||
Name: "status",
|
||||
Usage: "return the status of a pod",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "id",
|
||||
Value: "",
|
||||
Usage: "id of the pod",
|
||||
},
|
||||
},
|
||||
Action: func(context *cli.Context) error {
|
||||
// Set up a connection to the server.
|
||||
conn, err := getClientConnection(context)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
client := pb.NewRuntimeServiceClient(conn)
|
||||
|
||||
err = PodSandboxStatus(client, context.String("id"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting the pod sandbox status failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var listPodSandboxCommand = cli.Command{
|
||||
Name: "list",
|
||||
Usage: "list pod sandboxes",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "id",
|
||||
Value: "",
|
||||
Usage: "filter by pod sandbox id",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "state",
|
||||
Value: "",
|
||||
Usage: "filter by pod sandbox state",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "label",
|
||||
Usage: "filter by key=value label",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "quiet",
|
||||
Usage: "list only pod IDs",
|
||||
},
|
||||
},
|
||||
Action: func(context *cli.Context) error {
|
||||
// Set up a connection to the server.
|
||||
conn, err := getClientConnection(context)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
client := pb.NewRuntimeServiceClient(conn)
|
||||
|
||||
opts := listOptions{
|
||||
id: context.String("id"),
|
||||
state: context.String("state"),
|
||||
quiet: context.Bool("quiet"),
|
||||
labels: make(map[string]string),
|
||||
}
|
||||
|
||||
for _, l := range context.StringSlice("label") {
|
||||
pair := strings.Split(l, "=")
|
||||
if len(pair) != 2 {
|
||||
return fmt.Errorf("incorrectly specified label: %v", l)
|
||||
}
|
||||
opts.labels[pair[0]] = pair[1]
|
||||
}
|
||||
|
||||
err = ListPodSandboxes(client, opts)
|
||||
if err != nil {
|
||||
return fmt.Errorf("listing pod sandboxes failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// RunPodSandbox sends a RunPodSandboxRequest to the server, and parses
|
||||
// the returned RunPodSandboxResponse.
|
||||
func RunPodSandbox(client pb.RuntimeServiceClient, opts createOptions) error {
|
||||
config, err := loadPodSandboxConfig(opts.configPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Override the name by the one specified through CLI
|
||||
if opts.name != "" {
|
||||
config.Metadata.Name = opts.name
|
||||
}
|
||||
|
||||
for k, v := range opts.labels {
|
||||
config.Labels[k] = v
|
||||
}
|
||||
|
||||
r, err := client.RunPodSandbox(context.Background(), &pb.RunPodSandboxRequest{Config: config})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(r.PodSandboxId)
|
||||
return nil
|
||||
}
|
||||
|
||||
// StopPodSandbox sends a StopPodSandboxRequest to the server, and parses
|
||||
// the returned StopPodSandboxResponse.
|
||||
func StopPodSandbox(client pb.RuntimeServiceClient, ID string) error {
|
||||
if ID == "" {
|
||||
return fmt.Errorf("ID cannot be empty")
|
||||
}
|
||||
_, err := client.StopPodSandbox(context.Background(), &pb.StopPodSandboxRequest{PodSandboxId: ID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemovePodSandbox sends a RemovePodSandboxRequest to the server, and parses
|
||||
// the returned RemovePodSandboxResponse.
|
||||
func RemovePodSandbox(client pb.RuntimeServiceClient, ID string) error {
|
||||
if ID == "" {
|
||||
return fmt.Errorf("ID cannot be empty")
|
||||
}
|
||||
_, err := client.RemovePodSandbox(context.Background(), &pb.RemovePodSandboxRequest{PodSandboxId: ID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// PodSandboxStatus sends a PodSandboxStatusRequest to the server, and parses
|
||||
// the returned PodSandboxStatusResponse.
|
||||
func PodSandboxStatus(client pb.RuntimeServiceClient, ID string) error {
|
||||
if ID == "" {
|
||||
return fmt.Errorf("ID cannot be empty")
|
||||
}
|
||||
r, err := client.PodSandboxStatus(context.Background(), &pb.PodSandboxStatusRequest{PodSandboxId: ID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("ID: %s\n", r.Status.Id)
|
||||
if r.Status.Metadata != nil {
|
||||
if r.Status.Metadata.Name != "" {
|
||||
fmt.Printf("Name: %s\n", r.Status.Metadata.Name)
|
||||
}
|
||||
if r.Status.Metadata.Uid != "" {
|
||||
fmt.Printf("UID: %s\n", r.Status.Metadata.Uid)
|
||||
}
|
||||
if r.Status.Metadata.Namespace != "" {
|
||||
fmt.Printf("Namespace: %s\n", r.Status.Metadata.Namespace)
|
||||
}
|
||||
fmt.Printf("Attempt: %v\n", r.Status.Metadata.Attempt)
|
||||
}
|
||||
fmt.Printf("Status: %s\n", r.Status.State)
|
||||
ctm := time.Unix(0, r.Status.CreatedAt)
|
||||
fmt.Printf("Created: %v\n", ctm)
|
||||
if r.Status.Network != nil {
|
||||
fmt.Printf("IP Address: %v\n", r.Status.Network.Ip)
|
||||
}
|
||||
if r.Status.Labels != nil {
|
||||
fmt.Println("Labels:")
|
||||
for _, k := range getSortedKeys(r.Status.Labels) {
|
||||
fmt.Printf("\t%s -> %s\n", k, r.Status.Labels[k])
|
||||
}
|
||||
}
|
||||
if r.Status.Annotations != nil {
|
||||
fmt.Println("Annotations:")
|
||||
for _, k := range getSortedKeys(r.Status.Annotations) {
|
||||
fmt.Printf("\t%s -> %s\n", k, r.Status.Annotations[k])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListPodSandboxes sends a ListPodSandboxRequest to the server, and parses
|
||||
// the returned ListPodSandboxResponse.
|
||||
func ListPodSandboxes(client pb.RuntimeServiceClient, opts listOptions) error {
|
||||
filter := &pb.PodSandboxFilter{}
|
||||
if opts.id != "" {
|
||||
filter.Id = opts.id
|
||||
}
|
||||
if opts.state != "" {
|
||||
st := &pb.PodSandboxStateValue{}
|
||||
st.State = pb.PodSandboxState_SANDBOX_NOTREADY
|
||||
switch opts.state {
|
||||
case "ready":
|
||||
st.State = pb.PodSandboxState_SANDBOX_READY
|
||||
filter.State = st
|
||||
case "notready":
|
||||
st.State = pb.PodSandboxState_SANDBOX_NOTREADY
|
||||
filter.State = st
|
||||
default:
|
||||
log.Fatalf("--state should be ready or notready")
|
||||
}
|
||||
}
|
||||
if opts.labels != nil {
|
||||
filter.LabelSelector = opts.labels
|
||||
}
|
||||
r, err := client.ListPodSandbox(context.Background(), &pb.ListPodSandboxRequest{
|
||||
Filter: filter,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, pod := range r.Items {
|
||||
if opts.quiet {
|
||||
fmt.Println(pod.Id)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("ID: %s\n", pod.Id)
|
||||
if pod.Metadata != nil {
|
||||
if pod.Metadata.Name != "" {
|
||||
fmt.Printf("Name: %s\n", pod.Metadata.Name)
|
||||
}
|
||||
if pod.Metadata.Uid != "" {
|
||||
fmt.Printf("UID: %s\n", pod.Metadata.Uid)
|
||||
}
|
||||
if pod.Metadata.Namespace != "" {
|
||||
fmt.Printf("Namespace: %s\n", pod.Metadata.Namespace)
|
||||
}
|
||||
fmt.Printf("Attempt: %v\n", pod.Metadata.Attempt)
|
||||
}
|
||||
fmt.Printf("Status: %s\n", pod.State)
|
||||
ctm := time.Unix(0, pod.CreatedAt)
|
||||
fmt.Printf("Created: %v\n", ctm)
|
||||
if pod.Labels != nil {
|
||||
fmt.Println("Labels:")
|
||||
for _, k := range getSortedKeys(pod.Labels) {
|
||||
fmt.Printf("\t%s -> %s\n", k, pod.Labels[k])
|
||||
}
|
||||
}
|
||||
if pod.Annotations != nil {
|
||||
fmt.Println("Annotations:")
|
||||
for _, k := range getSortedKeys(pod.Annotations) {
|
||||
fmt.Printf("\t%s -> %s\n", k, pod.Annotations[k])
|
||||
}
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getSortedKeys(m map[string]string) []string {
|
||||
var keys []string
|
||||
for k := range m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
return keys
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
"golang.org/x/net/context"
|
||||
pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime"
|
||||
)
|
||||
|
||||
var runtimeVersionCommand = cli.Command{
|
||||
Name: "runtimeversion",
|
||||
Usage: "get runtime version information",
|
||||
Action: func(context *cli.Context) error {
|
||||
// Set up a connection to the server.
|
||||
conn, err := getClientConnection(context)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
client := pb.NewRuntimeServiceClient(conn)
|
||||
|
||||
// Test RuntimeServiceClient.Version
|
||||
version := "v1alpha1"
|
||||
err = Version(client, version)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Getting the runtime version failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// Version sends a VersionRequest to the server, and parses the returned VersionResponse.
|
||||
func Version(client pb.RuntimeServiceClient, version string) error {
|
||||
r, err := client.Version(context.Background(), &pb.VersionRequest{Version: version})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("VersionResponse: Version: %s, RuntimeName: %s, RuntimeVersion: %s, RuntimeApiVersion: %s\n", r.Version, r.RuntimeName, r.RuntimeVersion, r.RuntimeApiVersion)
|
||||
return nil
|
||||
}
|
|
@ -1,55 +1,3 @@
|
|||
## Kubernetes Community Code of Conduct
|
||||
# Kubernetes Community Code of Conduct
|
||||
|
||||
### Contributor Code of Conduct
|
||||
|
||||
As contributors and maintainers of this project, and in the interest of fostering
|
||||
an open and welcoming community, we pledge to respect all people who contribute
|
||||
through reporting issues, posting feature requests, updating documentation,
|
||||
submitting pull requests or patches, and other activities.
|
||||
|
||||
We are committed to making participation in this project a harassment-free experience for
|
||||
everyone, regardless of level of experience, gender, gender identity and expression,
|
||||
sexual orientation, disability, personal appearance, body size, race, ethnicity, age,
|
||||
religion, or nationality.
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery.
|
||||
* Personal attacks.
|
||||
* Trolling or insulting/derogatory comments.
|
||||
* Public or private harassment.
|
||||
* Publishing other's private information, such as physical or electronic addresses,
|
||||
without explicit permission.
|
||||
* Other unethical or unprofessional conduct.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are not
|
||||
aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers
|
||||
commit themselves to fairly and consistently applying these principles to every aspect
|
||||
of managing this project. Project maintainers who do not follow or enforce the Code of
|
||||
Conduct may be permanently removed from the project team.
|
||||
|
||||
This code of conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community.
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting a Kubernetes maintainer, Sarah Novotny <sarahnovotny@google.com>, and/or Dan Kohn <dan@linuxfoundation.org>.
|
||||
|
||||
This Code of Conduct is adapted from the Contributor Covenant
|
||||
(http://contributor-covenant.org), version 1.2.0, available at
|
||||
http://contributor-covenant.org/version/1/2/0/
|
||||
|
||||
### Kubernetes Events Code of Conduct
|
||||
|
||||
Kubernetes events are working conferences intended for professional networking and collaboration in the
|
||||
Kubernetes community. Attendees are expected to behave according to professional standards and in accordance
|
||||
with their employer's policies on appropriate workplace behavior.
|
||||
|
||||
While at Kubernetes events or related social networking opportunities, attendees should not engage in
|
||||
discriminatory or offensive speech or actions regarding gender, sexuality, race, or religion. Speakers should
|
||||
be especially aware of these concerns.
|
||||
|
||||
The Kubernetes team does not condone any statements by speakers contrary to these standards. The Kubernetes
|
||||
team reserves the right to deny entrance and/or eject from an event (without refund) any individual found to
|
||||
be engaging in discriminatory or offensive speech or actions.
|
||||
|
||||
Please bring any concerns to the immediate attention of the Kubernetes event staff.
|
||||
Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md)
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/stat.h>
|
||||
|
@ -296,7 +295,6 @@ const char *stdpipe_name(stdpipe_t pipe)
|
|||
static int write_k8s_log(int fd, stdpipe_t pipe, const char *buf, ssize_t buflen)
|
||||
{
|
||||
char tsbuf[TSBUFLEN];
|
||||
static stdpipe_t trailing_line = NO_PIPE;
|
||||
writev_buffer_t bufv = {0};
|
||||
static int64_t bytes_written = 0;
|
||||
int64_t bytes_to_be_written = 0;
|
||||
|
@ -313,35 +311,22 @@ static int write_k8s_log(int fd, stdpipe_t pipe, const char *buf, ssize_t buflen
|
|||
while (buflen > 0) {
|
||||
const char *line_end = NULL;
|
||||
ptrdiff_t line_len = 0;
|
||||
bool insert_newline = FALSE;
|
||||
bool insert_timestamp = FALSE;
|
||||
bool partial = FALSE;
|
||||
|
||||
/* Find the end of the line, or alternatively the end of the buffer. */
|
||||
line_end = memchr(buf, '\n', buflen);
|
||||
if (line_end == NULL)
|
||||
if (line_end == NULL) {
|
||||
line_end = &buf[buflen-1];
|
||||
partial = TRUE;
|
||||
}
|
||||
line_len = line_end - buf + 1;
|
||||
|
||||
bytes_to_be_written = line_len;
|
||||
if (trailing_line != pipe) {
|
||||
/*
|
||||
* Write the (timestamp, stream) tuple if there isn't any trailing
|
||||
* output from the previous line (or if there is trailing output but
|
||||
* the current buffer being printed is from a different pipe).
|
||||
*/
|
||||
insert_timestamp = TRUE;
|
||||
bytes_to_be_written += (TSBUFLEN - 1);
|
||||
/*
|
||||
* If there was a trailing line from a different pipe, prepend a
|
||||
* newline to split it properly. This technically breaks the flow
|
||||
* of the previous line (adding a newline in the log where there
|
||||
* wasn't one output) but without modifying the file in a
|
||||
* non-append-only way there's not much we can do.
|
||||
*/
|
||||
if (trailing_line != NO_PIPE) {
|
||||
insert_newline = TRUE;
|
||||
bytes_to_be_written += 1;
|
||||
}
|
||||
/* This is line_len bytes + TSBUFLEN - 1 + 2 (- 1 is for ignoring \0). */
|
||||
bytes_to_be_written = line_len + TSBUFLEN + 1;
|
||||
|
||||
/* If partial, then we add a \n */
|
||||
if (partial) {
|
||||
bytes_to_be_written += 1;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -351,8 +336,6 @@ static int write_k8s_log(int fd, stdpipe_t pipe, const char *buf, ssize_t buflen
|
|||
*/
|
||||
if ((opt_log_size_max > 0) && (bytes_written + bytes_to_be_written) > opt_log_size_max) {
|
||||
ninfo("Creating new log file");
|
||||
insert_newline = FALSE;
|
||||
insert_timestamp = TRUE;
|
||||
bytes_written = 0;
|
||||
|
||||
/* Close the existing fd */
|
||||
|
@ -366,22 +349,25 @@ static int write_k8s_log(int fd, stdpipe_t pipe, const char *buf, ssize_t buflen
|
|||
/* Open the log path file again */
|
||||
log_fd = open(opt_log_path, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, 0600);
|
||||
if (log_fd < 0)
|
||||
pexit("Failed to open log file");
|
||||
pexit("Failed to open log file %s: %s", opt_log_path, strerror(errno));
|
||||
fd = log_fd;
|
||||
}
|
||||
|
||||
/* Output a newline */
|
||||
if (insert_newline) {
|
||||
if (writev_buffer_append_segment(fd, &bufv, "\n", -1) < 0) {
|
||||
nwarn("failed to write newline to log");
|
||||
goto next;
|
||||
}
|
||||
/* Output the timestamp */
|
||||
if (writev_buffer_append_segment(fd, &bufv, tsbuf, -1) < 0) {
|
||||
nwarn("failed to write (timestamp, stream) to log");
|
||||
goto next;
|
||||
}
|
||||
|
||||
/* Output a timestamp */
|
||||
if (insert_timestamp) {
|
||||
if (writev_buffer_append_segment(fd, &bufv, tsbuf, -1) < 0) {
|
||||
nwarn("failed to write (timestamp, stream) to log");
|
||||
/* Output log tag for partial or newline */
|
||||
if (partial) {
|
||||
if (writev_buffer_append_segment(fd, &bufv, "P ", -1) < 0) {
|
||||
nwarn("failed to write partial log tag");
|
||||
goto next;
|
||||
}
|
||||
} else {
|
||||
if (writev_buffer_append_segment(fd, &bufv, "F ", -1) < 0) {
|
||||
nwarn("failed to write end log tag");
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
|
@ -392,11 +378,15 @@ static int write_k8s_log(int fd, stdpipe_t pipe, const char *buf, ssize_t buflen
|
|||
goto next;
|
||||
}
|
||||
|
||||
/* Output a newline for partial */
|
||||
if (partial) {
|
||||
if (writev_buffer_append_segment(fd, &bufv, "\n", -1) < 0) {
|
||||
nwarn("failed to write newline to log");
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
|
||||
bytes_written += bytes_to_be_written;
|
||||
|
||||
/* If we did not output a full line, then we are a trailing_line. */
|
||||
trailing_line = (*line_end == '\n') ? NO_PIPE : pipe;
|
||||
|
||||
next:
|
||||
/* Update the head of the buffer remaining to output. */
|
||||
buf += line_len;
|
||||
|
@ -1130,6 +1120,8 @@ int main(int argc, char *argv[])
|
|||
|
||||
if (opt_runtime_path == NULL)
|
||||
nexit("Runtime path not provided. Use --runtime");
|
||||
if (access(opt_runtime_path, X_OK) < 0)
|
||||
pexit("Runtime path %s is not valid: %s", opt_runtime_path, strerror(errno));
|
||||
|
||||
if (!opt_exec && opt_exit_dir == NULL)
|
||||
nexit("Container exit directory not provided. Use --exit-dir");
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
.PHONY: dist
|
||||
dist: crio.spec
|
||||
spectool -g crio.spec
|
||||
|
||||
.PHONY: rpm
|
||||
rpm: dist
|
||||
rpmbuild --define "_sourcedir `pwd`" --define "_specdir `pwd`" \
|
||||
--define "_rpmdir `pwd`" --define "_srcrpmdir `pwd`" -ba crio.spec
|
||||
|
||||
all: rpm
|
||||
|
||||
clean:
|
||||
rm -f *rpm *gz
|
||||
rm -rf x86_64
|
|
@ -1,76 +0,0 @@
|
|||
%define debug_package %{nil}
|
||||
%global provider github
|
||||
%global provider_tld com
|
||||
%global project kubernetes-incubator
|
||||
%global repo cri-o
|
||||
%global Name crio
|
||||
# https://github.com/kubernetes-incubator/cri-o
|
||||
%global provider_prefix %{provider}.%{provider_tld}/%{project}/%{repo}
|
||||
%global import_path %{provider_prefix}
|
||||
%global commit 8ba639952a95f2e24cc98987689138b67545576c
|
||||
%global shortcommit %(c=%{commit}; echo ${c:0:7})
|
||||
|
||||
Name: %{Name}
|
||||
Version: 0.0.1
|
||||
Release: 1.git%{shortcommit}%{?dist}
|
||||
Summary: Kubelet Container Runtime Interface (CRI) for OCI runtimes.
|
||||
Group: Applications/Text
|
||||
License: Apache 2.0
|
||||
URL: https://%{provider_prefix}
|
||||
Source0: https://%{provider_prefix}/archive/%{commit}/%{repo}-%{shortcommit}.tar.gz
|
||||
Provides: %{repo}
|
||||
|
||||
BuildRequires: golang-github-cpuguy83-go-md2man
|
||||
|
||||
%description
|
||||
The crio package provides an implementation of the
|
||||
Kubelet Container Runtime Interface (CRI) using OCI conformant runtimes.
|
||||
|
||||
crio provides following functionalities:
|
||||
|
||||
Support multiple image formats including the existing Docker image format
|
||||
Support for multiple means to download images including trust & image verification
|
||||
Container image management (managing image layers, overlay filesystems, etc)
|
||||
Container process lifecycle management
|
||||
Monitoring and logging required to satisfy the CRI
|
||||
Resource isolation as required by the CRI
|
||||
|
||||
%prep
|
||||
%setup -q -n %{repo}-%{commit}
|
||||
|
||||
%build
|
||||
make all
|
||||
|
||||
%install
|
||||
%make_install
|
||||
%make_install install.systemd
|
||||
|
||||
#define license tag if not already defined
|
||||
%{!?_licensedir:%global license %doc}
|
||||
%files
|
||||
%{_bindir}/crio
|
||||
%{_bindir}/crioctl
|
||||
%{_mandir}/man5/crio.conf.5*
|
||||
%{_mandir}/man8/crio.8*
|
||||
%{_sysconfdir}/crio.conf
|
||||
%{_sysconfdir}/seccomp.json
|
||||
%dir /%{_libexecdir}/crio
|
||||
/%{_libexecdir}/crio/conmon
|
||||
/%{_libexecdir}/crio/pause
|
||||
%{_unitdir}/crio.service
|
||||
%doc README.md
|
||||
%license LICENSE
|
||||
%dir /usr/share/oci-umount/oci-umount.d
|
||||
/usr/share/oci-umount/oci-umount.d/cri-umount.conf
|
||||
|
||||
|
||||
%preun
|
||||
%systemd_preun %{Name}
|
||||
|
||||
%postun
|
||||
%systemd_postun_with_restart %{Name}
|
||||
|
||||
%changelog
|
||||
* Mon Oct 31 2016 Dan Walsh <dwalsh@redhat.com> - 0.0.1
|
||||
- Initial RPM release
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
FROM centos
|
||||
|
||||
ENV VERSION=0 RELEASE=1 ARCH=x86_64
|
||||
LABEL com.redhat.component="cri-o" \
|
||||
name="$FGC/cri-o" \
|
||||
version="$VERSION" \
|
||||
release="$RELEASE.$DISTTAG" \
|
||||
architecture="$ARCH" \
|
||||
usage="atomic install --system --system-package=no crio && systemctl start crio" \
|
||||
summary="The cri-o daemon as a system container." \
|
||||
maintainer="Yu Qi Zhang <jzehrarnyg@gmail.com>" \
|
||||
atomic.type="system"
|
||||
|
||||
RUN yum-config-manager --nogpgcheck --add-repo https://cbs.centos.org/repos/virt7-container-common-candidate/x86_64/os/ && \
|
||||
yum install --disablerepo=extras --nogpgcheck --setopt=tsflags=nodocs -y iptables cri-o socat iproute runc && \
|
||||
rpm -V iptables cri-o iproute runc && \
|
||||
yum clean all && \
|
||||
mkdir -p /exports/hostfs/etc/crio /exports/hostfs/opt/cni/bin/ /exports/hostfs/var/lib/containers/storage/ && \
|
||||
cp /etc/crio/* /exports/hostfs/etc/crio && \
|
||||
if test -e /usr/libexec/cni; then cp -Lr /usr/libexec/cni/* /exports/hostfs/opt/cni/bin/; fi
|
||||
|
||||
RUN sed -i '/storage_option =/s/.*/&\n"overlay.override_kernel_check=1",/' /exports/hostfs/etc/crio/crio.conf
|
||||
|
||||
COPY manifest.json tmpfiles.template config.json.template service.template /exports/
|
||||
|
||||
COPY set_mounts.sh /
|
||||
COPY run.sh /usr/bin/
|
||||
|
||||
CMD ["/usr/bin/run.sh"]
|
|
@ -0,0 +1,57 @@
|
|||
# cri-o
|
||||
|
||||
This is the cri-o daemon as a system container.
|
||||
|
||||
## Building the image from source:
|
||||
|
||||
```
|
||||
# git clone https://github.com/projectatomic/atomic-system-containers
|
||||
# cd atomic-system-containers/cri-o
|
||||
# docker build -t crio .
|
||||
```
|
||||
|
||||
## Running the system container, with the atomic CLI:
|
||||
|
||||
Pull from registry into ostree:
|
||||
|
||||
```
|
||||
# atomic pull --storage ostree $REGISTRY/crio
|
||||
```
|
||||
|
||||
Or alternatively, pull from local docker:
|
||||
|
||||
```
|
||||
# atomic pull --storage ostree docker:crio:latest
|
||||
```
|
||||
|
||||
Install the container:
|
||||
|
||||
Currently we recommend using --system-package=no to avoid having rpmbuild create an rpm file
|
||||
during installation. This flag will tell the atomic CLI to fall back to copying files to the
|
||||
host instead.
|
||||
|
||||
```
|
||||
# atomic install --system --system-package=no --name=crio ($REGISTRY)/crio
|
||||
```
|
||||
|
||||
Start as a systemd service:
|
||||
|
||||
```
|
||||
# systemctl start crio
|
||||
```
|
||||
|
||||
Stopping the service
|
||||
|
||||
```
|
||||
# systemctl stop crio
|
||||
```
|
||||
|
||||
Removing the container
|
||||
|
||||
```
|
||||
# atomic uninstall crio
|
||||
```
|
||||
|
||||
## Binary version
|
||||
|
||||
You can find the image automatically built as: registry.centos.org/projectatomic/cri-o:latest
|
|
@ -0,0 +1,41 @@
|
|||
# This is for the purpose of building containers on the CentOS Community Container
|
||||
# Pipeline. The containers are built, tested and delivered to registry.centos.org and
|
||||
# lifecycled as well. A corresponding entry must exist in the container index itself,
|
||||
# located at https://github.com/CentOS/container-index/tree/master/index.d
|
||||
# You can know more at the following links:
|
||||
# * https://github.com/CentOS/container-pipeline-service/blob/master/README.md
|
||||
# * https://github.com/CentOS/container-index/blob/master/README.rst
|
||||
# * https://wiki.centos.org/ContainerPipeline
|
||||
|
||||
# This will be part of the name of the container. It should match the job-id in index entry
|
||||
job-id: cri-o
|
||||
|
||||
#the following are optional, can be left blank
|
||||
#defaults, where applicable are filled in
|
||||
#nulecule-file : nulecule
|
||||
|
||||
# This flag tells the container pipeline to skip user defined tests on their container
|
||||
test-skip : True
|
||||
|
||||
# This is path of the script that initiates the user defined tests. It must be able to
|
||||
# return an exit code.
|
||||
test-script : null
|
||||
|
||||
# This is the path of custom build script.
|
||||
build-script : null
|
||||
|
||||
# This is the path of the custom delivery script
|
||||
delivery-script : null
|
||||
|
||||
# This flag tells the pipeline to deliver this container to docker hub.
|
||||
docker-index : True
|
||||
|
||||
# This flag can be used to enable or disable the custom delivery
|
||||
custom-delivery : False
|
||||
|
||||
# This flag can be used to enable or disable delivery of container to local registry
|
||||
local-delivery : True
|
||||
|
||||
Upstreams :
|
||||
- ref :
|
||||
url :
|
|
@ -0,0 +1,427 @@
|
|||
{
|
||||
"ociVersion": "1.0.0",
|
||||
"platform": {
|
||||
"arch": "amd64",
|
||||
"os": "linux"
|
||||
},
|
||||
"process": {
|
||||
"args": [
|
||||
"/usr/bin/run.sh"
|
||||
],
|
||||
"capabilities": {
|
||||
"ambient": [
|
||||
"CAP_CHOWN",
|
||||
"CAP_FOWNER",
|
||||
"CAP_FSETID",
|
||||
"CAP_KILL",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_LINUX_IMMUTABLE",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_NET_BROADCAST",
|
||||
"CAP_NET_ADMIN",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_IPC_LOCK",
|
||||
"CAP_IPC_OWNER",
|
||||
"CAP_SYS_MODULE",
|
||||
"CAP_SYS_RAWIO",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_SYS_PTRACE",
|
||||
"CAP_SYS_PACCT",
|
||||
"CAP_SYS_ADMIN",
|
||||
"CAP_SYS_BOOT",
|
||||
"CAP_SYS_NICE",
|
||||
"CAP_SYS_RESOURCE",
|
||||
"CAP_SYS_TIME",
|
||||
"CAP_SYS_TTY_CONFIG",
|
||||
"CAP_MKNOD",
|
||||
"CAP_LEASE",
|
||||
"CAP_AUDIT_WRITE",
|
||||
"CAP_AUDIT_CONTROL",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_MAC_OVERRIDE",
|
||||
"CAP_DAC_READ_SEARCH",
|
||||
"CAP_MAC_ADMIN",
|
||||
"CAP_SYSLOG",
|
||||
"CAP_WAKE_ALARM",
|
||||
"CAP_BLOCK_SUSPEND"
|
||||
],
|
||||
"bounding": [
|
||||
"CAP_CHOWN",
|
||||
"CAP_FOWNER",
|
||||
"CAP_FSETID",
|
||||
"CAP_KILL",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_LINUX_IMMUTABLE",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_NET_BROADCAST",
|
||||
"CAP_NET_ADMIN",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_IPC_LOCK",
|
||||
"CAP_IPC_OWNER",
|
||||
"CAP_SYS_MODULE",
|
||||
"CAP_SYS_RAWIO",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_SYS_PTRACE",
|
||||
"CAP_SYS_PACCT",
|
||||
"CAP_SYS_ADMIN",
|
||||
"CAP_SYS_BOOT",
|
||||
"CAP_SYS_NICE",
|
||||
"CAP_SYS_RESOURCE",
|
||||
"CAP_SYS_TIME",
|
||||
"CAP_SYS_TTY_CONFIG",
|
||||
"CAP_MKNOD",
|
||||
"CAP_LEASE",
|
||||
"CAP_AUDIT_WRITE",
|
||||
"CAP_AUDIT_CONTROL",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_MAC_OVERRIDE",
|
||||
"CAP_DAC_READ_SEARCH",
|
||||
"CAP_MAC_ADMIN",
|
||||
"CAP_SYSLOG",
|
||||
"CAP_WAKE_ALARM",
|
||||
"CAP_BLOCK_SUSPEND"
|
||||
],
|
||||
"effective": [
|
||||
"CAP_CHOWN",
|
||||
"CAP_FOWNER",
|
||||
"CAP_FSETID",
|
||||
"CAP_KILL",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_LINUX_IMMUTABLE",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_NET_BROADCAST",
|
||||
"CAP_NET_ADMIN",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_IPC_LOCK",
|
||||
"CAP_IPC_OWNER",
|
||||
"CAP_SYS_MODULE",
|
||||
"CAP_SYS_RAWIO",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_SYS_PTRACE",
|
||||
"CAP_SYS_PACCT",
|
||||
"CAP_SYS_ADMIN",
|
||||
"CAP_SYS_BOOT",
|
||||
"CAP_SYS_NICE",
|
||||
"CAP_SYS_RESOURCE",
|
||||
"CAP_SYS_TIME",
|
||||
"CAP_SYS_TTY_CONFIG",
|
||||
"CAP_MKNOD",
|
||||
"CAP_LEASE",
|
||||
"CAP_AUDIT_WRITE",
|
||||
"CAP_AUDIT_CONTROL",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_MAC_OVERRIDE",
|
||||
"CAP_DAC_READ_SEARCH",
|
||||
"CAP_MAC_ADMIN",
|
||||
"CAP_SYSLOG",
|
||||
"CAP_WAKE_ALARM",
|
||||
"CAP_BLOCK_SUSPEND"
|
||||
],
|
||||
"inheritable": [
|
||||
"CAP_CHOWN",
|
||||
"CAP_FOWNER",
|
||||
"CAP_FSETID",
|
||||
"CAP_KILL",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_LINUX_IMMUTABLE",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_NET_BROADCAST",
|
||||
"CAP_NET_ADMIN",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_IPC_LOCK",
|
||||
"CAP_IPC_OWNER",
|
||||
"CAP_SYS_MODULE",
|
||||
"CAP_SYS_RAWIO",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_SYS_PTRACE",
|
||||
"CAP_SYS_PACCT",
|
||||
"CAP_SYS_ADMIN",
|
||||
"CAP_SYS_BOOT",
|
||||
"CAP_SYS_NICE",
|
||||
"CAP_SYS_RESOURCE",
|
||||
"CAP_SYS_TIME",
|
||||
"CAP_SYS_TTY_CONFIG",
|
||||
"CAP_MKNOD",
|
||||
"CAP_LEASE",
|
||||
"CAP_AUDIT_WRITE",
|
||||
"CAP_AUDIT_CONTROL",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_MAC_OVERRIDE",
|
||||
"CAP_DAC_READ_SEARCH",
|
||||
"CAP_MAC_ADMIN",
|
||||
"CAP_SYSLOG",
|
||||
"CAP_WAKE_ALARM",
|
||||
"CAP_BLOCK_SUSPEND"
|
||||
],
|
||||
"permitted": [
|
||||
"CAP_CHOWN",
|
||||
"CAP_FOWNER",
|
||||
"CAP_FSETID",
|
||||
"CAP_KILL",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_LINUX_IMMUTABLE",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_NET_BROADCAST",
|
||||
"CAP_NET_ADMIN",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_IPC_LOCK",
|
||||
"CAP_IPC_OWNER",
|
||||
"CAP_SYS_MODULE",
|
||||
"CAP_SYS_RAWIO",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_SYS_PTRACE",
|
||||
"CAP_SYS_PACCT",
|
||||
"CAP_SYS_ADMIN",
|
||||
"CAP_SYS_BOOT",
|
||||
"CAP_SYS_NICE",
|
||||
"CAP_SYS_RESOURCE",
|
||||
"CAP_SYS_TIME",
|
||||
"CAP_SYS_TTY_CONFIG",
|
||||
"CAP_MKNOD",
|
||||
"CAP_LEASE",
|
||||
"CAP_AUDIT_WRITE",
|
||||
"CAP_AUDIT_CONTROL",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_MAC_OVERRIDE",
|
||||
"CAP_DAC_READ_SEARCH",
|
||||
"CAP_MAC_ADMIN",
|
||||
"CAP_SYSLOG",
|
||||
"CAP_WAKE_ALARM",
|
||||
"CAP_BLOCK_SUSPEND"
|
||||
]
|
||||
},
|
||||
"selinuxLabel": "system_u:system_r:container_runtime_t:s0",
|
||||
"cwd": "/",
|
||||
"env": [
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/bin:/root/go/bin",
|
||||
"TERM=xterm",
|
||||
"LOG_LEVEL=$LOG_LEVEL",
|
||||
"NAME=$NAME"
|
||||
],
|
||||
"noNewPrivileges": false,
|
||||
"terminal": false,
|
||||
"user": {
|
||||
"gid": 0,
|
||||
"uid": 0
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"path": "rootfs",
|
||||
"readonly": true
|
||||
},
|
||||
"hooks": {},
|
||||
"linux": {
|
||||
"namespaces": [
|
||||
{
|
||||
"type": "mount"
|
||||
}
|
||||
],
|
||||
"resources": {
|
||||
"devices": [
|
||||
{
|
||||
"access": "rwm",
|
||||
"allow": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"rootfsPropagation": "private"
|
||||
},
|
||||
"mounts": [
|
||||
{
|
||||
"destination": "/tmp",
|
||||
"options": [
|
||||
"private",
|
||||
"bind",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/tmp",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/etc",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rprivate",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/etc",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/lib/modules",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rprivate",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/lib/modules",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/root",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rprivate",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/root",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/home",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rprivate",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/home",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/mnt",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rw",
|
||||
"rprivate",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/mnt",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"type": "bind",
|
||||
"source": "${RUN_DIRECTORY}",
|
||||
"destination": "/run",
|
||||
"options": [
|
||||
"rshared",
|
||||
"rbind",
|
||||
"rw",
|
||||
"mode=755"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "bind",
|
||||
"source": "${RUN_DIRECTORY}/systemd",
|
||||
"destination": "/run/systemd",
|
||||
"options": [
|
||||
"rslave",
|
||||
"bind",
|
||||
"rw",
|
||||
"mode=755"
|
||||
]
|
||||
},
|
||||
{
|
||||
"destination": "/var/log",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rslave",
|
||||
"rw"
|
||||
],
|
||||
"source": "/var/log",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/var/lib",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rprivate",
|
||||
"rw"
|
||||
],
|
||||
"source": "${STATE_DIRECTORY}",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/var/lib/containers/storage",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rshared",
|
||||
"rw"
|
||||
],
|
||||
"source": "${VAR_LIB_CONTAINERS_STORAGE}",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/var/lib/origin",
|
||||
"options": [
|
||||
"rshared",
|
||||
"bind",
|
||||
"rw"
|
||||
],
|
||||
"source": "${VAR_LIB_ORIGIN}",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/var/lib/kubelet",
|
||||
"options": [
|
||||
"rshared",
|
||||
"bind",
|
||||
"rw"
|
||||
],
|
||||
"source": "${VAR_LIB_KUBE}",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/opt/cni",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rprivate",
|
||||
"ro",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "${OPT_CNI}",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/dev",
|
||||
"options": [
|
||||
"rprivate",
|
||||
"rbind",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/dev",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/sys",
|
||||
"options": [
|
||||
"rprivate",
|
||||
"rbind",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/sys",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/proc",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/proc",
|
||||
"type": "proc"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"version": "1.0",
|
||||
"defaultValues": {
|
||||
"LOG_LEVEL" : "info",
|
||||
"OPT_CNI" : "/opt/cni",
|
||||
"VAR_LIB_CONTAINERS_STORAGE" : "/var/lib/containers/storage",
|
||||
"VAR_LIB_ORIGIN" : "/var/lib/origin",
|
||||
"VAR_LIB_KUBE" : "/var/lib/kubelet"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Ensure that new process maintain this SELinux label
|
||||
PID=$$
|
||||
LABEL=`tr -d '\000' < /proc/$PID/attr/current`
|
||||
printf %s $LABEL > /proc/self/attr/exec
|
||||
|
||||
test -e /etc/sysconfig/crio-storage && source /etc/sysconfig/crio-storage
|
||||
test -e /etc/sysconfig/crio-network && source /etc/sysconfig/crio-network
|
||||
|
||||
exec /usr/bin/crio --log-level=$LOG_LEVEL
|
|
@ -0,0 +1,20 @@
|
|||
[Unit]
|
||||
Description=crio daemon
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
ExecStartPre=/bin/sh $DESTDIR/rootfs/set_mounts.sh
|
||||
ExecStart=$EXEC_START
|
||||
ExecStop=$EXEC_STOP
|
||||
Restart=on-failure
|
||||
WorkingDirectory=$DESTDIR
|
||||
RuntimeDirectory=${NAME}
|
||||
TasksMax=infinity
|
||||
LimitNOFILE=1048576
|
||||
LimitNPROC=1048576
|
||||
LimitCORE=infinity
|
||||
TimeoutStartSec=0
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh
|
||||
|
||||
findmnt /var/lib/containers/storage > /dev/null || mount --rbind --make-shared /var/lib/containers/storage /var/lib/containers/storage
|
||||
findmnt /var/lib/origin > /dev/null || mount --bind --make-shared /var/lib/origin /var/lib/origin
|
||||
findmnt /var/lib/kubelet > /dev/null || mount --bind --make-shared /var/lib/kubelet /var/lib/kubelet
|
||||
mount --make-shared /run
|
||||
findmnt /run/systemd > /dev/null || mount --bind --make-rslave /run/systemd /run/systemd
|
|
@ -0,0 +1,5 @@
|
|||
d ${RUN_DIRECTORY}/crio - - - - -
|
||||
d /etc/crio - - - - -
|
||||
Z /etc/crio - - - - -
|
||||
d ${STATE_DIRECTORY}/origin - - - - -
|
||||
d ${STATE_DIRECTORY}/kubelet - - - - -
|
|
@ -0,0 +1,30 @@
|
|||
FROM registry.fedoraproject.org/fedora:27
|
||||
|
||||
ENV VERSION=0 RELEASE=1 ARCH=x86_64
|
||||
LABEL com.redhat.component="cri-o" \
|
||||
name="$FGC/cri-o" \
|
||||
version="$VERSION" \
|
||||
release="$RELEASE.$DISTTAG" \
|
||||
architecture="$ARCH" \
|
||||
usage="atomic install --system --system-package=no crio && systemctl start crio" \
|
||||
summary="The cri-o daemon as a system container." \
|
||||
maintainer="Yu Qi Zhang <jzehrarnyg@gmail.com>" \
|
||||
atomic.type="system"
|
||||
|
||||
COPY README.md /
|
||||
|
||||
RUN dnf install --enablerepo=updates-testing --setopt=tsflags=nodocs -y iptables cri-o socat iproute runc && \
|
||||
rpm -V iptables cri-o iproute runc && \
|
||||
dnf clean all && \
|
||||
mkdir -p /exports/hostfs/etc/crio /exports/hostfs/opt/cni/bin/ /exports/hostfs/var/lib/containers/storage/ && \
|
||||
cp /etc/crio/* /exports/hostfs/etc/crio && \
|
||||
if test -e /usr/libexec/cni; then cp -Lr /usr/libexec/cni/* /exports/hostfs/opt/cni/bin/; fi
|
||||
|
||||
RUN sed -i '/storage_option =/s/.*/&\n"overlay.override_kernel_check=1",/' /exports/hostfs/etc/crio/crio.conf
|
||||
|
||||
COPY manifest.json tmpfiles.template config.json.template service.template /exports/
|
||||
|
||||
COPY set_mounts.sh /
|
||||
COPY run.sh /usr/bin/
|
||||
|
||||
CMD ["/usr/bin/run.sh"]
|
|
@ -0,0 +1,53 @@
|
|||
# cri-o
|
||||
|
||||
This is the cri-o daemon as a system container.
|
||||
|
||||
## Building the image from source:
|
||||
|
||||
```
|
||||
# git clone https://github.com/projectatomic/atomic-system-containers
|
||||
# cd atomic-system-containers/cri-o
|
||||
# docker build -t crio .
|
||||
```
|
||||
|
||||
## Running the system container, with the atomic CLI:
|
||||
|
||||
Pull from registry into ostree:
|
||||
|
||||
```
|
||||
# atomic pull --storage ostree $REGISTRY/crio
|
||||
```
|
||||
|
||||
Or alternatively, pull from local docker:
|
||||
|
||||
```
|
||||
# atomic pull --storage ostree docker:crio:latest
|
||||
```
|
||||
|
||||
Install the container:
|
||||
|
||||
Currently we recommend using --system-package=no to avoid having rpmbuild create an rpm file
|
||||
during installation. This flag will tell the atomic CLI to fall back to copying files to the
|
||||
host instead.
|
||||
|
||||
```
|
||||
# atomic install --system --system-package=no --name=crio ($REGISTRY)/crio
|
||||
```
|
||||
|
||||
Start as a systemd service:
|
||||
|
||||
```
|
||||
# systemctl start crio
|
||||
```
|
||||
|
||||
Stopping the service
|
||||
|
||||
```
|
||||
# systemctl stop crio
|
||||
```
|
||||
|
||||
Removing the container
|
||||
|
||||
```
|
||||
# atomic uninstall crio
|
||||
```
|
|
@ -0,0 +1,432 @@
|
|||
{
|
||||
"ociVersion": "1.0.0",
|
||||
"platform": {
|
||||
"arch": "amd64",
|
||||
"os": "linux"
|
||||
},
|
||||
"process": {
|
||||
"args": [
|
||||
"/usr/bin/run.sh"
|
||||
],
|
||||
"selinuxLabel": "system_u:system_r:container_runtime_t:s0",
|
||||
"capabilities": {
|
||||
"ambient": [
|
||||
"CAP_CHOWN",
|
||||
"CAP_FOWNER",
|
||||
"CAP_FSETID",
|
||||
"CAP_KILL",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_LINUX_IMMUTABLE",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_NET_BROADCAST",
|
||||
"CAP_NET_ADMIN",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_IPC_LOCK",
|
||||
"CAP_IPC_OWNER",
|
||||
"CAP_SYS_MODULE",
|
||||
"CAP_SYS_RAWIO",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_SYS_PTRACE",
|
||||
"CAP_SYS_PACCT",
|
||||
"CAP_SYS_ADMIN",
|
||||
"CAP_SYS_BOOT",
|
||||
"CAP_SYS_NICE",
|
||||
"CAP_SYS_RESOURCE",
|
||||
"CAP_SYS_TIME",
|
||||
"CAP_SYS_TTY_CONFIG",
|
||||
"CAP_MKNOD",
|
||||
"CAP_LEASE",
|
||||
"CAP_AUDIT_WRITE",
|
||||
"CAP_AUDIT_CONTROL",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_MAC_OVERRIDE",
|
||||
"CAP_DAC_READ_SEARCH",
|
||||
"CAP_MAC_ADMIN",
|
||||
"CAP_SYSLOG",
|
||||
"CAP_WAKE_ALARM",
|
||||
"CAP_BLOCK_SUSPEND",
|
||||
"CAP_AUDIT_READ"
|
||||
],
|
||||
"bounding": [
|
||||
"CAP_CHOWN",
|
||||
"CAP_FOWNER",
|
||||
"CAP_FSETID",
|
||||
"CAP_KILL",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_LINUX_IMMUTABLE",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_NET_BROADCAST",
|
||||
"CAP_NET_ADMIN",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_IPC_LOCK",
|
||||
"CAP_IPC_OWNER",
|
||||
"CAP_SYS_MODULE",
|
||||
"CAP_SYS_RAWIO",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_SYS_PTRACE",
|
||||
"CAP_SYS_PACCT",
|
||||
"CAP_SYS_ADMIN",
|
||||
"CAP_SYS_BOOT",
|
||||
"CAP_SYS_NICE",
|
||||
"CAP_SYS_RESOURCE",
|
||||
"CAP_SYS_TIME",
|
||||
"CAP_SYS_TTY_CONFIG",
|
||||
"CAP_MKNOD",
|
||||
"CAP_LEASE",
|
||||
"CAP_AUDIT_WRITE",
|
||||
"CAP_AUDIT_CONTROL",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_MAC_OVERRIDE",
|
||||
"CAP_DAC_READ_SEARCH",
|
||||
"CAP_MAC_ADMIN",
|
||||
"CAP_SYSLOG",
|
||||
"CAP_WAKE_ALARM",
|
||||
"CAP_BLOCK_SUSPEND",
|
||||
"CAP_AUDIT_READ"
|
||||
],
|
||||
"effective": [
|
||||
"CAP_CHOWN",
|
||||
"CAP_FOWNER",
|
||||
"CAP_FSETID",
|
||||
"CAP_KILL",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_LINUX_IMMUTABLE",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_NET_BROADCAST",
|
||||
"CAP_NET_ADMIN",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_IPC_LOCK",
|
||||
"CAP_IPC_OWNER",
|
||||
"CAP_SYS_MODULE",
|
||||
"CAP_SYS_RAWIO",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_SYS_PTRACE",
|
||||
"CAP_SYS_PACCT",
|
||||
"CAP_SYS_ADMIN",
|
||||
"CAP_SYS_BOOT",
|
||||
"CAP_SYS_NICE",
|
||||
"CAP_SYS_RESOURCE",
|
||||
"CAP_SYS_TIME",
|
||||
"CAP_SYS_TTY_CONFIG",
|
||||
"CAP_MKNOD",
|
||||
"CAP_LEASE",
|
||||
"CAP_AUDIT_WRITE",
|
||||
"CAP_AUDIT_CONTROL",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_MAC_OVERRIDE",
|
||||
"CAP_DAC_READ_SEARCH",
|
||||
"CAP_MAC_ADMIN",
|
||||
"CAP_SYSLOG",
|
||||
"CAP_WAKE_ALARM",
|
||||
"CAP_BLOCK_SUSPEND",
|
||||
"CAP_AUDIT_READ"
|
||||
],
|
||||
"inheritable": [
|
||||
"CAP_CHOWN",
|
||||
"CAP_FOWNER",
|
||||
"CAP_FSETID",
|
||||
"CAP_KILL",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_LINUX_IMMUTABLE",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_NET_BROADCAST",
|
||||
"CAP_NET_ADMIN",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_IPC_LOCK",
|
||||
"CAP_IPC_OWNER",
|
||||
"CAP_SYS_MODULE",
|
||||
"CAP_SYS_RAWIO",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_SYS_PTRACE",
|
||||
"CAP_SYS_PACCT",
|
||||
"CAP_SYS_ADMIN",
|
||||
"CAP_SYS_BOOT",
|
||||
"CAP_SYS_NICE",
|
||||
"CAP_SYS_RESOURCE",
|
||||
"CAP_SYS_TIME",
|
||||
"CAP_SYS_TTY_CONFIG",
|
||||
"CAP_MKNOD",
|
||||
"CAP_LEASE",
|
||||
"CAP_AUDIT_WRITE",
|
||||
"CAP_AUDIT_CONTROL",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_MAC_OVERRIDE",
|
||||
"CAP_DAC_READ_SEARCH",
|
||||
"CAP_MAC_ADMIN",
|
||||
"CAP_SYSLOG",
|
||||
"CAP_WAKE_ALARM",
|
||||
"CAP_BLOCK_SUSPEND",
|
||||
"CAP_AUDIT_READ"
|
||||
],
|
||||
"permitted": [
|
||||
"CAP_CHOWN",
|
||||
"CAP_FOWNER",
|
||||
"CAP_FSETID",
|
||||
"CAP_KILL",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_LINUX_IMMUTABLE",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_NET_BROADCAST",
|
||||
"CAP_NET_ADMIN",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_IPC_LOCK",
|
||||
"CAP_IPC_OWNER",
|
||||
"CAP_SYS_MODULE",
|
||||
"CAP_SYS_RAWIO",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_SYS_PTRACE",
|
||||
"CAP_SYS_PACCT",
|
||||
"CAP_SYS_ADMIN",
|
||||
"CAP_SYS_BOOT",
|
||||
"CAP_SYS_NICE",
|
||||
"CAP_SYS_RESOURCE",
|
||||
"CAP_SYS_TIME",
|
||||
"CAP_SYS_TTY_CONFIG",
|
||||
"CAP_MKNOD",
|
||||
"CAP_LEASE",
|
||||
"CAP_AUDIT_WRITE",
|
||||
"CAP_AUDIT_CONTROL",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_MAC_OVERRIDE",
|
||||
"CAP_DAC_READ_SEARCH",
|
||||
"CAP_MAC_ADMIN",
|
||||
"CAP_SYSLOG",
|
||||
"CAP_WAKE_ALARM",
|
||||
"CAP_BLOCK_SUSPEND",
|
||||
"CAP_AUDIT_READ"
|
||||
]
|
||||
},
|
||||
"cwd": "/",
|
||||
"env": [
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/bin:/root/go/bin",
|
||||
"TERM=xterm",
|
||||
"LOG_LEVEL=$LOG_LEVEL",
|
||||
"NAME=$NAME"
|
||||
],
|
||||
"noNewPrivileges": false,
|
||||
"terminal": false,
|
||||
"user": {
|
||||
"gid": 0,
|
||||
"uid": 0
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"path": "rootfs",
|
||||
"readonly": true
|
||||
},
|
||||
"hooks": {},
|
||||
"linux": {
|
||||
"namespaces": [
|
||||
{
|
||||
"type": "mount"
|
||||
}
|
||||
],
|
||||
"resources": {
|
||||
"devices": [
|
||||
{
|
||||
"access": "rwm",
|
||||
"allow": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"rootfsPropagation": "private"
|
||||
},
|
||||
"mounts": [
|
||||
{
|
||||
"destination": "/tmp",
|
||||
"options": [
|
||||
"private",
|
||||
"bind",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/tmp",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/etc",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rprivate",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/etc",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/lib/modules",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rprivate",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/lib/modules",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/root",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rprivate",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/root",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/home",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rprivate",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/home",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/mnt",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rw",
|
||||
"rprivate",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/mnt",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"type": "bind",
|
||||
"source": "${RUN_DIRECTORY}",
|
||||
"destination": "/run",
|
||||
"options": [
|
||||
"rshared",
|
||||
"rbind",
|
||||
"rw",
|
||||
"mode=755"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "bind",
|
||||
"source": "${RUN_DIRECTORY}/systemd",
|
||||
"destination": "/run/systemd",
|
||||
"options": [
|
||||
"rslave",
|
||||
"bind",
|
||||
"rw",
|
||||
"mode=755"
|
||||
]
|
||||
},
|
||||
{
|
||||
"destination": "/var/log",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rslave",
|
||||
"rw"
|
||||
],
|
||||
"source": "/var/log",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/var/lib",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rprivate",
|
||||
"rw"
|
||||
],
|
||||
"source": "${STATE_DIRECTORY}",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/var/lib/containers/storage",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rshared",
|
||||
"rw"
|
||||
],
|
||||
"source": "${VAR_LIB_CONTAINERS_STORAGE}",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/var/lib/origin",
|
||||
"options": [
|
||||
"rshared",
|
||||
"bind",
|
||||
"rw"
|
||||
],
|
||||
"source": "${VAR_LIB_ORIGIN}",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/var/lib/kubelet",
|
||||
"options": [
|
||||
"rshared",
|
||||
"bind",
|
||||
"rw"
|
||||
],
|
||||
"source": "${VAR_LIB_KUBE}",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/opt/cni",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rprivate",
|
||||
"ro",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "${OPT_CNI}",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/dev",
|
||||
"options": [
|
||||
"rprivate",
|
||||
"rbind",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/dev",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/sys",
|
||||
"options": [
|
||||
"rprivate",
|
||||
"rbind",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/sys",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/proc",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/proc",
|
||||
"type": "proc"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"version": "1.0",
|
||||
"defaultValues": {
|
||||
"LOG_LEVEL" : "info",
|
||||
"OPT_CNI" : "/opt/cni",
|
||||
"VAR_LIB_CONTAINERS_STORAGE" : "/var/lib/containers/storage",
|
||||
"VAR_LIB_ORIGIN" : "/var/lib/origin",
|
||||
"VAR_LIB_KUBE" : "/var/lib/kubelet"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Ensure that new process maintain this SELinux label
|
||||
PID=$$
|
||||
LABEL=`tr -d '\000' < /proc/$PID/attr/current`
|
||||
printf %s $LABEL > /proc/self/attr/exec
|
||||
|
||||
test -e /etc/sysconfig/crio-storage && source /etc/sysconfig/crio-storage
|
||||
test -e /etc/sysconfig/crio-network && source /etc/sysconfig/crio-network
|
||||
|
||||
exec /usr/bin/crio --log-level=$LOG_LEVEL
|
|
@ -0,0 +1,20 @@
|
|||
[Unit]
|
||||
Description=crio daemon
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
ExecStartPre=/bin/sh $DESTDIR/rootfs/set_mounts.sh
|
||||
ExecStart=$EXEC_START
|
||||
ExecStop=$EXEC_STOP
|
||||
Restart=on-failure
|
||||
WorkingDirectory=$DESTDIR
|
||||
RuntimeDirectory=${NAME}
|
||||
TasksMax=infinity
|
||||
LimitNOFILE=1048576
|
||||
LimitNPROC=1048576
|
||||
LimitCORE=infinity
|
||||
TimeoutStartSec=0
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh
|
||||
|
||||
findmnt /var/lib/containers/storage > /dev/null || mount --rbind --make-shared /var/lib/containers/storage /var/lib/containers/storage
|
||||
findmnt /var/lib/origin > /dev/null || mount --bind --make-shared /var/lib/origin /var/lib/origin
|
||||
findmnt /var/lib/kubelet > /dev/null || mount --bind --make-shared /var/lib/kubelet /var/lib/kubelet
|
||||
mount --make-shared /run
|
||||
findmnt /run/systemd > /dev/null || mount --bind --make-rslave /run/systemd /run/systemd
|
|
@ -0,0 +1,5 @@
|
|||
d ${RUN_DIRECTORY}/crio - - - - -
|
||||
d /etc/crio - - - - -
|
||||
Z /etc/crio - - - - -
|
||||
d ${STATE_DIRECTORY}/origin - - - - -
|
||||
d ${STATE_DIRECTORY}/kubelet - - - - -
|
|
@ -0,0 +1,41 @@
|
|||
#oit## This file is managed by the OpenShift Image Tool
|
||||
#oit## by the OpenShift Continuous Delivery team.
|
||||
#oit##
|
||||
#oit## Any yum repos listed in this file will effectively be ignored during CD builds.
|
||||
#oit## Yum repos must be enabled in the oit configuration files.
|
||||
#oit## Some aspects of this file may be managed programmatically. For example, the image name, labels (version,
|
||||
#oit## release, and other), and the base FROM. Changes made directly in distgit may be lost during the next
|
||||
#oit## reconciliation.
|
||||
#oit##
|
||||
FROM rhel7:7-released
|
||||
|
||||
RUN \
|
||||
yum install --setopt=tsflags=nodocs -y socat iptables cri-o iproute runc skopeo-containers container-selinux && \
|
||||
rpm -V socat iptables cri-o iproute runc skopeo-containers container-selinux && \
|
||||
yum clean all && \
|
||||
mkdir -p /exports/hostfs/etc/crio /exports/hostfs/opt/cni/bin/ /exports/hostfs/var/lib/containers/storage/ && \
|
||||
cp /etc/crio/* /exports/hostfs/etc/crio && \
|
||||
if test -e /usr/libexec/cni; then cp -Lr /usr/libexec/cni/* /exports/hostfs/opt/cni/bin/; fi
|
||||
|
||||
COPY manifest.json tmpfiles.template config.json.template service.template /exports/
|
||||
|
||||
COPY set_mounts.sh /
|
||||
COPY run.sh /usr/bin/
|
||||
|
||||
CMD ["/usr/bin/run.sh"]
|
||||
|
||||
LABEL \
|
||||
com.redhat.component="cri-o-docker" \
|
||||
io.k8s.description="CRI-O is an implementation of the Kubernetes CRI. It is a lightweight, OCI-compliant runtime that is native to kubernetes. CRI-O supports OCI container images and can pull from any container registry." \
|
||||
maintainer="Jhon Honce <jhonce@redhat.com>" \
|
||||
name="openshift3/cri-o" \
|
||||
License="GPLv2+" \
|
||||
io.k8s.display-name="CRI-O" \
|
||||
summary="OCI-based implementation of Kubernetes Container Runtime Interface" \
|
||||
release="0.13.0.0" \
|
||||
version="v3.8.0" \
|
||||
architecture="x86_64" \
|
||||
usage="atomic install --system --system-package=no crio && systemctl start crio" \
|
||||
vendor="Red Hat" \
|
||||
io.openshift.tags="cri-o system rhel7" \
|
||||
atomic.type="system"
|
|
@ -0,0 +1,422 @@
|
|||
{
|
||||
"ociVersion": "1.0.0",
|
||||
"platform": {
|
||||
"arch": "amd64",
|
||||
"os": "linux"
|
||||
},
|
||||
"process": {
|
||||
"args": [
|
||||
"/usr/bin/run.sh"
|
||||
],
|
||||
"capabilities": {
|
||||
"ambient": [
|
||||
"CAP_CHOWN",
|
||||
"CAP_FOWNER",
|
||||
"CAP_FSETID",
|
||||
"CAP_KILL",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_LINUX_IMMUTABLE",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_NET_BROADCAST",
|
||||
"CAP_NET_ADMIN",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_IPC_LOCK",
|
||||
"CAP_IPC_OWNER",
|
||||
"CAP_SYS_MODULE",
|
||||
"CAP_SYS_RAWIO",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_SYS_PTRACE",
|
||||
"CAP_SYS_PACCT",
|
||||
"CAP_SYS_ADMIN",
|
||||
"CAP_SYS_BOOT",
|
||||
"CAP_SYS_NICE",
|
||||
"CAP_SYS_RESOURCE",
|
||||
"CAP_SYS_TIME",
|
||||
"CAP_SYS_TTY_CONFIG",
|
||||
"CAP_MKNOD",
|
||||
"CAP_LEASE",
|
||||
"CAP_AUDIT_WRITE",
|
||||
"CAP_AUDIT_CONTROL",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_MAC_OVERRIDE",
|
||||
"CAP_DAC_READ_SEARCH",
|
||||
"CAP_MAC_ADMIN",
|
||||
"CAP_SYSLOG",
|
||||
"CAP_WAKE_ALARM",
|
||||
"CAP_BLOCK_SUSPEND"
|
||||
],
|
||||
"bounding": [
|
||||
"CAP_CHOWN",
|
||||
"CAP_FOWNER",
|
||||
"CAP_FSETID",
|
||||
"CAP_KILL",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_LINUX_IMMUTABLE",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_NET_BROADCAST",
|
||||
"CAP_NET_ADMIN",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_IPC_LOCK",
|
||||
"CAP_IPC_OWNER",
|
||||
"CAP_SYS_MODULE",
|
||||
"CAP_SYS_RAWIO",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_SYS_PTRACE",
|
||||
"CAP_SYS_PACCT",
|
||||
"CAP_SYS_ADMIN",
|
||||
"CAP_SYS_BOOT",
|
||||
"CAP_SYS_NICE",
|
||||
"CAP_SYS_RESOURCE",
|
||||
"CAP_SYS_TIME",
|
||||
"CAP_SYS_TTY_CONFIG",
|
||||
"CAP_MKNOD",
|
||||
"CAP_LEASE",
|
||||
"CAP_AUDIT_WRITE",
|
||||
"CAP_AUDIT_CONTROL",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_MAC_OVERRIDE",
|
||||
"CAP_DAC_READ_SEARCH",
|
||||
"CAP_MAC_ADMIN",
|
||||
"CAP_SYSLOG",
|
||||
"CAP_WAKE_ALARM",
|
||||
"CAP_BLOCK_SUSPEND"
|
||||
],
|
||||
"effective": [
|
||||
"CAP_CHOWN",
|
||||
"CAP_FOWNER",
|
||||
"CAP_FSETID",
|
||||
"CAP_KILL",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_LINUX_IMMUTABLE",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_NET_BROADCAST",
|
||||
"CAP_NET_ADMIN",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_IPC_LOCK",
|
||||
"CAP_IPC_OWNER",
|
||||
"CAP_SYS_MODULE",
|
||||
"CAP_SYS_RAWIO",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_SYS_PTRACE",
|
||||
"CAP_SYS_PACCT",
|
||||
"CAP_SYS_ADMIN",
|
||||
"CAP_SYS_BOOT",
|
||||
"CAP_SYS_NICE",
|
||||
"CAP_SYS_RESOURCE",
|
||||
"CAP_SYS_TIME",
|
||||
"CAP_SYS_TTY_CONFIG",
|
||||
"CAP_MKNOD",
|
||||
"CAP_LEASE",
|
||||
"CAP_AUDIT_WRITE",
|
||||
"CAP_AUDIT_CONTROL",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_MAC_OVERRIDE",
|
||||
"CAP_DAC_READ_SEARCH",
|
||||
"CAP_MAC_ADMIN",
|
||||
"CAP_SYSLOG",
|
||||
"CAP_WAKE_ALARM",
|
||||
"CAP_BLOCK_SUSPEND"
|
||||
],
|
||||
"inheritable": [
|
||||
"CAP_CHOWN",
|
||||
"CAP_FOWNER",
|
||||
"CAP_FSETID",
|
||||
"CAP_KILL",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_LINUX_IMMUTABLE",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_NET_BROADCAST",
|
||||
"CAP_NET_ADMIN",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_IPC_LOCK",
|
||||
"CAP_IPC_OWNER",
|
||||
"CAP_SYS_MODULE",
|
||||
"CAP_SYS_RAWIO",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_SYS_PTRACE",
|
||||
"CAP_SYS_PACCT",
|
||||
"CAP_SYS_ADMIN",
|
||||
"CAP_SYS_BOOT",
|
||||
"CAP_SYS_NICE",
|
||||
"CAP_SYS_RESOURCE",
|
||||
"CAP_SYS_TIME",
|
||||
"CAP_SYS_TTY_CONFIG",
|
||||
"CAP_MKNOD",
|
||||
"CAP_LEASE",
|
||||
"CAP_AUDIT_WRITE",
|
||||
"CAP_AUDIT_CONTROL",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_MAC_OVERRIDE",
|
||||
"CAP_DAC_READ_SEARCH",
|
||||
"CAP_MAC_ADMIN",
|
||||
"CAP_SYSLOG",
|
||||
"CAP_WAKE_ALARM",
|
||||
"CAP_BLOCK_SUSPEND"
|
||||
],
|
||||
"permitted": [
|
||||
"CAP_CHOWN",
|
||||
"CAP_FOWNER",
|
||||
"CAP_FSETID",
|
||||
"CAP_KILL",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_LINUX_IMMUTABLE",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_NET_BROADCAST",
|
||||
"CAP_NET_ADMIN",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_IPC_LOCK",
|
||||
"CAP_IPC_OWNER",
|
||||
"CAP_SYS_MODULE",
|
||||
"CAP_SYS_RAWIO",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_SYS_PTRACE",
|
||||
"CAP_SYS_PACCT",
|
||||
"CAP_SYS_ADMIN",
|
||||
"CAP_SYS_BOOT",
|
||||
"CAP_SYS_NICE",
|
||||
"CAP_SYS_RESOURCE",
|
||||
"CAP_SYS_TIME",
|
||||
"CAP_SYS_TTY_CONFIG",
|
||||
"CAP_MKNOD",
|
||||
"CAP_LEASE",
|
||||
"CAP_AUDIT_WRITE",
|
||||
"CAP_AUDIT_CONTROL",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_MAC_OVERRIDE",
|
||||
"CAP_DAC_READ_SEARCH",
|
||||
"CAP_MAC_ADMIN",
|
||||
"CAP_SYSLOG",
|
||||
"CAP_WAKE_ALARM",
|
||||
"CAP_BLOCK_SUSPEND"
|
||||
]
|
||||
},
|
||||
"selinuxLabel": "system_u:system_r:container_runtime_t:s0",
|
||||
"cwd": "/",
|
||||
"env": [
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/bin:/root/go/bin",
|
||||
"TERM=xterm",
|
||||
"LOG_LEVEL=$LOG_LEVEL",
|
||||
"NAME=$NAME"
|
||||
],
|
||||
"noNewPrivileges": false,
|
||||
"terminal": false,
|
||||
"user": {
|
||||
"gid": 0,
|
||||
"uid": 0
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"path": "rootfs",
|
||||
"readonly": true
|
||||
},
|
||||
"hooks": {},
|
||||
"linux": {
|
||||
"namespaces": [{
|
||||
"type": "mount"
|
||||
}],
|
||||
"resources": {
|
||||
"devices": [{
|
||||
"access": "rwm",
|
||||
"allow": true
|
||||
}]
|
||||
},
|
||||
"rootfsPropagation": "private"
|
||||
},
|
||||
"mounts": [{
|
||||
"destination": "/tmp",
|
||||
"options": [
|
||||
"private",
|
||||
"bind",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/tmp",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/etc",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rprivate",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/etc",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/lib/modules",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rprivate",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/lib/modules",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/root",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rprivate",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/root",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/home",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rprivate",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/home",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/mnt",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rw",
|
||||
"rprivate",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/mnt",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"type": "bind",
|
||||
"source": "${RUN_DIRECTORY}",
|
||||
"destination": "/run",
|
||||
"options": [
|
||||
"rshared",
|
||||
"rbind",
|
||||
"rw",
|
||||
"mode=755"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "bind",
|
||||
"source": "${RUN_DIRECTORY}/systemd",
|
||||
"destination": "/run/systemd",
|
||||
"options": [
|
||||
"rslave",
|
||||
"bind",
|
||||
"rw",
|
||||
"mode=755"
|
||||
]
|
||||
},
|
||||
{
|
||||
"destination": "/var/log",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rslave",
|
||||
"rw"
|
||||
],
|
||||
"source": "/var/log",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/var/lib",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rprivate",
|
||||
"rw"
|
||||
],
|
||||
"source": "${STATE_DIRECTORY}",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/var/lib/containers/storage",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rshared",
|
||||
"rw"
|
||||
],
|
||||
"source": "${VAR_LIB_CONTAINERS_STORAGE}",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/var/lib/origin",
|
||||
"options": [
|
||||
"rshared",
|
||||
"bind",
|
||||
"rw"
|
||||
],
|
||||
"source": "${VAR_LIB_ORIGIN}",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/var/lib/kubelet",
|
||||
"options": [
|
||||
"rshared",
|
||||
"bind",
|
||||
"rw"
|
||||
],
|
||||
"source": "${VAR_LIB_KUBE}",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/opt/cni",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rprivate",
|
||||
"ro",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "${OPT_CNI}",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/dev",
|
||||
"options": [
|
||||
"rprivate",
|
||||
"rbind",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/dev",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/sys",
|
||||
"options": [
|
||||
"rprivate",
|
||||
"rbind",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/sys",
|
||||
"type": "bind"
|
||||
},
|
||||
{
|
||||
"destination": "/proc",
|
||||
"options": [
|
||||
"rbind",
|
||||
"rw",
|
||||
"mode=755"
|
||||
],
|
||||
"source": "/proc",
|
||||
"type": "proc"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
% CRI-O (1) Container Image Pages
|
||||
% Jhon Honce
|
||||
% September 7, 2017
|
||||
|
||||
# NAME
|
||||
cri-o - OCI-based implementation of Kubernetes Container Runtime Interface
|
||||
|
||||
# DESCRIPTION
|
||||
CRI-O is an implementation of the Kubernetes CRI. It is a lightweight, OCI-compliant runtime that is native to kubernetes. CRI-O supports OCI container images and can pull from any container registry.
|
||||
|
||||
You can find more information on the CRI-O project at <https://github.com/kubernetes-incubator/cri-o/>
|
||||
|
||||
# USAGE
|
||||
Pull from local docker and install system container:
|
||||
|
||||
```
|
||||
# atomic pull --storage ostree docker:openshift3/cri-o:latest
|
||||
# atomic install --system --system-package=no --name cri-o openshift3/cri-o
|
||||
```
|
||||
|
||||
Start and enable as a systemd service:
|
||||
```
|
||||
# systemctl enable --now cri-o
|
||||
```
|
||||
|
||||
Stopping the service
|
||||
```
|
||||
# systemctl stop cri-o
|
||||
```
|
||||
|
||||
Removing the container
|
||||
```
|
||||
# atomic uninstall cri-o
|
||||
```
|
||||
|
||||
# SEE ALSO
|
||||
man systemd(1)
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"version": "1.0",
|
||||
"defaultValues": {
|
||||
"LOG_LEVEL": "info",
|
||||
"OPT_CNI": "/opt/cni",
|
||||
"VAR_LIB_CONTAINERS_STORAGE": "/var/lib/containers/storage",
|
||||
"VAR_LIB_ORIGIN": "/var/lib/origin",
|
||||
"VAR_LIB_KUBE": "/var/lib/kubelet"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Ensure that new process maintain this SELinux label
|
||||
PID=$$
|
||||
LABEL=`tr -d '\000' < /proc/$PID/attr/current`
|
||||
printf %s $LABEL > /proc/self/attr/exec
|
||||
|
||||
test -e /etc/sysconfig/crio-storage && source /etc/sysconfig/crio-storage
|
||||
test -e /etc/sysconfig/crio-network && source /etc/sysconfig/crio-network
|
||||
|
||||
exec /usr/bin/crio --log-level=$LOG_LEVEL
|
|
@ -0,0 +1,20 @@
|
|||
[Unit]
|
||||
Description=crio daemon
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
ExecStartPre=/bin/sh $DESTDIR/rootfs/set_mounts.sh
|
||||
ExecStart=$EXEC_START
|
||||
ExecStop=$EXEC_STOP
|
||||
Restart=on-failure
|
||||
WorkingDirectory=$DESTDIR
|
||||
RuntimeDirectory=${NAME}
|
||||
TasksMax=infinity
|
||||
LimitNOFILE=1048576
|
||||
LimitNPROC=1048576
|
||||
LimitCORE=infinity
|
||||
TimeoutStartSec=0
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh
|
||||
|
||||
findmnt /var/lib/containers/storage > /dev/null || mount --rbind --make-shared /var/lib/containers/storage /var/lib/containers/storage
|
||||
findmnt /var/lib/origin > /dev/null || mount --bind --make-shared /var/lib/origin /var/lib/origin
|
||||
findmnt /var/lib/kubelet > /dev/null || mount --bind --make-shared /var/lib/kubelet /var/lib/kubelet
|
||||
mount --make-shared /run
|
||||
findmnt /run/systemd > /dev/null || mount --bind --make-rslave /run/systemd /run/systemd
|
|
@ -0,0 +1,5 @@
|
|||
d ${RUN_DIRECTORY}/crio - - - - -
|
||||
d /etc/crio - - - - -
|
||||
Z /etc/crio - - - - -
|
||||
d ${STATE_DIRECTORY}/origin - - - - -
|
||||
d ${STATE_DIRECTORY}/kubelet - - - - -
|
|
@ -4,13 +4,23 @@
|
|||
git:
|
||||
repo: "https://github.com/kubernetes-incubator/cri-tools.git"
|
||||
dest: "{{ ansible_env.GOPATH }}/src/github.com/kubernetes-incubator/cri-tools"
|
||||
version: "b42fc3f364dd48f649d55926c34492beeb9b2e99"
|
||||
version: "{{ cri_tools_git_version }}"
|
||||
force: "{{ force_clone | default(False) | bool}}"
|
||||
|
||||
- name: install crictl
|
||||
command: "/usr/bin/go install github.com/kubernetes-incubator/cri-tools/cmd/crictl"
|
||||
|
||||
- name: install critest
|
||||
command: "/usr/bin/go install github.com/kubernetes-incubator/cri-tools/cmd/critest"
|
||||
|
||||
- name: link crictl
|
||||
file:
|
||||
src: "{{ ansible_env.GOPATH }}/bin/crictl"
|
||||
dest: /usr/bin/crictl
|
||||
state: link
|
||||
|
||||
- name: link critest
|
||||
file:
|
||||
src: "{{ ansible_env.GOPATH }}/bin/critest"
|
||||
dest: /usr/bin/critest
|
||||
state: link
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
|
||||
- name: clone kubernetes source repo
|
||||
git:
|
||||
repo: "https://github.com/runcom/kubernetes.git"
|
||||
repo: "https://github.com/{{ k8s_github_fork }}/kubernetes.git"
|
||||
dest: "{{ ansible_env.GOPATH }}/src/k8s.io/kubernetes"
|
||||
version: "cri-o-patched-1.8"
|
||||
# based on kube v1.9.0-alpha.2, update as needed
|
||||
version: "{{ k8s_git_version }}"
|
||||
force: "{{ force_clone | default(False) | bool}}"
|
||||
|
||||
- name: install etcd
|
||||
|
@ -39,13 +40,15 @@
|
|||
export PATH=/usr/local/go/bin:/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/root/bin:{{ ansible_env.GOPATH }}/bin:{{ ansible_env.GOPATH }}/src/k8s.io/kubernetes/third_party/etcd:{{ ansible_env.GOPATH }}/src/k8s.io/kubernetes/_output/local/bin/linux/amd64/
|
||||
export CONTAINER_RUNTIME=remote
|
||||
export CGROUP_DRIVER=systemd
|
||||
export CONTAINER_RUNTIME_ENDPOINT='/var/run/crio.sock --runtime-request-timeout=5m'
|
||||
export CONTAINER_RUNTIME_ENDPOINT='{{ crio_socket }} --runtime-request-timeout=5m'
|
||||
export ALLOW_SECURITY_CONTEXT=","
|
||||
export ALLOW_PRIVILEGED=1
|
||||
export DNS_SERVER_IP={{ ansible_eth0.ipv4.address }}
|
||||
export API_HOST={{ ansible_eth0.ipv4.address }}
|
||||
export API_HOST_IP={{ ansible_eth0.ipv4.address }}
|
||||
export DNS_SERVER_IP={{ ansible_default_ipv4.address }}
|
||||
export API_HOST={{ ansible_default_ipv4.address }}
|
||||
export API_HOST_IP={{ ansible_default_ipv4.address }}
|
||||
export KUBE_ENABLE_CLUSTER_DNS=true
|
||||
export ENABLE_HOSTPATH_PROVISIONER=true
|
||||
export KUBE_ENABLE_CLUSTER_DASHBOARD=true
|
||||
./hack/local-up-cluster.sh
|
||||
mode: "u=rwx,g=rwx,o=x"
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
git:
|
||||
repo: "https://github.com/opencontainers/runc.git"
|
||||
dest: "{{ ansible_env.GOPATH }}/src/github.com/opencontainers/runc"
|
||||
version: "84a082bfef6f932de921437815355186db37aeb1"
|
||||
version: "c6e4a1ebeb1a72b529c6f1b6ee2b1ae5b868b14f"
|
||||
|
||||
- name: build runc
|
||||
make:
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
|
||||
- name: enable and start CRI-O
|
||||
systemd:
|
||||
name: crio
|
||||
state: started
|
||||
enabled: yes
|
||||
daemon_reload: yes
|
||||
|
||||
- name: Flush the iptables
|
||||
command: iptables -F
|
||||
|
||||
- name: Enable localnet routing
|
||||
command: sysctl -w net.ipv4.conf.all.route_localnet=1
|
||||
|
||||
- name: Add masquerade for localhost
|
||||
command: iptables -t nat -I POSTROUTING -s 127.0.0.1 ! -d 127.0.0.1 -j MASQUERADE
|
||||
|
||||
- name: run critest validation
|
||||
shell: "critest -c --runtime-endpoint /var/run/crio/crio.sock --image-endpoint /var/run/crio/crio.sock v"
|
||||
args:
|
||||
chdir: "{{ ansible_env.GOPATH }}/src/github.com/kubernetes-incubator/cri-o"
|
||||
async: 5400
|
||||
poll: 30
|
||||
when: ansible_distribution not in ['RedHat', 'CentOS']
|
||||
|
||||
# XXX: RHEL has an additional test which fails because of selinux but disabling
|
||||
# it doesn't solve the issue.
|
||||
# TODO(runcom): enable skipped tests once we fix them (selinux)
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=1414236
|
||||
# https://access.redhat.com/solutions/2897781
|
||||
- name: run critest validation
|
||||
shell: "critest -c --runtime-endpoint /var/run/crio/crio.sock --image-endpoint /var/run/crio/crio.sock -s 'should not allow privilege escalation when true' v"
|
||||
args:
|
||||
chdir: "{{ ansible_env.GOPATH }}/src/github.com/kubernetes-incubator/cri-o"
|
||||
async: 5400
|
||||
poll: 30
|
||||
when: ansible_distribution in ['RedHat', 'CentOS']
|
||||
|
||||
- name: run critest benchmarks
|
||||
shell: "critest -c --runtime-endpoint /var/run/crio/crio.sock --image-endpoint /var/run/crio/crio.sock b"
|
||||
args:
|
||||
chdir: "{{ ansible_env.GOPATH }}/src/github.com/kubernetes-incubator/cri-o"
|
||||
async: 5400
|
||||
poll: 30
|
|
@ -10,7 +10,7 @@
|
|||
- name: update the server address for the custom cluster
|
||||
lineinfile:
|
||||
dest: /usr/local/bin/createcluster.sh
|
||||
line: "export {{ item }}={{ ansible_eth0.ipv4.address }}"
|
||||
line: "export {{ item }}={{ ansible_default_ipv4.address }}"
|
||||
regexp: "^export {{ item }}="
|
||||
state: present
|
||||
with_items:
|
||||
|
@ -37,13 +37,14 @@
|
|||
path: "{{ artifacts }}"
|
||||
state: directory
|
||||
|
||||
# TODO remove the last test skipped once https://github.com/kubernetes-incubator/cri-o/pull/1217 is merged
|
||||
- name: Buffer the e2e testing command to workaround Ansible YAML folding "feature"
|
||||
set_fact:
|
||||
e2e_shell_cmd: >
|
||||
/usr/bin/go run hack/e2e.go
|
||||
--test
|
||||
--test_args="-host=https://{{ ansible_default_ipv4.address }}:6443
|
||||
--ginkgo.focus=\[Conformance\]
|
||||
--ginkgo.skip=\[Slow\]|\[Serial\]|\[Disruptive\]|\[Flaky\]|\[Feature:.+\]|PersistentVolumes|\[HPA\]|should.support.building.a.client.with.a.CSR|should.support.inline.execution.and.attach
|
||||
--report-dir={{ artifacts }}"
|
||||
&> {{ artifacts }}/e2e.log
|
||||
# Fix vim syntax hilighting: "
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
---
|
||||
|
||||
- name: ensure Golang dir is empty first
|
||||
file:
|
||||
path: /usr/local/go
|
||||
state: absent
|
||||
|
||||
- name: fetch Golang
|
||||
unarchive:
|
||||
remote_src: yes
|
||||
src: https://storage.googleapis.com/golang/go1.8.4.linux-amd64.tar.gz
|
||||
src: "https://storage.googleapis.com/golang/go{{ version }}.linux-amd64.tar.gz"
|
||||
dest: /usr/local
|
||||
|
||||
- name: link go toolchain
|
||||
|
@ -47,5 +52,4 @@
|
|||
- onsi/gomega
|
||||
- cloudflare/cfssl/cmd/...
|
||||
- jteeuwen/go-bindata/go-bindata
|
||||
- vbatts/git-validation
|
||||
- cpuguy83/go-md2man
|
||||
|
|
|
@ -10,15 +10,23 @@
|
|||
|
||||
- name: install Golang tools
|
||||
include: golang.yml
|
||||
vars:
|
||||
version: "1.8.5"
|
||||
|
||||
- name: clone build and install bats
|
||||
include: "build/bats.yml"
|
||||
|
||||
- name: clone build and install cri-tools
|
||||
include: "build/cri-tools.yml"
|
||||
vars:
|
||||
cri_tools_git_version: "b42fc3f364dd48f649d55926c34492beeb9b2e99"
|
||||
|
||||
- name: clone build and install kubernetes
|
||||
include: "build/kubernetes.yml"
|
||||
vars:
|
||||
k8s_git_version: "cri-o-node-e2e-patched-logs"
|
||||
k8s_github_fork: "runcom"
|
||||
crio_socket: "/var/run/crio.sock"
|
||||
|
||||
- name: clone build and install runc
|
||||
include: "build/runc.yml"
|
||||
|
@ -33,6 +41,8 @@
|
|||
tags:
|
||||
- integration
|
||||
- e2e
|
||||
- node-e2e
|
||||
- critest
|
||||
tasks:
|
||||
- name: clone build and install cri-o
|
||||
include: "build/cri-o.yml"
|
||||
|
@ -44,6 +54,11 @@
|
|||
tags:
|
||||
- integration
|
||||
tasks:
|
||||
- name: clone build and install cri-tools
|
||||
include: "build/cri-tools.yml"
|
||||
vars:
|
||||
force_clone: True
|
||||
cri_tools_git_version: "a9e38a4a000bc1a4052fb33de1c967b8cfe9ad40"
|
||||
- name: run cri-o integration tests
|
||||
include: test.yml
|
||||
|
||||
|
@ -52,12 +67,59 @@
|
|||
vars_files:
|
||||
- "{{ playbook_dir }}/vars.yml"
|
||||
tags:
|
||||
- e2e
|
||||
- critest
|
||||
tasks:
|
||||
- name: install Golang tools
|
||||
include: golang.yml
|
||||
vars:
|
||||
version: "1.9.2"
|
||||
- name: setup critest
|
||||
include: "build/cri-tools.yml"
|
||||
vars:
|
||||
force_clone: True
|
||||
cri_tools_git_version: "a9e38a4a000bc1a4052fb33de1c967b8cfe9ad40"
|
||||
- name: run critest validation and benchmarks
|
||||
include: critest.yml
|
||||
|
||||
- hosts: all
|
||||
remote_user: root
|
||||
vars_files:
|
||||
- "{{ playbook_dir }}/vars.yml"
|
||||
tags:
|
||||
- node-e2e
|
||||
tasks:
|
||||
- name: install Golang tools
|
||||
include: golang.yml
|
||||
vars:
|
||||
version: "1.9.2"
|
||||
- name: clone build and install kubernetes
|
||||
include: "build/kubernetes.yml"
|
||||
vars:
|
||||
force_clone: True
|
||||
k8s_git_version: "master"
|
||||
k8s_github_fork: "kubernetes"
|
||||
crio_socket: "/var/run/crio/crio.sock"
|
||||
- name: run k8s node-e2e tests
|
||||
include: node-e2e.yml
|
||||
|
||||
- hosts: all
|
||||
remote_user: root
|
||||
vars_files:
|
||||
- "{{ playbook_dir }}/vars.yml"
|
||||
tags:
|
||||
- e2e
|
||||
tasks:
|
||||
- name: install Golang tools
|
||||
include: golang.yml
|
||||
vars:
|
||||
version: "1.9.2"
|
||||
- name: clone build and install kubernetes
|
||||
include: "build/kubernetes.yml"
|
||||
vars:
|
||||
force_clone: True
|
||||
# master as of 12/11/2017
|
||||
k8s_git_version: "master-nfs-fix"
|
||||
k8s_github_fork: "runcom"
|
||||
crio_socket: "/var/run/crio/crio.sock"
|
||||
- name: run k8s e2e tests
|
||||
include: e2e.yml
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
|
||||
- name: enable and start CRI-O
|
||||
systemd:
|
||||
name: crio
|
||||
state: started
|
||||
enabled: yes
|
||||
daemon_reload: yes
|
||||
|
||||
- name: disable SELinux
|
||||
command: setenforce 0
|
||||
|
||||
- name: Flush the iptables
|
||||
command: iptables -F
|
||||
|
||||
- name: run node-e2e tests
|
||||
shell: |
|
||||
# parametrize crio socket
|
||||
# cgroup-driver???
|
||||
# TODO(runcom): remove conformance focus, we want everything for testgrid
|
||||
make test-e2e-node PARALLELISM=1 RUNTIME=remote CONTAINER_RUNTIME_ENDPOINT=/var/run/crio.sock IMAGE_SERVICE_ENDPOINT=/var/run/crio/crio.sock TEST_ARGS='--prepull-images=true --kubelet-flags="--cgroup-driver=systemd"' FOCUS="\[Conformance\]" &> {{ artifacts }}/node-e2e.log
|
||||
args:
|
||||
chdir: "{{ ansible_env.GOPATH }}/src/k8s.io/kubernetes"
|
||||
async: 7200
|
||||
poll: 10
|
||||
ignore_errors: true
|
|
@ -5,6 +5,7 @@
|
|||
name: "{{ item }}"
|
||||
state: present
|
||||
with_items:
|
||||
- atomic-registries
|
||||
- container-selinux
|
||||
- curl
|
||||
- device-mapper-devel
|
||||
|
@ -41,9 +42,9 @@
|
|||
- ostree-devel
|
||||
- pkgconfig
|
||||
- python
|
||||
- python2-boto
|
||||
- python2-crypto
|
||||
- python-devel
|
||||
- python-rhsm-certificates
|
||||
- python-virtualenv
|
||||
- PyYAML
|
||||
- redhat-rpm-config
|
||||
|
@ -57,6 +58,22 @@
|
|||
async: 600
|
||||
poll: 10
|
||||
|
||||
- name: Add python2-boto for Fedora
|
||||
package:
|
||||
name: "{{ item }}"
|
||||
state: present
|
||||
with_items:
|
||||
- python2-boto
|
||||
when: ansible_distribution in ['Fedora']
|
||||
|
||||
- name: Add python-boto for RHEL and CentOS
|
||||
package:
|
||||
name: "{{ item }}"
|
||||
state: present
|
||||
with_items:
|
||||
- python-boto
|
||||
when: ansible_distribution in ['RedHat', 'CentOS']
|
||||
|
||||
- name: Add Btrfs for Fedora
|
||||
package:
|
||||
name: "{{ item }}"
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
---
|
||||
|
||||
- name: clone build and install cri-tools
|
||||
include: "build/cri-tools.yml"
|
||||
|
||||
- name: Make testing output verbose so it can be converted to xunit
|
||||
lineinfile:
|
||||
dest: "{{ ansible_env.GOPATH }}/src/k8s.io/kubernetes/hack/make-rules/test.sh"
|
||||
|
|
|
@ -1 +1 @@
|
|||
runtime-endpoint: /var/run/crio.sock
|
||||
runtime-endpoint: /var/run/crio/crio.sock
|
||||
|
|
189
docs/crio.8.md
189
docs/crio.8.md
|
@ -5,157 +5,122 @@
|
|||
crio - OCI Kubernetes Container Runtime daemon
|
||||
|
||||
# SYNOPSIS
|
||||
**crio**
|
||||
[**--apparmor-profile**=[*value*]]
|
||||
[**--cgroup-manager**=[*value*]]
|
||||
[**--cni-config-dir**=[*value*]]
|
||||
[**--cni-plugin-dir**=[*value*]]
|
||||
[**--config**=[*value*]]
|
||||
[**--conmon**=[*value*]]
|
||||
[**--cpu-profile**=[*value*]]
|
||||
[**--default-transport**=[*value*]]
|
||||
[**--help**|**-h**]
|
||||
[**--insecure-registry**=[*value*]]
|
||||
[**--listen**=[*value*]]
|
||||
[**--log**=[*value*]]
|
||||
[**--log-format value**]
|
||||
[**--log-level value**]
|
||||
[**--pause-command**=[*value*]]
|
||||
[**--pause-image**=[*value*]]
|
||||
[**--registry**=[*value*]]
|
||||
[**--root**=[*value*]]
|
||||
[**--runroot**=[*value*]]
|
||||
[**--runtime**=[*value*]]
|
||||
[**--seccomp-profile**=[*value*]]
|
||||
[**--selinux**]
|
||||
[**--signature-policy**=[*value*]]
|
||||
[**--storage-driver**=[*value*]]
|
||||
[**--storage-opt**=[*value*]]
|
||||
[**--version**|**-v**]
|
||||
|
||||
crio
|
||||
```
|
||||
[--apparmor-profile=[value]]
|
||||
[--cgroup-manager=[value]]
|
||||
[--cni-config-dir=[value]]
|
||||
[--cni-plugin-dir=[value]]
|
||||
[--config=[value]]
|
||||
[--conmon=[value]]
|
||||
[--cpu-profile=[value]]
|
||||
[--default-transport=[value]]
|
||||
[--help|-h]
|
||||
[--insecure-registry=[value]]
|
||||
[--listen=[value]]
|
||||
[--log=[value]]
|
||||
[--log-format value]
|
||||
[--log-level value]
|
||||
[--pause-command=[value]]
|
||||
[--pause-image=[value]]
|
||||
[--registry=[value]]
|
||||
[--root=[value]]
|
||||
[--runroot=[value]]
|
||||
[--runtime=[value]]
|
||||
[--seccomp-profile=[value]]
|
||||
[--selinux]
|
||||
[--signature-policy=[value]]
|
||||
[--storage-driver=[value]]
|
||||
[--storage-opt=[value]]
|
||||
[--version|-v]
|
||||
```
|
||||
# DESCRIPTION
|
||||
OCI-based implementation of Kubernetes Container Runtime Interface Daemon
|
||||
|
||||
crio is meant to provide an integration path between OCI conformant runtimes and the kubelet. Specifically, it implements the Kubelet Container Runtime Interface (CRI) using OCI conformant runtimes. The scope of crio is tied to the scope of the CRI.
|
||||
|
||||
* Support multiple image formats including the existing Docker image format
|
||||
* Support for multiple means to download images including trust & image verification
|
||||
* Container image management (managing image layers, overlay filesystems, etc)
|
||||
* Container process lifecycle management
|
||||
* Monitoring and logging required to satisfy the CRI
|
||||
* Resource isolation as required by the CRI
|
||||
|
||||
**crio [GLOBAL OPTIONS]**
|
||||
|
||||
**crio [GLOBAL OPTIONS] config [OPTIONS]**
|
||||
1. Support multiple image formats including the existing Docker image format.
|
||||
2. Support for multiple means to download images including trust & image verification.
|
||||
3. Container image management (managing image layers, overlay filesystems, etc).
|
||||
4. Container process lifecycle management.
|
||||
5. Monitoring and logging required to satisfy the CRI.
|
||||
6. Resource isolation as required by the CRI.
|
||||
|
||||
**Usage**:
|
||||
```
|
||||
crio [GLOBAL OPTIONS]
|
||||
crio [GLOBAL OPTIONS] config [OPTIONS]
|
||||
```
|
||||
# GLOBAL OPTIONS
|
||||
**--apparmor_profile**="": Name of the apparmor profile to be used as the runtime's default (default: "crio-default")
|
||||
|
||||
**--apparmor_profile**=""
|
||||
Name of the apparmor profile to be used as the runtime's default (default: "crio-default")
|
||||
**--cgroup-manager**="": cgroup manager (cgroupfs or systemd)
|
||||
|
||||
**--cgroup-manager**=""
|
||||
cgroup manager (cgroupfs or systemd)
|
||||
**--config**="": path to configuration file
|
||||
|
||||
**--config**=""
|
||||
path to configuration file
|
||||
**--conmon**="": path to the conmon executable (default: "/usr/local/libexec/crio/conmon")
|
||||
|
||||
**--conmon**=""
|
||||
path to the conmon executable (default: "/usr/local/libexec/crio/conmon")
|
||||
**--cpu-profile**="": set the CPU profile file path
|
||||
|
||||
**--cpu-profile**=""
|
||||
set the CPU profile file path
|
||||
**--default-transport**: A prefix to prepend to image names that can't be pulled as-is.
|
||||
|
||||
**--default-transport**
|
||||
A prefix to prepend to image names that can't be pulled as-is.
|
||||
**--help, -h**: Print usage statement
|
||||
|
||||
**--help, -h**
|
||||
Print usage statement
|
||||
**--insecure-registry=**: Enable insecure registry communication, i.e., enable un-encrypted and/or untrusted communication.
|
||||
|
||||
**--insecure-registry=**
|
||||
Enable insecure registry communication, i.e., enable un-encrypted
|
||||
and/or untrusted communication.
|
||||
1. List of insecure registries can contain an element with CIDR notation to specify a whole subnet.
|
||||
2. Insecure registries accept HTTP or accept HTTPS with certificates from unknown CAs.
|
||||
3. Enabling `--insecure-registry` is useful when running a local registry. However, because its use creates security vulnerabilities, **it should ONLY be enabled for testing purposes**. For increased security, users should add their CA to their system's list of trusted CAs instead of using `--insecure-registry`.
|
||||
|
||||
List of insecure registries can contain an element with CIDR notation
|
||||
to specify a whole subnet. Insecure registries accept HTTP and/or
|
||||
accept HTTPS with certificates from unknown CAs.
|
||||
**--image-volumes**="": Image volume handling ('mkdir', 'bind' or 'ignore') (default: "mkdir")
|
||||
|
||||
Enabling --insecure-registry is useful when running a local registry.
|
||||
However, because its use creates security vulnerabilities it should
|
||||
ONLY be enabled for testing purposes. For increased security, users
|
||||
should add their CA to their system's list of trusted CAs instead of
|
||||
using --insecure-registry.
|
||||
1. mkdir: A directory is created inside the container root filesystem for the volumes.
|
||||
2. bind: A directory is created inside container state directory and bind mounted into the container for the volumes.
|
||||
3. ignore: All volumes are just ignored and no action is taken.
|
||||
|
||||
**--image-volumes**=""
|
||||
Image volume handling ('mkdir', 'bind' or 'ignore') (default: "mkdir")
|
||||
mkdir: A directory is created inside the container root filesystem for the volumes.
|
||||
bind: A directory is created inside container state directory and bind mounted into
|
||||
the container for the volumes.
|
||||
ignore: All volumes are just ignored and no action is taken.
|
||||
**--listen**="": Path to CRI-O socket (default: "/var/run/crio/crio.sock")
|
||||
|
||||
**--listen**=""
|
||||
Path to CRI-O socket (default: "/var/run/crio.sock")
|
||||
**--log**="": Set the log file path where internal debug information is written
|
||||
|
||||
**--log**=""
|
||||
Set the log file path where internal debug information is written
|
||||
**--log-format**="": Set the format used by logs ('text' (default), or 'json') (default: "text")
|
||||
|
||||
**--log-format**=""
|
||||
Set the format used by logs ('text' (default), or 'json') (default: "text")
|
||||
**--log-level**="": log crio messages above specified level: debug, info (default), warn, error, fatal or panic
|
||||
|
||||
**--log-level**=""
|
||||
log crio messages above specified level: debug, info (default), warn, error, fatal or panic
|
||||
**--log-size-max**="": Maximum log size in bytes for a container (default: -1 (no limit)). If it is positive, it must be >= 8192 (to match/exceed conmon read buffer).
|
||||
|
||||
**--log-size-max**=""
|
||||
Maximum log size in bytes for a container (default: -1 (no limit)).
|
||||
If it is positive, it must be >= 8192 (to match/exceed conmon read buffer).
|
||||
**--pause-command**="": Path to the pause executable in the pause image (default: "/pause")
|
||||
|
||||
**--pause-command**=""
|
||||
Path to the pause executable in the pause image (default: "/pause")
|
||||
**--pause-image**="": Image which contains the pause executable (default: "kubernetes/pause")
|
||||
|
||||
**--pause-image**=""
|
||||
Image which contains the pause executable (default: "kubernetes/pause")
|
||||
**--pids-limit**="": Maximum number of processes allowed in a container (default: 1024)
|
||||
|
||||
**--pids-limit**=""
|
||||
Maximum number of processes allowed in a container (default: 1024)
|
||||
**--enable-shared-pid-namespace**="": Enable using a shared PID namespace for containers in a pod (default: false)
|
||||
|
||||
**--root**=""
|
||||
The crio root dir (default: "/var/lib/containers/storage")
|
||||
**--root**="": The crio root dir (default: "/var/lib/containers/storage")
|
||||
|
||||
**--registry**=""
|
||||
Registry host which will be prepended to unqualified images, can be specified multiple times
|
||||
**--registry**="": Registry host which will be prepended to unqualified images, can be specified multiple times
|
||||
|
||||
**--runroot**=""
|
||||
The crio state dir (default: "/var/run/containers/storage")
|
||||
**--runroot**="": The crio state dir (default: "/var/run/containers/storage")
|
||||
|
||||
**--runtime**=""
|
||||
OCI runtime path (default: "/usr/bin/runc")
|
||||
**--runtime**="": OCI runtime path (default: "/usr/bin/runc")
|
||||
|
||||
**--selinux**=*true*|*false*
|
||||
Enable selinux support (default: false)
|
||||
**--selinux**=**true**|**false**: Enable selinux support (default: false)
|
||||
|
||||
**--seccomp-profile**=""
|
||||
Path to the seccomp json profile to be used as the runtime's default (default: "/etc/crio/seccomp.json")
|
||||
**--seccomp-profile**="": Path to the seccomp json profile to be used as the runtime's default (default: "/etc/crio/seccomp.json")
|
||||
|
||||
**--signature-policy**=""
|
||||
Path to the signature policy json file (default: "", to use the system-wide default)
|
||||
**--signature-policy**="": Path to the signature policy json file (default: "", to use the system-wide default)
|
||||
|
||||
**--storage-driver**
|
||||
OCI storage driver (default: "devicemapper")
|
||||
**--storage-driver**: OCI storage driver (default: "devicemapper")
|
||||
|
||||
**--storage-opt**
|
||||
OCI storage driver option (no default)
|
||||
**--storage-opt**: OCI storage driver option (no default)
|
||||
|
||||
**--cni-config-dir**=""
|
||||
CNI configuration files directory (default: "/etc/cni/net.d/")
|
||||
**--cni-config-dir**="": CNI configuration files directory (default: "/etc/cni/net.d/")
|
||||
|
||||
**--cni-plugin-dir**=""
|
||||
CNI plugin binaries directory (default: "/opt/cni/bin/")
|
||||
**--cni-plugin-dir**="": CNI plugin binaries directory (default: "/opt/cni/bin/")
|
||||
|
||||
**--cpu-profile**
|
||||
Set the CPU profile file path
|
||||
**--cpu-profile**: Set the CPU profile file path
|
||||
|
||||
**--version, -v**
|
||||
Print the version
|
||||
**--version, -v**: Print the version
|
||||
|
||||
# COMMANDS
|
||||
CRI-O's default command is to start the daemon. However, it currently offers a
|
||||
|
|
|
@ -68,7 +68,7 @@ Example:
|
|||
## CRIO.API TABLE
|
||||
|
||||
**listen**=""
|
||||
Path to crio socket (default: "/var/run/crio.sock")
|
||||
Path to crio socket (default: "/var/run/crio/crio.sock")
|
||||
|
||||
## CRIO.RUNTIME TABLE
|
||||
|
||||
|
@ -87,6 +87,9 @@ Example:
|
|||
**pids_limit**=""
|
||||
Maximum number of processes allowed in a container (default: 1024)
|
||||
|
||||
**enable_shared_pid_namespace**=""
|
||||
Enable using a shared PID namespace for containers in a pod (default: false)
|
||||
|
||||
**runtime**=""
|
||||
OCI runtime path (default: "/usr/bin/runc")
|
||||
|
||||
|
|
BIN
docs/play.png
BIN
docs/play.png
Binary file not shown.
Before Width: | Height: | Size: 1.6 KiB |
|
@ -0,0 +1,7 @@
|
|||
#!/bin/bash
|
||||
cc -E - > /dev/null 2> /dev/null << EOF
|
||||
#include <libdevmapper.h>
|
||||
EOF
|
||||
if test $? -ne 0 ; then
|
||||
echo exclude_graphdriver_devicemapper
|
||||
fi
|
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e -o pipefail
|
||||
|
||||
if [ -z "$VALIDATE_UPSTREAM" ]; then
|
||||
# this is kind of an expensive check, so let's not do this twice if we
|
||||
# are running more than one validate bundlescript
|
||||
|
||||
VALIDATE_REPO='https://github.com/kubernetes-incubator/cri-o.git'
|
||||
VALIDATE_BRANCH='master'
|
||||
|
||||
VALIDATE_HEAD="$(git rev-parse --verify HEAD)"
|
||||
|
||||
git fetch -q "$VALIDATE_REPO" "refs/heads/$VALIDATE_BRANCH"
|
||||
VALIDATE_UPSTREAM="$(git rev-parse --verify FETCH_HEAD)"
|
||||
|
||||
VALIDATE_COMMIT_LOG="$VALIDATE_UPSTREAM..$VALIDATE_HEAD"
|
||||
VALIDATE_COMMIT_DIFF="$VALIDATE_UPSTREAM...$VALIDATE_HEAD"
|
||||
|
||||
validate_diff() {
|
||||
if [ "$VALIDATE_UPSTREAM" != "$VALIDATE_HEAD" ]; then
|
||||
git diff "$VALIDATE_COMMIT_DIFF" "$@"
|
||||
fi
|
||||
}
|
||||
validate_log() {
|
||||
if [ "$VALIDATE_UPSTREAM" != "$VALIDATE_HEAD" ]; then
|
||||
git log "$VALIDATE_COMMIT_LOG" "$@"
|
||||
fi
|
||||
}
|
||||
fi
|
|
@ -1,21 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
|
||||
find_files() {
|
||||
find . -not \( \
|
||||
\( \
|
||||
-wholename '*/vendor/*' \
|
||||
\) -prune \
|
||||
\) -name '*.go'
|
||||
}
|
||||
|
||||
GOFMT="gofmt -s"
|
||||
bad_files=$(find_files | xargs $GOFMT -l)
|
||||
if [[ -n "${bad_files}" ]]; then
|
||||
echo "!!! '$GOFMT' needs to be run on the following files: "
|
||||
echo "${bad_files}"
|
||||
exit 1
|
||||
fi
|
5
hooks.md
5
hooks.md
|
@ -53,6 +53,7 @@ type HookParams struct {
|
|||
Cmds []string `json:"cmds"`
|
||||
Annotations []string `json:"annotations"`
|
||||
HasBindMounts bool `json:"hasbindmounts"`
|
||||
Arguments []string `json:"arguments"`
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -63,6 +64,7 @@ type HookParams struct {
|
|||
| cmds | List of regular expressions to match the command for running the container. If the command matches a regex, the hook will be run | Optional |
|
||||
| annotations | List of regular expressions to match against the Annotations in the container runtime spec, if an Annotation matches the hook will be run|optional |
|
||||
| hasbindmounts | Tells CRI-O to run the hook if the container has bind mounts from the host into the container | Optional |
|
||||
| arguments | Additional arguments to append to the hook command when executing it. For example --debug | Optional |
|
||||
|
||||
### Example
|
||||
|
||||
|
@ -85,6 +87,7 @@ cat /etc/containers/oci/hooks.d/oci-systemd-hook.json
|
|||
"hasbindmounts": true,
|
||||
"hook": "/usr/libexec/oci/hooks.d/oci-umount",
|
||||
"stages": [ "prestart" ]
|
||||
"arguments": [ "--debug" ]
|
||||
}
|
||||
```
|
||||
In this example the oci-umount will only be run during the prestart phase if the container has volume/bind mounts from the host into the container.
|
||||
In this example the oci-umount will only be run during the prestart phase if the container has volume/bind mounts from the host into the container, it will also execute oci-umount with the --debug argument.
|
||||
|
|
|
@ -13,17 +13,15 @@ Below, you can find an instruction how to switch one or more nodes on running ku
|
|||
|
||||
### Preparing crio
|
||||
|
||||
You must prepare and install `crio` on each node you would like to switch. Here's the list of files that must be provided:
|
||||
You must prepare and install `crio` on each node you would like to switch.
|
||||
Besides the files installed by `make install install.config`, here's the list of files that must be provided:
|
||||
|
||||
| File path | Description | Location |
|
||||
|--------------------------------------------|----------------------------|-----------------------------------------------------|
|
||||
| `/etc/crio/crio.conf` | crio configuration | Generated on cri-o `make install` |
|
||||
| `/etc/crio/seccomp.conf` | seccomp config | Example stored in cri-o repository |
|
||||
| `/etc/containers/policy.json` | containers policy | Example stored in cri-o repository |
|
||||
| `/bin/{crio, runc}` | `crio` and `runc` binaries | Built from cri-o repository |
|
||||
| `/usr/local/libexec/crio/conmon` | `conmon` binary | Built from cri-o repository |
|
||||
| `/opt/cni/bin/{flannel, bridge,...}` | CNI plugins binaries | Can be built from sources `containernetworking/cni` |
|
||||
| `/etc/cni/net.d/10-mynet.conf` | Network config | Example stored in [README file](README.md) |
|
||||
| File path | Description | Location |
|
||||
|--------------------------------------------|-----------------------------|---------------------------------------------------------|
|
||||
| `/etc/containers/policy.json` | containers policy | [Example](test/policy.json) stored in cri-o repository |
|
||||
| `/bin/runc` | `runc` or other OCI runtime | Can be build from sources `opencontainers/runc` |
|
||||
| `/opt/cni/bin/{flannel, bridge,...}` | CNI plugins binaries | Can be built from sources `containernetworking/plugins` |
|
||||
| `/etc/cni/net.d/...` | CNI network config | Example [here](contrib/cni) |
|
||||
|
||||
`crio` binary can be executed directly on host, inside the container or in any way.
|
||||
However, recommended way is to set it as a systemd service.
|
||||
|
@ -79,7 +77,7 @@ KUBELET_ARGS="--pod-manifest-path=/etc/kubernetes/manifests
|
|||
You need to add following parameters to `KUBELET_ARGS`:
|
||||
* `--experimental-cri=true` - Use Container Runtime Interface. Will be true by default from kubernetes 1.6 release.
|
||||
* `--container-runtime=remote` - Use remote runtime with provided socket.
|
||||
* `--container-runtime-endpoint=/var/run/crio.sock` - Socket for remote runtime (default `crio` socket localization).
|
||||
* `--container-runtime-endpoint=/var/run/crio/crio.sock` - Socket for remote runtime (default `crio` socket localization).
|
||||
* `--runtime-request-timeout=10m` - Optional but useful. Some requests, especially pulling huge images, may take longer than default (2 minutes) and will cause an error.
|
||||
|
||||
Kubelet is prepared now.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package libkpod
|
||||
package lib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -83,7 +83,7 @@ type RootConfig struct {
|
|||
LogDir string `toml:"log_dir"`
|
||||
|
||||
// FileLocking specifies whether to use file-based or in-memory locking
|
||||
// File-based locking is required when multiple users of libkpod are
|
||||
// File-based locking is required when multiple users of lib are
|
||||
// present on the same system
|
||||
FileLocking bool `toml:"file_locking"`
|
||||
}
|
||||
|
@ -121,6 +121,9 @@ type RuntimeConfig struct {
|
|||
// NoPivot instructs the runtime to not use `pivot_root`, but instead use `MS_MOVE`
|
||||
NoPivot bool `toml:"no_pivot"`
|
||||
|
||||
// EnableSharePidNamespace instructs the runtime to enable share pid namespace
|
||||
EnableSharedPIDNamespace bool `toml:"enable_shared_pid_namespace"`
|
||||
|
||||
// Conmon is the path to conmon binary, used for managing the runtime.
|
||||
Conmon string `toml:"conmon"`
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package libkpod
|
||||
package lib
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
|
@ -1,10 +1,10 @@
|
|||
package libkpod
|
||||
package lib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
cstorage "github.com/containers/storage"
|
||||
"github.com/kubernetes-incubator/cri-o/libkpod/sandbox"
|
||||
"github.com/kubernetes-incubator/cri-o/lib/sandbox"
|
||||
"github.com/kubernetes-incubator/cri-o/oci"
|
||||
"github.com/kubernetes-incubator/cri-o/pkg/registrar"
|
||||
"github.com/pkg/errors"
|
|
@ -1,4 +1,4 @@
|
|||
package libkpod
|
||||
package lib
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -12,7 +12,7 @@ import (
|
|||
cstorage "github.com/containers/storage"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/truncindex"
|
||||
"github.com/kubernetes-incubator/cri-o/libkpod/sandbox"
|
||||
"github.com/kubernetes-incubator/cri-o/lib/sandbox"
|
||||
"github.com/kubernetes-incubator/cri-o/oci"
|
||||
"github.com/kubernetes-incubator/cri-o/pkg/annotations"
|
||||
"github.com/kubernetes-incubator/cri-o/pkg/registrar"
|
||||
|
@ -168,7 +168,7 @@ func New(config *Config) (*ContainerServer, error) {
|
|||
state: &containerServerState{
|
||||
containers: oci.NewMemoryStore(),
|
||||
infraContainers: oci.NewMemoryStore(),
|
||||
sandboxes: make(map[string]*sandbox.Sandbox),
|
||||
sandboxes: sandbox.NewMemoryStore(),
|
||||
processLevels: make(map[string]int),
|
||||
},
|
||||
config: config,
|
||||
|
@ -617,70 +617,53 @@ func (c *ContainerServer) Shutdown() error {
|
|||
type containerServerState struct {
|
||||
containers oci.ContainerStorer
|
||||
infraContainers oci.ContainerStorer
|
||||
sandboxes map[string]*sandbox.Sandbox
|
||||
sandboxes sandbox.Storer
|
||||
// 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
|
||||
func (c *ContainerServer) AddContainer(ctr *oci.Container) {
|
||||
c.stateLock.Lock()
|
||||
defer c.stateLock.Unlock()
|
||||
sandbox := c.state.sandboxes[ctr.Sandbox()]
|
||||
sandbox := c.state.sandboxes.Get(ctr.Sandbox())
|
||||
sandbox.AddContainer(ctr)
|
||||
c.state.containers.Add(ctr.ID(), ctr)
|
||||
}
|
||||
|
||||
// AddInfraContainer adds a container to the container state store
|
||||
func (c *ContainerServer) AddInfraContainer(ctr *oci.Container) {
|
||||
c.stateLock.Lock()
|
||||
defer c.stateLock.Unlock()
|
||||
c.state.infraContainers.Add(ctr.ID(), ctr)
|
||||
}
|
||||
|
||||
// GetContainer returns a container by its ID
|
||||
func (c *ContainerServer) GetContainer(id string) *oci.Container {
|
||||
c.stateLock.Lock()
|
||||
defer c.stateLock.Unlock()
|
||||
return c.state.containers.Get(id)
|
||||
}
|
||||
|
||||
// GetInfraContainer returns a container by its ID
|
||||
func (c *ContainerServer) GetInfraContainer(id string) *oci.Container {
|
||||
c.stateLock.Lock()
|
||||
defer c.stateLock.Unlock()
|
||||
return c.state.infraContainers.Get(id)
|
||||
}
|
||||
|
||||
// HasContainer checks if a container exists in the state
|
||||
func (c *ContainerServer) HasContainer(id string) bool {
|
||||
c.stateLock.Lock()
|
||||
defer c.stateLock.Unlock()
|
||||
ctr := c.state.containers.Get(id)
|
||||
return ctr != nil
|
||||
return c.state.containers.Get(id) != nil
|
||||
}
|
||||
|
||||
// RemoveContainer removes a container from the container state store
|
||||
func (c *ContainerServer) RemoveContainer(ctr *oci.Container) {
|
||||
c.stateLock.Lock()
|
||||
defer c.stateLock.Unlock()
|
||||
sbID := ctr.Sandbox()
|
||||
sb := c.state.sandboxes[sbID]
|
||||
sb := c.state.sandboxes.Get(sbID)
|
||||
sb.RemoveContainer(ctr)
|
||||
c.state.containers.Delete(ctr.ID())
|
||||
}
|
||||
|
||||
// RemoveInfraContainer removes a container from the container state store
|
||||
func (c *ContainerServer) RemoveInfraContainer(ctr *oci.Container) {
|
||||
c.stateLock.Lock()
|
||||
defer c.stateLock.Unlock()
|
||||
c.state.infraContainers.Delete(ctr.ID())
|
||||
}
|
||||
|
||||
// listContainers returns a list of all containers stored by the server state
|
||||
func (c *ContainerServer) listContainers() []*oci.Container {
|
||||
c.stateLock.Lock()
|
||||
defer c.stateLock.Unlock()
|
||||
return c.state.containers.List()
|
||||
}
|
||||
|
||||
|
@ -704,62 +687,52 @@ func (c *ContainerServer) ListContainers(filters ...func(*oci.Container) bool) (
|
|||
|
||||
// AddSandbox adds a sandbox to the sandbox state store
|
||||
func (c *ContainerServer) AddSandbox(sb *sandbox.Sandbox) {
|
||||
c.state.sandboxes.Add(sb.ID(), sb)
|
||||
|
||||
c.stateLock.Lock()
|
||||
defer c.stateLock.Unlock()
|
||||
c.state.sandboxes[sb.ID()] = sb
|
||||
c.state.processLevels[selinux.NewContext(sb.ProcessLabel())["level"]]++
|
||||
c.stateLock.Unlock()
|
||||
}
|
||||
|
||||
// GetSandbox returns a sandbox by its ID
|
||||
func (c *ContainerServer) GetSandbox(id string) *sandbox.Sandbox {
|
||||
c.stateLock.Lock()
|
||||
defer c.stateLock.Unlock()
|
||||
return c.state.sandboxes[id]
|
||||
return c.state.sandboxes.Get(id)
|
||||
}
|
||||
|
||||
// GetSandboxContainer returns a sandbox's infra container
|
||||
func (c *ContainerServer) GetSandboxContainer(id string) *oci.Container {
|
||||
c.stateLock.Lock()
|
||||
defer c.stateLock.Unlock()
|
||||
sb, ok := c.state.sandboxes[id]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
sb := c.state.sandboxes.Get(id)
|
||||
return sb.InfraContainer()
|
||||
}
|
||||
|
||||
// HasSandbox checks if a sandbox exists in the state
|
||||
func (c *ContainerServer) HasSandbox(id string) bool {
|
||||
c.stateLock.Lock()
|
||||
defer c.stateLock.Unlock()
|
||||
_, ok := c.state.sandboxes[id]
|
||||
return ok
|
||||
return c.state.sandboxes.Get(id) != nil
|
||||
}
|
||||
|
||||
// RemoveSandbox removes a sandbox from the state store
|
||||
func (c *ContainerServer) RemoveSandbox(id string) {
|
||||
c.stateLock.Lock()
|
||||
defer c.stateLock.Unlock()
|
||||
processLabel := c.state.sandboxes[id].ProcessLabel()
|
||||
delete(c.state.sandboxes, id)
|
||||
sb := c.state.sandboxes.Get(id)
|
||||
processLabel := sb.ProcessLabel()
|
||||
level := selinux.NewContext(processLabel)["level"]
|
||||
c.state.processLevels[level]--
|
||||
if c.state.processLevels[level] == 0 {
|
||||
label.ReleaseLabel(processLabel)
|
||||
delete(c.state.processLevels, level)
|
||||
|
||||
c.stateLock.Lock()
|
||||
pl, ok := c.state.processLevels[level]
|
||||
if ok {
|
||||
c.state.processLevels[level] = pl - 1
|
||||
if c.state.processLevels[level] == 0 {
|
||||
label.ReleaseLabel(processLabel)
|
||||
delete(c.state.processLevels, level)
|
||||
}
|
||||
}
|
||||
c.stateLock.Unlock()
|
||||
|
||||
c.state.sandboxes.Delete(id)
|
||||
}
|
||||
|
||||
// ListSandboxes lists all sandboxes in the state store
|
||||
func (c *ContainerServer) ListSandboxes() []*sandbox.Sandbox {
|
||||
c.stateLock.Lock()
|
||||
defer c.stateLock.Unlock()
|
||||
sbArray := make([]*sandbox.Sandbox, 0, len(c.state.sandboxes))
|
||||
for _, sb := range c.state.sandboxes {
|
||||
sbArray = append(sbArray, sb)
|
||||
}
|
||||
|
||||
return sbArray
|
||||
return c.state.sandboxes.List()
|
||||
}
|
||||
|
||||
// LibcontainerStats gets the stats for the container with the given id from runc/libcontainer
|
|
@ -1,4 +1,4 @@
|
|||
package libkpod
|
||||
package lib
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -27,6 +27,7 @@ type HookParams struct {
|
|||
Cmds []string `json:"cmd"`
|
||||
Annotations []string `json:"annotation"`
|
||||
HasBindMounts bool `json:"hasbindmounts"`
|
||||
Arguments []string `json:"arguments"`
|
||||
}
|
||||
|
||||
// readHook reads hooks json files, verifies it and returns the json config
|
|
@ -1,4 +1,4 @@
|
|||
package libkpod
|
||||
package lib
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/pkg/signal"
|
|
@ -1,4 +1,4 @@
|
|||
package libkpod
|
||||
package lib
|
||||
|
||||
import (
|
||||
"path"
|
|
@ -1,4 +1,4 @@
|
|||
package libkpod
|
||||
package lib
|
||||
|
||||
import (
|
||||
"github.com/kubernetes-incubator/cri-o/oci"
|
|
@ -1,4 +1,4 @@
|
|||
package libkpod
|
||||
package lib
|
||||
|
||||
import (
|
||||
"os"
|
|
@ -1,4 +1,4 @@
|
|||
package libkpod
|
||||
package lib
|
||||
|
||||
import (
|
||||
"encoding/json"
|
|
@ -0,0 +1,31 @@
|
|||
package sandbox
|
||||
|
||||
import "sort"
|
||||
|
||||
// History is a convenience type for storing a list of sandboxes,
|
||||
// sorted by creation date in descendant order.
|
||||
type History []*Sandbox
|
||||
|
||||
// Len returns the number of sandboxes in the history.
|
||||
func (history *History) Len() int {
|
||||
return len(*history)
|
||||
}
|
||||
|
||||
// Less compares two sandboxes and returns true if the second one
|
||||
// was created before the first one.
|
||||
func (history *History) Less(i, j int) bool {
|
||||
sandboxes := *history
|
||||
// FIXME: state access should be serialized
|
||||
return sandboxes[j].created.Before(sandboxes[i].created)
|
||||
}
|
||||
|
||||
// Swap switches sandboxes i and j positions in the history.
|
||||
func (history *History) Swap(i, j int) {
|
||||
sandboxes := *history
|
||||
sandboxes[i], sandboxes[j] = sandboxes[j], sandboxes[i]
|
||||
}
|
||||
|
||||
// sort orders the history by creation date in descendant order.
|
||||
func (history *History) sort() {
|
||||
sort.Sort(history)
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package sandbox
|
||||
|
||||
import "sync"
|
||||
|
||||
// memoryStore implements a Store in memory.
|
||||
type memoryStore struct {
|
||||
s map[string]*Sandbox
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// NewMemoryStore initializes a new memory store.
|
||||
func NewMemoryStore() Storer {
|
||||
return &memoryStore{
|
||||
s: make(map[string]*Sandbox),
|
||||
}
|
||||
}
|
||||
|
||||
// Add appends a new sandbox to the memory store.
|
||||
// It overrides the id if it existed before.
|
||||
func (c *memoryStore) Add(id string, cont *Sandbox) {
|
||||
c.Lock()
|
||||
c.s[id] = cont
|
||||
c.Unlock()
|
||||
}
|
||||
|
||||
// Get returns a sandbox from the store by id.
|
||||
func (c *memoryStore) Get(id string) *Sandbox {
|
||||
var res *Sandbox
|
||||
c.RLock()
|
||||
res = c.s[id]
|
||||
c.RUnlock()
|
||||
return res
|
||||
}
|
||||
|
||||
// Delete removes a sandbox from the store by id.
|
||||
func (c *memoryStore) Delete(id string) {
|
||||
c.Lock()
|
||||
delete(c.s, id)
|
||||
c.Unlock()
|
||||
}
|
||||
|
||||
// List returns a sorted list of sandboxes from the store.
|
||||
// The sandboxes are ordered by creation date.
|
||||
func (c *memoryStore) List() []*Sandbox {
|
||||
sandboxes := History(c.all())
|
||||
sandboxes.sort()
|
||||
return sandboxes
|
||||
}
|
||||
|
||||
// Size returns the number of sandboxes in the store.
|
||||
func (c *memoryStore) Size() int {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
return len(c.s)
|
||||
}
|
||||
|
||||
// First returns the first sandbox found in the store by a given filter.
|
||||
func (c *memoryStore) First(filter StoreFilter) *Sandbox {
|
||||
for _, cont := range c.all() {
|
||||
if filter(cont) {
|
||||
return cont
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ApplyAll calls the reducer function with every sandbox in the store.
|
||||
// This operation is asynchronous in the memory store.
|
||||
// NOTE: Modifications to the store MUST NOT be done by the StoreReducer.
|
||||
func (c *memoryStore) ApplyAll(apply StoreReducer) {
|
||||
wg := new(sync.WaitGroup)
|
||||
for _, cont := range c.all() {
|
||||
wg.Add(1)
|
||||
go func(sandbox *Sandbox) {
|
||||
apply(sandbox)
|
||||
wg.Done()
|
||||
}(cont)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func (c *memoryStore) all() []*Sandbox {
|
||||
c.RLock()
|
||||
sandboxes := make([]*Sandbox, 0, len(c.s))
|
||||
for _, cont := range c.s {
|
||||
sandboxes = append(sandboxes, cont)
|
||||
}
|
||||
c.RUnlock()
|
||||
return sandboxes
|
||||
}
|
||||
|
||||
var _ Storer = &memoryStore{}
|
|
@ -7,6 +7,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/containernetworking/plugins/pkg/ns"
|
||||
"github.com/docker/docker/pkg/mount"
|
||||
|
@ -158,6 +159,7 @@ type Sandbox struct {
|
|||
// ipv4 or ipv6 cache
|
||||
ip string
|
||||
seccompProfilePath string
|
||||
created time.Time
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -202,6 +204,7 @@ func New(id, namespace, name, kubeName, logDir string, labels, annotations map[s
|
|||
sb.resolvPath = resolvPath
|
||||
sb.hostname = hostname
|
||||
sb.portMappings = portMappings
|
||||
sb.created = time.Now()
|
||||
|
||||
return sb, nil
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package sandbox
|
||||
|
||||
// StoreFilter defines a function to filter
|
||||
// sandboxes in the store.
|
||||
type StoreFilter func(*Sandbox) bool
|
||||
|
||||
// StoreReducer defines a function to
|
||||
// manipulate sandboxes in the store
|
||||
type StoreReducer func(*Sandbox)
|
||||
|
||||
// Storer defines an interface that any container store must implement.
|
||||
type Storer interface {
|
||||
// Add appends a new sandbox to the store.
|
||||
Add(string, *Sandbox)
|
||||
// Get returns a sandbox from the store by the identifier it was stored with.
|
||||
Get(string) *Sandbox
|
||||
// Delete removes a sandbox from the store by the identifier it was stored with.
|
||||
Delete(string)
|
||||
// List returns a list of sandboxes from the store.
|
||||
List() []*Sandbox
|
||||
// Size returns the number of sandboxes in the store.
|
||||
Size() int
|
||||
// First returns the first sandbox found in the store by a given filter.
|
||||
First(StoreFilter) *Sandbox
|
||||
// ApplyAll calls the reducer function with every sandbox in the store.
|
||||
ApplyAll(StoreReducer)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package libkpod
|
||||
package lib
|
||||
|
||||
import (
|
||||
"path/filepath"
|
|
@ -1,4 +1,4 @@
|
|||
package libkpod
|
||||
package lib
|
||||
|
||||
import (
|
||||
"github.com/kubernetes-incubator/cri-o/oci"
|
|
@ -1,4 +1,4 @@
|
|||
package libkpod
|
||||
package lib
|
||||
|
||||
import (
|
||||
"github.com/kubernetes-incubator/cri-o/oci"
|
|
@ -25,8 +25,9 @@ func (c *memoryStore) Add(id string, cont *Container) {
|
|||
|
||||
// Get returns a container from the store by id.
|
||||
func (c *memoryStore) Get(id string) *Container {
|
||||
var res *Container
|
||||
c.RLock()
|
||||
res := c.s[id]
|
||||
res = c.s[id]
|
||||
c.RUnlock()
|
||||
return res
|
||||
}
|
||||
|
|
66
oci/oci.go
66
oci/oci.go
|
@ -359,9 +359,9 @@ func parseLog(log []byte) (stdout, stderr []byte) {
|
|||
continue
|
||||
}
|
||||
|
||||
// The format of log lines is "DATE pipe REST".
|
||||
parts := bytes.SplitN(line, []byte{' '}, 3)
|
||||
if len(parts) < 3 {
|
||||
// The format of log lines is "DATE pipe LogTag REST".
|
||||
parts := bytes.SplitN(line, []byte{' '}, 4)
|
||||
if len(parts) < 4 {
|
||||
// Ignore the line if it's formatted incorrectly, but complain
|
||||
// about it so it can be debugged.
|
||||
logrus.Warnf("hit invalid log format: %q", string(line))
|
||||
|
@ -369,7 +369,15 @@ func parseLog(log []byte) (stdout, stderr []byte) {
|
|||
}
|
||||
|
||||
pipe := string(parts[1])
|
||||
content := parts[2]
|
||||
content := parts[3]
|
||||
|
||||
linetype := string(parts[2])
|
||||
if linetype == "P" {
|
||||
contentLen := len(content)
|
||||
if content[contentLen-1] == '\n' {
|
||||
content = content[:contentLen-1]
|
||||
}
|
||||
}
|
||||
|
||||
switch pipe {
|
||||
case "stdout":
|
||||
|
@ -415,15 +423,6 @@ func (r *Runtime) ExecSync(c *Container, command []string, timeout int64) (resp
|
|||
os.RemoveAll(logPath)
|
||||
}()
|
||||
|
||||
f, err := ioutil.TempFile("", "exec-process")
|
||||
if err != nil {
|
||||
return nil, ExecSyncError{
|
||||
ExitCode: -1,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
defer os.RemoveAll(f.Name())
|
||||
|
||||
var args []string
|
||||
args = append(args, "-c", c.id)
|
||||
args = append(args, "-r", r.Path(c))
|
||||
|
@ -439,25 +438,16 @@ func (r *Runtime) ExecSync(c *Container, command []string, timeout int64) (resp
|
|||
args = append(args, "-l", logPath)
|
||||
args = append(args, "--socket-dir-path", ContainerAttachSocketDir)
|
||||
|
||||
pspec := c.Spec().Process
|
||||
pspec.Env = append(pspec.Env, r.conmonEnv...)
|
||||
pspec.Args = command
|
||||
processJSON, err := json.Marshal(pspec)
|
||||
processFile, err := PrepareProcessExec(c, command, false)
|
||||
if err != nil {
|
||||
return nil, ExecSyncError{
|
||||
ExitCode: -1,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
defer os.RemoveAll(processFile.Name())
|
||||
|
||||
if err := ioutil.WriteFile(f.Name(), processJSON, 0644); err != nil {
|
||||
return nil, ExecSyncError{
|
||||
ExitCode: -1,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
args = append(args, "--exec-process-spec", f.Name())
|
||||
args = append(args, "--exec-process-spec", processFile.Name())
|
||||
|
||||
cmd := exec.Command(r.conmonPath, args...)
|
||||
|
||||
|
@ -662,7 +652,7 @@ func (r *Runtime) SetStartFailed(c *Container, err error) {
|
|||
func (r *Runtime) UpdateStatus(c *Container) error {
|
||||
c.opLock.Lock()
|
||||
defer c.opLock.Unlock()
|
||||
out, err := exec.Command(r.Path(c), "state", c.id).CombinedOutput()
|
||||
out, err := exec.Command(r.Path(c), "state", c.id).Output()
|
||||
if err != nil {
|
||||
// there are many code paths that could lead to have a bad state in the
|
||||
// underlying runtime.
|
||||
|
@ -765,3 +755,27 @@ func (r *Runtime) UnpauseContainer(c *Container) error {
|
|||
_, err := utils.ExecCmd(r.Path(c), "resume", c.id)
|
||||
return err
|
||||
}
|
||||
|
||||
// PrepareProcessExec returns the path of the process.json used in runc exec -p
|
||||
// caller is responsible to close the returned *os.File if needed.
|
||||
func PrepareProcessExec(c *Container, cmd []string, tty bool) (*os.File, error) {
|
||||
f, err := ioutil.TempFile("", "exec-process-")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pspec := c.Spec().Process
|
||||
pspec.Args = cmd
|
||||
if tty {
|
||||
pspec.Terminal = true
|
||||
}
|
||||
processJSON, err := json.Marshal(pspec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(f.Name(), processJSON, 0644); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
|
|
@ -2,33 +2,42 @@ package storage
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/image/copy"
|
||||
"github.com/containers/image/docker/reference"
|
||||
"github.com/containers/image/image"
|
||||
"github.com/containers/image/manifest"
|
||||
"github.com/containers/image/signature"
|
||||
istorage "github.com/containers/image/storage"
|
||||
"github.com/containers/image/transports/alltransports"
|
||||
"github.com/containers/image/types"
|
||||
"github.com/containers/storage"
|
||||
distreference "github.com/docker/distribution/reference"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
const (
|
||||
minimumTruncatedIDLength = 3
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrCannotParseImageID is returned when we try to ResolveNames for an image ID
|
||||
ErrCannotParseImageID = errors.New("cannot parse an image ID")
|
||||
// ErrImageMultiplyTagged is returned when we try to remove an image that still has multiple names
|
||||
ErrImageMultiplyTagged = errors.New("image still has multiple names applied")
|
||||
)
|
||||
|
||||
// ImageResult wraps a subset of information about an image: its ID, its names,
|
||||
// and the size, if known, or nil if it isn't.
|
||||
type ImageResult struct {
|
||||
ID string
|
||||
Names []string
|
||||
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).
|
||||
ID string
|
||||
Name string
|
||||
RepoTags []string
|
||||
RepoDigests []string
|
||||
Size *uint64
|
||||
Digest digest.Digest
|
||||
ConfigDigest digest.Digest
|
||||
}
|
||||
|
||||
|
@ -45,6 +54,11 @@ type imageService struct {
|
|||
registries []string
|
||||
}
|
||||
|
||||
// sizer knows its size.
|
||||
type sizer interface {
|
||||
Size() (int64, error)
|
||||
}
|
||||
|
||||
// ImageServer wraps up various CRI-related activities into a reusable
|
||||
// implementation.
|
||||
type ImageServer interface {
|
||||
|
@ -57,6 +71,9 @@ type ImageServer interface {
|
|||
PrepareImage(systemContext *types.SystemContext, imageName string, options *copy.Options) (types.Image, error)
|
||||
// PullImage imports an image from the specified location.
|
||||
PullImage(systemContext *types.SystemContext, imageName string, options *copy.Options) (types.ImageReference, error)
|
||||
// UntagImage removes a name from the specified image, and if it was
|
||||
// the only name the image had, removes the image.
|
||||
UntagImage(systemContext *types.SystemContext, imageName string) error
|
||||
// RemoveImage deletes the specified image.
|
||||
RemoveImage(systemContext *types.SystemContext, imageName string) error
|
||||
// GetStore returns the reference to the storage library Store which
|
||||
|
@ -86,6 +103,66 @@ func (svc *imageService) getRef(name string) (types.ImageReference, error) {
|
|||
return ref, nil
|
||||
}
|
||||
|
||||
func sortNamesByType(names []string) (bestName string, tags, digests []string) {
|
||||
for _, name := range names {
|
||||
if len(name) > 72 && name[len(name)-72:len(name)-64] == "@sha256:" {
|
||||
digests = append(digests, name)
|
||||
} else {
|
||||
tags = append(tags, name)
|
||||
}
|
||||
}
|
||||
if len(digests) > 0 {
|
||||
bestName = digests[0]
|
||||
}
|
||||
if len(tags) > 0 {
|
||||
bestName = tags[0]
|
||||
}
|
||||
return bestName, tags, digests
|
||||
}
|
||||
|
||||
func (svc *imageService) makeRepoDigests(knownRepoDigests, tags []string, imageID string) (imageDigest digest.Digest, repoDigests []string) {
|
||||
// Look up the image's digest.
|
||||
img, err := svc.store.Image(imageID)
|
||||
if err != nil {
|
||||
return "", knownRepoDigests
|
||||
}
|
||||
imageDigest = img.Digest
|
||||
if imageDigest == "" {
|
||||
imgDigest, err := svc.store.ImageBigDataDigest(imageID, storage.ImageDigestBigDataKey)
|
||||
if err != nil || imgDigest == "" {
|
||||
return "", knownRepoDigests
|
||||
}
|
||||
imageDigest = imgDigest
|
||||
}
|
||||
// If there are no names to convert to canonical references, we're done.
|
||||
if len(tags) == 0 {
|
||||
return imageDigest, knownRepoDigests
|
||||
}
|
||||
// We only want to supplement what's already explicitly in the list, so keep track of values
|
||||
// that we already know.
|
||||
digestMap := make(map[string]struct{})
|
||||
repoDigests = knownRepoDigests
|
||||
for _, repoDigest := range knownRepoDigests {
|
||||
digestMap[repoDigest] = struct{}{}
|
||||
}
|
||||
// For each tagged name, parse the name, and if we can extract a named reference, convert
|
||||
// it into a canonical reference using the digest and add it to the list.
|
||||
for _, tag := range tags {
|
||||
if ref, err2 := reference.ParseAnyReference(tag); err2 == nil {
|
||||
if name, ok := ref.(reference.Named); ok {
|
||||
trimmed := reference.TrimNamed(name)
|
||||
if imageRef, err3 := reference.WithDigest(trimmed, imageDigest); err3 == nil {
|
||||
if _, ok := digestMap[imageRef.String()]; !ok {
|
||||
repoDigests = append(repoDigests, imageRef.String())
|
||||
digestMap[imageRef.String()] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return imageDigest, repoDigests
|
||||
}
|
||||
|
||||
func (svc *imageService) ListImages(systemContext *types.SystemContext, filter string) ([]ImageResult, error) {
|
||||
results := []ImageResult{}
|
||||
if filter != "" {
|
||||
|
@ -94,16 +171,26 @@ func (svc *imageService) ListImages(systemContext *types.SystemContext, filter s
|
|||
return nil, err
|
||||
}
|
||||
if image, err := istorage.Transport.GetStoreImage(svc.store, ref); err == nil {
|
||||
img, err := ref.NewImage(systemContext)
|
||||
img, err := ref.NewImageSource(systemContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
size := imageSize(img)
|
||||
configDigest, err := imageConfigDigest(img, nil)
|
||||
img.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name, tags, digests := sortNamesByType(image.Names)
|
||||
imageDigest, repoDigests := svc.makeRepoDigests(digests, tags, image.ID)
|
||||
results = append(results, ImageResult{
|
||||
ID: image.ID,
|
||||
Names: image.Names,
|
||||
Size: size,
|
||||
ID: image.ID,
|
||||
Name: name,
|
||||
RepoTags: tags,
|
||||
RepoDigests: repoDigests,
|
||||
Size: size,
|
||||
Digest: imageDigest,
|
||||
ConfigDigest: configDigest,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
|
@ -116,16 +203,26 @@ func (svc *imageService) ListImages(systemContext *types.SystemContext, filter s
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
img, err := ref.NewImage(systemContext)
|
||||
img, err := ref.NewImageSource(systemContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
size := imageSize(img)
|
||||
configDigest, err := imageConfigDigest(img, nil)
|
||||
img.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name, tags, digests := sortNamesByType(image.Names)
|
||||
imageDigest, repoDigests := svc.makeRepoDigests(digests, tags, image.ID)
|
||||
results = append(results, ImageResult{
|
||||
ID: image.ID,
|
||||
Names: image.Names,
|
||||
Size: size,
|
||||
ID: image.ID,
|
||||
Name: name,
|
||||
RepoTags: tags,
|
||||
RepoDigests: repoDigests,
|
||||
Size: size,
|
||||
Digest: imageDigest,
|
||||
ConfigDigest: configDigest,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -150,29 +247,54 @@ func (svc *imageService) ImageStatus(systemContext *types.SystemContext, nameOrI
|
|||
return nil, err
|
||||
}
|
||||
|
||||
img, err := ref.NewImage(systemContext)
|
||||
img, err := ref.NewImageSource(systemContext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer img.Close()
|
||||
size := imageSize(img)
|
||||
configDigest, err := imageConfigDigest(img, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ImageResult{
|
||||
name, tags, digests := sortNamesByType(image.Names)
|
||||
imageDigest, repoDigests := svc.makeRepoDigests(digests, tags, image.ID)
|
||||
result := ImageResult{
|
||||
ID: image.ID,
|
||||
Names: image.Names,
|
||||
Name: name,
|
||||
RepoTags: tags,
|
||||
RepoDigests: repoDigests,
|
||||
Size: size,
|
||||
ConfigDigest: img.ConfigInfo().Digest,
|
||||
}, nil
|
||||
Digest: imageDigest,
|
||||
ConfigDigest: configDigest,
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func imageSize(img types.Image) *uint64 {
|
||||
if sum, err := img.Size(); err == nil {
|
||||
usum := uint64(sum)
|
||||
return &usum
|
||||
func imageSize(img types.ImageSource) *uint64 {
|
||||
if s, ok := img.(sizer); ok {
|
||||
if sum, err := s.Size(); err == nil {
|
||||
usum := uint64(sum)
|
||||
return &usum
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func imageConfigDigest(img types.ImageSource, instanceDigest *digest.Digest) (digest.Digest, error) {
|
||||
manifestBytes, manifestType, err := img.GetManifest(instanceDigest)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
imgManifest, err := manifest.FromBlob(manifestBytes, manifestType)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return imgManifest.ConfigInfo().Digest, nil
|
||||
}
|
||||
|
||||
func (svc *imageService) CanPull(imageName string, options *copy.Options) (bool, error) {
|
||||
srcRef, err := svc.prepareReference(imageName, options)
|
||||
if err != nil {
|
||||
|
@ -182,7 +304,11 @@ func (svc *imageService) CanPull(imageName string, options *copy.Options) (bool,
|
|||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
src, err := image.FromSource(rawSource)
|
||||
sourceCtx := &types.SystemContext{}
|
||||
if options.SourceCtx != nil {
|
||||
sourceCtx = options.SourceCtx
|
||||
}
|
||||
src, err := image.FromSource(sourceCtx, rawSource)
|
||||
if err != nil {
|
||||
rawSource.Close()
|
||||
return false, err
|
||||
|
@ -272,6 +398,57 @@ func (svc *imageService) PullImage(systemContext *types.SystemContext, imageName
|
|||
return destRef, nil
|
||||
}
|
||||
|
||||
func (svc *imageService) UntagImage(systemContext *types.SystemContext, nameOrID string) error {
|
||||
ref, err := alltransports.ParseImageName(nameOrID)
|
||||
if err != nil {
|
||||
ref2, err2 := istorage.Transport.ParseStoreReference(svc.store, "@"+nameOrID)
|
||||
if err2 != nil {
|
||||
ref3, err3 := istorage.Transport.ParseStoreReference(svc.store, nameOrID)
|
||||
if err3 != nil {
|
||||
return err
|
||||
}
|
||||
ref2 = ref3
|
||||
}
|
||||
ref = ref2
|
||||
}
|
||||
|
||||
img, err := istorage.Transport.GetStoreImage(svc.store, ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(img.ID, nameOrID) {
|
||||
namedRef, err := svc.prepareReference(nameOrID, ©.Options{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name := nameOrID
|
||||
if namedRef.DockerReference() != nil {
|
||||
name = namedRef.DockerReference().Name()
|
||||
if tagged, ok := namedRef.DockerReference().(reference.NamedTagged); ok {
|
||||
name = name + ":" + tagged.Tag()
|
||||
}
|
||||
if canonical, ok := namedRef.DockerReference().(reference.Canonical); ok {
|
||||
name = name + "@" + canonical.Digest().String()
|
||||
}
|
||||
}
|
||||
|
||||
prunedNames := make([]string, 0, len(img.Names))
|
||||
for _, imgName := range img.Names {
|
||||
if imgName != name && imgName != nameOrID {
|
||||
prunedNames = append(prunedNames, imgName)
|
||||
}
|
||||
}
|
||||
|
||||
if len(prunedNames) > 0 {
|
||||
return svc.store.SetNames(img.ID, prunedNames)
|
||||
}
|
||||
}
|
||||
|
||||
return ref.DeleteImage(systemContext)
|
||||
}
|
||||
|
||||
func (svc *imageService) RemoveImage(systemContext *types.SystemContext, nameOrID string) error {
|
||||
ref, err := alltransports.ParseImageName(nameOrID)
|
||||
if err != nil {
|
||||
|
@ -328,113 +505,35 @@ func (svc *imageService) isSecureIndex(indexName string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func isValidHostname(hostname string) bool {
|
||||
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) {
|
||||
func splitDockerDomain(name string) (domain, remainder string) {
|
||||
i := strings.IndexRune(name, '/')
|
||||
if i == -1 || (!strings.ContainsAny(name[:i], ".:") && name[:i] != "localhost") {
|
||||
hostname, remoteName = "", name
|
||||
domain, remainder = "", name
|
||||
} else {
|
||||
hostname, remoteName = name[:i], name[i+1:]
|
||||
}
|
||||
if hostname == legacyDefaultHostname {
|
||||
hostname = defaultHostname
|
||||
}
|
||||
if hostname == defaultHostname && !strings.ContainsRune(remoteName, '/') {
|
||||
remoteName = defaultRepoPrefix + remoteName
|
||||
domain, remainder = name[:i], name[i+1:]
|
||||
}
|
||||
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) {
|
||||
r, err := reference.ParseNormalizedNamed(imageName)
|
||||
// _Maybe_ it's a truncated image ID. Don't prepend a registry name, then.
|
||||
if len(imageName) >= minimumTruncatedIDLength && svc.store != nil {
|
||||
if img, err := svc.store.Image(imageName); err == nil && img != nil && strings.HasPrefix(img.ID, imageName) {
|
||||
// It's a truncated version of the ID of an image that's present in local storage;
|
||||
// we need to expand it.
|
||||
return []string{img.ID}, nil
|
||||
}
|
||||
}
|
||||
// This to prevent any image ID to go through this routine
|
||||
_, err := reference.ParseNormalizedNamed(imageName)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "cannot specify 64-byte hexadecimal strings") {
|
||||
return nil, ErrCannotParseImageID
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if isReferenceFullyQualified(r) {
|
||||
domain, remainder := splitDockerDomain(imageName)
|
||||
if domain != "" {
|
||||
// this means the image is already fully qualified
|
||||
return []string{imageName}, nil
|
||||
}
|
||||
|
@ -446,10 +545,13 @@ func (svc *imageService) ResolveNames(imageName string) ([]string, error) {
|
|||
// this means we got an image in the form of "busybox"
|
||||
// we need to use additional registries...
|
||||
// normalize the unqualified image to be domain/repo/image...
|
||||
_, rest := splitDomain(r.Name())
|
||||
images := []string{}
|
||||
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, path.Join(r, rem))
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,10 +3,6 @@ package apparmor
|
|||
const (
|
||||
// DefaultApparmorProfile is the name of default apparmor profile name.
|
||||
DefaultApparmorProfile = "crio-default"
|
||||
|
||||
// ContainerAnnotationKeyPrefix is the prefix to an annotation key specifying a container profile.
|
||||
ContainerAnnotationKeyPrefix = "container.apparmor.security.beta.kubernetes.io/"
|
||||
|
||||
// ProfileRuntimeDefault is he profile specifying the runtime default.
|
||||
ProfileRuntimeDefault = "runtime/default"
|
||||
// ProfileNamePrefix is the prefix for specifying profiles loaded on the node.
|
||||
|
|
|
@ -34,7 +34,7 @@ type profileData struct {
|
|||
|
||||
// EnsureDefaultApparmorProfile loads default apparmor profile, if it is not loaded.
|
||||
func EnsureDefaultApparmorProfile() error {
|
||||
if apparmor.IsEnabled() {
|
||||
if IsEnabled() {
|
||||
loaded, err := IsLoaded(DefaultApparmorProfile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not check if %s AppArmor profile was loaded: %s", DefaultApparmorProfile, err)
|
||||
|
@ -59,12 +59,6 @@ func IsEnabled() bool {
|
|||
return apparmor.IsEnabled()
|
||||
}
|
||||
|
||||
// GetProfileNameFromPodAnnotations gets the name of the profile to use with container from
|
||||
// pod annotations
|
||||
func GetProfileNameFromPodAnnotations(annotations map[string]string, containerName string) string {
|
||||
return annotations[ContainerAnnotationKeyPrefix+containerName]
|
||||
}
|
||||
|
||||
// InstallDefault generates a default profile in a temp directory determined by
|
||||
// os.TempDir(), then loads the profile into the kernel using 'apparmor_parser'.
|
||||
func InstallDefault(name string) error {
|
||||
|
|
|
@ -11,8 +11,3 @@ func IsEnabled() bool {
|
|||
func EnsureDefaultApparmorProfile() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetProfileNameFromPodAnnotations dose nothing, when build without apparmor build tag.
|
||||
func GetProfileNameFromPodAnnotations(annotations map[string]string, containerName string) string {
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"io/ioutil"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/kubernetes-incubator/cri-o/libkpod"
|
||||
"github.com/kubernetes-incubator/cri-o/lib"
|
||||
)
|
||||
|
||||
//CrioConfigPath is the default location for the conf file
|
||||
|
@ -14,7 +14,7 @@ const CrioConfigPath = "/etc/crio/crio.conf"
|
|||
// Config represents the entire set of configuration values that can be set for
|
||||
// the server. This is intended to be loaded from a toml-encoded config file.
|
||||
type Config struct {
|
||||
libkpod.Config
|
||||
lib.Config
|
||||
APIConfig
|
||||
}
|
||||
|
||||
|
@ -37,11 +37,11 @@ type APIConfig struct {
|
|||
// conversions.
|
||||
type tomlConfig struct {
|
||||
Crio struct {
|
||||
libkpod.RootConfig
|
||||
API struct{ APIConfig } `toml:"api"`
|
||||
Runtime struct{ libkpod.RuntimeConfig } `toml:"runtime"`
|
||||
Image struct{ libkpod.ImageConfig } `toml:"image"`
|
||||
Network struct{ libkpod.NetworkConfig } `toml:"network"`
|
||||
lib.RootConfig
|
||||
API struct{ APIConfig } `toml:"api"`
|
||||
Runtime struct{ lib.RuntimeConfig } `toml:"runtime"`
|
||||
Image struct{ lib.ImageConfig } `toml:"image"`
|
||||
Network struct{ lib.NetworkConfig } `toml:"network"`
|
||||
} `toml:"crio"`
|
||||
}
|
||||
|
||||
|
@ -102,9 +102,9 @@ func (c *Config) ToFile(path string) error {
|
|||
// DefaultConfig returns the default configuration for crio.
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
Config: *libkpod.DefaultConfig(),
|
||||
Config: *lib.DefaultConfig(),
|
||||
APIConfig: APIConfig{
|
||||
Listen: "/var/run/crio.sock",
|
||||
Listen: "/var/run/crio/crio.sock",
|
||||
StreamAddress: "",
|
||||
StreamPort: "10010",
|
||||
},
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-incubator/cri-o/lib"
|
||||
)
|
||||
|
||||
const fixturePath = "fixtures/crio.conf"
|
||||
|
||||
func must(t *testing.T, err error) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func assertAllFieldsEquality(t *testing.T, c Config) {
|
||||
testCases := []struct {
|
||||
fieldValue, expected interface{}
|
||||
}{
|
||||
{c.RootConfig.Root, "/var/lib/containers/storage"},
|
||||
{c.RootConfig.RunRoot, "/var/run/containers/storage"},
|
||||
{c.RootConfig.Storage, "overlay"},
|
||||
{c.RootConfig.StorageOptions[0], "overlay.override_kernel_check=1"},
|
||||
|
||||
{c.APIConfig.Listen, "/var/run/crio.sock"},
|
||||
{c.APIConfig.StreamPort, "10010"},
|
||||
{c.APIConfig.StreamAddress, "localhost"},
|
||||
|
||||
{c.RuntimeConfig.Runtime, "/usr/local/bin/runc"},
|
||||
{c.RuntimeConfig.RuntimeUntrustedWorkload, "untrusted"},
|
||||
{c.RuntimeConfig.DefaultWorkloadTrust, "trusted"},
|
||||
{c.RuntimeConfig.Conmon, "/usr/local/libexec/crio/conmon"},
|
||||
{c.RuntimeConfig.ConmonEnv[0], "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},
|
||||
{c.RuntimeConfig.SELinux, true},
|
||||
{c.RuntimeConfig.SeccompProfile, "/etc/crio/seccomp.json"},
|
||||
{c.RuntimeConfig.ApparmorProfile, "crio-default"},
|
||||
{c.RuntimeConfig.CgroupManager, "cgroupfs"},
|
||||
{c.RuntimeConfig.PidsLimit, int64(1024)},
|
||||
|
||||
{c.ImageConfig.DefaultTransport, "docker://"},
|
||||
{c.ImageConfig.PauseImage, "kubernetes/pause"},
|
||||
{c.ImageConfig.PauseCommand, "/pause"},
|
||||
{c.ImageConfig.SignaturePolicyPath, "/tmp"},
|
||||
{c.ImageConfig.ImageVolumes, lib.ImageVolumesType("mkdir")},
|
||||
{c.ImageConfig.InsecureRegistries[0], "insecure-registry:1234"},
|
||||
{c.ImageConfig.Registries[0], "registry:4321"},
|
||||
|
||||
{c.NetworkConfig.NetworkDir, "/etc/cni/net.d/"},
|
||||
{c.NetworkConfig.PluginDir, "/opt/cni/bin/"},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
if tc.fieldValue != tc.expected {
|
||||
t.Errorf(`Expecting: "%s", got: "%s"`, tc.expected, tc.fieldValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateFromFile(t *testing.T) {
|
||||
c := Config{}
|
||||
|
||||
must(t, c.UpdateFromFile(fixturePath))
|
||||
|
||||
assertAllFieldsEquality(t, c)
|
||||
}
|
||||
|
||||
func TestToFile(t *testing.T) {
|
||||
configFromFixture := Config{}
|
||||
|
||||
must(t, configFromFixture.UpdateFromFile(fixturePath))
|
||||
|
||||
f, err := ioutil.TempFile("", "crio.conf")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
must(t, configFromFixture.ToFile(f.Name()))
|
||||
|
||||
writtenConfig := Config{}
|
||||
err = writtenConfig.UpdateFromFile(f.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assertAllFieldsEquality(t, writtenConfig)
|
||||
}
|
|
@ -14,12 +14,11 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
dockermounts "github.com/docker/docker/pkg/mount"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/pkg/symlink"
|
||||
"github.com/kubernetes-incubator/cri-o/libkpod"
|
||||
"github.com/kubernetes-incubator/cri-o/libkpod/sandbox"
|
||||
"github.com/kubernetes-incubator/cri-o/lib"
|
||||
"github.com/kubernetes-incubator/cri-o/lib/sandbox"
|
||||
"github.com/kubernetes-incubator/cri-o/oci"
|
||||
"github.com/kubernetes-incubator/cri-o/pkg/annotations"
|
||||
"github.com/kubernetes-incubator/cri-o/pkg/storage"
|
||||
|
@ -234,11 +233,11 @@ func addImageVolumes(rootfs string, s *Server, containerInfo *storage.ContainerI
|
|||
return nil, err
|
||||
}
|
||||
switch s.config.ImageVolumes {
|
||||
case libkpod.ImageVolumesMkdir:
|
||||
case lib.ImageVolumesMkdir:
|
||||
if err1 := os.MkdirAll(fp, 0644); err1 != nil {
|
||||
return nil, err1
|
||||
}
|
||||
case libkpod.ImageVolumesBind:
|
||||
case lib.ImageVolumesBind:
|
||||
volumeDirName := stringid.GenerateNonCryptoID()
|
||||
src := filepath.Join(containerInfo.RunDir, "mounts", volumeDirName)
|
||||
if err1 := os.MkdirAll(src, 0644); err1 != nil {
|
||||
|
@ -258,7 +257,7 @@ func addImageVolumes(rootfs string, s *Server, containerInfo *storage.ContainerI
|
|||
Options: []string{"rw"},
|
||||
})
|
||||
|
||||
case libkpod.ImageVolumesIgnore:
|
||||
case lib.ImageVolumesIgnore:
|
||||
logrus.Debugf("Ignoring volume %v", dest)
|
||||
default:
|
||||
logrus.Fatalf("Unrecognized image volumes setting")
|
||||
|
@ -424,18 +423,21 @@ func buildOCIProcessArgs(containerKubeConfig *pb.ContainerConfig, imageOCIConfig
|
|||
}
|
||||
|
||||
// addOCIHook look for hooks programs installed in hooksDirPath and add them to spec
|
||||
func addOCIHook(specgen *generate.Generator, hook libkpod.HookParams) error {
|
||||
func addOCIHook(specgen *generate.Generator, hook lib.HookParams) error {
|
||||
logrus.Debugf("AddOCIHook", hook)
|
||||
for _, stage := range hook.Stage {
|
||||
h := rspec.Hook{
|
||||
Path: hook.Hook,
|
||||
Args: append([]string{hook.Hook}, hook.Arguments...),
|
||||
Env: []string{fmt.Sprintf("stage=%s", stage)},
|
||||
}
|
||||
switch stage {
|
||||
case "prestart":
|
||||
specgen.AddPreStartHook(hook.Hook, []string{hook.Hook, "prestart"})
|
||||
|
||||
specgen.AddPreStartHook(h)
|
||||
case "poststart":
|
||||
specgen.AddPostStartHook(hook.Hook, []string{hook.Hook, "poststart"})
|
||||
|
||||
specgen.AddPostStartHook(h)
|
||||
case "poststop":
|
||||
specgen.AddPostStopHook(hook.Hook, []string{hook.Hook, "poststop"})
|
||||
specgen.AddPostStopHook(h)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -488,6 +490,110 @@ func setupContainerUser(specgen *generate.Generator, rootfs string, sc *pb.Linux
|
|||
return nil
|
||||
}
|
||||
|
||||
// setupCapabilities sets process.capabilities in the OCI runtime config.
|
||||
func setupCapabilities(specgen *generate.Generator, capabilities *pb.Capability) error {
|
||||
if capabilities == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
toCAPPrefixed := func(cap string) string {
|
||||
if !strings.HasPrefix(strings.ToLower(cap), "cap_") {
|
||||
return "CAP_" + strings.ToUpper(cap)
|
||||
}
|
||||
return cap
|
||||
}
|
||||
|
||||
// Add/drop all capabilities if "all" is specified, so that
|
||||
// following individual add/drop could still work. E.g.
|
||||
// AddCapabilities: []string{"ALL"}, DropCapabilities: []string{"CHOWN"}
|
||||
// will be all capabilities without `CAP_CHOWN`.
|
||||
// see https://github.com/kubernetes/kubernetes/issues/51980
|
||||
if inStringSlice(capabilities.GetAddCapabilities(), "ALL") {
|
||||
for _, c := range getOCICapabilitiesList() {
|
||||
if err := specgen.AddProcessCapabilityAmbient(c); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := specgen.AddProcessCapabilityBounding(c); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := specgen.AddProcessCapabilityEffective(c); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := specgen.AddProcessCapabilityInheritable(c); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := specgen.AddProcessCapabilityPermitted(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if inStringSlice(capabilities.GetDropCapabilities(), "ALL") {
|
||||
for _, c := range getOCICapabilitiesList() {
|
||||
if err := specgen.DropProcessCapabilityAmbient(c); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := specgen.DropProcessCapabilityBounding(c); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := specgen.DropProcessCapabilityEffective(c); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := specgen.DropProcessCapabilityInheritable(c); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := specgen.DropProcessCapabilityPermitted(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, cap := range capabilities.GetAddCapabilities() {
|
||||
if strings.ToUpper(cap) == "ALL" {
|
||||
continue
|
||||
}
|
||||
capPrefixed := toCAPPrefixed(cap)
|
||||
if err := specgen.AddProcessCapabilityAmbient(capPrefixed); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := specgen.AddProcessCapabilityBounding(capPrefixed); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := specgen.AddProcessCapabilityEffective(capPrefixed); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := specgen.AddProcessCapabilityInheritable(capPrefixed); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := specgen.AddProcessCapabilityPermitted(capPrefixed); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, cap := range capabilities.GetDropCapabilities() {
|
||||
if strings.ToUpper(cap) == "ALL" {
|
||||
continue
|
||||
}
|
||||
capPrefixed := toCAPPrefixed(cap)
|
||||
if err := specgen.DropProcessCapabilityAmbient(capPrefixed); err != nil {
|
||||
return fmt.Errorf("failed to drop cap %s %v", capPrefixed, err)
|
||||
}
|
||||
if err := specgen.DropProcessCapabilityBounding(capPrefixed); err != nil {
|
||||
return fmt.Errorf("failed to drop cap %s %v", capPrefixed, err)
|
||||
}
|
||||
if err := specgen.DropProcessCapabilityEffective(capPrefixed); err != nil {
|
||||
return fmt.Errorf("failed to drop cap %s %v", capPrefixed, err)
|
||||
}
|
||||
if err := specgen.DropProcessCapabilityInheritable(capPrefixed); err != nil {
|
||||
return fmt.Errorf("failed to drop cap %s %v", capPrefixed, err)
|
||||
}
|
||||
if err := specgen.DropProcessCapabilityPermitted(capPrefixed); err != nil {
|
||||
return fmt.Errorf("failed to drop cap %s %v", capPrefixed, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func hostNetwork(containerConfig *pb.ContainerConfig) bool {
|
||||
securityContext := containerConfig.GetLinux().GetSecurityContext()
|
||||
if securityContext == nil || securityContext.GetNamespaceOptions() == nil {
|
||||
|
@ -563,7 +669,11 @@ func (s *Server) CreateContainer(ctx context.Context, req *pb.CreateContainerReq
|
|||
return nil, fmt.Errorf("CreateContainerRequest.ContainerConfig is nil")
|
||||
}
|
||||
|
||||
name := containerConfig.GetMetadata().Name
|
||||
if containerConfig.GetMetadata() == nil {
|
||||
return nil, fmt.Errorf("CreateContainerRequest.ContainerConfig.Metadata is nil")
|
||||
}
|
||||
|
||||
name := containerConfig.GetMetadata().GetName()
|
||||
if name == "" {
|
||||
return nil, fmt.Errorf("CreateContainerRequest.ContainerConfig.Name is empty")
|
||||
}
|
||||
|
@ -616,7 +726,7 @@ func (s *Server) CreateContainer(ctx context.Context, req *pb.CreateContainerReq
|
|||
func (s *Server) setupOCIHooks(specgen *generate.Generator, sb *sandbox.Sandbox, containerConfig *pb.ContainerConfig, command string) error {
|
||||
mounts := containerConfig.GetMounts()
|
||||
addedHooks := map[string]struct{}{}
|
||||
addHook := func(hook libkpod.HookParams) error {
|
||||
addHook := func(hook lib.HookParams) error {
|
||||
// Only add a hook once
|
||||
if _, ok := addedHooks[hook.Hook]; !ok {
|
||||
if err := addOCIHook(specgen, hook); err != nil {
|
||||
|
@ -711,8 +821,14 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
|||
}
|
||||
specgen.AddAnnotation(annotations.Volumes, string(volumesJSON))
|
||||
|
||||
mnt := rspec.Mount{
|
||||
Destination: "/sys/fs/cgroup",
|
||||
Type: "cgroup",
|
||||
Source: "cgroup",
|
||||
Options: []string{"nosuid", "noexec", "nodev", "relatime", "ro"},
|
||||
}
|
||||
// Add cgroup mount so container process can introspect its own limits
|
||||
specgen.AddCgroupsMount("ro")
|
||||
specgen.AddMount(mnt)
|
||||
|
||||
if err := addDevices(sb, containerConfig, &specgen); err != nil {
|
||||
return nil, err
|
||||
|
@ -740,7 +856,8 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
|||
|
||||
// set this container's apparmor profile if it is set by sandbox
|
||||
if s.appArmorEnabled && !privileged {
|
||||
appArmorProfileName := s.getAppArmorProfileName(sb.Annotations(), metadata.Name)
|
||||
|
||||
appArmorProfileName := s.getAppArmorProfileName(containerConfig.GetLinux().GetSecurityContext().GetApparmorProfile())
|
||||
if appArmorProfileName != "" {
|
||||
// reload default apparmor profile if it is unloaded.
|
||||
if s.appArmorProfile == apparmor.DefaultApparmorProfile {
|
||||
|
@ -751,6 +868,7 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
|||
|
||||
specgen.SetProcessApparmorProfile(appArmorProfileName)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
logPath := containerConfig.LogPath
|
||||
|
@ -784,28 +902,13 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
|||
if linux != nil {
|
||||
resources := linux.GetResources()
|
||||
if resources != nil {
|
||||
cpuPeriod := resources.CpuPeriod
|
||||
if cpuPeriod != 0 {
|
||||
specgen.SetLinuxResourcesCPUPeriod(uint64(cpuPeriod))
|
||||
}
|
||||
|
||||
cpuQuota := resources.CpuQuota
|
||||
if cpuQuota != 0 {
|
||||
specgen.SetLinuxResourcesCPUQuota(cpuQuota)
|
||||
}
|
||||
|
||||
cpuShares := resources.CpuShares
|
||||
if cpuShares != 0 {
|
||||
specgen.SetLinuxResourcesCPUShares(uint64(cpuShares))
|
||||
}
|
||||
|
||||
memoryLimit := resources.MemoryLimitInBytes
|
||||
if memoryLimit != 0 {
|
||||
specgen.SetLinuxResourcesMemoryLimit(memoryLimit)
|
||||
}
|
||||
|
||||
oomScoreAdj := resources.OomScoreAdj
|
||||
specgen.SetProcessOOMScoreAdj(int(oomScoreAdj))
|
||||
specgen.SetLinuxResourcesCPUPeriod(uint64(resources.GetCpuPeriod()))
|
||||
specgen.SetLinuxResourcesCPUQuota(resources.GetCpuQuota())
|
||||
specgen.SetLinuxResourcesCPUShares(uint64(resources.GetCpuShares()))
|
||||
specgen.SetLinuxResourcesMemoryLimit(resources.GetMemoryLimitInBytes())
|
||||
specgen.SetProcessOOMScoreAdj(int(resources.GetOomScoreAdj()))
|
||||
specgen.SetLinuxResourcesCPUCpus(resources.GetCpusetCpus())
|
||||
specgen.SetLinuxResourcesCPUMems(resources.GetCpusetMems())
|
||||
}
|
||||
|
||||
var cgPath string
|
||||
|
@ -824,57 +927,13 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
|||
}
|
||||
specgen.SetLinuxCgroupsPath(cgPath)
|
||||
|
||||
capabilities := linux.GetSecurityContext().GetCapabilities()
|
||||
if privileged {
|
||||
// this is setting correct capabilities as well for privileged mode
|
||||
specgen.SetupPrivileged(true)
|
||||
setOCIBindMountsPrivileged(&specgen)
|
||||
} else {
|
||||
toCAPPrefixed := func(cap string) string {
|
||||
if !strings.HasPrefix(strings.ToLower(cap), "cap_") {
|
||||
return "CAP_" + strings.ToUpper(cap)
|
||||
}
|
||||
return cap
|
||||
}
|
||||
|
||||
// Add/drop all capabilities if "all" is specified, so that
|
||||
// following individual add/drop could still work. E.g.
|
||||
// AddCapabilities: []string{"ALL"}, DropCapabilities: []string{"CHOWN"}
|
||||
// will be all capabilities without `CAP_CHOWN`.
|
||||
// see https://github.com/kubernetes/kubernetes/issues/51980
|
||||
if inStringSlice(capabilities.GetAddCapabilities(), "ALL") {
|
||||
for _, c := range getOCICapabilitiesList() {
|
||||
if err := specgen.AddProcessCapability(c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if inStringSlice(capabilities.GetDropCapabilities(), "ALL") {
|
||||
for _, c := range getOCICapabilitiesList() {
|
||||
if err := specgen.DropProcessCapability(c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if capabilities != nil {
|
||||
for _, cap := range capabilities.GetAddCapabilities() {
|
||||
if strings.ToUpper(cap) == "ALL" {
|
||||
continue
|
||||
}
|
||||
if err := specgen.AddProcessCapability(toCAPPrefixed(cap)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, cap := range capabilities.GetDropCapabilities() {
|
||||
if strings.ToUpper(cap) == "ALL" {
|
||||
continue
|
||||
}
|
||||
if err := specgen.DropProcessCapability(toCAPPrefixed(cap)); err != nil {
|
||||
return nil, fmt.Errorf("failed to drop cap %s %v", toCAPPrefixed(cap), err)
|
||||
}
|
||||
}
|
||||
err = setupCapabilities(&specgen, linux.GetSecurityContext().GetCapabilities())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
specgen.SetProcessSelinuxLabel(processLabel)
|
||||
|
@ -889,6 +948,7 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
|||
"/proc/timer_list",
|
||||
"/proc/timer_stats",
|
||||
"/proc/sched_debug",
|
||||
"/proc/scsi",
|
||||
"/sys/firmware",
|
||||
} {
|
||||
specgen.AddLinuxMaskedPaths(mp)
|
||||
|
@ -921,9 +981,15 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Do not share pid ns for now
|
||||
if containerConfig.GetLinux().GetSecurityContext().GetNamespaceOptions().GetHostPid() {
|
||||
// kubernetes PodSpec specify to use Host PID namespace
|
||||
specgen.RemoveLinuxNamespace(string(rspec.PIDNamespace))
|
||||
} else if s.config.EnableSharedPIDNamespace {
|
||||
// share Pod PID namespace
|
||||
pidNsPath := fmt.Sprintf("/proc/%d/ns/pid", podInfraState.Pid)
|
||||
if err := specgen.AddOrReplaceLinuxNamespace(string(rspec.PIDNamespace), pidNsPath); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
netNsPath := sb.NetNsPath()
|
||||
|
@ -948,53 +1014,37 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
|||
}
|
||||
images, err := s.StorageImageServer().ResolveNames(image)
|
||||
if err != nil {
|
||||
// This means we got an image ID
|
||||
if strings.Contains(err.Error(), "cannot specify 64-byte hexadecimal strings") {
|
||||
if err == storage.ErrCannotParseImageID {
|
||||
images = append(images, image)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
image = images[0]
|
||||
|
||||
// Get imageName and imageRef that are requested in container status
|
||||
imageName := image
|
||||
status, err := s.StorageImageServer().ImageStatus(s.ImageContext(), image)
|
||||
// Get imageName and imageRef that are later requested in container status
|
||||
status, err := s.StorageImageServer().ImageStatus(s.ImageContext(), images[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
imageName := status.Name
|
||||
imageRef := status.ID
|
||||
//
|
||||
// TODO: https://github.com/kubernetes-incubator/cri-o/issues/531
|
||||
//
|
||||
//for _, n := range status.Names {
|
||||
//r, err := reference.ParseNormalizedNamed(n)
|
||||
//if err != nil {
|
||||
//return nil, fmt.Errorf("failed to normalize image name for ImageRef: %v", err)
|
||||
//}
|
||||
//if digested, isDigested := r.(reference.Canonical); isDigested {
|
||||
//imageRef = reference.FamiliarString(digested)
|
||||
//break
|
||||
//}
|
||||
//}
|
||||
for _, n := range status.Names {
|
||||
r, err := reference.ParseNormalizedNamed(n)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to normalize image name for Image: %v", err)
|
||||
}
|
||||
if tagged, isTagged := r.(reference.Tagged); isTagged {
|
||||
imageName = reference.FamiliarString(tagged)
|
||||
break
|
||||
}
|
||||
if len(status.RepoDigests) > 0 {
|
||||
imageRef = status.RepoDigests[0]
|
||||
}
|
||||
|
||||
specgen.AddAnnotation(annotations.Image, image)
|
||||
specgen.AddAnnotation(annotations.ImageName, imageName)
|
||||
specgen.AddAnnotation(annotations.ImageRef, imageRef)
|
||||
specgen.AddAnnotation(annotations.IP, sb.IP())
|
||||
|
||||
mnt = rspec.Mount{
|
||||
Type: "bind",
|
||||
Source: sb.ShmPath(),
|
||||
Destination: "/etc/shm",
|
||||
Options: []string{"rw", "bind"},
|
||||
}
|
||||
// bind mount the pod shm
|
||||
specgen.AddBindMount(sb.ShmPath(), "/dev/shm", []string{"rw"})
|
||||
specgen.AddMount(mnt)
|
||||
|
||||
options := []string{"rw"}
|
||||
if readOnlyRootfs {
|
||||
|
@ -1005,8 +1055,14 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
mnt = rspec.Mount{
|
||||
Type: "bind",
|
||||
Source: sb.ResolvPath(),
|
||||
Destination: "/etc/resolv.conf",
|
||||
Options: append(options, "bind"),
|
||||
}
|
||||
// bind mount the pod resolver file
|
||||
specgen.AddBindMount(sb.ResolvPath(), "/etc/resolv.conf", options)
|
||||
specgen.AddMount(mnt)
|
||||
}
|
||||
|
||||
if sb.HostnamePath() != "" {
|
||||
|
@ -1014,12 +1070,24 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
specgen.AddBindMount(sb.HostnamePath(), "/etc/hostname", options)
|
||||
mnt = rspec.Mount{
|
||||
Type: "bind",
|
||||
Source: sb.HostnamePath(),
|
||||
Destination: "/etc/hostname",
|
||||
Options: append(options, "bind"),
|
||||
}
|
||||
specgen.AddMount(mnt)
|
||||
}
|
||||
|
||||
// Bind mount /etc/hosts for host networking containers
|
||||
if hostNetwork(containerConfig) {
|
||||
specgen.AddBindMount("/etc/hosts", "/etc/hosts", options)
|
||||
mnt = rspec.Mount{
|
||||
Type: "bind",
|
||||
Source: "/etc/hosts",
|
||||
Destination: "/etc/hosts",
|
||||
Options: append(options, "bind"),
|
||||
}
|
||||
specgen.AddMount(mnt)
|
||||
}
|
||||
|
||||
// Set hostname and add env for hostname
|
||||
|
@ -1035,7 +1103,6 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
|||
specgen.AddAnnotation(annotations.TTY, fmt.Sprintf("%v", containerConfig.Tty))
|
||||
specgen.AddAnnotation(annotations.Stdin, fmt.Sprintf("%v", containerConfig.Stdin))
|
||||
specgen.AddAnnotation(annotations.StdinOnce, fmt.Sprintf("%v", containerConfig.StdinOnce))
|
||||
specgen.AddAnnotation(annotations.Image, image)
|
||||
specgen.AddAnnotation(annotations.ResolvPath, sb.InfraContainer().CrioAnnotations()[annotations.ResolvPath])
|
||||
|
||||
created := time.Now()
|
||||
|
@ -1066,13 +1133,12 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
|||
}
|
||||
}
|
||||
specgen.AddAnnotation(annotations.SeccompProfilePath, spp)
|
||||
// TODO(runcom): add spp to container...
|
||||
|
||||
metaname := metadata.Name
|
||||
attempt := metadata.Attempt
|
||||
containerInfo, err := s.StorageRuntimeServer().CreateContainer(s.ImageContext(),
|
||||
sb.Name(), sb.ID(),
|
||||
image, image,
|
||||
image, status.ID,
|
||||
containerName, containerID,
|
||||
metaname,
|
||||
attempt,
|
||||
|
@ -1081,6 +1147,14 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
err2 := s.StorageRuntimeServer().DeleteContainer(containerInfo.ID)
|
||||
if err2 != nil {
|
||||
logrus.Warnf("Failed to cleanup container directory: %v", err2)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
mountPoint, err := s.StorageRuntimeServer().StartContainer(containerID)
|
||||
if err != nil {
|
||||
|
@ -1090,7 +1164,8 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
|||
|
||||
containerImageConfig := containerInfo.Config
|
||||
if containerImageConfig == nil {
|
||||
return nil, fmt.Errorf("empty image config for %s", image)
|
||||
err = fmt.Errorf("empty image config for %s", image)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if containerImageConfig.Config.StopSignal != "" {
|
||||
|
@ -1110,30 +1185,10 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
|||
}
|
||||
specgen.SetProcessArgs(processArgs)
|
||||
|
||||
// Add environment variables from CRI and image config
|
||||
envs := containerConfig.GetEnvs()
|
||||
if envs != nil {
|
||||
for _, item := range envs {
|
||||
key := item.Key
|
||||
value := item.Value
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
specgen.AddProcessEnv(key, value)
|
||||
}
|
||||
}
|
||||
if containerImageConfig != nil {
|
||||
for _, item := range containerImageConfig.Config.Env {
|
||||
parts := strings.SplitN(item, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("invalid env from image: %s", item)
|
||||
}
|
||||
|
||||
if parts[0] == "" {
|
||||
continue
|
||||
}
|
||||
specgen.AddProcessEnv(parts[0], parts[1])
|
||||
}
|
||||
envs := mergeEnvs(containerImageConfig, containerConfig.GetEnvs())
|
||||
for _, e := range envs {
|
||||
parts := strings.SplitN(e, "=", 2)
|
||||
specgen.AddProcessEnv(parts[0], parts[1])
|
||||
}
|
||||
|
||||
// Set working directory
|
||||
|
@ -1174,7 +1229,13 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
|||
sort.Sort(orderedMounts(mounts))
|
||||
|
||||
for _, m := range mounts {
|
||||
specgen.AddBindMount(m.Source, m.Destination, m.Options)
|
||||
mnt = rspec.Mount{
|
||||
Type: "bind",
|
||||
Source: m.Source,
|
||||
Destination: m.Destination,
|
||||
Options: append(m.Options, "bind"),
|
||||
}
|
||||
specgen.AddMount(mnt)
|
||||
}
|
||||
|
||||
if err := s.setupOCIHooks(&specgen, sb, containerConfig, processArgs[0]); err != nil {
|
||||
|
@ -1213,6 +1274,7 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
|
|||
}
|
||||
container.SetSpec(specgen.Spec())
|
||||
container.SetMountPoint(mountPoint)
|
||||
container.SetSeccompProfilePath(spp)
|
||||
|
||||
for _, cv := range containerVolumes {
|
||||
container.AddVolume(cv)
|
||||
|
@ -1253,9 +1315,7 @@ func (s *Server) setupSeccomp(specgen *generate.Generator, profile string) error
|
|||
}
|
||||
|
||||
// getAppArmorProfileName gets the profile name for the given container.
|
||||
func (s *Server) getAppArmorProfileName(annotations map[string]string, ctrName string) string {
|
||||
profile := apparmor.GetProfileNameFromPodAnnotations(annotations, ctrName)
|
||||
|
||||
func (s *Server) getAppArmorProfileName(profile string) string {
|
||||
if profile == "" {
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -53,12 +53,15 @@ func (ss streamService) Exec(containerID string, cmd []string, stdin io.Reader,
|
|||
return fmt.Errorf("container is not created or running")
|
||||
}
|
||||
|
||||
args := []string{"exec"}
|
||||
if tty {
|
||||
args = append(args, "-t")
|
||||
processFile, err := oci.PrepareProcessExec(c, cmd, tty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(processFile.Name())
|
||||
|
||||
args := []string{"exec"}
|
||||
args = append(args, "--process", processFile.Name())
|
||||
args = append(args, c.ID())
|
||||
args = append(args, cmd...)
|
||||
execCmd := exec.Command(ss.runtimeServer.Runtime().Path(c), args...)
|
||||
var cmdErr error
|
||||
if tty {
|
||||
|
|
|
@ -97,6 +97,7 @@ func (s *Server) ListContainers(ctx context.Context, req *pb.ListContainersReque
|
|||
Metadata: ctr.Metadata(),
|
||||
Annotations: ctr.Annotations(),
|
||||
Image: img,
|
||||
ImageRef: ctr.ImageRef(),
|
||||
}
|
||||
|
||||
switch cState.Status {
|
||||
|
|
|
@ -3,6 +3,7 @@ package server
|
|||
import (
|
||||
"time"
|
||||
|
||||
"github.com/containers/image/types"
|
||||
"github.com/kubernetes-incubator/cri-o/oci"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
|
@ -38,7 +39,10 @@ func (s *Server) ContainerStatus(ctx context.Context, req *pb.ContainerStatusReq
|
|||
ImageRef: c.ImageRef(),
|
||||
},
|
||||
}
|
||||
resp.Status.Image = &pb.ImageSpec{Image: c.ImageName()}
|
||||
resp.Status.Image = &pb.ImageSpec{Image: c.Image()}
|
||||
if status, err := s.StorageImageServer().ImageStatus(&types.SystemContext{}, c.ImageRef()); err == nil {
|
||||
resp.Status.Image.Image = status.Name
|
||||
}
|
||||
|
||||
mounts := []*pb.Mount{}
|
||||
for _, cv := range c.Volumes() {
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
[crio]
|
||||
root = "/var/lib/containers/storage"
|
||||
runroot = "/var/run/containers/storage"
|
||||
storage_driver = "overlay"
|
||||
storage_option = ["overlay.override_kernel_check=1"]
|
||||
|
||||
[crio.api]
|
||||
listen = "/var/run/crio.sock"
|
||||
stream_address = "localhost"
|
||||
stream_port = "10010"
|
||||
|
||||
[crio.runtime]
|
||||
runtime = "/usr/local/bin/runc"
|
||||
runtime_untrusted_workload = "untrusted"
|
||||
default_workload_trust = "trusted"
|
||||
conmon = "/usr/local/libexec/crio/conmon"
|
||||
conmon_env = [
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
]
|
||||
selinux = true
|
||||
seccomp_profile = "/etc/crio/seccomp.json"
|
||||
apparmor_profile = "crio-default"
|
||||
cgroup_manager = "cgroupfs"
|
||||
pids_limit = 1024
|
||||
|
||||
[crio.image]
|
||||
default_transport = "docker://"
|
||||
pause_image = "kubernetes/pause"
|
||||
pause_command = "/pause"
|
||||
signature_policy = "/tmp"
|
||||
image_volumes = "mkdir"
|
||||
insecure_registries = [
|
||||
"insecure-registry:1234",
|
||||
]
|
||||
registries = [
|
||||
"registry:4321",
|
||||
]
|
||||
|
||||
[crio.network]
|
||||
network_dir = "/etc/cni/net.d/"
|
||||
plugin_dir = "/opt/cni/bin/"
|
|
@ -0,0 +1,4 @@
|
|||
search 192.30.253.113 192.30.252.153
|
||||
nameserver cri-o.io
|
||||
nameserver github.com
|
||||
options timeout:5 attempts:3
|
|
@ -0,0 +1 @@
|
|||
secretDataA
|
|
@ -0,0 +1 @@
|
|||
secretDataB
|
|
@ -33,14 +33,16 @@ func (s *Server) ListImages(ctx context.Context, req *pb.ListImagesRequest) (res
|
|||
for _, result := range results {
|
||||
if result.Size != nil {
|
||||
resp.Images = append(resp.Images, &pb.Image{
|
||||
Id: result.ID,
|
||||
RepoTags: result.Names,
|
||||
Size_: *result.Size,
|
||||
Id: result.ID,
|
||||
RepoTags: result.RepoTags,
|
||||
RepoDigests: result.RepoDigests,
|
||||
Size_: *result.Size,
|
||||
})
|
||||
} else {
|
||||
resp.Images = append(resp.Images, &pb.Image{
|
||||
Id: result.ID,
|
||||
RepoTags: result.Names,
|
||||
Id: result.ID,
|
||||
RepoTags: result.RepoTags,
|
||||
RepoDigests: result.RepoDigests,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,8 +104,16 @@ func (s *Server) PullImage(ctx context.Context, req *pb.PullImageRequest) (resp
|
|||
if pulled == "" && err != nil {
|
||||
return nil, err
|
||||
}
|
||||
status, err := s.StorageImageServer().ImageStatus(s.ImageContext(), pulled)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
imageRef := status.ID
|
||||
if len(status.RepoDigests) > 0 {
|
||||
imageRef = status.RepoDigests[0]
|
||||
}
|
||||
resp = &pb.PullImageResponse{
|
||||
ImageRef: pulled,
|
||||
ImageRef: imageRef,
|
||||
}
|
||||
logrus.Debugf("PullImageResponse: %+v", resp)
|
||||
return resp, nil
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue