Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to export/launch Windows-based images #310

Merged
merged 36 commits into from
Jul 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
951f092
Add ability to export Windows-based images
Jun 9, 2020
4f069b9
Use exec for windows sys call
ameyer-pivotal Jun 10, 2020
0bdce47
Update Makefile
ameyer-pivotal Jun 11, 2020
18c9b9f
Slight makefile cleanup
ameyer-pivotal Jun 11, 2020
ae3507e
Remove testsyscall package
ameyer-pivotal Jun 15, 2020
539f081
Revert fatal error
ameyer-pivotal Jun 15, 2020
79dc3ae
Strip 'sha256:' for Windows only
ameyer-pivotal Jun 15, 2020
3474231
Test cleanup
ameyer-pivotal Jun 15, 2020
5981f5c
Fix exporter tests
ameyer-pivotal Jun 15, 2020
d7df44e
Revert layer ID format change
ameyer-pivotal Jun 15, 2020
5c4d64f
Unexport syscallExecWithStdout
ameyer-pivotal Jun 15, 2020
4ee9527
Remove whitespace
ameyer-pivotal Jun 15, 2020
5a8a481
Fix format
ameyer-pivotal Jun 15, 2020
0c5c2f1
Fix launcher acceptance tests
ameyer-pivotal Jun 15, 2020
77d86a0
Refactor default args to be more maintainable
ameyer-pivotal Jun 16, 2020
a14a853
Remove make target for docker-run-linux
Jun 16, 2020
0386084
Remove redundant env vars when running launcher
ameyer-pivotal Jun 16, 2020
3c77d7f
Fixes restore phase on windows
aemengo Jun 16, 2020
bb8a894
Refactor make targets to run on Windows
Jun 16, 2020
c143b70
Fixes implementation of cleanImageLayerPath in archive#UntarLayer
aemengo Jun 17, 2020
19f5009
Fix package-windows to run on Linux/Windows
Jun 17, 2020
78886b6
Refactor Makefile for build-* target parity
Jun 18, 2020
ba20c05
Flesh out launcher for windows
ameyer-pivotal Jun 19, 2020
aabeb66
Refactor default vals to use 'var'
ameyer-pivotal Jun 19, 2020
2324097
Add /s to Windows shell commands
Jun 19, 2020
1af518a
Refactor Vars usage
ameyer-pivotal Jun 22, 2020
a2a8e32
Update Makefile
ameyer-pivotal Jun 23, 2020
c808922
Update tar extraction and test
ameyer-pivotal Jun 23, 2020
0880d8e
Remove sys package, merge with env
ameyer-pivotal Jun 23, 2020
e2f9687
Slight refactor of tar package
Jun 23, 2020
78787a3
Revert removal of documentation strings in tar_test.go
ameyer-pivotal Jun 29, 2020
79c6b3d
Update UntarLayer()
ameyer-pivotal Jun 29, 2020
27587be
Remove LayerWriterFactory constructor
ameyer-pivotal Jun 29, 2020
b62429e
Don't export Root and ExecExt
ekcasey Jun 29, 2020
5e2f700
Fix failing tests
ameyer-pivotal Jun 29, 2020
eb421a1
Remove test dependency on bash for Windows
ameyer-pivotal Jun 30, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
out
25 changes: 23 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,30 @@ jobs:
run: make test
- name: Build
run: |
make build
make package
make build-linux
make package-linux
- uses: actions/upload-artifact@v2
with:
name: lifecycle-linux-x86-64
path: out/lifecycle-v*+linux.x86-64.tgz
test-and-build-windows:
runs-on: windows-latest
steps:
- name: Set git to use LF and symlinks
run: |
git config --global core.autocrlf false
git config --global core.eol lf
git config --global core.symlinks true
- uses: actions/checkout@v2
- name: Test
run: |
make docker-run-windows DOCKER_CMD="make test"
shell: cmd
- name: Build
run: |
make docker-run-windows DOCKER_CMD="make build-windows package-windows"
shell: cmd
- uses: actions/upload-artifact@v2
with:
name: lifecycle-windows-x86-64
path: out/lifecycle-v*+windows.x86-64.tgz
Copy link
Member

@micahyoung micahyoung Jun 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Windows jobs ends up simpler than Linux since dependencies are in the tools/Dockerfile.windows and it's run through the make docker-run-windows target.

The tradeoff is that Windows build is slower than Linux (~7 mins vs ~4 mins resp) but I feel the consistency/simplicity is worth it. More thoughts on the Makefile#docker-run-windows target below

2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
*~
/out

acceptance/testdata/*/container/cnb/lifecycle/*
acceptance/testdata/launcher/*/container/cnb/lifecycle/launcher*
134 changes: 77 additions & 57 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
ifeq ($(OS),Windows_NT)
SHELL:=cmd.exe
PWD?=$(subst /,\,${CURDIR})
LDFLAGS=-s -w
BLANK:=
/:=\$(BLANK)
LIFECYCLE_VERSION?=$(shell type VERSION)
else
/:=/
LIFECYCLE_VERSION?=$(shell cat VERSION)
endif

GOCMD?=go
GOARCH?=amd64
GOENV=GOARCH=$(GOARCH) CGO_ENABLED=0
Expand All @@ -8,38 +20,34 @@ LDFLAGS+=-X 'github.com/buildpacks/lifecycle/cmd.SCMCommit=$(SCM_COMMIT)'
LDFLAGS+=-X 'github.com/buildpacks/lifecycle/cmd.PlatformAPI=$(PLATFORM_API)'
GOBUILD=go build $(GOFLAGS) -ldflags "$(LDFLAGS)"
GOTEST=$(GOCMD) test $(GOFLAGS)
LIFECYCLE_VERSION?=$(shell cat VERSION)
PLATFORM_API?=0.3
BUILDPACK_API?=0.2
SCM_REPO?=github.com/buildpacks/lifecycle
PARSED_COMMIT:=$(shell git rev-parse --short HEAD)
SCM_COMMIT?=$(PARSED_COMMIT)
BUILD_DIR?=$(PWD)/out
COMPILATION_IMAGE?=golang:1.13-alpine

define LIFECYCLE_DESCRIPTOR
[api]
platform = "$(PLATFORM_API)"
buildpack = "$(BUILDPACK_API)"

[lifecycle]
version = "$(LIFECYCLE_VERSION)"
endef
BUILD_DIR?=$(PWD)$/out
LINUX_COMPILATION_IMAGE?=golang:1.14-alpine
WINDOWS_COMPILATION_IMAGE?=golang:1.14-windowsservercore-1809
SOURCE_COMPILATION_IMAGE?=lifecycle-img
BUILD_CTR?=lifecycle-ctr
DOCKER_CMD?=make test

all: test build package

build: build-linux build-windows

build-linux: build-linux-lifecycle build-linux-symlinks build-linux-launcher
build-windows: build-windows-lifecycle build-windows-symlinks build-windows-launcher

build-linux-lifecycle: export GOOS:=linux
build-linux-lifecycle: OUT_DIR:=$(BUILD_DIR)/$(GOOS)/lifecycle
build-linux-lifecycle: GOENV:=GOARCH=$(GOARCH) CGO_ENABLED=1
build-linux-lifecycle: DOCKER_RUN=docker run --workdir=/lifecycle -v $(OUT_DIR):/out -v $(PWD):/lifecycle $(COMPILATION_IMAGE)
build-linux-lifecycle: DOCKER_RUN=docker run --workdir=/lifecycle -v $(OUT_DIR):/out -v $(PWD):/lifecycle $(LINUX_COMPILATION_IMAGE)
build-linux-lifecycle:
@echo "> Building lifecycle/lifecycle for linux..."
mkdir -p $(OUT_DIR)
$(DOCKER_RUN) sh -c 'apk add build-base && $(GOENV) $(GOBUILD) -o /out/lifecycle -a ./cmd/lifecycle'


build-linux-launcher: export GOOS:=linux
build-linux-launcher: OUT_DIR?=$(BUILD_DIR)/$(GOOS)/lifecycle
build-linux-launcher:
Expand All @@ -60,30 +68,45 @@ build-linux-symlinks:
ln -sf lifecycle $(OUT_DIR)/rebaser
ln -sf lifecycle $(OUT_DIR)/creator

build-linux: build-linux-lifecycle build-linux-symlinks build-linux-launcher

build-windows: export GOOS:=windows
build-windows: OUT_DIR:=$(BUILD_DIR)/$(GOOS)/lifecycle
build-windows:
@echo "> Building for windows..."
mkdir -p $(OUT_DIR)
$(GOENV) $(GOBUILD) -o $(OUT_DIR)/launcher -a ./cmd/launcher
test $$(du -m $(OUT_DIR)/launcher|cut -f 1) -le 3
$(GOENV) $(GOBUILD) -o $(OUT_DIR)/lifecycle.exe -a ./cmd/lifecycle
ln -sf lifecycle.exe $(OUT_DIR)/analyzer.exe
ln -sf lifecycle.exe $(OUT_DIR)/restorer.exe
ln -sf lifecycle.exe $(OUT_DIR)/builder.exe
ln -sf lifecycle.exe $(OUT_DIR)/exporter.exe
ln -sf lifecycle.exe $(OUT_DIR)/rebaser.exe
ln -sf lifecycle.exe $(OUT_DIR)/creator.exe
build-windows-lifecycle: export GOOS:=windows
build-windows-lifecycle: OUT_DIR?=$(BUILD_DIR)$/$(GOOS)$/lifecycle
build-windows-lifecycle:
@echo "> Building lifecycle/lifecycle for Windows..."
$(GOBUILD) -o $(OUT_DIR)$/lifecycle.exe -a ./cmd/lifecycle

build-windows-launcher: export GOOS:=windows
build-windows-launcher: OUT_DIR?=$(BUILD_DIR)$/$(GOOS)$/lifecycle
build-windows-launcher:
@echo "> Building lifecycle/launcher for Windows..."
$(GOBUILD) -o $(OUT_DIR)$/launcher.exe -a ./cmd/launcher

build-windows-symlinks: export GOOS:=windows
build-windows-symlinks: OUT_DIR?=$(BUILD_DIR)$/$(GOOS)$/lifecycle
build-windows-symlinks:
@echo "> Creating phase symlinks for Windows..."
ifeq ($(OS),Windows_NT)
call mklink $(OUT_DIR)$/detector.exe lifecycle.exe
call mklink $(OUT_DIR)$/analyzer.exe lifecycle.exe
call mklink $(OUT_DIR)$/restorer.exe lifecycle.exe
call mklink $(OUT_DIR)$/builder.exe lifecycle.exe
call mklink $(OUT_DIR)$/exporter.exe lifecycle.exe
call mklink $(OUT_DIR)$/rebaser.exe lifecycle.exe
else
ln -sf lifecycle.exe $(OUT_DIR)$/detector.exe
ln -sf lifecycle.exe $(OUT_DIR)$/analyzer.exe
ln -sf lifecycle.exe $(OUT_DIR)$/restorer.exe
ln -sf lifecycle.exe $(OUT_DIR)$/builder.exe
ln -sf lifecycle.exe $(OUT_DIR)$/exporter.exe
ln -sf lifecycle.exe $(OUT_DIR)$/rebaser.exe
endif

build-darwin: export GOOS:=darwin
build-darwin: OUT_DIR:=$(BUILD_DIR)/$(GOOS)/lifecycle
build-darwin:
@echo "> Building for macos..."
mkdir -p $(OUT_DIR)
$(GOENV) $(GOBUILD) -o $(OUT_DIR)/launcher -a ./cmd/launcher
test $$(du -m $(OUT_DIR)/launcher|cut -f 1) -le 3
test $$(du -m $(OUT_DIR)/launcher|cut -f 1) -le 4
$(GOENV) $(GOBUILD) -o $(OUT_DIR)/lifecycle -a ./cmd/lifecycle
ln -sf lifecycle $(OUT_DIR)/detector
ln -sf lifecycle $(OUT_DIR)/analyzer
Expand All @@ -94,19 +117,19 @@ build-darwin:

install-goimports:
@echo "> Installing goimports..."
cd tools; $(GOCMD) install golang.org/x/tools/cmd/goimports
cd tools && $(GOCMD) install golang.org/x/tools/cmd/goimports

install-yj:
@echo "> Installing yj..."
cd tools; $(GOCMD) install github.com/sclevine/yj
cd tools && $(GOCMD) install github.com/sclevine/yj

install-mockgen:
@echo "> Installing mockgen..."
cd tools; $(GOCMD) install github.com/golang/mock/mockgen
cd tools && $(GOCMD) install github.com/golang/mock/mockgen

install-golangci-lint:
@echo "> Installing golangci-lint..."
cd tools; $(GOCMD) install github.com/golangci/golangci-lint/cmd/golangci-lint
cd tools && $(GOCMD) install github.com/golangci/golangci-lint/cmd/golangci-lint

lint: install-golangci-lint
@echo "> Linting code..."
Expand All @@ -119,23 +142,19 @@ generate: install-mockgen

format: install-goimports
@echo "> Formating code..."
test -z $$(goimports -l -w -local github.com/buildpacks/lifecycle .)

verify-jq:
ifeq (, $(shell which jq))
$(error "No jq in $$PATH, please install jq")
endif
$(if $(shell goimports -l -w -local github.com/buildpacks/lifecycle .), @echo Fixed formatting errors. Re-run && exit 1)

test: unit acceptance

unit: verify-jq format lint install-yj
unit: UNIT_PACKAGES=$(shell $(GOCMD) list ./... | grep -v acceptance)
unit: format lint install-yj
@echo "> Running unit tests..."
$(GOTEST) -v -count=1 $$($(GOCMD) list ./... | grep -v acceptance)
$(GOTEST) -v -count=1 $(UNIT_PACKAGES)
ameyer-pivotal marked this conversation as resolved.
Show resolved Hide resolved

acceptance: format lint
@echo "> Running acceptance tests..."
$(GOTEST) -v -count=1 -tags=acceptance ./acceptance/...

acceptance-darwin: format lint
@echo "> Running acceptance tests..."
$(GOTEST) -v -count=1 -tags=acceptance ./acceptance/...
Expand All @@ -151,23 +170,24 @@ package-linux: GOOS:=linux
package-linux: GOOS_DIR:=$(BUILD_DIR)/$(GOOS)
package-linux: ARCHIVE_NAME=lifecycle-v$(LIFECYCLE_VERSION)+$(GOOS).x86-64
package-linux:
@echo "> Writing descriptor file for $(GOOS)..."
mkdir -p $(GOOS_DIR)
echo "$${LIFECYCLE_DESCRIPTOR}" > $(GOOS_DIR)/lifecycle.toml

@echo "> Packaging lifecycle for $(GOOS)..."
tar czf $(BUILD_DIR)/$(ARCHIVE_NAME).tgz -C $(GOOS_DIR) lifecycle.toml lifecycle
$(GOCMD) run tools/packager/main.go -os $(GOOS) -launcherExePath $(GOOS_DIR)/lifecycle/launcher -lifecycleExePath $(GOOS_DIR)/lifecycle/lifecycle -lifecycleVersion $(LIFECYCLE_VERSION) -platformAPI $(PLATFORM_API) -buildpackAPI $(BUILDPACK_API) -outputPackagePath $(BUILD_DIR)/$(ARCHIVE_NAME).tgz
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the packager allows consistent tarball artifacts regardless of OS and identical target syntax. Issues were:

  • tar.exe is not equivalent to POSIX tar (i.e. can't do symlinks)
  • echoing LIFECYCLE_DESCRIPTOR to a file was messy on Windows and felt better outside Makefile.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not attached to go run over go install like our other tools. Happy to change if preferred.


package-windows: export LIFECYCLE_DESCRIPTOR:=$(LIFECYCLE_DESCRIPTOR)
package-windows: GOOS:=windows
package-windows: GOOS_DIR:=$(BUILD_DIR)/$(GOOS)
package-windows: GOOS_DIR:=$(BUILD_DIR)$/$(GOOS)
ameyer-pivotal marked this conversation as resolved.
Show resolved Hide resolved
package-windows: ARCHIVE_NAME=lifecycle-v$(LIFECYCLE_VERSION)+$(GOOS).x86-64
package-windows:
@echo "> Writing descriptor file for $(GOOS)..."
mkdir -p $(GOOS_DIR)
echo "$${LIFECYCLE_DESCRIPTOR}" > $(GOOS_DIR)/lifecycle.toml

@echo "> Packaging lifecycle for $(GOOS)..."
tar czf $(BUILD_DIR)/$(ARCHIVE_NAME).tgz -C $(GOOS_DIR) lifecycle.toml lifecycle
$(GOCMD) run tools$/packager$/main.go -os $(GOOS) -launcherExePath $(GOOS_DIR)$/lifecycle$/launcher.exe -lifecycleExePath $(GOOS_DIR)$/lifecycle$/lifecycle.exe -lifecycleVersion $(LIFECYCLE_VERSION) -platformAPI $(PLATFORM_API) -buildpackAPI $(BUILDPACK_API) -outputPackagePath $(BUILD_DIR)$/$(ARCHIVE_NAME).tgz

docker-build-source-image-windows:
docker build -f tools/Dockerfile.windows --tag $(SOURCE_COMPILATION_IMAGE) --build-arg image_tag=$(WINDOWS_COMPILATION_IMAGE) --cache-from=$(SOURCE_COMPILATION_IMAGE) --isolation=process --quiet .

docker-run-windows: docker-build-source-image-windows
docker-run-windows:
@echo "> Running '$(DOCKER_CMD)' in docker windows..."
@docker volume rm -f lifecycle-out
docker run -v lifecycle-out:c:/lifecycle/out -e LIFECYCLE_VERSION -e PLATFORM_API -e BUILDPACK_API -v gopathcache:c:/gopath -v '\\.\pipe\docker_engine:\\.\pipe\docker_engine' --isolation=process --rm $(SOURCE_COMPILATION_IMAGE) $(DOCKER_CMD)
docker run -v lifecycle-out:c:/lifecycle/out --rm $(SOURCE_COMPILATION_IMAGE) tar -cf- out | tar -xf-
@docker volume rm -f lifecycle-out

Copy link
Member

@micahyoung micahyoung Jun 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This target is meant to be an easy and consistent wrapper for any Windows command (i.e. make unit|acceptance|test, make build-windows package-windows, go run ...)

Our hope is to make building, running, and troubleshooting on Windows extremely consistent for any CNB contributor and CI by ensuring everyone's using an identical Dockerfile-based Windows environment w/o any manual setup.

Copy link
Member

@micahyoung micahyoung Jun 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative implementation we tried was docker build w/o COPY . src, docker create, docker cp, docker start but symlinks were not copied over properly (ended up as empty files)

.PHONY: verify-jq
11 changes: 5 additions & 6 deletions acceptance/acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type testCase struct {
func testVersion(t *testing.T, when spec.G, it spec.S) {
when("All", func() {
when("CNB_PLATFORM_API is set and incompatible", func() {
for _, binary := range []string{
for _, phase := range []string{
"analyzer",
"builder",
"detector",
Expand All @@ -47,9 +47,8 @@ func testVersion(t *testing.T, when spec.G, it spec.S) {
"rebaser",
"lifecycle",
} {
binary := binary
it(binary+"/should fail with error message and exit code 11", func() {
cmd := lifecycleCmd(binary)
it(phase+"/should fail with error message and exit code 11", func() {
cmd := lifecycleCmd(phase)
cmd.Env = append(os.Environ(), "CNB_PLATFORM_API=0.8")

_, exitCode, err := h.RunE(cmd)
Expand Down Expand Up @@ -153,8 +152,8 @@ func testVersion(t *testing.T, when spec.G, it spec.S) {
})
}

func lifecycleCmd(binary string, args ...string) *exec.Cmd {
return exec.Command(filepath.Join(buildDir, runtime.GOOS, "lifecycle", binary), args...)
func lifecycleCmd(phase string, args ...string) *exec.Cmd {
return exec.Command(filepath.Join(buildDir, runtime.GOOS, "lifecycle", phase), args...)
}

func buildBinaries(t *testing.T, dir string, goos string) {
Expand Down
Loading