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  }