mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Make FROM scratch
a special cased 'no-base' spec
There has been a lot of discussion (issues 4242 and 5262) about making `FROM scratch` either a special case or making `FROM` optional, implying starting from an empty file system. This patch makes the build command `FROM scratch` special cased from now on and if used does not pull/set the the initial layer of the build to the ancient image ID (511136ea..) but instead marks the build as having no base image. The next command in the dockerfile will create an image with a parent image ID of "". This means every image ever can now use one fewer layer! This also makes the image name `scratch` a reserved name by the TagStore. You will not be able to tag an image with this name from now on. If any users currently have an image tagged as `scratch`, they will still be able to use that image, but will not be able to tag a new image with that name. Goodbye '511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158', it was nice knowing you. Fixes #4242 Docker-DCO-1.1-Signed-off-by: Josh Hawn <josh.hawn@docker.com> (github: jlhawn)
This commit is contained in:
parent
364720b5e7
commit
8936789919
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")))
|
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))),
|
units.HumanDuration(time.Now().UTC().Sub(time.Unix(out.GetInt64("Created"), 0))),
|
||||||
out.Get("Status"), api.DisplayablePorts(ports), strings.Join(outNames, ","))
|
out.Get("Status"), api.DisplayablePorts(ports), strings.Join(outNames, ","))
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,12 @@ import (
|
||||||
"github.com/docker/docker/runconfig"
|
"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.
|
// dispatch with no layer / parsing. This is effectively not a command.
|
||||||
func nullDispatch(b *Builder, args []string, attributes map[string]bool, original string) error {
|
func nullDispatch(b *Builder, args []string, attributes map[string]bool, original string) error {
|
||||||
return nil
|
return nil
|
||||||
|
@ -115,6 +121,12 @@ func from(b *Builder, args []string, attributes map[string]bool, original string
|
||||||
|
|
||||||
name := args[0]
|
name := args[0]
|
||||||
|
|
||||||
|
if name == NoBaseImageSpecifier {
|
||||||
|
b.image = ""
|
||||||
|
b.noBaseImage = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
image, err := b.Daemon.Repositories().LookupImage(name)
|
image, err := b.Daemon.Repositories().LookupImage(name)
|
||||||
if b.Pull {
|
if b.Pull {
|
||||||
image, err = b.pullImage(name)
|
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
|
// RUN [ "echo", "hi" ] # echo hi
|
||||||
//
|
//
|
||||||
func run(b *Builder, args []string, attributes map[string]bool, original string) error {
|
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")
|
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
|
cmdSet bool // indicates is CMD was set in current Dockerfile
|
||||||
context tarsum.TarSum // the context is a tarball that is uploaded by the client
|
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)
|
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
|
// 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 {
|
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")
|
return fmt.Errorf("Please provide a source image with `from` prior to commit")
|
||||||
}
|
}
|
||||||
b.Config.Image = b.image
|
b.Config.Image = b.image
|
||||||
|
@ -513,7 +513,7 @@ func (b *Builder) probeCache() (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Builder) create() (*daemon.Container, 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")
|
return nil, fmt.Errorf("Please provide a source image with `from` prior to run")
|
||||||
}
|
}
|
||||||
b.Config.Image = b.image
|
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
|
// Create a new image from the container's base layers + a new layer from container changes
|
||||||
var (
|
var (
|
||||||
containerID, containerImage string
|
containerID, parentImageID string
|
||||||
containerConfig *runconfig.Config
|
containerConfig *runconfig.Config
|
||||||
)
|
)
|
||||||
|
|
||||||
if container != nil {
|
if container != nil {
|
||||||
containerID = container.ID
|
containerID = container.ID
|
||||||
containerImage = container.Image
|
parentImageID = container.ImageID
|
||||||
containerConfig = container.Config
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,8 +62,8 @@ type Container struct {
|
||||||
Path string
|
Path string
|
||||||
Args []string
|
Args []string
|
||||||
|
|
||||||
Config *runconfig.Config
|
Config *runconfig.Config
|
||||||
Image string
|
ImageID string `json:"Image"`
|
||||||
|
|
||||||
NetworkSettings *NetworkSettings
|
NetworkSettings *NetworkSettings
|
||||||
|
|
||||||
|
@ -186,7 +186,7 @@ func (container *Container) WriteHostConfig() error {
|
||||||
|
|
||||||
func (container *Container) LogEvent(action string) {
|
func (container *Container) LogEvent(action string) {
|
||||||
d := container.daemon
|
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)
|
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 {
|
if container.daemon == nil {
|
||||||
return nil, fmt.Errorf("Can't get image of unregistered container")
|
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 {
|
func (container *Container) Unmount() error {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
|
|
||||||
"github.com/docker/docker/engine"
|
"github.com/docker/docker/engine"
|
||||||
"github.com/docker/docker/graph"
|
"github.com/docker/docker/graph"
|
||||||
|
"github.com/docker/docker/image"
|
||||||
"github.com/docker/docker/pkg/parsers"
|
"github.com/docker/docker/pkg/parsers"
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
"github.com/docker/libcontainer/label"
|
"github.com/docker/libcontainer/label"
|
||||||
|
@ -68,15 +69,22 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos
|
||||||
var (
|
var (
|
||||||
container *Container
|
container *Container
|
||||||
warnings []string
|
warnings []string
|
||||||
|
img *image.Image
|
||||||
|
imgID string
|
||||||
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
img, err := daemon.repositories.LookupImage(config.Image)
|
if config.Image != "" {
|
||||||
if err != nil {
|
img, err = daemon.repositories.LookupImage(config.Image)
|
||||||
return nil, nil, err
|
if err != nil {
|
||||||
}
|
return nil, nil, err
|
||||||
if err := img.CheckDepth(); err != nil {
|
}
|
||||||
return nil, nil, err
|
if err = img.CheckDepth(); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
imgID = img.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
if warnings, err = daemon.mergeAndVerifyConfig(config, img); err != nil {
|
if warnings, err = daemon.mergeAndVerifyConfig(config, img); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -86,13 +94,13 @@ func (daemon *Daemon) Create(config *runconfig.Config, hostConfig *runconfig.Hos
|
||||||
return nil, nil, err
|
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
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if err := daemon.Register(container); err != nil {
|
if err := daemon.Register(container); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if err := daemon.createRootfs(container, img); err != nil {
|
if err := daemon.createRootfs(container); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
if hostConfig != nil {
|
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) {
|
func (daemon *Daemon) mergeAndVerifyConfig(config *runconfig.Config, img *image.Image) ([]string, error) {
|
||||||
warnings := []string{}
|
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.")
|
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 {
|
if err := runconfig.Merge(config, img.Config); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -557,7 +557,7 @@ func parseSecurityOpt(container *Container, config *runconfig.HostConfig) error
|
||||||
return err
|
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 (
|
var (
|
||||||
id string
|
id string
|
||||||
err error
|
err error
|
||||||
|
@ -578,7 +578,7 @@ func (daemon *Daemon) newContainer(name string, config *runconfig.Config, img *i
|
||||||
Args: args, //FIXME: de-duplicate from config
|
Args: args, //FIXME: de-duplicate from config
|
||||||
Config: config,
|
Config: config,
|
||||||
hostConfig: &runconfig.HostConfig{},
|
hostConfig: &runconfig.HostConfig{},
|
||||||
Image: img.ID, // Always use the resolved image id
|
ImageID: imgID,
|
||||||
NetworkSettings: &NetworkSettings{},
|
NetworkSettings: &NetworkSettings{},
|
||||||
Name: name,
|
Name: name,
|
||||||
Driver: daemon.driver.String(),
|
Driver: daemon.driver.String(),
|
||||||
|
@ -590,14 +590,14 @@ func (daemon *Daemon) newContainer(name string, config *runconfig.Config, img *i
|
||||||
return container, err
|
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.
|
// Step 1: create the container directory.
|
||||||
// This doubles as a barrier to avoid race conditions.
|
// This doubles as a barrier to avoid race conditions.
|
||||||
if err := os.Mkdir(container.root, 0700); err != nil {
|
if err := os.Mkdir(container.root, 0700); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
initID := fmt.Sprintf("%s-init", container.ID)
|
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
|
return err
|
||||||
}
|
}
|
||||||
initPath, err := daemon.driver.Get(initID, "")
|
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 {
|
func (daemon *Daemon) canDeleteImage(imgID string, force bool) error {
|
||||||
for _, container := range daemon.List() {
|
for _, container := range daemon.List() {
|
||||||
parent, err := daemon.Repositories().LookupImage(container.Image)
|
parent, err := daemon.Repositories().LookupImage(container.ImageID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if daemon.Graph().IsNotExist(err) {
|
if daemon.Graph().IsNotExist(err) {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -35,7 +35,7 @@ func (daemon *Daemon) ContainerInspect(job *engine.Job) engine.Status {
|
||||||
out.SetList("Args", container.Args)
|
out.SetList("Args", container.Args)
|
||||||
out.SetJson("Config", container.Config)
|
out.SetJson("Config", container.Config)
|
||||||
out.SetJson("State", container.State)
|
out.SetJson("State", container.State)
|
||||||
out.SetJson("Image", container.Image)
|
out.Set("Image", container.ImageID)
|
||||||
out.SetJson("NetworkSettings", container.NetworkSettings)
|
out.SetJson("NetworkSettings", container.NetworkSettings)
|
||||||
out.Set("ResolvConfPath", container.ResolvConfPath)
|
out.Set("ResolvConfPath", container.ResolvConfPath)
|
||||||
out.Set("HostnamePath", container.HostnamePath)
|
out.Set("HostnamePath", container.HostnamePath)
|
||||||
|
|
|
@ -116,7 +116,7 @@ func (daemon *Daemon) Containers(job *engine.Job) engine.Status {
|
||||||
out := &engine.Env{}
|
out := &engine.Env{}
|
||||||
out.SetJson("Id", container.ID)
|
out.SetJson("Id", container.ID)
|
||||||
out.SetList("Names", names[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 {
|
if len(container.Args) > 0 {
|
||||||
args := []string{}
|
args := []string{}
|
||||||
for _, arg := range container.Args {
|
for _, arg := range container.Args {
|
||||||
|
|
|
@ -298,6 +298,9 @@ func validateRepoName(name string) error {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return fmt.Errorf("Repository name can't be empty")
|
return fmt.Errorf("Repository name can't be empty")
|
||||||
}
|
}
|
||||||
|
if name == "scratch" {
|
||||||
|
return fmt.Errorf("'scratch' is a reserved name")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -230,9 +230,9 @@ func TestEventsRedirectStdout(t *testing.T) {
|
||||||
|
|
||||||
func TestEventsImagePull(t *testing.T) {
|
func TestEventsImagePull(t *testing.T) {
|
||||||
since := time.Now().Unix()
|
since := time.Now().Unix()
|
||||||
pullCmd := exec.Command(dockerBinary, "pull", "scratch")
|
pullCmd := exec.Command(dockerBinary, "pull", "hello-world")
|
||||||
if out, _, err := runCommandWithOutput(pullCmd); err != nil {
|
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",
|
eventsCmd := exec.Command(dockerBinary, "events",
|
||||||
|
@ -243,7 +243,7 @@ func TestEventsImagePull(t *testing.T) {
|
||||||
events := strings.Split(strings.TrimSpace(out), "\n")
|
events := strings.Split(strings.TrimSpace(out), "\n")
|
||||||
event := strings.TrimSpace(events[len(events)-1])
|
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)
|
t.Fatalf("Missing pull event - got:%q", event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestInspectImage(t *testing.T) {
|
func TestInspectImage(t *testing.T) {
|
||||||
imageTest := "scratch"
|
imageTest := "emptyfs"
|
||||||
imageTestID := "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158"
|
imageTestID := "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158"
|
||||||
imagesCmd := exec.Command(dockerBinary, "inspect", "--format='{{.Id}}'", imageTest)
|
imagesCmd := exec.Command(dockerBinary, "inspect", "--format='{{.Id}}'", imageTest)
|
||||||
out, exitCode, err := runCommandWithOutput(imagesCmd)
|
out, exitCode, err := runCommandWithOutput(imagesCmd)
|
||||||
|
|
|
@ -9,11 +9,11 @@ import (
|
||||||
|
|
||||||
// pulling an image from the central registry should work
|
// pulling an image from the central registry should work
|
||||||
func TestPullImageFromCentralRegistry(t *testing.T) {
|
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 {
|
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
|
// 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) {
|
func TestSaveImageId(t *testing.T) {
|
||||||
repoName := "foobar-save-image-id-test"
|
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)
|
tagCmd := exec.Command("bash", "-c", tagCmdFinal)
|
||||||
if out, _, err := runCommandWithOutput(tagCmd); err != nil {
|
if out, _, err := runCommandWithOutput(tagCmd); err != nil {
|
||||||
t.Fatalf("failed to tag repo: %s, %v", out, err)
|
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"
|
repoName := "foobar-save-multi-name-test"
|
||||||
|
|
||||||
// Make one image
|
// 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)
|
tagCmd := exec.Command("bash", "-c", tagCmdFinal)
|
||||||
if out, _, err := runCommandWithOutput(tagCmd); err != nil {
|
if out, _, err := runCommandWithOutput(tagCmd); err != nil {
|
||||||
t.Fatalf("failed to tag repo: %s, %v", out, err)
|
t.Fatalf("failed to tag repo: %s, %v", out, err)
|
||||||
|
@ -378,7 +378,7 @@ func TestSaveMultipleNames(t *testing.T) {
|
||||||
defer deleteImages(repoName + "-one")
|
defer deleteImages(repoName + "-one")
|
||||||
|
|
||||||
// Make two images
|
// 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)
|
tagCmd = exec.Command("bash", "-c", tagCmdFinal)
|
||||||
if out, _, err := runCommandWithOutput(tagCmd); err != nil {
|
if out, _, err := runCommandWithOutput(tagCmd); err != nil {
|
||||||
t.Fatalf("failed to tag repo: %s, %v", out, err)
|
t.Fatalf("failed to tag repo: %s, %v", out, err)
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
if ! docker inspect scratch &> /dev/null; then
|
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
|
# see https://github.com/docker/docker/pull/5262
|
||||||
# and also https://github.com/docker/docker/issues/4242
|
# and also https://github.com/docker/docker/issues/4242
|
||||||
mkdir -p /docker-scratch
|
mkdir -p /docker-scratch
|
||||||
(
|
(
|
||||||
cd /docker-scratch
|
cd /docker-scratch
|
||||||
echo '{"scratch":{"latest":"511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158"}}' > repositories
|
echo '{"emptyfs":{"latest":"511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158"}}' > repositories
|
||||||
mkdir -p 511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158
|
mkdir -p 511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158
|
||||||
(
|
(
|
||||||
cd 511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158
|
cd 511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue