github.com/coreos/rocket@v1.30.1-0.20200224141603-171c416fac02/rkt/image/dockerfetcher.go (about) 1 // Copyright 2015 The rkt Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package image 16 17 import ( 18 "errors" 19 "fmt" 20 "io/ioutil" 21 "net/url" 22 "os" 23 "path" 24 "strings" 25 "time" 26 27 "github.com/hashicorp/errwrap" 28 "github.com/rkt/rkt/rkt/config" 29 rktflag "github.com/rkt/rkt/rkt/flag" 30 "github.com/rkt/rkt/store/imagestore" 31 32 docker2aci "github.com/appc/docker2aci/lib" 33 d2acommon "github.com/appc/docker2aci/lib/common" 34 ) 35 36 // dockerFetcher is used to fetch images from docker:// URLs. It uses 37 // a docker2aci library to perform this task. 38 type dockerFetcher struct { 39 // TODO(krnowak): Fix the docs when we support docker image 40 // verification. Will that ever happen? 41 // InsecureFlags tells which insecure functionality should 42 // be enabled. No image verification must be true for now. 43 InsecureFlags *rktflag.SecFlags 44 DockerAuth map[string]config.BasicCredentials 45 S *imagestore.Store 46 Debug bool 47 } 48 49 // Hash uses docker2aci to download the image and convert it to 50 // ACI, then stores it in the store and returns the hash. 51 func (f *dockerFetcher) Hash(u *url.URL) (string, error) { 52 ensureLogger(f.Debug) 53 dockerURL, err := d2acommon.ParseDockerURL(path.Join(u.Host, u.Path)) 54 if err != nil { 55 return "", fmt.Errorf(`invalid docker URL %q; expected syntax is "docker://[REGISTRY_HOST[:REGISTRY_PORT]/]IMAGE_NAME[:TAG]"`, u) 56 } 57 latest := dockerURL.Tag == "latest" 58 return f.fetchImageFrom(u, latest) 59 } 60 61 func (f *dockerFetcher) fetchImageFrom(u *url.URL, latest bool) (string, error) { 62 if !f.InsecureFlags.SkipImageCheck() { 63 return "", fmt.Errorf("signature verification for docker images is not supported (try --insecure-options=image)") 64 } 65 66 diag.Printf("fetching image from %s", u.String()) 67 68 aciFile, err := f.fetch(u) 69 if err != nil { 70 return "", err 71 } 72 // At this point, the ACI file is removed, but it is kept 73 // alive, because we have an fd to it opened. 74 defer aciFile.Close() 75 76 key, err := f.S.WriteACI(aciFile, imagestore.ACIFetchInfo{ 77 Latest: latest, 78 }) 79 if err != nil { 80 return "", err 81 } 82 83 // docker images don't have signature URL 84 newRem := imagestore.NewRemote(u.String(), "") 85 newRem.BlobKey = key 86 newRem.DownloadTime = time.Now() 87 err = f.S.WriteRemote(newRem) 88 if err != nil { 89 return "", err 90 } 91 92 return key, nil 93 } 94 95 func (f *dockerFetcher) fetch(u *url.URL) (*os.File, error) { 96 tmpDir, err := f.getTmpDir() 97 if err != nil { 98 return nil, err 99 } 100 defer os.RemoveAll(tmpDir) 101 102 registryURL := strings.TrimPrefix(u.String(), "docker://") 103 user, password := f.getCreds(registryURL) 104 config := docker2aci.RemoteConfig{ 105 Username: user, 106 Password: password, 107 Insecure: d2acommon.InsecureConfig{ 108 SkipVerify: f.InsecureFlags.SkipTLSCheck(), 109 AllowHTTP: f.InsecureFlags.AllowHTTP(), 110 }, 111 CommonConfig: docker2aci.CommonConfig{ 112 Squash: true, 113 OutputDir: tmpDir, 114 TmpDir: tmpDir, 115 Compression: d2acommon.NoCompression, 116 }, 117 } 118 acis, err := docker2aci.ConvertRemoteRepo(registryURL, config) 119 if err != nil { 120 return nil, errwrap.Wrap(errors.New("error converting docker image to ACI"), err) 121 } 122 123 aciFile, err := os.Open(acis[0]) 124 if err != nil { 125 return nil, errwrap.Wrap(errors.New("error opening squashed ACI file"), err) 126 } 127 128 return aciFile, nil 129 } 130 131 func (f *dockerFetcher) getTmpDir() (string, error) { 132 storeTmpDir, err := f.S.TmpDir() 133 if err != nil { 134 return "", errwrap.Wrap(errors.New("error creating temporary dir for docker to ACI conversion"), err) 135 } 136 return ioutil.TempDir(storeTmpDir, "docker2aci-") 137 } 138 139 func (f *dockerFetcher) getCreds(registryURL string) (string, string) { 140 indexName := docker2aci.GetIndexName(registryURL) 141 if creds, ok := f.DockerAuth[indexName]; ok { 142 return creds.User, creds.Password 143 } 144 return "", "" 145 }