diff options
-rw-r--r-- | .cirrus.yml | 35 | ||||
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | cmd/podman/shared/create.go | 24 | ||||
-rw-r--r-- | contrib/cirrus/lib.sh | 1 | ||||
-rwxr-xr-x | contrib/cirrus/notice_branch_failure.sh (renamed from contrib/cirrus/notice_master_failure.sh) | 4 | ||||
-rw-r--r-- | dependencies/analyses/README.md | 88 | ||||
-rwxr-xr-x | dependencies/analyses/dependency-tree.sh | 17 | ||||
-rwxr-xr-x | dependencies/analyses/go-archive-analysis.sh | 12 | ||||
-rwxr-xr-x | dependencies/analyses/nm-symbols-analysis.sh | 9 | ||||
-rw-r--r-- | libpod/healthcheck.go | 23 |
10 files changed, 193 insertions, 25 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index e9e843be6..04d535356 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -13,6 +13,8 @@ env: #### #### Global variables used for all tasks #### + # Name of the ultimate destination branch for this build + DEST_BRANCH: "master" # Overrides default location (/tmp/cirrus) for repo clone GOPATH: "/var/tmp/go" GOSRC: "/var/tmp/go/src/github.com/containers/libpod" @@ -118,7 +120,7 @@ gating_task: pipefail_enabledscript: 'if /bin/false | /bin/true; then echo "pipefail fault" && exit 72; fi' on_failure: - failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh' + failed_branch_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_branch_failure.sh' # This task runs `make vendor` followed by ./hack/tree_status.sh to check @@ -150,7 +152,7 @@ vendor_task: - 'cd ${GOSRC} && ./hack/tree_status.sh |& ${TIMESTAMP}' on_failure: - failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh |& ${TIMESTAMP}' + failed_branch_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_branch_failure.sh |& ${TIMESTAMP}' # This task runs `make varlink_api_generate` followed by ./hack/tree_status.sh to check @@ -182,7 +184,7 @@ varlink_api_task: - 'cd ${GOSRC} && ./hack/tree_status.sh |& ${TIMESTAMP}' on_failure: - failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh' + failed_branch_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_branch_failure.sh' build_each_commit_task: @@ -193,7 +195,7 @@ build_each_commit_task: - "varlink_api" # $CIRRUS_BASE_BRANCH is only set when testing a PR - only_if: $CIRRUS_BRANCH != 'master' && + only_if: $CIRRUS_BRANCH != $DEST_BRANCH && $CIRRUS_CHANGE_MESSAGE !=~ '.*\*\*\*\s*CIRRUS:\s*TEST\s*IMAGES\s*\*\*\*.*' gce_instance: @@ -213,7 +215,7 @@ build_each_commit_task: - 'make build-all-new-commits GIT_BASE_BRANCH=origin/$CIRRUS_BASE_BRANCH |& ${TIMESTAMP}' on_failure: - failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh' + failed_branch_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_branch_failure.sh' build_without_cgo_task: @@ -224,7 +226,7 @@ build_without_cgo_task: - "varlink_api" # $CIRRUS_BASE_BRANCH is only set when testing a PR - only_if: $CIRRUS_BRANCH != 'master' && + only_if: $CIRRUS_BRANCH != $DEST_BRANCH && $CIRRUS_CHANGE_MESSAGE !=~ '.*\*\*\*\s*CIRRUS:\s*TEST\s*IMAGES\s*\*\*\*.*' gce_instance: @@ -243,7 +245,7 @@ build_without_cgo_task: - 'make build-no-cgo' on_failure: - failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh' + failed_branch_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_branch_failure.sh' # Update metadata on VM images referenced by this repository state @@ -316,7 +318,7 @@ testing_task: $SCRIPT_BASE/cache_release_archive.sh |& ${TIMESTAMP} on_failure: - failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh' + failed_branch_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_branch_failure.sh' always: &standardlogs ginkgo_node_logs_script: '$SCRIPT_BASE/logcollector.sh ginkgo' @@ -351,7 +353,7 @@ special_testing_rootless_task: system_test_script: '$SCRIPT_BASE/system_test.sh |& ${TIMESTAMP}' on_failure: - failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh' + failed_branch_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_branch_failure.sh' always: <<: *standardlogs @@ -377,7 +379,7 @@ special_testing_in_podman_task: integration_test_script: '$SCRIPT_BASE/integration_test.sh |& ${TIMESTAMP}' on_failure: - failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh' + failed_branch_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_branch_failure.sh' always: <<: *standardlogs @@ -403,14 +405,14 @@ special_testing_cross_task: cache_release_archive_script: '$SCRIPT_BASE/cache_release_archive.sh |& ${TIMESTAMP}' on_failure: - failed_master_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_master_failure.sh' + failed_branch_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_branch_failure.sh' # Test building of new cache-images for future PR testing, in this PR. test_build_cache_images_task: only_if: >- - $CIRRUS_BRANCH != 'master' && + $CIRRUS_BRANCH != $DEST_BRANCH && $CIRRUS_CHANGE_MESSAGE =~ '.*\*\*\*\s*CIRRUS:\s*TEST\s*IMAGES\s*\*\*\*.*' && $CIRRUS_CHANGE_MESSAGE !=~ '.*\*\*\*\s*CIRRUS:\s*SYSTEM\s*TEST\s*\*\*\*.*' @@ -445,7 +447,7 @@ test_build_cache_images_task: verify_test_built_images_task: only_if: >- - $CIRRUS_BRANCH != 'master' && + $CIRRUS_BRANCH != $DEST_BRANCH && $CIRRUS_CHANGE_MESSAGE =~ '.*\*\*\*\s*CIRRUS:\s*TEST\s*IMAGES\s*\*\*\*.*' && $CIRRUS_CHANGE_MESSAGE !=~ '.*\*\*\*\s*CIRRUS:\s*SYSTEM\s*TEST\s*\*\*\*.*' @@ -480,7 +482,9 @@ verify_test_built_images_task: # Post message to IRC if everything passed PR testing success_task: - only_if: $CIRRUS_BRANCH != 'master' + # This task is a required-pass in github settings, + # it blocks PRs from merging if a depends_on task fails + only_if: $CIRRUS_BRANCH != $DEST_BRANCH # ignores any dependent task conditions, include everything except 'release' depends_on: &alltasks @@ -530,3 +534,6 @@ release_task: GCPROJECT: ENCRYPTED[7c80e728e046b1c76147afd156a32c1c57d4a1ac1eab93b7e68e718c61ca8564fc61fef815952b8ae0a64e7034b8fe4f] uncache_release_archives_script: '$SCRIPT_BASE/uncache_release_archives.sh |& ${TIMESTAMP}' + + on_failure: + failed_branch_script: '$CIRRUS_WORKING_DIR/$SCRIPT_BASE/notice_branch_failure.sh' @@ -20,6 +20,7 @@ SHAREDIR_CONTAINERS ?= ${PREFIX}/share/containers ETCDIR ?= /etc TMPFILESDIR ?= ${PREFIX}/lib/tmpfiles.d SYSTEMDDIR ?= ${PREFIX}/lib/systemd/system +BUILDFLAGS ?= BUILDTAGS ?= \ $(shell hack/apparmor_tag.sh) \ $(shell hack/btrfs_installed_tag.sh) \ @@ -147,10 +148,10 @@ test/goecho/goecho: .gopathok $(wildcard test/goecho/*.go) $(GO) build -ldflags '$(LDFLAGS)' -o $@ $(PROJECT)/test/goecho podman: .gopathok $(PODMAN_VARLINK_DEPENDENCIES) ## Build with podman - $(GO) build -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "$(BUILDTAGS)" -o bin/$@ $(PROJECT)/cmd/podman + $(GO) build $(BUILDFLAGS) -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "$(BUILDTAGS)" -o bin/$@ $(PROJECT)/cmd/podman podman-remote: .gopathok $(PODMAN_VARLINK_DEPENDENCIES) ## Build with podman on remote environment - $(GO) build -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "$(BUILDTAGS) remoteclient" -o bin/$@ $(PROJECT)/cmd/podman + $(GO) build $(BUILDFLAGS) -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "$(BUILDTAGS) remoteclient" -o bin/$@ $(PROJECT)/cmd/podman podman-remote-darwin: .gopathok $(PODMAN_VARLINK_DEPENDENCIES) ## Build with podman on remote OSX environment CGO_ENABLED=0 GOOS=darwin $(GO) build -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "remoteclient containers_image_openpgp exclude_graphdriver_devicemapper" -o bin/$@ $(PROJECT)/cmd/podman diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go index 17455c4c9..fd319e215 100644 --- a/cmd/podman/shared/create.go +++ b/cmd/podman/shared/create.go @@ -115,6 +115,30 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod. if err != nil { return nil, nil, errors.Wrapf(err, "unable to get healthcheck for %s", c.InputArgs[0]) } + + if healthCheck != nil { + hcCommand := healthCheck.Test + if len(hcCommand) < 1 || hcCommand[0] == "" || hcCommand[0] == "NONE" { + // disable health check + healthCheck = nil + } else { + // apply defaults if image doesn't override them + if healthCheck.Interval == 0 { + healthCheck.Interval = 30 * time.Second + } + if healthCheck.Timeout == 0 { + healthCheck.Timeout = 30 * time.Second + } + /* Docker default is 0s, so the following would be a no-op + if healthCheck.StartPeriod == 0 { + healthCheck.StartPeriod = 0 * time.Second + } + */ + if healthCheck.Retries == 0 { + healthCheck.Retries = 3 + } + } + } } } } diff --git a/contrib/cirrus/lib.sh b/contrib/cirrus/lib.sh index b2fcaa749..4acdb460a 100644 --- a/contrib/cirrus/lib.sh +++ b/contrib/cirrus/lib.sh @@ -45,6 +45,7 @@ fi # Defaults when not running under CI export CI="${CI:-false}" CIRRUS_CI="${CIRRUS_CI:-false}" +DEST_BRANCH="${DEST_BRANCH:-master}" CONTINUOUS_INTEGRATION="${CONTINUOUS_INTEGRATION:-false}" CIRRUS_REPO_NAME=${CIRRUS_REPO_NAME:-libpod} CIRRUS_BASE_SHA=${CIRRUS_BASE_SHA:-unknown$(date +%s)} # difficult to reliably discover diff --git a/contrib/cirrus/notice_master_failure.sh b/contrib/cirrus/notice_branch_failure.sh index 1fc15cdf9..423231dfd 100755 --- a/contrib/cirrus/notice_master_failure.sh +++ b/contrib/cirrus/notice_branch_failure.sh @@ -9,10 +9,10 @@ ETX="$(echo -n -e '\x03')" RED="${ETX}4" NOR="$(echo -n -e '\x0f')" -if [[ "$CIRRUS_BRANCH" =~ "master" ]] +if [[ "$CIRRUS_BRANCH" = "$DEST_BRANCH" ]] then BURL="https://cirrus-ci.com/build/$CIRRUS_BUILD_ID" - ircmsg "${RED}[Action Recommended]: ${NOR}Post-merge testing ${RED}$CIRRUS_BRANCH failed${NOR} in $CIRRUS_TASK_NAME on $(OS_RELEASE_ID)-$(OS_RELEASE_VER): $BURL. Please investigate, and re-run if appropriate." + ircmsg "${RED}[Action Recommended]: ${NOR}Post-merge testing on ${RED}$CIRRUS_BRANCH failed${NOR} in $CIRRUS_TASK_NAME on $(OS_RELEASE_ID)-$(OS_RELEASE_VER): $BURL. Please investigate, and re-run if appropriate." fi # This script assumed to be executed on failure diff --git a/dependencies/analyses/README.md b/dependencies/analyses/README.md new file mode 100644 index 000000000..a440a0ebd --- /dev/null +++ b/dependencies/analyses/README.md @@ -0,0 +1,88 @@ +# A set of scripts and instructions that help to analyze and debloat go-lang dependencies + +Note that all scripts mentioned below follow the [KISS principle](https://en.wikipedia.org/wiki/KISS_principle) on purpose. +The scripts are meant to be used in combination to aid in understanding the packages' dependencies and how they contribute to the size of the compiled binary. + +## Size of packages + +To analyze the size of all go packages used during the build process, pass the `-work -a` build flags to `go build`. +The `-a` flag forces go to rebuild all packages even if they are already up-to-date (e.g., in the build cache), while the `-work` flag instructs go to print the temporary work directory used for compiling the packages. +The path to the temporary work directory of `go-build` must be passed to `go-archive-analysis.sh` by setting it as an environment variable. +The analysis script will then read and parse the build data and print a sorted table of the package size in bytes followed by the package name. + +Running such an analysis on libpod may look as follows: + +``` +# 1) Build the podman binary with `-work -a`. +[libpod]$ BUILDFLAGS="-work -a" make podman +[...] +WORK=/tmp/go-build794287815 + +# 2) Set the work directory as an environment variable and call the analysis script +[libpod]$ WORK=/tmp/go-build794287815 ./dependencies/analyses/go-archive-analysis.sh | head -n10 +17M github.com/containers/libpod/cmd/podman/cliconfig +13M github.com/containers/libpod/vendor/github.com/DataDog/zstd +10M github.com/containers/libpod/vendor/k8s.io/api/core/v1 +3.7M net/http +3.7M github.com/containers/libpod/libpod +3.2M runtime +2.7M github.com/containers/libpod/vendor/github.com/gogo/protobuf/proto +2.5M github.com/containers/libpod/vendor/k8s.io/apimachinery/pkg/apis/meta/v1 +2.3M github.com/containers/libpod/vendor/github.com/vishvananda/netlink +2.1M github.com/containers/libpod/cmd/podman/varlink +``` + +The output of the `go-archive-analysis.sh` script is a sorted table with the size in bytes followed by the package. +The size denotes the size of the compiled package (i.e., the `.a` file). + + +## Size of symbols in binary + +Once the binary is compiled, we can run another set of analyses on it. +The `nm-symbols-analysis.sh` is a wrapper around `go tool nm` and prints a table with the size in bytes followed by the symbol's name. +To avoid information overload, the scripts prints only symbols from the text/code segment. + +Running such an analysis on libpod may look as follows: + +``` +# 1) Compile the binary +[libpod]$ make podman +[...] + +# 2) Run the script with the binary as an argument +[libpod]$ ./dependencies/analyses/nm-symbols-analysis.sh ./bin/podman | grep "containers/libpod/libpod" | head -n10 +299 github.com/containers/libpod/libpod.(*BoltState).AddContainer +658 github.com/containers/libpod/libpod.(*BoltState).AddContainerToPod +2120 github.com/containers/libpod/libpod.(*BoltState).AddPod +3773 github.com/containers/libpod/libpod.(*BoltState).AddPod.func1 +965 github.com/containers/libpod/libpod.(*BoltState).AddVolume +1651 github.com/containers/libpod/libpod.(*BoltState).AddVolume.func1 +558 github.com/containers/libpod/libpod.(*BoltState).AllContainers +282 github.com/containers/libpod/libpod.(*BoltState).AllContainers.func1 +1121 github.com/containers/libpod/libpod.(*BoltState).AllContainers.func1.1 +558 github.com/containers/libpod/libpod.(*BoltState).AllPods +``` + +Running the script can help identify sources of bloat and reveal potential candidates (e.g., entire packages, types, or function) for refactoring. + + +## Dependency Tree + +Use the `dependency-tree.sh` script to figure out which package includes which packages. +The output of the script has the format `package: dependency_1, dependency_2, ...`. +Each line is followed by a blank line to make it easier to read. +The script generates two files: + + - `direct-tree.txt` - listing direct dependencies + - `transitive-tree.txt` - listing direct and transitive dependencies + +Running such a dependency-tree analysis may look as follows: + + +``` +[libpod]$ ./dependencies/analyses/dependency-tree.sh github.com/containers/libpod +[libpod]$ grep "^github.com/containers/libpod/pkg/registries" direct-tree.txt +github.com/containers/libpod/pkg/registries: github.com/containers/libpod/vendor/github.com/containers/image/pkg/sysregistriesv2, github.com/containers/libpod/vendor/github.com/containers/image/types, github.com/containers/libpod/pkg/rootless, github.com/containers/libpod/vendor/github.com/docker/distribution/reference, github.com/containers/libpod/vendor/github.com/pkg/errors, os, path/filepath, strings +``` + +As shown above, the script's output can then be used to query for specific packages (e.g, with `grep`). diff --git a/dependencies/analyses/dependency-tree.sh b/dependencies/analyses/dependency-tree.sh new file mode 100755 index 000000000..84085a50d --- /dev/null +++ b/dependencies/analyses/dependency-tree.sh @@ -0,0 +1,17 @@ +#!/usr/bin/bash + +if test "$#" -ne 1; then + echo "invalid arguments: usage: $0 path to package" + exit 1 +fi + +go list $1/... \ + | xargs -d '\n' go list -f '{{ .ImportPath }}: {{ join .Imports ", " }}' \ + | awk '{ printf "%s\n\n", $0 }' \ + > direct-tree.tmp.$$ && mv -f direct-tree.tmp.$$ direct-tree.txt + + +go list $1/... \ + | xargs -d '\n' go list -f '{{ .ImportPath }}: {{ join .Deps ", " }}' \ + | awk '{ printf "%s\n\n", $0 }' \ + > transitive-tree.tmp.$$ && mv -f transitive-tree.tmp.$$ transitive-tree.txt diff --git a/dependencies/analyses/go-archive-analysis.sh b/dependencies/analyses/go-archive-analysis.sh new file mode 100755 index 000000000..f10145dad --- /dev/null +++ b/dependencies/analyses/go-archive-analysis.sh @@ -0,0 +1,12 @@ +#!/usr/bin/bash + +if [ -z "$WORK" ] +then + echo "WORK environment variable must be set" + exit 1 +fi + +grep --no-filename packagefile $WORK/**/importcfg \ + | awk '{ split($2, data, "="); printf "%s ", data[1]; system("du -sh " data[2]) }' \ + | awk '{ printf "%s %s\n", $2, $1 }' \ + | sort -u | sort -rh diff --git a/dependencies/analyses/nm-symbols-analysis.sh b/dependencies/analyses/nm-symbols-analysis.sh new file mode 100755 index 000000000..361b746e4 --- /dev/null +++ b/dependencies/analyses/nm-symbols-analysis.sh @@ -0,0 +1,9 @@ +#!/usr/bin/bash + +if test "$#" -ne 1; then + echo "invalid arguments: usage: $0 path/to/binary" + exit 1 +fi + +go tool nm -size "$1" \ + | awk 'NF==4 && $3=="t" {printf "%s\t\t%s\n", $2, $4}' diff --git a/libpod/healthcheck.go b/libpod/healthcheck.go index f4ea6c694..8ed2b12e1 100644 --- a/libpod/healthcheck.go +++ b/libpod/healthcheck.go @@ -107,16 +107,25 @@ func (c *Container) runHealthCheck() (HealthCheckStatus, error) { capture bytes.Buffer inStartPeriod bool ) - hcStatus, err := checkHealthCheckCanBeRun(c) - if err != nil { - return hcStatus, err - } hcCommand := c.HealthCheckConfig().Test - if len(hcCommand) > 0 && hcCommand[0] == "CMD-SHELL" { - newCommand = []string{"sh", "-c", strings.Join(hcCommand[1:], " ")} - } else { + if len(hcCommand) < 1 { + return HealthCheckNotDefined, errors.Errorf("container %s has no defined healthcheck", c.ID()) + } + switch hcCommand[0] { + case "", "NONE": + return HealthCheckNotDefined, errors.Errorf("container %s has no defined healthcheck", c.ID()) + case "CMD": + newCommand = hcCommand[1:] + case "CMD-SHELL": + // TODO: SHELL command from image not available in Container - use Docker default + newCommand = []string{"/bin/sh", "-c", strings.Join(hcCommand[1:], " ")} + default: + // command supplied on command line - pass as-is newCommand = hcCommand } + if len(newCommand) < 1 || newCommand[0] == "" { + return HealthCheckNotDefined, errors.Errorf("container %s has no defined healthcheck", c.ID()) + } captureBuffer := bufio.NewWriter(&capture) hcw := hcWriteCloser{ captureBuffer, |