~ubuntu-branches/ubuntu/vivid/juju-core/vivid-updates

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
package v4_test

import (
	"encoding/json"
	"io"
	"net/http"
	"net/http/httptest"

	gc "gopkg.in/check.v1"

	"github.com/julienschmidt/httprouter"
	"gopkg.in/errgo.v1"
	"gopkg.in/macaroon-bakery.v0/bakery"
	"gopkg.in/macaroon-bakery.v0/bakery/checkers"
	"gopkg.in/macaroon-bakery.v0/bakerytest"
	"gopkg.in/macaroon-bakery.v0/httpbakery"

	"gopkg.in/juju/charmstore.v4/internal/charmstore"
	"gopkg.in/juju/charmstore.v4/internal/storetesting"
	"gopkg.in/juju/charmstore.v4/internal/v4"
)

type commonSuite struct {
	storetesting.IsolatedMgoSuite

	// srv holds the store HTTP handler.
	srv http.Handler

	// srvParams holds the parameters that the
	// srv handler was started with
	srvParams charmstore.ServerParams

	// noMacaroonSrv holds the store HTTP handler
	// for an instance of the store without identity
	// enabled. If enableIdentity is false, this is
	// the same as srv.
	noMacaroonSrv http.Handler

	// noMacaroonSrvParams holds the parameters that the
	// noMacaroonSrv handler was started with
	noMacaroonSrvParams charmstore.ServerParams

	// store holds an instance of *charm.Store
	// that can be used to access the charmstore database
	// directly.
	store *charmstore.Store

	// esSuite is set only when enableES is set to true.
	esSuite *storetesting.ElasticSearchSuite

	// discharge holds the function that will be used
	// to check third party caveats by the mock
	// discharger. This will be ignored if enableIdentity was
	// not true before commonSuite.SetUpTest is invoked.
	//
	// It may be set by tests to influence the behavior of the
	// discharger.
	discharge func(cav, arg string) ([]checkers.Caveat, error)

	discharger *bakerytest.Discharger
	idM        *idM
	idMServer  *httptest.Server

	// The following fields may be set before
	// SetUpSuite is invoked on commonSuite
	// and influences how the suite sets itself up.

	// enableIdentity holds whether the charmstore server
	// will be started with a configured identity service.
	enableIdentity bool

	// enableES holds whether the charmstore server will be
	// started with Elastic Search enabled.
	enableES bool
}

func (s *commonSuite) SetUpSuite(c *gc.C) {
	s.IsolatedMgoSuite.SetUpSuite(c)
	if s.enableES {
		s.esSuite = new(storetesting.ElasticSearchSuite)
		s.esSuite.SetUpSuite(c)
	}
}

func (s *commonSuite) TearDownSuite(c *gc.C) {
	if s.esSuite != nil {
		s.esSuite.TearDownSuite(c)
	}
}

func (s *commonSuite) SetUpTest(c *gc.C) {
	s.IsolatedMgoSuite.SetUpTest(c)
	if s.esSuite != nil {
		s.esSuite.SetUpTest(c)
	}
	if s.enableIdentity {
		s.idM = newIdM()
		s.idMServer = httptest.NewServer(s.idM)
	}
	s.startServer(c)
}

func (s *commonSuite) TearDownTest(c *gc.C) {
	s.store.Close()
	if s.esSuite != nil {
		s.esSuite.TearDownTest(c)
	}
	if s.discharger != nil {
		s.discharger.Close()
		s.idMServer.Close()
	}
	s.IsolatedMgoSuite.TearDownTest(c)
}

// startServer creates a new charmstore server.
func (s *commonSuite) startServer(c *gc.C) {
	config := charmstore.ServerParams{
		AuthUsername: testUsername,
		AuthPassword: testPassword,
	}
	if s.enableIdentity {
		s.discharge = func(_, _ string) ([]checkers.Caveat, error) {
			return nil, errgo.New("no discharge")
		}
		discharger := bakerytest.NewDischarger(nil, func(_ *http.Request, cond string, arg string) ([]checkers.Caveat, error) {
			return s.discharge(cond, arg)
		})
		config.IdentityLocation = discharger.Location()
		config.PublicKeyLocator = discharger
		config.IdentityAPIURL = s.idMServer.URL
	}
	var si *charmstore.SearchIndex
	if s.enableES {
		si = &charmstore.SearchIndex{
			Database: s.esSuite.ES,
			Index:    s.esSuite.TestIndex,
		}
	}
	db := s.Session.DB("charmstore")
	var err error
	s.srv, err = charmstore.NewServer(db, si, config, map[string]charmstore.NewAPIHandlerFunc{"v4": v4.NewAPIHandler})
	c.Assert(err, gc.IsNil)
	s.srvParams = config

	if s.enableIdentity {
		config.IdentityLocation = ""
		config.PublicKeyLocator = nil
		config.IdentityAPIURL = ""
		s.noMacaroonSrv, err = charmstore.NewServer(db, si, config, map[string]charmstore.NewAPIHandlerFunc{"v4": v4.NewAPIHandler})
		c.Assert(err, gc.IsNil)
	} else {
		s.noMacaroonSrv = s.srv
	}
	s.noMacaroonSrvParams = config

	pool, err := charmstore.NewPool(db, si, &bakery.NewServiceParams{})
	c.Assert(err, gc.IsNil)
	s.store = pool.Store()
}

func storeURL(path string) string {
	return "/v4/" + path
}

func bakeryDo(client *http.Client) func(*http.Request) (*http.Response, error) {
	if client == nil {
		client = httpbakery.NewHTTPClient()
	}
	return func(req *http.Request) (*http.Response, error) {
		if req.Body != nil {
			return httpbakery.DoWithBody(client, req, httpbakery.SeekerBody(req.Body.(io.ReadSeeker)), noInteraction)
		}
		return httpbakery.Do(client, req, noInteraction)
	}
}

type idM struct {
	// groups may be set to determine the mapping
	// from user to groups for that user.
	groups map[string][]string

	// body may be set to cause serveGroups to return
	// an arbitrary HTTP response body.
	body string

	// status may be set to indicate the HTTP status code
	// when body is not nil.
	status int

	router *httprouter.Router
}

func newIdM() *idM {
	idM := &idM{
		groups: make(map[string][]string),
		router: httprouter.New(),
	}
	idM.router.GET("/v1/u/:user/idpgroups", idM.serveGroups)
	return idM
}

func (idM *idM) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	idM.router.ServeHTTP(w, req)
}

func (idM *idM) serveGroups(w http.ResponseWriter, req *http.Request, p httprouter.Params) {
	if idM.body != "" {
		if idM.status != 0 {
			w.WriteHeader(idM.status)
		}
		w.Write([]byte(idM.body))
		return
	}
	u := p.ByName("user")
	if u == "" {
		panic("no user")
	}
	enc := json.NewEncoder(w)
	if err := enc.Encode(idM.groups[u]); err != nil {
		panic(err)
	}
}