package scripts import ( "io" "net/http" "net/url" "os" "path/filepath" utilglog "github.com/openshift/source-to-image/pkg/util/glog" "github.com/openshift/source-to-image/pkg/api" "github.com/openshift/source-to-image/pkg/errors" ) var glog = utilglog.StderrLog // Downloader downloads the specified URL to the target file location type Downloader interface { Download(url *url.URL, target string) (*api.SourceInfo, error) } // schemeReader creates an io.Reader from the given url. type schemeReader interface { Read(*url.URL) (io.ReadCloser, error) } type downloader struct { schemeReaders map[string]schemeReader } // NewDownloader creates an instance of the default Downloader implementation func NewDownloader(proxyConfig *api.ProxyConfig) Downloader { httpReader := NewHTTPURLReader(proxyConfig) return &downloader{ schemeReaders: map[string]schemeReader{ "http": httpReader, "https": httpReader, "file": &FileURLReader{}, "image": &ImageReader{}, }, } } // Download downloads the file pointed to by URL into local targetFile // Returns information a boolean flag informing whether any download/copy operation // happened and an error if there was a problem during that operation func (d *downloader) Download(url *url.URL, targetFile string) (*api.SourceInfo, error) { r := d.schemeReaders[url.Scheme] info := &api.SourceInfo{} if r == nil { glog.Errorf("No URL handler found for %s", url.String()) return nil, errors.NewURLHandlerError(url.String()) } reader, err := r.Read(url) if err != nil { return nil, err } defer reader.Close() out, err := os.Create(targetFile) defer out.Close() if err != nil { glog.Errorf("Unable to create target file %s (%s)", targetFile, err) return nil, err } if _, err = io.Copy(out, reader); err != nil { os.Remove(targetFile) glog.Warningf("Skipping file %s due to error copying from source: %s", targetFile, err) return nil, err } glog.V(2).Infof("Downloaded '%s'", url.String()) info.Location = url.String() return info, nil } // HTTPURLReader retrieves a response from a given HTTP(S) URL. type HTTPURLReader struct { Get func(url string) (*http.Response, error) } // NewHTTPURLReader returns a new HTTPURLReader. func NewHTTPURLReader(proxyConfig *api.ProxyConfig) *HTTPURLReader { getFunc := http.Get if proxyConfig != nil { transport := &http.Transport{ Proxy: func(req *http.Request) (*url.URL, error) { if proxyConfig.HTTPSProxy != nil && req.URL.Scheme == "https" { return proxyConfig.HTTPSProxy, nil } return proxyConfig.HTTPProxy, nil }, } client := &http.Client{ Transport: transport, } getFunc = client.Get } return &HTTPURLReader{Get: getFunc} } // Read produces an io.Reader from an http(s) URL. func (h *HTTPURLReader) Read(url *url.URL) (io.ReadCloser, error) { resp, err := h.Get(url.String()) if err != nil { if resp != nil { defer resp.Body.Close() } return nil, err } if resp.StatusCode == 200 || resp.StatusCode == 201 { return resp.Body, nil } return nil, errors.NewDownloadError(url.String(), resp.StatusCode) } // FileURLReader opens a specified file and returns its stream type FileURLReader struct{} // Read produces an io.Reader from a file URL func (*FileURLReader) Read(url *url.URL) (io.ReadCloser, error) { // for some reason url.Host may contain information about the ./ or ../ when // specifying relative path, thus using that value as well return os.Open(filepath.Join(url.Host, url.Path)) } // ImageReader just returns information the URL is from inside the image type ImageReader struct{} // Read throws Not implemented error func (*ImageReader) Read(url *url.URL) (io.ReadCloser, error) { return nil, errors.NewScriptsInsideImageError(url.String()) }