1package images // import "github.com/docker/docker/daemon/images"
2
3import (
4	"encoding/json"
5	"io"
6	"net/http"
7	"net/url"
8	"runtime"
9	"strings"
10	"time"
11
12	"github.com/docker/distribution/reference"
13	"github.com/docker/docker/api/types/container"
14	"github.com/docker/docker/builder/dockerfile"
15	"github.com/docker/docker/builder/remotecontext"
16	"github.com/docker/docker/dockerversion"
17	"github.com/docker/docker/errdefs"
18	"github.com/docker/docker/image"
19	"github.com/docker/docker/layer"
20	"github.com/docker/docker/pkg/archive"
21	"github.com/docker/docker/pkg/progress"
22	"github.com/docker/docker/pkg/streamformatter"
23	"github.com/pkg/errors"
24)
25
26// ImportImage imports an image, getting the archived layer data either from
27// inConfig (if src is "-"), or from a URI specified in src. Progress output is
28// written to outStream. Repository and tag names can optionally be given in
29// the repo and tag arguments, respectively.
30func (i *ImageService) ImportImage(src string, repository, os string, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error {
31	var (
32		rc     io.ReadCloser
33		resp   *http.Response
34		newRef reference.Named
35	)
36
37	// Default the operating system if not supplied.
38	if os == "" {
39		os = runtime.GOOS
40	}
41
42	if repository != "" {
43		var err error
44		newRef, err = reference.ParseNormalizedNamed(repository)
45		if err != nil {
46			return errdefs.InvalidParameter(err)
47		}
48		if _, isCanonical := newRef.(reference.Canonical); isCanonical {
49			return errdefs.InvalidParameter(errors.New("cannot import digest reference"))
50		}
51
52		if tag != "" {
53			newRef, err = reference.WithTag(newRef, tag)
54			if err != nil {
55				return errdefs.InvalidParameter(err)
56			}
57		}
58	}
59
60	config, err := dockerfile.BuildFromConfig(&container.Config{}, changes, os)
61	if err != nil {
62		return err
63	}
64	if src == "-" {
65		rc = inConfig
66	} else {
67		inConfig.Close()
68		if len(strings.Split(src, "://")) == 1 {
69			src = "http://" + src
70		}
71		u, err := url.Parse(src)
72		if err != nil {
73			return errdefs.InvalidParameter(err)
74		}
75
76		resp, err = remotecontext.GetWithStatusError(u.String())
77		if err != nil {
78			return err
79		}
80		outStream.Write(streamformatter.FormatStatus("", "Downloading from %s", u))
81		progressOutput := streamformatter.NewJSONProgressOutput(outStream, true)
82		rc = progress.NewProgressReader(resp.Body, progressOutput, resp.ContentLength, "", "Importing")
83	}
84
85	defer rc.Close()
86	if len(msg) == 0 {
87		msg = "Imported from " + src
88	}
89
90	inflatedLayerData, err := archive.DecompressStream(rc)
91	if err != nil {
92		return err
93	}
94	l, err := i.layerStores[os].Register(inflatedLayerData, "")
95	if err != nil {
96		return err
97	}
98	defer layer.ReleaseAndLog(i.layerStores[os], l)
99
100	created := time.Now().UTC()
101	imgConfig, err := json.Marshal(&image.Image{
102		V1Image: image.V1Image{
103			DockerVersion: dockerversion.Version,
104			Config:        config,
105			Architecture:  runtime.GOARCH,
106			OS:            os,
107			Created:       created,
108			Comment:       msg,
109		},
110		RootFS: &image.RootFS{
111			Type:    "layers",
112			DiffIDs: []layer.DiffID{l.DiffID()},
113		},
114		History: []image.History{{
115			Created: created,
116			Comment: msg,
117		}},
118	})
119	if err != nil {
120		return err
121	}
122
123	id, err := i.imageStore.Create(imgConfig)
124	if err != nil {
125		return err
126	}
127
128	// FIXME: connect with commit code and call refstore directly
129	if newRef != nil {
130		if err := i.TagImageWithReference(id, newRef); err != nil {
131			return err
132		}
133	}
134
135	i.LogImageEvent(id.String(), id.String(), "import")
136	outStream.Write(streamformatter.FormatStatus("", id.String()))
137	return nil
138}
139