github.com/portworx/docker@v1.12.1/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  	dockerdist "github.com/docker/docker/distribution"
    18  	archive "github.com/docker/docker/pkg/chrootarchive"
    19  	"github.com/docker/docker/reference"
    20  	"github.com/docker/docker/registry"
    21  	"github.com/docker/engine-api/types"
    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  // Pull downloads the plugin from Store
    66  func Pull(name string, rs registry.Service, metaheader http.Header, authConfig *types.AuthConfig) (PullData, error) {
    67  	ref, err := reference.ParseNamed(name)
    68  	if err != nil {
    69  		logrus.Debugf("pull.go: error in ParseNamed: %v", err)
    70  		return nil, err
    71  	}
    72  
    73  	repoInfo, err := rs.ResolveRepository(ref)
    74  	if err != nil {
    75  		logrus.Debugf("pull.go: error in ResolveRepository: %v", err)
    76  		return nil, err
    77  	}
    78  
    79  	if err := dockerdist.ValidateRepoName(repoInfo.Name()); err != nil {
    80  		logrus.Debugf("pull.go: error in ValidateRepoName: %v", err)
    81  		return nil, err
    82  	}
    83  
    84  	endpoints, err := rs.LookupPullEndpoints(repoInfo.Hostname())
    85  	if err != nil {
    86  		logrus.Debugf("pull.go: error in LookupPullEndpoints: %v", err)
    87  		return nil, err
    88  	}
    89  
    90  	var confirmedV2 bool
    91  	var repository distribution.Repository
    92  
    93  	for _, endpoint := range endpoints {
    94  		if confirmedV2 && endpoint.Version == registry.APIVersion1 {
    95  			logrus.Debugf("Skipping v1 endpoint %s because v2 registry was detected", endpoint.URL)
    96  			continue
    97  		}
    98  
    99  		// TODO: reuse contexts
   100  		repository, confirmedV2, err = dockerdist.NewV2Repository(context.Background(), repoInfo, endpoint, metaheader, authConfig, "pull")
   101  		if err != nil {
   102  			logrus.Debugf("pull.go: error in NewV2Repository: %v", err)
   103  			return nil, err
   104  		}
   105  		if !confirmedV2 {
   106  			logrus.Debugf("pull.go: !confirmedV2")
   107  			return nil, ErrUnsupportedRegistry
   108  		}
   109  		logrus.Debugf("Trying to pull %s from %s %s", repoInfo.Name(), endpoint.URL, endpoint.Version)
   110  		break
   111  	}
   112  
   113  	tag := DefaultTag
   114  	if ref, ok := ref.(reference.NamedTagged); ok {
   115  		tag = ref.Tag()
   116  	}
   117  
   118  	// tags := repository.Tags(context.Background())
   119  	// desc, err := tags.Get(context.Background(), tag)
   120  	// 	if err != nil {
   121  	// 		return nil, err
   122  	// 	}
   123  	//
   124  	msv, err := repository.Manifests(context.Background())
   125  	if err != nil {
   126  		logrus.Debugf("pull.go: error in repository.Manifests: %v", err)
   127  		return nil, err
   128  	}
   129  	manifest, err := msv.Get(context.Background(), "", distribution.WithTag(tag))
   130  	if err != nil {
   131  		// TODO: change 401 to 404
   132  		logrus.Debugf("pull.go: error in msv.Get(): %v", err)
   133  		return nil, err
   134  	}
   135  
   136  	_, pl, err := manifest.Payload()
   137  	if err != nil {
   138  		logrus.Debugf("pull.go: error in manifest.Payload(): %v", err)
   139  		return nil, err
   140  	}
   141  	var m schema2.Manifest
   142  	if err := json.Unmarshal(pl, &m); err != nil {
   143  		logrus.Debugf("pull.go: error in json.Unmarshal(): %v", err)
   144  		return nil, err
   145  	}
   146  	if m.Config.MediaType != MediaTypeConfig {
   147  		return nil, ErrUnsupportedMediaType
   148  	}
   149  
   150  	pd := &pullData{
   151  		repository: repository,
   152  		manifest:   m,
   153  	}
   154  
   155  	logrus.Debugf("manifest: %s", pl)
   156  	return pd, nil
   157  }
   158  
   159  // WritePullData extracts manifest and rootfs to the disk.
   160  func WritePullData(pd PullData, dest string, extract bool) error {
   161  	config, err := pd.Config()
   162  	if err != nil {
   163  		return err
   164  	}
   165  	var p types.Plugin
   166  	if err := json.Unmarshal(config, &p); err != nil {
   167  		return err
   168  	}
   169  	logrus.Debugf("%#v", p)
   170  
   171  	if err := os.MkdirAll(dest, 0700); err != nil {
   172  		return err
   173  	}
   174  
   175  	if extract {
   176  		if err := ioutil.WriteFile(filepath.Join(dest, "manifest.json"), config, 0600); err != nil {
   177  			return err
   178  		}
   179  
   180  		if err := os.MkdirAll(filepath.Join(dest, "rootfs"), 0700); err != nil {
   181  			return err
   182  		}
   183  	}
   184  
   185  	for i := 0; ; i++ {
   186  		l, err := pd.Layer()
   187  		if err == io.EOF {
   188  			break
   189  		}
   190  		if err != nil {
   191  			return err
   192  		}
   193  
   194  		if !extract {
   195  			f, err := os.Create(filepath.Join(dest, fmt.Sprintf("layer%d.tar", i)))
   196  			if err != nil {
   197  				l.Close()
   198  				return err
   199  			}
   200  			io.Copy(f, l)
   201  			l.Close()
   202  			f.Close()
   203  			continue
   204  		}
   205  
   206  		if _, err := archive.ApplyLayer(filepath.Join(dest, "rootfs"), l); err != nil {
   207  			return err
   208  		}
   209  
   210  	}
   211  	return nil
   212  }