From 7c2c9a8c85e8fb3278a49dcded77499e06834535 Mon Sep 17 00:00:00 2001 From: Mrunal Patel Date: Tue, 10 Oct 2017 16:23:54 -0700 Subject: [PATCH 01/43] test: Fix format specifier Signed-off-by: Mrunal Patel --- server/inspect_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/inspect_test.go b/server/inspect_test.go index 8c892e43..e0479494 100644 --- a/server/inspect_test.go +++ b/server/inspect_test.go @@ -96,7 +96,7 @@ func TestGetContainerInfo(t *testing.T) { t.Fatalf("expected same created time %d, got %d", created.UnixNano(), ci.CreatedTime) } if ci.Pid != 42 { - t.Fatalf("expected pid 42, got %s", ci.Pid) + t.Fatalf("expected pid 42, got %v", ci.Pid) } if ci.Name != "testname" { t.Fatalf("expected name testname, got %s", ci.Name) From 3907e0d346adbbc93497034cff4c4cd648c9c8b9 Mon Sep 17 00:00:00 2001 From: baude Date: Mon, 11 Sep 2017 15:51:12 -0500 Subject: [PATCH 02/43] Return Valid JSON for empty data For commands that ask for JSON results, if the input to the Go JSON marshaller is empty, it will return a byte array with a literal "null" in it. If that is the case, we should output [] instead as at least that is valid JSON and will not break consumers of the data. Signed-off-by: baude --- cmd/kpod/formats/formats.go | 11 +++++++++++ cmd/kpod/ps.go | 4 +++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/cmd/kpod/formats/formats.go b/cmd/kpod/formats/formats.go index 007f09c6..6e5dd242 100644 --- a/cmd/kpod/formats/formats.go +++ b/cmd/kpod/formats/formats.go @@ -8,6 +8,7 @@ import ( "text/tabwriter" "text/template" + "bytes" "github.com/ghodss/yaml" "github.com/pkg/errors" ) @@ -59,6 +60,16 @@ func (j JSONStructArray) Out() error { if err != nil { return err } + + // JSON returns a byte array with a literal null [110 117 108 108] in it + // if it is passed empty data. We used bytes.Compare to see if that is + // the case. + if diff := bytes.Compare(data, []byte("null")); diff == 0 { + data = []byte("[]") + } + + // If the we did get NULL back, we should spit out {} which is + // at least valid JSON for the consumer. fmt.Printf("%s\n", data) return nil } diff --git a/cmd/kpod/ps.go b/cmd/kpod/ps.go index 11dcae5e..76bc8b8b 100644 --- a/cmd/kpod/ps.go +++ b/cmd/kpod/ps.go @@ -398,7 +398,9 @@ func getJSONOutput(containers []*libkpod.ContainerData, nSpace bool) (psOutput [ func generatePsOutput(containers []*libkpod.ContainerData, server *libkpod.ContainerServer, opts psOptions) error { containersOutput := getContainers(containers, opts) - if len(containersOutput) == 0 { + // In the case of JSON, we want to continue so we at least pass + // {} --valid JSON-- to the consumer + if len(containersOutput) == 0 && opts.format != formats.JSONString { return nil } From bb4b2e9fea27753ed07aaf3354cc05d16058f09e Mon Sep 17 00:00:00 2001 From: Mrunal Patel Date: Wed, 11 Oct 2017 14:49:35 -0700 Subject: [PATCH 03/43] test: Modify Fatal to Fatalf as we have a specifier Signed-off-by: Mrunal Patel --- server/inspect_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/inspect_test.go b/server/inspect_test.go index e0479494..7be46c4e 100644 --- a/server/inspect_test.go +++ b/server/inspect_test.go @@ -114,7 +114,7 @@ func TestGetContainerInfo(t *testing.T) { t.Fatalf("expected sandbox to be testsandboxid, got %s", ci.Sandbox) } if ci.IP != "1.1.1.42" { - t.Fatal("expected ip 1.1.1.42, got %s", ci.IP) + t.Fatalf("expected ip 1.1.1.42, got %s", ci.IP) } if len(ci.Annotations) == 0 { t.Fatal("annotations are empty") From d5b5028cb93fbf51342e1e1620a05492864950d1 Mon Sep 17 00:00:00 2001 From: umohnani8 Date: Fri, 22 Sep 2017 11:10:15 -0400 Subject: [PATCH 04/43] Add secrets patch to crio Allows the user to define secret paths in /etc/containers/mounts.conf These are then volume mounted into the container Signed-off-by: umohnani8 --- cmd/crio/config.go | 3 + cmd/crio/main.go | 8 ++ libkpod/config.go | 8 ++ server/container_create.go | 25 +++++ server/secrets.go | 186 +++++++++++++++++++++++++++++++++++++ test/crio_secrets.bats | 44 +++++++++ test/helpers.bash | 9 +- 7 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 server/secrets.go create mode 100644 test/crio_secrets.bats diff --git a/cmd/crio/config.go b/cmd/crio/config.go index 855bd466..af1af302 100644 --- a/cmd/crio/config.go +++ b/cmd/crio/config.go @@ -108,6 +108,9 @@ cgroup_manager = "{{ .CgroupManager }}" # hooks_dir_path is the oci hooks directory for automatically executed hooks hooks_dir_path = "{{ .HooksDirPath }}" +# default_mounts_path is the secrets mounts file path +default_mounts_path = "{{ .DefaultMountsPath }}" + # pids_limit is the number of processes allowed in a container pids_limit = {{ .PidsLimit }} diff --git a/cmd/crio/main.go b/cmd/crio/main.go index 93044e88..1059d60f 100644 --- a/cmd/crio/main.go +++ b/cmd/crio/main.go @@ -127,6 +127,9 @@ func mergeConfig(config *server.Config, ctx *cli.Context) error { if ctx.GlobalIsSet("hooks-dir-path") { config.HooksDirPath = ctx.GlobalString("hooks-dir-path") } + if ctx.GlobalIsSet("default-mounts-path") { + config.DefaultMountsPath = ctx.GlobalString("default-mounts-path") + } if ctx.GlobalIsSet("pids-limit") { config.PidsLimit = ctx.GlobalInt64("pids-limit") } @@ -322,6 +325,11 @@ func main() { Value: libkpod.DefaultHooksDirPath, Hidden: true, }, + cli.StringFlag{ + Name: "default-mounts-path", + Usage: "set the default mounts file path", + Hidden: true, + }, cli.BoolFlag{ Name: "profile", Usage: "enable pprof remote profiler on localhost:6060", diff --git a/libkpod/config.go b/libkpod/config.go index 123bece8..1fd86a3b 100644 --- a/libkpod/config.go +++ b/libkpod/config.go @@ -24,6 +24,10 @@ const ( cgroupManager = oci.CgroupfsCgroupsManager lockPath = "/run/crio.lock" containerExitsDir = oci.ContainerExitsDir + // DefaultMountsFile holds the default mount paths in the form "host:container" + DefaultMountsFile = "/usr/share/containers/mounts.conf" + // OverrideMountsFile holds the override mount paths in the form "host:container" + OverrideMountsFile = "/etc/containers/mounts.conf" ) // Config represents the entire set of configuration values that can be set for @@ -145,6 +149,9 @@ type RuntimeConfig struct { // HooksDirPath location of oci hooks config files HooksDirPath string `toml:"hooks_dir_path"` + // DefaultMountsPath location of the default mounts file + DefaultMountsPath string `toml:"default_mounts_path"` + // Hooks List of hooks to run with container Hooks map[string]HookParams @@ -288,6 +295,7 @@ func DefaultConfig() *Config { ContainerExitsDir: containerExitsDir, HooksDirPath: DefaultHooksDirPath, LogSizeMax: DefaultLogSizeMax, + DefaultMountsPath: DefaultMountsFile, }, ImageConfig: ImageConfig{ DefaultTransport: defaultTransport, diff --git a/server/container_create.go b/server/container_create.go index 6d93408c..1e036554 100644 --- a/server/container_create.go +++ b/server/container_create.go @@ -384,6 +384,27 @@ func ensureSaneLogPath(logPath string) error { return nil } +// addSecretsBindMounts mounts user defined secrets to the container +func addSecretsBindMounts(mountLabel, ctrRunDir, configDefaultMountsPath string, specgen generate.Generator) error { + mountPaths := []string{libkpod.OverrideMountsFile, libkpod.DefaultMountsFile} + // configDefaultMountsPath is used to override the mount file path for testing purposes only when set in the runtime config + if configDefaultMountsPath != "" { + mountPaths = []string{configDefaultMountsPath} + } + for _, path := range mountPaths { + containerMounts := specgen.Spec().Mounts + mounts, err := secretMounts(mountLabel, path, ctrRunDir, containerMounts) + if err != nil { + return err + } + for _, m := range mounts { + specgen.AddBindMount(m.Source, m.Destination, nil) + + } + } + return nil +} + // CreateContainer creates a new container in specified PodSandbox func (s *Server) CreateContainer(ctx context.Context, req *pb.CreateContainerRequest) (res *pb.CreateContainerResponse, err error) { logrus.Debugf("CreateContainerRequest %+v", req) @@ -911,6 +932,10 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string, return nil, err } + if err = addSecretsBindMounts(mountLabel, containerInfo.RunDir, s.config.DefaultMountsPath, specgen); err != nil { + return nil, fmt.Errorf("failed to mount secrets: %v", err) + } + mountPoint, err := s.StorageRuntimeServer().StartContainer(containerID) if err != nil { return nil, fmt.Errorf("failed to mount container %s(%s): %v", containerName, containerID, err) diff --git a/server/secrets.go b/server/secrets.go new file mode 100644 index 00000000..34236c1f --- /dev/null +++ b/server/secrets.go @@ -0,0 +1,186 @@ +package server + +import ( + "bufio" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + + rspec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/selinux/go-selinux/label" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// SecretData info +type SecretData struct { + Name string + Data []byte +} + +// SaveTo saves secret data to given directory +func (s SecretData) SaveTo(dir string) error { + path := filepath.Join(dir, s.Name) + if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil && !os.IsExist(err) { + return err + } + return ioutil.WriteFile(path, s.Data, 0700) +} + +// readMountFile returns a list of the host:container paths +func readMountFile(mountFilePath string) ([]string, error) { + var mountPaths []string + file, err := os.Open(mountFilePath) + if err != nil { + logrus.Warnf("file doesn't exist %q", mountFilePath) + return nil, nil + } + defer file.Close() + + scanner := bufio.NewScanner(file) + scanner.Split(bufio.ScanLines) + for scanner.Scan() { + mountPaths = append(mountPaths, scanner.Text()) + } + + return mountPaths, nil +} + +func readAll(root, prefix string) ([]SecretData, error) { + path := filepath.Join(root, prefix) + + data := []SecretData{} + + files, err := ioutil.ReadDir(path) + if err != nil { + if os.IsNotExist(err) { + return data, nil + } + + return nil, err + } + + for _, f := range files { + fileData, err := readFile(root, filepath.Join(prefix, f.Name())) + if err != nil { + // If the file did not exist, might be a dangling symlink + // Ignore the error + if os.IsNotExist(err) { + continue + } + return nil, err + } + data = append(data, fileData...) + } + + return data, nil +} + +func readFile(root, name string) ([]SecretData, error) { + path := filepath.Join(root, name) + + s, err := os.Stat(path) + if err != nil { + return nil, err + } + + if s.IsDir() { + dirData, err := readAll(root, name) + if err != nil { + return nil, err + } + return dirData, nil + } + bytes, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + return []SecretData{{Name: name, Data: bytes}}, nil +} + +// getHostAndCtrDir separates the host:container paths +func getMountsMap(path string) (string, string, error) { + arr := strings.SplitN(path, ":", 2) + if len(arr) == 2 { + return arr[0], arr[1], nil + } + return "", "", errors.Errorf("unable to get host and container dir") +} + +func getHostSecretData(hostDir string) ([]SecretData, error) { + var allSecrets []SecretData + hostSecrets, err := readAll(hostDir, "") + if err != nil { + return nil, errors.Wrapf(err, "failed to read secrets from %q", hostDir) + } + return append(allSecrets, hostSecrets...), nil +} + +// secretMount copies the contents of host directory to container directory +// and returns a list of mounts +func secretMounts(mountLabel, mountFilePath, containerWorkingDir string, runtimeMounts []rspec.Mount) ([]rspec.Mount, error) { + var mounts []rspec.Mount + mountPaths, err := readMountFile(mountFilePath) + if err != nil { + return nil, err + } + for _, path := range mountPaths { + hostDir, ctrDir, err := getMountsMap(path) + if err != nil { + return nil, err + } + // skip if the hostDir path doesn't exist + if _, err := os.Stat(hostDir); os.IsNotExist(err) { + logrus.Warnf("%q doesn't exist, skipping", hostDir) + continue + } + + ctrDir = filepath.Join(containerWorkingDir, ctrDir) + // skip if ctrDir has already been mounted by caller + if isAlreadyMounted(runtimeMounts, ctrDir) { + logrus.Warnf("%q has already been mounted; cannot override mount", ctrDir) + continue + } + + if err := os.RemoveAll(ctrDir); err != nil { + return nil, fmt.Errorf("remove container directory failed: %v", err) + } + + if err := os.MkdirAll(ctrDir, 0755); err != nil { + return nil, fmt.Errorf("making container directory failed: %v", err) + } + + hostDir, err = resolveSymbolicLink(hostDir) + if err != nil { + return nil, err + } + + data, err := getHostSecretData(hostDir) + if err != nil { + return nil, errors.Wrapf(err, "getting host secret data failed") + } + for _, s := range data { + s.SaveTo(ctrDir) + } + label.Relabel(ctrDir, mountLabel, false) + + m := rspec.Mount{ + Source: hostDir, + Destination: ctrDir, + } + + mounts = append(mounts, m) + } + return mounts, nil +} + +func isAlreadyMounted(mounts []rspec.Mount, mountPath string) bool { + for _, mount := range mounts { + if mount.Destination == mountPath { + return true + } + } + return false +} diff --git a/test/crio_secrets.bats b/test/crio_secrets.bats new file mode 100644 index 00000000..83945aea --- /dev/null +++ b/test/crio_secrets.bats @@ -0,0 +1,44 @@ +#!/usr/bin/env bats + +load helpers + +IMAGE="redis:alpine" + +function teardown() { + cleanup_test +} + +function setup() { + MOUNT_PATH="$TESTDIR/secrets" + mkdir ${MOUNT_PATH} + MOUNT_FILE="${MOUNT_PATH}/test.txt" + touch ${MOUNT_FILE} + echo "Testing secrets mounts!" > ${MOUNT_FILE} + + echo "${MOUNT_PATH}:/container/path1" > ${DEFAULT_MOUNTS_FILE} +} + +@test "bind secrets mounts to container" { + start_crio + run crioctl pod run --config "$TESTDATA"/sandbox_config.json + echo "$output" + [ "$status" -eq 0 ] + pod_id="$output" + run crioctl image pull "$IMAGE" + [ "$status" -eq 0 ] + run crioctl ctr create --config "$TESTDATA"/container_redis.json --pod "$pod_id" + echo "$output" + [ "$status" -eq 0 ] + ctr_id="$output" + run crioctl ctr execsync --id "$ctr_id" mount + echo "$output" + [ "$status" -eq 0 ] + mount_info="$output" + grep $ctr_id/userdata/container/path1 <<< "$mount_info" + echo "$output" + [ "$status" -eq 0 ] + rm -rf MOUNT_PATH + cleanup_ctrs + cleanup_pods + stop_crio +} diff --git a/test/helpers.bash b/test/helpers.bash index ac30f22d..8d38f1fd 100644 --- a/test/helpers.bash +++ b/test/helpers.bash @@ -69,6 +69,13 @@ HOOKSDIR=$TESTDIR/hooks mkdir ${HOOKSDIR} HOOKS_OPTS="--hooks-dir-path=$HOOKSDIR" +# Setup default secrets mounts file +MOUNTS_DIR="$TESTDIR/containers" +mkdir ${MOUNTS_DIR} +DEFAULT_MOUNTS_FILE="${MOUNTS_DIR}/mounts.conf" +touch ${DEFAULT_MOUNTS_FILE} +DEFAULT_MOUNTS_OPTS="--default-mounts-path=$DEFAULT_MOUNTS_FILE" + # We may need to set some default storage options. case "$(stat -f -c %T ${TESTDIR})" in aufs) @@ -235,7 +242,7 @@ function start_crio() { "$COPYIMG_BINARY" --root "$TESTDIR/crio" $STORAGE_OPTS --runroot "$TESTDIR/crio-run" --image-name=mrunalp/image-volume-test --import-from=dir:"$ARTIFACTS_PATH"/image-volume-test-image --add-name=docker.io/library/mrunalp/image-volume-test --signature-policy="$INTEGRATION_ROOT"/policy.json "$COPYIMG_BINARY" --root "$TESTDIR/crio" $STORAGE_OPTS --runroot "$TESTDIR/crio-run" --image-name=busybox:latest --import-from=dir:"$ARTIFACTS_PATH"/busybox-image --add-name=docker.io/library/busybox:latest --signature-policy="$INTEGRATION_ROOT"/policy.json "$COPYIMG_BINARY" --root "$TESTDIR/crio" $STORAGE_OPTS --runroot "$TESTDIR/crio-run" --image-name=runcom/stderr-test:latest --import-from=dir:"$ARTIFACTS_PATH"/stderr-test --add-name=docker.io/runcom/stderr-test:latest --signature-policy="$INTEGRATION_ROOT"/policy.json - "$CRIO_BINARY" ${HOOKS_OPTS} --conmon "$CONMON_BINARY" --listen "$CRIO_SOCKET" --cgroup-manager "$CGROUP_MANAGER" --registry "docker.io" --runtime "$RUNTIME_BINARY" --root "$TESTDIR/crio" --runroot "$TESTDIR/crio-run" $STORAGE_OPTS --seccomp-profile "$seccomp" --apparmor-profile "$apparmor" --cni-config-dir "$CRIO_CNI_CONFIG" --cni-plugin-dir "$CRIO_CNI_PLUGIN" --signature-policy "$INTEGRATION_ROOT"/policy.json --image-volumes "$IMAGE_VOLUMES" --pids-limit "$PIDS_LIMIT" --log-size-max "$LOG_SIZE_MAX_LIMIT" --config /dev/null config >$CRIO_CONFIG + "$CRIO_BINARY" ${DEFAULT_MOUNTS_OPTS} ${HOOKS_OPTS} --conmon "$CONMON_BINARY" --listen "$CRIO_SOCKET" --cgroup-manager "$CGROUP_MANAGER" --registry "docker.io" --runtime "$RUNTIME_BINARY" --root "$TESTDIR/crio" --runroot "$TESTDIR/crio-run" $STORAGE_OPTS --seccomp-profile "$seccomp" --apparmor-profile "$apparmor" --cni-config-dir "$CRIO_CNI_CONFIG" --signature-policy "$INTEGRATION_ROOT"/policy.json --image-volumes "$IMAGE_VOLUMES" --pids-limit "$PIDS_LIMIT" --log-size-max "$LOG_SIZE_MAX_LIMIT" --config /dev/null config >$CRIO_CONFIG # Prepare the CNI configuration files, we're running with non host networking by default if [[ -n "$4" ]]; then From d1aea317869fc752a3177f32addf38489b76b452 Mon Sep 17 00:00:00 2001 From: umohnani8 Date: Thu, 12 Oct 2017 14:14:42 -0400 Subject: [PATCH 05/43] Follow up changes on secrets patch Deleted mounts.conf file and moved the secrets mount paths to a list (default-mounts) in crio.conf Signed-off-by: umohnani8 --- cmd/crio/config.go | 5 +++-- cmd/crio/main.go | 10 +++++----- docs/crio.conf.5.md | 3 +++ libkpod/config.go | 10 +++------- server/container_create.go | 27 +++++++++++---------------- server/secrets.go | 28 ++-------------------------- test/crio_secrets.bats | 10 ---------- test/helpers.bash | 14 ++++++++------ 8 files changed, 35 insertions(+), 72 deletions(-) diff --git a/cmd/crio/config.go b/cmd/crio/config.go index af1af302..76e3361d 100644 --- a/cmd/crio/config.go +++ b/cmd/crio/config.go @@ -108,8 +108,9 @@ cgroup_manager = "{{ .CgroupManager }}" # hooks_dir_path is the oci hooks directory for automatically executed hooks hooks_dir_path = "{{ .HooksDirPath }}" -# default_mounts_path is the secrets mounts file path -default_mounts_path = "{{ .DefaultMountsPath }}" +# default_mounts is the mounts list to be mounted for the container when created +default_mounts = [ +{{ range $mount := .DefaultMounts }}{{ printf "\t%q, \n" $mount }}{{ end }}] # pids_limit is the number of processes allowed in a container pids_limit = {{ .PidsLimit }} diff --git a/cmd/crio/main.go b/cmd/crio/main.go index 1059d60f..2446bdf6 100644 --- a/cmd/crio/main.go +++ b/cmd/crio/main.go @@ -127,8 +127,8 @@ func mergeConfig(config *server.Config, ctx *cli.Context) error { if ctx.GlobalIsSet("hooks-dir-path") { config.HooksDirPath = ctx.GlobalString("hooks-dir-path") } - if ctx.GlobalIsSet("default-mounts-path") { - config.DefaultMountsPath = ctx.GlobalString("default-mounts-path") + if ctx.GlobalIsSet("default-mounts") { + config.DefaultMounts = ctx.GlobalStringSlice("default-mounts") } if ctx.GlobalIsSet("pids-limit") { config.PidsLimit = ctx.GlobalInt64("pids-limit") @@ -325,9 +325,9 @@ func main() { Value: libkpod.DefaultHooksDirPath, Hidden: true, }, - cli.StringFlag{ - Name: "default-mounts-path", - Usage: "set the default mounts file path", + cli.StringSliceFlag{ + Name: "default-mounts", + Usage: "add one or more default mount paths in the form host:container", Hidden: true, }, cli.BoolFlag{ diff --git a/docs/crio.conf.5.md b/docs/crio.conf.5.md index ced28c37..32cac7a4 100644 --- a/docs/crio.conf.5.md +++ b/docs/crio.conf.5.md @@ -105,6 +105,9 @@ Example: **no_pivot**=*true*|*false* Instructs the runtime to not use pivot_root, but instead use MS_MOVE +**default_mounts**=[] + List of mount points, in the form host:container, to be mounted in every container + ## CRIO.IMAGE TABLE **default_transport** diff --git a/libkpod/config.go b/libkpod/config.go index 1fd86a3b..687b4b38 100644 --- a/libkpod/config.go +++ b/libkpod/config.go @@ -24,10 +24,6 @@ const ( cgroupManager = oci.CgroupfsCgroupsManager lockPath = "/run/crio.lock" containerExitsDir = oci.ContainerExitsDir - // DefaultMountsFile holds the default mount paths in the form "host:container" - DefaultMountsFile = "/usr/share/containers/mounts.conf" - // OverrideMountsFile holds the override mount paths in the form "host:container" - OverrideMountsFile = "/etc/containers/mounts.conf" ) // Config represents the entire set of configuration values that can be set for @@ -149,8 +145,9 @@ type RuntimeConfig struct { // HooksDirPath location of oci hooks config files HooksDirPath string `toml:"hooks_dir_path"` - // DefaultMountsPath location of the default mounts file - DefaultMountsPath string `toml:"default_mounts_path"` + // DefaultMounts is the list of mounts to be mounted for each container + // The format of each mount is "host-path:container-path" + DefaultMounts []string `toml:"default_mounts"` // Hooks List of hooks to run with container Hooks map[string]HookParams @@ -295,7 +292,6 @@ func DefaultConfig() *Config { ContainerExitsDir: containerExitsDir, HooksDirPath: DefaultHooksDirPath, LogSizeMax: DefaultLogSizeMax, - DefaultMountsPath: DefaultMountsFile, }, ImageConfig: ImageConfig{ DefaultTransport: defaultTransport, diff --git a/server/container_create.go b/server/container_create.go index 1e036554..18b33f2e 100644 --- a/server/container_create.go +++ b/server/container_create.go @@ -385,22 +385,15 @@ func ensureSaneLogPath(logPath string) error { } // addSecretsBindMounts mounts user defined secrets to the container -func addSecretsBindMounts(mountLabel, ctrRunDir, configDefaultMountsPath string, specgen generate.Generator) error { - mountPaths := []string{libkpod.OverrideMountsFile, libkpod.DefaultMountsFile} - // configDefaultMountsPath is used to override the mount file path for testing purposes only when set in the runtime config - if configDefaultMountsPath != "" { - mountPaths = []string{configDefaultMountsPath} +func addSecretsBindMounts(mountLabel, ctrRunDir string, defaultMounts []string, specgen generate.Generator) error { + containerMounts := specgen.Spec().Mounts + mounts, err := secretMounts(defaultMounts, mountLabel, ctrRunDir, containerMounts) + if err != nil { + return err } - for _, path := range mountPaths { - containerMounts := specgen.Spec().Mounts - mounts, err := secretMounts(mountLabel, path, ctrRunDir, containerMounts) - if err != nil { - return err - } - for _, m := range mounts { - specgen.AddBindMount(m.Source, m.Destination, nil) + for _, m := range mounts { + specgen.AddBindMount(m.Source, m.Destination, nil) - } } return nil } @@ -932,8 +925,10 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string, return nil, err } - if err = addSecretsBindMounts(mountLabel, containerInfo.RunDir, s.config.DefaultMountsPath, specgen); err != nil { - return nil, fmt.Errorf("failed to mount secrets: %v", err) + if len(s.config.DefaultMounts) > 0 { + if err = addSecretsBindMounts(mountLabel, containerInfo.RunDir, s.config.DefaultMounts, specgen); err != nil { + return nil, fmt.Errorf("failed to mount secrets: %v", err) + } } mountPoint, err := s.StorageRuntimeServer().StartContainer(containerID) diff --git a/server/secrets.go b/server/secrets.go index 34236c1f..c512aede 100644 --- a/server/secrets.go +++ b/server/secrets.go @@ -1,7 +1,6 @@ package server import ( - "bufio" "fmt" "io/ioutil" "os" @@ -29,25 +28,6 @@ func (s SecretData) SaveTo(dir string) error { return ioutil.WriteFile(path, s.Data, 0700) } -// readMountFile returns a list of the host:container paths -func readMountFile(mountFilePath string) ([]string, error) { - var mountPaths []string - file, err := os.Open(mountFilePath) - if err != nil { - logrus.Warnf("file doesn't exist %q", mountFilePath) - return nil, nil - } - defer file.Close() - - scanner := bufio.NewScanner(file) - scanner.Split(bufio.ScanLines) - for scanner.Scan() { - mountPaths = append(mountPaths, scanner.Text()) - } - - return mountPaths, nil -} - func readAll(root, prefix string) ([]SecretData, error) { path := filepath.Join(root, prefix) @@ -120,13 +100,9 @@ func getHostSecretData(hostDir string) ([]SecretData, error) { // secretMount copies the contents of host directory to container directory // and returns a list of mounts -func secretMounts(mountLabel, mountFilePath, containerWorkingDir string, runtimeMounts []rspec.Mount) ([]rspec.Mount, error) { +func secretMounts(defaultMountsPaths []string, mountLabel, containerWorkingDir string, runtimeMounts []rspec.Mount) ([]rspec.Mount, error) { var mounts []rspec.Mount - mountPaths, err := readMountFile(mountFilePath) - if err != nil { - return nil, err - } - for _, path := range mountPaths { + for _, path := range defaultMountsPaths { hostDir, ctrDir, err := getMountsMap(path) if err != nil { return nil, err diff --git a/test/crio_secrets.bats b/test/crio_secrets.bats index 83945aea..fe1e5230 100644 --- a/test/crio_secrets.bats +++ b/test/crio_secrets.bats @@ -8,16 +8,6 @@ function teardown() { cleanup_test } -function setup() { - MOUNT_PATH="$TESTDIR/secrets" - mkdir ${MOUNT_PATH} - MOUNT_FILE="${MOUNT_PATH}/test.txt" - touch ${MOUNT_FILE} - echo "Testing secrets mounts!" > ${MOUNT_FILE} - - echo "${MOUNT_PATH}:/container/path1" > ${DEFAULT_MOUNTS_FILE} -} - @test "bind secrets mounts to container" { start_crio run crioctl pod run --config "$TESTDATA"/sandbox_config.json diff --git a/test/helpers.bash b/test/helpers.bash index 8d38f1fd..03079865 100644 --- a/test/helpers.bash +++ b/test/helpers.bash @@ -69,12 +69,14 @@ HOOKSDIR=$TESTDIR/hooks mkdir ${HOOKSDIR} HOOKS_OPTS="--hooks-dir-path=$HOOKSDIR" -# Setup default secrets mounts file -MOUNTS_DIR="$TESTDIR/containers" -mkdir ${MOUNTS_DIR} -DEFAULT_MOUNTS_FILE="${MOUNTS_DIR}/mounts.conf" -touch ${DEFAULT_MOUNTS_FILE} -DEFAULT_MOUNTS_OPTS="--default-mounts-path=$DEFAULT_MOUNTS_FILE" +# Setup default secrets mounts +MOUNT_PATH="$TESTDIR/secrets" +mkdir ${MOUNT_PATH} +MOUNT_FILE="${MOUNT_PATH}/test.txt" +touch ${MOUNT_FILE} +echo "Testing secrets mounts!" > ${MOUNT_FILE} + +DEFAULT_MOUNTS_OPTS="--default-mounts=${MOUNT_PATH}:/container/path1" # We may need to set some default storage options. case "$(stat -f -c %T ${TESTDIR})" in From a88f6840d8b86afe84762b4f4cfa03df30d5416d Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Tue, 10 Oct 2017 17:51:28 -0400 Subject: [PATCH 06/43] Look up the container's name for kpod-stop-by-name In the kpod-stop-by-name test, use 'kpod inspect' to look up the name of the container, rather than predicting the name that crio will assign. Signed-off-by: Nalin Dahyabhai --- contrib/test/integration/system.yml | 2 +- test/kpod_stop.bats | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/contrib/test/integration/system.yml b/contrib/test/integration/system.yml index 6adc4f2a..da1e8a93 100644 --- a/contrib/test/integration/system.yml +++ b/contrib/test/integration/system.yml @@ -108,4 +108,4 @@ - name: Update the kernel cmdline to include quota support command: grubby --update-kernel=ALL --args="rootflags=pquota" - when: ansible_distribution in ['RedHat', 'CentOS'] \ No newline at end of file + when: ansible_distribution in ['RedHat', 'CentOS'] diff --git a/test/kpod_stop.bats b/test/kpod_stop.bats index 08b4c933..2ba7899a 100644 --- a/test/kpod_stop.bats +++ b/test/kpod_stop.bats @@ -41,8 +41,15 @@ function teardown() { [ "$status" -eq 0 ] ctr_id="$output" run crioctl ctr start --id "$ctr_id" + [ "$status" -eq 0 ] + run crioctl ctr inspect --id "$ctr_id" echo "$output" - run ${KPOD_BINARY} ${KPOD_OPTIONS} stop "k8s_podsandbox1-redis_podsandbox1_redhat.test.crio_redhat-test-crio_0" + [ "$status" -eq 0 ] + ctr_name=$(python -c 'import json; import sys; print json.load(sys.stdin)["crio_annotations"]["io.kubernetes.cri-o.Name"]' <<< "$output") + echo container name is \""$ctr_name"\" + run ${KPOD_BINARY} ${KPOD_OPTIONS} stop "$ctr_name" + echo "$output" + [ "$status" -eq 0 ] cleanup_pods stop_crio } From ddb8fb30cc7bd14e0e700948770f729a484f542b Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Thu, 28 Sep 2017 13:46:07 -0400 Subject: [PATCH 07/43] Correct our usage of the bats run helper The bats "run" helper function sets "$status", so there's no point to checking the value of "$status" when we haven't used the "run" helper to run a command, and we almost always want to be checking the value after we have used the helper. There's no need to run commands like 'sleep' or 'rm -f' with the helper, since they're not expected to fail, and if they do, it's probably indicative of a larger problem that we want to allow to cause tests to fail. Helper functions like start_crio already check "$status" when they call "run", so we don't need to check it again after they return. Signed-off-by: Nalin Dahyabhai --- test/apparmor.bats | 4 ---- test/ctr.bats | 3 ++- test/hooks.bats | 2 +- test/kpod_images.bats | 1 + test/kpod_pause.bats | 3 +++ test/kpod_ps.bats | 4 ---- test/kpod_rename.bats | 1 + test/kpod_save.bats | 1 - test/kpod_stop.bats | 3 +++ test/kpod_wait.bats | 3 +++ test/network.bats | 5 +++-- 11 files changed, 17 insertions(+), 13 deletions(-) diff --git a/test/apparmor.bats b/test/apparmor.bats index babfb170..e5c89bf0 100644 --- a/test/apparmor.bats +++ b/test/apparmor.bats @@ -27,7 +27,6 @@ function teardown() { echo "$output" [ "$status" -eq 0 ] ctr_id="$output" - [ "$status" -eq 0 ] run crioctl ctr execsync --id "$ctr_id" touch test.txt echo "$output" [ "$status" -eq 0 ] @@ -60,7 +59,6 @@ function teardown() { echo "$output" [ "$status" -eq 0 ] ctr_id="$output" - [ "$status" -eq 0 ] run crioctl ctr execsync --id "$ctr_id" touch test.txt echo "$output" [ "$status" -ne 0 ] @@ -94,7 +92,6 @@ function teardown() { echo "$output" [ "$status" -eq 0 ] ctr_id="$output" - [ "$status" -eq 0 ] run crioctl ctr execsync --id "$ctr_id" touch test.txt echo "$output" [ "$status" -ne 0 ] @@ -156,7 +153,6 @@ function teardown() { echo "$output" [ "$status" -eq 0 ] ctr_id="$output" - [ "$status" -eq 0 ] run crioctl ctr execsync --id "$ctr_id" touch test.txt echo "$output" [ "$status" -eq 0 ] diff --git a/test/ctr.bats b/test/ctr.bats index 79eae2a3..2f225187 100644 --- a/test/ctr.bats +++ b/test/ctr.bats @@ -560,6 +560,7 @@ function teardown() { run crioctl ctr execsync --id "$ctr_id" --timeout 1 sleep 10 echo "$output" [[ "$output" =~ "command timed out" ]] + [ "$status" -ne 0 ] run crioctl pod stop --id "$pod_id" echo "$output" [ "$status" -eq 0 ] @@ -766,7 +767,7 @@ function teardown() { echo "$output" [ "$status" -eq 0 ] # Wait for container to OOM - run sleep 100 + sleep 100 run crioctl ctr status --id "$ctr_id" echo "$output" [ "$status" -eq 0 ] diff --git a/test/hooks.bats b/test/hooks.bats index 0c1a51ea..92aa725f 100644 --- a/test/hooks.bats +++ b/test/hooks.bats @@ -10,7 +10,7 @@ cp hooks/checkhook.sh ${HOOKSDIR} sed "s|HOOKSDIR|${HOOKSDIR}|" hooks/checkhook.json > ${HOOKSDIR}/checkhook.json @test "pod test hooks" { - run rm -f /run/hookscheck + rm -f /run/hookscheck start_crio run crioctl pod run --config "$TESTDATA"/sandbox_config.json echo "$output" diff --git a/test/kpod_images.bats b/test/kpod_images.bats index 92e63aa3..0448d61b 100644 --- a/test/kpod_images.bats +++ b/test/kpod_images.bats @@ -38,6 +38,7 @@ function teardown() { [ "$status" -eq 0 ] run ${KPOD_BINARY} ${KPOD_OPTIONS} images --format json echo "$output" + [ "$status" -eq 0 ] name=$(echo $output | python -c 'import sys; import json; print(json.loads(sys.stdin.read())[0])["names"][0]') [ "$name" = "docker.io/library/${IMAGE}" ] run ${KPOD_BINARY} ${KPOD_OPTIONS} rmi ${IMAGE} diff --git a/test/kpod_pause.bats b/test/kpod_pause.bats index 746d39db..84321beb 100644 --- a/test/kpod_pause.bats +++ b/test/kpod_pause.bats @@ -59,6 +59,7 @@ function teardown() { ctr_id="$output" run crioctl ctr start --id "$ctr_id" echo "$output" + [ "$status" -eq 0 ] id="$output" run ${KPOD_BINARY} ${KPOD_OPTIONS} pause "$id" echo "$output" @@ -87,6 +88,7 @@ function teardown() { ctr_id="$output" run crioctl ctr start --id "$ctr_id" echo "$output" + [ "$status" -eq 0 ] run ${KPOD_BINARY} ${KPOD_OPTIONS} pause "k8s_podsandbox1-redis_podsandbox1_redhat.test.crio_redhat-test-crio_0" echo "$output" [ "$status" -eq 0 ] @@ -115,6 +117,7 @@ function teardown() { run crioctl ctr start --id "$ctr_id" echo "$output" id="$output" + [ "$status" -eq 0 ] run ${KPOD_BINARY} ${KPOD_OPTIONS} pause "$id" echo "$output" [ "$status" -eq 0 ] diff --git a/test/kpod_ps.bats b/test/kpod_ps.bats index 4b2628d3..a4a7b6cb 100644 --- a/test/kpod_ps.bats +++ b/test/kpod_ps.bats @@ -167,12 +167,10 @@ IMAGE="redis:alpine" cleanup_ctrs cleanup_pods stop_crio - [ "$status" -eq 0 ] } @test "kpod ps namespace flag" { start_crio - [ "$status" -eq 0 ] run crioctl pod run --config "$TESTDATA"/sandbox_config.json echo "$output" [ "$status" -eq 0 ] @@ -215,7 +213,6 @@ IMAGE="redis:alpine" @test "kpod ps without namespace flag and format flag = json" { start_crio - [ "$status" -eq 0 ] run crioctl pod run --config "$TESTDATA"/sandbox_config.json echo "$output" [ "$status" -eq 0 ] @@ -231,7 +228,6 @@ IMAGE="redis:alpine" cleanup_ctrs cleanup_pods stop_crio - [ "$status" -eq 0 ] } @test "kpod ps format flag = go template" { diff --git a/test/kpod_rename.bats b/test/kpod_rename.bats index 488449aa..ed3fdada 100644 --- a/test/kpod_rename.bats +++ b/test/kpod_rename.bats @@ -19,6 +19,7 @@ function teardown() { [ "$status" -eq 0 ] run crioctl ctr create --config "$TESTDATA"/container_config.json --pod "$pod_id" ctr_id="$output" + [ "$status" -eq 0 ] run ${KPOD_BINARY} $KPOD_OPTIONS rename "$ctr_id" "$NEW_NAME" echo "$output" [ "$status" -eq 0 ] diff --git a/test/kpod_save.bats b/test/kpod_save.bats index 4f71ae78..d8c581a5 100644 --- a/test/kpod_save.bats +++ b/test/kpod_save.bats @@ -30,7 +30,6 @@ function teardown() { run ${KPOD_BINARY} ${KPOD_OPTIONS} rmi $IMAGE [ "$status" -eq 0 ] rm -f alpine.tar - [ "$status" -eq 0 ] } @test "kpod save using stdout" { diff --git a/test/kpod_stop.bats b/test/kpod_stop.bats index 2ba7899a..72e818d4 100644 --- a/test/kpod_stop.bats +++ b/test/kpod_stop.bats @@ -25,7 +25,10 @@ function teardown() { run crioctl ctr start --id "$ctr_id" echo "$output" id="$output" + [ "$status" -eq 0 ] run ${KPOD_BINARY} ${KPOD_OPTIONS} stop "$id" + echo "$output" + [ "$status" -eq 0 ] cleanup_pods stop_crio } diff --git a/test/kpod_wait.bats b/test/kpod_wait.bats index f1e02b7c..ba7556b2 100644 --- a/test/kpod_wait.bats +++ b/test/kpod_wait.bats @@ -34,6 +34,7 @@ function container_start() { @test "wait on a stopped container" { run ${KPOD_BINARY} ${KPOD_OPTIONS} pull docker.io/library/busybox:latest echo $output + [ "$status" -eq 0 ] start_crio pod_id=$( pod_run_from_template "test" "test" "test1-1" ) echo $pod_id @@ -50,6 +51,7 @@ function container_start() { @test "wait on a sleeping container" { run ${KPOD_BINARY} ${KPOD_OPTIONS} pull docker.io/library/busybox:latest echo $output + [ "$status" -eq 0 ] start_crio pod_id=$( pod_run_from_template "test" "test" "test1-1" ) echo $pod_id @@ -57,6 +59,7 @@ function container_start() { echo $ctr_id run container_start $ctr_id echo $output + [ "$status" -eq 0 ] run ${KPOD_BINARY} ${KPOD_OPTIONS} wait $ctr_id echo $output [ "$status" -eq 0 ] diff --git a/test/network.bats b/test/network.bats index eef4bbe0..d9d0304b 100644 --- a/test/network.bats +++ b/test/network.bats @@ -121,10 +121,8 @@ function teardown() { ctr2_id="$output" ping_pod_from_pod $ctr1_id $ctr2_id - [ "$status" -eq 0 ] ping_pod_from_pod $ctr2_id $ctr1_id - [ "$status" -eq 0 ] } @test "Ensure correct CNI plugin namespace/name/container-id arguments" { @@ -165,6 +163,7 @@ function teardown() { [ "$status" -eq 0 ] run crioctl ctr stop --id "$ctr_id" echo "$output" + [ "$status" -eq 0 ] } @test "Clean up network if pod sandbox fails" { @@ -174,6 +173,8 @@ function teardown() { # networking has been configured chmod 0644 /go/src/github.com/kubernetes-incubator/cri-o/conmon/conmon run crioctl pod run --config "$TESTDATA"/sandbox_config.json + echo "$output" + [ "$status" -ne 0 ] chmod 0755 /go/src/github.com/kubernetes-incubator/cri-o/conmon/conmon # ensure that the server cleaned up sandbox networking if the sandbox From 29121c8c0c712595115b1905658d60747c016ce2 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Fri, 13 Oct 2017 11:27:56 +0200 Subject: [PATCH 08/43] oci: Remove useless crio-conmon- cgroup deletion It always fails because conmon is still there. But more importantly it adds a 2 seconds delay to the container creation as we're trying to delete a cgroup but we can't. With this patch a container creation is down to typically less than 150ms instead of 2+ seconds. Signed-off-by: Samuel Ortiz --- oci/oci.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/oci/oci.go b/oci/oci.go index 2e72b9cf..dc7b2e3a 100644 --- a/oci/oci.go +++ b/oci/oci.go @@ -224,11 +224,12 @@ func (r *Runtime) CreateContainer(c *Container, cgroupParent string) (err error) if err != nil { logrus.Warnf("Failed to add conmon to cgroupfs sandbox cgroup: %v", err) } else { - // XXX: this defer does nothing as the cgroup can't be deleted cause - // it contains the conmon pid in tasks - // we need to remove this defer and delete the cgroup once conmon exits - // maybe need a conmon monitor? - defer control.Delete() + // Here we should defer a crio-connmon- cgroup hierarchy deletion, but it will + // always fail as conmon's pid is still there. + // Fortunately, kubelet takes care of deleting this for us, so the leak will + // only happens in corner case where one does a manual deletion of the container + // through e.g. runc. This should be handled by implementing a conmon monitoring + // routine that does the cgroup cleanup once conmon is terminated. if err := control.Add(cgroups.Process{Pid: cmd.Process.Pid}); err != nil { logrus.Warnf("Failed to add conmon to cgroupfs sandbox cgroup: %v", err) } From ab2a4839d7f0d69769f6c88ebd5ba61cd6d09430 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Fri, 13 Oct 2017 14:34:51 +0200 Subject: [PATCH 09/43] oci: kill all processes in a container not just the main one Signed-off-by: Antonio Murdaca --- oci/oci.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oci/oci.go b/oci/oci.go index 2e72b9cf..7548ef90 100644 --- a/oci/oci.go +++ b/oci/oci.go @@ -553,7 +553,7 @@ func (r *Runtime) StopContainer(c *Container, timeout int64) error { return nil } - if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, r.Path(c), "kill", c.id, c.GetStopSignal()); err != nil { + if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, r.Path(c), "kill", "--all", c.id, c.GetStopSignal()); err != nil { return fmt.Errorf("failed to stop container %s, %v", c.id, err) } if timeout == -1 { From a636972c3e3c86e398a4fa0d7b76386d4683072e Mon Sep 17 00:00:00 2001 From: Mrunal Patel Date: Fri, 13 Oct 2017 11:26:46 -0700 Subject: [PATCH 10/43] version: Release 1.0.0 Signed-off-by: Mrunal Patel --- README.md | 3 +-- VERSION | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d1cddd1f..0d7dbaaa 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Build Status](https://img.shields.io/travis/kubernetes-incubator/cri-o.svg?maxAge=2592000&style=flat-square)](https://travis-ci.org/kubernetes-incubator/cri-o) [![Go Report Card](https://goreportcard.com/badge/github.com/kubernetes-incubator/cri-o?style=flat-square)](https://goreportcard.com/report/github.com/kubernetes-incubator/cri-o) -### Status: Release Candidate 3 +### Status: Stable ## What is the scope of this project? @@ -256,5 +256,4 @@ To run a full cluster, see [the instructions](kubernetes.md). 1. Support for log management, networking integration using CNI, pluggable image/storage management (done) 1. Support for exec/attach (done) 1. Target fully automated kubernetes testing without failures [e2e status](https://github.com/kubernetes-incubator/cri-o/issues/533) -1. Release 1.0 1. Track upstream k8s releases diff --git a/VERSION b/VERSION index 1e78071b..3eefcb9d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.0-rc4-dev +1.0.0 From 7efdae80bcb55b488956063f3bba965960b45d3c Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Tue, 17 Oct 2017 10:44:46 +0200 Subject: [PATCH 11/43] version: fix version handling and kube info Signed-off-by: Antonio Murdaca --- Makefile | 3 +-- cmd/crio/main.go | 9 ++------- server/server.go | 3 +-- server/version.go | 32 +++++++++++++++----------------- version/version.go | 4 ++++ 5 files changed, 23 insertions(+), 28 deletions(-) create mode 100644 version/version.go diff --git a/Makefile b/Makefile index ef6129eb..27bd3725 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,6 @@ COMMIT_NO := $(shell git rev-parse HEAD 2> /dev/null || true) GIT_COMMIT := $(if $(shell git status --porcelain --untracked-files=no),"${COMMIT_NO}-dirty","${COMMIT_NO}") BUILD_INFO := $(shell date +%s) -VERSION := ${shell cat ./VERSION} KPOD_VERSION := ${shell cat ./KPOD_VERSION} # If GOPATH not specified, use one in the local directory @@ -36,7 +35,7 @@ GOPKGBASEDIR := $(shell dirname "$(GOPKGDIR)") # Update VPATH so make finds .gopathok VPATH := $(VPATH):$(GOPATH) -LDFLAGS := -ldflags '-X main.gitCommit=${GIT_COMMIT} -X main.buildInfo=${BUILD_INFO} -X main.version=${VERSION} -X main.kpodVersion=${KPOD_VERSION}' +LDFLAGS := -ldflags '-X main.gitCommit=${GIT_COMMIT} -X main.buildInfo=${BUILD_INFO} -X main.kpodVersion=${KPOD_VERSION}' all: binaries crio.conf docs diff --git a/cmd/crio/main.go b/cmd/crio/main.go index 2446bdf6..95289956 100644 --- a/cmd/crio/main.go +++ b/cmd/crio/main.go @@ -14,6 +14,7 @@ import ( "github.com/containers/storage/pkg/reexec" "github.com/kubernetes-incubator/cri-o/libkpod" "github.com/kubernetes-incubator/cri-o/server" + "github.com/kubernetes-incubator/cri-o/version" "github.com/opencontainers/selinux/go-selinux" "github.com/sirupsen/logrus" "github.com/soheilhy/cmux" @@ -23,10 +24,6 @@ import ( "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" ) -// This is populated by the Makefile from the VERSION file -// in the repository -var version = "" - // gitCommit is the commit that the binary is being built from. // It will be populated by the Makefile. var gitCommit = "" @@ -182,9 +179,7 @@ func main() { app := cli.NewApp() var v []string - if version != "" { - v = append(v, version) - } + v = append(v, version.Version) if gitCommit != "" { v = append(v, fmt.Sprintf("commit: %s", gitCommit)) } diff --git a/server/server.go b/server/server.go index 5139a398..637ab860 100644 --- a/server/server.go +++ b/server/server.go @@ -35,8 +35,7 @@ import ( ) const ( - runtimeAPIVersion = "v1alpha1" - shutdownFile = "/var/lib/crio/crio.shutdown" + shutdownFile = "/var/lib/crio/crio.shutdown" ) func isTrue(annotaton string) bool { diff --git a/server/version.go b/server/version.go index d55cd046..5f98e5f0 100644 --- a/server/version.go +++ b/server/version.go @@ -1,29 +1,27 @@ package server import ( + "github.com/kubernetes-incubator/cri-o/version" "golang.org/x/net/context" pb "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1/runtime" ) +const ( + // kubeAPIVersion is the api version of kubernetes. + // TODO: Track upstream code. For now it expects 0.1.0 + kubeAPIVersion = "0.1.0" + // containerName is the name prepended in kubectl describe->Container ID: + // cri-o:// + containerName = "cri-o" + runtimeAPIVersion = "v1alpha1" +) + // Version returns the runtime name, runtime version and runtime API version func (s *Server) Version(ctx context.Context, req *pb.VersionRequest) (*pb.VersionResponse, error) { - - runtimeVersion, err := s.Runtime().Version() - if err != nil { - return nil, err - } - - // TODO: Track upstream code. For now it expects 0.1.0 - version := "0.1.0" - - // taking const address - rav := runtimeAPIVersion - runtimeName := s.Runtime().Name() - return &pb.VersionResponse{ - Version: version, - RuntimeName: runtimeName, - RuntimeVersion: runtimeVersion, - RuntimeApiVersion: rav, + Version: kubeAPIVersion, + RuntimeName: containerName, + RuntimeVersion: version.Version, + RuntimeApiVersion: runtimeAPIVersion, }, nil } diff --git a/version/version.go b/version/version.go new file mode 100644 index 00000000..ac18bd01 --- /dev/null +++ b/version/version.go @@ -0,0 +1,4 @@ +package version + +// Version is the version of the build. +const Version = "1.0.1-dev" From c0f6f4fb485176e060124f10cab2580c478c2c3d Mon Sep 17 00:00:00 2001 From: umohnani8 Date: Tue, 17 Oct 2017 13:55:29 -0400 Subject: [PATCH 12/43] Fix logic flaw in secrets mount Tested on a REHL box and found out that the mounts were not showing up Had a logic flaw, where if the mount was "host:container" Was setting the mount source to "host" and destination to "ctrRunDir/container" When instead, the mount source should be "ctrRunDir/container" and destination "container" with the data copied from "host" to "ctrRunDir/container" Signed-off-by: umohnani8 --- server/container_create.go | 1 - server/secrets.go | 12 ++++++------ test/crio_secrets.bats | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/server/container_create.go b/server/container_create.go index 18b33f2e..ba9df0d6 100644 --- a/server/container_create.go +++ b/server/container_create.go @@ -393,7 +393,6 @@ func addSecretsBindMounts(mountLabel, ctrRunDir string, defaultMounts []string, } for _, m := range mounts { specgen.AddBindMount(m.Source, m.Destination, nil) - } return nil } diff --git a/server/secrets.go b/server/secrets.go index c512aede..56d3ba81 100644 --- a/server/secrets.go +++ b/server/secrets.go @@ -113,18 +113,18 @@ func secretMounts(defaultMountsPaths []string, mountLabel, containerWorkingDir s continue } - ctrDir = filepath.Join(containerWorkingDir, ctrDir) + ctrDirOnHost := filepath.Join(containerWorkingDir, ctrDir) // skip if ctrDir has already been mounted by caller if isAlreadyMounted(runtimeMounts, ctrDir) { logrus.Warnf("%q has already been mounted; cannot override mount", ctrDir) continue } - if err := os.RemoveAll(ctrDir); err != nil { + if err := os.RemoveAll(ctrDirOnHost); err != nil { return nil, fmt.Errorf("remove container directory failed: %v", err) } - if err := os.MkdirAll(ctrDir, 0755); err != nil { + if err := os.MkdirAll(ctrDirOnHost, 0755); err != nil { return nil, fmt.Errorf("making container directory failed: %v", err) } @@ -138,12 +138,12 @@ func secretMounts(defaultMountsPaths []string, mountLabel, containerWorkingDir s return nil, errors.Wrapf(err, "getting host secret data failed") } for _, s := range data { - s.SaveTo(ctrDir) + s.SaveTo(ctrDirOnHost) } - label.Relabel(ctrDir, mountLabel, false) + label.Relabel(ctrDirOnHost, mountLabel, false) m := rspec.Mount{ - Source: hostDir, + Source: ctrDirOnHost, Destination: ctrDir, } diff --git a/test/crio_secrets.bats b/test/crio_secrets.bats index fe1e5230..2f36d18d 100644 --- a/test/crio_secrets.bats +++ b/test/crio_secrets.bats @@ -24,7 +24,7 @@ function teardown() { echo "$output" [ "$status" -eq 0 ] mount_info="$output" - grep $ctr_id/userdata/container/path1 <<< "$mount_info" + grep /container/path1 <<< "$mount_info" echo "$output" [ "$status" -eq 0 ] rm -rf MOUNT_PATH From 9b797f0cb96e3a021a37ddada8e01cd61e43d12d Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Sun, 15 Oct 2017 22:05:41 +0200 Subject: [PATCH 13/43] oci: fixes to properly handle container stop action Signed-off-by: Antonio Murdaca --- cmd/kpod/rm.go | 3 +- cmd/kpod/stop.go | 3 +- libkpod/remove.go | 5 ++-- libkpod/stop.go | 5 ++-- oci/oci.go | 61 +++++++++++++++++++++++++------------- server/container_remove.go | 2 +- server/container_stop.go | 2 +- server/sandbox_remove.go | 2 +- server/sandbox_stop.go | 2 +- 9 files changed, 54 insertions(+), 31 deletions(-) diff --git a/cmd/kpod/rm.go b/cmd/kpod/rm.go index 69f68302..c40fa41c 100644 --- a/cmd/kpod/rm.go +++ b/cmd/kpod/rm.go @@ -6,6 +6,7 @@ import ( "github.com/kubernetes-incubator/cri-o/libkpod" "github.com/pkg/errors" "github.com/urfave/cli" + "golang.org/x/net/context" ) var ( @@ -53,7 +54,7 @@ func rmCmd(c *cli.Context) error { force := c.Bool("force") for _, container := range c.Args() { - id, err2 := server.Remove(container, force) + id, err2 := server.Remove(context.Background(), container, force) if err2 != nil { if err == nil { err = err2 diff --git a/cmd/kpod/stop.go b/cmd/kpod/stop.go index 06b26bb9..279f7b76 100644 --- a/cmd/kpod/stop.go +++ b/cmd/kpod/stop.go @@ -7,6 +7,7 @@ import ( "github.com/kubernetes-incubator/cri-o/libkpod" "github.com/pkg/errors" "github.com/urfave/cli" + "golang.org/x/net/context" ) var ( @@ -61,7 +62,7 @@ func stopCmd(c *cli.Context) error { } var lastError error for _, container := range c.Args() { - cid, err := server.ContainerStop(container, stopTimeout) + cid, err := server.ContainerStop(context.Background(), container, stopTimeout) if err != nil { if lastError != nil { fmt.Fprintln(os.Stderr, lastError) diff --git a/libkpod/remove.go b/libkpod/remove.go index a3aa6eea..5df9e8f7 100644 --- a/libkpod/remove.go +++ b/libkpod/remove.go @@ -6,10 +6,11 @@ import ( "github.com/kubernetes-incubator/cri-o/oci" "github.com/pkg/errors" + "golang.org/x/net/context" ) // Remove removes a container -func (c *ContainerServer) Remove(container string, force bool) (string, error) { +func (c *ContainerServer) Remove(ctx context.Context, container string, force bool) (string, error) { ctr, err := c.LookupContainer(container) if err != nil { return "", err @@ -22,7 +23,7 @@ func (c *ContainerServer) Remove(container string, force bool) (string, error) { return "", errors.Errorf("cannot remove paused container %s", ctrID) case oci.ContainerStateCreated, oci.ContainerStateRunning: if force { - _, err = c.ContainerStop(container, -1) + _, err = c.ContainerStop(ctx, container, 10) if err != nil { return "", errors.Wrapf(err, "unable to stop container %s", ctrID) } diff --git a/libkpod/stop.go b/libkpod/stop.go index 06712d45..86c9cbec 100644 --- a/libkpod/stop.go +++ b/libkpod/stop.go @@ -3,10 +3,11 @@ package libkpod import ( "github.com/kubernetes-incubator/cri-o/oci" "github.com/pkg/errors" + "golang.org/x/net/context" ) // ContainerStop stops a running container with a grace period (i.e., timeout). -func (c *ContainerServer) ContainerStop(container string, timeout int64) (string, error) { +func (c *ContainerServer) ContainerStop(ctx context.Context, container string, timeout int64) (string, error) { ctr, err := c.LookupContainer(container) if err != nil { return "", errors.Wrapf(err, "failed to find container %s", container) @@ -20,7 +21,7 @@ func (c *ContainerServer) ContainerStop(container string, timeout int64) (string return "", errors.Errorf("cannot stop paused container %s", ctrID) default: if cStatus.Status != oci.ContainerStateStopped { - if err := c.runtime.StopContainer(ctr, timeout); err != nil { + if err := c.runtime.StopContainer(ctx, ctr, timeout); err != nil { return "", errors.Wrapf(err, "failed to stop container %s", ctrID) } if err := c.storageRuntimeServer.StopContainer(ctrID); err != nil { diff --git a/oci/oci.go b/oci/oci.go index a5f14ea3..b5a433c3 100644 --- a/oci/oci.go +++ b/oci/oci.go @@ -17,6 +17,7 @@ import ( "github.com/kubernetes-incubator/cri-o/utils" rspec "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" + "golang.org/x/net/context" "golang.org/x/sys/unix" kwait "k8s.io/apimachinery/pkg/util/wait" ) @@ -39,6 +40,10 @@ const ( SystemdCgroupsManager = "systemd" // ContainerExitsDir is the location of container exit dirs ContainerExitsDir = "/var/run/crio/exits" + + // killContainerTimeout is the timeout that we wait for the container to + // be SIGKILLed. + killContainerTimeout = 2 * time.Minute ) // New creates a new Runtime with options provided @@ -542,25 +547,7 @@ func (r *Runtime) ExecSync(c *Container, command []string, timeout int64) (resp }, nil } -// StopContainer stops a container. Timeout is given in seconds. -func (r *Runtime) StopContainer(c *Container, timeout int64) error { - c.opLock.Lock() - defer c.opLock.Unlock() - - // Check if the process is around before sending a signal - err := unix.Kill(c.state.Pid, 0) - if err == unix.ESRCH { - c.state.Finished = time.Now() - return nil - } - - if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, r.Path(c), "kill", "--all", c.id, c.GetStopSignal()); err != nil { - return fmt.Errorf("failed to stop container %s, %v", c.id, err) - } - if timeout == -1 { - // default 10 seconds delay - timeout = 10 - } +func waitContainerStop(ctx context.Context, c *Container, timeout time.Duration) error { done := make(chan struct{}) // we could potentially re-use "done" channel to exit the loop on timeout // but we use another channel "chControl" so that we won't never incur in the @@ -588,7 +575,10 @@ func (r *Runtime) StopContainer(c *Container, timeout int64) error { select { case <-done: return nil - case <-time.After(time.Duration(timeout) * time.Second): + case <-ctx.Done(): + close(chControl) + return ctx.Err() + case <-time.After(timeout): close(chControl) err := unix.Kill(c.state.Pid, unix.SIGKILL) if err != nil && err != unix.ESRCH { @@ -597,10 +587,39 @@ func (r *Runtime) StopContainer(c *Container, timeout int64) error { } c.state.Finished = time.Now() - return nil } +// StopContainer stops a container. Timeout is given in seconds. +func (r *Runtime) StopContainer(ctx context.Context, c *Container, timeout int64) error { + c.opLock.Lock() + defer c.opLock.Unlock() + + // Check if the process is around before sending a signal + err := unix.Kill(c.state.Pid, 0) + if err == unix.ESRCH { + c.state.Finished = time.Now() + return nil + } + + if timeout > 0 { + if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, r.Path(c), "kill", c.id, c.GetStopSignal()); err != nil { + return fmt.Errorf("failed to stop container %s, %v", c.id, err) + } + err = waitContainerStop(ctx, c, time.Duration(timeout)*time.Second) + if err == nil { + return nil + } + logrus.Warnf("Stop container %q timed out: %v", c.ID(), err) + } + + if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, r.Path(c), "kill", "--all", c.id, "KILL"); err != nil { + return fmt.Errorf("failed to stop container %s, %v", c.id, err) + } + + return waitContainerStop(ctx, c, killContainerTimeout) +} + // DeleteContainer deletes a container. func (r *Runtime) DeleteContainer(c *Container) error { c.opLock.Lock() diff --git a/server/container_remove.go b/server/container_remove.go index cedfc602..87102372 100644 --- a/server/container_remove.go +++ b/server/container_remove.go @@ -9,7 +9,7 @@ import ( // RemoveContainer removes the container. If the container is running, the container // should be force removed. func (s *Server) RemoveContainer(ctx context.Context, req *pb.RemoveContainerRequest) (*pb.RemoveContainerResponse, error) { - _, err := s.ContainerServer.Remove(req.ContainerId, true) + _, err := s.ContainerServer.Remove(ctx, req.ContainerId, true) if err != nil { return nil, err } diff --git a/server/container_stop.go b/server/container_stop.go index c0093cfd..f74ed86e 100644 --- a/server/container_stop.go +++ b/server/container_stop.go @@ -8,7 +8,7 @@ import ( // StopContainer stops a running container with a grace period (i.e., timeout). func (s *Server) StopContainer(ctx context.Context, req *pb.StopContainerRequest) (*pb.StopContainerResponse, error) { - _, err := s.ContainerServer.ContainerStop(req.ContainerId, req.Timeout) + _, err := s.ContainerServer.ContainerStop(ctx, req.ContainerId, req.Timeout) if err != nil { return nil, err } diff --git a/server/sandbox_remove.go b/server/sandbox_remove.go index 856b8938..b0e07384 100644 --- a/server/sandbox_remove.go +++ b/server/sandbox_remove.go @@ -41,7 +41,7 @@ func (s *Server) RemovePodSandbox(ctx context.Context, req *pb.RemovePodSandboxR if !sb.Stopped() { cState := s.Runtime().ContainerStatus(c) if cState.Status == oci.ContainerStateCreated || cState.Status == oci.ContainerStateRunning { - if err := s.Runtime().StopContainer(c, -1); err != nil { + if err := s.Runtime().StopContainer(ctx, c, 10); err != nil { // Assume container is already stopped logrus.Warnf("failed to stop container %s: %v", c.Name(), err) } diff --git a/server/sandbox_stop.go b/server/sandbox_stop.go index 7db436d1..9d6a5aa3 100644 --- a/server/sandbox_stop.go +++ b/server/sandbox_stop.go @@ -56,7 +56,7 @@ func (s *Server) StopPodSandbox(ctx context.Context, req *pb.StopPodSandboxReque for _, c := range containers { cStatus := s.Runtime().ContainerStatus(c) if cStatus.Status != oci.ContainerStateStopped { - if err := s.Runtime().StopContainer(c, -1); err != nil { + if err := s.Runtime().StopContainer(ctx, c, 10); err != nil { return nil, fmt.Errorf("failed to stop container %s in pod sandbox %s: %v", c.Name(), sb.ID(), err) } if c.ID() == podInfraContainer.ID() { From ed89aa630ee3d71966cc8f13f0fdd1fff0ebd58e Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Thu, 19 Oct 2017 16:37:26 +0200 Subject: [PATCH 14/43] contrib: test: fix e2e cmdline Signed-off-by: Antonio Murdaca --- contrib/test/integration/e2e.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/test/integration/e2e.yml b/contrib/test/integration/e2e.yml index a95b2231..5c4d656e 100644 --- a/contrib/test/integration/e2e.yml +++ b/contrib/test/integration/e2e.yml @@ -42,7 +42,7 @@ e2e_shell_cmd: > /usr/bin/go run hack/e2e.go --test - -test_args="-host=https://{{ ansible_default_ipv4.address }}:6443 + --test_args="-host=https://{{ ansible_default_ipv4.address }}:6443 --ginkgo.focus=\[Conformance\] --report-dir={{ artifacts }}" &> {{ artifacts }}/e2e.log From a32c3d4b9aa82e7f7a82683458fc2f4c6deaa798 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Thu, 19 Oct 2017 15:02:56 +0200 Subject: [PATCH 15/43] oci: respect process spec on exec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes exec to use the original (start-time) process exec configuration. Otherwise, we were creating a brand new spec process w/o additional groups for instance. Spotted while integrating CRI-O with cri-test...The test was failing with: ``` • Failure [10.640 seconds] [k8s.io] Security Context /home/amurdaca/go/src/github.com/kubernetes-incubator/cri-tools/pkg/framework/framework.go:72 bucket /home/amurdaca/go/src/github.com/kubernetes-incubator/cri-tools/pkg/validate/security_context.go:407 runtime should support SupplementalGroups [It] /home/amurdaca/go/src/github.com/kubernetes-incubator/cri-tools/pkg/validate/security_context.go:272 Expected <[]string | len:1, cap:1>: ["0"] to contain element matching : 1234 ``` Signed-off-by: Antonio Murdaca --- libkpod/container_server.go | 2 ++ oci/container.go | 11 +++++++++++ oci/oci.go | 8 +++----- server/container_create.go | 1 + server/sandbox_run.go | 1 + 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/libkpod/container_server.go b/libkpod/container_server.go index 1bc94871..4c6c2d98 100644 --- a/libkpod/container_server.go +++ b/libkpod/container_server.go @@ -388,6 +388,7 @@ func (c *ContainerServer) LoadSandbox(id string) error { if err != nil { return err } + scontainer.SetSpec(&m) scontainer.SetMountPoint(m.Annotations[annotations.MountPoint]) if m.Annotations[annotations.Volumes] != "" { @@ -511,6 +512,7 @@ func (c *ContainerServer) LoadContainer(id string) error { if err != nil { return err } + ctr.SetSpec(&m) ctr.SetMountPoint(m.Annotations[annotations.MountPoint]) c.ContainerStateFromDisk(ctr) diff --git a/oci/container.go b/oci/container.go index 197c3d85..c0eff2fd 100644 --- a/oci/container.go +++ b/oci/container.go @@ -48,6 +48,7 @@ type Container struct { imageRef string volumes []ContainerVolume mountPoint string + spec *specs.Spec } // ContainerVolume is a bind mount for the container. @@ -99,6 +100,16 @@ func NewContainer(id string, name string, bundlePath string, logPath string, net return c, nil } +// SetSpec loads the OCI spec in the container struct +func (c *Container) SetSpec(s *specs.Spec) { + c.spec = s +} + +// Spec returns a copy of the spec for the container +func (c *Container) Spec() specs.Spec { + return *c.spec +} + // GetStopSignal returns the container's own stop signal configured from the // image configuration or the default one. func (c *Container) GetStopSignal() string { diff --git a/oci/oci.go b/oci/oci.go index b5a433c3..fba80c6a 100644 --- a/oci/oci.go +++ b/oci/oci.go @@ -435,11 +435,9 @@ func (r *Runtime) ExecSync(c *Container, command []string, timeout int64) (resp } args = append(args, "-l", logPath) - pspec := rspec.Process{ - Env: r.conmonEnv, - Args: command, - Cwd: "/", - } + pspec := c.Spec().Process + pspec.Env = append(pspec.Env, r.conmonEnv...) + pspec.Args = command processJSON, err := json.Marshal(pspec) if err != nil { return nil, ExecSyncError{ diff --git a/server/container_create.go b/server/container_create.go index ba9df0d6..0be5b5b8 100644 --- a/server/container_create.go +++ b/server/container_create.go @@ -1032,6 +1032,7 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string, if err != nil { return nil, err } + container.SetSpec(specgen.Spec()) container.SetMountPoint(mountPoint) for _, cv := range containerVolumes { diff --git a/server/sandbox_run.go b/server/sandbox_run.go index 0bebef84..92b286da 100644 --- a/server/sandbox_run.go +++ b/server/sandbox_run.go @@ -492,6 +492,7 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest if err != nil { return nil, err } + container.SetSpec(g.Spec()) container.SetMountPoint(mountPoint) sb.SetInfraContainer(container) From 17db40dcda2655f739db117f56d5002c124827b8 Mon Sep 17 00:00:00 2001 From: Mrunal Patel Date: Mon, 23 Oct 2017 15:42:59 -0700 Subject: [PATCH 16/43] test: Test for OOM condition in a loop Signed-off-by: Mrunal Patel --- test/ctr.bats | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/test/ctr.bats b/test/ctr.bats index 2f225187..90f42b68 100644 --- a/test/ctr.bats +++ b/test/ctr.bats @@ -767,10 +767,16 @@ function teardown() { echo "$output" [ "$status" -eq 0 ] # Wait for container to OOM - sleep 100 - run crioctl ctr status --id "$ctr_id" - echo "$output" - [ "$status" -eq 0 ] + attempt=0 + while [ $attempt -le 100 ]; do + attempt=$((attempt+1)) + run crioctl ctr status --id "$ctr_id" + echo "$output" + if [[ "$output" =~ "OOMKilled" ]]; then + break + fi + sleep 10 + done [[ "$output" =~ "OOMKilled" ]] run crioctl pod stop --id "$pod_id" echo "$output" From a90213930bcd78a2d62f1c49b0aaf1e6c784f0f0 Mon Sep 17 00:00:00 2001 From: Mrunal Patel Date: Mon, 23 Oct 2017 12:34:05 -0700 Subject: [PATCH 17/43] vendor: Update godbus dependency to a389bdde4dd695d414e47b755e95e72b7826432c Signed-off-by: Mrunal Patel --- vendor.conf | 2 +- .../github.com/docker/docker/hack/README.md | 60 ---- .../hack/integration-cli-on-swarm/README.md | 69 ---- .../agent/vendor.conf | 2 - vendor/github.com/godbus/dbus/README.markdown | 3 + vendor/github.com/godbus/dbus/conn.go | 143 +++++--- vendor/github.com/godbus/dbus/conn_darwin.go | 20 +- vendor/github.com/godbus/dbus/conn_other.go | 19 +- vendor/github.com/godbus/dbus/dbus.go | 315 ++++++++++++++---- .../github.com/godbus/dbus/default_handler.go | 291 ++++++++++++++++ vendor/github.com/godbus/dbus/doc.go | 6 + vendor/github.com/godbus/dbus/encoder.go | 6 +- vendor/github.com/godbus/dbus/export.go | 275 ++++++--------- vendor/github.com/godbus/dbus/object.go | 21 +- .../godbus/dbus/server_interfaces.go | 89 +++++ vendor/github.com/godbus/dbus/sig.go | 8 +- .../godbus/dbus/transport_generic.go | 17 +- .../github.com/godbus/dbus/transport_unix.go | 4 +- .../godbus/dbus/transport_unixcred_freebsd.go | 91 +++++ .../godbus/dbus/transport_unixcred_openbsd.go | 14 + vendor/github.com/godbus/dbus/variant.go | 7 +- 21 files changed, 1023 insertions(+), 439 deletions(-) delete mode 100644 vendor/github.com/docker/docker/hack/README.md delete mode 100644 vendor/github.com/docker/docker/hack/integration-cli-on-swarm/README.md delete mode 100644 vendor/github.com/docker/docker/hack/integration-cli-on-swarm/agent/vendor.conf create mode 100644 vendor/github.com/godbus/dbus/default_handler.go create mode 100644 vendor/github.com/godbus/dbus/server_interfaces.go create mode 100644 vendor/github.com/godbus/dbus/transport_unixcred_freebsd.go create mode 100644 vendor/github.com/godbus/dbus/transport_unixcred_openbsd.go diff --git a/vendor.conf b/vendor.conf index 557701e9..563fd6ac 100644 --- a/vendor.conf +++ b/vendor.conf @@ -71,7 +71,7 @@ github.com/Microsoft/hcsshim 43f9725307998e09f2e3816c2c0c36dc98f0c982 github.com/emicklei/go-restful ff4f55a206334ef123e4f79bbf348980da81ca46 github.com/emicklei/go-restful-swagger12 1.0.1 github.com/pkg/errors v0.8.0 -github.com/godbus/dbus v4.0.0 +github.com/godbus/dbus a389bdde4dd695d414e47b755e95e72b7826432c github.com/urfave/cli v1.20.0 github.com/vbatts/tar-split v0.10.1 github.com/renstrom/dedent v1.0.0 diff --git a/vendor/github.com/docker/docker/hack/README.md b/vendor/github.com/docker/docker/hack/README.md deleted file mode 100644 index 802395d5..00000000 --- a/vendor/github.com/docker/docker/hack/README.md +++ /dev/null @@ -1,60 +0,0 @@ -## About - -This directory contains a collection of scripts used to build and manage this -repository. If there are any issues regarding the intention of a particular -script (or even part of a certain script), please reach out to us. -It may help us either refine our current scripts, or add on new ones -that are appropriate for a given use case. - -## DinD (dind.sh) - -DinD is a wrapper script which allows Docker to be run inside a Docker -container. DinD requires the container to -be run with privileged mode enabled. - -## Generate Authors (generate-authors.sh) - -Generates AUTHORS; a file with all the names and corresponding emails of -individual contributors. AUTHORS can be found in the home directory of -this repository. - -## Make - -There are two make files, each with different extensions. Neither are supposed -to be called directly; only invoke `make`. Both scripts run inside a Docker -container. - -### make.ps1 - -- The Windows native build script that uses PowerShell semantics; it is limited -unlike `hack\make.sh` since it does not provide support for the full set of -operations provided by the Linux counterpart, `make.sh`. However, `make.ps1` -does provide support for local Windows development and Windows to Windows CI. -More information is found within `make.ps1` by the author, @jhowardmsft - -### make.sh - -- Referenced via `make test` when running tests on a local machine, -or directly referenced when running tests inside a Docker development container. -- When running on a local machine, `make test` to run all tests found in -`test`, `test-unit`, `test-integration-cli`, and `test-docker-py` on -your local machine. The default timeout is set in `make.sh` to 60 minutes -(`${TIMEOUT:=60m}`), since it currently takes up to an hour to run -all of the tests. -- When running inside a Docker development container, `hack/make.sh` does -not have a single target that runs all the tests. You need to provide a -single command line with multiple targets that performs the same thing. -An example referenced from [Run targets inside a development container](https://docs.docker.com/opensource/project/test-and-docs/#run-targets-inside-a-development-container): `root@5f8630b873fe:/go/src/github.com/moby/moby# hack/make.sh dynbinary binary cross test-unit test-integration-cli test-docker-py` -- For more information related to testing outside the scope of this README, -refer to -[Run tests and test documentation](https://docs.docker.com/opensource/project/test-and-docs/) - -## Release (release.sh) - -Releases any bundles built by `make` on a public AWS S3 bucket. -For information regarding configuration, please view `release.sh`. - -## Vendor (vendor.sh) - -A shell script that is a wrapper around Vndr. For information on how to use -this, please refer to [vndr's README](https://github.com/LK4D4/vndr/blob/master/README.md) diff --git a/vendor/github.com/docker/docker/hack/integration-cli-on-swarm/README.md b/vendor/github.com/docker/docker/hack/integration-cli-on-swarm/README.md deleted file mode 100644 index 1cea5252..00000000 --- a/vendor/github.com/docker/docker/hack/integration-cli-on-swarm/README.md +++ /dev/null @@ -1,69 +0,0 @@ -# Integration Testing on Swarm - -IT on Swarm allows you to execute integration test in parallel across a Docker Swarm cluster - -## Architecture - -### Master service - - - Works as a funker caller - - Calls a worker funker (`-worker-service`) with a chunk of `-check.f` filter strings (passed as a file via `-input` flag, typically `/mnt/input`) - -### Worker service - - - Works as a funker callee - - Executes an equivalent of `TESTFLAGS=-check.f TestFoo|TestBar|TestBaz ... make test-integration-cli` using the bind-mounted API socket (`docker.sock`) - -### Client - - - Controls master and workers via `docker stack` - - No need to have a local daemon - -Typically, the master and workers are supposed to be running on a cloud environment, -while the client is supposed to be running on a laptop, e.g. Docker for Mac/Windows. - -## Requirement - - - Docker daemon 1.13 or later - - Private registry for distributed execution with multiple nodes - -## Usage - -### Step 1: Prepare images - - $ make build-integration-cli-on-swarm - -Following environment variables are known to work in this step: - - - `BUILDFLAGS` - - `DOCKER_INCREMENTAL_BINARY` - -Note: during the transition into Moby Project, you might need to create a symbolic link `$GOPATH/src/github.com/docker/docker` to `$GOPATH/src/github.com/moby/moby`. - -### Step 2: Execute tests - - $ ./hack/integration-cli-on-swarm/integration-cli-on-swarm -replicas 40 -push-worker-image YOUR_REGISTRY.EXAMPLE.COM/integration-cli-worker:latest - -Following environment variables are known to work in this step: - - - `DOCKER_GRAPHDRIVER` - - `DOCKER_EXPERIMENTAL` - -#### Flags - -Basic flags: - - - `-replicas N`: the number of worker service replicas. i.e. degree of parallelism. - - `-chunks N`: the number of chunks. By default, `chunks` == `replicas`. - - `-push-worker-image REGISTRY/IMAGE:TAG`: push the worker image to the registry. Note that if you have only single node and hence you do not need a private registry, you do not need to specify `-push-worker-image`. - -Experimental flags for mitigating makespan nonuniformity: - - - `-shuffle`: Shuffle the test filter strings - -Flags for debugging IT on Swarm itself: - - - `-rand-seed N`: the random seed. This flag is useful for deterministic replaying. By default(0), the timestamp is used. - - `-filters-file FILE`: the file contains `-check.f` strings. By default, the file is automatically generated. - - `-dry-run`: skip the actual workload - - `keep-executor`: do not auto-remove executor containers, which is used for running privileged programs on Swarm diff --git a/vendor/github.com/docker/docker/hack/integration-cli-on-swarm/agent/vendor.conf b/vendor/github.com/docker/docker/hack/integration-cli-on-swarm/agent/vendor.conf deleted file mode 100644 index efd6d6d0..00000000 --- a/vendor/github.com/docker/docker/hack/integration-cli-on-swarm/agent/vendor.conf +++ /dev/null @@ -1,2 +0,0 @@ -# dependencies specific to worker (i.e. github.com/docker/docker/...) are not vendored here -github.com/bfirsh/funker-go eaa0a2e06f30e72c9a0b7f858951e581e26ef773 diff --git a/vendor/github.com/godbus/dbus/README.markdown b/vendor/github.com/godbus/dbus/README.markdown index 0a6e7e5b..d37f4e2e 100644 --- a/vendor/github.com/godbus/dbus/README.markdown +++ b/vendor/github.com/godbus/dbus/README.markdown @@ -1,3 +1,5 @@ +[![Build Status](https://travis-ci.org/godbus/dbus.svg?branch=master)](https://travis-ci.org/godbus/dbus) + dbus ---- @@ -29,6 +31,7 @@ gives a short overview over the basic usage. #### Projects using godbus - [notify](https://github.com/esiqveland/notify) provides desktop notifications over dbus into a library. +- [go-bluetooth](https://github.com/muka/go-bluetooth) provides a bluetooth client over bluez dbus API. Please note that the API is considered unstable for now and may change without further notice. diff --git a/vendor/github.com/godbus/dbus/conn.go b/vendor/github.com/godbus/dbus/conn.go index 9aa2e128..5720e2eb 100644 --- a/vendor/github.com/godbus/dbus/conn.go +++ b/vendor/github.com/godbus/dbus/conn.go @@ -9,8 +9,6 @@ import ( "sync" ) -const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket" - var ( systemBus *Conn systemBusLck sync.Mutex @@ -47,15 +45,13 @@ type Conn struct { calls map[uint32]*Call callsLck sync.RWMutex - handlers map[ObjectPath]map[string]exportedObj - handlersLck sync.RWMutex + handler Handler out chan *Message closed bool outLck sync.RWMutex - signals []chan<- *Signal - signalsLck sync.Mutex + signalHandler SignalHandler eavesdropped chan<- *Message eavesdroppedLck sync.Mutex @@ -90,16 +86,33 @@ func SessionBus() (conn *Conn, err error) { return } -// SessionBusPrivate returns a new private connection to the session bus. -func SessionBusPrivate() (*Conn, error) { +func getSessionBusAddress() (string, error) { sessionEnvLck.Lock() defer sessionEnvLck.Unlock() address := os.Getenv("DBUS_SESSION_BUS_ADDRESS") if address != "" && address != "autolaunch:" { - return Dial(address) + return address, nil + } + return getSessionBusPlatformAddress() +} + +// SessionBusPrivate returns a new private connection to the session bus. +func SessionBusPrivate() (*Conn, error) { + address, err := getSessionBusAddress() + if err != nil { + return nil, err } - return sessionBusPlatform() + return Dial(address) +} + +// SessionBusPrivate returns a new private connection to the session bus. +func SessionBusPrivateHandler(handler Handler, signalHandler SignalHandler) (*Conn, error) { + address, err := getSessionBusAddress() + if err != nil { + return nil, err + } + return DialHandler(address, handler, signalHandler) } // SystemBus returns a shared connection to the system bus, connecting to it if @@ -133,11 +146,12 @@ func SystemBus() (conn *Conn, err error) { // SystemBusPrivate returns a new private connection to the system bus. func SystemBusPrivate() (*Conn, error) { - address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS") - if address != "" { - return Dial(address) - } - return Dial(defaultSystemBusAddress) + return Dial(getSystemBusPlatformAddress()) +} + +// SystemBusPrivateHandler returns a new private connection to the system bus, using the provided handlers. +func SystemBusPrivateHandler(handler Handler, signalHandler SignalHandler) (*Conn, error) { + return DialHandler(getSystemBusPlatformAddress(), handler, signalHandler) } // Dial establishes a new private connection to the message bus specified by address. @@ -146,21 +160,36 @@ func Dial(address string) (*Conn, error) { if err != nil { return nil, err } - return newConn(tr) + return newConn(tr, NewDefaultHandler(), NewDefaultSignalHandler()) +} + +// DialHandler establishes a new private connection to the message bus specified by address, using the supplied handlers. +func DialHandler(address string, handler Handler, signalHandler SignalHandler) (*Conn, error) { + tr, err := getTransport(address) + if err != nil { + return nil, err + } + return newConn(tr, handler, signalHandler) } // NewConn creates a new private *Conn from an already established connection. func NewConn(conn io.ReadWriteCloser) (*Conn, error) { - return newConn(genericTransport{conn}) + return NewConnHandler(conn, NewDefaultHandler(), NewDefaultSignalHandler()) +} + +// NewConnHandler creates a new private *Conn from an already established connection, using the supplied handlers. +func NewConnHandler(conn io.ReadWriteCloser, handler Handler, signalHandler SignalHandler) (*Conn, error) { + return newConn(genericTransport{conn}, handler, signalHandler) } // newConn creates a new *Conn from a transport. -func newConn(tr transport) (*Conn, error) { +func newConn(tr transport, handler Handler, signalHandler SignalHandler) (*Conn, error) { conn := new(Conn) conn.transport = tr conn.calls = make(map[uint32]*Call) conn.out = make(chan *Message, 10) - conn.handlers = make(map[ObjectPath]map[string]exportedObj) + conn.handler = handler + conn.signalHandler = signalHandler conn.nextSerial = 1 conn.serialUsed = map[uint32]bool{0: true} conn.busObj = conn.Object("org.freedesktop.DBus", "/org/freedesktop/DBus") @@ -188,16 +217,21 @@ func (conn *Conn) Close() error { close(conn.out) conn.closed = true conn.outLck.Unlock() - conn.signalsLck.Lock() - for _, ch := range conn.signals { - close(ch) + + if term, ok := conn.signalHandler.(Terminator); ok { + term.Terminate() } - conn.signalsLck.Unlock() + + if term, ok := conn.handler.(Terminator); ok { + term.Terminate() + } + conn.eavesdroppedLck.Lock() if conn.eavesdropped != nil { close(conn.eavesdropped) } conn.eavesdroppedLck.Unlock() + return conn.transport.Close() } @@ -334,17 +368,7 @@ func (conn *Conn) inWorker() { conn.namesLck.Unlock() } } - signal := &Signal{ - Sender: sender, - Path: msg.Headers[FieldPath].value.(ObjectPath), - Name: iface + "." + member, - Body: msg.Body, - } - conn.signalsLck.Lock() - for _, ch := range conn.signals { - ch <- signal - } - conn.signalsLck.Unlock() + conn.handleSignal(msg) case TypeMethodCall: go conn.handleCall(msg) } @@ -365,6 +389,21 @@ func (conn *Conn) inWorker() { } } +func (conn *Conn) handleSignal(msg *Message) { + iface := msg.Headers[FieldInterface].value.(string) + member := msg.Headers[FieldMember].value.(string) + // as per http://dbus.freedesktop.org/doc/dbus-specification.html , + // sender is optional for signals. + sender, _ := msg.Headers[FieldSender].value.(string) + signal := &Signal{ + Sender: sender, + Path: msg.Headers[FieldPath].value.(ObjectPath), + Name: iface + "." + member, + Body: msg.Body, + } + conn.signalHandler.DeliverSignal(iface, member, signal) +} + // Names returns the list of all names that are currently owned by this // connection. The slice is always at least one element long, the first element // being the unique name of the connection. @@ -455,7 +494,19 @@ func (conn *Conn) Send(msg *Message, ch chan *Call) *Call { // sendError creates an error message corresponding to the parameters and sends // it to conn.out. -func (conn *Conn) sendError(e Error, dest string, serial uint32) { +func (conn *Conn) sendError(err error, dest string, serial uint32) { + var e *Error + switch em := err.(type) { + case Error: + e = &em + case *Error: + e = em + case DBusError: + name, body := em.DBusError() + e = NewError(name, body) + default: + e = MakeFailedError(err) + } msg := new(Message) msg.Type = TypeError msg.serial = conn.getSerial() @@ -498,6 +549,14 @@ func (conn *Conn) sendReply(dest string, serial uint32, values ...interface{}) { conn.outLck.RUnlock() } +func (conn *Conn) defaultSignalAction(fn func(h *defaultSignalHandler, ch chan<- *Signal), ch chan<- *Signal) { + if !isDefaultSignalHandler(conn.signalHandler) { + return + } + handler := conn.signalHandler.(*defaultSignalHandler) + fn(handler, ch) +} + // Signal registers the given channel to be passed all received signal messages. // The caller has to make sure that ch is sufficiently buffered; if a message // arrives when a write to c is not possible, it is discarded. @@ -508,22 +567,12 @@ func (conn *Conn) sendReply(dest string, serial uint32, values ...interface{}) { // channel for eavesdropped messages, this channel receives all signals, and // none of the channels passed to Signal will receive any signals. func (conn *Conn) Signal(ch chan<- *Signal) { - conn.signalsLck.Lock() - conn.signals = append(conn.signals, ch) - conn.signalsLck.Unlock() + conn.defaultSignalAction((*defaultSignalHandler).addSignal, ch) } // RemoveSignal removes the given channel from the list of the registered channels. func (conn *Conn) RemoveSignal(ch chan<- *Signal) { - conn.signalsLck.Lock() - for i := len(conn.signals) - 1; i >= 0; i-- { - if ch == conn.signals[i] { - copy(conn.signals[i:], conn.signals[i+1:]) - conn.signals[len(conn.signals)-1] = nil - conn.signals = conn.signals[:len(conn.signals)-1] - } - } - conn.signalsLck.Unlock() + conn.defaultSignalAction((*defaultSignalHandler).removeSignal, ch) } // SupportsUnixFDs returns whether the underlying transport supports passing of diff --git a/vendor/github.com/godbus/dbus/conn_darwin.go b/vendor/github.com/godbus/dbus/conn_darwin.go index b67bb1b8..c015f80c 100644 --- a/vendor/github.com/godbus/dbus/conn_darwin.go +++ b/vendor/github.com/godbus/dbus/conn_darwin.go @@ -2,20 +2,32 @@ package dbus import ( "errors" + "fmt" + "os" "os/exec" ) -func sessionBusPlatform() (*Conn, error) { +const defaultSystemBusAddress = "unix:path=/opt/local/var/run/dbus/system_bus_socket" + +func getSessionBusPlatformAddress() (string, error) { cmd := exec.Command("launchctl", "getenv", "DBUS_LAUNCHD_SESSION_BUS_SOCKET") b, err := cmd.CombinedOutput() if err != nil { - return nil, err + return "", err } if len(b) == 0 { - return nil, errors.New("dbus: couldn't determine address of session bus") + return "", errors.New("dbus: couldn't determine address of session bus") } - return Dial("unix:path=" + string(b[:len(b)-1])) + return "unix:path=" + string(b[:len(b)-1]), nil +} + +func getSystemBusPlatformAddress() string { + address := os.Getenv("DBUS_LAUNCHD_SESSION_BUS_SOCKET") + if address != "" { + return fmt.Sprintf("unix:path=%s", address) + } + return defaultSystemBusAddress } diff --git a/vendor/github.com/godbus/dbus/conn_other.go b/vendor/github.com/godbus/dbus/conn_other.go index 289e8c5d..254c9f2e 100644 --- a/vendor/github.com/godbus/dbus/conn_other.go +++ b/vendor/github.com/godbus/dbus/conn_other.go @@ -5,27 +5,38 @@ package dbus import ( "bytes" "errors" + "fmt" "os" "os/exec" ) -func sessionBusPlatform() (*Conn, error) { +const defaultSystemBusAddress = "unix:path=/var/run/dbus/system_bus_socket" + +func getSessionBusPlatformAddress() (string, error) { cmd := exec.Command("dbus-launch") b, err := cmd.CombinedOutput() if err != nil { - return nil, err + return "", err } i := bytes.IndexByte(b, '=') j := bytes.IndexByte(b, '\n') if i == -1 || j == -1 { - return nil, errors.New("dbus: couldn't determine address of session bus") + return "", errors.New("dbus: couldn't determine address of session bus") } env, addr := string(b[0:i]), string(b[i+1:j]) os.Setenv(env, addr) - return Dial(addr) + return addr, nil +} + +func getSystemBusPlatformAddress() string { + address := os.Getenv("DBUS_SYSTEM_BUS_ADDRESS") + if address != "" { + return fmt.Sprintf("unix:path=%s", address) + } + return defaultSystemBusAddress } diff --git a/vendor/github.com/godbus/dbus/dbus.go b/vendor/github.com/godbus/dbus/dbus.go index 2ce68735..c6d0d3ce 100644 --- a/vendor/github.com/godbus/dbus/dbus.go +++ b/vendor/github.com/godbus/dbus/dbus.go @@ -2,6 +2,7 @@ package dbus import ( "errors" + "fmt" "reflect" "strings" ) @@ -12,6 +13,8 @@ var ( uint8Type = reflect.TypeOf(uint8(0)) int16Type = reflect.TypeOf(int16(0)) uint16Type = reflect.TypeOf(uint16(0)) + intType = reflect.TypeOf(int(0)) + uintType = reflect.TypeOf(uint(0)) int32Type = reflect.TypeOf(int32(0)) uint32Type = reflect.TypeOf(uint32(0)) int64Type = reflect.TypeOf(int64(0)) @@ -22,6 +25,7 @@ var ( objectPathType = reflect.TypeOf(ObjectPath("")) variantType = reflect.TypeOf(Variant{Signature{""}, nil}) interfacesType = reflect.TypeOf([]interface{}{}) + interfaceType = reflect.TypeOf((*interface{})(nil)).Elem() unixFDType = reflect.TypeOf(UnixFD(0)) unixFDIndexType = reflect.TypeOf(UnixFDIndex(0)) ) @@ -46,86 +50,251 @@ func Store(src []interface{}, dest ...interface{}) error { } for i := range src { - if err := store(src[i], dest[i]); err != nil { + if err := storeInterfaces(src[i], dest[i]); err != nil { return err } } return nil } -func store(src, dest interface{}) error { - if reflect.TypeOf(dest).Elem() == reflect.TypeOf(src) { - reflect.ValueOf(dest).Elem().Set(reflect.ValueOf(src)) - return nil - } else if hasStruct(dest) { - rv := reflect.ValueOf(dest).Elem() - switch rv.Kind() { - case reflect.Struct: - vs, ok := src.([]interface{}) - if !ok { - return errors.New("dbus.Store: type mismatch") - } - t := rv.Type() - ndest := make([]interface{}, 0, rv.NumField()) - for i := 0; i < rv.NumField(); i++ { - field := t.Field(i) - if field.PkgPath == "" && field.Tag.Get("dbus") != "-" { - ndest = append(ndest, rv.Field(i).Addr().Interface()) - } - } - if len(vs) != len(ndest) { - return errors.New("dbus.Store: type mismatch") - } - err := Store(vs, ndest...) - if err != nil { - return errors.New("dbus.Store: type mismatch") - } - case reflect.Slice: - sv := reflect.ValueOf(src) - if sv.Kind() != reflect.Slice { - return errors.New("dbus.Store: type mismatch") - } - rv.Set(reflect.MakeSlice(rv.Type(), sv.Len(), sv.Len())) - for i := 0; i < sv.Len(); i++ { - if err := store(sv.Index(i).Interface(), rv.Index(i).Addr().Interface()); err != nil { - return err - } - } - case reflect.Map: - sv := reflect.ValueOf(src) - if sv.Kind() != reflect.Map { - return errors.New("dbus.Store: type mismatch") - } - keys := sv.MapKeys() - rv.Set(reflect.MakeMap(sv.Type())) - for _, key := range keys { - v := reflect.New(sv.Type().Elem()) - if err := store(v, sv.MapIndex(key).Interface()); err != nil { - return err - } - rv.SetMapIndex(key, v.Elem()) - } - default: - return errors.New("dbus.Store: type mismatch") - } - return nil - } else { - return errors.New("dbus.Store: type mismatch") +func storeInterfaces(src, dest interface{}) error { + return store(reflect.ValueOf(dest), reflect.ValueOf(src)) +} + +func store(dest, src reflect.Value) error { + if dest.Kind() == reflect.Ptr { + return store(dest.Elem(), src) + } + switch src.Kind() { + case reflect.Slice: + return storeSlice(dest, src) + case reflect.Map: + return storeMap(dest, src) + default: + return storeBase(dest, src) } } -func hasStruct(v interface{}) bool { - t := reflect.TypeOf(v) - for { - switch t.Kind() { - case reflect.Struct: - return true - case reflect.Slice, reflect.Ptr, reflect.Map: - t = t.Elem() - default: - return false +func storeBase(dest, src reflect.Value) error { + return setDest(dest, src) +} + +func setDest(dest, src reflect.Value) error { + if !isVariant(src.Type()) && isVariant(dest.Type()) { + //special conversion for dbus.Variant + dest.Set(reflect.ValueOf(MakeVariant(src.Interface()))) + return nil + } + if isVariant(src.Type()) && !isVariant(dest.Type()) { + src = getVariantValue(src) + } + if !src.Type().ConvertibleTo(dest.Type()) { + return fmt.Errorf( + "dbus.Store: type mismatch: cannot convert %s to %s", + src.Type(), dest.Type()) + } + dest.Set(src.Convert(dest.Type())) + return nil +} + +func kindsAreCompatible(dest, src reflect.Type) bool { + switch { + case isVariant(dest): + return true + case dest.Kind() == reflect.Interface: + return true + default: + return dest.Kind() == src.Kind() + } +} + +func isConvertibleTo(dest, src reflect.Type) bool { + switch { + case isVariant(dest): + return true + case dest.Kind() == reflect.Interface: + return true + case dest.Kind() == reflect.Slice: + return src.Kind() == reflect.Slice && + isConvertibleTo(dest.Elem(), src.Elem()) + case dest.Kind() == reflect.Struct: + return src == interfacesType + default: + return src.ConvertibleTo(dest) + } +} + +func storeMap(dest, src reflect.Value) error { + switch { + case !kindsAreCompatible(dest.Type(), src.Type()): + return fmt.Errorf( + "dbus.Store: type mismatch: "+ + "map: cannot store a value of %s into %s", + src.Type(), dest.Type()) + case isVariant(dest.Type()): + return storeMapIntoVariant(dest, src) + case dest.Kind() == reflect.Interface: + return storeMapIntoInterface(dest, src) + case isConvertibleTo(dest.Type().Key(), src.Type().Key()) && + isConvertibleTo(dest.Type().Elem(), src.Type().Elem()): + return storeMapIntoMap(dest, src) + default: + return fmt.Errorf( + "dbus.Store: type mismatch: "+ + "map: cannot convert a value of %s into %s", + src.Type(), dest.Type()) + } +} + +func storeMapIntoVariant(dest, src reflect.Value) error { + dv := reflect.MakeMap(src.Type()) + err := store(dv, src) + if err != nil { + return err + } + return storeBase(dest, dv) +} + +func storeMapIntoInterface(dest, src reflect.Value) error { + var dv reflect.Value + if isVariant(src.Type().Elem()) { + //Convert variants to interface{} recursively when converting + //to interface{} + dv = reflect.MakeMap( + reflect.MapOf(src.Type().Key(), interfaceType)) + } else { + dv = reflect.MakeMap(src.Type()) + } + err := store(dv, src) + if err != nil { + return err + } + return storeBase(dest, dv) +} + +func storeMapIntoMap(dest, src reflect.Value) error { + if dest.IsNil() { + dest.Set(reflect.MakeMap(dest.Type())) + } + keys := src.MapKeys() + for _, key := range keys { + dkey := key.Convert(dest.Type().Key()) + dval := reflect.New(dest.Type().Elem()).Elem() + err := store(dval, getVariantValue(src.MapIndex(key))) + if err != nil { + return err + } + dest.SetMapIndex(dkey, dval) + } + return nil +} + +func storeSlice(dest, src reflect.Value) error { + switch { + case src.Type() == interfacesType && dest.Kind() == reflect.Struct: + //The decoder always decodes structs as slices of interface{} + return storeStruct(dest, src) + case !kindsAreCompatible(dest.Type(), src.Type()): + return fmt.Errorf( + "dbus.Store: type mismatch: "+ + "slice: cannot store a value of %s into %s", + src.Type(), dest.Type()) + case isVariant(dest.Type()): + return storeSliceIntoVariant(dest, src) + case dest.Kind() == reflect.Interface: + return storeSliceIntoInterface(dest, src) + case isConvertibleTo(dest.Type().Elem(), src.Type().Elem()): + return storeSliceIntoSlice(dest, src) + default: + return fmt.Errorf( + "dbus.Store: type mismatch: "+ + "slice: cannot convert a value of %s into %s", + src.Type(), dest.Type()) + } +} + +func storeStruct(dest, src reflect.Value) error { + if isVariant(dest.Type()) { + return storeBase(dest, src) + } + dval := make([]interface{}, 0, dest.NumField()) + dtype := dest.Type() + for i := 0; i < dest.NumField(); i++ { + field := dest.Field(i) + ftype := dtype.Field(i) + if ftype.PkgPath != "" { + continue + } + if ftype.Tag.Get("dbus") == "-" { + continue + } + dval = append(dval, field.Addr().Interface()) + } + if src.Len() != len(dval) { + return fmt.Errorf( + "dbus.Store: type mismatch: "+ + "destination struct does not have "+ + "enough fields need: %d have: %d", + src.Len(), len(dval)) + } + return Store(src.Interface().([]interface{}), dval...) +} + +func storeSliceIntoVariant(dest, src reflect.Value) error { + dv := reflect.MakeSlice(src.Type(), src.Len(), src.Cap()) + err := store(dv, src) + if err != nil { + return err + } + return storeBase(dest, dv) +} + +func storeSliceIntoInterface(dest, src reflect.Value) error { + var dv reflect.Value + if isVariant(src.Type().Elem()) { + //Convert variants to interface{} recursively when converting + //to interface{} + dv = reflect.MakeSlice(reflect.SliceOf(interfaceType), + src.Len(), src.Cap()) + } else { + dv = reflect.MakeSlice(src.Type(), src.Len(), src.Cap()) + } + err := store(dv, src) + if err != nil { + return err + } + return storeBase(dest, dv) +} + +func storeSliceIntoSlice(dest, src reflect.Value) error { + if dest.IsNil() || dest.Len() < src.Len() { + dest.Set(reflect.MakeSlice(dest.Type(), src.Len(), src.Cap())) + } + if dest.Len() != src.Len() { + return fmt.Errorf( + "dbus.Store: type mismatch: "+ + "slices are different lengths "+ + "need: %d have: %d", + src.Len(), dest.Len()) + } + for i := 0; i < src.Len(); i++ { + err := store(dest.Index(i), getVariantValue(src.Index(i))) + if err != nil { + return err } } + return nil +} + +func getVariantValue(in reflect.Value) reflect.Value { + if isVariant(in.Type()) { + return reflect.ValueOf(in.Interface().(Variant).Value()) + } + return in +} + +func isVariant(t reflect.Type) bool { + return t == variantType } // An ObjectPath is an object path as defined by the D-Bus spec. @@ -177,15 +346,15 @@ func alignment(t reflect.Type) int { return 4 case signatureType: return 1 - case interfacesType: // sometimes used for structs - return 8 + case interfacesType: + return 4 } switch t.Kind() { case reflect.Uint8: return 1 case reflect.Uint16, reflect.Int16: return 2 - case reflect.Uint32, reflect.Int32, reflect.String, reflect.Array, reflect.Slice, reflect.Map: + case reflect.Uint, reflect.Int, reflect.Uint32, reflect.Int32, reflect.String, reflect.Array, reflect.Slice, reflect.Map: return 4 case reflect.Uint64, reflect.Int64, reflect.Float64, reflect.Struct: return 8 @@ -200,7 +369,7 @@ func isKeyType(t reflect.Type) bool { switch t.Kind() { case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float64, - reflect.String: + reflect.String, reflect.Uint, reflect.Int: return true } diff --git a/vendor/github.com/godbus/dbus/default_handler.go b/vendor/github.com/godbus/dbus/default_handler.go new file mode 100644 index 00000000..e81f73ac --- /dev/null +++ b/vendor/github.com/godbus/dbus/default_handler.go @@ -0,0 +1,291 @@ +package dbus + +import ( + "bytes" + "reflect" + "strings" + "sync" +) + +func newIntrospectIntf(h *defaultHandler) *exportedIntf { + methods := make(map[string]Method) + methods["Introspect"] = exportedMethod{ + reflect.ValueOf(func(msg Message) (string, *Error) { + path := msg.Headers[FieldPath].value.(ObjectPath) + return h.introspectPath(path), nil + }), + } + return newExportedIntf(methods, true) +} + +//NewDefaultHandler returns an instance of the default +//call handler. This is useful if you want to implement only +//one of the two handlers but not both. +func NewDefaultHandler() *defaultHandler { + h := &defaultHandler{ + objects: make(map[ObjectPath]*exportedObj), + defaultIntf: make(map[string]*exportedIntf), + } + h.defaultIntf["org.freedesktop.DBus.Introspectable"] = newIntrospectIntf(h) + return h +} + +type defaultHandler struct { + sync.RWMutex + objects map[ObjectPath]*exportedObj + defaultIntf map[string]*exportedIntf +} + +func (h *defaultHandler) PathExists(path ObjectPath) bool { + _, ok := h.objects[path] + return ok +} + +func (h *defaultHandler) introspectPath(path ObjectPath) string { + subpath := make(map[string]struct{}) + var xml bytes.Buffer + xml.WriteString("") + for obj, _ := range h.objects { + p := string(path) + if p != "/" { + p += "/" + } + if strings.HasPrefix(string(obj), p) { + node_name := strings.Split(string(obj[len(p):]), "/")[0] + subpath[node_name] = struct{}{} + } + } + for s, _ := range subpath { + xml.WriteString("\n\t") + } + xml.WriteString("\n") + return xml.String() +} + +func (h *defaultHandler) LookupObject(path ObjectPath) (ServerObject, bool) { + h.RLock() + defer h.RUnlock() + object, ok := h.objects[path] + if ok { + return object, ok + } + + // If an object wasn't found for this exact path, + // look for a matching subtree registration + subtreeObject := newExportedObject() + path = path[:strings.LastIndex(string(path), "/")] + for len(path) > 0 { + object, ok = h.objects[path] + if ok { + for name, iface := range object.interfaces { + // Only include this handler if it registered for the subtree + if iface.isFallbackInterface() { + subtreeObject.interfaces[name] = iface + } + } + break + } + + path = path[:strings.LastIndex(string(path), "/")] + } + + for name, intf := range h.defaultIntf { + if _, exists := subtreeObject.interfaces[name]; exists { + continue + } + subtreeObject.interfaces[name] = intf + } + + return subtreeObject, true +} + +func (h *defaultHandler) AddObject(path ObjectPath, object *exportedObj) { + h.Lock() + h.objects[path] = object + h.Unlock() +} + +func (h *defaultHandler) DeleteObject(path ObjectPath) { + h.Lock() + delete(h.objects, path) + h.Unlock() +} + +type exportedMethod struct { + reflect.Value +} + +func (m exportedMethod) Call(args ...interface{}) ([]interface{}, error) { + t := m.Type() + + params := make([]reflect.Value, len(args)) + for i := 0; i < len(args); i++ { + params[i] = reflect.ValueOf(args[i]).Elem() + } + + ret := m.Value.Call(params) + + err := ret[t.NumOut()-1].Interface().(*Error) + ret = ret[:t.NumOut()-1] + out := make([]interface{}, len(ret)) + for i, val := range ret { + out[i] = val.Interface() + } + if err == nil { + //concrete type to interface nil is a special case + return out, nil + } + return out, err +} + +func (m exportedMethod) NumArguments() int { + return m.Value.Type().NumIn() +} + +func (m exportedMethod) ArgumentValue(i int) interface{} { + return reflect.Zero(m.Type().In(i)).Interface() +} + +func (m exportedMethod) NumReturns() int { + return m.Value.Type().NumOut() +} + +func (m exportedMethod) ReturnValue(i int) interface{} { + return reflect.Zero(m.Type().Out(i)).Interface() +} + +func newExportedObject() *exportedObj { + return &exportedObj{ + interfaces: make(map[string]*exportedIntf), + } +} + +type exportedObj struct { + interfaces map[string]*exportedIntf +} + +func (obj *exportedObj) LookupInterface(name string) (Interface, bool) { + if name == "" { + return obj, true + } + intf, exists := obj.interfaces[name] + return intf, exists +} + +func (obj *exportedObj) AddInterface(name string, iface *exportedIntf) { + obj.interfaces[name] = iface +} + +func (obj *exportedObj) DeleteInterface(name string) { + delete(obj.interfaces, name) +} + +func (obj *exportedObj) LookupMethod(name string) (Method, bool) { + for _, intf := range obj.interfaces { + method, exists := intf.LookupMethod(name) + if exists { + return method, exists + } + } + return nil, false +} + +func (obj *exportedObj) isFallbackInterface() bool { + return false +} + +func newExportedIntf(methods map[string]Method, includeSubtree bool) *exportedIntf { + return &exportedIntf{ + methods: methods, + includeSubtree: includeSubtree, + } +} + +type exportedIntf struct { + methods map[string]Method + + // Whether or not this export is for the entire subtree + includeSubtree bool +} + +func (obj *exportedIntf) LookupMethod(name string) (Method, bool) { + out, exists := obj.methods[name] + return out, exists +} + +func (obj *exportedIntf) isFallbackInterface() bool { + return obj.includeSubtree +} + +//NewDefaultSignalHandler returns an instance of the default +//signal handler. This is useful if you want to implement only +//one of the two handlers but not both. +func NewDefaultSignalHandler() *defaultSignalHandler { + return &defaultSignalHandler{} +} + +func isDefaultSignalHandler(handler SignalHandler) bool { + _, ok := handler.(*defaultSignalHandler) + return ok +} + +type defaultSignalHandler struct { + sync.RWMutex + closed bool + signals []chan<- *Signal +} + +func (sh *defaultSignalHandler) DeliverSignal(intf, name string, signal *Signal) { + go func() { + sh.RLock() + defer sh.RUnlock() + if sh.closed { + return + } + for _, ch := range sh.signals { + ch <- signal + } + }() +} + +func (sh *defaultSignalHandler) Init() error { + sh.Lock() + sh.signals = make([]chan<- *Signal, 0) + sh.Unlock() + return nil +} + +func (sh *defaultSignalHandler) Terminate() { + sh.Lock() + sh.closed = true + for _, ch := range sh.signals { + close(ch) + } + sh.signals = nil + sh.Unlock() +} + +func (sh *defaultSignalHandler) addSignal(ch chan<- *Signal) { + sh.Lock() + defer sh.Unlock() + if sh.closed { + return + } + sh.signals = append(sh.signals, ch) + +} + +func (sh *defaultSignalHandler) removeSignal(ch chan<- *Signal) { + sh.Lock() + defer sh.Unlock() + if sh.closed { + return + } + for i := len(sh.signals) - 1; i >= 0; i-- { + if ch == sh.signals[i] { + copy(sh.signals[i:], sh.signals[i+1:]) + sh.signals[len(sh.signals)-1] = nil + sh.signals = sh.signals[:len(sh.signals)-1] + } + } +} diff --git a/vendor/github.com/godbus/dbus/doc.go b/vendor/github.com/godbus/dbus/doc.go index deff554a..895036a8 100644 --- a/vendor/github.com/godbus/dbus/doc.go +++ b/vendor/github.com/godbus/dbus/doc.go @@ -19,6 +19,8 @@ respective D-Bus equivalents: bool | BOOLEAN int16 | INT16 uint16 | UINT16 + int | INT32 + uint | UINT32 int32 | INT32 uint32 | UINT32 int64 | INT64 @@ -28,6 +30,7 @@ respective D-Bus equivalents: ObjectPath | OBJECT_PATH Signature | SIGNATURE Variant | VARIANT + interface{} | VARIANT UnixFDIndex | UNIX_FD Slices and arrays encode as ARRAYs of their element type. @@ -41,6 +44,9 @@ be skipped. Pointers encode as the value they're pointed to. +Types convertible to one of the base types above will be mapped as the +base type. + Trying to encode any other type or a slice, map or struct containing an unsupported type will result in an InvalidTypeError. diff --git a/vendor/github.com/godbus/dbus/encoder.go b/vendor/github.com/godbus/dbus/encoder.go index 9f0a9e89..8bb71776 100644 --- a/vendor/github.com/godbus/dbus/encoder.go +++ b/vendor/github.com/godbus/dbus/encoder.go @@ -96,10 +96,10 @@ func (enc *encoder) encode(v reflect.Value, depth int) { case reflect.Uint16: enc.binwrite(uint16(v.Uint())) enc.pos += 2 - case reflect.Int32: + case reflect.Int, reflect.Int32: enc.binwrite(int32(v.Int())) enc.pos += 4 - case reflect.Uint32: + case reflect.Uint, reflect.Uint32: enc.binwrite(uint32(v.Uint())) enc.pos += 4 case reflect.Int64: @@ -202,6 +202,8 @@ func (enc *encoder) encode(v reflect.Value, depth int) { panic(err) } enc.pos += length + case reflect.Interface: + enc.encode(reflect.ValueOf(MakeVariant(v.Interface())), depth) default: panic(InvalidTypeError{v.Type()}) } diff --git a/vendor/github.com/godbus/dbus/export.go b/vendor/github.com/godbus/dbus/export.go index 6c335220..aae97088 100644 --- a/vendor/github.com/godbus/dbus/export.go +++ b/vendor/github.com/godbus/dbus/export.go @@ -1,7 +1,6 @@ package dbus import ( - "bytes" "errors" "fmt" "reflect" @@ -9,32 +8,29 @@ import ( ) var ( - errmsgInvalidArg = Error{ + ErrMsgInvalidArg = Error{ "org.freedesktop.DBus.Error.InvalidArgs", []interface{}{"Invalid type / number of args"}, } - errmsgNoObject = Error{ + ErrMsgNoObject = Error{ "org.freedesktop.DBus.Error.NoSuchObject", []interface{}{"No such object"}, } - errmsgUnknownMethod = Error{ + ErrMsgUnknownMethod = Error{ "org.freedesktop.DBus.Error.UnknownMethod", []interface{}{"Unknown / invalid method"}, } + ErrMsgUnknownInterface = Error{ + "org.freedesktop.DBus.Error.UnknownInterface", + []interface{}{"Object does not implement the interface"}, + } ) -// exportedObj represents an exported object. It stores a precomputed -// method table that represents the methods exported on the bus. -type exportedObj struct { - methods map[string]reflect.Value - - // Whether or not this export is for the entire subtree - includeSubtree bool -} - -func (obj exportedObj) Method(name string) (reflect.Value, bool) { - out, exists := obj.methods[name] - return out, exists +func MakeFailedError(err error) *Error { + return &Error{ + "org.freedesktop.DBus.Error.Failed", + []interface{}{err.Error()}, + } } // Sender is a type which can be used in exported methods to receive the message @@ -63,7 +59,7 @@ func getMethods(in interface{}, mapping map[string]string) map[string]reflect.Va // only track valid methods must return *Error as last arg // and must be exported if t.NumOut() == 0 || - t.Out(t.NumOut()-1) != reflect.TypeOf(&errmsgInvalidArg) || + t.Out(t.NumOut()-1) != reflect.TypeOf(&ErrMsgInvalidArg) || methtype.PkgPath != "" { continue } @@ -73,119 +69,12 @@ func getMethods(in interface{}, mapping map[string]string) map[string]reflect.Va return methods } -// searchHandlers will look through all registered handlers looking for one -// to handle the given path. If a verbatim one isn't found, it will check for -// a subtree registration for the path as well. -func (conn *Conn) searchHandlers(path ObjectPath) (map[string]exportedObj, bool) { - conn.handlersLck.RLock() - defer conn.handlersLck.RUnlock() +func standardMethodArgumentDecode(m Method, sender string, msg *Message, body []interface{}) ([]interface{}, error) { + pointers := make([]interface{}, m.NumArguments()) + decode := make([]interface{}, 0, len(body)) - handlers, ok := conn.handlers[path] - if ok { - return handlers, ok - } - - // If handlers weren't found for this exact path, look for a matching subtree - // registration - handlers = make(map[string]exportedObj) - path = path[:strings.LastIndex(string(path), "/")] - for len(path) > 0 { - var subtreeHandlers map[string]exportedObj - subtreeHandlers, ok = conn.handlers[path] - if ok { - for iface, handler := range subtreeHandlers { - // Only include this handler if it registered for the subtree - if handler.includeSubtree { - handlers[iface] = handler - } - } - - break - } - - path = path[:strings.LastIndex(string(path), "/")] - } - - return handlers, ok -} - -// handleCall handles the given method call (i.e. looks if it's one of the -// pre-implemented ones and searches for a corresponding handler if not). -func (conn *Conn) handleCall(msg *Message) { - name := msg.Headers[FieldMember].value.(string) - path := msg.Headers[FieldPath].value.(ObjectPath) - ifaceName, hasIface := msg.Headers[FieldInterface].value.(string) - sender, hasSender := msg.Headers[FieldSender].value.(string) - serial := msg.serial - if ifaceName == "org.freedesktop.DBus.Peer" { - switch name { - case "Ping": - conn.sendReply(sender, serial) - case "GetMachineId": - conn.sendReply(sender, serial, conn.uuid) - default: - conn.sendError(errmsgUnknownMethod, sender, serial) - } - return - } else if ifaceName == "org.freedesktop.DBus.Introspectable" && name == "Introspect" { - if _, ok := conn.handlers[path]; !ok { - subpath := make(map[string]struct{}) - var xml bytes.Buffer - xml.WriteString("") - for h, _ := range conn.handlers { - p := string(path) - if p != "/" { - p += "/" - } - if strings.HasPrefix(string(h), p) { - node_name := strings.Split(string(h[len(p):]), "/")[0] - subpath[node_name] = struct{}{} - } - } - for s, _ := range subpath { - xml.WriteString("\n\t") - } - xml.WriteString("\n") - conn.sendReply(sender, serial, xml.String()) - return - } - } - if len(name) == 0 { - conn.sendError(errmsgUnknownMethod, sender, serial) - } - - // Find the exported handler (if any) for this path - handlers, ok := conn.searchHandlers(path) - if !ok { - conn.sendError(errmsgNoObject, sender, serial) - return - } - - var m reflect.Value - var exists bool - if hasIface { - iface := handlers[ifaceName] - m, exists = iface.Method(name) - } else { - for _, v := range handlers { - m, exists = v.Method(name) - if exists { - break - } - } - } - - if !exists { - conn.sendError(errmsgUnknownMethod, sender, serial) - return - } - - t := m.Type() - vs := msg.Body - pointers := make([]interface{}, t.NumIn()) - decode := make([]interface{}, 0, len(vs)) - for i := 0; i < t.NumIn(); i++ { - tp := t.In(i) + for i := 0; i < m.NumArguments(); i++ { + tp := reflect.TypeOf(m.ArgumentValue(i)) val := reflect.New(tp) pointers[i] = val.Interface() if tp == reflect.TypeOf((*Sender)(nil)).Elem() { @@ -197,26 +86,73 @@ func (conn *Conn) handleCall(msg *Message) { } } - if len(decode) != len(vs) { - conn.sendError(errmsgInvalidArg, sender, serial) + if len(decode) != len(body) { + return nil, ErrMsgInvalidArg + } + + if err := Store(body, decode...); err != nil { + return nil, ErrMsgInvalidArg + } + + return pointers, nil +} + +func (conn *Conn) decodeArguments(m Method, sender string, msg *Message) ([]interface{}, error) { + if decoder, ok := m.(ArgumentDecoder); ok { + return decoder.DecodeArguments(conn, sender, msg, msg.Body) + } + return standardMethodArgumentDecode(m, sender, msg, msg.Body) +} + +// handleCall handles the given method call (i.e. looks if it's one of the +// pre-implemented ones and searches for a corresponding handler if not). +func (conn *Conn) handleCall(msg *Message) { + name := msg.Headers[FieldMember].value.(string) + path := msg.Headers[FieldPath].value.(ObjectPath) + ifaceName, _ := msg.Headers[FieldInterface].value.(string) + sender, hasSender := msg.Headers[FieldSender].value.(string) + serial := msg.serial + if ifaceName == "org.freedesktop.DBus.Peer" { + switch name { + case "Ping": + conn.sendReply(sender, serial) + case "GetMachineId": + conn.sendReply(sender, serial, conn.uuid) + default: + conn.sendError(ErrMsgUnknownMethod, sender, serial) + } + return + } + if len(name) == 0 { + conn.sendError(ErrMsgUnknownMethod, sender, serial) + } + + object, ok := conn.handler.LookupObject(path) + if !ok { + conn.sendError(ErrMsgNoObject, sender, serial) return } - if err := Store(vs, decode...); err != nil { - conn.sendError(errmsgInvalidArg, sender, serial) + iface, exists := object.LookupInterface(ifaceName) + if !exists { + conn.sendError(ErrMsgUnknownInterface, sender, serial) return } - // Extract parameters - params := make([]reflect.Value, len(pointers)) - for i := 0; i < len(pointers); i++ { - params[i] = reflect.ValueOf(pointers[i]).Elem() + m, exists := iface.LookupMethod(name) + if !exists { + conn.sendError(ErrMsgUnknownMethod, sender, serial) + return + } + args, err := conn.decodeArguments(m, sender, msg) + if err != nil { + conn.sendError(err, sender, serial) + return } - // Call method - ret := m.Call(params) - if em := ret[t.NumOut()-1].Interface().(*Error); em != nil { - conn.sendError(*em, sender, serial) + ret, err := m.Call(args...) + if err != nil { + conn.sendError(err, sender, serial) return } @@ -229,13 +165,11 @@ func (conn *Conn) handleCall(msg *Message) { reply.Headers[FieldDestination] = msg.Headers[FieldSender] } reply.Headers[FieldReplySerial] = MakeVariant(msg.serial) - reply.Body = make([]interface{}, len(ret)-1) - for i := 0; i < len(ret)-1; i++ { - reply.Body[i] = ret[i].Interface() - } - if len(ret) != 1 { - reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...)) + reply.Body = make([]interface{}, len(ret)) + for i := 0; i < len(ret); i++ { + reply.Body[i] = ret[i] } + reply.Headers[FieldSignature] = MakeVariant(SignatureOf(reply.Body...)) conn.outLck.RLock() if !conn.closed { conn.out <- reply @@ -375,7 +309,7 @@ func (conn *Conn) exportMethodTable(methods map[string]interface{}, path ObjectP t := rval.Type() // only track valid methods must return *Error as last arg if t.NumOut() == 0 || - t.Out(t.NumOut()-1) != reflect.TypeOf(&errmsgInvalidArg) { + t.Out(t.NumOut()-1) != reflect.TypeOf(&ErrMsgInvalidArg) { continue } out[name] = rval @@ -383,38 +317,49 @@ func (conn *Conn) exportMethodTable(methods map[string]interface{}, path ObjectP return conn.export(out, path, iface, includeSubtree) } +func (conn *Conn) unexport(h *defaultHandler, path ObjectPath, iface string) error { + if h.PathExists(path) { + obj := h.objects[path] + obj.DeleteInterface(iface) + if len(obj.interfaces) == 0 { + h.DeleteObject(path) + } + } + return nil +} + // exportWithMap is the worker function for all exports/registrations. func (conn *Conn) export(methods map[string]reflect.Value, path ObjectPath, iface string, includeSubtree bool) error { + h, ok := conn.handler.(*defaultHandler) + if !ok { + return fmt.Errorf( + `dbus: export only allowed on the default hander handler have %T"`, + conn.handler) + } + if !path.IsValid() { return fmt.Errorf(`dbus: Invalid path name: "%s"`, path) } - conn.handlersLck.Lock() - defer conn.handlersLck.Unlock() - // Remove a previous export if the interface is nil if methods == nil { - if _, ok := conn.handlers[path]; ok { - delete(conn.handlers[path], iface) - if len(conn.handlers[path]) == 0 { - delete(conn.handlers, path) - } - } - - return nil + return conn.unexport(h, path, iface) } // If this is the first handler for this path, make a new map to hold all // handlers for this path. - if _, ok := conn.handlers[path]; !ok { - conn.handlers[path] = make(map[string]exportedObj) + if !h.PathExists(path) { + h.AddObject(path, newExportedObject()) + } + + exportedMethods := make(map[string]Method) + for name, method := range methods { + exportedMethods[name] = exportedMethod{method} } // Finally, save this handler - conn.handlers[path][iface] = exportedObj{ - methods: methods, - includeSubtree: includeSubtree, - } + obj := h.objects[path] + obj.AddInterface(iface, newExportedIntf(exportedMethods, includeSubtree)) return nil } diff --git a/vendor/github.com/godbus/dbus/object.go b/vendor/github.com/godbus/dbus/object.go index 9573b709..6d95583d 100644 --- a/vendor/github.com/godbus/dbus/object.go +++ b/vendor/github.com/godbus/dbus/object.go @@ -43,7 +43,8 @@ func (o *Object) AddMatchSignal(iface, member string) *Call { // will be allocated. Otherwise, ch has to be buffered or Go will panic. // // If the flags include FlagNoReplyExpected, ch is ignored and a Call structure -// is returned of which only the Err member is valid. +// is returned with any error in Err and a closed channel in Done containing +// the returned Call as it's one entry. // // If the method parameter contains a dot ('.'), the part before the last dot // specifies the interface on which the method is called. @@ -97,11 +98,21 @@ func (o *Object) Go(method string, flags Flags, ch chan *Call, args ...interface } o.conn.outLck.RLock() defer o.conn.outLck.RUnlock() + done := make(chan *Call, 1) + call := &Call{ + Err: nil, + Done: done, + } + defer func() { + call.Done <- call + close(done) + }() if o.conn.closed { - return &Call{Err: ErrClosed} + call.Err = ErrClosed + return call } o.conn.out <- msg - return &Call{Err: nil} + return call } // GetProperty calls org.freedesktop.DBus.Properties.GetProperty on the given @@ -125,12 +136,12 @@ func (o *Object) GetProperty(p string) (Variant, error) { return result, nil } -// Destination returns the destination that calls on o are sent to. +// Destination returns the destination that calls on (o *Object) are sent to. func (o *Object) Destination() string { return o.dest } -// Path returns the path that calls on o are sent to. +// Path returns the path that calls on (o *Object") are sent to. func (o *Object) Path() ObjectPath { return o.path } diff --git a/vendor/github.com/godbus/dbus/server_interfaces.go b/vendor/github.com/godbus/dbus/server_interfaces.go new file mode 100644 index 00000000..091948ae --- /dev/null +++ b/vendor/github.com/godbus/dbus/server_interfaces.go @@ -0,0 +1,89 @@ +package dbus + +// Terminator allows a handler to implement a shutdown mechanism that +// is called when the connection terminates. +type Terminator interface { + Terminate() +} + +// Handler is the representation of a D-Bus Application. +// +// The Handler must have a way to lookup objects given +// an ObjectPath. The returned object must implement the +// ServerObject interface. +type Handler interface { + LookupObject(path ObjectPath) (ServerObject, bool) +} + +// ServerObject is the representation of an D-Bus Object. +// +// Objects are registered at a path for a given Handler. +// The Objects implement D-Bus interfaces. The semantics +// of Interface lookup is up to the implementation of +// the ServerObject. The ServerObject implementation may +// choose to implement empty string as a valid interface +// represeting all methods or not per the D-Bus specification. +type ServerObject interface { + LookupInterface(name string) (Interface, bool) +} + +// An Interface is the representation of a D-Bus Interface. +// +// Interfaces are a grouping of methods implemented by the Objects. +// Interfaces are responsible for routing method calls. +type Interface interface { + LookupMethod(name string) (Method, bool) +} + +// A Method represents the exposed methods on D-Bus. +type Method interface { + // Call requires that all arguments are decoded before being passed to it. + Call(args ...interface{}) ([]interface{}, error) + NumArguments() int + NumReturns() int + // ArgumentValue returns a representative value for the argument at position + // it should be of the proper type. reflect.Zero would be a good mechanism + // to use for this Value. + ArgumentValue(position int) interface{} + // ReturnValue returns a representative value for the return at position + // it should be of the proper type. reflect.Zero would be a good mechanism + // to use for this Value. + ReturnValue(position int) interface{} +} + +// An Argument Decoder can decode arguments using the non-standard mechanism +// +// If a method implements this interface then the non-standard +// decoder will be used. +// +// Method arguments must be decoded from the message. +// The mechanism for doing this will vary based on the +// implementation of the method. A normal approach is provided +// as part of this library, but may be replaced with +// any other decoding scheme. +type ArgumentDecoder interface { + // To decode the arguments of a method the sender and message are + // provided incase the semantics of the implementer provides access + // to these as part of the method invocation. + DecodeArguments(conn *Conn, sender string, msg *Message, args []interface{}) ([]interface{}, error) +} + +// A SignalHandler is responsible for delivering a signal. +// +// Signal delivery may be changed from the default channel +// based approach by Handlers implementing the SignalHandler +// interface. +type SignalHandler interface { + DeliverSignal(iface, name string, signal *Signal) +} + +// A DBusError is used to convert a generic object to a D-Bus error. +// +// Any custom error mechanism may implement this interface to provide +// a custom encoding of the error on D-Bus. By default if a normal +// error is returned, it will be encoded as the generic +// "org.freedesktop.DBus.Error.Failed" error. By implementing this +// interface as well a custom encoding may be provided. +type DBusError interface { + DBusError() (string, []interface{}) +} diff --git a/vendor/github.com/godbus/dbus/sig.go b/vendor/github.com/godbus/dbus/sig.go index f45b53ce..c1b80920 100644 --- a/vendor/github.com/godbus/dbus/sig.go +++ b/vendor/github.com/godbus/dbus/sig.go @@ -57,12 +57,12 @@ func getSignature(t reflect.Type) string { return "n" case reflect.Uint16: return "q" - case reflect.Int32: + case reflect.Int, reflect.Int32: if t == unixFDType { return "h" } return "i" - case reflect.Uint32: + case reflect.Uint, reflect.Uint32: if t == unixFDIndexType { return "h" } @@ -101,6 +101,8 @@ func getSignature(t reflect.Type) string { panic(InvalidTypeError{t}) } return "a{" + getSignature(t.Key()) + getSignature(t.Elem()) + "}" + case reflect.Interface: + return "v" } panic(InvalidTypeError{t}) } @@ -162,7 +164,7 @@ func (e SignatureError) Error() string { return fmt.Sprintf("dbus: invalid signature: %q (%s)", e.Sig, e.Reason) } -// Try to read a single type from this string. If it was successfull, err is nil +// Try to read a single type from this string. If it was successful, err is nil // and rem is the remaining unparsed part. Otherwise, err is a non-nil // SignatureError and rem is "". depth is the current recursion depth which may // not be greater than 64 and should be given as 0 on the first call. diff --git a/vendor/github.com/godbus/dbus/transport_generic.go b/vendor/github.com/godbus/dbus/transport_generic.go index 46f8f49d..3fad859a 100644 --- a/vendor/github.com/godbus/dbus/transport_generic.go +++ b/vendor/github.com/godbus/dbus/transport_generic.go @@ -4,8 +4,23 @@ import ( "encoding/binary" "errors" "io" + "unsafe" ) +var nativeEndian binary.ByteOrder + +func detectEndianness() binary.ByteOrder { + var x uint32 = 0x01020304 + if *(*byte)(unsafe.Pointer(&x)) == 0x01 { + return binary.BigEndian + } + return binary.LittleEndian +} + +func init() { + nativeEndian = detectEndianness() +} + type genericTransport struct { io.ReadWriteCloser } @@ -31,5 +46,5 @@ func (t genericTransport) SendMessage(msg *Message) error { return errors.New("dbus: unix fd passing not enabled") } } - return msg.EncodeTo(t, binary.LittleEndian) + return msg.EncodeTo(t, nativeEndian) } diff --git a/vendor/github.com/godbus/dbus/transport_unix.go b/vendor/github.com/godbus/dbus/transport_unix.go index a1d00cbc..e56d5ca9 100644 --- a/vendor/github.com/godbus/dbus/transport_unix.go +++ b/vendor/github.com/godbus/dbus/transport_unix.go @@ -175,7 +175,7 @@ func (t *unixTransport) SendMessage(msg *Message) error { msg.Headers[FieldUnixFDs] = MakeVariant(uint32(len(fds))) oob := syscall.UnixRights(fds...) buf := new(bytes.Buffer) - msg.EncodeTo(buf, binary.LittleEndian) + msg.EncodeTo(buf, nativeEndian) n, oobn, err := t.UnixConn.WriteMsgUnix(buf.Bytes(), oob, nil) if err != nil { return err @@ -184,7 +184,7 @@ func (t *unixTransport) SendMessage(msg *Message) error { return io.ErrShortWrite } } else { - if err := msg.EncodeTo(t, binary.LittleEndian); err != nil { + if err := msg.EncodeTo(t, nativeEndian); err != nil { return nil } } diff --git a/vendor/github.com/godbus/dbus/transport_unixcred_freebsd.go b/vendor/github.com/godbus/dbus/transport_unixcred_freebsd.go new file mode 100644 index 00000000..0fc5b927 --- /dev/null +++ b/vendor/github.com/godbus/dbus/transport_unixcred_freebsd.go @@ -0,0 +1,91 @@ +// The UnixCredentials system call is currently only implemented on Linux +// http://golang.org/src/pkg/syscall/sockcmsg_linux.go +// https://golang.org/s/go1.4-syscall +// http://code.google.com/p/go/source/browse/unix/sockcmsg_linux.go?repo=sys + +// Local implementation of the UnixCredentials system call for FreeBSD + +package dbus + +/* +const int sizeofPtr = sizeof(void*); +#define _WANT_UCRED +#include +*/ +import "C" + +import ( + "io" + "os" + "syscall" + "unsafe" +) + +// http://golang.org/src/pkg/syscall/ztypes_linux_amd64.go +// https://golang.org/src/syscall/ztypes_freebsd_amd64.go +type Ucred struct { + Pid int32 + Uid uint32 + Gid uint32 +} + +// http://golang.org/src/pkg/syscall/types_linux.go +// https://golang.org/src/syscall/types_freebsd.go +// https://github.com/freebsd/freebsd/blob/master/sys/sys/ucred.h +const ( + SizeofUcred = C.sizeof_struct_ucred +) + +// http://golang.org/src/pkg/syscall/sockcmsg_unix.go +func cmsgAlignOf(salen int) int { + salign := C.sizeofPtr + + return (salen + salign - 1) & ^(salign - 1) +} + +// http://golang.org/src/pkg/syscall/sockcmsg_unix.go +func cmsgData(h *syscall.Cmsghdr) unsafe.Pointer { + return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(syscall.SizeofCmsghdr))) +} + +// http://golang.org/src/pkg/syscall/sockcmsg_linux.go +// UnixCredentials encodes credentials into a socket control message +// for sending to another process. This can be used for +// authentication. +func UnixCredentials(ucred *Ucred) []byte { + b := make([]byte, syscall.CmsgSpace(SizeofUcred)) + h := (*syscall.Cmsghdr)(unsafe.Pointer(&b[0])) + h.Level = syscall.SOL_SOCKET + h.Type = syscall.SCM_CREDS + h.SetLen(syscall.CmsgLen(SizeofUcred)) + *((*Ucred)(cmsgData(h))) = *ucred + return b +} + +// http://golang.org/src/pkg/syscall/sockcmsg_linux.go +// ParseUnixCredentials decodes a socket control message that contains +// credentials in a Ucred structure. To receive such a message, the +// SO_PASSCRED option must be enabled on the socket. +func ParseUnixCredentials(m *syscall.SocketControlMessage) (*Ucred, error) { + if m.Header.Level != syscall.SOL_SOCKET { + return nil, syscall.EINVAL + } + if m.Header.Type != syscall.SCM_CREDS { + return nil, syscall.EINVAL + } + ucred := *(*Ucred)(unsafe.Pointer(&m.Data[0])) + return &ucred, nil +} + +func (t *unixTransport) SendNullByte() error { + ucred := &Ucred{Pid: int32(os.Getpid()), Uid: uint32(os.Getuid()), Gid: uint32(os.Getgid())} + b := UnixCredentials(ucred) + _, oobn, err := t.UnixConn.WriteMsgUnix([]byte{0}, b, nil) + if err != nil { + return err + } + if oobn != len(b) { + return io.ErrShortWrite + } + return nil +} diff --git a/vendor/github.com/godbus/dbus/transport_unixcred_openbsd.go b/vendor/github.com/godbus/dbus/transport_unixcred_openbsd.go new file mode 100644 index 00000000..af7bafdf --- /dev/null +++ b/vendor/github.com/godbus/dbus/transport_unixcred_openbsd.go @@ -0,0 +1,14 @@ +package dbus + +import "io" + +func (t *unixTransport) SendNullByte() error { + n, _, err := t.UnixConn.WriteMsgUnix([]byte{0}, nil, nil) + if err != nil { + return err + } + if n != 1 { + return io.ErrShortWrite + } + return nil +} diff --git a/vendor/github.com/godbus/dbus/variant.go b/vendor/github.com/godbus/dbus/variant.go index b7b13ae9..0ca123b0 100644 --- a/vendor/github.com/godbus/dbus/variant.go +++ b/vendor/github.com/godbus/dbus/variant.go @@ -17,7 +17,12 @@ type Variant struct { // MakeVariant converts the given value to a Variant. It panics if v cannot be // represented as a D-Bus type. func MakeVariant(v interface{}) Variant { - return Variant{SignatureOf(v), v} + return MakeVariantWithSignature(v, SignatureOf(v)) +} + +// MakeVariantWithSignature converts the given value to a Variant. +func MakeVariantWithSignature(v interface{}, s Signature) Variant { + return Variant{s, v} } // ParseVariant parses the given string as a variant as described at From a45c16d7fad89c7351315e1a5bf800995a3992f7 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Thu, 19 Oct 2017 21:12:55 +0200 Subject: [PATCH 18/43] fix host pid handling for containers and share uts ns Signed-off-by: Antonio Murdaca --- server/container_create.go | 14 ++++++++++++-- server/sandbox_run.go | 13 +++++++------ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/server/container_create.go b/server/container_create.go index ba9df0d6..d401a0ea 100644 --- a/server/container_create.go +++ b/server/container_create.go @@ -769,10 +769,20 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string, logrus.Debugf("pod container state %+v", podInfraState) ipcNsPath := fmt.Sprintf("/proc/%d/ns/ipc", podInfraState.Pid) - if err := specgen.AddOrReplaceLinuxNamespace("ipc", ipcNsPath); err != nil { + if err := specgen.AddOrReplaceLinuxNamespace(string(rspec.IPCNamespace), ipcNsPath); err != nil { return nil, err } + utsNsPath := fmt.Sprintf("/proc/%d/ns/uts", podInfraState.Pid) + if err := specgen.AddOrReplaceLinuxNamespace(string(rspec.UTSNamespace), utsNsPath); err != nil { + return nil, err + } + + // Do not share pid ns for now + if containerConfig.GetLinux().GetSecurityContext().GetNamespaceOptions().GetHostPid() { + specgen.RemoveLinuxNamespace(string(rspec.PIDNamespace)) + } + netNsPath := sb.NetNsPath() if netNsPath == "" { // The sandbox does not have a permanent namespace, @@ -780,7 +790,7 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string, netNsPath = fmt.Sprintf("/proc/%d/ns/net", podInfraState.Pid) } - if err := specgen.AddOrReplaceLinuxNamespace("network", netNsPath); err != nil { + if err := specgen.AddOrReplaceLinuxNamespace(string(rspec.NetworkNamespace), netNsPath); err != nil { return nil, err } diff --git a/server/sandbox_run.go b/server/sandbox_run.go index 0bebef84..20a10d1e 100644 --- a/server/sandbox_run.go +++ b/server/sandbox_run.go @@ -16,6 +16,7 @@ import ( "github.com/kubernetes-incubator/cri-o/libkpod/sandbox" "github.com/kubernetes-incubator/cri-o/oci" "github.com/kubernetes-incubator/cri-o/pkg/annotations" + runtimespec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" @@ -424,7 +425,7 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest // set up namespaces if hostNetwork { - err = g.RemoveLinuxNamespace("network") + err = g.RemoveLinuxNamespace(string(runtimespec.NetworkNamespace)) if err != nil { return nil, err } @@ -445,21 +446,21 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest }() // Pass the created namespace path to the runtime - err = g.AddOrReplaceLinuxNamespace("network", sb.NetNsPath()) + err = g.AddOrReplaceLinuxNamespace(string(runtimespec.NetworkNamespace), sb.NetNsPath()) if err != nil { return nil, err } } - if namespaceOptions.HostPid { - err = g.RemoveLinuxNamespace("pid") + if securityContext.GetNamespaceOptions().GetHostPid() { + err = g.RemoveLinuxNamespace(string(runtimespec.PIDNamespace)) if err != nil { return nil, err } } - if namespaceOptions.HostIpc { - err = g.RemoveLinuxNamespace("ipc") + if securityContext.GetNamespaceOptions().GetHostIpc() { + err = g.RemoveLinuxNamespace(string(runtimespec.IPCNamespace)) if err != nil { return nil, err } From d2ea9cc2fa3d381b00a8b445619fc9967fe93ef9 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Sun, 22 Oct 2017 10:26:14 +0000 Subject: [PATCH 19/43] We need to release the SELinux label when we destroy the sandbox This will release the MCS Label to be used again. Only do this if we don't have another sandbox using the same label. Also vendor in the latest selinux go bindings, which fixes a leak and properly reserves the SELinux label we are going to use. Signed-off-by: Daniel J Walsh Signed-off-by: Daniel J Walsh --- libkpod/container_server.go | 12 ++++++++++++ vendor.conf | 2 +- .../selinux/go-selinux/label/label_selinux.go | 2 ++ .../opencontainers/selinux/go-selinux/selinux.go | 4 ++-- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/libkpod/container_server.go b/libkpod/container_server.go index 1bc94871..f4a728e9 100644 --- a/libkpod/container_server.go +++ b/libkpod/container_server.go @@ -19,6 +19,7 @@ import ( "github.com/kubernetes-incubator/cri-o/pkg/storage" "github.com/opencontainers/runc/libcontainer" rspec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/selinux/go-selinux" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -168,6 +169,7 @@ func New(config *Config) (*ContainerServer, error) { containers: oci.NewMemoryStore(), infraContainers: oci.NewMemoryStore(), sandboxes: make(map[string]*sandbox.Sandbox), + processLevels: make(map[string]int), }, config: config, }, nil @@ -609,6 +611,8 @@ type containerServerState struct { containers oci.ContainerStorer infraContainers oci.ContainerStorer sandboxes map[string]*sandbox.Sandbox + // processLevels The number of sandboxes using the same SELinux MCS level. Need to release MCS Level, when count reaches 0 + processLevels map[string]int } // AddContainer adds a container to the container state store @@ -696,6 +700,7 @@ func (c *ContainerServer) AddSandbox(sb *sandbox.Sandbox) { c.stateLock.Lock() defer c.stateLock.Unlock() c.state.sandboxes[sb.ID()] = sb + c.state.processLevels[selinux.NewContext(sb.ProcessLabel())["level"]]++ } // GetSandbox returns a sandbox by its ID @@ -728,7 +733,14 @@ func (c *ContainerServer) HasSandbox(id string) bool { func (c *ContainerServer) RemoveSandbox(id string) { c.stateLock.Lock() defer c.stateLock.Unlock() + processLabel := c.state.sandboxes[id].ProcessLabel() delete(c.state.sandboxes, id) + level := selinux.NewContext(processLabel)["level"] + c.state.processLevels[level]-- + if c.state.processLevels[level] == 0 { + label.ReleaseLabel(processLabel) + delete(c.state.processLevels, level) + } } // ListSandboxes lists all sandboxes in the state store diff --git a/vendor.conf b/vendor.conf index 557701e9..5ac9f585 100644 --- a/vendor.conf +++ b/vendor.conf @@ -10,7 +10,7 @@ github.com/ostreedev/ostree-go master github.com/containers/storage 64bf27465d0d1edd89e7a4ce49866fea01145782 github.com/containernetworking/cni v0.4.0 google.golang.org/grpc v1.0.4 https://github.com/grpc/grpc-go -github.com/opencontainers/selinux v1.0.0-rc1 +github.com/opencontainers/selinux b29023b86e4a69d1b46b7e7b4e2b6fda03f0b9cd github.com/opencontainers/go-digest v1.0.0-rc0 github.com/opencontainers/runtime-tools d3f7e9e9e631c7e87552d67dc7c86de33c3fb68a github.com/opencontainers/runc 45bde006ca8c90e089894508708bcf0e2cdf9e13 diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go index 569dcf08..c008a387 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/label/label_selinux.go @@ -49,8 +49,10 @@ func InitLabels(options []string) (string, string, error) { mcon[con[0]] = con[1] } } + _ = ReleaseLabel(processLabel) processLabel = pcon.Get() mountLabel = mcon.Get() + _ = ReserveLabel(processLabel) } return processLabel, mountLabel, nil } diff --git a/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go b/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go index 4cf2c45d..de9316c2 100644 --- a/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go +++ b/vendor/github.com/opencontainers/selinux/go-selinux/selinux.go @@ -213,7 +213,7 @@ func SetFileLabel(path string, label string) error { return lsetxattr(path, xattrNameSelinux, []byte(label), 0) } -// Filecon returns the SELinux label for this path or returns an error. +// FileLabel returns the SELinux label for this path or returns an error. func FileLabel(path string) (string, error) { label, err := lgetxattr(path, xattrNameSelinux) if err != nil { @@ -331,7 +331,7 @@ func EnforceMode() int { } /* -SetEnforce sets the current SELinux mode Enforcing, Permissive. +SetEnforceMode sets the current SELinux mode Enforcing, Permissive. Disabled is not valid, since this needs to be set at boot time. */ func SetEnforceMode(mode int) error { From 64a30e16547f1f93e20e6bc0b2e81ab2e04319bd Mon Sep 17 00:00:00 2001 From: Mrunal Patel Date: Wed, 25 Oct 2017 07:42:06 -0700 Subject: [PATCH 20/43] version: Release 1.0.1 Signed-off-by: Mrunal Patel --- version/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version/version.go b/version/version.go index ac18bd01..9e0b8266 100644 --- a/version/version.go +++ b/version/version.go @@ -1,4 +1,4 @@ package version // Version is the version of the build. -const Version = "1.0.1-dev" +const Version = "1.0.1" From c1ccd5f29585fada27fc087d11d4c567c56ea4f1 Mon Sep 17 00:00:00 2001 From: Mrunal Patel Date: Wed, 25 Oct 2017 07:44:40 -0700 Subject: [PATCH 21/43] version: Bump up to 1.0.2-dev Signed-off-by: Mrunal Patel --- version/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version/version.go b/version/version.go index 9e0b8266..8f0928d3 100644 --- a/version/version.go +++ b/version/version.go @@ -1,4 +1,4 @@ package version // Version is the version of the build. -const Version = "1.0.1" +const Version = "1.0.2-dev" From 98ca0803d701435d5745a630dc22314958a3998a Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Thu, 26 Oct 2017 16:54:19 +0200 Subject: [PATCH 22/43] server: correctly return and close ch from exits routine Signed-off-by: Antonio Murdaca --- server/server.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/server.go b/server/server.go index 637ab860..6aafd0c9 100644 --- a/server/server.go +++ b/server/server.go @@ -418,6 +418,7 @@ func (s *Server) StartExitMonitor() { }() if err := watcher.Add(s.config.ContainerExitsDir); err != nil { logrus.Errorf("watcher.Add(%q) failed: %s", s.config.ContainerExitsDir, err) + close(done) } <-done } From 73b65438646819b55718c736b6ee7a4070e626c1 Mon Sep 17 00:00:00 2001 From: Lokesh Mandvekar Date: Fri, 27 Oct 2017 10:42:00 -0400 Subject: [PATCH 23/43] systemd: expand limits for tests Borrowed from: https://github.com/projectatomic/atomic-system-containers/pull/136 From: Antonio Murdaca Signed-off-by: Lokesh Mandvekar --- contrib/systemd/crio.service | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/systemd/crio.service b/contrib/systemd/crio.service index 70a3d26b..35d6d427 100644 --- a/contrib/systemd/crio.service +++ b/contrib/systemd/crio.service @@ -12,7 +12,7 @@ ExecStart=/usr/local/bin/crio \ $CRIO_STORAGE_OPTIONS \ $CRIO_NETWORK_OPTIONS ExecReload=/bin/kill -s HUP $MAINPID -TasksMax=8192 +TasksMax=infinity LimitNOFILE=1048576 LimitNPROC=1048576 LimitCORE=infinity From 085bdf8ff59ccb1e6ce244c2b4829c471e60637c Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Sat, 28 Oct 2017 23:43:20 +0200 Subject: [PATCH 24/43] container_create: sort mounts before adding them to the spec Signed-off-by: Antonio Murdaca --- server/container_create.go | 106 +++++++++++++----- test/crio_secrets.bats | 34 ------ test/default_mounts.bats | 69 ++++++++++++ .../container_redis_default_mounts.json | 67 +++++++++++ 4 files changed, 213 insertions(+), 63 deletions(-) delete mode 100644 test/crio_secrets.bats create mode 100644 test/default_mounts.bats create mode 100644 test/testdata/container_redis_default_mounts.json diff --git a/server/container_create.go b/server/container_create.go index d1da4cf1..04f7c2ea 100644 --- a/server/container_create.go +++ b/server/container_create.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "regexp" + "sort" "strconv" "strings" "time" @@ -46,29 +47,54 @@ const ( defaultSystemdParent = "system.slice" ) -func addOCIBindMounts(mountLabel string, containerConfig *pb.ContainerConfig, specgen *generate.Generator) ([]oci.ContainerVolume, error) { +type orderedMounts []rspec.Mount + +// Len returns the number of mounts. Used in sorting. +func (m orderedMounts) Len() int { + return len(m) +} + +// Less returns true if the number of parts (a/b/c would be 3 parts) in the +// mount indexed by parameter 1 is less than that of the mount indexed by +// parameter 2. Used in sorting. +func (m orderedMounts) Less(i, j int) bool { + return m.parts(i) < m.parts(j) +} + +// Swap swaps two items in an array of mounts. Used in sorting +func (m orderedMounts) Swap(i, j int) { + m[i], m[j] = m[j], m[i] +} + +// parts returns the number of parts in the destination of a mount. Used in sorting. +func (m orderedMounts) parts(i int) int { + return strings.Count(filepath.Clean(m[i].Destination), string(os.PathSeparator)) +} + +func addOCIBindMounts(mountLabel string, containerConfig *pb.ContainerConfig, specgen *generate.Generator) ([]oci.ContainerVolume, []rspec.Mount, error) { volumes := []oci.ContainerVolume{} + ociMounts := []rspec.Mount{} mounts := containerConfig.GetMounts() for _, mount := range mounts { dest := mount.ContainerPath if dest == "" { - return nil, fmt.Errorf("Mount.ContainerPath is empty") + return nil, nil, fmt.Errorf("Mount.ContainerPath is empty") } src := mount.HostPath if src == "" { - return nil, fmt.Errorf("Mount.HostPath is empty") + return nil, nil, fmt.Errorf("Mount.HostPath is empty") } if _, err := os.Stat(src); err != nil && os.IsNotExist(err) { if err1 := os.MkdirAll(src, 0644); err1 != nil { - return nil, fmt.Errorf("Failed to mkdir %s: %s", src, err) + return nil, nil, fmt.Errorf("Failed to mkdir %s: %s", src, err) } } src, err := resolveSymbolicLink(src) if err != nil { - return nil, fmt.Errorf("failed to resolve symlink %q: %v", src, err) + return nil, nil, fmt.Errorf("failed to resolve symlink %q: %v", src, err) } options := []string{"rw"} @@ -80,7 +106,7 @@ func addOCIBindMounts(mountLabel string, containerConfig *pb.ContainerConfig, sp if mount.SelinuxRelabel { // Need a way in kubernetes to determine if the volume is shared or private if err := label.Relabel(src, mountLabel, true); err != nil && err != unix.ENOTSUP { - return nil, fmt.Errorf("relabel failed %s: %v", src, err) + return nil, nil, fmt.Errorf("relabel failed %s: %v", src, err) } } @@ -90,45 +116,55 @@ func addOCIBindMounts(mountLabel string, containerConfig *pb.ContainerConfig, sp Readonly: mount.Readonly, }) - specgen.AddBindMount(src, dest, options) + ociMounts = append(ociMounts, rspec.Mount{ + Source: src, + Destination: dest, + Options: options, + }) } - return volumes, nil + return volumes, ociMounts, nil } -func addImageVolumes(rootfs string, s *Server, containerInfo *storage.ContainerInfo, specgen *generate.Generator, mountLabel string) error { +func addImageVolumes(rootfs string, s *Server, containerInfo *storage.ContainerInfo, specgen *generate.Generator, mountLabel string) ([]rspec.Mount, error) { + mounts := []rspec.Mount{} for dest := range containerInfo.Config.Config.Volumes { fp, err := symlink.FollowSymlinkInScope(filepath.Join(rootfs, dest), rootfs) if err != nil { - return err + return nil, err } switch s.config.ImageVolumes { case libkpod.ImageVolumesMkdir: if err1 := os.MkdirAll(fp, 0644); err1 != nil { - return err1 + return nil, err1 } case libkpod.ImageVolumesBind: volumeDirName := stringid.GenerateNonCryptoID() src := filepath.Join(containerInfo.RunDir, "mounts", volumeDirName) if err1 := os.MkdirAll(src, 0644); err1 != nil { - return err1 + return nil, err1 } // Label the source with the sandbox selinux mount label if mountLabel != "" { if err1 := label.Relabel(src, mountLabel, true); err1 != nil && err1 != unix.ENOTSUP { - return fmt.Errorf("relabel failed %s: %v", src, err1) + return nil, fmt.Errorf("relabel failed %s: %v", src, err1) } } logrus.Debugf("Adding bind mounted volume: %s to %s", src, dest) - specgen.AddBindMount(src, dest, []string{"rw"}) + mounts = append(mounts, rspec.Mount{ + Source: src, + Destination: dest, + Options: []string{"rw"}, + }) + case libkpod.ImageVolumesIgnore: logrus.Debugf("Ignoring volume %v", dest) default: logrus.Fatalf("Unrecognized image volumes setting") } } - return nil + return mounts, nil } // resolveSymbolicLink resolves a possbile symlink path. If the path is a symlink, returns resolved @@ -385,16 +421,13 @@ func ensureSaneLogPath(logPath string) error { } // addSecretsBindMounts mounts user defined secrets to the container -func addSecretsBindMounts(mountLabel, ctrRunDir string, defaultMounts []string, specgen generate.Generator) error { +func addSecretsBindMounts(mountLabel, ctrRunDir string, defaultMounts []string, specgen generate.Generator) ([]rspec.Mount, error) { containerMounts := specgen.Spec().Mounts mounts, err := secretMounts(defaultMounts, mountLabel, ctrRunDir, containerMounts) if err != nil { - return err + return nil, err } - for _, m := range mounts { - specgen.AddBindMount(m.Source, m.Destination, nil) - } - return nil + return mounts, nil } // CreateContainer creates a new container in specified PodSandbox @@ -562,7 +595,7 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string, } } - containerVolumes, err := addOCIBindMounts(mountLabel, containerConfig, &specgen) + containerVolumes, ociMounts, err := addOCIBindMounts(mountLabel, containerConfig, &specgen) if err != nil { return nil, err } @@ -934,12 +967,6 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string, return nil, err } - if len(s.config.DefaultMounts) > 0 { - if err = addSecretsBindMounts(mountLabel, containerInfo.RunDir, s.config.DefaultMounts, specgen); err != nil { - return nil, fmt.Errorf("failed to mount secrets: %v", err) - } - } - mountPoint, err := s.StorageRuntimeServer().StartContainer(containerID) if err != nil { return nil, fmt.Errorf("failed to mount container %s(%s): %v", containerName, containerID, err) @@ -957,7 +984,8 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string, } // Add image volumes - if err := addImageVolumes(mountPoint, s, &containerInfo, &specgen, mountLabel); err != nil { + volumeMounts, err := addImageVolumes(mountPoint, s, &containerInfo, &specgen, mountLabel) + if err != nil { return nil, err } @@ -1008,6 +1036,26 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string, } specgen.SetProcessCwd(containerCwd) + var secretMounts []rspec.Mount + if len(s.config.DefaultMounts) > 0 { + var err error + secretMounts, err = addSecretsBindMounts(mountLabel, containerInfo.RunDir, s.config.DefaultMounts, specgen) + if err != nil { + return nil, fmt.Errorf("failed to mount secrets: %v", err) + } + } + + mounts := []rspec.Mount{} + mounts = append(mounts, ociMounts...) + mounts = append(mounts, volumeMounts...) + mounts = append(mounts, secretMounts...) + + sort.Sort(orderedMounts(mounts)) + + for _, m := range mounts { + specgen.AddBindMount(m.Source, m.Destination, m.Options) + } + if err := s.setupOCIHooks(&specgen, sb, containerConfig, processArgs[0]); err != nil { return nil, err } diff --git a/test/crio_secrets.bats b/test/crio_secrets.bats deleted file mode 100644 index 2f36d18d..00000000 --- a/test/crio_secrets.bats +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bats - -load helpers - -IMAGE="redis:alpine" - -function teardown() { - cleanup_test -} - -@test "bind secrets mounts to container" { - start_crio - run crioctl pod run --config "$TESTDATA"/sandbox_config.json - echo "$output" - [ "$status" -eq 0 ] - pod_id="$output" - run crioctl image pull "$IMAGE" - [ "$status" -eq 0 ] - run crioctl ctr create --config "$TESTDATA"/container_redis.json --pod "$pod_id" - echo "$output" - [ "$status" -eq 0 ] - ctr_id="$output" - run crioctl ctr execsync --id "$ctr_id" mount - echo "$output" - [ "$status" -eq 0 ] - mount_info="$output" - grep /container/path1 <<< "$mount_info" - echo "$output" - [ "$status" -eq 0 ] - rm -rf MOUNT_PATH - cleanup_ctrs - cleanup_pods - stop_crio -} diff --git a/test/default_mounts.bats b/test/default_mounts.bats new file mode 100644 index 00000000..8e727085 --- /dev/null +++ b/test/default_mounts.bats @@ -0,0 +1,69 @@ +#!/usr/bin/env bats + +load helpers + +IMAGE="redis:alpine" + +function teardown() { + cleanup_test +} + +@test "bind secrets mounts to container" { + start_crio + run crioctl pod run --config "$TESTDATA"/sandbox_config.json + echo "$output" + [ "$status" -eq 0 ] + pod_id="$output" + run crioctl image pull "$IMAGE" + [ "$status" -eq 0 ] + run crioctl ctr create --config "$TESTDATA"/container_redis.json --pod "$pod_id" + echo "$output" + [ "$status" -eq 0 ] + ctr_id="$output" + run crioctl ctr execsync --id "$ctr_id" cat /proc/mounts + echo "$output" + [ "$status" -eq 0 ] + mount_info="$output" + run grep /container/path1 <<< "$mount_info" + echo "$output" + [ "$status" -eq 0 ] + cleanup_ctrs + cleanup_pods + stop_crio +} + +@test "default mounts correctly sorted with other mounts" { + start_crio + run crioctl pod run --config "$TESTDATA"/sandbox_config.json + echo "$output" + [ "$status" -eq 0 ] + pod_id="$output" + run crioctl image pull "$IMAGE" + [ "$status" -eq 0 ] + host_path="$TESTDIR"/clash + mkdir "$host_path" + echo "clashing..." > "$host_path"/clashing.txt + sed -e "s,%HPATH%,$host_path,g" "$TESTDATA"/container_redis_default_mounts.json > "$TESTDIR"/defmounts_pre.json + sed -e 's,%CPATH%,\/container\/path1\/clash,g' "$TESTDIR"/defmounts_pre.json > "$TESTDIR"/defmounts.json + run crioctl ctr create --config "$TESTDIR"/defmounts.json --pod "$pod_id" + echo "$output" + [ "$status" -eq 0 ] + ctr_id="$output" + run crioctl ctr execsync --id "$ctr_id" ls -la /container/path1/clash + echo "$output" + [ "$status" -eq 0 ] + run crioctl ctr execsync --id "$ctr_id" cat /container/path1/clash/clashing.txt + echo "$output" + [ "$status" -eq 0 ] + [[ "$output" =~ "clashing..." ]] + run crioctl ctr execsync --id "$ctr_id" ls -la /container/path1 + echo "$output" + [ "$status" -eq 0 ] + run crioctl ctr execsync --id "$ctr_id" cat /container/path1/test.txt + echo "$output" + [ "$status" -eq 0 ] + [[ "$output" =~ "Testing secrets mounts!" ]] + cleanup_ctrs + cleanup_pods + stop_crio +} diff --git a/test/testdata/container_redis_default_mounts.json b/test/testdata/container_redis_default_mounts.json new file mode 100644 index 00000000..dff3db5a --- /dev/null +++ b/test/testdata/container_redis_default_mounts.json @@ -0,0 +1,67 @@ +{ + "metadata": { + "name": "podsandbox1-redis" + }, + "image": { + "image": "redis:alpine" + }, + "args": [ + "docker-entrypoint.sh", + "redis-server" + ], + "mounts": [ + { + "container_path": "%CPATH%", + "host_path": "%HPATH%" + } + ], + "working_dir": "/data", + "envs": [ + { + "key": "PATH", + "value": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + }, + { + "key": "TERM", + "value": "xterm" + }, + { + "key": "REDIS_VERSION", + "value": "3.2.3" + }, + { + "key": "REDIS_DOWNLOAD_URL", + "value": "http://download.redis.io/releases/redis-3.2.3.tar.gz" + }, + { + "key": "REDIS_DOWNLOAD_SHA1", + "value": "92d6d93ef2efc91e595c8bf578bf72baff397507" + } + ], + "labels": { + "tier": "backend" + }, + "annotations": { + "pod": "podsandbox1" + }, + "readonly_rootfs": false, + "log_path": "", + "stdin": false, + "stdin_once": false, + "tty": false, + "linux": { + "resources": { + "cpu_period": 10000, + "cpu_quota": 20000, + "cpu_shares": 512, + "oom_score_adj": 30 + }, + "security_context": { + "capabilities": { + "add_capabilities": [ + "sys_admin" + ] + } + } + } +} From 6a912eb2d3d3d287ef3825833344c0a4acc447a5 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Sun, 29 Oct 2017 21:56:54 +0100 Subject: [PATCH 25/43] cmd: crio: set ReadTimeout on the info endpoint This will avoid the goroutines leak we've been seeing during performance tests. Goroutines count returns to normal after containers cleanup. Signed-off-by: Antonio Murdaca --- cmd/crio/main.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/crio/main.go b/cmd/crio/main.go index 95289956..f7856c7f 100644 --- a/cmd/crio/main.go +++ b/cmd/crio/main.go @@ -10,6 +10,7 @@ import ( "os/signal" "sort" "strings" + "time" "github.com/containers/storage/pkg/reexec" "github.com/kubernetes-incubator/cri-o/libkpod" @@ -470,7 +471,8 @@ func main() { infoMux := service.GetInfoMux() srv := &http.Server{ - Handler: infoMux, + Handler: infoMux, + ReadTimeout: 5 * time.Second, } graceful := false From 266e1c10ec8c2734a902ac1a9ab7a20b130fcb03 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Thu, 19 Oct 2017 17:30:50 +0000 Subject: [PATCH 26/43] Change buildtags based on installed environment. Determine if selinux is available before building cri-o with support. Don't add ostree support to crio or any tools other then kpod. cri-o does not use ostree. Signed-off-by: Daniel J Walsh --- Makefile | 13 +++++++------ hack/ostree_tag.sh | 4 ++++ hack/selinux_tag.sh | 4 ++++ 3 files changed, 15 insertions(+), 6 deletions(-) create mode 100755 hack/ostree_tag.sh create mode 100755 hack/selinux_tag.sh diff --git a/Makefile b/Makefile index 27bd3725..0a920297 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,8 @@ LIBEXECDIR ?= ${PREFIX}/libexec MANDIR ?= ${PREFIX}/share/man ETCDIR ?= ${DESTDIR}/etc ETCDIR_CRIO ?= ${ETCDIR}/crio -BUILDTAGS ?= selinux seccomp $(shell hack/btrfs_tag.sh) $(shell hack/libdm_tag.sh) $(shell hack/btrfs_installed_tag.sh) +BUILDTAGS ?= seccomp $(shell hack/btrfs_tag.sh) $(shell hack/libdm_tag.sh) $(shell hack/btrfs_installed_tag.sh) $(shell hack/ostree_tag.sh) $(shell hack/selinux_tag.sh) + BASHINSTALLDIR=${PREFIX}/share/bash-completion/completions OCIUMOUNTINSTALLDIR=$(PREFIX)/share/oci-umount/oci-umount.d @@ -72,19 +73,19 @@ pause: $(MAKE) -C $@ test/bin2img/bin2img: .gopathok $(wildcard test/bin2img/*.go) - $(GO) build $(LDFLAGS) -tags "$(BUILDTAGS)" -o $@ $(PROJECT)/test/bin2img + $(GO) build $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/test/bin2img test/copyimg/copyimg: .gopathok $(wildcard test/copyimg/*.go) - $(GO) build $(LDFLAGS) -tags "$(BUILDTAGS)" -o $@ $(PROJECT)/test/copyimg + $(GO) build $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/test/copyimg test/checkseccomp/checkseccomp: .gopathok $(wildcard test/checkseccomp/*.go) - $(GO) build $(LDFLAGS) -tags "$(BUILDTAGS)" -o $@ $(PROJECT)/test/checkseccomp + $(GO) build $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/test/checkseccomp crio: .gopathok $(shell hack/find-godeps.sh $(GOPKGDIR) cmd/crio $(PROJECT)) - $(GO) build $(LDFLAGS) -tags "$(BUILDTAGS)" -o $@ $(PROJECT)/cmd/crio + $(GO) build $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/cmd/crio crioctl: .gopathok $(shell hack/find-godeps.sh $(GOPKGDIR) cmd/crioctl $(PROJECT)) - $(GO) build $(LDFLAGS) -tags "$(BUILDTAGS)" -o $@ $(PROJECT)/cmd/crioctl + $(GO) build $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/cmd/crioctl kpod: .gopathok $(shell hack/find-godeps.sh $(GOPKGDIR) cmd/kpod $(PROJECT)) $(GO) build $(LDFLAGS) -tags "$(BUILDTAGS)" -o $@ $(PROJECT)/cmd/kpod diff --git a/hack/ostree_tag.sh b/hack/ostree_tag.sh new file mode 100755 index 00000000..89499c5e --- /dev/null +++ b/hack/ostree_tag.sh @@ -0,0 +1,4 @@ +#!/bin/bash +if ! pkg-config ostree-1 2> /dev/null ; then + echo containers_image_ostree_stub +fi diff --git a/hack/selinux_tag.sh b/hack/selinux_tag.sh new file mode 100755 index 00000000..ff80fda0 --- /dev/null +++ b/hack/selinux_tag.sh @@ -0,0 +1,4 @@ +#!/bin/bash +if pkg-config libselinux 2> /dev/null ; then + echo selinux +fi From fc2457a3f00d47421fe7d960acaaa96848c569b8 Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Thu, 19 Oct 2017 17:32:41 +0000 Subject: [PATCH 27/43] Strip out debuginfo and other content to make images smaller This can be overriden by passing in the make SHRINKFLAGS= Signed-off-by: Daniel J Walsh --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 0a920297..a7f86f89 100644 --- a/Makefile +++ b/Makefile @@ -35,8 +35,8 @@ GOPKGBASEDIR := $(shell dirname "$(GOPKGDIR)") # Update VPATH so make finds .gopathok VPATH := $(VPATH):$(GOPATH) - -LDFLAGS := -ldflags '-X main.gitCommit=${GIT_COMMIT} -X main.buildInfo=${BUILD_INFO} -X main.kpodVersion=${KPOD_VERSION}' +SHRINKFLAGS := -s -w +LDFLAGS := -ldflags '${SHRINKFLAGS} -X main.gitCommit=${GIT_COMMIT} -X main.buildInfo=${BUILD_INFO} -X main.kpodVersion=${KPOD_VERSION}' all: binaries crio.conf docs From e4f8dff2309257082eeed00e07d56d6bb542f421 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Mon, 30 Oct 2017 15:12:40 +0100 Subject: [PATCH 28/43] *: remove VERSION file Signed-off-by: Antonio Murdaca --- VERSION | 1 - 1 file changed, 1 deletion(-) delete mode 100644 VERSION diff --git a/VERSION b/VERSION deleted file mode 100644 index 3eefcb9d..00000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -1.0.0 From 748bc46605589d6021f0f15b3b44284cccd986ca Mon Sep 17 00:00:00 2001 From: Mrunal Patel Date: Mon, 30 Oct 2017 09:40:10 -0700 Subject: [PATCH 29/43] Release v1.0.2 Signed-off-by: Mrunal Patel --- version/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version/version.go b/version/version.go index 8f0928d3..dafd07b6 100644 --- a/version/version.go +++ b/version/version.go @@ -1,4 +1,4 @@ package version // Version is the version of the build. -const Version = "1.0.2-dev" +const Version = "1.0.2" From 532ed47d03c95b62e4b7e4e94ed9647f9faa3ab8 Mon Sep 17 00:00:00 2001 From: Mrunal Patel Date: Mon, 30 Oct 2017 09:40:35 -0700 Subject: [PATCH 30/43] Bump up to 1.0.3-dev Signed-off-by: Mrunal Patel --- version/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version/version.go b/version/version.go index dafd07b6..da50737c 100644 --- a/version/version.go +++ b/version/version.go @@ -1,4 +1,4 @@ package version // Version is the version of the build. -const Version = "1.0.2" +const Version = "1.0.3-dev" From 6dcf27f8cb4f1d44feec1fd9fd7bbc22b8505eb8 Mon Sep 17 00:00:00 2001 From: Ed Santiago Date: Tue, 17 Oct 2017 10:07:35 -0600 Subject: [PATCH 31/43] Issue #1024: don't chmod a nonexistent file New network test makes improper assumptions about conmon path. Use predefined CONMON_BINARY variable instead. Signed-off-by: Ed Santiago --- test/network.bats | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/network.bats b/test/network.bats index d9d0304b..294ef217 100644 --- a/test/network.bats +++ b/test/network.bats @@ -171,11 +171,11 @@ function teardown() { # make conmon non-executable to cause the sandbox setup to fail after # networking has been configured - chmod 0644 /go/src/github.com/kubernetes-incubator/cri-o/conmon/conmon + chmod 0644 $CONMON_BINARY run crioctl pod run --config "$TESTDATA"/sandbox_config.json + chmod 0755 $CONMON_BINARY echo "$output" [ "$status" -ne 0 ] - chmod 0755 /go/src/github.com/kubernetes-incubator/cri-o/conmon/conmon # ensure that the server cleaned up sandbox networking if the sandbox # failed after network setup From e78c44fe25a0cbc67ae0fa81ed4403df6298aa58 Mon Sep 17 00:00:00 2001 From: Ed Santiago Date: Tue, 17 Oct 2017 11:00:26 -0600 Subject: [PATCH 32/43] fixup! Restore conmon permissions in teardown() Signed-off-by: Ed Santiago --- test/network.bats | 1 + 1 file changed, 1 insertion(+) diff --git a/test/network.bats b/test/network.bats index 294ef217..dc8143c2 100644 --- a/test/network.bats +++ b/test/network.bats @@ -7,6 +7,7 @@ function teardown() { cleanup_pods stop_crio rm -f /var/lib/cni/networks/crionet_test_args/* + chmod 0755 $CONMON_BINARY cleanup_test } From f8e583fca133ad1c5dcead9d03720d34db142a61 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Mon, 30 Oct 2017 11:59:17 +0100 Subject: [PATCH 33/43] Makefile: output binaries under bin/ Signed-off-by: Antonio Murdaca --- .gitignore | 6 +----- Makefile | 32 ++++++++++++++++++-------------- conmon/Makefile | 4 ++-- pause/Makefile | 6 +++--- test/helpers.bash | 10 +++++----- 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/.gitignore b/.gitignore index f9c8e7d9..ee43c42d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,13 @@ /.artifacts/ /_output/ -/conmon/conmon /conmon/conmon.o /docs/*.[158] /docs/*.[158].gz -/kpod -/crioctl -/crio /crio.conf *.o *.orig -/pause/pause /pause/pause.o +/bin/ /test/bin2img/bin2img /test/checkseccomp/checkseccomp /test/copyimg/copyimg diff --git a/Makefile b/Makefile index a7f86f89..24bf0e2b 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,10 @@ GOPKGBASEDIR := $(shell dirname "$(GOPKGDIR)") # Update VPATH so make finds .gopathok VPATH := $(VPATH):$(GOPATH) SHRINKFLAGS := -s -w -LDFLAGS := -ldflags '${SHRINKFLAGS} -X main.gitCommit=${GIT_COMMIT} -X main.buildInfo=${BUILD_INFO} -X main.kpodVersion=${KPOD_VERSION}' +BASE_LDFLAGS := ${SHRINKFLAGS} -X main.gitCommit=${GIT_COMMIT} -X main.buildInfo=${BUILD_INFO} +KPOD_LDFLAGS := -X main.kpodVersion=${KPOD_VERSION} +LDFLAGS := -ldflags '${BASE_LDFLAGS}' +LDFLAGS_KPOD := -ldflags '${BASE_LDFLAGS} ${KPOD_LDFLAGS}' all: binaries crio.conf docs @@ -46,7 +49,7 @@ help: @echo "Usage: make " @echo @echo " * 'install' - Install binaries to system locations" - @echo " * 'binaries' - Build crio, conmon and crioctl" + @echo " * 'binaries' - Build crio, conmon, pause, crioctl and kpod" @echo " * 'integration' - Execute integration tests" @echo " * 'clean' - Clean artifacts" @echo " * 'lint' - Execute the source code linter" @@ -82,16 +85,16 @@ test/checkseccomp/checkseccomp: .gopathok $(wildcard test/checkseccomp/*.go) $(GO) build $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/test/checkseccomp crio: .gopathok $(shell hack/find-godeps.sh $(GOPKGDIR) cmd/crio $(PROJECT)) - $(GO) build $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/cmd/crio + $(GO) build $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o bin/$@ $(PROJECT)/cmd/crio crioctl: .gopathok $(shell hack/find-godeps.sh $(GOPKGDIR) cmd/crioctl $(PROJECT)) - $(GO) build $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o $@ $(PROJECT)/cmd/crioctl + $(GO) build $(LDFLAGS) -tags "$(BUILDTAGS) containers_image_ostree_stub" -o bin/$@ $(PROJECT)/cmd/crioctl kpod: .gopathok $(shell hack/find-godeps.sh $(GOPKGDIR) cmd/kpod $(PROJECT)) - $(GO) build $(LDFLAGS) -tags "$(BUILDTAGS)" -o $@ $(PROJECT)/cmd/kpod + $(GO) build $(LDFLAGS_KPOD) -tags "$(BUILDTAGS)" -o bin/$@ $(PROJECT)/cmd/kpod crio.conf: crio - ./crio --config="" config --default > crio.conf + ./bin/crio --config="" config --default > crio.conf clean: ifneq ($(GOPATH),) @@ -102,7 +105,7 @@ endif rm -fr test/testdata/redis-image find . -name \*~ -delete find . -name \#\* -delete - rm -f crioctl crio kpod + rm -f bin/crioctl bin/crio bin/kpod make -C conmon clean make -C pause clean rm -f test/bin2img/bin2img @@ -121,10 +124,11 @@ integration: crioimage testunit: $(GO) test -tags "$(BUILDTAGS)" -cover $(PACKAGES) -localintegration: clean binaries +localintegration: clean binaries test-binaries ./test/test_runner.sh ${TESTFLAGS} -binaries: crio crioctl kpod conmon pause test/bin2img/bin2img test/copyimg/copyimg test/checkseccomp/checkseccomp +binaries: crio conmon pause kpod crioctl +test-binaries: test/bin2img/bin2img test/copyimg/copyimg test/checkseccomp/checkseccomp MANPAGES_MD := $(wildcard docs/*.md) MANPAGES := $(MANPAGES_MD:%.md=%) @@ -143,11 +147,11 @@ docs: $(MANPAGES) install: .gopathok install.bin install.man install.bin: - install ${SELINUXOPT} -D -m 755 crio $(BINDIR)/crio - install ${SELINUXOPT} -D -m 755 crioctl $(BINDIR)/crioctl - install ${SELINUXOPT} -D -m 755 kpod $(BINDIR)/kpod - install ${SELINUXOPT} -D -m 755 conmon/conmon $(LIBEXECDIR)/crio/conmon - install ${SELINUXOPT} -D -m 755 pause/pause $(LIBEXECDIR)/crio/pause + install ${SELINUXOPT} -D -m 755 bin/crio $(BINDIR)/crio + install ${SELINUXOPT} -D -m 755 bin/crioctl $(BINDIR)/crioctl + install ${SELINUXOPT} -D -m 755 bin/kpod $(BINDIR)/kpod + install ${SELINUXOPT} -D -m 755 bin/conmon $(LIBEXECDIR)/crio/conmon + install ${SELINUXOPT} -D -m 755 bin/pause $(LIBEXECDIR)/crio/pause install.man: install ${SELINUXOPT} -d -m 755 $(MANDIR)/man1 diff --git a/conmon/Makefile b/conmon/Makefile index 460c1faa..b75605d9 100644 --- a/conmon/Makefile +++ b/conmon/Makefile @@ -5,8 +5,8 @@ override LIBS += $(shell pkg-config --libs glib-2.0) override CFLAGS += -std=c99 -Os -Wall -Wextra $(shell pkg-config --cflags glib-2.0) conmon: $(obj) - $(CC) -o $@ $^ $(CFLAGS) $(LIBS) + $(CC) -o ../bin/$@ $^ $(CFLAGS) $(LIBS) .PHONY: clean clean: - rm -f $(obj) conmon + rm -f $(obj) ../bin/conmon diff --git a/pause/Makefile b/pause/Makefile index da24f7fe..f0951af7 100644 --- a/pause/Makefile +++ b/pause/Makefile @@ -5,9 +5,9 @@ override LIBS += override CFLAGS += -std=c99 -Os -Wall -Wextra -static pause: $(obj) - $(CC) -o $@ $^ $(CFLAGS) $(LIBS) - strip $@ + $(CC) -o ../bin/$@ $^ $(CFLAGS) $(LIBS) + strip ../bin/$@ .PHONY: clean clean: - rm -f $(obj) pause + rm -f $(obj) ../bin/pause diff --git a/test/helpers.bash b/test/helpers.bash index 03079865..409ea74b 100644 --- a/test/helpers.bash +++ b/test/helpers.bash @@ -10,16 +10,16 @@ TESTDATA="${INTEGRATION_ROOT}/testdata" CRIO_ROOT=${CRIO_ROOT:-$(cd "$INTEGRATION_ROOT/../.."; pwd -P)} # Path of the crio binary. -CRIO_BINARY=${CRIO_BINARY:-${CRIO_ROOT}/cri-o/crio} +CRIO_BINARY=${CRIO_BINARY:-${CRIO_ROOT}/cri-o/bin/crio} # Path of the crictl binary. CRICTL_PATH=$(command -v crictl || true) CRICTL_BINARY=${CRICTL_PATH:-/usr/bin/crictl} # Path to kpod binary. -KPOD_BINARY=${KPOD_BINARY:-${CRIO_ROOT}/cri-o/kpod} +KPOD_BINARY=${KPOD_BINARY:-${CRIO_ROOT}/cri-o/bin/kpod} # Path of the conmon binary. -CONMON_BINARY=${CONMON_BINARY:-${CRIO_ROOT}/cri-o/conmon/conmon} +CONMON_BINARY=${CONMON_BINARY:-${CRIO_ROOT}/cri-o/bin/conmon} # Path of the pause binary. -PAUSE_BINARY=${PAUSE_BINARY:-${CRIO_ROOT}/cri-o/pause/pause} +PAUSE_BINARY=${PAUSE_BINARY:-${CRIO_ROOT}/cri-o/bin/pause} # Path of the default seccomp profile. SECCOMP_PROFILE=${SECCOMP_PROFILE:-${CRIO_ROOT}/cri-o/seccomp.json} # Name of the default apparmor profile. @@ -174,7 +174,7 @@ function crio() { } # DEPRECATED -OCIC_BINARY=${OCIC_BINARY:-${CRIO_ROOT}/cri-o/crioctl} +OCIC_BINARY=${OCIC_BINARY:-${CRIO_ROOT}/cri-o/bin/crioctl} # Run crioctl using the binary specified by $OCIC_BINARY. function crioctl() { "$OCIC_BINARY" --connect "$CRIO_SOCKET" "$@" From 1ac7da151be7483d4175cc0c956ee45a1543ab49 Mon Sep 17 00:00:00 2001 From: Ed Santiago Date: Tue, 17 Oct 2017 09:02:20 -0600 Subject: [PATCH 34/43] restore lost cni-plugin option Commit d5b5028c undid part of my pr#953 (cni plugin path). Restore it. Signed-off-by: Ed Santiago --- test/helpers.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/helpers.bash b/test/helpers.bash index 409ea74b..9c6d8d91 100644 --- a/test/helpers.bash +++ b/test/helpers.bash @@ -244,7 +244,7 @@ function start_crio() { "$COPYIMG_BINARY" --root "$TESTDIR/crio" $STORAGE_OPTS --runroot "$TESTDIR/crio-run" --image-name=mrunalp/image-volume-test --import-from=dir:"$ARTIFACTS_PATH"/image-volume-test-image --add-name=docker.io/library/mrunalp/image-volume-test --signature-policy="$INTEGRATION_ROOT"/policy.json "$COPYIMG_BINARY" --root "$TESTDIR/crio" $STORAGE_OPTS --runroot "$TESTDIR/crio-run" --image-name=busybox:latest --import-from=dir:"$ARTIFACTS_PATH"/busybox-image --add-name=docker.io/library/busybox:latest --signature-policy="$INTEGRATION_ROOT"/policy.json "$COPYIMG_BINARY" --root "$TESTDIR/crio" $STORAGE_OPTS --runroot "$TESTDIR/crio-run" --image-name=runcom/stderr-test:latest --import-from=dir:"$ARTIFACTS_PATH"/stderr-test --add-name=docker.io/runcom/stderr-test:latest --signature-policy="$INTEGRATION_ROOT"/policy.json - "$CRIO_BINARY" ${DEFAULT_MOUNTS_OPTS} ${HOOKS_OPTS} --conmon "$CONMON_BINARY" --listen "$CRIO_SOCKET" --cgroup-manager "$CGROUP_MANAGER" --registry "docker.io" --runtime "$RUNTIME_BINARY" --root "$TESTDIR/crio" --runroot "$TESTDIR/crio-run" $STORAGE_OPTS --seccomp-profile "$seccomp" --apparmor-profile "$apparmor" --cni-config-dir "$CRIO_CNI_CONFIG" --signature-policy "$INTEGRATION_ROOT"/policy.json --image-volumes "$IMAGE_VOLUMES" --pids-limit "$PIDS_LIMIT" --log-size-max "$LOG_SIZE_MAX_LIMIT" --config /dev/null config >$CRIO_CONFIG + "$CRIO_BINARY" ${DEFAULT_MOUNTS_OPTS} ${HOOKS_OPTS} --conmon "$CONMON_BINARY" --listen "$CRIO_SOCKET" --cgroup-manager "$CGROUP_MANAGER" --registry "docker.io" --runtime "$RUNTIME_BINARY" --root "$TESTDIR/crio" --runroot "$TESTDIR/crio-run" $STORAGE_OPTS --seccomp-profile "$seccomp" --apparmor-profile "$apparmor" --cni-config-dir "$CRIO_CNI_CONFIG" --cni-plugin-dir "$CRIO_CNI_PLUGIN" --signature-policy "$INTEGRATION_ROOT"/policy.json --image-volumes "$IMAGE_VOLUMES" --pids-limit "$PIDS_LIMIT" --log-size-max "$LOG_SIZE_MAX_LIMIT" --config /dev/null config >$CRIO_CONFIG # Prepare the CNI configuration files, we're running with non host networking by default if [[ -n "$4" ]]; then From 222216256fee67f1faf00a6bd6fd7799b408b41e Mon Sep 17 00:00:00 2001 From: Daniel J Walsh Date: Mon, 30 Oct 2017 12:12:40 -0400 Subject: [PATCH 35/43] Report error when arguments given to crio command Signed-off-by: Daniel J Walsh --- cmd/crio/main.go | 10 ++++++++++ test/command.bats | 12 ++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 test/command.bats diff --git a/cmd/crio/main.go b/cmd/crio/main.go index f7856c7f..e58adb11 100644 --- a/cmd/crio/main.go +++ b/cmd/crio/main.go @@ -409,6 +409,16 @@ func main() { }() } + args := c.Args() + if len(args) > 0 { + for _, command := range app.Commands { + if args[0] == command.Name { + break + } + } + return fmt.Errorf("command %q not supported", args[0]) + } + config := c.App.Metadata["config"].(*server.Config) if !config.SELinux { diff --git a/test/command.bats b/test/command.bats new file mode 100644 index 00000000..86e58f9d --- /dev/null +++ b/test/command.bats @@ -0,0 +1,12 @@ +#!/usr/bin/env bats + +load helpers + +@test "crio commands" { + run ${CRIO_BINARY} --config /dev/null config > /dev/null + echo "$output" + [ "$status" -eq 0 ] + run ${CRIO_BINARY} badoption > /dev/null + echo "$output" + [ "$status" -ne 0 ] +} From 430ff2b01df36ef8df5e5410e268acceabb3fa97 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Wed, 1 Nov 2017 19:43:22 +0100 Subject: [PATCH 36/43] *: add .github Signed-off-by: Antonio Murdaca --- .github/CODEOWNERS | 7 ++++ .github/ISSUE_TEMPLATE.md | 58 ++++++++++++++++++++++++++++++++ .github/PULL_REQUEST_TEMPLATE.md | 23 +++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..da3ba441 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,7 @@ +# GitHub code owners +# See https://help.github.com/articles/about-codeowners/ +# +# KEEP THIS FILE SORTED. Order is important. Last match takes precedence. + +* @mrunalp @runcom +pkg/storage/** @nalind @runcom @rhatdan diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..296f83e6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,58 @@ + + +**Description** + + + +**Steps to reproduce the issue:** +1. +2. +3. + +**Describe the results you received:** + + +**Describe the results you expected:** + + +**Additional information you deem important (e.g. issue happens only occasionally):** + +**Output of `crio --version`:** + +``` +(paste your output here) +``` + +**Additional environment details (AWS, VirtualBox, physical, etc.):** diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..d25e940f --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,23 @@ + + +**- What I did** + +**- How I did it** + +**- How to verify it** + +**- Description for the changelog** + From 7e1e21f1f4507bd21e6638c117737d4f300dc34a Mon Sep 17 00:00:00 2001 From: Mrunal Patel Date: Thu, 2 Nov 2017 10:20:45 -0700 Subject: [PATCH 37/43] Add HOSTNAME env var to container Signed-off-by: Mrunal Patel --- server/container_create.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/container_create.go b/server/container_create.go index 04f7c2ea..6501db79 100644 --- a/server/container_create.go +++ b/server/container_create.go @@ -912,7 +912,9 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string, specgen.AddBindMount("/etc/hosts", "/etc/hosts", options) } + // Set hostname and add env for hostname specgen.SetHostname(sb.Hostname()) + specgen.AddProcessEnv("HOSTNAME", sb.Hostname()) specgen.AddAnnotation(annotations.Name, containerName) specgen.AddAnnotation(annotations.ContainerID, containerID) From 894c98bde875479bb4eea6f8f2bb02d442ed1c16 Mon Sep 17 00:00:00 2001 From: Mrunal Patel Date: Thu, 2 Nov 2017 10:54:26 -0700 Subject: [PATCH 38/43] test: Add a test for HOSTNAME env Signed-off-by: Mrunal Patel --- test/ctr.bats | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/ctr.bats b/test/ctr.bats index 90f42b68..e13b4e9d 100644 --- a/test/ctr.bats +++ b/test/ctr.bats @@ -600,6 +600,31 @@ function teardown() { stop_crio } +@test "ctr hostname env" { + start_crio + run crioctl pod run --config "$TESTDATA"/sandbox_config.json + echo "$output" + [ "$status" -eq 0 ] + pod_id="$output" + run crioctl ctr create --config "$TESTDATA"/container_config.json --pod "$pod_id" + echo "$output" + [ "$status" -eq 0 ] + ctr_id="$output" + run crioctl ctr execsync --id "$ctr_id" env + echo "$output" + [ "$status" -eq 0 ] + [[ "$output" =~ "HOSTNAME" ]] + run crioctl pod stop --id "$pod_id" + echo "$output" + [ "$status" -eq 0 ] + run crioctl pod remove --id "$pod_id" + echo "$output" + [ "$status" -eq 0 ] + cleanup_ctrs + cleanup_pods + stop_crio +} + @test "ctr execsync failure" { start_crio run crioctl pod run --config "$TESTDATA"/sandbox_config.json From 06b4946e68ce2ecdec91cfe7c878d3508499f18f Mon Sep 17 00:00:00 2001 From: Mrunal Patel Date: Thu, 2 Nov 2017 14:41:51 -0700 Subject: [PATCH 39/43] travis: Take out make lint for go tip It is failing and our source can't be compatible with stable and tip at the same time. Signed-off-by: Mrunal Patel --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index be326c1a..3e1047b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -47,8 +47,6 @@ jobs: go: 1.9.x - script: - make .gitvalidation - - make gofmt - - make lint - make testunit - make docs - make From 762827be57f92978847d532344bc7d590d520268 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Fri, 3 Nov 2017 18:59:52 +0100 Subject: [PATCH 40/43] container_create: setup cwd for containers Signed-off-by: Antonio Murdaca --- server/container_create.go | 22 ++++++++++++++++++++++ test/ctr.bats | 29 +++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/server/container_create.go b/server/container_create.go index 6501db79..b576cc1e 100644 --- a/server/container_create.go +++ b/server/container_create.go @@ -1037,6 +1037,12 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string, containerCwd = runtimeCwd } specgen.SetProcessCwd(containerCwd) + if err := setupWorkingDirectory(mountPoint, mountLabel, containerCwd); err != nil { + if err1 := s.StorageRuntimeServer().StopContainer(containerID); err1 != nil { + return nil, fmt.Errorf("can't umount container after cwd error %v: %v", err, err1) + } + return nil, err + } var secretMounts []rspec.Mount if len(s.config.DefaultMounts) > 0 { @@ -1215,3 +1221,19 @@ func clearReadOnly(m *rspec.Mount) { } m.Options = opt } + +func setupWorkingDirectory(rootfs, mountLabel, containerCwd string) error { + fp, err := symlink.FollowSymlinkInScope(filepath.Join(rootfs, containerCwd), rootfs) + if err != nil { + return err + } + if err := os.MkdirAll(fp, 0755); err != nil { + return err + } + if mountLabel != "" { + if err1 := label.Relabel(fp, mountLabel, true); err1 != nil && err1 != unix.ENOTSUP { + return fmt.Errorf("relabel failed %s: %v", fp, err1) + } + } + return nil +} diff --git a/test/ctr.bats b/test/ctr.bats index e13b4e9d..fecd5860 100644 --- a/test/ctr.bats +++ b/test/ctr.bats @@ -896,3 +896,32 @@ function teardown() { cleanup_pods stop_crio } + +@test "ctr correctly setup working directory" { + start_crio + run crioctl pod run --config "$TESTDATA"/sandbox_config.json + echo "$output" + [ "$status" -eq 0 ] + pod_id="$output" + notexistcwd=$(cat "$TESTDATA"/container_config.json | python -c 'import json,sys;obj=json.load(sys.stdin);obj["working_dir"] = "/thisshouldntexistatall"; json.dump(obj, sys.stdout)') + echo "$notexistcwd" > "$TESTDIR"/container_cwd_notexist.json + run crioctl ctr create --config "$TESTDIR"/container_cwd_notexist.json --pod "$pod_id" + echo "$output" + [ "$status" -eq 0 ] + ctr_id="$output" + run crioctl ctr start --id "$ctr_id" + echo "$output" + [ "$status" -eq 0 ] + + filecwd=$(cat "$TESTDATA"/container_config.json | python -c 'import json,sys;obj=json.load(sys.stdin);obj["working_dir"] = "/etc/passwd"; obj["metadata"]["name"] = "container2"; json.dump(obj, sys.stdout)') + echo "$filecwd" > "$TESTDIR"/container_cwd_file.json + run crioctl ctr create --config "$TESTDIR"/container_cwd_file.json --pod "$pod_id" + echo "$output" + [ "$status" -ne 0 ] + ctr_id="$output" + [[ "$output" =~ "not a directory" ]] + + cleanup_ctrs + cleanup_pods + stop_crio +} From b0da62fcb09f1b66702f407bc6172217f291c636 Mon Sep 17 00:00:00 2001 From: Mrunal Patel Date: Fri, 3 Nov 2017 12:42:40 -0700 Subject: [PATCH 41/43] test: Fix partial log line parsing Signed-off-by: Mrunal Patel Signed-off-by: Antonio Murdaca --- contrib/test/integration/build/kubernetes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/test/integration/build/kubernetes.yml b/contrib/test/integration/build/kubernetes.yml index 206cba44..2af919a5 100644 --- a/contrib/test/integration/build/kubernetes.yml +++ b/contrib/test/integration/build/kubernetes.yml @@ -4,7 +4,7 @@ git: repo: "https://github.com/runcom/kubernetes.git" dest: "{{ ansible_env.GOPATH }}/src/k8s.io/kubernetes" - version: "cri-o-node-e2e-patched" + version: "cri-o-node-e2e-patched-logs" - name: install etcd command: "hack/install-etcd.sh" From b63b96722cdb30ce2911a6eb3ef9ee5ae2e00e06 Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Thu, 9 Nov 2017 11:50:34 +0100 Subject: [PATCH 42/43] vendor.conf: update vbatts/tar-split to v0.10.2 Fix CVE-2017-14992 Signed-off-by: Antonio Murdaca --- vendor.conf | 2 +- vendor/github.com/vbatts/tar-split/README.md | 3 +- .../vbatts/tar-split/tar/asm/disassemble.go | 43 ++++++++++++------- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/vendor.conf b/vendor.conf index f36fcc14..1d8a8bf2 100644 --- a/vendor.conf +++ b/vendor.conf @@ -73,7 +73,7 @@ github.com/emicklei/go-restful-swagger12 1.0.1 github.com/pkg/errors v0.8.0 github.com/godbus/dbus a389bdde4dd695d414e47b755e95e72b7826432c github.com/urfave/cli v1.20.0 -github.com/vbatts/tar-split v0.10.1 +github.com/vbatts/tar-split v0.10.2 github.com/renstrom/dedent v1.0.0 github.com/hpcloud/tail v1.0.0 gopkg.in/fsnotify.v1 v1.4.2 diff --git a/vendor/github.com/vbatts/tar-split/README.md b/vendor/github.com/vbatts/tar-split/README.md index 4c544d82..03e3ec43 100644 --- a/vendor/github.com/vbatts/tar-split/README.md +++ b/vendor/github.com/vbatts/tar-split/README.md @@ -1,6 +1,7 @@ # tar-split [![Build Status](https://travis-ci.org/vbatts/tar-split.svg?branch=master)](https://travis-ci.org/vbatts/tar-split) +[![Go Report Card](https://goreportcard.com/badge/github.com/vbatts/tar-split)](https://goreportcard.com/report/github.com/vbatts/tar-split) Pristinely disassembling a tar archive, and stashing needed raw bytes and offsets to reassemble a validating original archive. @@ -50,7 +51,7 @@ For example stored sparse files that have "holes" in them, will be read as a contiguous file, though the archive contents may be recorded in sparse format. Therefore when adding the file payload to a reassembled tar, to achieve identical output, the file payload would need be precisely re-sparsified. This -is not something I seek to fix imediately, but would rather have an alert that +is not something I seek to fix immediately, but would rather have an alert that precise reassembly is not possible. (see more http://www.gnu.org/software/tar/manual/html_node/Sparse-Formats.html) diff --git a/vendor/github.com/vbatts/tar-split/tar/asm/disassemble.go b/vendor/github.com/vbatts/tar-split/tar/asm/disassemble.go index 54ef23ae..009b3f5d 100644 --- a/vendor/github.com/vbatts/tar-split/tar/asm/disassemble.go +++ b/vendor/github.com/vbatts/tar-split/tar/asm/disassemble.go @@ -2,7 +2,6 @@ package asm import ( "io" - "io/ioutil" "github.com/vbatts/tar-split/archive/tar" "github.com/vbatts/tar-split/tar/storage" @@ -119,20 +118,34 @@ func NewInputTarStream(r io.Reader, p storage.Packer, fp storage.FilePutter) (io } } - // it is allowable, and not uncommon that there is further padding on the - // end of an archive, apart from the expected 1024 null bytes. - remainder, err := ioutil.ReadAll(outputRdr) - if err != nil && err != io.EOF { - pW.CloseWithError(err) - return - } - _, err = p.AddEntry(storage.Entry{ - Type: storage.SegmentType, - Payload: remainder, - }) - if err != nil { - pW.CloseWithError(err) - return + // It is allowable, and not uncommon that there is further padding on + // the end of an archive, apart from the expected 1024 null bytes. We + // do this in chunks rather than in one go to avoid cases where a + // maliciously crafted tar file tries to trick us into reading many GBs + // into memory. + const paddingChunkSize = 1024 * 1024 + var paddingChunk [paddingChunkSize]byte + for { + var isEOF bool + n, err := outputRdr.Read(paddingChunk[:]) + if err != nil { + if err != io.EOF { + pW.CloseWithError(err) + return + } + isEOF = true + } + _, err = p.AddEntry(storage.Entry{ + Type: storage.SegmentType, + Payload: paddingChunk[:n], + }) + if err != nil { + pW.CloseWithError(err) + return + } + if isEOF { + break + } } pW.Close() }() From 17bcfb495caf9454911c8b8e28134194ce947d5a Mon Sep 17 00:00:00 2001 From: Antonio Murdaca Date: Thu, 9 Nov 2017 11:59:18 +0100 Subject: [PATCH 43/43] version: bump v1.0.3 Signed-off-by: Antonio Murdaca --- version/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version/version.go b/version/version.go index da50737c..fb6cf040 100644 --- a/version/version.go +++ b/version/version.go @@ -1,4 +1,4 @@ package version // Version is the version of the build. -const Version = "1.0.3-dev" +const Version = "1.0.3"