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

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

merged 36 commits into from
Jul 1, 2020

Conversation

ameyer-pivotal
Copy link
Contributor

@ameyer-pivotal ameyer-pivotal commented Jun 9, 2020

In general, changes relate to the following:

  • Replacing tar.NewWriter calls with a factory call which can get a layer writer appropriate for the target image OS
  • Fixing OS-related path and delimiter issues
  • Implements full launcher support for Windows
  • Makefile/CI-related changes to support Windows testing and artifact upload

Signed-off-by: Andrew Meyer meyeran@vmware.com
Signed-off-by: Anthony Emengo aemengo@vmware.com
Signed-off-by: Micah Young ymicah@vmware.com

@ameyer-pivotal ameyer-pivotal requested a review from a team as a code owner June 9, 2020 18:15
@ameyer-pivotal
Copy link
Contributor Author

@aemengo
@micahyoung

cache/volume_cache.go Outdated Show resolved Hide resolved
Makefile Outdated Show resolved Hide resolved
@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.

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)

Makefile Outdated Show resolved Hide resolved
- 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

Copy link
Member

@ekcasey ekcasey left a comment

Choose a reason for hiding this comment

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

I still need to try out the packager but I left some comments.

Also, why did we move from symlinking the bin dir for the test buildpacks to symlinking each individual file? It seems like we would end up with a lot more symlinks and the same behavior?

Makefile Outdated Show resolved Hide resolved
Makefile Outdated Show resolved Hide resolved
cache/volume_cache.go Outdated Show resolved Hide resolved
cmd/defaults_windows.go Outdated Show resolved Hide resolved
env/env_test.go Outdated Show resolved Hide resolved
layers.go Outdated Show resolved Hide resolved
archive/tar.go Show resolved Hide resolved
exporter.go Outdated Show resolved Hide resolved
archive/tar_test.go Show resolved Hide resolved
launch/launcher_test.go Outdated Show resolved Hide resolved
exporter.go Outdated Show resolved Hide resolved
exporter.go Outdated Show resolved Hide resolved
@micahyoung
Copy link
Member

micahyoung commented Jun 16, 2020

Also, why did we move from symlinking the bin dir for the test buildpacks to symlinking each individual file? It seems like we would end up with a lot more symlinks and the same behavior?

docker cp and docker build have very limited support for directory symlinks with Windows daemons but apparently complete support for file symlinks. The only directory symlinks that we could make work are ones with a target in the symlink's same directory or subdirectory (and even then only if the symlink file name is derived from the target name).

Keeping these test buidpacks as directory symlinks instead of file symlinks would at minimum break our make docker-run-windows task (which we use for the new job CI and more) which does a docker build to copy in the repo source, then build, run tests, etc.

It's worth nothing that this underlying issue with directory symlinks will also effect users during pack build if their apps have them (since we're using CopyToContainer under the hood). This is likely a bug which would need to be fixed in Docker, though I suspect the only reason it hasn't already been fix is that these types of symlinks are uncommon in most Docker apps.

We recorded some experiments and findings here

There's not a specific WCOW Docker/moby issue I can find for this, though a similar open issue for LCOW is present (which also would have effected pack LCOW users), suggesting bugs in symlink support are not uncommon: docker/for-win#5778 (comment)

Copy link
Member

@natalieparellano natalieparellano left a comment

Choose a reason for hiding this comment

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

This is super exciting! I left a couple of comments. Mostly, I am just trying to follow along with what's happening. LMK if I can be of any help with manual validation, etc.

cmd/launcher/main.go Outdated Show resolved Hide resolved
cmd/lifecycle/main.go Outdated Show resolved Hide resolved
Makefile Show resolved Hide resolved
cmd/flags.go Outdated Show resolved Hide resolved
cmd/launcher/main.go Outdated Show resolved Hide resolved
@ekcasey ekcasey changed the base branch from master to main June 18, 2020 14:09
@ameyer-pivotal ameyer-pivotal changed the title Add ability to export Windows-based images Add ability to export/launch Windows-based images Jun 19, 2020
env/env.go Outdated Show resolved Hide resolved
Makefile Outdated Show resolved Hide resolved
Makefile Outdated Show resolved Hide resolved
Makefile Outdated Show resolved Hide resolved
Comment on lines +11 to +12
volumeName := filepath.VolumeName(path)
path = strings.TrimPrefix(path, volumeName)
return filepath.ToSlash(path)
Copy link
Member

Choose a reason for hiding this comment

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

Question: Are the constraints we are addressing here constraints of the TAR header spec itself or constraints of the OCI spec and the format it expects from layers? I tried to find this requirement in the TAR spec but couldn't (doesn't mean they don't exist I just didn't find them in the time I allocated to looking).

Copy link
Contributor Author

@ameyer-pivotal ameyer-pivotal Jun 23, 2020

Choose a reason for hiding this comment

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

Interesting question... Originally, this wasn't based on any concrete evidence other than the patterns in our code for both pack and lifecycle. We chose to "normalize" the creation of tars to simplify the code (and because somewhere along the way it was stuck in our heads that tar headers always used forward slashes).

Looking at the TAR spec seems to be consistent...

For the slashes (vs backslashes), I found some evidence for that here:

The name field is the file name of the file, with directory names (if any) preceding the file name, separated by slashes.

The volume part is harder to nail down in the TAR spec as it's not mentioned in the above quote. Based on pages like this, it doesn't seem like volume names would be expected in the name field though. I'd imagine having a volume name in the header path would make untarring just have to do the same trimming that we're doing upon creation here.

archive/tar_test.go Outdated Show resolved Hide resolved
archive/tar_test.go Outdated Show resolved Hide resolved
archive/tar_test.go Outdated Show resolved Hide resolved
Comment on lines +63 to +65
if len(profileCmds) > 0 {
launcher = strings.Join(profileCmds, " && ") + " && "
}
Copy link
Member

Choose a reason for hiding this comment

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

Joining with " && " should work for both bash and cmd

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm happy to do this, but we were trying to leave the Linux case rather untouched, as they were joined with newlines before.

sys/sys_unix.go Outdated
Comment on lines 3 to 8
package sys

const (
Root = "/"
ExecExt = ""
)
Copy link
Member

Choose a reason for hiding this comment

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

I don't think these two constants deserve their own package. Can we move them to cmd? If there are import issues I think it is okay to repeat these constants in other packages. I would prioritize keeping the import graph tidy. There may be cases like the usage in restorer where we could remove them entirely (we can make UntarLayer default to the root when given an empty destination).

Copy link
Contributor Author

@ameyer-pivotal ameyer-pivotal Jun 23, 2020

Choose a reason for hiding this comment

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

The benefit to having these in their own package is that it addresses two concerns:

  • Avoids import cycles since this can be seen as a "leaf" node in the import tree
  • This was created when addressing the concern about keeping the default paths in sync between OSes (refactored separate OS-dependent files to a single file that uses filepath.Join, etc. in combination with these constants)

It seems the second bullet would sort of go against repeating these constants in multiple places. I'll see if I can move them to a different package though.

Could you clarify what you meant about UntarLayer? I think using these there will still cause import cycles. I'll have to try though.

Copy link
Contributor Author

@ameyer-pivotal ameyer-pivotal Jun 23, 2020

Choose a reason for hiding this comment

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

So tinkering a bit, cmd seems like too high-level of a package to move this to without causing import cycles. How important is this? It feels like one of those cases that can go either way, depending on stylistic opinions. I'd argue that the import tree is simpler to understand and use with this as a leaf node (i.e. modular, self-contained packages), but other folks might argue that simpler means fewer packages altogether.

I see a few options:

  • Leave as is
  • Find a lower-level package where this could live instead
  • Force it into cmd and spend time debugging import cycles

Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ekcasey Actually, it worked pretty nicely to move into the env package. That feels like a reasonable compromise. Let me know what you think :)

Copy link
Member

@ekcasey ekcasey Jun 29, 2020

Choose a reason for hiding this comment

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

It seems the second bullet would sort of go against repeating these constants in multiple places. I'll see if I can move them to a different package though.

I feel like this concern is less relevant for these constants than for the default input values. We will realistically change default input values at some point and we don't want linux/windows values to fall out of sync. The posix and windows fs root dirs on the other hand, should not change. This is how I would handle it:

035a3c0

I have removed some unnecessary usage of these constants leaving us with the original motivating usage in cmd and one other usage in archive (since we already have very specific OS-dependent path logic there it doesn't feel crazy to me to redefine these in that context).

Copy link
Member

Choose a reason for hiding this comment

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

My main concern is that when these are exported they become part of the public package level interface for env and I don't think we want that. I would like the keep the import graph and the godoc as clean and straightforward as possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK. Makes sense. We'll pull in your suggested change :)

Anthony Emengo and others added 6 commits June 26, 2020 11:15
Signed-off-by: Andrew Meyer <meyeran@vmware.com>
Signed-off-by: Anthony Emengo <aemengo@vmware.com>
Signed-off-by: Micah Young <ymicah@vmware.com>
Signed-off-by: Andrew Meyer <meyeran@vmware.com>
- Add os-specific slash variable, for convenience
- Move PWD redefinition into Windows-only if-case

Signed-off-by: Andrew Meyer <meyeran@vmware.com>
Signed-off-by: Andrew Meyer <meyeran@vmware.com>
- Also fixed typo in makefile

Signed-off-by: Andrew Meyer <meyeran@vmware.com>
Signed-off-by: Andrew Meyer <meyeran@vmware.com>
ameyer-pivotal and others added 19 commits June 26, 2020 11:15
Signed-off-by: Andrew Meyer <meyeran@vmware.com>
Signed-off-by: Andrew Meyer <meyeran@vmware.com>
Signed-off-by: Andrew Meyer <meyeran@vmware.com>
Signed-off-by: Andrew Meyer <meyeran@vmware.com>
- Pending discussion on how to handle potential Docker-in-Docker needs for build-linux
- Will open separate PR once other work is merged

Signed-off-by: Micah Young <ymicah@vmware.com>
Signed-off-by: Andrew Meyer <meyeran@vmware.com>
- the Files/ Hives/ directories must be trimmed of the container image
  layer before writing to the container filesystem
- Untar() -> UntarLayer() to justify adding container specific behavior

Signed-off-by: Anthony Emengo <anthonyemengojr@gmail.com>
- launcher increased in size due `runtime` import for `GOOS` detection
- inlined commands do not work well on Windows

Signed-off-by: Micah Young <ymicah@vmware.com>
Signed-off-by: Anthony Emengo <anthonyemengojr@gmail.com>
Signed-off-by: Micah Young <ymicah@vmware.com>
- Allows faster builds
- Inline OS check since it is only used for -symlinks target

Signed-off-by: Micah Young <ymicah@vmware.com>
Signed-off-by: Andrew Meyer <meyeran@vmware.com>
Signed-off-by: Anthony Emengo <aemengo@vmware.com>
Signed-off-by: Andrew Meyer <meyeran@vmware.com>
Signed-off-by: Anthony Emengo <aemengo@vmware.com>
Signed-off-by: Andrew Meyer <meyeran@vmware.com>
Signed-off-by: Anthony Emengo <aemengo@vmware.com>
Signed-off-by: Micah Young <ymicah@vmware.com>
Signed-off-by: Andrew Meyer <meyeran@vmware.com>
- Restore removed LDFLAGS
- Bump linux compilation image go version

Signed-off-by: Andrew Meyer <meyeran@vmware.com>
- More robust 'Files' prefix trimming
- Skip 'Hives' paths instead of trimming
- Make tests easier to debug
- Some minor test cleanup

Signed-off-by: Andrew Meyer <meyeran@vmware.com>
Signed-off-by: Andrew Meyer <meyeran@vmware.com>
Signed-off-by: Micah Young <ymicah@vmware.com>
- Add LayerOS() helper to communicate intention

Signed-off-by: Micah Young <ymicah@vmware.com>
Signed-off-by: Andrew Meyer <meyeran@vmware.com>
archive/tar_test.go Outdated Show resolved Hide resolved
archive/tar_test.go Show resolved Hide resolved
image/writer_factory.go Outdated Show resolved Hide resolved
@@ -0,0 +1,2 @@
@echo off
bash %~dp0\detect %1 %2
Copy link
Member

Choose a reason for hiding this comment

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

I was surprised to find that I couldn't run the detect unit tests on a windows machine w/o bash installed. The failures give the false impression that bash is a dependency of the detector. However the implementation is correctly calling detect.bat which in turn requires bash. Given that we generally won't have bash available when the lifecycle runs on windows I am not sure I am comfortable with the dependency in the test. It makes it impossible to validate that the lifecycle can execute correctly in an environment sans bash. Can we reimplement the detect test logic in the *.bat files?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can rewrite these in pure batch. The makefile provides a way to run the tests in a docker container that has all the necessary dependencies, but I understand the overall concerns.

ameyer-pivotal and others added 5 commits June 29, 2020 11:08
Signed-off-by: Andrew Meyer <meyeran@vmware.com>
- Skips all non-Files-prefixed entries
- Make test fixture clearer

Signed-off-by: Andrew Meyer <meyeran@vmware.com>
Signed-off-by: Malini Valliath <mvalliath@pivotal.io>
Signed-off-by: Andrew Meyer <meyeran@vmware.com>
Signed-off-by: Malini Valliath <mvalliath@pivotal.io>
Signed-off-by: Emily Casey <ecasey@vmware.com>
- We were writing improper layers for restorer_test.go

Signed-off-by: Andrew Meyer <meyeran@vmware.com>
Signed-off-by: Andrew Meyer <meyeran@vmware.com>
Signed-off-by: Victoria Henry <vhenry@pivotal.io>
Signed-off-by: Micah Young<ymicah@vmware.com>
@ekcasey ekcasey merged commit a7e916f into buildpacks:main Jul 1, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
5 participants