github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/provider/dummy/storage.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package dummy
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"io"
    10  	"io/ioutil"
    11  	"net"
    12  	"net/http"
    13  	"sort"
    14  	"strings"
    15  	"time"
    16  
    17  	"github.com/juju/errors"
    18  	"github.com/juju/utils"
    19  
    20  	"github.com/juju/juju/environs/storage"
    21  )
    22  
    23  // IsSameStorage returns whether the storage instances are the same.
    24  // Both storages must have been created through the dummy provider.
    25  func IsSameStorage(s1, s2 storage.Storage) bool {
    26  	localS1, localS2 := s1.(*dummyStorage), s2.(*dummyStorage)
    27  	return localS1.env.name == localS2.env.name
    28  }
    29  
    30  func (e *environ) Storage() storage.Storage {
    31  	return &dummyStorage{env: e}
    32  }
    33  
    34  // storageServer holds the storage for an environState.
    35  type storageServer struct {
    36  	path     string // path prefix in http space.
    37  	state    *environState
    38  	files    map[string][]byte
    39  	poisoned map[string]error
    40  }
    41  
    42  func newStorageServer(state *environState, path string) *storageServer {
    43  	return &storageServer{
    44  		state:    state,
    45  		files:    make(map[string][]byte),
    46  		path:     path,
    47  		poisoned: make(map[string]error),
    48  	}
    49  }
    50  
    51  // Poison causes all fetches of the given path to
    52  // return the given error.
    53  func Poison(ss storage.Storage, path string, poisonErr error) {
    54  	s := ss.(*dummyStorage)
    55  	srv, err := s.server()
    56  	if err != nil {
    57  		panic("cannot poison destroyed storage")
    58  	}
    59  	srv.state.mu.Lock()
    60  	srv.poisoned[path] = poisonErr
    61  	srv.state.mu.Unlock()
    62  }
    63  
    64  func (s *storageServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    65  	if req.Method != "GET" {
    66  		http.Error(w, "only GET is supported", http.StatusMethodNotAllowed)
    67  		return
    68  	}
    69  	data, err := s.dataWithDelay(req.URL.Path)
    70  	if err != nil {
    71  		http.Error(w, "404 "+err.Error(), http.StatusNotFound)
    72  		return
    73  	}
    74  	w.Header().Set("Content-Type", "application/octet-stream")
    75  	// If the write fails, the rest of the tests should pick up the problem.
    76  	// It's more likely because the client has legitimately dropped the
    77  	// connection.
    78  	w.Write(data)
    79  }
    80  
    81  // dataWithDelay returns the data for the given path,
    82  // waiting for the configured amount of time before
    83  // accessing it.
    84  func (s *storageServer) dataWithDelay(path string) (data []byte, err error) {
    85  	s.state.mu.Lock()
    86  	delay := s.state.storageDelay
    87  	s.state.mu.Unlock()
    88  	time.Sleep(delay)
    89  	s.state.mu.Lock()
    90  	defer s.state.mu.Unlock()
    91  	if err := s.poisoned[path]; err != nil {
    92  		return nil, err
    93  	}
    94  	data, ok := s.files[path]
    95  	if !ok {
    96  		return nil, errors.NotFoundf("file %q not found", path)
    97  	}
    98  	return data, nil
    99  }
   100  
   101  func (s *storageServer) Put(name string, r io.Reader, length int64) error {
   102  	// Allow Put to be poisoned as well.
   103  	if err := s.poisoned[name]; err != nil {
   104  		return err
   105  	}
   106  
   107  	// We only log Put requests on private storage.
   108  	if strings.HasSuffix(s.path, "/private") {
   109  		s.state.ops <- OpPutFile{s.state.name, name}
   110  	}
   111  	var buf bytes.Buffer
   112  	_, err := io.Copy(&buf, r)
   113  	if err != nil {
   114  		return err
   115  	}
   116  	s.state.mu.Lock()
   117  	s.files[name] = buf.Bytes()
   118  	s.state.mu.Unlock()
   119  	return nil
   120  }
   121  
   122  func (s *storageServer) Get(name string) (io.ReadCloser, error) {
   123  	data, err := s.dataWithDelay(name)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  	return ioutil.NopCloser(bytes.NewBuffer(data)), nil
   128  }
   129  
   130  func (s *storageServer) URL(name string) (string, error) {
   131  	// Mimic the MAAS behaviour so we are testing with the
   132  	// lowest common denominator.
   133  	if name != "" {
   134  		if _, ok := s.files[name]; !ok {
   135  			found := false
   136  			for file := range s.files {
   137  				found = strings.HasPrefix(file, name+"/")
   138  				if found {
   139  					break
   140  				}
   141  			}
   142  			if !found {
   143  				return "", errors.NotFoundf(name)
   144  			}
   145  		}
   146  	}
   147  	_, port, err := net.SplitHostPort(s.state.httpListener.Addr().String())
   148  	if err != nil {
   149  		panic(err.Error())
   150  	}
   151  	hostPort := ""
   152  	if s.state.preferIPv6 {
   153  		hostPort = net.JoinHostPort("::1", port)
   154  	} else {
   155  		hostPort = net.JoinHostPort("127.0.0.1", port)
   156  	}
   157  	return fmt.Sprintf("http://%s%s/%s", hostPort, s.path, name), nil
   158  }
   159  
   160  func (s *storageServer) Remove(name string) error {
   161  	s.state.mu.Lock()
   162  	delete(s.files, name)
   163  	s.state.mu.Unlock()
   164  	return nil
   165  }
   166  
   167  func (s *storageServer) DefaultConsistencyStrategy() utils.AttemptStrategy {
   168  	return utils.AttemptStrategy{}
   169  }
   170  
   171  // ShouldRetry is specified in the StorageReader interface.
   172  func (s *storageServer) ShouldRetry(err error) bool {
   173  	return false
   174  }
   175  
   176  func (s *storageServer) RemoveAll() error {
   177  	s.state.mu.Lock()
   178  	s.files = make(map[string][]byte)
   179  	s.state.mu.Unlock()
   180  	return nil
   181  }
   182  
   183  func (s *storageServer) List(prefix string) ([]string, error) {
   184  	s.state.mu.Lock()
   185  	defer s.state.mu.Unlock()
   186  	var names []string
   187  	for name := range s.files {
   188  		if strings.HasPrefix(name, prefix) {
   189  			names = append(names, name)
   190  		}
   191  	}
   192  	sort.Strings(names)
   193  	return names, nil
   194  }
   195  
   196  // dummyStorage implements the client side of the Storage interface.
   197  type dummyStorage struct {
   198  	env *environ
   199  }
   200  
   201  // server returns the server side of the given storage.
   202  func (s *dummyStorage) server() (*storageServer, error) {
   203  	st, err := s.env.state()
   204  	if err != nil {
   205  		return nil, err
   206  	}
   207  	return st.storage, nil
   208  }
   209  
   210  func (s *dummyStorage) Get(name string) (io.ReadCloser, error) {
   211  	srv, err := s.server()
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  	return srv.Get(name)
   216  }
   217  
   218  func (s *dummyStorage) URL(name string) (string, error) {
   219  	srv, err := s.server()
   220  	if err != nil {
   221  		return "", err
   222  	}
   223  	return srv.URL(name)
   224  }
   225  
   226  func (s *dummyStorage) DefaultConsistencyStrategy() utils.AttemptStrategy {
   227  	return utils.AttemptStrategy{}
   228  }
   229  
   230  // ShouldRetry is specified in the StorageReader interface.
   231  func (s *dummyStorage) ShouldRetry(err error) bool {
   232  	return false
   233  }
   234  
   235  func (s *dummyStorage) Put(name string, r io.Reader, length int64) error {
   236  	srv, err := s.server()
   237  	if err != nil {
   238  		return err
   239  	}
   240  	return srv.Put(name, r, length)
   241  }
   242  
   243  func (s *dummyStorage) Remove(name string) error {
   244  	srv, err := s.server()
   245  	if err != nil {
   246  		return err
   247  	}
   248  	return srv.Remove(name)
   249  }
   250  
   251  func (s *dummyStorage) RemoveAll() error {
   252  	srv, err := s.server()
   253  	if err != nil {
   254  		return err
   255  	}
   256  	return srv.RemoveAll()
   257  }
   258  
   259  func (s *dummyStorage) List(prefix string) ([]string, error) {
   260  	srv, err := s.server()
   261  	if err != nil {
   262  		return nil, err
   263  	}
   264  	return srv.List(prefix)
   265  }