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 }