launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/charm/testing/mockstore.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package testing
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  	"io"
    10  	"net"
    11  	"net/http"
    12  	"os"
    13  	"strconv"
    14  	"strings"
    15  
    16  	"github.com/loggo/loggo"
    17  	gc "launchpad.net/gocheck"
    18  
    19  	"launchpad.net/juju-core/charm"
    20  	"launchpad.net/juju-core/testing"
    21  	"launchpad.net/juju-core/utils"
    22  )
    23  
    24  var logger = loggo.GetLogger("juju.charm.testing.mockstore")
    25  
    26  // MockStore provides a mock charm store implementation useful when testing.
    27  type MockStore struct {
    28  	mux            *http.ServeMux
    29  	listener       net.Listener
    30  	bundleBytes    []byte
    31  	bundleSha256   string
    32  	Downloads      []*charm.URL
    33  	Authorizations []string
    34  	Metadata       []string
    35  
    36  	charms map[string]int
    37  }
    38  
    39  // NewMockStore creates a mock charm store containing the specified charms.
    40  func NewMockStore(c *gc.C, charms map[string]int) *MockStore {
    41  	s := &MockStore{charms: charms}
    42  	f, err := os.Open(testing.Charms.BundlePath(c.MkDir(), "dummy"))
    43  	c.Assert(err, gc.IsNil)
    44  	defer f.Close()
    45  	buf := &bytes.Buffer{}
    46  	s.bundleSha256, _, err = utils.ReadSHA256(io.TeeReader(f, buf))
    47  	c.Assert(err, gc.IsNil)
    48  	s.bundleBytes = buf.Bytes()
    49  	c.Assert(err, gc.IsNil)
    50  	s.mux = http.NewServeMux()
    51  	s.mux.HandleFunc("/charm-info", s.serveInfo)
    52  	s.mux.HandleFunc("/charm-event", s.serveEvent)
    53  	s.mux.HandleFunc("/charm/", s.serveCharm)
    54  	lis, err := net.Listen("tcp", "127.0.0.1:0")
    55  	c.Assert(err, gc.IsNil)
    56  	s.listener = lis
    57  	go http.Serve(s.listener, s)
    58  	return s
    59  }
    60  
    61  // Close closes the mock store's socket.
    62  func (s *MockStore) Close() {
    63  	s.listener.Close()
    64  }
    65  
    66  // Address returns the URL used to make requests to the mock store.
    67  func (s *MockStore) Address() string {
    68  	return "http://" + s.listener.Addr().String()
    69  }
    70  
    71  // UpdateStoreRevision sets the revision of the specified charm to rev.
    72  func (s *MockStore) UpdateStoreRevision(ch string, rev int) {
    73  	s.charms[ch] = rev
    74  }
    75  
    76  // ServeHTTP implements http.ServeHTTP
    77  func (s *MockStore) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    78  	s.mux.ServeHTTP(w, r)
    79  }
    80  
    81  func (s *MockStore) serveInfo(w http.ResponseWriter, r *http.Request) {
    82  	if metadata := r.Header.Get("Juju-Metadata"); metadata != "" {
    83  		s.Metadata = append(s.Metadata, metadata)
    84  		logger.Infof("Juju metadata: " + metadata)
    85  	}
    86  
    87  	r.ParseForm()
    88  	response := map[string]*charm.InfoResponse{}
    89  	for _, url := range r.Form["charms"] {
    90  		cr := &charm.InfoResponse{}
    91  		response[url] = cr
    92  		charmURL := charm.MustParseURL(url)
    93  		switch charmURL.Name {
    94  		case "borken":
    95  			cr.Errors = append(cr.Errors, "badness")
    96  		case "terracotta":
    97  			cr.Errors = append(cr.Errors, "cannot get revision")
    98  		case "unwise":
    99  			cr.Warnings = append(cr.Warnings, "foolishness")
   100  			fallthrough
   101  		default:
   102  			if rev, ok := s.charms[charmURL.WithRevision(-1).String()]; ok {
   103  				if charmURL.Revision == -1 {
   104  					cr.Revision = rev
   105  				} else {
   106  					cr.Revision = charmURL.Revision
   107  				}
   108  				cr.Sha256 = s.bundleSha256
   109  			} else {
   110  				cr.Errors = append(cr.Errors, "entry not found")
   111  			}
   112  		}
   113  	}
   114  	data, err := json.Marshal(response)
   115  	if err != nil {
   116  		panic(err)
   117  	}
   118  	w.Header().Set("Content-Type", "application/json")
   119  	_, err = w.Write(data)
   120  	if err != nil {
   121  		panic(err)
   122  	}
   123  }
   124  
   125  func (s *MockStore) serveEvent(w http.ResponseWriter, r *http.Request) {
   126  	r.ParseForm()
   127  	response := map[string]*charm.EventResponse{}
   128  	for _, url := range r.Form["charms"] {
   129  		digest := ""
   130  		if i := strings.Index(url, "@"); i >= 0 {
   131  			digest = url[i+1:]
   132  			url = url[:i]
   133  		}
   134  		er := &charm.EventResponse{}
   135  		response[url] = er
   136  		if digest != "" && digest != "the-digest" {
   137  			er.Kind = "not-found"
   138  			er.Errors = []string{"entry not found"}
   139  			continue
   140  		}
   141  		charmURL := charm.MustParseURL(url)
   142  		switch charmURL.Name {
   143  		case "borken":
   144  			er.Kind = "publish-error"
   145  			er.Errors = append(er.Errors, "badness")
   146  		case "unwise":
   147  			er.Warnings = append(er.Warnings, "foolishness")
   148  			fallthrough
   149  		default:
   150  			if rev, ok := s.charms[charmURL.WithRevision(-1).String()]; ok {
   151  				er.Kind = "published"
   152  				er.Revision = rev
   153  				er.Digest = "the-digest"
   154  			} else {
   155  				er.Kind = "not-found"
   156  				er.Errors = []string{"entry not found"}
   157  			}
   158  		}
   159  	}
   160  	data, err := json.Marshal(response)
   161  	if err != nil {
   162  		panic(err)
   163  	}
   164  	w.Header().Set("Content-Type", "application/json")
   165  	_, err = w.Write(data)
   166  	if err != nil {
   167  		panic(err)
   168  	}
   169  }
   170  
   171  func (s *MockStore) serveCharm(w http.ResponseWriter, r *http.Request) {
   172  	charmURL := charm.MustParseURL("cs:" + r.URL.Path[len("/charm/"):])
   173  	s.Downloads = append(s.Downloads, charmURL)
   174  
   175  	if auth := r.Header.Get("Authorization"); auth != "" {
   176  		s.Authorizations = append(s.Authorizations, auth)
   177  	}
   178  
   179  	w.Header().Set("Connection", "close")
   180  	w.Header().Set("Content-Type", "application/octet-stream")
   181  	w.Header().Set("Content-Length", strconv.Itoa(len(s.bundleBytes)))
   182  	_, err := w.Write(s.bundleBytes)
   183  	if err != nil {
   184  		panic(err)
   185  	}
   186  }