kubesphere.io/s2irun@v3.2.1+incompatible/pkg/scripts/download.go (about) 1 package scripts 2 3 import ( 4 "io" 5 "net/http" 6 "net/url" 7 "os" 8 "path/filepath" 9 "sync" 10 11 "github.com/kubesphere/s2irun/pkg/api" 12 s2ierr "github.com/kubesphere/s2irun/pkg/errors" 13 "github.com/kubesphere/s2irun/pkg/scm/git" 14 utilglog "github.com/kubesphere/s2irun/pkg/utils/glog" 15 ) 16 17 var glog = utilglog.StderrLog 18 19 // Downloader downloads the specified URL to the target file location 20 type Downloader interface { 21 Download(url *url.URL, target string) (*git.SourceInfo, error) 22 } 23 24 // schemeReader creates an io.Reader from the given url. 25 type schemeReader interface { 26 Read(*url.URL) (io.ReadCloser, error) 27 } 28 29 type downloader struct { 30 schemeReaders map[string]schemeReader 31 } 32 33 // NewDownloader creates an instance of the default Downloader implementation 34 func NewDownloader(proxyConfig *api.ProxyConfig) Downloader { 35 httpReader := NewHTTPURLReader(proxyConfig) 36 return &downloader{ 37 schemeReaders: map[string]schemeReader{ 38 "http": httpReader, 39 "https": httpReader, 40 "file": &FileURLReader{}, 41 "image": &ImageReader{}, 42 }, 43 } 44 } 45 46 // Download downloads the file pointed to by URL into local targetFile 47 // Returns information a boolean flag informing whether any download/copy operation 48 // happened and an error if there was a problem during that operation 49 func (d *downloader) Download(url *url.URL, targetFile string) (*git.SourceInfo, error) { 50 r := d.schemeReaders[url.Scheme] 51 info := &git.SourceInfo{} 52 if r == nil { 53 glog.Errorf("No URL handler found for %s", url.String()) 54 return nil, s2ierr.NewURLHandlerError(url.String()) 55 } 56 57 reader, err := r.Read(url) 58 if err != nil { 59 return nil, err 60 } 61 defer reader.Close() 62 63 out, err := os.Create(targetFile) 64 defer out.Close() 65 66 if err != nil { 67 glog.Errorf("Unable to create target file %s (%v)", targetFile, err) 68 return nil, err 69 } 70 71 if _, err = io.Copy(out, reader); err != nil { 72 os.Remove(targetFile) 73 glog.Warningf("Skipping file %s due to error copying from source: %v", targetFile, err) 74 return nil, err 75 } 76 77 glog.V(2).Infof("Downloaded '%s'", url.String()) 78 info.Location = url.String() 79 return info, nil 80 } 81 82 // HTTPURLReader retrieves a response from a given HTTP(S) URL. 83 type HTTPURLReader struct { 84 Get func(url string) (*http.Response, error) 85 } 86 87 var transportMap map[api.ProxyConfig]*http.Transport 88 var transportMapMutex sync.Mutex 89 90 func init() { 91 transportMap = make(map[api.ProxyConfig]*http.Transport) 92 } 93 94 // NewHTTPURLReader returns a new HTTPURLReader. 95 func NewHTTPURLReader(proxyConfig *api.ProxyConfig) *HTTPURLReader { 96 getFunc := http.Get 97 if proxyConfig != nil { 98 transportMapMutex.Lock() 99 transport, ok := transportMap[*proxyConfig] 100 if !ok { 101 transport = &http.Transport{ 102 Proxy: func(req *http.Request) (*url.URL, error) { 103 if proxyConfig.HTTPSProxy != nil && req.URL.Scheme == "https" { 104 return proxyConfig.HTTPSProxy, nil 105 } 106 return proxyConfig.HTTPProxy, nil 107 }, 108 } 109 transportMap[*proxyConfig] = transport 110 } 111 transportMapMutex.Unlock() 112 client := &http.Client{ 113 Transport: transport, 114 } 115 getFunc = client.Get 116 } 117 return &HTTPURLReader{Get: getFunc} 118 } 119 120 // Read produces an io.Reader from an http(s) URL. 121 func (h *HTTPURLReader) Read(url *url.URL) (io.ReadCloser, error) { 122 resp, err := h.Get(url.String()) 123 if err != nil { 124 if resp != nil { 125 defer resp.Body.Close() 126 } 127 return nil, err 128 } 129 if resp.StatusCode == 200 || resp.StatusCode == 201 { 130 return resp.Body, nil 131 } 132 return nil, s2ierr.NewDownloadError(url.String(), resp.StatusCode) 133 } 134 135 // FileURLReader opens a specified file and returns its stream 136 type FileURLReader struct{} 137 138 // Read produces an io.Reader from a file URL 139 func (*FileURLReader) Read(url *url.URL) (io.ReadCloser, error) { 140 // for some reason url.Host may contain information about the ./ or ../ when 141 // specifying relative path, thus using that value as well 142 return os.Open(filepath.Join(url.Host, url.Path)) 143 } 144 145 // ImageReader just returns information the URL is from inside the image 146 type ImageReader struct{} 147 148 // Read throws Not implemented error 149 func (*ImageReader) Read(url *url.URL) (io.ReadCloser, error) { 150 return nil, s2ierr.NewScriptsInsideImageError(url.String()) 151 }