diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 41d4c415..4dee7251 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -13,3 +13,7 @@ jobs: uses: ./.github/workflows/build.yaml with: slow-test: false + coverage: + uses: ./.github/workflows/coverage.yaml + with: + slow-test: false diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml new file mode 100644 index 00000000..3df6c81d --- /dev/null +++ b/.github/workflows/coverage.yaml @@ -0,0 +1,105 @@ +name: Reusable stacker build for coverage +on: + workflow_call: + inputs: + # note >-, args needs to be strings to be used as inputs + # for the reusable build.yaml workflow + go-version: + required: false + type: string + description: 'Stringified JSON object listing go versions' + default: >- + ["1.20.x"] + privilege-level: + required: false + type: string + description: 'Stringified JSON object listing stacker privilege-level' + default: >- + ["unpriv", "priv"] + build-id: + required: false + type: string + description: 'build-id' + default: "${{ github.sha }}" + slow-test: + required: false + type: boolean + description: 'Should slow tests be run?' + default: true + +jobs: + build: + runs-on: ubuntu-22.04 + services: + registry: + image: ghcr.io/project-stacker/registry:2 + ports: + - 5000:5000 + strategy: + matrix: + go-version: ${{fromJson(inputs.go-version)}} + privilege-level: ${{fromJson(inputs.privilege-level)}} + name: "golang ${{ matrix.go-version }} privilege ${{ matrix.privilege-level }}" + steps: + - uses: actions/checkout@v3 + - name: Clean disk space + uses: ./.github/actions/clean-runner + - uses: benjlevesque/short-sha@v2.1 + id: short-sha + - name: Set up golang ${{ matrix.go-version }} + uses: actions/setup-go@v3 + with: + go-version: ${{ matrix.go-version }} + - name: Setup Environment + run: | + gopath=$PWD/.build/gopath + echo "GOPATH=$gopath" >> $GITHUB_ENV + echo "GOCACHE=$gopath/gocache" >> $GITHUB_ENV + echo "PATH=$gopath/bin:$PATH" >> $GITHUB_ENV + echo "SLOW_TEST=${{inputs.slow-test}}" >> $GITHUB_ENV + echo "STACKER_DOCKER_BASE=oci:$PWD/.build/oci-clone:" >> $GITHUB_ENV + GOCOVERDIR=$(mktemp -d) + echo "GOCOVERDIR=$GOCOVERDIR" >> $GITHUB_ENV + echo "PWD=$PWD" + cat "$GITHUB_ENV" + - name: install dependencies + run: | + ./install-build-deps.sh + echo "running kernel is: $(uname -a)" + - name: docker-clone + run: | + make docker-clone "STACKER_DOCKER_BASE=docker://" CLONE_D="$PWD/.build/oci-clone" + - name: Go-download + run: | + make go-download + - name: Show disk usage before building the binaries + uses: ./.github/actions/show-disk-usage + - name: Build-level1 + run: | + make show-info + make stacker-dynamic VERSION_FULL=${{ inputs.build-id }} + - name: Show disk usage before running the tests + if: always() + uses: ./.github/actions/show-disk-usage + - name: Build and test + run: | + make check-cov GOCOVERDIR=$GOCOVERDIR PRIVILEGE_LEVEL=${{ matrix.privilege-level }} + go tool covdata textfmt -i $GOCOVERDIR -o coverage-${{ matrix.privilege-level }}.txt + go tool covdata percent -i $GOCOVERDIR + ls -altR $GOCOVERDIR + env: + REGISTRY_URL: localhost:5000 + ZOT_HOST: localhost + ZOT_PORT: 8080 + - name: Show disk usage after running the tests + if: always() + uses: ./.github/actions/show-disk-usage + - name: Upload code coverage + uses: codecov/codecov-action@v3 + with: + files: coverage-${{ matrix.privilege-level}}.txt + - uses: actions/cache@v3 + id: restore-build + with: + path: stacker + key: ${{ inputs.build-id }} diff --git a/Makefile b/Makefile index 5dd54a16..0b37b30a 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ BUILD_TAGS = exclude_graphdriver_btrfs exclude_graphdriver_devicemapper containe STACKER_OPTS=--oci-dir=$(BUILD_D)/oci --roots-dir=$(BUILD_D)/roots --stacker-dir=$(BUILD_D)/stacker --storage-type=overlay -build_stacker = go build -tags "$(BUILD_TAGS) $1" -ldflags "-X main.version=$(VERSION_FULL) -X main.lxc_version=$(LXC_VERSION) $2" -o $3 ./cmd/stacker +build_stacker = go build $1 -tags "$(BUILD_TAGS) $2" -ldflags "-X main.version=$(VERSION_FULL) -X main.lxc_version=$(LXC_VERSION) $3" -o $4 ./cmd/stacker # See doc/hacking.md for how to use a local oci or docker repository. STACKER_DOCKER_BASE?=docker://ghcr.io/project-stacker/ @@ -57,15 +57,31 @@ stacker: $(STAGE1_STACKER) $(STACKER_DEPS) cmd/stacker/lxc-wrapper/lxc-wrapper.c --substitute STACKER_BUILD_BASE_IMAGE=$(STACKER_BUILD_BASE_IMAGE) \ --substitute LXC_CLONE_URL=$(LXC_CLONE_URL) \ --substitute LXC_BRANCH=$(LXC_BRANCH) \ - --substitute VERSION_FULL=$(VERSION_FULL) + --substitute VERSION_FULL=$(VERSION_FULL) \ + --substitute WITH_COV=no + +stacker-cov: $(STAGE1_STACKER) $(STACKER_DEPS) cmd/stacker/lxc-wrapper/lxc-wrapper.c + $(STAGE1_STACKER) --debug $(STACKER_OPTS) build \ + -f build.yaml \ + --substitute BUILD_D=$(BUILD_D) \ + --substitute STACKER_BUILD_BASE_IMAGE=$(STACKER_BUILD_BASE_IMAGE) \ + --substitute LXC_CLONE_URL=$(LXC_CLONE_URL) \ + --substitute LXC_BRANCH=$(LXC_BRANCH) \ + --substitute VERSION_FULL=$(VERSION_FULL) \ + --substitute WITH_COV=yes stacker-static: $(STACKER_DEPS) cmd/stacker/lxc-wrapper/lxc-wrapper - $(call build_stacker,static_build,-extldflags '-static',stacker) + $(call build_stacker,,static_build,-extldflags '-static',stacker) + +# can't use a comma in func call args, so do this instead +, := , +stacker-static-cov: $(GO_SRC) go.mod go.sum cmd/stacker/lxc-wrapper/lxc-wrapper + $(call build_stacker,-cover -coverpkg="./pkg/...$(,)./cmd/...",static_build,-extldflags '-static',stacker) # TODO: because we clean lxc-wrapper in the nested build, this always rebuilds. # Could find a better way to do this. stacker-dynamic: $(STACKER_DEPS) cmd/stacker/lxc-wrapper/lxc-wrapper - $(call build_stacker,,,stacker-dynamic) + $(call build_stacker,,,,stacker-dynamic) cmd/stacker/lxc-wrapper/lxc-wrapper: cmd/stacker/lxc-wrapper/lxc-wrapper.c make -C cmd/stacker/lxc-wrapper LDFLAGS=-static LDLIBS="$(shell pkg-config --static --libs lxc) -lpthread -ldl" lxc-wrapper @@ -141,6 +157,21 @@ test: stacker $(REGCLIENT) $(SKOPEO) $(ZOT) $(shell [ -z $(PRIVILEGE_LEVEL) ] || echo --privilege-level=$(PRIVILEGE_LEVEL)) \ $(patsubst %,test/%.bats,$(TEST)) +.PHONY: check-cov +check-cov: lint test-cov + +.PHONY: test-cov +test-cov: stacker-cov $(REGCLIENT) $(SKOPEO) $(ZOT) + sudo -E PATH="$$PATH" \ + -E GOCOVERDIR="$$GOCOVERDIR" \ + LXC_BRANCH=$(LXC_BRANCH) \ + LXC_CLONE_URL=$(LXC_CLONE_URL) \ + STACKER_BUILD_BASE_IMAGE=$(STACKER_BUILD_BASE_IMAGE) \ + STACKER_BUILD_CENTOS_IMAGE=$(STACKER_BUILD_CENTOS_IMAGE) \ + STACKER_BUILD_UBUNTU_IMAGE=$(STACKER_BUILD_UBUNTU_IMAGE) \ + ./test/main.py \ + $(shell [ -z $(PRIVILEGE_LEVEL) ] || echo --privilege-level=$(PRIVILEGE_LEVEL)) \ + $(patsubst %,test/%.bats,$(TEST)) CLONE_D = $(BUILD_D)/oci-clone CLONE_RETRIES = 3 diff --git a/build.yaml b/build.yaml index c466b9f5..ad82e793 100644 --- a/build.yaml +++ b/build.yaml @@ -93,4 +93,8 @@ build: cd /stacker-tree make BUILD_D=/build show-info make BUILD_D=/build -C cmd/stacker/lxc-wrapper clean - make BUILD_D=/build stacker-static + if [ x${{WITH_COV}} = x"yes" ]; then + make BUILD_D=/build stacker-static-cov + else + make -C /stacker-tree stacker-static + fi diff --git a/pkg/stacker/build.go b/pkg/stacker/build.go index 2866a5cb..88b11203 100644 --- a/pkg/stacker/build.go +++ b/pkg/stacker/build.go @@ -18,6 +18,7 @@ import ( "gopkg.in/yaml.v2" "stackerbuild.io/stacker/pkg/container" "stackerbuild.io/stacker/pkg/log" + "stackerbuild.io/stacker/pkg/test" "stackerbuild.io/stacker/pkg/types" ) @@ -699,6 +700,25 @@ func SetupBuildContainerConfig(config types.StackerConfig, storage types.Storage return err } + // code coverage from inside the container + if test.IsCoverageEnabled() { + log.Infof("coverage enabled") + + if test.GetCoverageDir() != test.CoverageBindPath { + err := c.BindMount(test.GetCoverageDir(), test.CoverageBindPath, "rw") + if err != nil { + return err + } + + log.Debugf("bind mounting %s into container", test.GetCoverageDir()) + } + + err = c.SetConfig("lxc.environment", fmt.Sprintf("GOCOVERDIR=%s", test.CoverageBindPath)) + if err != nil { + return err + } + } + rootfs, err := storage.GetLXCRootfsConfig(name) if err != nil { return err diff --git a/pkg/test/cover.go b/pkg/test/cover.go new file mode 100644 index 00000000..dbf397e9 --- /dev/null +++ b/pkg/test/cover.go @@ -0,0 +1,19 @@ +package test + +import "os" + +const CoverageBindPath = "/stacker/.coverage" + +func IsCoverageEnabled() bool { + _, ok := os.LookupEnv("GOCOVERDIR") + return ok +} + +func GetCoverageDir() string { + val, ok := os.LookupEnv("GOCOVERDIR") + if ok { + return val + } + + return "" +}