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 }