Implemented new version of PullRepository. Missing support for whole repository pull (= no tag specified)
This commit is contained in:
parent
2421838b0a
commit
2f082510a7
2 changed files with 144 additions and 35 deletions
|
@ -553,6 +553,8 @@ func (srv *Server) CmdPush(stdin io.ReadCloser, stdout rcli.DockerConn, args ...
|
||||||
|
|
||||||
func (srv *Server) CmdPull(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
func (srv *Server) CmdPull(stdin io.ReadCloser, stdout io.Writer, args ...string) error {
|
||||||
cmd := rcli.Subcmd(stdout, "pull", "NAME", "Pull an image or a repository from the registry")
|
cmd := rcli.Subcmd(stdout, "pull", "NAME", "Pull an image or a repository from the registry")
|
||||||
|
tag := cmd.String("t", "", "Download tagged image in repository")
|
||||||
|
registry := cmd.String("registry", "", "Registry to download from. Necessary if image is pulled by ID")
|
||||||
if err := cmd.Parse(args); err != nil {
|
if err := cmd.Parse(args); err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -563,14 +565,14 @@ func (srv *Server) CmdPull(stdin io.ReadCloser, stdout io.Writer, args ...string
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: CmdPull should be a wrapper around Runtime.Pull()
|
// FIXME: CmdPull should be a wrapper around Runtime.Pull()
|
||||||
if srv.runtime.graph.LookupRemoteImage(remote, srv.runtime.authConfig) {
|
if *registry != "" {
|
||||||
if err := srv.runtime.graph.PullImage(stdout, remote, srv.runtime.authConfig); err != nil {
|
if err := srv.runtime.graph.PullImage(stdout, remote, *registry, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// FIXME: Allow pull repo:tag
|
// FIXME: Allow pull repo:tag
|
||||||
if err := srv.runtime.graph.PullRepository(stdout, remote, "", srv.runtime.repositories, srv.runtime.authConfig); err != nil {
|
if err := srv.runtime.graph.PullRepository(stdout, remote, *tag, srv.runtime.repositories, srv.runtime.authConfig); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
171
registry.go
171
registry.go
|
@ -48,14 +48,15 @@ func NewMultipleImgJson(src []byte) ([]*Image, error) {
|
||||||
|
|
||||||
// Retrieve the history of a given image from the Registry.
|
// Retrieve the history of a given image from the Registry.
|
||||||
// Return a list of the parent's json (requested image included)
|
// Return a list of the parent's json (requested image included)
|
||||||
func (graph *Graph) getRemoteHistory(imgId, registry string, authConfig *auth.AuthConfig) ([]*Image, error) {
|
func (graph *Graph) getRemoteHistory(imgId, registry string, token []string) ([]*Image, error) {
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", registry+"/images/"+imgId+"/ancestry", nil)
|
req, err := http.NewRequest("GET", registry+"/images/"+imgId+"/ancestry", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
req.SetBasicAuth(authConfig.Username, authConfig.Password)
|
req.Header["X-Docker-Token"] = token
|
||||||
|
// req.SetBasicAuth(authConfig.Username, authConfig.Password)
|
||||||
res, err := client.Do(req)
|
res, err := client.Do(req)
|
||||||
if err != nil || res.StatusCode != 200 {
|
if err != nil || res.StatusCode != 200 {
|
||||||
if res != nil {
|
if res != nil {
|
||||||
|
@ -92,7 +93,7 @@ func (graph *Graph) LookupRemoteImage(imgId, registry string, authConfig *auth.A
|
||||||
|
|
||||||
// Retrieve an image from the Registry.
|
// Retrieve an image from the Registry.
|
||||||
// Returns the Image object as well as the layer as an Archive (io.Reader)
|
// Returns the Image object as well as the layer as an Archive (io.Reader)
|
||||||
func (graph *Graph) getRemoteImage(stdout io.Writer, imgId, registry string, authConfig *auth.AuthConfig) (*Image, Archive, error) {
|
func (graph *Graph) getRemoteImage(stdout io.Writer, imgId, registry string, token []string) (*Image, Archive, error) {
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
|
|
||||||
fmt.Fprintf(stdout, "Pulling %s metadata\r\n", imgId)
|
fmt.Fprintf(stdout, "Pulling %s metadata\r\n", imgId)
|
||||||
|
@ -101,7 +102,8 @@ func (graph *Graph) getRemoteImage(stdout io.Writer, imgId, registry string, aut
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("Failed to download json: %s", err)
|
return nil, nil, fmt.Errorf("Failed to download json: %s", err)
|
||||||
}
|
}
|
||||||
req.SetBasicAuth(authConfig.Username, authConfig.Password)
|
req.Header["X-Docker-Token"] = token
|
||||||
|
// req.SetBasicAuth(authConfig.Username, authConfig.Password)
|
||||||
res, err := client.Do(req)
|
res, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("Failed to download json: %s", err)
|
return nil, nil, fmt.Errorf("Failed to download json: %s", err)
|
||||||
|
@ -128,7 +130,8 @@ func (graph *Graph) getRemoteImage(stdout io.Writer, imgId, registry string, aut
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("Error while getting from the server: %s\n", err)
|
return nil, nil, fmt.Errorf("Error while getting from the server: %s\n", err)
|
||||||
}
|
}
|
||||||
req.SetBasicAuth(authConfig.Username, authConfig.Password)
|
req.Header["X-Docker-Token"] = token
|
||||||
|
// req.SetBasicAuth(authConfig.Username, authConfig.Password)
|
||||||
res, err = client.Do(req)
|
res, err = client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -136,16 +139,16 @@ func (graph *Graph) getRemoteImage(stdout io.Writer, imgId, registry string, aut
|
||||||
return img, ProgressReader(res.Body, int(res.ContentLength), stdout, "Downloading %v/%v (%v)"), nil
|
return img, ProgressReader(res.Body, int(res.ContentLength), stdout, "Downloading %v/%v (%v)"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (graph *Graph) PullImage(stdout io.Writer, imgId, registry string, authConfig *auth.AuthConfig) error {
|
func (graph *Graph) PullImage(stdout io.Writer, imgId, registry string, token []string) error {
|
||||||
history, err := graph.getRemoteHistory(imgId, authConfig)
|
history, err := graph.getRemoteHistory(imgId, registry, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// FIXME: Try to stream the images?
|
// FIXME: Try to stream the images?
|
||||||
// FIXME: Lunch the getRemoteImage() in goroutines
|
// FIXME: Launch the getRemoteImage() in goroutines
|
||||||
for _, j := range history {
|
for _, j := range history {
|
||||||
if !graph.Exists(j.Id) {
|
if !graph.Exists(j.Id) {
|
||||||
img, layer, err := graph.getRemoteImage(stdout, j.Id, registry, authConfig)
|
img, layer, err := graph.getRemoteImage(stdout, j.Id, registry, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// FIXME: Keep goging in case of error?
|
// FIXME: Keep goging in case of error?
|
||||||
return err
|
return err
|
||||||
|
@ -158,58 +161,162 @@ func (graph *Graph) PullImage(stdout io.Writer, imgId, registry string, authConf
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Handle the askedTag parameter
|
// // FIXME: Handle the askedTag parameter
|
||||||
func (graph *Graph) PullRepository(stdout io.Writer, remote, askedTag, registry string, repositories *TagStore, authConfig *auth.AuthConfig) error {
|
// func (graph *Graph) PullRepository(stdout io.Writer, remote, askedTag, registry string, repositories *TagStore, authConfig *auth.AuthConfig) error {
|
||||||
|
// client := &http.Client{}
|
||||||
|
|
||||||
|
// fmt.Fprintf(stdout, "Pulling repository %s\r\n", remote)
|
||||||
|
|
||||||
|
// var repositoryTarget string
|
||||||
|
// // If we are asking for 'root' repository, lookup on the Library's registry
|
||||||
|
// if strings.Index(remote, "/") == -1 {
|
||||||
|
// repositoryTarget = registry + "/library/" + remote
|
||||||
|
// } else {
|
||||||
|
// repositoryTarget = registry + "/users/" + remote
|
||||||
|
// }
|
||||||
|
|
||||||
|
// req, err := http.NewRequest("GET", repositoryTarget, nil)
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// req.SetBasicAuth(authConfig.Username, authConfig.Password)
|
||||||
|
// res, err := client.Do(req)
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// defer res.Body.Close()
|
||||||
|
// if res.StatusCode != 200 {
|
||||||
|
// return fmt.Errorf("HTTP code: %d", res.StatusCode)
|
||||||
|
// }
|
||||||
|
// rawJson, err := ioutil.ReadAll(res.Body)
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// t := map[string]string{}
|
||||||
|
// if err = json.Unmarshal(rawJson, &t); err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// for tag, rev := range t {
|
||||||
|
// fmt.Fprintf(stdout, "Pulling tag %s:%s\r\n", remote, tag)
|
||||||
|
// if err = graph.PullImage(stdout, rev, registry, authConfig); err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// if err = repositories.Set(remote, tag, rev, true); err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if err = repositories.Save(); err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (graph *Graph) PullRepository(stdout io.Writer, remote, askedTag string, repositories *TagStore, authConfig *auth.AuthConfig) error {
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
|
|
||||||
fmt.Fprintf(stdout, "Pulling repository %s\r\n", remote)
|
fmt.Fprintf(stdout, "Pulling repository %s\r\n", remote)
|
||||||
|
|
||||||
var repositoryTarget string
|
var repositoryTarget string
|
||||||
// If we are asking for 'root' repository, lookup on the Library's registry
|
// If we are asking for 'root' repository, lookup on the Library's registry
|
||||||
if strings.Index(remote, "/") == -1 {
|
if strings.Index(remote, "/") == -1 {
|
||||||
repositoryTarget = registry + "/library/" + remote
|
repositoryTarget = INDEX_ENDPOINT + "/repositories/library/" + remote + "/checksums"
|
||||||
} else {
|
} else {
|
||||||
repositoryTarget = registry + "/users/" + remote
|
repositoryTarget = INDEX_ENDPOINT + "/repositories/" + remote + "/checksums"
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", repositoryTarget, nil)
|
req, err := http.NewRequest("GET", repositoryTarget, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
req.SetBasicAuth(authConfig.Username, authConfig.Password)
|
if authConfig != nil {
|
||||||
|
req.SetBasicAuth(authConfig.Username, authConfig.Password)
|
||||||
|
}
|
||||||
|
|
||||||
res, err := client.Do(req)
|
res, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer res.Body.Close()
|
||||||
|
// TODO: Right now we're ignoring checksums in the response body.
|
||||||
|
// In the future, we need to use them to check image validity.
|
||||||
if res.StatusCode != 200 {
|
if res.StatusCode != 200 {
|
||||||
return fmt.Errorf("HTTP code: %d", res.StatusCode)
|
return fmt.Errorf("HTTP code: %d", res.StatusCode)
|
||||||
}
|
}
|
||||||
rawJson, err := ioutil.ReadAll(res.Body)
|
|
||||||
if err != nil {
|
var token, endpoints []string
|
||||||
return err
|
if res.Header.Get("X-Docker-Token") != "" {
|
||||||
|
token = res.Header["X-Docker-Token"]
|
||||||
}
|
}
|
||||||
t := map[string]string{}
|
if res.Header.Get("X-Docker-Endpoints") != "" {
|
||||||
if err = json.Unmarshal(rawJson, &t); err != nil {
|
endpoints = res.Header["X-Docker-Endpoints"]
|
||||||
return err
|
} else {
|
||||||
|
return fmt.Errorf("Index response didn't contain any endpoints")
|
||||||
}
|
}
|
||||||
for tag, rev := range t {
|
|
||||||
fmt.Fprintf(stdout, "Pulling tag %s:%s\r\n", remote, tag)
|
// FIXME: If askedTag is empty, fetch all tags.
|
||||||
if err = graph.PullImage(stdout, rev, registry, authConfig); err != nil {
|
if askedTag == "" {
|
||||||
|
askedTag = "latest"
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, registry := range endpoints {
|
||||||
|
registryEndpoint := "https://" + registry + "/v1"
|
||||||
|
if strings.Index(remote, "/") == -1 {
|
||||||
|
repositoryTarget = registryEndpoint + "/repositories/library/" +
|
||||||
|
remote + "/tags/" + askedTag
|
||||||
|
} else {
|
||||||
|
repositoryTarget = registryEndpoint + "/repositories/users/" +
|
||||||
|
remote + "/tags/" + askedTag
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err = http.NewRequest("GET", repositoryTarget, nil)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = repositories.Set(remote, tag, rev, true); err != nil {
|
req.Header["X-Docker-Token"] = token
|
||||||
|
res, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(stdout, "Error while retrieving repository info: %v ; " +
|
||||||
|
"checking next endpoint")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
if res.StatusCode == 403 {
|
||||||
|
if authConfig == nil {
|
||||||
|
return fmt.Errorf("You need to be authenticated to access this resource")
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("You aren't authorized to access this resource")
|
||||||
|
}
|
||||||
|
} else if res.StatusCode != 200 {
|
||||||
|
return fmt.Errorf("HTTP code: %d", res.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
var imgId string
|
||||||
|
rawJson, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err = json.Unmarshal(rawJson, &imgId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := graph.PullImage(stdout, imgId, registryEndpoint, token); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = repositories.Set(remote, askedTag, imgId, true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = repositories.Save(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
if err = repositories.Save(); err != nil {
|
return fmt.Errorf("Could not find repository on any of the indexed registries.")
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push a local image to the registry with its history if needed
|
// Push a local image to the registry with its history if needed
|
||||||
func (graph *Graph) PushImage(stdout io.Writer, imgOrig *Image, authConfig *auth.AuthConfig) error {
|
func (graph *Graph) PushImage(stdout io.Writer, imgOrig *Image, registry string, authConfig *auth.AuthConfig) error {
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
|
|
||||||
// FIXME: Factorize the code
|
// FIXME: Factorize the code
|
||||||
|
@ -225,7 +332,7 @@ func (graph *Graph) PushImage(stdout io.Writer, imgOrig *Image, authConfig *auth
|
||||||
|
|
||||||
// FIXME: try json with UTF8
|
// FIXME: try json with UTF8
|
||||||
jsonData := strings.NewReader(string(jsonRaw))
|
jsonData := strings.NewReader(string(jsonRaw))
|
||||||
req, err := http.NewRequest("PUT", REGISTRY_ENDPOINT+"/images/"+img.Id+"/json", jsonData)
|
req, err := http.NewRequest("PUT", registry+"/images/"+img.Id+"/json", jsonData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -252,7 +359,7 @@ func (graph *Graph) PushImage(stdout io.Writer, imgOrig *Image, authConfig *auth
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(stdout, "Pushing %s fs layer\r\n", img.Id)
|
fmt.Fprintf(stdout, "Pushing %s fs layer\r\n", img.Id)
|
||||||
req2, err := http.NewRequest("PUT", REGISTRY_ENDPOINT+"/images/"+img.Id+"/layer", nil)
|
req2, err := http.NewRequest("PUT", registry+"/images/"+img.Id+"/layer", nil)
|
||||||
req2.SetBasicAuth(authConfig.Username, authConfig.Password)
|
req2.SetBasicAuth(authConfig.Username, authConfig.Password)
|
||||||
res2, err := client.Do(req2)
|
res2, err := client.Do(req2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Add table
Reference in a new issue