github.com/zhuohuang-hust/src-cbuild@v0.0.0-20230105071821-c7aab3e7c840/plugin/distribution/pull.go (about)

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