github.com/vieux/docker@v0.6.3-0.20161004191708-e097c2a938c7/plugin/distribution/pull.go (about)

     1  // +build experimental
     2  
     3  package distribution
     4  
     5  import (
     6  	"encoding/json"
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"os"
    12  	"path/filepath"
    13  
    14  	"github.com/Sirupsen/logrus"
    15  	"github.com/docker/distribution"
    16  	"github.com/docker/distribution/manifest/schema2"
    17  	"github.com/docker/docker/api/types"
    18  	dockerdist "github.com/docker/docker/distribution"
    19  	archive "github.com/docker/docker/pkg/chrootarchive"
    20  	"github.com/docker/docker/reference"
    21  	"github.com/docker/docker/registry"
    22  	"golang.org/x/net/context"
    23  )
    24  
    25  // PullData is the plugin manifest and the rootfs
    26  type PullData interface {
    27  	Config() ([]byte, error)
    28  	Layer() (io.ReadCloser, error)
    29  }
    30  
    31  type pullData struct {
    32  	repository distribution.Repository
    33  	manifest   schema2.Manifest
    34  	index      int
    35  }
    36  
    37  func (pd *pullData) Config() ([]byte, error) {
    38  	blobs := pd.repository.Blobs(context.Background())
    39  	config, err := blobs.Get(context.Background(), pd.manifest.Config.Digest)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  	// validate
    44  	var p types.Plugin
    45  	if err := json.Unmarshal(config, &p); err != nil {
    46  		return nil, err
    47  	}
    48  	return config, nil
    49  }
    50  
    51  func (pd *pullData) Layer() (io.ReadCloser, error) {
    52  	if pd.index >= len(pd.manifest.Layers) {
    53  		return nil, io.EOF
    54  	}
    55  
    56  	blobs := pd.repository.Blobs(context.Background())
    57  	rsc, err := blobs.Open(context.Background(), pd.manifest.Layers[pd.index].Digest)
    58  	if err != nil {
    59  		return nil, err
    60  	}
    61  	pd.index++
    62  	return rsc, nil
    63  }
    64  
    65  // GetRef returns the distribution reference for a given name.
    66  func GetRef(name string) (reference.Named, error) {
    67  	ref, err := reference.ParseNamed(name)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  	return ref, nil
    72  }
    73  
    74  // GetTag returns the tag associated with the given reference name.
    75  func GetTag(ref reference.Named) string {
    76  	tag := DefaultTag
    77  	if ref, ok := ref.(reference.NamedTagged); ok {
    78  		tag = ref.Tag()
    79  	}
    80  	return tag
    81  }
    82  
    83  // Pull downloads the plugin from Store
    84  func Pull(ref reference.Named, rs registry.Service, metaheader http.Header, authConfig *types.AuthConfig) (PullData, error) {
    85  	repoInfo, err := rs.ResolveRepository(ref)
    86  	if err != nil {
    87  		logrus.Debugf("pull.go: error in ResolveRepository: %v", err)
    88  		return nil, err
    89  	}
    90  
    91  	if err := dockerdist.ValidateRepoName(repoInfo.Name()); err != nil {
    92  		logrus.Debugf("pull.go: error in ValidateRepoName: %v", err)
    93  		return nil, err
    94  	}
    95  
    96  	endpoints, err := rs.LookupPullEndpoints(repoInfo.Hostname())
    97  	if err != nil {
    98  		logrus.Debugf("pull.go: error in LookupPullEndpoints: %v", err)
    99  		return nil, err
   100  	}
   101  
   102  	var confirmedV2 bool
   103  	var repository distribution.Repository
   104  
   105  	for _, endpoint := range endpoints {
   106  		if confirmedV2 && endpoint.Version == registry.APIVersion1 {
   107  			logrus.Debugf("Skipping v1 endpoint %s because v2 registry was detected", endpoint.URL)
   108  			continue
   109  		}
   110  
   111  		// TODO: reuse contexts
   112  		repository, confirmedV2, err = dockerdist.NewV2Repository(context.Background(), repoInfo, endpoint, metaheader, authConfig, "pull")
   113  		if err != nil {
   114  			logrus.Debugf("pull.go: error in NewV2Repository: %v", err)
   115  			return nil, err
   116  		}
   117  		if !confirmedV2 {
   118  			logrus.Debugf("pull.go: !confirmedV2")
   119  			return nil, ErrUnsupportedRegistry
   120  		}
   121  		logrus.Debugf("Trying to pull %s from %s %s", repoInfo.Name(), endpoint.URL, endpoint.Version)
   122  		break
   123  	}
   124  
   125  	tag := DefaultTag
   126  	if ref, ok := ref.(reference.NamedTagged); ok {
   127  		tag = ref.Tag()
   128  	}
   129  
   130  	// tags := repository.Tags(context.Background())
   131  	// desc, err := tags.Get(context.Background(), tag)
   132  	// 	if err != nil {
   133  	// 		return nil, err
   134  	// 	}
   135  	//
   136  	msv, err := repository.Manifests(context.Background())
   137  	if err != nil {
   138  		logrus.Debugf("pull.go: error in repository.Manifests: %v", err)
   139  		return nil, err
   140  	}
   141  	manifest, err := msv.Get(context.Background(), "", distribution.WithTag(tag))
   142  	if err != nil {
   143  		// TODO: change 401 to 404
   144  		logrus.Debugf("pull.go: error in msv.Get(): %v", err)
   145  		return nil, err
   146  	}
   147  
   148  	_, pl, err := manifest.Payload()
   149  	if err != nil {
   150  		logrus.Debugf("pull.go: error in manifest.Payload(): %v", err)
   151  		return nil, err
   152  	}
   153  	var m schema2.Manifest
   154  	if err := json.Unmarshal(pl, &m); err != nil {
   155  		logrus.Debugf("pull.go: error in json.Unmarshal(): %v", err)
   156  		return nil, err
   157  	}
   158  	if m.Config.MediaType != schema2.MediaTypePluginConfig {
   159  		return nil, ErrUnsupportedMediaType
   160  	}
   161  
   162  	pd := &pullData{
   163  		repository: repository,
   164  		manifest:   m,
   165  	}
   166  
   167  	logrus.Debugf("manifest: %s", pl)
   168  	return pd, nil
   169  }
   170  
   171  // WritePullData extracts manifest and rootfs to the disk.
   172  func WritePullData(pd PullData, dest string, extract bool) error {
   173  	config, err := pd.Config()
   174  	if err != nil {
   175  		return err
   176  	}
   177  	var p types.Plugin
   178  	if err := json.Unmarshal(config, &p); err != nil {
   179  		return err
   180  	}
   181  	logrus.Debugf("%#v", p)
   182  
   183  	if err := os.MkdirAll(dest, 0700); err != nil {
   184  		return err
   185  	}
   186  
   187  	if extract {
   188  		if err := ioutil.WriteFile(filepath.Join(dest, "manifest.json"), config, 0600); err != nil {
   189  			return err
   190  		}
   191  
   192  		if err := os.MkdirAll(filepath.Join(dest, "rootfs"), 0700); err != nil {
   193  			return err
   194  		}
   195  	}
   196  
   197  	for i := 0; ; i++ {
   198  		l, err := pd.Layer()
   199  		if err == io.EOF {
   200  			break
   201  		}
   202  		if err != nil {
   203  			return err
   204  		}
   205  
   206  		if !extract {
   207  			f, err := os.Create(filepath.Join(dest, fmt.Sprintf("layer%d.tar", i)))
   208  			if err != nil {
   209  				l.Close()
   210  				return err
   211  			}
   212  			io.Copy(f, l)
   213  			l.Close()
   214  			f.Close()
   215  			continue
   216  		}
   217  
   218  		if _, err := archive.ApplyLayer(filepath.Join(dest, "rootfs"), l); err != nil {
   219  			return err
   220  		}
   221  
   222  	}
   223  	return nil
   224  }