github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/cmd/camput/camput.go (about)

     1  /*
     2  Copyright 2011 Google Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8       http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"flag"
    21  	"fmt"
    22  	"log"
    23  	"net/http"
    24  	"net/url"
    25  	"os"
    26  	"strconv"
    27  	"strings"
    28  	"sync"
    29  
    30  	"camlistore.org/pkg/blobserver/dir"
    31  	"camlistore.org/pkg/client"
    32  	"camlistore.org/pkg/cmdmain"
    33  	"camlistore.org/pkg/httputil"
    34  	"camlistore.org/pkg/syncutil"
    35  )
    36  
    37  const buffered = 16 // arbitrary
    38  
    39  var (
    40  	flagProxyLocal = false
    41  	flagHTTP       = flag.Bool("verbose_http", false, "show HTTP request summaries")
    42  	flagHaveCache  = true
    43  	flagBlobDir    = flag.String("blobdir", "", "If non-empty, the local directory to put blobs, instead of sending them over the network. If the string \"discard\", no blobs are written or sent over the network anywhere.")
    44  )
    45  
    46  var (
    47  	uploaderOnce sync.Once
    48  	uploader     *Uploader // initialized by getUploader
    49  )
    50  
    51  var debugFlagOnce sync.Once
    52  
    53  func registerDebugFlags() {
    54  	flag.BoolVar(&flagProxyLocal, "proxy_local", false, "If true, the HTTP_PROXY environment is also used for localhost requests. This can be helpful during debugging.")
    55  	flag.BoolVar(&flagHaveCache, "havecache", true, "Use the 'have cache', a cache keeping track of what blobs the remote server should already have from previous uploads.")
    56  }
    57  
    58  func init() {
    59  	if debug, _ := strconv.ParseBool(os.Getenv("CAMLI_DEBUG")); debug {
    60  		debugFlagOnce.Do(registerDebugFlags)
    61  	}
    62  	cmdmain.ExtraFlagRegistration = client.AddFlags
    63  	cmdmain.PreExit = func() {
    64  		if up := uploader; up != nil {
    65  			up.Close()
    66  			stats := up.Stats()
    67  			if *cmdmain.FlagVerbose {
    68  				log.Printf("Client stats: %s", stats.String())
    69  				if up.transport != nil {
    70  					log.Printf("  #HTTP reqs: %d", up.transport.Requests())
    71  				}
    72  			}
    73  		}
    74  
    75  		// So multiple cmd/camput TestFoo funcs run, each with
    76  		// an fresh (and not previously closed) Uploader:
    77  		uploader = nil
    78  		uploaderOnce = sync.Once{}
    79  	}
    80  }
    81  
    82  func getUploader() *Uploader {
    83  	uploaderOnce.Do(initUploader)
    84  	return uploader
    85  }
    86  
    87  func initUploader() {
    88  	up := newUploader()
    89  	if flagHaveCache && *flagBlobDir == "" {
    90  		gen, err := up.StorageGeneration()
    91  		if err != nil {
    92  			log.Printf("WARNING: not using local server inventory cache; failed to retrieve server's storage generation: %v", err)
    93  		} else {
    94  			up.haveCache = NewKvHaveCache(gen)
    95  			up.Client.SetHaveCache(up.haveCache)
    96  		}
    97  	}
    98  	uploader = up
    99  }
   100  
   101  func handleResult(what string, pr *client.PutResult, err error) error {
   102  	if err != nil {
   103  		log.Printf("Error putting %s: %s", what, err)
   104  		cmdmain.ExitWithFailure = true
   105  		return err
   106  	}
   107  	fmt.Fprintln(cmdmain.Stdout, pr.BlobRef.String())
   108  	return nil
   109  }
   110  
   111  func getenvEitherCase(k string) string {
   112  	if v := os.Getenv(strings.ToUpper(k)); v != "" {
   113  		return v
   114  	}
   115  	return os.Getenv(strings.ToLower(k))
   116  }
   117  
   118  // proxyFromEnvironment is similar to http.ProxyFromEnvironment but it skips
   119  // $NO_PROXY blacklist so it proxies every requests, including localhost
   120  // requests.
   121  func proxyFromEnvironment(req *http.Request) (*url.URL, error) {
   122  	proxy := getenvEitherCase("HTTP_PROXY")
   123  	if proxy == "" {
   124  		return nil, nil
   125  	}
   126  	proxyURL, err := url.Parse(proxy)
   127  	if err != nil || proxyURL.Scheme == "" {
   128  		if u, err := url.Parse("http://" + proxy); err == nil {
   129  			proxyURL = u
   130  			err = nil
   131  		}
   132  	}
   133  	if err != nil {
   134  		return nil, fmt.Errorf("invalid proxy address %q: %v", proxy, err)
   135  	}
   136  	return proxyURL, nil
   137  }
   138  
   139  func newUploader() *Uploader {
   140  	var cc *client.Client
   141  	var httpStats *httputil.StatsTransport
   142  	if d := *flagBlobDir; d != "" {
   143  		ss, err := dir.New(d)
   144  		if err != nil && d == "discard" {
   145  			ss = discardStorage{}
   146  			err = nil
   147  		}
   148  		if err != nil {
   149  			log.Fatalf("Error using dir %s as storage: %v", d, err)
   150  		}
   151  		cc = client.NewStorageClient(ss)
   152  	} else {
   153  		cc = client.NewOrFail()
   154  		proxy := http.ProxyFromEnvironment
   155  		if flagProxyLocal {
   156  			proxy = proxyFromEnvironment
   157  		}
   158  		tr := cc.TransportForConfig(
   159  			&client.TransportConfig{
   160  				Proxy:   proxy,
   161  				Verbose: *flagHTTP,
   162  			})
   163  		httpStats, _ = tr.(*httputil.StatsTransport)
   164  		cc.SetHTTPClient(&http.Client{Transport: tr})
   165  	}
   166  	if !*cmdmain.FlagVerbose {
   167  		cc.SetLogger(nil)
   168  	}
   169  
   170  	pwd, err := os.Getwd()
   171  	if err != nil {
   172  		log.Fatalf("os.Getwd: %v", err)
   173  	}
   174  
   175  	return &Uploader{
   176  		Client:    cc,
   177  		transport: httpStats,
   178  		pwd:       pwd,
   179  		fdGate:    syncutil.NewGate(100), // gate things that waste fds, assuming a low system limit
   180  	}
   181  }
   182  
   183  func main() {
   184  	cmdmain.Main()
   185  }