diff options
80 files changed, 1238 insertions, 286 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 00cf1ea5c..a019f4072 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -215,6 +215,35 @@ build_each_commit_task: on_failure: failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh' +build_without_cgo: + + depends_on: + - "gating" + - "vendor" + - "varlink_api" + + # $CIRRUS_BASE_BRANCH is only set when testing a PR + only_if: $CIRRUS_BRANCH != 'master' && + $CIRRUS_CHANGE_MESSAGE !=~ '.*\*\*\*\s*CIRRUS:\s*TEST\s*IMAGES\s*\*\*\*.*' + + gce_instance: + image_project: "libpod-218412" + zone: "us-central1-a" # Required by Cirrus for the time being + cpu: 8 + memory: "8Gb" + disk: 200 + image_name: "${FEDORA_CACHE_IMAGE_NAME}" + + timeout_in: 30m + + setup_environment_script: '$SCRIPT_BASE/setup_environment.sh |& ${TIMESTAMP}' + build_without_cgo_script: + - 'source $SCRIPT_BASE/lib.sh' + - 'make build-no-cgo' + + on_failure: + failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh' + # Update metadata on VM images referenced by this repository state meta_task: @@ -224,6 +253,7 @@ meta_task: - "vendor" - "varlink_api" - "build_each_commit" + - "build_without_cgo" container: image: "quay.io/libpod/imgts:latest" # see contrib/imgts @@ -257,6 +287,7 @@ testing_task: - "vendor" - "varlink_api" - "build_each_commit" + - "build_without_cgo" # Only test build cache-images, if that's what's requested only_if: $CIRRUS_CHANGE_MESSAGE !=~ '.*\*\*\*\s*CIRRUS:\s*TEST\s*IMAGES\s*\*\*\*.*' @@ -298,6 +329,7 @@ special_testing_rootless_task: - "varlink_api" - "vendor" - "build_each_commit" + - "build_without_cgo" only_if: $CIRRUS_CHANGE_MESSAGE !=~ '.*\*\*\*\s*CIRRUS:\s*TEST\s*IMAGES\s*\*\*\*.*' @@ -328,6 +360,7 @@ special_testing_in_podman_task: - "varlink_api" - "vendor" - "build_each_commit" + - "build_without_cgo" only_if: $CIRRUS_CHANGE_MESSAGE !=~ '.*\*\*\*\s*CIRRUS:\s*TEST\s*IMAGES\s*\*\*\*.*' @@ -433,6 +466,7 @@ success_task: - "special_testing_in_podman" - "test_build_cache_images" - "verify_test_built_images" + - "build_without_cgo" env: CIRRUS_WORKING_DIR: "/usr/src/libpod" @@ -2,7 +2,7 @@ export GO111MODULE=off GO ?= go DESTDIR ?= -EPOCH_TEST_COMMIT ?= 5b7086abda91f4301af3bfb642d416a22349c276 +EPOCH_TEST_COMMIT ?= 55e028a12ee003e057c65e376fe4b723d28ae52e HEAD ?= HEAD CHANGELOG_BASE ?= HEAD~ CHANGELOG_TARGET ?= HEAD @@ -399,6 +399,9 @@ build-all-new-commits: # Validate that all the commits build on top of $(GIT_BASE_BRANCH) git rebase $(GIT_BASE_BRANCH) -x make +build-no-cgo: + env BUILDTAGS="containers_image_openpgp containers_image_ostree_stub exclude_graphdriver_btrfs exclude_graphdriver_devicemapper exclude_disk_quota" CGO_ENABLED=0 $(MAKE) + vendor: export GO111MODULE=on \ $(GO) mod tidy && \ @@ -5,7 +5,7 @@ Libpod provides a library for applications looking to use the Container Pod concept, popularized by Kubernetes. Libpod also contains the Pod Manager tool `(Podman)`. Podman manages pods, containers, container images, and container volumes. -* [Latest Version: 1.4.0](https://github.com/containers/libpod/releases/latest) +* [Latest Version: 1.4.4](https://github.com/containers/libpod/releases/latest) * [Continuous Integration:](contrib/cirrus/README.md) [![Build Status](https://api.cirrus-ci.com/github/containers/libpod.svg)](https://cirrus-ci.com/github/containers/libpod/master) ## Overview and scope diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index e825e9a5c..69244bb09 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,17 @@ # Release Notes +## 1.4.4 +### Bugfixes +- Fixed a bug where rootless Podman would attempt to use the entire root configuration if no rootless configuration was present for the user, breaking rootless Podman for new installations +- Fixed a bug where rootless Podman's pause process would block SIGTERM, preventing graceful system shutdown and hanging until the system's init send SIGKILL +- Fixed a bug where running Podman as root with `sudo -E` would not work after running rootless Podman at least once +- Fixed a bug where options for `tmpfs` volumes added with the `--tmpfs` flag were being ignored +- Fixed a bug where images with no layers could not properly be displayed and removed by Podman +- Fixed a bug where locks were not properly freed on failure to create a container or pod + +### Misc +- Updated containers/storage to v1.12.13 + ## 1.4.3 ### Features - Podman now has greatly improved support for containers using multiple OCI runtimes. Containers now remember if they were created with a different runtime using `--runtime` and will always use that runtime diff --git a/changelog.txt b/changelog.txt index 3bc7720a8..51ac92979 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,39 @@ +- Changelog for v1.4.4 (2019-07-02) + * Fix release notes + * Ensure locks are freed when ctr/pod creation fails + * Update release notes for 1.4.4 + * stats: use runtime.NumCPU when percpu counters are not available + * cgroups: fix times conversion + * Update to containers/storage v1.12.13 + * rootless: do not join namespace if it has already euid == 0 + * Exclude SIGTERM from blocked signals for pause process. + * Remove umount command from remote client. + * rootless: enable linger if /run/user/UID not exists + * Makefile: set GO111MODULE=off + * libpod removal from main (phase 2) + * runtime: do not attempt to use global conf file + * runtime: use GetRootlessUID() to get rootless uid + * Remove refs to crio/conmon + * Handle images which contain no layers + * Add tests that we don't hit errors with layerless images + * stats: fix cgroup path for rootless containers + * pkg, cgroups: add initial support for cgroup v2 + * util: drop IsCgroup2UnifiedMode and use it from cgroups + * vendor: drop github.com/containerd/cgroups + * libpod: use pkg/cgroups instead of containerd/cgroups + * pkg: new package cgroups + * Remove unnecessary blackfriday dependency + * libpod: fix hang on container start and attach + * podman: clarify the format of --detach-keys argument + * libpod: specify a detach keys sequence in libpod.conf + * Fix parsing of the --tmpfs option + * Fix crash for when remote host IP or Username is not set in conf file & conf file exists. + * Bump gitvalidation epoch + * Bump to v1.4.4-dev + * Cirrus: More tests to verify cache_images + * Update release notes for 1.4.3 release + * remove libpod from main + - Changelog for v1.4.3 (2019-06-25) * Update 'generate kube' tests to verify YAML * Use a different method to retrieve YAML output in tests diff --git a/cmd/podman/build.go b/cmd/podman/build.go index 6e70c6540..5e2b1aa82 100644 --- a/cmd/podman/build.go +++ b/cmd/podman/build.go @@ -62,7 +62,7 @@ func init() { layerFlags := buildahcli.GetLayerFlags(&layerValues) flag = layerFlags.Lookup("layers") flag.Value.Set(useLayers()) - flag.DefValue = (useLayers()) + flag.DefValue = useLayers() flag = layerFlags.Lookup("force-rm") flag.Value.Set("true") flag.DefValue = "true" diff --git a/cmd/podman/cp.go b/cmd/podman/cp.go index 2d92fbb47..f6ac5f8f7 100644 --- a/cmd/podman/cp.go +++ b/cmd/podman/cp.go @@ -86,7 +86,7 @@ func copyBetweenHostAndContainer(runtime *libpod.Runtime, src string, dest strin return errors.Errorf("invalid arguments %s, %s you must specify paths", src, dest) } ctr := srcCtr - isFromHostToCtr := (ctr == nil) + isFromHostToCtr := ctr == nil if isFromHostToCtr { ctr = destCtr } @@ -307,7 +307,7 @@ func copy(src, destPath, dest string, idMappingOpts storage.IDMappingOptions, ch if err != nil && !os.IsNotExist(err) { return errors.Wrapf(err, "error checking directory %q", destdir) } - destDirIsExist := (err == nil) + destDirIsExist := err == nil if err = os.MkdirAll(destdir, 0755); err != nil { return errors.Wrapf(err, "error creating directory %q", destdir) } diff --git a/cmd/podman/exec.go b/cmd/podman/exec.go index accb15936..bf8de69fc 100644 --- a/cmd/podman/exec.go +++ b/cmd/podman/exec.go @@ -60,7 +60,7 @@ func execCmd(c *cliconfig.ExecValues) error { argStart = 0 } cmd := args[argStart:] - runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) + runtime, err := adapter.GetRuntimeNoStore(getContext(), &c.PodmanCommand) if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } diff --git a/cmd/podman/exists.go b/cmd/podman/exists.go index 3d001f3d1..1e052e25f 100644 --- a/cmd/podman/exists.go +++ b/cmd/podman/exists.go @@ -107,7 +107,7 @@ func containerExistsCmd(c *cliconfig.ContainerExistsValues) error { if len(args) > 1 || len(args) < 1 { return errors.New("you may only check for the existence of one container at a time") } - runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) + runtime, err := adapter.GetRuntimeNoStore(getContext(), &c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } @@ -126,7 +126,7 @@ func podExistsCmd(c *cliconfig.PodExistsValues) error { if len(args) > 1 || len(args) < 1 { return errors.New("you may only check for the existence of one pod at a time") } - runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) + runtime, err := adapter.GetRuntimeNoStore(getContext(), &c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") } diff --git a/cmd/podman/history.go b/cmd/podman/history.go index cebf99a9f..0998a023c 100644 --- a/cmd/podman/history.go +++ b/cmd/podman/history.go @@ -154,7 +154,7 @@ func getHistoryTemplateOutput(history []*image.History, opts historyOptions) (hi } if opts.human { - createdTime = units.HumanDuration(time.Since((*hist.Created))) + " ago" + createdTime = units.HumanDuration(time.Since(*hist.Created)) + " ago" outputSize = units.HumanSize(float64(hist.Size)) } else { createdTime = (hist.Created).Format(time.RFC3339) diff --git a/cmd/podman/images.go b/cmd/podman/images.go index 3f755efc1..33cf11ab7 100644 --- a/cmd/podman/images.go +++ b/cmd/podman/images.go @@ -280,7 +280,7 @@ func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerIma ID: imageID, Digest: img.Digest(), CreatedTime: createdTime, - Created: units.HumanDuration(time.Since((createdTime))) + " ago", + Created: units.HumanDuration(time.Since(createdTime)) + " ago", Size: sizeStr, } imagesOutput = append(imagesOutput, params) diff --git a/cmd/podman/libpodruntime/runtime.go b/cmd/podman/libpodruntime/runtime.go index 2d511f7f8..570288837 100644 --- a/cmd/podman/libpodruntime/runtime.go +++ b/cmd/podman/libpodruntime/runtime.go @@ -15,20 +15,25 @@ import ( // GetRuntimeMigrate gets a libpod runtime that will perform a migration of existing containers func GetRuntimeMigrate(ctx context.Context, c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { - return getRuntime(ctx, c, false, true) + return getRuntime(ctx, c, false, true, false) } // GetRuntimeRenumber gets a libpod runtime that will perform a lock renumber func GetRuntimeRenumber(ctx context.Context, c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { - return getRuntime(ctx, c, true, false) + return getRuntime(ctx, c, true, false, false) } // GetRuntime generates a new libpod runtime configured by command line options func GetRuntime(ctx context.Context, c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { - return getRuntime(ctx, c, false, false) + return getRuntime(ctx, c, false, false, false) } -func getRuntime(ctx context.Context, c *cliconfig.PodmanCommand, renumber bool, migrate bool) (*libpod.Runtime, error) { +// GetRuntimeNoStore generates a new libpod runtime configured by command line options +func GetRuntimeNoStore(ctx context.Context, c *cliconfig.PodmanCommand) (*libpod.Runtime, error) { + return getRuntime(ctx, c, false, false, true) +} + +func getRuntime(ctx context.Context, c *cliconfig.PodmanCommand, renumber, migrate, noStore bool) (*libpod.Runtime, error) { options := []libpod.RuntimeOption{} storageOpts := storage.StoreOptions{} storageSet := false @@ -89,6 +94,9 @@ func getRuntime(ctx context.Context, c *cliconfig.PodmanCommand, renumber bool, options = append(options, libpod.WithStorageConfig(storageOpts)) } + if !storageSet && noStore { + options = append(options, libpod.WithNoStore()) + } // TODO CLI flags for image config? // TODO CLI flag for signature policy? diff --git a/cmd/podman/pause.go b/cmd/podman/pause.go index 4bef20867..ee5fd352d 100644 --- a/cmd/podman/pause.go +++ b/cmd/podman/pause.go @@ -1,11 +1,10 @@ package main import ( - "os" - "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/adapter" + "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -39,7 +38,7 @@ func init() { } func pauseCmd(c *cliconfig.PauseValues) error { - if os.Geteuid() != 0 { + if rootless.IsRootless() && !remoteclient { return errors.New("pause is not supported for rootless containers") } diff --git a/cmd/podman/pod_kill.go b/cmd/podman/pod_kill.go index c1ea66126..6be79363a 100644 --- a/cmd/podman/pod_kill.go +++ b/cmd/podman/pod_kill.go @@ -55,7 +55,7 @@ func podKillCmd(c *cliconfig.PodKillValues) error { } defer runtime.Shutdown(false) - var killSignal uint = uint(syscall.SIGTERM) + killSignal := uint(syscall.SIGTERM) if c.Signal != "" { // Check if the signalString provided by the user is valid diff --git a/cmd/podman/pod_stats.go b/cmd/podman/pod_stats.go index c33c97602..97aa52f5d 100644 --- a/cmd/podman/pod_stats.go +++ b/cmd/podman/pod_stats.go @@ -271,7 +271,7 @@ func printPSFormat(format string, stats []*podStatOut, headerNames map[string]st func outputToStdOut(stats []*podStatOut) { w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - outFormat := ("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n") + outFormat := "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n" fmt.Fprintf(w, outFormat, "POD", "CID", "NAME", "CPU %", "MEM USAGE/ LIMIT", "MEM %", "NET IO", "BLOCK IO", "PIDS") for _, i := range stats { if len(stats) == 0 { diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go index eb5181126..75e07d325 100644 --- a/cmd/podman/ps.go +++ b/cmd/podman/ps.go @@ -197,7 +197,11 @@ func init() { } func psCmd(c *cliconfig.PsValues) error { - var watch bool + var ( + watch bool + runtime *adapter.LocalRuntime + err error + ) if c.Watch > 0 { watch = true @@ -210,8 +214,11 @@ func psCmd(c *cliconfig.PsValues) error { if err := checkFlagsPassed(c); err != nil { return errors.Wrapf(err, "error with flags passed") } - - runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) + if !c.Size { + runtime, err = adapter.GetRuntimeNoStore(getContext(), &c.PodmanCommand) + } else { + runtime, err = adapter.GetRuntime(getContext(), &c.PodmanCommand) + } if err != nil { return errors.Wrapf(err, "error creating libpod runtime") } @@ -308,57 +315,6 @@ func sortPsOutput(sortBy string, psOutput psSorted) (psSorted, error) { return psOutput, nil } -// getLabels converts the labels to a string of the form "key=value, key2=value2" -func formatLabels(labels map[string]string) string { - var arr []string - if len(labels) > 0 { - for key, val := range labels { - temp := key + "=" + val - arr = append(arr, temp) - } - return strings.Join(arr, ",") - } - return "" -} - -// getMounts converts the volumes mounted to a string of the form "mount1, mount2" -// it truncates it if noTrunc is false -func getMounts(mounts []string, noTrunc bool) string { - return strings.Join(getMountsArray(mounts, noTrunc), ",") -} - -func getMountsArray(mounts []string, noTrunc bool) []string { - var arr []string - if len(mounts) == 0 { - return mounts - } - for _, mount := range mounts { - splitArr := strings.Split(mount, ":") - if len(splitArr[0]) > mountTruncLength && !noTrunc { - arr = append(arr, splitArr[0][:mountTruncLength]+"...") - continue - } - arr = append(arr, splitArr[0]) - } - return arr -} - -// portsToString converts the ports used to a string of the from "port1, port2" -func portsToString(ports []ocicni.PortMapping) string { - var portDisplay []string - if len(ports) == 0 { - return "" - } - for _, v := range ports { - hostIP := v.HostIP - if hostIP == "" { - hostIP = "0.0.0.0" - } - portDisplay = append(portDisplay, fmt.Sprintf("%s:%d->%d/%s", hostIP, v.HostPort, v.ContainerPort, v.Protocol)) - } - return strings.Join(portDisplay, ", ") -} - func printFormat(format string, containers []shared.PsContainerOutput) error { // return immediately if no containers are present if len(containers) == 0 { diff --git a/cmd/podman/pull.go b/cmd/podman/pull.go index 115f437d8..a05c78928 100644 --- a/cmd/podman/pull.go +++ b/cmd/podman/pull.go @@ -14,7 +14,7 @@ import ( "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/adapter" "github.com/containers/libpod/pkg/util" - opentracing "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" diff --git a/cmd/podman/run.go b/cmd/podman/run.go index 7d84d716b..6f089e5a4 100644 --- a/cmd/podman/run.go +++ b/cmd/podman/run.go @@ -3,7 +3,7 @@ package main import ( "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/pkg/adapter" - opentracing "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/spf13/cobra" ) diff --git a/cmd/podman/shared/container.go b/cmd/podman/shared/container.go index de850a7c3..df4583be6 100644 --- a/cmd/podman/shared/container.go +++ b/cmd/podman/shared/container.go @@ -279,8 +279,8 @@ func generateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) return strings.Contains(c.ID(), filterValue) }, nil case "label": - var filterArray []string = strings.SplitN(filterValue, "=", 2) - var filterKey string = filterArray[0] + var filterArray = strings.SplitN(filterValue, "=", 2) + var filterKey = filterArray[0] if len(filterArray) > 1 { filterValue = filterArray[1] } else { diff --git a/cmd/podman/shared/container_inspect.go b/cmd/podman/shared/container_inspect.go index c89daf6bb..a8094466e 100644 --- a/cmd/podman/shared/container_inspect.go +++ b/cmd/podman/shared/container_inspect.go @@ -4,7 +4,7 @@ import ( "github.com/containers/libpod/libpod" cc "github.com/containers/libpod/pkg/spec" "github.com/docker/go-connections/nat" - specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/runtime-spec/specs-go" ) // InspectContainer holds all inspect data for a container. diff --git a/cmd/podman/tree.go b/cmd/podman/tree.go index 0f62858e8..48e192990 100644 --- a/cmd/podman/tree.go +++ b/cmd/podman/tree.go @@ -127,7 +127,7 @@ func printImageChildren(layerMap map[string]*image.LayerInfo, layerID string, pr } fmt.Printf("%sID: %s Size: %7v%s\n", intend, ll.ID[:12], units.HumanSizeWithPrecision(float64(ll.Size), 4), tags) for count, childID := range ll.ChildID { - if err := printImageChildren(layerMap, childID, prefix, (count == len(ll.ChildID)-1)); err != nil { + if err := printImageChildren(layerMap, childID, prefix, count == len(ll.ChildID)-1); err != nil { return err } } diff --git a/cmd/podman/unpause.go b/cmd/podman/unpause.go index 55bfe584e..8126ebfbd 100644 --- a/cmd/podman/unpause.go +++ b/cmd/podman/unpause.go @@ -1,11 +1,10 @@ package main import ( - "os" - "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/adapter" + "github.com/containers/libpod/pkg/rootless" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -38,7 +37,7 @@ func init() { } func unpauseCmd(c *cliconfig.UnpauseValues) error { - if os.Geteuid() != 0 { + if rootless.IsRootless() && !remoteclient { return errors.New("unpause is not supported for rootless containers") } diff --git a/contrib/spec/podman.spec.in b/contrib/spec/podman.spec.in index 8e13f86de..ce5901f69 100644 --- a/contrib/spec/podman.spec.in +++ b/contrib/spec/podman.spec.in @@ -39,7 +39,7 @@ %global shortcommit_conmon %(c=%{commit_conmon}; echo ${c:0:7}) Name: podman -Version: 1.4.4 +Version: 1.4.5 Release: #COMMITDATE#.git%{shortcommit0}%{?dist} Summary: Manage Pods, Containers and Container Images License: ASL 2.0 diff --git a/docs/libpod.conf.5.md b/docs/libpod.conf.5.md index c57dcca8f..097d0764a 100644 --- a/docs/libpod.conf.5.md +++ b/docs/libpod.conf.5.md @@ -27,6 +27,9 @@ libpod to manage containers. **cgroup_manager**="" Specify the CGroup Manager to use; valid values are "systemd" and "cgroupfs" +**lock_type**="" + Specify the locking mechanism to use; valid values are "shm" and "file". Change the default only if you are sure of what you are doing, in general "file" is useful only on platforms where cgo is not available for using the faster "shm" lock type. You may need to run "podman system renumber" after you change the lock type. + **init_path**="" Path to the container-init binary, which forwards signals and reaps processes within containers. Note that the container-init binary will only be used when the `--init` for podman-create and podman-run is set. @@ -19,14 +19,14 @@ require ( github.com/containernetworking/plugins v0.8.1 github.com/containers/buildah v1.9.0 github.com/containers/image v2.0.0+incompatible - github.com/containers/psgo v1.3.0 - github.com/containers/storage v1.12.12 + github.com/containers/psgo v1.3.1 + github.com/containers/storage v1.12.13 github.com/coreos/bbolt v1.3.3 // indirect github.com/coreos/etcd v3.3.13+incompatible // indirect github.com/coreos/go-iptables v0.4.1 github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-systemd v0.0.0-20190620071333-e64a0ec8b42a - github.com/cri-o/ocicni v0.0.0-20190328132530-0c180f981b27 + github.com/cri-o/ocicni v0.1.1-0.20190702175919-7762645d18ca github.com/cyphar/filepath-securejoin v0.2.2 github.com/davecgh/go-spew v1.1.1 github.com/docker/distribution v2.7.1+incompatible @@ -72,10 +72,18 @@ github.com/containers/image v2.0.0+incompatible h1:FTr6Br7jlIKNCKMjSOMbAxKp2keQ0 github.com/containers/image v2.0.0+incompatible/go.mod h1:8Vtij257IWSanUQKe1tAeNOm2sRVkSqQTVQ1IlwI3+M= github.com/containers/psgo v1.3.0 h1:kDhiA4gNNyJ2qCzmOuBf6AmrF/Pp+6Jo98P68R7fB8I= github.com/containers/psgo v1.3.0/go.mod h1:7MELvPTW1fj6yMrwD9I1Iasx1vU+hKlRkHXAJ51sFtU= +github.com/containers/psgo v1.3.1-0.20190626112706-fbef66e4ce92 h1:aVJs/Av0Yc9uNoWnIwmG+6Z+XozuRXFwvLwAOVmwlvI= +github.com/containers/psgo v1.3.1-0.20190626112706-fbef66e4ce92/go.mod h1:LLiRMmxZ6FWP4bB/fOUu6kDT+4okk/ZCeeykqh0O5Ns= +github.com/containers/psgo v1.3.1 h1:1kE+jJ9Ou5f9zQT/M2IdeSclsKWsXrSFlOcnqc+F2TA= +github.com/containers/psgo v1.3.1/go.mod h1:LLiRMmxZ6FWP4bB/fOUu6kDT+4okk/ZCeeykqh0O5Ns= +github.com/containers/storage v1.12.10-0.20190627120555-8eed0c36d1e3 h1:kO/YA36sGuPDFvVIzZxJp7xmwa+/wCVADxDSuFzsZwM= +github.com/containers/storage v1.12.10-0.20190627120555-8eed0c36d1e3/go.mod h1:+RirK6VQAqskQlaTBrOG6ulDvn4si2QjFE1NZCn06MM= github.com/containers/storage v1.12.11 h1:r35VsROen9Kw3+LN/v4O4g7cT5zQPX06vkcjqScJ2z8= github.com/containers/storage v1.12.11/go.mod h1:+RirK6VQAqskQlaTBrOG6ulDvn4si2QjFE1NZCn06MM= github.com/containers/storage v1.12.12 h1:gao0GNzjmSX4Ai/StOHtUVIrBguC0OKyvx/ZMwBdyuY= github.com/containers/storage v1.12.12/go.mod h1:+RirK6VQAqskQlaTBrOG6ulDvn4si2QjFE1NZCn06MM= +github.com/containers/storage v1.12.13 h1:GtaLCY8p1Drlk1Oew581jGvB137UaO+kpz0HII67T0A= +github.com/containers/storage v1.12.13/go.mod h1:+RirK6VQAqskQlaTBrOG6ulDvn4si2QjFE1NZCn06MM= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -99,6 +107,8 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cri-o/ocicni v0.0.0-20190328132530-0c180f981b27 h1:c3yt54JU7t7bzcae8YwI6+TvbWeQWrBfDxYi7zL9XPE= github.com/cri-o/ocicni v0.0.0-20190328132530-0c180f981b27/go.mod h1:BO0al9TKber3XUTucLzKgoG5sq8qiOB41H7zSdfw6r8= +github.com/cri-o/ocicni v0.1.1-0.20190702175919-7762645d18ca h1:CJstDqYy9ClWuPcDHMTCAiUS+ckekluYetGR2iYYWuo= +github.com/cri-o/ocicni v0.1.1-0.20190702175919-7762645d18ca/go.mod h1:BO0al9TKber3XUTucLzKgoG5sq8qiOB41H7zSdfw6r8= github.com/cyphar/filepath-securejoin v0.2.1 h1:5DPkzz/0MwUpvR4fxASKzgApeq2OMFY5FfYtrX28Coo= github.com/cyphar/filepath-securejoin v0.2.1/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg= diff --git a/install.md b/install.md index 7839c3e7d..368cdd386 100644 --- a/install.md +++ b/install.md @@ -2,7 +2,7 @@ ## Installing packaged versions of Podman -#### [Arch Linux](https://www.archlinux.org) +#### [Arch Linux](https://www.archlinux.org) & [Manjaro Linux](https://manjaro.org) ```bash sudo pacman -S podman @@ -138,6 +138,32 @@ sudo apt-get install \ uidmap ``` +On Manjaro (and maybe other Linux distributions): + +Make sure that the Linux kernel supports user namespaces: + +``` +> zgrep CONFIG_USER_NS /proc/config.gz +CONFIG_USER_NS=y + +``` + +If not, please update the kernel. +For Manjaro Linux the instructions can be found here: +https://wiki.manjaro.org/index.php/Manjaro_Kernels + +After that enable user namespaces: + +``` +sudo sysctl kernel.unprivileged_userns_clone=1 +``` + +To enable the user namespaces permanenty: + +``` +echo 'kernel.unprivileged_userns_clone=1' > /etc/sysctl.d/userns.conf +``` + ### Building missing dependencies If any dependencies cannot be installed or are not sufficiently current, they have to be built from source. diff --git a/libpod.conf b/libpod.conf index 71ac79dd4..c92f60a10 100644 --- a/libpod.conf +++ b/libpod.conf @@ -87,6 +87,9 @@ infra_command = "/pause" # Default libpod support for container labeling # label=true +# The locking mechanism to use +lock_type = "shm" + # Number of locks available for containers and pods. # If this is changed, a lock renumber must be performed (e.g. with the # 'podman system renumber' command). diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go index b047c9fa0..4dda3a7f0 100644 --- a/libpod/boltdb_state.go +++ b/libpod/boltdb_state.go @@ -381,11 +381,6 @@ func (s *BoltState) LookupContainer(idOrName string) (*Container, error) { defer s.closeDBCon(db) err = db.View(func(tx *bolt.Tx) error { - idBucket, err := getIDBucket(tx) - if err != nil { - return err - } - ctrBucket, err := getCtrBucket(tx) if err != nil { return err @@ -436,7 +431,7 @@ func (s *BoltState) LookupContainer(idOrName string) (*Container, error) { // We were not given a full container ID or name. // Search for partial ID matches. exists := false - err = idBucket.ForEach(func(checkID, checkName []byte) error { + err = ctrBucket.ForEach(func(checkID, checkName []byte) error { // If the container isn't in our namespace, we // can't match it if s.namespaceBytes != nil { @@ -963,11 +958,6 @@ func (s *BoltState) LookupPod(idOrName string) (*Pod, error) { defer s.closeDBCon(db) err = db.View(func(tx *bolt.Tx) error { - idBucket, err := getIDBucket(tx) - if err != nil { - return err - } - podBkt, err := getPodBucket(tx) if err != nil { return err @@ -1015,7 +1005,7 @@ func (s *BoltState) LookupPod(idOrName string) (*Pod, error) { // They did not give us a full pod name or ID. // Search for partial ID matches. exists := false - err = idBucket.ForEach(func(checkID, checkName []byte) error { + err = podBkt.ForEach(func(checkID, checkName []byte) error { // If the pod isn't in our namespace, we // can't match it if s.namespaceBytes != nil { diff --git a/libpod/container.go b/libpod/container.go index 713386477..bfbc47d76 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -168,6 +168,8 @@ type ContainerState struct { OOMKilled bool `json:"oomKilled,omitempty"` // PID is the PID of a running container PID int `json:"pid,omitempty"` + // ConmonPID is the PID of the container's conmon + ConmonPID int `json:"conmonPid,omitempty"` // ExecSessions contains active exec sessions for container // Exec session ID is mapped to PID of exec process ExecSessions map[string]*ExecSession `json:"execSessions,omitempty"` @@ -849,7 +851,7 @@ func (c *Container) OOMKilled() (bool, error) { return c.state.OOMKilled, nil } -// PID returns the PID of the container +// PID returns the PID of the container. // If the container is not running, a pid of 0 will be returned. No error will // occur. func (c *Container) PID() (int, error) { @@ -865,6 +867,22 @@ func (c *Container) PID() (int, error) { return c.state.PID, nil } +// ConmonPID Returns the PID of the container's conmon process. +// If the container is not running, a PID of 0 will be returned. No error will +// occur. +func (c *Container) ConmonPID() (int, error) { + if !c.batched { + c.lock.Lock() + defer c.lock.Unlock() + + if err := c.syncContainer(); err != nil { + return -1, err + } + } + + return c.state.ConmonPID, nil +} + // ExecSessions retrieves active exec sessions running in the container func (c *Container) ExecSessions() ([]string, error) { if !c.batched { diff --git a/libpod/container_attach_linux.go b/libpod/container_attach_linux.go index f5aac5794..fc53268c3 100644 --- a/libpod/container_attach_linux.go +++ b/libpod/container_attach_linux.go @@ -19,10 +19,6 @@ import ( "k8s.io/client-go/tools/remotecommand" ) -//#include <sys/un.h> -// extern int unix_path_length(){struct sockaddr_un addr; return sizeof(addr.sun_path) - 1;} -import "C" - /* Sync with stdpipe_t in conmon.c */ const ( AttachPipeStdin = 1 @@ -80,7 +76,7 @@ func (c *Container) attachContainerSocket(resize <-chan remotecommand.TerminalSi socketPath := c.AttachSocketPath() - maxUnixLength := int(C.unix_path_length()) + maxUnixLength := unixPathLength() if maxUnixLength < len(socketPath) { socketPath = socketPath[0:maxUnixLength] } diff --git a/libpod/container_attach_linux_cgo.go b/libpod/container_attach_linux_cgo.go new file mode 100644 index 000000000..d81243360 --- /dev/null +++ b/libpod/container_attach_linux_cgo.go @@ -0,0 +1,11 @@ +//+build linux,cgo + +package libpod + +//#include <sys/un.h> +// extern int unix_path_length(){struct sockaddr_un addr; return sizeof(addr.sun_path) - 1;} +import "C" + +func unixPathLength() int { + return int(C.unix_path_length()) +} diff --git a/libpod/container_attach_linux_nocgo.go b/libpod/container_attach_linux_nocgo.go new file mode 100644 index 000000000..a514a555d --- /dev/null +++ b/libpod/container_attach_linux_nocgo.go @@ -0,0 +1,7 @@ +//+build linux,!cgo + +package libpod + +func unixPathLength() int { + return 107 +} diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go index 0a4d7016f..18e84e1f1 100644 --- a/libpod/container_inspect.go +++ b/libpod/container_inspect.go @@ -145,6 +145,7 @@ type InspectContainerState struct { OOMKilled bool `json:"OOMKilled"` Dead bool `json:"Dead"` Pid int `json:"Pid"` + ConmonPid int `json:"ConmonPid,omitempty"` ExitCode int32 `json:"ExitCode"` Error string `json:"Error"` // TODO StartedAt time.Time `json:"StartedAt"` @@ -261,6 +262,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *driver.Data) OOMKilled: runtimeInfo.OOMKilled, Dead: runtimeInfo.State.String() == "bad state", Pid: runtimeInfo.PID, + ConmonPid: runtimeInfo.ConmonPID, ExitCode: runtimeInfo.ExitCode, Error: "", // can't get yet StartedAt: runtimeInfo.StartedTime, diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 884f5a0a9..611fa9800 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -452,6 +452,7 @@ func (c *Container) teardownStorage() error { // It does not save the results - assumes the database will do that for us func resetState(state *ContainerState) error { state.PID = 0 + state.ConmonPID = 0 state.Mountpoint = "" state.Mounted = false if state.State != define.ContainerStateExited { @@ -609,7 +610,7 @@ func (c *Container) isStopped() (bool, error) { if err != nil { return true, err } - return (c.state.State != define.ContainerStateRunning && c.state.State != define.ContainerStatePaused), nil + return c.state.State != define.ContainerStateRunning && c.state.State != define.ContainerStatePaused, nil } // save container state to the database @@ -1043,6 +1044,8 @@ func (c *Container) stop(timeout uint) error { return err } + c.state.PID = 0 + c.state.ConmonPID = 0 c.state.StoppedByUser = true if err := c.save(); err != nil { return errors.Wrapf(err, "error saving container %s state after stopping", c.ID()) diff --git a/libpod/image/pull.go b/libpod/image/pull.go index 644a9ae86..e5765febc 100644 --- a/libpod/image/pull.go +++ b/libpod/image/pull.go @@ -19,8 +19,8 @@ import ( "github.com/containers/image/types" "github.com/containers/libpod/libpod/events" "github.com/containers/libpod/pkg/registries" - multierror "github.com/hashicorp/go-multierror" - opentracing "github.com/opentracing/opentracing-go" + "github.com/hashicorp/go-multierror" + "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) diff --git a/libpod/lock/file/file_lock.go b/libpod/lock/file/file_lock.go new file mode 100644 index 000000000..e50d67321 --- /dev/null +++ b/libpod/lock/file/file_lock.go @@ -0,0 +1,175 @@ +package file + +import ( + "io/ioutil" + "os" + "path/filepath" + "strconv" + "syscall" + + "github.com/containers/storage" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// FileLocks is a struct enabling POSIX lock locking in a shared memory +// segment. +type FileLocks struct { // nolint + lockPath string + valid bool +} + +// CreateFileLock sets up a directory containing the various lock files. +func CreateFileLock(path string) (*FileLocks, error) { + _, err := os.Stat(path) + if err == nil { + return nil, errors.Wrapf(syscall.EEXIST, "directory %s exists", path) + } + if err := os.MkdirAll(path, 0711); err != nil { + return nil, errors.Wrapf(err, "cannot create %s", path) + } + + locks := new(FileLocks) + locks.lockPath = path + locks.valid = true + + return locks, nil +} + +// OpenFileLock opens an existing directory with the lock files. +func OpenFileLock(path string) (*FileLocks, error) { + _, err := os.Stat(path) + if err != nil { + return nil, errors.Wrapf(err, "accessing directory %s", path) + } + + locks := new(FileLocks) + locks.lockPath = path + locks.valid = true + + return locks, nil +} + +// Close closes an existing shared-memory segment. +// The segment will be rendered unusable after closing. +// WARNING: If you Close() while there are still locks locked, these locks may +// fail to release, causing a program freeze. +// Close() is only intended to be used while testing the locks. +func (locks *FileLocks) Close() error { + if !locks.valid { + return errors.Wrapf(syscall.EINVAL, "locks have already been closed") + } + err := os.RemoveAll(locks.lockPath) + if err != nil { + return errors.Wrapf(err, "deleting directory %s", locks.lockPath) + } + return nil +} + +func (locks *FileLocks) getLockPath(lck uint32) string { + return filepath.Join(locks.lockPath, strconv.FormatInt(int64(lck), 10)) +} + +// AllocateLock allocates a lock and returns the index of the lock that was allocated. +func (locks *FileLocks) AllocateLock() (uint32, error) { + if !locks.valid { + return 0, errors.Wrapf(syscall.EINVAL, "locks have already been closed") + } + + id := uint32(0) + for ; ; id++ { + path := locks.getLockPath(id) + f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) + if err != nil { + if os.IsExist(err) { + continue + } + return 0, errors.Wrapf(err, "creating lock file") + } + f.Close() + break + } + return id, nil +} + +// AllocateGivenLock allocates the given lock from the shared-memory +// segment for use by a container or pod. +// If the lock is already in use or the index is invalid an error will be +// returned. +func (locks *FileLocks) AllocateGivenLock(lck uint32) error { + if !locks.valid { + return errors.Wrapf(syscall.EINVAL, "locks have already been closed") + } + + f, err := os.OpenFile(locks.getLockPath(lck), os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666) + if err != nil { + return errors.Wrapf(err, "error creating lock %d", lck) + } + f.Close() + + return nil +} + +// DeallocateLock frees a lock in a shared-memory segment so it can be +// reallocated to another container or pod. +// The given lock must be already allocated, or an error will be returned. +func (locks *FileLocks) DeallocateLock(lck uint32) error { + if !locks.valid { + return errors.Wrapf(syscall.EINVAL, "locks have already been closed") + } + if err := os.Remove(locks.getLockPath(lck)); err != nil { + return errors.Wrapf(err, "deallocating lock %d", lck) + } + return nil +} + +// DeallocateAllLocks frees all locks so they can be reallocated to +// other containers and pods. +func (locks *FileLocks) DeallocateAllLocks() error { + if !locks.valid { + return errors.Wrapf(syscall.EINVAL, "locks have already been closed") + } + files, err := ioutil.ReadDir(locks.lockPath) + if err != nil { + return errors.Wrapf(err, "error reading directory %s", locks.lockPath) + } + var lastErr error + for _, f := range files { + p := filepath.Join(locks.lockPath, f.Name()) + err := os.Remove(p) + if err != nil { + lastErr = err + logrus.Errorf("deallocating lock %s", p) + } + } + return lastErr +} + +// LockFileLock locks the given lock. +func (locks *FileLocks) LockFileLock(lck uint32) error { + if !locks.valid { + return errors.Wrapf(syscall.EINVAL, "locks have already been closed") + } + + l, err := storage.GetLockfile(locks.getLockPath(lck)) + if err != nil { + return errors.Wrapf(err, "error acquiring lock") + } + + l.Lock() + return nil +} + +// UnlockFileLock unlocks the given lock. +func (locks *FileLocks) UnlockFileLock(lck uint32) error { + if !locks.valid { + return errors.Wrapf(syscall.EINVAL, "locks have already been closed") + } + l, err := storage.GetLockfile(locks.getLockPath(lck)) + if err != nil { + return errors.Wrapf(err, "error acquiring lock") + } + + l.Unlock() + return nil +} diff --git a/libpod/lock/file/file_lock_test.go b/libpod/lock/file/file_lock_test.go new file mode 100644 index 000000000..6320d6b70 --- /dev/null +++ b/libpod/lock/file/file_lock_test.go @@ -0,0 +1,74 @@ +package file + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" +) + +// Test that creating and destroying locks work +func TestCreateAndDeallocate(t *testing.T) { + d, err := ioutil.TempDir("", "filelock") + assert.NoError(t, err) + defer os.RemoveAll(d) + + l, err := OpenFileLock(filepath.Join(d, "locks")) + assert.Error(t, err) + + l, err = CreateFileLock(filepath.Join(d, "locks")) + assert.NoError(t, err) + + lock, err := l.AllocateLock() + assert.NoError(t, err) + + err = l.AllocateGivenLock(lock) + assert.Error(t, err) + + err = l.DeallocateLock(lock) + assert.NoError(t, err) + + err = l.AllocateGivenLock(lock) + assert.NoError(t, err) + + err = l.DeallocateAllLocks() + assert.NoError(t, err) + + err = l.AllocateGivenLock(lock) + assert.NoError(t, err) + + err = l.DeallocateAllLocks() + assert.NoError(t, err) +} + +// Test that creating and destroying locks work +func TestLockAndUnlock(t *testing.T) { + d, err := ioutil.TempDir("", "filelock") + assert.NoError(t, err) + defer os.RemoveAll(d) + + l, err := CreateFileLock(filepath.Join(d, "locks")) + assert.NoError(t, err) + + lock, err := l.AllocateLock() + assert.NoError(t, err) + + err = l.LockFileLock(lock) + assert.NoError(t, err) + + lslocks, err := exec.LookPath("lslocks") + if err == nil { + lockPath := l.getLockPath(lock) + out, err := exec.Command(lslocks, "--json", "-p", fmt.Sprintf("%d", os.Getpid())).CombinedOutput() + assert.NoError(t, err) + + assert.Contains(t, string(out), lockPath) + } + + err = l.UnlockFileLock(lock) + assert.NoError(t, err) +} diff --git a/libpod/lock/file_lock_manager.go b/libpod/lock/file_lock_manager.go new file mode 100644 index 000000000..8a4d939d3 --- /dev/null +++ b/libpod/lock/file_lock_manager.go @@ -0,0 +1,110 @@ +package lock + +import ( + "github.com/containers/libpod/libpod/lock/file" +) + +// FileLockManager manages shared memory locks. +type FileLockManager struct { + locks *file.FileLocks +} + +// NewFileLockManager makes a new FileLockManager at the specified directory. +func NewFileLockManager(lockPath string) (Manager, error) { + locks, err := file.CreateFileLock(lockPath) + if err != nil { + return nil, err + } + + manager := new(FileLockManager) + manager.locks = locks + + return manager, nil +} + +// OpenFileLockManager opens an existing FileLockManager at the specified directory. +func OpenFileLockManager(path string) (Manager, error) { + locks, err := file.OpenFileLock(path) + if err != nil { + return nil, err + } + + manager := new(FileLockManager) + manager.locks = locks + + return manager, nil +} + +// AllocateLock allocates a new lock from the manager. +func (m *FileLockManager) AllocateLock() (Locker, error) { + semIndex, err := m.locks.AllocateLock() + if err != nil { + return nil, err + } + + lock := new(FileLock) + lock.lockID = semIndex + lock.manager = m + + return lock, nil +} + +// AllocateAndRetrieveLock allocates the lock with the given ID and returns it. +// If the lock is already allocated, error. +func (m *FileLockManager) AllocateAndRetrieveLock(id uint32) (Locker, error) { + lock := new(FileLock) + lock.lockID = id + lock.manager = m + + if err := m.locks.AllocateGivenLock(id); err != nil { + return nil, err + } + + return lock, nil +} + +// RetrieveLock retrieves a lock from the manager given its ID. +func (m *FileLockManager) RetrieveLock(id uint32) (Locker, error) { + lock := new(FileLock) + lock.lockID = id + lock.manager = m + + return lock, nil +} + +// FreeAllLocks frees all locks in the manager. +// This function is DANGEROUS. Please read the full comment in locks.go before +// trying to use it. +func (m *FileLockManager) FreeAllLocks() error { + return m.locks.DeallocateAllLocks() +} + +// FileLock is an individual shared memory lock. +type FileLock struct { + lockID uint32 + manager *FileLockManager +} + +// ID returns the ID of the lock. +func (l *FileLock) ID() uint32 { + return l.lockID +} + +// Lock acquires the lock. +func (l *FileLock) Lock() { + if err := l.manager.locks.LockFileLock(l.lockID); err != nil { + panic(err.Error()) + } +} + +// Unlock releases the lock. +func (l *FileLock) Unlock() { + if err := l.manager.locks.UnlockFileLock(l.lockID); err != nil { + panic(err.Error()) + } +} + +// Free releases the lock, allowing it to be reused. +func (l *FileLock) Free() error { + return l.manager.locks.DeallocateLock(l.lockID) +} diff --git a/libpod/lock/shm/shm_lock.go b/libpod/lock/shm/shm_lock.go index 76dd5729e..322e92a8f 100644 --- a/libpod/lock/shm/shm_lock.go +++ b/libpod/lock/shm/shm_lock.go @@ -1,3 +1,5 @@ +// +build linux,cgo + package shm // #cgo LDFLAGS: -lrt -lpthread @@ -20,7 +22,7 @@ var ( // BitmapSize is the size of the bitmap used when managing SHM locks. // an SHM lock manager's max locks will be rounded up to a multiple of // this number. - BitmapSize uint32 = uint32(C.bitmap_size_c) + BitmapSize = uint32(C.bitmap_size_c) ) // SHMLocks is a struct enabling POSIX semaphore locking in a shared memory diff --git a/libpod/lock/shm/shm_lock_nocgo.go b/libpod/lock/shm/shm_lock_nocgo.go new file mode 100644 index 000000000..ea1488c90 --- /dev/null +++ b/libpod/lock/shm/shm_lock_nocgo.go @@ -0,0 +1,102 @@ +// +build linux,!cgo + +package shm + +import ( + "github.com/sirupsen/logrus" +) + +// SHMLocks is a struct enabling POSIX semaphore locking in a shared memory +// segment. +type SHMLocks struct { +} + +// CreateSHMLock sets up a shared-memory segment holding a given number of POSIX +// semaphores, and returns a struct that can be used to operate on those locks. +// numLocks must not be 0, and may be rounded up to a multiple of the bitmap +// size used by the underlying implementation. +func CreateSHMLock(path string, numLocks uint32) (*SHMLocks, error) { + logrus.Error("locks are not supported without cgo") + return &SHMLocks{}, nil +} + +// OpenSHMLock opens an existing shared-memory segment holding a given number of +// POSIX semaphores. numLocks must match the number of locks the shared memory +// segment was created with. +func OpenSHMLock(path string, numLocks uint32) (*SHMLocks, error) { + logrus.Error("locks are not supported without cgo") + return &SHMLocks{}, nil +} + +// GetMaxLocks returns the maximum number of locks in the SHM +func (locks *SHMLocks) GetMaxLocks() uint32 { + logrus.Error("locks are not supported without cgo") + return 0 +} + +// Close closes an existing shared-memory segment. +// The segment will be rendered unusable after closing. +// WARNING: If you Close() while there are still locks locked, these locks may +// fail to release, causing a program freeze. +// Close() is only intended to be used while testing the locks. +func (locks *SHMLocks) Close() error { + logrus.Error("locks are not supported without cgo") + return nil +} + +// AllocateSemaphore allocates a semaphore from a shared-memory segment for use +// by a container or pod. +// Returns the index of the semaphore that was allocated. +// Allocations past the maximum number of locks given when the SHM segment was +// created will result in an error, and no semaphore will be allocated. +func (locks *SHMLocks) AllocateSemaphore() (uint32, error) { + logrus.Error("locks are not supported without cgo") + return 0, nil +} + +// AllocateGivenSemaphore allocates the given semaphore from the shared-memory +// segment for use by a container or pod. +// If the semaphore is already in use or the index is invalid an error will be +// returned. +func (locks *SHMLocks) AllocateGivenSemaphore(sem uint32) error { + logrus.Error("locks are not supported without cgo") + return nil +} + +// DeallocateSemaphore frees a semaphore in a shared-memory segment so it can be +// reallocated to another container or pod. +// The given semaphore must be already allocated, or an error will be returned. +func (locks *SHMLocks) DeallocateSemaphore(sem uint32) error { + logrus.Error("locks are not supported without cgo") + return nil +} + +// DeallocateAllSemaphores frees all semaphores so they can be reallocated to +// other containers and pods. +func (locks *SHMLocks) DeallocateAllSemaphores() error { + logrus.Error("locks are not supported without cgo") + return nil +} + +// LockSemaphore locks the given semaphore. +// If the semaphore is already locked, LockSemaphore will block until the lock +// can be acquired. +// There is no requirement that the given semaphore be allocated. +// This ensures that attempts to lock a container after it has been deleted, +// but before the caller has queried the database to determine this, will +// succeed. +func (locks *SHMLocks) LockSemaphore(sem uint32) error { + logrus.Error("locks are not supported without cgo") + return nil +} + +// UnlockSemaphore unlocks the given semaphore. +// Unlocking a semaphore that is already unlocked with return EBUSY. +// There is no requirement that the given semaphore be allocated. +// This ensures that attempts to lock a container after it has been deleted, +// but before the caller has queried the database to determine this, will +// succeed. +func (locks *SHMLocks) UnlockSemaphore(sem uint32) error { + logrus.Error("locks are not supported without cgo") + return nil +} diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index 93ec157c5..27585b8d5 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -28,21 +28,23 @@ import ( // Get an OCICNI network config func (r *Runtime) getPodNetwork(id, name, nsPath string, networks []string, ports []ocicni.PortMapping, staticIP net.IP) ocicni.PodNetwork { + defaultNetwork := r.netPlugin.GetDefaultNetworkName() network := ocicni.PodNetwork{ - Name: name, - Namespace: name, // TODO is there something else we should put here? We don't know about Kube namespaces - ID: id, - NetNS: nsPath, - PortMappings: ports, - Networks: networks, + Name: name, + Namespace: name, // TODO is there something else we should put here? We don't know about Kube namespaces + ID: id, + NetNS: nsPath, + Networks: networks, + RuntimeConfig: map[string]ocicni.RuntimeConfig{ + defaultNetwork: {PortMappings: ports}, + }, } if staticIP != nil { - defaultNetwork := r.netPlugin.GetDefaultNetworkName() - network.Networks = []string{defaultNetwork} - network.NetworkConfig = make(map[string]ocicni.NetworkConfig) - network.NetworkConfig[defaultNetwork] = ocicni.NetworkConfig{IP: staticIP.String()} + network.RuntimeConfig = map[string]ocicni.RuntimeConfig{ + defaultNetwork: {IP: staticIP.String(), PortMappings: ports}, + } } return network diff --git a/libpod/oci.go b/libpod/oci.go index efb5e42cc..fdd783100 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -234,6 +234,8 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container, useRuntime bool) erro // Alright, it exists. Transition to Stopped state. ctr.state.State = define.ContainerStateStopped + ctr.state.PID = 0 + ctr.state.ConmonPID = 0 // Read the exit file to get our stopped time and exit code. return ctr.handleExitFile(exitFile, info) diff --git a/libpod/oci_linux.go b/libpod/oci_linux.go index 7d9f47ae2..24502ef4f 100644 --- a/libpod/oci_linux.go +++ b/libpod/oci_linux.go @@ -446,6 +446,9 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string, res return errors.Wrapf(define.ErrInternal, "container create failed") } ctr.state.PID = ss.si.Pid + if cmd.Process != nil { + ctr.state.ConmonPID = cmd.Process.Pid + } case <-time.After(ContainerCreateTimeout): return errors.Wrapf(define.ErrInternal, "container creation timeout") } diff --git a/libpod/options.go b/libpod/options.go index 0f23a6c97..78634e953 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -300,6 +300,15 @@ func WithTmpDir(dir string) RuntimeOption { } } +// WithNoStore sets a bool on the runtime that we do not need +// any containers storage. +func WithNoStore() RuntimeOption { + return func(rt *Runtime) error { + rt.noStore = true + return nil + } +} + // WithMaxLogSize sets the maximum size of container logs. // Positive sizes are limits in bytes, -1 is unlimited. func WithMaxLogSize(limit int64) RuntimeOption { diff --git a/libpod/runtime.go b/libpod/runtime.go index 02aa76731..6c61e15d3 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -125,6 +125,9 @@ type Runtime struct { // mechanism to read and write even logs eventer events.Eventer + + // noStore indicates whether we need to interact with a store or not + noStore bool } // RuntimeConfig contains configuration options used to set up the runtime @@ -236,6 +239,9 @@ type RuntimeConfig struct { // pods. NumLocks uint32 `toml:"num_locks,omitempty"` + // LockType is the type of locking to use. + LockType string `toml:"lock_type,omitempty"` + // EventsLogger determines where events should be logged EventsLogger string `toml:"events_logger"` // EventsLogFilePath is where the events log is stored. @@ -315,6 +321,7 @@ func defaultRuntimeConfig() (RuntimeConfig, error) { NumLocks: 2048, EventsLogger: events.DefaultEventerType.String(), DetachKeys: DefaultDetachKeys, + LockType: "shm", }, nil } @@ -656,6 +663,62 @@ func newRuntimeFromConfig(ctx context.Context, userConfigPath string, options .. return runtime, nil } +func getLockManager(runtime *Runtime) (lock.Manager, error) { + var err error + var manager lock.Manager + + switch runtime.config.LockType { + case "file": + lockPath := filepath.Join(runtime.config.TmpDir, "locks") + manager, err = lock.OpenFileLockManager(lockPath) + if err != nil { + if os.IsNotExist(errors.Cause(err)) { + manager, err = lock.NewFileLockManager(lockPath) + if err != nil { + return nil, errors.Wrapf(err, "failed to get new file lock manager") + } + } else { + return nil, err + } + } + + case "", "shm": + lockPath := DefaultSHMLockPath + if rootless.IsRootless() { + lockPath = fmt.Sprintf("%s_%d", DefaultRootlessSHMLockPath, rootless.GetRootlessUID()) + } + // Set up the lock manager + manager, err = lock.OpenSHMLockManager(lockPath, runtime.config.NumLocks) + if err != nil { + if os.IsNotExist(errors.Cause(err)) { + manager, err = lock.NewSHMLockManager(lockPath, runtime.config.NumLocks) + if err != nil { + return nil, errors.Wrapf(err, "failed to get new shm lock manager") + } + } else if errors.Cause(err) == syscall.ERANGE && runtime.doRenumber { + logrus.Debugf("Number of locks does not match - removing old locks") + + // ERANGE indicates a lock numbering mismatch. + // Since we're renumbering, this is not fatal. + // Remove the earlier set of locks and recreate. + if err := os.Remove(filepath.Join("/dev/shm", lockPath)); err != nil { + return nil, errors.Wrapf(err, "error removing libpod locks file %s", lockPath) + } + + manager, err = lock.NewSHMLockManager(lockPath, runtime.config.NumLocks) + if err != nil { + return nil, err + } + } else { + return nil, err + } + } + default: + return nil, errors.Wrapf(define.ErrInvalidArg, "unknown lock type %s", runtime.config.LockType) + } + return manager, nil +} + // Make a new runtime based on the given configuration // Sets up containers/storage, state store, OCI runtime func makeRuntime(ctx context.Context, runtime *Runtime) (err error) { @@ -784,11 +847,14 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) { var store storage.Store if os.Geteuid() != 0 { logrus.Debug("Not configuring container store") + } else if runtime.noStore { + logrus.Debug("No store required. Not opening container store.") } else { store, err = storage.GetStore(runtime.config.StorageConfig) if err != nil { return err } + err = nil defer func() { if err != nil && store != nil { @@ -1038,37 +1104,10 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (err error) { } } - lockPath := DefaultSHMLockPath - if rootless.IsRootless() { - lockPath = fmt.Sprintf("%s_%d", DefaultRootlessSHMLockPath, rootless.GetRootlessUID()) - } - // Set up the lock manager - manager, err := lock.OpenSHMLockManager(lockPath, runtime.config.NumLocks) + runtime.lockManager, err = getLockManager(runtime) if err != nil { - if os.IsNotExist(errors.Cause(err)) { - manager, err = lock.NewSHMLockManager(lockPath, runtime.config.NumLocks) - if err != nil { - return errors.Wrapf(err, "failed to get new shm lock manager") - } - } else if errors.Cause(err) == syscall.ERANGE && runtime.doRenumber { - logrus.Debugf("Number of locks does not match - removing old locks") - - // ERANGE indicates a lock numbering mismatch. - // Since we're renumbering, this is not fatal. - // Remove the earlier set of locks and recreate. - if err := os.Remove(filepath.Join("/dev/shm", lockPath)); err != nil { - return errors.Wrapf(err, "error removing libpod locks file %s", lockPath) - } - - manager, err = lock.NewSHMLockManager(lockPath, runtime.config.NumLocks) - if err != nil { - return err - } - } else { - return err - } + return err } - runtime.lockManager = manager // If we're renumbering locks, do it now. // It breaks out of normal runtime init, and will not return a valid @@ -1148,6 +1187,8 @@ func (r *Runtime) Shutdown(force bool) error { } var lastError error + // If no store was requested, it can bew nil and there is no need to + // attempt to shut it down if r.store != nil { if _, err := r.store.Shutdown(force); err != nil { lastError = errors.Wrapf(err, "Error shutting down container storage") diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 0d0f700a6..9daac161c 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -132,6 +132,14 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container, restore bo ctr.config.LockID = ctr.lock.ID() logrus.Debugf("Allocated lock %d for container %s", ctr.lock.ID(), ctr.ID()) + defer func() { + if err != nil { + if err2 := ctr.lock.Free(); err2 != nil { + logrus.Errorf("Error freeing lock for container after creation failed: %v", err2) + } + } + }() + ctr.valid = true ctr.state.State = config2.ContainerStateConfigured ctr.runtime = r diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go index e9ce130da..d667d3a25 100644 --- a/libpod/runtime_pod_linux.go +++ b/libpod/runtime_pod_linux.go @@ -19,7 +19,7 @@ import ( ) // NewPod makes a new, empty pod -func (r *Runtime) NewPod(ctx context.Context, options ...PodCreateOption) (*Pod, error) { +func (r *Runtime) NewPod(ctx context.Context, options ...PodCreateOption) (_ *Pod, Err error) { r.lock.Lock() defer r.lock.Unlock() @@ -60,6 +60,14 @@ func (r *Runtime) NewPod(ctx context.Context, options ...PodCreateOption) (*Pod, pod.lock = lock pod.config.LockID = pod.lock.ID() + defer func() { + if Err != nil { + if err := pod.lock.Free(); err != nil { + logrus.Errorf("Error freeing pod lock after failed creation: %v", err) + } + } + }() + pod.valid = true // Check CGroup parent sanity, and set it if it was not set @@ -113,15 +121,17 @@ func (r *Runtime) NewPod(ctx context.Context, options ...PodCreateOption) (*Pod, if err := r.state.AddPod(pod); err != nil { return nil, errors.Wrapf(err, "error adding pod to state") } + defer func() { + if Err != nil { + if err := r.removePod(ctx, pod, true, true); err != nil { + logrus.Errorf("Error removing pod after pause container creation failure: %v", err) + } + } + }() if pod.HasInfraContainer() { ctr, err := r.createInfraContainer(ctx, pod) if err != nil { - // Tear down pod, as it is assumed a the pod will contain - // a pause container, and it does not. - if err2 := r.removePod(ctx, pod, true, true); err2 != nil { - logrus.Errorf("Error removing pod after pause container creation failure: %v", err2) - } return nil, errors.Wrapf(err, "error adding Infra Container") } pod.state.InfraContainerID = ctr.ID() diff --git a/libpod/stats.go b/libpod/stats.go index da383e9d9..eb5ed95c4 100644 --- a/libpod/stats.go +++ b/libpod/stats.go @@ -3,6 +3,7 @@ package libpod import ( + "runtime" "strings" "syscall" "time" @@ -105,7 +106,11 @@ func calculateCPUPercent(stats *cgroups.Metrics, previousCPU, previousSystem uin if systemDelta > 0.0 && cpuDelta > 0.0 { // gets a ratio of container cpu usage total, multiplies it by the number of cores (4 cores running // at 100% utilization should be 400% utilization), and multiplies that by 100 to get a percentage - cpuPercent = (cpuDelta / systemDelta) * float64(len(stats.CPU.Usage.PerCPU)) * 100 + nCPUS := len(stats.CPU.Usage.PerCPU) + if nCPUS == 0 { + nCPUS = runtime.NumCPU() + } + cpuPercent = (cpuDelta / systemDelta) * float64(nCPUS) * 100 } return cpuPercent } diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go index dd77b3a3e..8ef88f36b 100644 --- a/pkg/adapter/runtime.go +++ b/pkg/adapter/runtime.go @@ -58,12 +58,26 @@ type Volume struct { // VolumeFilter is for filtering volumes on the client type VolumeFilter func(*Volume) bool +// GetRuntimeNoStore returns a localruntime struct wit an embedded runtime but +// without a configured storage. +func GetRuntimeNoStore(ctx context.Context, c *cliconfig.PodmanCommand) (*LocalRuntime, error) { + runtime, err := libpodruntime.GetRuntimeNoStore(ctx, c) + if err != nil { + return nil, err + } + return getRuntime(runtime) +} + // GetRuntime returns a LocalRuntime struct with the actual runtime embedded in it func GetRuntime(ctx context.Context, c *cliconfig.PodmanCommand) (*LocalRuntime, error) { runtime, err := libpodruntime.GetRuntime(ctx, c) if err != nil { return nil, err } + return getRuntime(runtime) +} + +func getRuntime(runtime *libpod.Runtime) (*LocalRuntime, error) { return &LocalRuntime{ Runtime: runtime, }, nil diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go index 3be89233d..800ed7569 100644 --- a/pkg/adapter/runtime_remote.go +++ b/pkg/adapter/runtime_remote.go @@ -50,6 +50,12 @@ type LocalRuntime struct { *RemoteRuntime } +// GetRuntimeNoStore returns a LocalRuntime struct with the actual runtime embedded in it +// The nostore is ignored +func GetRuntimeNoStore(ctx context.Context, c *cliconfig.PodmanCommand) (*LocalRuntime, error) { + return GetRuntime(ctx, c) +} + // GetRuntime returns a LocalRuntime struct with the actual runtime embedded in it func GetRuntime(ctx context.Context, c *cliconfig.PodmanCommand) (*LocalRuntime, error) { var ( diff --git a/pkg/cgroups/cgroups.go b/pkg/cgroups/cgroups.go index 426bda559..d6c19212b 100644 --- a/pkg/cgroups/cgroups.go +++ b/pkg/cgroups/cgroups.go @@ -30,7 +30,7 @@ type CgroupControl struct { additionalControllers []controller } -// CPUUsage keeps stats for the CPU usage +// CPUUsage keeps stats for the CPU usage (unit: nanoseconds) type CPUUsage struct { Kernel uint64 Total uint64 diff --git a/pkg/cgroups/cpu.go b/pkg/cgroups/cpu.go index 3f969fd3c..8640d490e 100644 --- a/pkg/cgroups/cpu.go +++ b/pkg/cgroups/cpu.go @@ -85,12 +85,14 @@ func (c *cpuHandler) Stat(ctr *CgroupControl, m *Metrics) error { if err != nil { return err } + usage.Kernel *= 1000 } if val, found := values["system_usec"]; found { usage.Total, err = strconv.ParseUint(cleanString(val[0]), 10, 0) if err != nil { return err } + usage.Total *= 1000 } // FIXME: How to read usage.PerCPU? } else { diff --git a/pkg/hooks/0.1.0/hook.go b/pkg/hooks/0.1.0/hook.go index ba68b0f10..88a387647 100644 --- a/pkg/hooks/0.1.0/hook.go +++ b/pkg/hooks/0.1.0/hook.go @@ -6,7 +6,7 @@ import ( "errors" "strings" - hooks "github.com/containers/libpod/pkg/hooks" + "github.com/containers/libpod/pkg/hooks" current "github.com/containers/libpod/pkg/hooks/1.0.0" rspec "github.com/opencontainers/runtime-spec/specs-go" ) diff --git a/pkg/hooks/1.0.0/when_test.go b/pkg/hooks/1.0.0/when_test.go index 7187b297b..a749063ff 100644 --- a/pkg/hooks/1.0.0/when_test.go +++ b/pkg/hooks/1.0.0/when_test.go @@ -30,7 +30,7 @@ func TestAlways(t *testing.T) { for _, always := range []bool{true, false} { for _, or := range []bool{true, false} { for _, process := range []*rspec.Process{processStruct, nil} { - t.Run(fmt.Sprintf("always %t, or %t, has process %t", always, or, (process != nil)), func(t *testing.T) { + t.Run(fmt.Sprintf("always %t, or %t, has process %t", always, or, process != nil), func(t *testing.T) { config.Process = process when := When{Always: &always, Or: or} match, err := when.Match(config, map[string]string{}, false) diff --git a/pkg/logs/logs.go b/pkg/logs/logs.go index 1f0ede6f0..0f684750e 100644 --- a/pkg/logs/logs.go +++ b/pkg/logs/logs.go @@ -135,7 +135,7 @@ func parseCRILog(log []byte, msg *logMessage) error { } // Keep this forward compatible. tags := bytes.Split(log[:idx], tagDelimiter) - partial := (LogTag(tags[0]) == LogTagPartial) + partial := LogTag(tags[0]) == LogTagPartial // Trim the tailing new line if this is a partial line. if partial && len(log) > 0 && log[len(log)-1] == '\n' { log = log[:len(log)-1] diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c index d58a08801..19b76f387 100644 --- a/pkg/rootless/rootless_linux.c +++ b/pkg/rootless/rootless_linux.c @@ -244,7 +244,7 @@ static void __attribute__((constructor)) init() /* Shortcut. If we are able to join the pause pid file, do it now so we don't need to re-exec. */ xdg_runtime_dir = getenv ("XDG_RUNTIME_DIR"); - if (xdg_runtime_dir && xdg_runtime_dir[0] && can_use_shortcut ()) + if (geteuid () != 0 && xdg_runtime_dir && xdg_runtime_dir[0] && can_use_shortcut ()) { int r; int fd; diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index d51f32d68..8028a359c 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -1,4 +1,4 @@ -// +build linux +// +build linux,cgo package rootless @@ -215,7 +215,7 @@ func EnableLinger() (string, error) { // If we have a D-BUS connection, attempt to read the LINGER property from it. if conn != nil { - path := dbus.ObjectPath((fmt.Sprintf("/org/freedesktop/login1/user/_%s", uid))) + path := dbus.ObjectPath(fmt.Sprintf("/org/freedesktop/login1/user/_%s", uid)) ret, err := conn.Object("org.freedesktop.login1", path).GetProperty("org.freedesktop.login1.User.Linger") if err == nil && ret.Value().(bool) { lingerEnabled = true @@ -265,7 +265,7 @@ func EnableLinger() (string, error) { // If we have a D-BUS connection, attempt to read the RUNTIME PATH from it. if conn != nil { - path := dbus.ObjectPath((fmt.Sprintf("/org/freedesktop/login1/user/_%s", uid))) + path := dbus.ObjectPath(fmt.Sprintf("/org/freedesktop/login1/user/_%s", uid)) ret, err := conn.Object("org.freedesktop.login1", path).GetProperty("org.freedesktop.login1.User.RuntimePath") if err == nil { return strings.Trim(ret.String(), "\"\n"), nil diff --git a/pkg/rootless/rootless_unsupported.go b/pkg/rootless/rootless_unsupported.go index 52863580e..a8485c083 100644 --- a/pkg/rootless/rootless_unsupported.go +++ b/pkg/rootless/rootless_unsupported.go @@ -1,14 +1,21 @@ -// +build !linux +// +build !linux !cgo package rootless import ( + "os" + "github.com/pkg/errors" ) -// IsRootless returns false on all non-linux platforms +// IsRootless returns whether the user is rootless func IsRootless() bool { - return false + uid := os.Geteuid() + // os.Geteuid() on Windows returns -1 + if uid == -1 { + return false + } + return uid != 0 } // BecomeRootInUserNS re-exec podman in a new userNS. It returns whether podman was re-executed diff --git a/pkg/spec/config_linux.go b/pkg/spec/config_linux.go index eb2acf984..9f6a4a058 100644 --- a/pkg/spec/config_linux.go +++ b/pkg/spec/config_linux.go @@ -4,12 +4,10 @@ package createconfig import ( "fmt" - "io/ioutil" "os" "path/filepath" "strings" - "github.com/docker/docker/profiles/seccomp" "github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/devices" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -130,29 +128,6 @@ func (c *CreateConfig) addPrivilegedDevices(g *generate.Generator) error { return nil } -func getSeccompConfig(config *CreateConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) { - var seccompConfig *spec.LinuxSeccomp - var err error - - if config.SeccompProfilePath != "" { - seccompProfile, err := ioutil.ReadFile(config.SeccompProfilePath) - if err != nil { - return nil, errors.Wrapf(err, "opening seccomp profile (%s) failed", config.SeccompProfilePath) - } - seccompConfig, err = seccomp.LoadProfile(string(seccompProfile), configSpec) - if err != nil { - return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", config.SeccompProfilePath) - } - } else { - seccompConfig, err = seccomp.GetDefaultProfile(configSpec) - if err != nil { - return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", config.SeccompProfilePath) - } - } - - return seccompConfig, nil -} - func (c *CreateConfig) createBlockIO() (*spec.LinuxBlockIO, error) { var ret *spec.LinuxBlockIO bio := &spec.LinuxBlockIO{} diff --git a/pkg/spec/config_linux_cgo.go b/pkg/spec/config_linux_cgo.go new file mode 100644 index 000000000..e6e92a7cc --- /dev/null +++ b/pkg/spec/config_linux_cgo.go @@ -0,0 +1,34 @@ +// +build linux,cgo + +package createconfig + +import ( + "io/ioutil" + + "github.com/docker/docker/profiles/seccomp" + spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" +) + +func getSeccompConfig(config *CreateConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) { + var seccompConfig *spec.LinuxSeccomp + var err error + + if config.SeccompProfilePath != "" { + seccompProfile, err := ioutil.ReadFile(config.SeccompProfilePath) + if err != nil { + return nil, errors.Wrapf(err, "opening seccomp profile (%s) failed", config.SeccompProfilePath) + } + seccompConfig, err = seccomp.LoadProfile(string(seccompProfile), configSpec) + if err != nil { + return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", config.SeccompProfilePath) + } + } else { + seccompConfig, err = seccomp.GetDefaultProfile(configSpec) + if err != nil { + return nil, errors.Wrapf(err, "loading seccomp profile (%s) failed", config.SeccompProfilePath) + } + } + + return seccompConfig, nil +} diff --git a/pkg/spec/config_linux_nocgo.go b/pkg/spec/config_linux_nocgo.go new file mode 100644 index 000000000..10329ff3b --- /dev/null +++ b/pkg/spec/config_linux_nocgo.go @@ -0,0 +1,11 @@ +// +build linux,!cgo + +package createconfig + +import ( + spec "github.com/opencontainers/runtime-spec/specs-go" +) + +func getSeccompConfig(config *CreateConfig, configSpec *spec.Spec) (*spec.LinuxSeccomp, error) { + return nil, nil +} diff --git a/pkg/sysinfo/sysinfo_test.go b/pkg/sysinfo/sysinfo_test.go index b61fbcf54..895828f26 100644 --- a/pkg/sysinfo/sysinfo_test.go +++ b/pkg/sysinfo/sysinfo_test.go @@ -20,7 +20,7 @@ func TestIsCpusetListAvailable(t *testing.T) { for _, c := range cases { r, err := isCpusetListAvailable(c.provided, c.available) if (c.err && err == nil) && r != c.res { - t.Fatalf("Expected pair: %v, %v for %s, %s. Got %v, %v instead", c.res, c.err, c.provided, c.available, (c.err && err == nil), r) + t.Fatalf("Expected pair: %v, %v for %s, %s. Got %v, %v instead", c.res, c.err, c.provided, c.available, c.err && err == nil, r) } } } diff --git a/pkg/tracing/tracing.go b/pkg/tracing/tracing.go index cae76dee8..d028ddf8f 100644 --- a/pkg/tracing/tracing.go +++ b/pkg/tracing/tracing.go @@ -4,9 +4,9 @@ import ( "fmt" "io" - opentracing "github.com/opentracing/opentracing-go" - jaeger "github.com/uber/jaeger-client-go" - config "github.com/uber/jaeger-client-go/config" + "github.com/opentracing/opentracing-go" + "github.com/uber/jaeger-client-go" + "github.com/uber/jaeger-client-go/config" ) // Init returns an instance of Jaeger Tracer that samples 100% of traces and logs all spans to stdout. diff --git a/pkg/trust/trust.go b/pkg/trust/trust.go index 9a75474ae..3bfe4bda1 100644 --- a/pkg/trust/trust.go +++ b/pkg/trust/trust.go @@ -14,7 +14,7 @@ import ( "github.com/containers/image/types" "github.com/pkg/errors" "github.com/sirupsen/logrus" - yaml "gopkg.in/yaml.v2" + "gopkg.in/yaml.v2" ) // PolicyContent struct for policy.json file diff --git a/vendor/github.com/containers/psgo/Makefile b/vendor/github.com/containers/psgo/Makefile index 08a1ac623..6050b9d5b 100644 --- a/vendor/github.com/containers/psgo/Makefile +++ b/vendor/github.com/containers/psgo/Makefile @@ -1,3 +1,5 @@ +export GO111MODULE=off + SHELL= /bin/bash GO ?= go BUILD_DIR := ./bin @@ -51,7 +53,7 @@ install: .PHONY: .install.lint .install.lint: # Workaround for https://github.com/golangci/golangci-lint/issues/523 - go get -u github.com/golangci/golangci-lint/cmd/golangci-lint@master + go get -u github.com/golangci/golangci-lint/cmd/golangci-lint .PHONY: uninstall uninstall: diff --git a/vendor/github.com/containers/psgo/go.mod b/vendor/github.com/containers/psgo/go.mod index dd671bbb0..a194ec196 100644 --- a/vendor/github.com/containers/psgo/go.mod +++ b/vendor/github.com/containers/psgo/go.mod @@ -6,6 +6,6 @@ require ( github.com/opencontainers/runc v0.0.0-20190425234816-dae70e8efea4 github.com/pkg/errors v0.0.0-20190227000051-27936f6d90f9 github.com/sirupsen/logrus v0.0.0-20190403091019-9b3cdde74fbe - github.com/stretchr/testify v1.2.2 + github.com/stretchr/testify v1.3.0 golang.org/x/sys v0.0.0-20190425145619-16072639606e ) diff --git a/vendor/github.com/containers/psgo/go.sum b/vendor/github.com/containers/psgo/go.sum index f8a7d1f0c..da6c750db 100644 --- a/vendor/github.com/containers/psgo/go.sum +++ b/vendor/github.com/containers/psgo/go.sum @@ -1,3 +1,4 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= @@ -10,9 +11,13 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sirupsen/logrus v0.0.0-20190403091019-9b3cdde74fbe h1:PBQLA9wc7FrXiUBnlfs/diNlg3ZdrP21tzcgL3OlVhU= github.com/sirupsen/logrus v0.0.0-20190403091019-9b3cdde74fbe/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190425145619-16072639606e h1:4ktJgTV34+N3qOZUc5fAaG3Pb11qzMm3PkAoTAgUZ2I= golang.org/x/sys v0.0.0-20190425145619-16072639606e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/vendor/github.com/containers/psgo/internal/host/host.go b/vendor/github.com/containers/psgo/internal/host/host.go index 4b145ecfb..33ad67a11 100644 --- a/vendor/github.com/containers/psgo/internal/host/host.go +++ b/vendor/github.com/containers/psgo/internal/host/host.go @@ -24,26 +24,6 @@ import ( "strings" ) -/* -#include <unistd.h> -*/ -import "C" - -var ( - // cache host queries to redundant calculations - clockTicks *int64 - bootTime *int64 -) - -// ClockTicks returns sysconf(SC_CLK_TCK). -func ClockTicks() int64 { - if clockTicks == nil { - ticks := int64(C.sysconf(C._SC_CLK_TCK)) - clockTicks = &ticks - } - return *clockTicks -} - // BootTime parses /proc/uptime returns the boot time in seconds since the // Epoch, 1970-01-01 00:00:00 +0000 (UTC). func BootTime() (int64, error) { diff --git a/vendor/github.com/containers/psgo/internal/host/host_cgo.go b/vendor/github.com/containers/psgo/internal/host/host_cgo.go new file mode 100644 index 000000000..eac9fe5ce --- /dev/null +++ b/vendor/github.com/containers/psgo/internal/host/host_cgo.go @@ -0,0 +1,37 @@ +// Copyright 2018 psgo authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package host extracts data from the host, such as the system's boot time or +// the tick rate of the system clock. +package host + +/* +#include <unistd.h> +*/ +import "C" + +var ( + // cache host queries to redundant calculations + clockTicks *int64 + bootTime *int64 +) + +// ClockTicks returns sysconf(SC_CLK_TCK). +func ClockTicks() (int64, error) { + if clockTicks == nil { + ticks := int64(C.sysconf(C._SC_CLK_TCK)) + clockTicks = &ticks + } + return *clockTicks, nil +} diff --git a/vendor/github.com/containers/psgo/internal/host/host_nocgo.go b/vendor/github.com/containers/psgo/internal/host/host_nocgo.go new file mode 100644 index 000000000..6ff337415 --- /dev/null +++ b/vendor/github.com/containers/psgo/internal/host/host_nocgo.go @@ -0,0 +1,84 @@ +// +build !cgo + +// Copyright 2018 psgo authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package host extracts data from the host, such as the system's boot time or +// the tick rate of the system clock. +package host + +import ( + "encoding/binary" + "fmt" + "io/ioutil" + "unsafe" +) + +var ( + // cache host queries to redundant calculations + clockTicks *int64 + bootTime *int64 +) + +func getNativeEndianness() binary.ByteOrder { + var i int32 = 0x00000001 + u := unsafe.Pointer(&i) + if *((*byte)(u)) == 0x01 { + return binary.LittleEndian + } + return binary.BigEndian +} + +const ( + atClktck = 17 +) + +func getFromAuxv(what uint, whatName string) (uint, error) { + dataLen := int(unsafe.Sizeof(int(0))) + p, err := ioutil.ReadFile("/proc/self/auxv") + if err != nil { + return 0, err + } + native := getNativeEndianness() + for i := 0; i < len(p); { + var k, v uint + + switch dataLen { + case 4: + k = uint(native.Uint32(p[i : i+dataLen])) + v = uint(native.Uint32(p[i+dataLen : i+dataLen*2])) + case 8: + k = uint(native.Uint64(p[i : i+dataLen])) + v = uint(native.Uint64(p[i+dataLen : i+dataLen*2])) + } + i += dataLen * 2 + if k == what { + return v, nil + } + } + return 0, fmt.Errorf("cannot find %s in auxv", whatName) +} + +// ClockTicks returns sysconf(SC_CLK_TCK). +func ClockTicks() (int64, error) { + if clockTicks == nil { + ret, err := getFromAuxv(atClktck, "AT_CLKTCK") + if err != nil { + return -1, err + } + ticks := int64(ret) + clockTicks = &ticks + } + return *clockTicks, nil +} diff --git a/vendor/github.com/containers/psgo/internal/process/process.go b/vendor/github.com/containers/psgo/internal/process/process.go index 68241264e..20e40163f 100644 --- a/vendor/github.com/containers/psgo/internal/process/process.go +++ b/vendor/github.com/containers/psgo/internal/process/process.go @@ -192,8 +192,12 @@ func (p *Process) ElapsedTime() (time.Duration, error) { if err != nil { return 0, err } + clockTicks, err := host.ClockTicks() + if err != nil { + return 0, err + } - sinceBoot = sinceBoot / host.ClockTicks() + sinceBoot = sinceBoot / clockTicks bootTime, err := host.BootTime() if err != nil { @@ -213,7 +217,11 @@ func (p *Process) CPUTime() (time.Duration, error) { if err != nil { return 0, err } - secs := (user + system) / host.ClockTicks() + clockTicks, err := host.ClockTicks() + if err != nil { + return 0, err + } + secs := (user + system) / clockTicks cpu := time.Unix(secs, 0) return cpu.Sub(time.Unix(0, 0)), nil } diff --git a/vendor/github.com/containers/storage/VERSION b/vendor/github.com/containers/storage/VERSION index 656fd0d7e..434711004 100644 --- a/vendor/github.com/containers/storage/VERSION +++ b/vendor/github.com/containers/storage/VERSION @@ -1 +1 @@ -1.12.12 +1.12.13 diff --git a/vendor/github.com/containers/storage/drivers/quota/projectquota.go b/vendor/github.com/containers/storage/drivers/quota/projectquota.go index 93e744371..6ef35d8ad 100644 --- a/vendor/github.com/containers/storage/drivers/quota/projectquota.go +++ b/vendor/github.com/containers/storage/drivers/quota/projectquota.go @@ -1,4 +1,4 @@ -// +build linux +// +build linux,!exclude_disk_quota // // projectquota.go - implements XFS project quota controls diff --git a/vendor/github.com/containers/storage/drivers/quota/projectquota_unsupported.go b/vendor/github.com/containers/storage/drivers/quota/projectquota_unsupported.go new file mode 100644 index 000000000..b6db1e1d8 --- /dev/null +++ b/vendor/github.com/containers/storage/drivers/quota/projectquota_unsupported.go @@ -0,0 +1,32 @@ +// +build linux,exclude_disk_quota + +package quota + +import ( + "github.com/pkg/errors" +) + +// Quota limit params - currently we only control blocks hard limit +type Quota struct { + Size uint64 +} + +// Control - Context to be used by storage driver (e.g. overlay) +// who wants to apply project quotas to container dirs +type Control struct { +} + +func NewControl(basePath string) (*Control, error) { + return nil, errors.New("filesystem does not support, or has not enabled quotas") +} + +// SetQuota - assign a unique project id to directory and set the quota limits +// for that project id +func (q *Control) SetQuota(targetPath string, quota Quota) error { + return errors.New("filesystem does not support, or has not enabled quotas") +} + +// GetQuota - get the quota limits of a directory that was configured with SetQuota +func (q *Control) GetQuota(targetPath string, quota *Quota) error { + return errors.New("filesystem does not support, or has not enabled quotas") +} diff --git a/vendor/github.com/containers/storage/pkg/idtools/idtools.go b/vendor/github.com/containers/storage/pkg/idtools/idtools.go index 815589382..a5c73d311 100644 --- a/vendor/github.com/containers/storage/pkg/idtools/idtools.go +++ b/vendor/github.com/containers/storage/pkg/idtools/idtools.go @@ -4,6 +4,7 @@ import ( "bufio" "fmt" "os" + "os/user" "sort" "strconv" "strings" @@ -244,7 +245,13 @@ func parseSubgid(username string) (ranges, error) { // and return all found ranges for a specified username. If the special value // "ALL" is supplied for username, then all ranges in the file will be returned func parseSubidFile(path, username string) (ranges, error) { - var rangeList ranges + var ( + rangeList ranges + uidstr string + ) + if u, err := user.Lookup(username); err == nil { + uidstr = u.Uid + } subidFile, err := os.Open(path) if err != nil { @@ -266,7 +273,7 @@ func parseSubidFile(path, username string) (ranges, error) { if len(parts) != 3 { return rangeList, fmt.Errorf("Cannot parse subuid/gid information: Format not correct for %s file", path) } - if parts[0] == username || username == "ALL" { + if parts[0] == username || username == "ALL" || (parts[0] == uidstr && parts[0] != "") { startid, err := strconv.Atoi(parts[1]) if err != nil { return rangeList, fmt.Errorf("String to int conversion failed during subuid/gid parsing of %s: %v", path, err) diff --git a/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go b/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go index a08be9ecd..8743abc56 100644 --- a/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go +++ b/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go @@ -382,7 +382,7 @@ func (plugin *cniNetworkPlugin) Name() string { return CNIPluginName } -func (plugin *cniNetworkPlugin) forEachNetwork(podNetwork *PodNetwork, forEachFunc func(*cniNetwork, string, *PodNetwork) error) error { +func (plugin *cniNetworkPlugin) forEachNetwork(podNetwork *PodNetwork, forEachFunc func(*cniNetwork, string, *PodNetwork, RuntimeConfig) error) error { networks := podNetwork.Networks if len(networks) == 0 { networks = append(networks, plugin.GetDefaultNetworkName()) @@ -395,7 +395,7 @@ func (plugin *cniNetworkPlugin) forEachNetwork(podNetwork *PodNetwork, forEachFu logrus.Errorf(err.Error()) return err } - if err := forEachFunc(network, ifName, podNetwork); err != nil { + if err := forEachFunc(network, ifName, podNetwork, podNetwork.RuntimeConfig[netName]); err != nil { return err } } @@ -410,20 +410,15 @@ func (plugin *cniNetworkPlugin) SetUpPod(podNetwork PodNetwork) ([]cnitypes.Resu plugin.podLock(podNetwork).Lock() defer plugin.podUnlock(podNetwork) - _, err := plugin.loNetwork.addToNetwork(plugin.cacheDir, &podNetwork, "lo", "") + _, err := plugin.loNetwork.addToNetwork(plugin.cacheDir, &podNetwork, "lo", RuntimeConfig{}) if err != nil { logrus.Errorf("Error while adding to cni lo network: %s", err) return nil, err } results := make([]cnitypes.Result, 0) - if err := plugin.forEachNetwork(&podNetwork, func(network *cniNetwork, ifName string, podNetwork *PodNetwork) error { - ip := "" - if conf, ok := podNetwork.NetworkConfig[network.name]; ok { - ip = conf.IP - } - - result, err := network.addToNetwork(plugin.cacheDir, podNetwork, ifName, ip) + if err := plugin.forEachNetwork(&podNetwork, func(network *cniNetwork, ifName string, podNetwork *PodNetwork, runtimeConfig RuntimeConfig) error { + result, err := network.addToNetwork(plugin.cacheDir, podNetwork, ifName, runtimeConfig) if err != nil { logrus.Errorf("Error while adding pod to CNI network %q: %s", network.name, err) return err @@ -445,13 +440,8 @@ func (plugin *cniNetworkPlugin) TearDownPod(podNetwork PodNetwork) error { plugin.podLock(podNetwork).Lock() defer plugin.podUnlock(podNetwork) - return plugin.forEachNetwork(&podNetwork, func(network *cniNetwork, ifName string, podNetwork *PodNetwork) error { - ip := "" - if conf, ok := podNetwork.NetworkConfig[network.name]; ok { - ip = conf.IP - } - - if err := network.deleteFromNetwork(plugin.cacheDir, podNetwork, ifName, ip); err != nil { + return plugin.forEachNetwork(&podNetwork, func(network *cniNetwork, ifName string, podNetwork *PodNetwork, runtimeConfig RuntimeConfig) error { + if err := network.deleteFromNetwork(plugin.cacheDir, podNetwork, ifName, runtimeConfig); err != nil { logrus.Errorf("Error while removing pod from CNI network %q: %s", network.name, err) return err } @@ -466,35 +456,15 @@ func (plugin *cniNetworkPlugin) GetPodNetworkStatus(podNetwork PodNetwork) ([]cn defer plugin.podUnlock(podNetwork) results := make([]cnitypes.Result, 0) - if err := plugin.forEachNetwork(&podNetwork, func(network *cniNetwork, ifName string, podNetwork *PodNetwork) error { - version := "4" - ip, mac, err := getContainerDetails(plugin.nsManager, podNetwork.NetNS, ifName, "-4") + if err := plugin.forEachNetwork(&podNetwork, func(network *cniNetwork, ifName string, podNetwork *PodNetwork, runtimeConfig RuntimeConfig) error { + result, err := network.checkNetwork(plugin.cacheDir, podNetwork, ifName, runtimeConfig, plugin.nsManager) if err != nil { - ip, mac, err = getContainerDetails(plugin.nsManager, podNetwork.NetNS, ifName, "-6") - if err != nil { - return err - } - version = "6" + logrus.Errorf("Error while checking pod to CNI network %q: %s", network.name, err) + return err + } + if result != nil { + results = append(results, result) } - - // Until CNI's GET request lands, construct the Result manually - results = append(results, &cnicurrent.Result{ - CNIVersion: "0.3.1", - Interfaces: []*cnicurrent.Interface{ - { - Name: ifName, - Mac: mac.String(), - Sandbox: podNetwork.NetNS, - }, - }, - IPs: []*cnicurrent.IPConfig{ - { - Version: version, - Interface: cnicurrent.Int(0), - Address: *ip, - }, - }, - }) return nil }); err != nil { return nil, err @@ -503,8 +473,8 @@ func (plugin *cniNetworkPlugin) GetPodNetworkStatus(podNetwork PodNetwork) ([]cn return results, nil } -func (network *cniNetwork) addToNetwork(cacheDir string, podNetwork *PodNetwork, ifName, ip string) (cnitypes.Result, error) { - rt, err := buildCNIRuntimeConf(cacheDir, podNetwork, ifName, ip) +func (network *cniNetwork) addToNetwork(cacheDir string, podNetwork *PodNetwork, ifName string, runtimeConfig RuntimeConfig) (cnitypes.Result, error) { + rt, err := buildCNIRuntimeConf(cacheDir, podNetwork, ifName, runtimeConfig) if err != nil { logrus.Errorf("Error adding network: %v", err) return nil, err @@ -521,8 +491,82 @@ func (network *cniNetwork) addToNetwork(cacheDir string, podNetwork *PodNetwork, return res, nil } -func (network *cniNetwork) deleteFromNetwork(cacheDir string, podNetwork *PodNetwork, ifName, ip string) error { - rt, err := buildCNIRuntimeConf(cacheDir, podNetwork, ifName, ip) +func (network *cniNetwork) checkNetwork(cacheDir string, podNetwork *PodNetwork, ifName string, runtimeConfig RuntimeConfig, nsManager *nsManager) (cnitypes.Result, error) { + + rt, err := buildCNIRuntimeConf(cacheDir, podNetwork, ifName, runtimeConfig) + if err != nil { + logrus.Errorf("Error checking network: %v", err) + return nil, err + } + + netconf, cninet := network.NetworkConfig, network.CNIConfig + logrus.Infof("About to check CNI network %s (type=%v)", netconf.Name, netconf.Plugins[0].Network.Type) + + gtet, err := cniversion.GreaterThanOrEqualTo(netconf.CNIVersion, "0.4.0") + if err != nil { + return nil, err + } + + var result cnitypes.Result + + // When CNIVersion supports Check, use it. Otherwise fall back on what was done initially. + if gtet { + err = cninet.CheckNetworkList(context.Background(), netconf, rt) + logrus.Infof("Checking CNI network %s (config version=%v)", netconf.Name, netconf.CNIVersion) + if err != nil { + logrus.Errorf("Error checking network: %v", err) + return nil, err + } + } + + result, err = cninet.GetNetworkListCachedResult(netconf, rt) + if err != nil { + logrus.Errorf("Error GetNetworkListCachedResult: %v", err) + return nil, err + } else if result != nil { + return result, nil + } + + // result doesn't exist, create one + logrus.Infof("Checking CNI network %s (config version=%v) nsManager=%v", netconf.Name, netconf.CNIVersion, nsManager) + + var cniInterface *cnicurrent.Interface + ips := []*cnicurrent.IPConfig{} + errs := []error{} + for _, version := range []string{"4", "6"} { + ip, mac, err := getContainerDetails(nsManager, podNetwork.NetNS, ifName, "-"+version) + if err == nil { + if cniInterface == nil { + cniInterface = &cnicurrent.Interface{ + Name: ifName, + Mac: mac.String(), + Sandbox: podNetwork.NetNS, + } + } + ips = append(ips, &cnicurrent.IPConfig{ + Version: version, + Interface: cnicurrent.Int(0), + Address: *ip, + }) + } else { + errs = append(errs, err) + } + } + if cniInterface == nil || len(ips) == 0 { + return nil, fmt.Errorf("neither IPv4 nor IPv6 found when retrieving network status: %v", errs) + } + + result = &cnicurrent.Result{ + CNIVersion: netconf.CNIVersion, + Interfaces: []*cnicurrent.Interface{cniInterface}, + IPs: ips, + } + + return result, nil +} + +func (network *cniNetwork) deleteFromNetwork(cacheDir string, podNetwork *PodNetwork, ifName string, runtimeConfig RuntimeConfig) error { + rt, err := buildCNIRuntimeConf(cacheDir, podNetwork, ifName, runtimeConfig) if err != nil { logrus.Errorf("Error deleting network: %v", err) return err @@ -538,7 +582,7 @@ func (network *cniNetwork) deleteFromNetwork(cacheDir string, podNetwork *PodNet return nil } -func buildCNIRuntimeConf(cacheDir string, podNetwork *PodNetwork, ifName, ip string) (*libcni.RuntimeConf, error) { +func buildCNIRuntimeConf(cacheDir string, podNetwork *PodNetwork, ifName string, runtimeConfig RuntimeConfig) (*libcni.RuntimeConf, error) { logrus.Infof("Got pod network %+v", podNetwork) rt := &libcni.RuntimeConf{ @@ -552,9 +596,11 @@ func buildCNIRuntimeConf(cacheDir string, podNetwork *PodNetwork, ifName, ip str {"K8S_POD_NAME", podNetwork.Name}, {"K8S_POD_INFRA_CONTAINER_ID", podNetwork.ID}, }, + CapabilityArgs: map[string]interface{}{}, } // Add requested static IP to CNI_ARGS + ip := runtimeConfig.IP if ip != "" { if tstIP := net.ParseIP(ip); tstIP == nil { return nil, fmt.Errorf("unable to parse IP address %q", ip) @@ -562,13 +608,26 @@ func buildCNIRuntimeConf(cacheDir string, podNetwork *PodNetwork, ifName, ip str rt.Args = append(rt.Args, [2]string{"IP", ip}) } - if len(podNetwork.PortMappings) == 0 { - return rt, nil + // Set PortMappings in Capabilities + if len(runtimeConfig.PortMappings) != 0 { + rt.CapabilityArgs["portMappings"] = runtimeConfig.PortMappings + } + + // Set Bandwidth in Capabilities + if runtimeConfig.Bandwidth != nil { + rt.CapabilityArgs["bandwidth"] = map[string]uint64{ + "ingressRate": runtimeConfig.Bandwidth.IngressRate, + "ingressBurst": runtimeConfig.Bandwidth.IngressBurst, + "egressRate": runtimeConfig.Bandwidth.EgressRate, + "egressBurst": runtimeConfig.Bandwidth.EgressBurst, + } } - rt.CapabilityArgs = map[string]interface{}{ - "portMappings": podNetwork.PortMappings, + // Set IpRanges in Capabilities + if len(runtimeConfig.IpRanges) > 0 { + rt.CapabilityArgs["ipRanges"] = runtimeConfig.IpRanges } + return rt, nil } diff --git a/vendor/github.com/cri-o/ocicni/pkg/ocicni/types.go b/vendor/github.com/cri-o/ocicni/pkg/ocicni/types.go index d76094292..8709711e0 100644 --- a/vendor/github.com/cri-o/ocicni/pkg/ocicni/types.go +++ b/vendor/github.com/cri-o/ocicni/pkg/ocicni/types.go @@ -24,12 +24,44 @@ type PortMapping struct { HostIP string `json:"hostIP"` } -// NetworkConfig is additional configuration for a single CNI network. -type NetworkConfig struct { +// IpRange maps to the standard CNI ipRanges Capability +// see: https://github.com/containernetworking/cni/blob/master/CONVENTIONS.md +type IpRange struct { + // Subnet is the whole CIDR + Subnet string `json:"subnet"` + // RangeStart is the first available IP in subnet + RangeStart string `json:"rangeStart,omitempty"` + // RangeEnd is the last available IP in subnet + RangeEnd string `json:"rangeEnd,omitempty"` + // Gateway is the gateway of subnet + Gateway string `json:"gateway,omitempty"` +} + +// RuntimeConfig is additional configuration for a single CNI network that +// is pod-specific rather than general to the network. +type RuntimeConfig struct { // IP is a static IP to be specified in the network. Can only be used // with the hostlocal IP allocator. If left unset, an IP will be // dynamically allocated. IP string + // PortMappings is the port mapping of the sandbox. + PortMappings []PortMapping + // Bandwidth is the bandwidth limiting of the pod + Bandwidth *BandwidthConfig + // IpRanges is the ip range gather which is used for address allocation + IpRanges [][]IpRange +} + +// BandwidthConfig maps to the standard CNI bandwidth Capability +// see: https://github.com/containernetworking/cni/blob/master/CONVENTIONS.md +type BandwidthConfig struct { + // IngressRate is a limit for incoming traffic in bps + IngressRate uint64 + IngressBurst uint64 + + // EgressRate is a limit for outgoing traffic in bps + EgressRate uint64 + EgressBurst uint64 } // PodNetwork configures the network of a pod sandbox. @@ -42,8 +74,6 @@ type PodNetwork struct { ID string // NetNS is the network namespace path of the sandbox. NetNS string - // PortMappings is the port mapping of the sandbox. - PortMappings []PortMapping // Networks is a list of CNI network names to attach to the sandbox // Leave this list empty to attach the default network to the sandbox @@ -52,7 +82,7 @@ type PodNetwork struct { // NetworkConfig is configuration specific to a single CNI network. // It is optional, and can be omitted for some or all specified networks // without issue. - NetworkConfig map[string]NetworkConfig + RuntimeConfig map[string]RuntimeConfig } // CNIPlugin is the interface that needs to be implemented by a plugin diff --git a/vendor/modules.txt b/vendor/modules.txt index 914472508..6a7c04267 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -98,14 +98,14 @@ github.com/containers/image/pkg/compression github.com/containers/image/pkg/blobinfocache/boltdb github.com/containers/image/pkg/blobinfocache/memory github.com/containers/image/pkg/blobinfocache/internal/prioritize -# github.com/containers/psgo v1.3.0 +# github.com/containers/psgo v1.3.1 github.com/containers/psgo github.com/containers/psgo/internal/capabilities github.com/containers/psgo/internal/dev github.com/containers/psgo/internal/proc github.com/containers/psgo/internal/process github.com/containers/psgo/internal/host -# github.com/containers/storage v1.12.12 +# github.com/containers/storage v1.12.13 github.com/containers/storage github.com/containers/storage/pkg/archive github.com/containers/storage/pkg/chrootarchive @@ -153,7 +153,7 @@ github.com/coreos/go-systemd/sdjournal github.com/coreos/go-systemd/journal # github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f github.com/coreos/pkg/dlopen -# github.com/cri-o/ocicni v0.0.0-20190328132530-0c180f981b27 +# github.com/cri-o/ocicni v0.1.1-0.20190702175919-7762645d18ca github.com/cri-o/ocicni/pkg/ocicni # github.com/cyphar/filepath-securejoin v0.2.2 github.com/cyphar/filepath-securejoin diff --git a/version/version.go b/version/version.go index f19d56c31..286f66093 100644 --- a/version/version.go +++ b/version/version.go @@ -4,7 +4,7 @@ package version // NOTE: remember to bump the version at the top // of the top-level README.md file when this is // bumped. -const Version = "1.4.4-dev" +const Version = "1.4.5-dev" // RemoteAPIVersion is the version for the remote // client API. It is used to determine compatibility |