Skip to content

LCOW: API: Add platform to /images/create and /build#34642

Merged
johnstep merged 2 commits intomoby:masterfrom
microsoft:jjh/add-platform-to-api
Oct 7, 2017
Merged

LCOW: API: Add platform to /images/create and /build#34642
johnstep merged 2 commits intomoby:masterfrom
microsoft:jjh/add-platform-to-api

Conversation

@lowenna
Copy link
Member

@lowenna lowenna commented Aug 27, 2017

Signed-off-by: John Howard jhoward@microsoft.com

This PR has the API changes described in #34617.

Update 9/13 based on feedback:
Specifically, it adds an HTTP header "X-Requested-Platform" which is a JSON-encoded
OCI Image-spec Platform structure to /images/create and /build

Specifically, it adds a query parameter which is a simple string such as platform=linux/amd64 to /images/create and /build.

In addition, it renames (almost all) uses of the string variable platform (and associated
methods/functions) to os, or operatingSystem where it clashes with the os import. This makes it much clearer to disambiguate with the swarm "platform" which is really os/arch. This is a stepping stone to getting the daemon towards fully multi-platform/arch-aware, and makes it clear when "operating system" is being referred to rather than "platform" which is misleadingly used - sometimes in the swarm
meaning, but more often as just the operating system.

@lowenna lowenna force-pushed the jjh/add-platform-to-api branch 2 times, most recently from 5f2907a to 66b10da Compare August 27, 2017 01:13
@lowenna
Copy link
Member Author

lowenna commented Aug 27, 2017

This is what the behaviour with this change (and a CLI updated to support the --platform flag) looks like:

This is where the image does not exist locally, and no platform flag has been passed. The engine will default to the host OS which is Windows, but no Windows busybox exists in the registry.

PS E:\> docker run --rm busybox echo hello
Unable to find image 'busybox:latest' locally
latest: Pulling from library/busybox
e:\go\src\github.com\docker\cli\binary\docker.exe: cannot download image with operating system "linux" when requesting "windows".
See 'e:\go\src\github.com\docker\cli\binary\docker.exe run --help'.
PS E:\>

Now doing the same adding the CLI/API flag. This succeeds.

PS E:\> docker run --rm --platform=linux busybox echo hello
Unable to find image 'busybox:latest' locally
latest: Pulling from library/busybox
add3ddb21ede: Pull complete
Digest: sha256:b82b5740006c1ab823596d2c07f081084ecdb32fd258072707b99f52a3cb8692
Status: Downloaded newer image for busybox:latest
hello

Now the image exists locally, there's no need to pass the API/CLI flag to run an image based off it:

PS E:\> docker run --rm busybox echo hello
hello

Delete the image for the next step

PS E:\> docker rmi busybox
Untagged: busybox:latest
Untagged: busybox@sha256:b82b5740006c1ab823596d2c07f081084ecdb32fd258072707b99f52a3cb8692
Deleted: sha256:d20ae45477cbc89863fff11d01cdccf28e4ff06ce2eb2f0206ef971b14eaf6c0
Deleted: sha256:6a749002dd6a65988a6696ca4d0c4cbe87145df74e3bf6feae4025ab28f420f2

Attempt to pull without an API/CLI flag. This also fails as there is no busybox image for Windows in the registry

PS E:\> docker pull busybox
Using default tag: latest
latest: Pulling from library/busybox
cannot download image with operating system "linux" when requesting "windows"

So adding the flag

PS E:\> docker pull --platform=linux busybox
Using default tag: latest
latest: Pulling from library/busybox
add3ddb21ede: Pull complete
Digest: sha256:b82b5740006c1ab823596d2c07f081084ecdb32fd258072707b99f52a3cb8692
Status: Downloaded newer image for busybox:latest

And the same for create. busybox doesn't exist locally first. So the first one will fail:

PS E:\docker\build\lcow> docker create busybox /bin/echo hello
Unable to find image 'busybox:latest' locally
latest: Pulling from library/busybox
cannot download image with operating system "linux" when requesting "windows"

So add the CLI/API flag:

PS E:\docker\build\lcow> docker create --platform=linux/amd64 busybox /bin/echo hello
Unable to find image 'busybox:latest' locally
latest: Pulling from library/busybox
Digest: sha256:b82b5740006c1ab823596d2c07f081084ecdb32fd258072707b99f52a3cb8692
Status: Downloaded newer image for busybox:latest
6cd0ad51750d3bf72eea042cfb10fe7335f633a7108324bebd8a7b82cd3a0c1d

Start a container from the new image

PS E:\docker\build\lcow> docker start 6
6

And query the output

PS E:\docker\build\lcow> docker logs 6cd
hello
PS E:\docker\build\lcow>

@lowenna lowenna force-pushed the jjh/add-platform-to-api branch 2 times, most recently from 287dc3f to 8fae079 Compare August 27, 2017 01:29
@lowenna
Copy link
Member Author

lowenna commented Aug 27, 2017

And showing the same build step from #34401, which this PR replaces, with the API changes, demonstrated via the CLI:

PS E:\docker\build\lcow> docker build --no-cache -f test3 --platform=linux .
Sending build context to Docker daemon  19.93MB
Step 1/3 : FROM busybox
latest: Pulling from library/busybox
add3ddb21ede: Pull complete
Digest: sha256:b82b5740006c1ab823596d2c07f081084ecdb32fd258072707b99f52a3cb8692
Status: Downloaded newer image for busybox:latest
 ---> d20ae45477cb
Step 2/3 : RUN mkdir /foo
 ---> Running in b83d7de0c946
 ---> 4755c8944ab4
Removing intermediate container b83d7de0c946
Step 3/3 : RUN ls -l /
 ---> Running in 5458cc315cd3
total 36
drwxr-xr-x    2 root     root         12288 Aug 23 00:00 bin
drwxr-xr-x    5 root     root           340 Aug 27 01:34 dev
drwxr-xr-x    1 root     root            60 Aug 27 01:34 etc
drwxr-xr-x    2 root     root          4096 Aug 27 01:34 foo
drwxr-xr-x    2 nobody   nogroup       4096 Aug 23 00:00 home
dr-xr-xr-x  118 root     root             0 Aug 27 01:34 proc
drwxr-xr-x    2 root     root          4096 Aug 23 00:00 root
dr-xr-xr-x   12 root     root             0 Aug 27 01:34 sys
drwxrwxrwt    2 root     root          4096 Aug 23 00:00 tmp
drwxr-xr-x    3 root     root          4096 Aug 23 00:00 usr
drwxr-xr-x    4 root     root          4096 Aug 23 00:00 var
 ---> eaa4cf7ef226
Removing intermediate container 5458cc315cd3
Successfully built eaa4cf7ef226
PS E:\docker\build\lcow>

@lowenna lowenna force-pushed the jjh/add-platform-to-api branch from 8fae079 to e65babf Compare August 27, 2017 01:39
@lowenna lowenna changed the title WIP - *IGNORE FOR NOW* - LCOW: API: Add platform to /images/create and /build LCOW: API: Add platform to /images/create and /build Aug 27, 2017
@lowenna
Copy link
Member Author

lowenna commented Aug 27, 2017

@tonistiigi @dmcgowan @johnstep @mlaventure @stevvooe @vieux @friism PTAL.

This is now ready for review, and can be tested in conjunction with the matching (somewhat hacked as it's dependent on this PR) CLI changes in docker/cli#474. Manual testing results above.

After this, the next step is re-coalescing the image store so that there is a single one in the engine. With that, the Windows daemon should be able to run in dual-LCOW & WCOW modes again, and the environment variable LCOW_SUPPORTED can be removed. I'm working on that next step now.

@dnephin @simonferquel @jstarks @darrenstahlmsft @PatrickLang @thaJeztah @crosbymichael FYI.

@lowenna
Copy link
Member Author

lowenna commented Aug 27, 2017

@stevvooe Note the 3 functions I've added in pkg\system\lcow.go which do the translation/parsing/validation of a platform flag passed from the CLI as os[/arch[/variant]] to an OCI image-spec Platform. I've switched the engine to use the OCI image-spec Platform structure as far as reasonably possible, without implementing the full multi-platform/architecture awareness the engine needs, which, as discussed, is out of scope of just the LCOW work.

I think I will need similar functionality in the containerd package in containerd/containerd#1403 to support these same three functions. However, we can migrate to those at a later date. I've put some pretty clear comments in that the existing functions I've introduced are temporary, and will migrate in the future.

@lowenna lowenna requested review from mlaventure, stevvooe and vieux and removed request for cpuguy83 August 27, 2017 03:26
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Can we avoid this extra indent?

For instance, we could name the function return in which case the following defer should work:

defer func() {
    if err != nil {
        if !output.Flushed() {
            return
        }
        output.Write(streamformatter.FormatError(err))
    } 
    err = nil
}()

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure we can that simply. The code can't go down the pull/import path if GetRequestedPlatform fails. Unless I'm missing something obvious, which is very possible.

Copy link
Contributor

Choose a reason for hiding this comment

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

Why the rename?

Copy link
Member Author

Choose a reason for hiding this comment

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

Same response :)

Copy link
Contributor

Choose a reason for hiding this comment

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

Why the rename?

Copy link
Member Author

Choose a reason for hiding this comment

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

@mlaventure It's due to this that I put at the top:

In addition, it renames (almost all) uses of the string variable platform (and associated
methods/functions) to os, or operatingSystem where it clashes with the os import. This makes it much clearer to disambiguate with the swarm "platform" which is really os/arch. This is a stepping stone to getting the daemon towards fully multi-platform/arch-aware, and makes it clear when "operating system" is being referred to rather than "platform" which is misleadingly used - sometimes in the swarm
meaning, but more often as just the operating system.

Copy link
Contributor

Choose a reason for hiding this comment

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

In this case, shouldn't we use an OCI platform spec?

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, yes, indeed. Platform was added much earlier in the LCOW work. This PR simply renamed it to OS. But in the light of the discussions about the OCI spec and specifiers, I agree, this should be the OCI image-spec Platform. Will update.

Copy link
Member Author

Choose a reason for hiding this comment

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

@stevvooe - That's updated now. For the time being, only the OS field is populated as that the only field in the Container object currently. As the daemon is moved to be fully multi-platform/arch (etc) aware, as per the F2F discussions, this field can be extended.

Copy link
Contributor

Choose a reason for hiding this comment

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

?

Copy link
Member Author

Choose a reason for hiding this comment

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

Oops. Yes, will fix

Copy link
Contributor

Choose a reason for hiding this comment

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

nit: how about something like:

	imageImage := &image.Image{}
	imageImage.OS = runtime.GOOS
	if runtime.GOOS == "windows" {
		switch b.options.Platform.OS {
		case "windows":
			return nil, errors.New("Windows does not support FROM scratch")
		case "linux":
			if !system.LCOWSupported() {
				return nil, errors.New("LCOW is not supported on this system")
			}
			imageImage.OS = "linux"
		default:
			return nil, errors.Errorf("OS %s is not supported", b.options.Platform.OS)
		}
	}
	return builder.Image(imageImage), nil

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure, will update

Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't we just pass the platform here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually, the same comment as before. When things are fully multi-platform/arch aware throughout the daemon, I agree. However, for now, the only bit which is actually used is the operating system, hence I'm making that obvious. (Clearly as mentioned before, full multi-platform/arch is out of scope of LCOW directly)

@lowenna lowenna force-pushed the jjh/add-platform-to-api branch from e65babf to d3d7ce3 Compare August 30, 2017 02:07
@lowenna
Copy link
Member Author

lowenna commented Aug 30, 2017

@mlaventure comments addressed and rebased.

api/swagger.yaml Outdated

Only the registry domain name (and port if not the default 443) are required. However, for legacy reasons, the Docker Hub registry must be specified with both a `https://` prefix and a `/v1/` suffix even though Docker will prefer to use the v2 registry API.
type: "string"
- name: "X-Requested-Platform"
Copy link
Contributor

Choose a reason for hiding this comment

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

Why a header with json in it?

Copy link
Member Author

Choose a reason for hiding this comment

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

Answered in larger comment below.

@stevvooe
Copy link
Contributor

@jhowardmsft Rather than use a header with json, let's define a parameter using the platform selector syntax in containerd/containerd#1403.

@lowenna
Copy link
Member Author

lowenna commented Aug 31, 2017

@stevvooe. Am I missing what you mean by "as a parameter"? You mean as in POST /endpoint?foo=bar&platform=.....? That already was ruled out from a previous comment.....

Otherwise there's only two ways to send "structured data" from the CLI through the API to the daemon. That being either an HTTP header, or JSON encoded in the body.

I can't use the body for this as it build uses the body for the build context. Hence the reason for adding it as a header, avoiding a breaking change.

As for encoding, there's really only a handful of choices here.

a) Pass an un-parsed string and handle it server side. That simply isn't clean.
b) Parse it (and validate) it client side and pass structured data to the server. In terms of encoding structured data, the real two choices open are JSON or base64. JSON IMO seems better here.

I simply went for b). That seems a far better option.

As for using containerd/containerd#1403, as I have commented in the code, the parsing functions are to be updated once that is merged. It isn't yet. However, that PR still bases the underlying platform structure on the OCI Image specs Platform definition, which is exactly what I have used here. I don't think it is unreasonable to use that in the API.

@lowenna lowenna force-pushed the jjh/add-platform-to-api branch from 0b4b8f4 to 5a2f85a Compare September 22, 2017 17:38
Copy link
Contributor

Choose a reason for hiding this comment

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

What err is this? This doesn't look like it gets set.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good catch, thanks. It was a copy/paste error on my part into the if block above where the platform validation could fail. In this case, we still need to flush the output at the end of this function, but obviously not do the PullImage or ImportImage calls into the backend. Fixed, pushing it shortly.

Copy link
Contributor

Choose a reason for hiding this comment

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

👍

@lowenna lowenna force-pushed the jjh/add-platform-to-api branch 2 times, most recently from ee92aea to 4b914b8 Compare September 22, 2017 19:23
@lowenna
Copy link
Member Author

lowenna commented Sep 25, 2017

Hey @stevvooe - any other issues you can see in here? Thanks :)

@stevvooe
Copy link
Contributor

@jhowardmsft I think you need to remove that err check still.

@lowenna
Copy link
Member Author

lowenna commented Oct 2, 2017

@stevvooe Ah, I see why you say that now. Came back to it with fresh eyes this morning. The intent was that the output was flushed/written, but the backend wasn't called in the case the platform passed in the API was invalid. I've now fixed that, but it requires keeping the error check in.

@stevvooe
Copy link
Contributor

stevvooe commented Oct 2, 2017

@jhowardmsft What can set that error? Based on the current code, it is always nil, unless I'm missing something.

@lowenna
Copy link
Member Author

lowenna commented Oct 2, 2017

@stevvooe It's set in the block immediately above it where it's validating what is passed in via the API:

...
		platform = system.ParsePlatform(apiPlatform)
		if err = system.ValidatePlatform(platform); err != nil {
			err = fmt.Errorf("invalid platform: %s", err)
		}
	}

	if err == nil {
		if image != "" {
...

@stevvooe
Copy link
Contributor

stevvooe commented Oct 2, 2017

LGTM

My that is awkward code.

Copy link
Member

@johnstep johnstep left a comment

Choose a reason for hiding this comment

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

LGTM

daemon/create.go Outdated
Copy link
Member

Choose a reason for hiding this comment

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

s/mean/means/

daemon/create.go Outdated
Copy link
Member

Choose a reason for hiding this comment

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

The earlier scratch case checks for system.LCOWSupported(). Is that required here, or unnecessary there?

Copy link
Member

Choose a reason for hiding this comment

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

bath? Is that short for bailout path?

Copy link
Member

Choose a reason for hiding this comment

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

16299.15? Is the UBR (15, for example) important, and is there a way to deal with it?

Copy link
Member

Choose a reason for hiding this comment

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

No need to change this, since it is temporary and should be short-lived, but I would have checked for >= 1 first and then nested >=2 in that, and nested == 3 in that.

Signed-off-by: John Howard <jhoward@microsoft.com>

This PR has the API changes described in moby#34617.
Specifically, it adds an HTTP header "X-Requested-Platform" which is a JSON-encoded
OCI Image-spec `Platform` structure.

In addition, it renames (almost all) uses of a string variable platform (and associated)
methods/functions to os. This makes it much clearer to disambiguate with the swarm
"platform" which is really os/arch. This is a stepping stone to getting the daemon towards
fully multi-platform/arch-aware, and makes it clear when "operating system" is being
referred to rather than "platform" which is misleadingly used - sometimes in the swarm
meaning, but more often as just the operating system.
Signed-off-by: John Howard <jhoward@microsoft.com>
@lowenna lowenna force-pushed the jjh/add-platform-to-api branch from 4b914b8 to d98ecf2 Compare October 6, 2017 22:28
@lowenna
Copy link
Member Author

lowenna commented Oct 6, 2017

@johnstep (@dmcgowen) - rebased after #35090 was merged and re-verified. Note, I've left the regression introduced by #35090 (comment) in there for you guys to come up with a spot fix (with the further caveat I still have reservations about the approach 35090 takes)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/lcow Issues and PR's related to the experimental LCOW feature status/4-merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants