mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #8827 from jlhawn/build_implied_from_scratch
Make `FROM scratch` a special cased 'no-base' spec
This commit is contained in:
commit
610842f906
17 changed files with 70 additions and 42 deletions
|
@ -1696,7 +1696,12 @@ func (cli *DockerCli) CmdPs(args ...string) error {
|
|||
|
||||
ports.ReadListFrom([]byte(out.Get("Ports")))
|
||||
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t", outID, out.Get("Image"), outCommand,
|
||||
image := out.Get("Image")
|
||||
if image == "" {
|
||||
image = "<no image>"
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t", outID, image, outCommand,
|
||||
units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))),
|
||||
out.Get("Status"), api.DisplayablePorts(ports), strings.Join(outNames, ","))
|
||||
|
||||
|
|
|
@ -21,6 +21,12 @@ import (
|
|||
"github.com/docker/docker/runconfig"
|
||||
)
|
||||
|
||||
const (
|
||||
// NoBaseImageSpecifier is the symbol used by the FROM
|
||||
// command to specify that no base image is to be used.
|
||||
NoBaseImageSpecifier string = "scratch"
|
||||
)
|
||||
|
||||
// dispatch with no layer / parsing. This is effectively not a command.
|
||||
func nullDispatch(b *Builder, args []string, attributes map[string]bool, original string) error {
|
||||
return nil
|
||||
|
@ -115,6 +121,12 @@ func from(b *Builder, args []string, attributes map[string]bool, original string
|
|||
|
||||
name := args[0]
|
||||
|
||||
if name == NoBaseImageSpecifier {
|
||||
b.image = ""
|
||||
b.noBaseImage = true
|
||||
return nil
|
||||
}
|
||||
|
||||
image, err := b.Daemon.Repositories().LookupImage(name)
|
||||
if b.Pull {
|
||||
image, err = b.pullImage(name)
|
||||
|
@ -191,7 +203,7 @@ func workdir(b *Builder, args []string, attributes map[string]bool, original str
|
|||
// RUN [ "echo", "hi" ] # echo hi
|
||||
//
|
||||
func run(b *Builder, args []string, attributes map[string]bool, original string) error {
|
||||
if b.image == "" {
|
||||
if b.image == "" && !b.noBaseImage {
|
||||
return fmt.Errorf("Please provide a source image with `from` prior to run")
|
||||
}
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@ type Builder struct {
|
|||
cmdSet bool // indicates is CMD was set in current Dockerfile
|
||||
context tarsum.TarSum // the context is a tarball that is uploaded by the client
|
||||
contextPath string // the path of the temporary directory the local context is unpacked to (server side)
|
||||
|
||||
noBaseImage bool // indicates that this build does not start from any base image, but is being built from an empty file system.
|
||||
}
|
||||
|
||||
// Run the builder with the context. This is the lynchpin of this package. This
|
||||
|
|
|
@ -58,7 +58,7 @@ func (b *Builder) readContext(context io.Reader) error {
|
|||
}
|
||||
|
||||
func (b *Builder) commit(id string, autoCmd []string, comment string) error {
|
||||
if b.image == "" {
|
||||
if b.image == "" && !b.noBaseImage {
|
||||
return fmt.Errorf("Please provide a source image with `from` prior to commit")
|
||||
}
|
||||
b.Config.Image = b.image
|
||||
|
@ -513,7 +513,7 @@ func (b *Builder) probeCache() (bool, error) {
|
|||
}
|
||||
|
||||
func (b *Builder) create() (*daemon.Container, error) {
|
||||
if b.image == "" {
|
||||
if b.image == "" && !b.noBaseImage {
|
||||
return nil, fmt.Errorf("Please provide a source image with `from` prior to run")
|
||||
}
|
||||
b.Config.Image = b.image
|
||||
|
|
|
@ -59,17 +59,17 @@ func (daemon *Daemon) Commit(container *Container, repository, tag, comment, aut
|
|||
|
||||
// Create a new image from the container's base layers + a new layer from container changes
|
||||
var (
|
||||
containerID, containerImage string
|
||||
containerID, parentImageID string
|
||||
containerConfig *runconfig.Config
|
||||
)
|
||||
|
||||
if container != nil {
|
||||
containerID = container.ID
|
||||
containerImage = container.Image
|
||||
parentImageID = container.ImageID
|
||||
containerConfig = container.Config
|
||||
}
|
||||
|
||||
img, err := daemon.graph.Create(rwTar, containerID, containerImage, comment, author, containerConfig, config)
|
||||
img, err := daemon.graph.Create(rwTar, containerID, parentImageID, comment, author, containerConfig, config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ type Container struct {
|
|||
Args []string
|
||||
|
||||
Config *runconfig.Config
|
||||
Image string
|
||||
ImageID string `json:"Image"`
|
||||
|
||||
NetworkSettings *NetworkSettings
|
||||
|
||||
|
@ -186,7 +186,7 @@ func (container *Container) WriteHostConfig() error {
|
|||
|
||||
func (container *Container) LogEvent(action string) {
|
||||
d := container.daemon
|
||||
if err := d.eng.Job("log", action, container.ID, d.Repositories().ImageName(container.Image)).Run(); err != nil {
|
||||
if err := d.eng.Job("log", action, container.ID, d.Repositories().ImageName(container.ImageID)).Run(); err != nil {
|
||||
log.Errorf("Error logging event %s for %s: %s", action, container.ID, err)
|
||||
}
|
||||
}
|
||||
|
@ -786,7 +786,7 @@ func (container *Container) GetImage() (*image.Image, error) {
|
|||
if container.daemon == nil {
|
||||
return nil, fmt.Errorf("Can't get image of unregistered container")
|
||||
}
|
||||
return container.daemon.graph.Get(container.Image)
|
||||
return container.daemon.graph.Get(container.ImageID)
|
||||
}
|
||||
|
||||
func (container *Container) Unmount() error {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
"github.com/docker/docker/engine"
|
||||
"github.com/docker/docker/graph"
|
||||
"github.com/docker/docker/image"
|
||||
"github.com/docker/docker/pkg/parsers"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/libcontainer/label"
|
||||
|
@ -68,15 +69,22 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos
|
|||
var (
|
||||
container *Container
|
||||
warnings []string
|
||||
img *image.Image
|
||||
imgID string
|
||||
err error
|
||||
)
|
||||
|
||||
img, err := daemon.repositories.LookupImage(config.Image)
|
||||
if config.Image != "" {
|
||||
img, err = daemon.repositories.LookupImage(config.Image)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := img.CheckDepth(); err != nil {
|
||||
if err = img.CheckDepth(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
imgID = img.ID
|
||||
}
|
||||
|
||||
if warnings, err = daemon.mergeAndVerifyConfig(config, img); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -86,13 +94,13 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos
|
|||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
if container, err = daemon.newContainer(name, config, img); err != nil {
|
||||
if container, err = daemon.newContainer(name, config, imgID); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := daemon.Register(container); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := daemon.createRootfs(container, img); err != nil {
|
||||
if err := daemon.createRootfs(container); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if hostConfig != nil {
|
||||
|
|
|
@ -417,10 +417,10 @@ func (daemon *Daemon) checkDeprecatedExpose(config *runconfig.Config) bool {
|
|||
|
||||
func (daemon *Daemon) mergeAndVerifyConfig(config *runconfig.Config, img *image.Image) ([]string, error) {
|
||||
warnings := []string{}
|
||||
if daemon.checkDeprecatedExpose(img.Config) || daemon.checkDeprecatedExpose(config) {
|
||||
if (img != nil && daemon.checkDeprecatedExpose(img.Config)) || daemon.checkDeprecatedExpose(config) {
|
||||
warnings = append(warnings, "The mapping to public ports on your host via Dockerfile EXPOSE (host:port:port) has been deprecated. Use -p to publish the ports.")
|
||||
}
|
||||
if img.Config != nil {
|
||||
if img != nil && img.Config != nil {
|
||||
if err := runconfig.Merge(config, img.Config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -557,7 +557,7 @@ func parseSecurityOpt(container *Container, config *runconfig.HostConfig) error
|
|||
return err
|
||||
}
|
||||
|
||||
func (daemon *Daemon) newContainer(name string, config *runconfig.Config, img *image.Image) (*Container, error) {
|
||||
func (daemon *Daemon) newContainer(name string, config *runconfig.Config, imgID string) (*Container, error) {
|
||||
var (
|
||||
id string
|
||||
err error
|
||||
|
@ -578,7 +578,7 @@ func (daemon *Daemon) newContainer(name string, config *runconfig.Config, img *i
|
|||
Args: args, //FIXME: de-duplicate from config
|
||||
Config: config,
|
||||
hostConfig: &runconfig.HostConfig{},
|
||||
Image: img.ID, // Always use the resolved image id
|
||||
ImageID: imgID,
|
||||
NetworkSettings: &NetworkSettings{},
|
||||
Name: name,
|
||||
Driver: daemon.driver.String(),
|
||||
|
@ -590,14 +590,14 @@ func (daemon *Daemon) newContainer(name string, config *runconfig.Config, img *i
|
|||
return container, err
|
||||
}
|
||||
|
||||
func (daemon *Daemon) createRootfs(container *Container, img *image.Image) error {
|
||||
func (daemon *Daemon) createRootfs(container *Container) error {
|
||||
// Step 1: create the container directory.
|
||||
// This doubles as a barrier to avoid race conditions.
|
||||
if err := os.Mkdir(container.root, 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
initID := fmt.Sprintf("%s-init", container.ID)
|
||||
if err := daemon.driver.Create(initID, img.ID); err != nil {
|
||||
if err := daemon.driver.Create(initID, container.ImageID); err != nil {
|
||||
return err
|
||||
}
|
||||
initPath, err := daemon.driver.Get(initID, "")
|
||||
|
|
|
@ -131,7 +131,7 @@ func (daemon *Daemon) DeleteImage(eng *engine.Engine, name string, imgs *engine.
|
|||
|
||||
func (daemon *Daemon) canDeleteImage(imgID string, force bool) error {
|
||||
for _, container := range daemon.List() {
|
||||
parent, err := daemon.Repositories().LookupImage(container.Image)
|
||||
parent, err := daemon.Repositories().LookupImage(container.ImageID)
|
||||
if err != nil {
|
||||
if daemon.Graph().IsNotExist(err) {
|
||||
return nil
|
||||
|
|
|
@ -35,7 +35,7 @@ func (daemon *Daemon) ContainerInspect(job *engine.Job) engine.Status {
|
|||
out.SetList("Args", container.Args)
|
||||
out.SetJson("Config", container.Config)
|
||||
out.SetJson("State", container.State)
|
||||
out.SetJson("Image", container.Image)
|
||||
out.Set("Image", container.ImageID)
|
||||
out.SetJson("NetworkSettings", container.NetworkSettings)
|
||||
out.Set("ResolvConfPath", container.ResolvConfPath)
|
||||
out.Set("HostnamePath", container.HostnamePath)
|
||||
|
|
|
@ -116,7 +116,7 @@ func (daemon *Daemon) Containers(job *engine.Job) engine.Status {
|
|||
out := &engine.Env{}
|
||||
out.SetJson("Id", container.ID)
|
||||
out.SetList("Names", names[container.ID])
|
||||
out.SetJson("Image", daemon.Repositories().ImageName(container.Image))
|
||||
out.SetJson("Image", daemon.Repositories().ImageName(container.ImageID))
|
||||
if len(container.Args) > 0 {
|
||||
args := []string{}
|
||||
for _, arg := range container.Args {
|
||||
|
|
|
@ -298,6 +298,9 @@ func validateRepoName(name string) error {
|
|||
if name == "" {
|
||||
return fmt.Errorf("Repository name can't be empty")
|
||||
}
|
||||
if name == "scratch" {
|
||||
return fmt.Errorf("'scratch' is a reserved name")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -230,9 +230,9 @@ func TestEventsRedirectStdout(t *testing.T) {
|
|||
|
||||
func TestEventsImagePull(t *testing.T) {
|
||||
since := time.Now().Unix()
|
||||
pullCmd := exec.Command(dockerBinary, "pull", "scratch")
|
||||
pullCmd := exec.Command(dockerBinary, "pull", "hello-world")
|
||||
if out, _, err := runCommandWithOutput(pullCmd); err != nil {
|
||||
t.Fatalf("pulling the scratch image from has failed: %s, %v", out, err)
|
||||
t.Fatalf("pulling the hello-world image from has failed: %s, %v", out, err)
|
||||
}
|
||||
|
||||
eventsCmd := exec.Command(dockerBinary, "events",
|
||||
|
@ -243,7 +243,7 @@ func TestEventsImagePull(t *testing.T) {
|
|||
events := strings.Split(strings.TrimSpace(out), "\n")
|
||||
event := strings.TrimSpace(events[len(events)-1])
|
||||
|
||||
if !strings.HasSuffix(event, "scratch:latest: pull") {
|
||||
if !strings.HasSuffix(event, "hello-world:latest: pull") {
|
||||
t.Fatalf("Missing pull event - got:%q", event)
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
)
|
||||
|
||||
func TestInspectImage(t *testing.T) {
|
||||
imageTest := "scratch"
|
||||
imageTest := "emptyfs"
|
||||
imageTestID := "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158"
|
||||
imagesCmd := exec.Command(dockerBinary, "inspect", "--format='{{.Id}}'", imageTest)
|
||||
out, exitCode, err := runCommandWithOutput(imagesCmd)
|
||||
|
|
|
@ -9,11 +9,11 @@ import (
|
|||
|
||||
// pulling an image from the central registry should work
|
||||
func TestPullImageFromCentralRegistry(t *testing.T) {
|
||||
pullCmd := exec.Command(dockerBinary, "pull", "scratch")
|
||||
pullCmd := exec.Command(dockerBinary, "pull", "hello-world")
|
||||
if out, _, err := runCommandWithOutput(pullCmd); err != nil {
|
||||
t.Fatalf("pulling the scratch image from the registry has failed: %s, %v", out, err)
|
||||
t.Fatalf("pulling the hello-world image from the registry has failed: %s, %v", out, err)
|
||||
}
|
||||
logDone("pull - pull scratch")
|
||||
logDone("pull - pull hello-world")
|
||||
}
|
||||
|
||||
// pulling a non-existing image from the central registry should return a non-zero exit code
|
||||
|
|
|
@ -270,7 +270,7 @@ func TestSaveSingleTag(t *testing.T) {
|
|||
func TestSaveImageId(t *testing.T) {
|
||||
repoName := "foobar-save-image-id-test"
|
||||
|
||||
tagCmdFinal := fmt.Sprintf("%v tag scratch:latest %v:latest", dockerBinary, repoName)
|
||||
tagCmdFinal := fmt.Sprintf("%v tag emptyfs:latest %v:latest", dockerBinary, repoName)
|
||||
tagCmd := exec.Command("bash", "-c", tagCmdFinal)
|
||||
if out, _, err := runCommandWithOutput(tagCmd); err != nil {
|
||||
t.Fatalf("failed to tag repo: %s, %v", out, err)
|
||||
|
@ -370,7 +370,7 @@ func TestSaveMultipleNames(t *testing.T) {
|
|||
repoName := "foobar-save-multi-name-test"
|
||||
|
||||
// Make one image
|
||||
tagCmdFinal := fmt.Sprintf("%v tag scratch:latest %v-one:latest", dockerBinary, repoName)
|
||||
tagCmdFinal := fmt.Sprintf("%v tag emptyfs:latest %v-one:latest", dockerBinary, repoName)
|
||||
tagCmd := exec.Command("bash", "-c", tagCmdFinal)
|
||||
if out, _, err := runCommandWithOutput(tagCmd); err != nil {
|
||||
t.Fatalf("failed to tag repo: %s, %v", out, err)
|
||||
|
@ -378,7 +378,7 @@ func TestSaveMultipleNames(t *testing.T) {
|
|||
defer deleteImages(repoName + "-one")
|
||||
|
||||
// Make two images
|
||||
tagCmdFinal = fmt.Sprintf("%v tag scratch:latest %v-two:latest", dockerBinary, repoName)
|
||||
tagCmdFinal = fmt.Sprintf("%v tag emptyfs:latest %v-two:latest", dockerBinary, repoName)
|
||||
tagCmd = exec.Command("bash", "-c", tagCmdFinal)
|
||||
if out, _, err := runCommandWithOutput(tagCmd); err != nil {
|
||||
t.Fatalf("failed to tag repo: %s, %v", out, err)
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
#!/bin/bash
|
||||
|
||||
if ! docker inspect scratch &> /dev/null; then
|
||||
# let's build a "docker save" tarball for "scratch"
|
||||
# let's build a "docker save" tarball for "emptyfs"
|
||||
# see https://github.com/docker/docker/pull/5262
|
||||
# and also https://github.com/docker/docker/issues/4242
|
||||
mkdir -p /docker-scratch
|
||||
(
|
||||
cd /docker-scratch
|
||||
echo '{"scratch":{"latest":"511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158"}}' > repositories
|
||||
echo '{"emptyfs":{"latest":"511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158"}}' > repositories
|
||||
mkdir -p 511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158
|
||||
(
|
||||
cd 511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158
|
||||
|
|
Loading…
Add table
Reference in a new issue