func (fetcher *DockerRepositoryFetcher) fetchFromEndpoint(endpoint string, imgID string, token []string) error { history, err := fetcher.registry.GetRemoteHistory(imgID, endpoint, token) if err != nil { return err } for i := len(history) - 1; i >= 0; i-- { id := history[i] if fetcher.graph.Exists(id) { log.Println("already exists:", id) continue } imgJSON, _, err := fetcher.registry.GetRemoteImageJSON(id, endpoint, token) if err != nil { return err } img, err := image.NewImgJSON(imgJSON) if err != nil { return err } layer, err := fetcher.registry.GetRemoteImageLayer(img.ID, endpoint, token) if err != nil { return err } defer layer.Close() log.Println("downloading layer:", id) err = fetcher.graph.Register(imgJSON, layer, img) if err != nil { return err } } return nil }
// CmdSet stores a new image in the graph. // Images are stored in the graph using 4 elements: // - A user-defined ID // - A collection of metadata describing the image // - A directory tree stored as a tar archive (also called the "layer") // - A reference to a "parent" ID on top of which the layer should be applied // // NOTE: even though the parent ID is only useful in relation to the layer and how // to apply it (ie you could represent the full directory tree as 'parent_layer + layer', // it is treated as a top-level property of the image. This is an artifact of early // design and should probably be cleaned up in the future to simplify the design. // // Syntax: image_set ID // Input: // - Layer content must be streamed in tar format on stdin. An empty input is // valid and represents a nil layer. // // - Image metadata must be passed in the command environment. // 'json': a json-encoded object with all image metadata. // It will be stored as-is, without any encoding/decoding artifacts. // That is a requirement of the current registry client implementation, // because a re-encoded json might invalidate the image checksum at // the next upload, even with functionaly identical content. func (s *TagStore) CmdSet(job *engine.Job) engine.Status { if len(job.Args) != 1 { return job.Errorf("usage: %s NAME", job.Name) } var ( imgJSON = []byte(job.Getenv("json")) layer = job.Stdin ) if len(imgJSON) == 0 { return job.Errorf("mandatory key 'json' is not set") } // We have to pass an *image.Image object, even though it will be completely // ignored in favor of the redundant json data. // FIXME: the current prototype of Graph.Register is stupid and redundant. img, err := image.NewImgJSON(imgJSON) if err != nil { return job.Error(err) } if err := s.graph.Register(imgJSON, layer, img); err != nil { return job.Error(err) } return engine.StatusOK }