mirror of
https://github.com/openshift/source-to-image.git
synced 2026-02-06 06:44:58 +01:00
152 lines
4.1 KiB
Go
152 lines
4.1 KiB
Go
package scripts
|
|
|
|
import (
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
|
|
"github.com/openshift/source-to-image/pkg/api"
|
|
s2ierr "github.com/openshift/source-to-image/pkg/errors"
|
|
"github.com/openshift/source-to-image/pkg/scm/git"
|
|
utillog "github.com/openshift/source-to-image/pkg/util/log"
|
|
)
|
|
|
|
var log = utillog.StderrLog
|
|
|
|
// Downloader downloads the specified URL to the target file location
|
|
type Downloader interface {
|
|
Download(url *url.URL, target string) (*git.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) (*git.SourceInfo, error) {
|
|
r := d.schemeReaders[url.Scheme]
|
|
info := &git.SourceInfo{}
|
|
if r == nil {
|
|
log.Errorf("No URL handler found for %s", url.String())
|
|
return nil, s2ierr.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 {
|
|
log.Errorf("Unable to create target file %s (%v)", targetFile, err)
|
|
return nil, err
|
|
}
|
|
|
|
if _, err = io.Copy(out, reader); err != nil {
|
|
os.Remove(targetFile)
|
|
log.Warningf("Skipping file %s due to error copying from source: %v", targetFile, err)
|
|
return nil, err
|
|
}
|
|
|
|
log.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)
|
|
}
|
|
|
|
var transportMap map[api.ProxyConfig]*http.Transport
|
|
var transportMapMutex sync.Mutex
|
|
|
|
func init() {
|
|
transportMap = make(map[api.ProxyConfig]*http.Transport)
|
|
}
|
|
|
|
// NewHTTPURLReader returns a new HTTPURLReader.
|
|
func NewHTTPURLReader(proxyConfig *api.ProxyConfig) *HTTPURLReader {
|
|
getFunc := http.Get
|
|
if proxyConfig != nil {
|
|
transportMapMutex.Lock()
|
|
transport, ok := transportMap[*proxyConfig]
|
|
if !ok {
|
|
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
|
|
},
|
|
}
|
|
transportMap[*proxyConfig] = transport
|
|
}
|
|
transportMapMutex.Unlock()
|
|
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, s2ierr.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.Clean(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, s2ierr.NewScriptsInsideImageError(url.String())
|
|
}
|