Merge lp:~bac/juju-core/get-endpoints into lp:~juju/juju-core/trunk

Proposed by Brad Crittenden
Status: Work in progress
Proposed branch: lp:~bac/juju-core/get-endpoints
Merge into: lp:~juju/juju-core/trunk
Diff against target: 312 lines (+255/-0) (has conflicts)
4 files modified
state/api/apiclient.go (+11/-0)
state/api/params/params.go (+20/-0)
state/apiserver/api_test.go (+164/-0)
state/apiserver/apiserver.go (+60/-0)
Text conflict in state/apiserver/api_test.go
To merge this branch: bzr merge lp:~bac/juju-core/get-endpoints
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+154843@code.launchpad.net

Description of the change

Add GetEndpoints to the API.

https://codereview.appspot.com/7668045/

To post a comment you must log in.
Revision history for this message
Brad Crittenden (bac) wrote :

Reviewers: mp+154843_code.launchpad.net,

Message:
Please take a look.

Description:
Add GetEndpoints to the API.

https://code.launchpad.net/~bac/juju-core/get-endpoints/+merge/154843

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/7668045/

Affected files:
   A [revision details]
   M state/api/apiclient.go
   M state/api/params/params.go
   M state/apiserver/api_test.go
   M state/apiserver/apiserver.go

Revision history for this message
Dimiter Naydenov (dimitern) wrote :

LGTM modulo the suggestions below.

https://codereview.appspot.com/7668045/diff/2001/state/api/params/params.go
File state/api/params/params.go (right):

https://codereview.appspot.com/7668045/diff/2001/state/api/params/params.go#newcode152
state/api/params/params.go:152: // of service names or an empty array to
indicate all services.
should empty and nil here mean the same thing (len=0)?

https://codereview.appspot.com/7668045/diff/2001/state/apiserver/api_test.go
File state/apiserver/api_test.go (right):

https://codereview.appspot.com/7668045/diff/2001/state/apiserver/api_test.go#newcode1447
state/apiserver/api_test.go:1447: // normalize will replace the list
with a map keyed by name for
which list? (i know, but it'll help to be a bit more descriptive in the
comment).

https://codereview.appspot.com/7668045/diff/2001/state/apiserver/apiserver.go
File state/apiserver/apiserver.go (right):

https://codereview.appspot.com/7668045/diff/2001/state/apiserver/apiserver.go#newcode471
state/apiserver/apiserver.go:471: if meta == nil {
you don't need this if at all, since if len(meta) == 0, the range loop
won't run anyway.

https://codereview.appspot.com/7668045/diff/2001/state/apiserver/apiserver.go#newcode481
state/apiserver/apiserver.go:481: // rel["limit"] =
strconv.Itoa(value.Limit)
then remove this line

https://codereview.appspot.com/7668045/

Unmerged revisions

1030. By Brad Crittenden

Remove 'limit' from GetEndpoints data per conversation with William.

1029. By Brad Crittenden

Added another test for GetEndpoints

1028. By Brad Crittenden

Add GetEndpoints to the API

1027. By Brad Crittenden

before expected rewrite

1026. By Brad Crittenden

Merge from trunk

1025. By Brad Crittenden

checkpoint

1024. By Brad Crittenden

linking branch

1023. By Brad Crittenden

Add destroy-relation to API.

R=dimitern, fwereade, dfc
CC=
https://codereview.appspot.com/7610046

1022. By Aaron Bentley

Add category list to charms

Added category list to charms to support charmworld, which will display charm
categories. While most charms will have only one, some will fit into multiple
categories, such as Apache, which is both a web server and a proxy.

There will be an official list of categories, but supplying an unofficial
category is a lint error, not a syntax error, so this branch does not address
that. The order of the values is not significant, so in Python terms
categories are a set, not a list, but since none of JSON, BSON and Go has
direct support for sets, a list is used.

This branch also adds tests to ensure that Meta objects can be roundtripped
via BSON.

R=dfc, fwereade, jameinel
CC=
https://codereview.appspot.com/7762043

1021. By William Reade

cmd/juju: bootstrap accepts --constraints

...and passes them on to environs.Bootstrap, which passes them on to
Environ.Bootstrap. Only the dummy provider actually handles constraints
at the moment -- so we can test the bootstrap command -- all the others
will be implemented in followups.

R=dimitern, jameinel, dfc, thumper
CC=
https://codereview.appspot.com/7610048

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'state/api/apiclient.go'
2--- state/api/apiclient.go 2013-03-21 09:32:47 +0000
3+++ state/api/apiclient.go 2013-03-22 14:08:21 +0000
4@@ -199,6 +199,17 @@
5 return info, clientError(err)
6 }
7
8+// GetEndpoints returns the endpoints for the specified services. If
9+// no services are passed then endpoints for all services are returned.
10+func (c *Client) GetEndpoints(services []string) (*params.GetEndpointsResults, error) {
11+ args := params.GetEndpoints{
12+ Services: services,
13+ }
14+ result := new(params.GetEndpointsResults)
15+ err := c.st.client.Call("Client", "", "GetEndpoints", args, result)
16+ return result, clientError(err)
17+}
18+
19 // AllWatcher holds information allowing us to get Deltas describing changes
20 // to the entire environment.
21 type AllWatcher struct {
22
23=== modified file 'state/api/params/params.go'
24--- state/api/params/params.go 2013-03-21 09:32:47 +0000
25+++ state/api/params/params.go 2013-03-22 14:08:21 +0000
26@@ -154,6 +154,26 @@
27 Constraints constraints.Value
28 }
29
30+// GetEndpoints has the parameters for making the GetEndpoints call, an array
31+// of service names or an empty array to indicate all services.
32+type GetEndpoints struct {
33+ Services []string
34+}
35+
36+// GetEndpointsResults is the return structure for the GetEndpoints call.
37+type GetEndpointsResults struct {
38+ // The Endpoints is a pretty complicated scheme of maps to maps. The
39+ // keys for each level are shown here:
40+ // svcname provides
41+ // requires
42+ // interface
43+ // name
44+ // role
45+ // etc
46+ // value
47+ Endpoints map[string]map[string][]map[string]string
48+}
49+
50 // Delta holds details of a change to the environment.
51 type Delta struct {
52 // If Removed is true, the entity has been removed;
53
54=== modified file 'state/apiserver/api_test.go'
55--- state/apiserver/api_test.go 2013-03-21 09:32:47 +0000
56+++ state/apiserver/api_test.go 2013-03-22 14:08:21 +0000
57@@ -151,6 +151,10 @@
58 about: "Client.DestroyRelation",
59 op: opClientDestroyRelation,
60 allow: []string{"user-admin", "user-other"},
61+}, {
62+ about: "Client.GetEndpoints",
63+ op: opClientGetEndpoints,
64+ allow: []string{"user-admin", "user-other"},
65 },
66 }
67
68@@ -273,6 +277,15 @@
69 return func() {}, nil
70 }
71
72+func opClientGetEndpoints(c *C, st *api.State, mst *state.State) (func(), error) {
73+ endpoints, err := st.Client().GetEndpoints([]string{})
74+ if err != nil {
75+ return func() {}, err
76+ }
77+ c.Assert(endpoints, NotNil)
78+ return func() {}, nil
79+}
80+
81 func opClientStatus(c *C, st *api.State, mst *state.State) (func(), error) {
82 status, err := st.Client().Status()
83 if err != nil {
84@@ -1375,6 +1388,157 @@
85 c.Assert(err, ErrorMatches, `relation "wordpress:db mysql:server" not found`)
86 }
87
88+<<<<<<< TREE
89+=======
90+// makeExpectedWordpress returns the expected endpoints for wordpress.
91+func makeExpectedWordpress() map[string][]map[string]string {
92+ results := map[string][]map[string]string{
93+ "provides": []map[string]string{
94+ map[string]string{
95+ "name": "url",
96+ "interface": "http",
97+ "optional": "false",
98+ "scope": "global",
99+ },
100+ map[string]string{
101+ "name": "logging-dir",
102+ "interface": "logging",
103+ "scope": "container",
104+ "optional": "false",
105+ },
106+ },
107+ "requires": []map[string]string{
108+ map[string]string{
109+ "name": "db",
110+ "interface": "mysql",
111+ "optional": "false",
112+ "scope": "global",
113+ },
114+ map[string]string{
115+ "name": "cache",
116+ "interface": "varnish",
117+ "optional": "true",
118+ "scope": "global",
119+ },
120+ },
121+ }
122+ return results
123+}
124+
125+// makeExpectedLogging returns the expected endpoints for logging.
126+func makeExpectedLogging() map[string][]map[string]string {
127+ results := map[string][]map[string]string{
128+
129+ "provides": []map[string]string{
130+ map[string]string{
131+ "name": "logging-client",
132+ "interface": "logging",
133+ "scope": "global",
134+ "optional": "false",
135+ },
136+ },
137+
138+ "requires": []map[string]string{
139+ map[string]string{
140+ "name": "logging-directory",
141+ "interface": "logging",
142+ "scope": "container",
143+ "optional": "false",
144+ },
145+ map[string]string{
146+ "name": "info",
147+ "interface": "juju-info",
148+ "scope": "container",
149+ "optional": "false",
150+ },
151+ },
152+ }
153+ return results
154+}
155+
156+// makeExpectedMysql returns the expected endpoints for mysql.
157+func makeExpectedMysql() map[string][]map[string]string {
158+ results := map[string][]map[string]string{
159+
160+ "provides": []map[string]string{
161+ map[string]string{
162+ "name": "server",
163+ "scope": "global",
164+ "optional": "false",
165+ "interface": "mysql",
166+ },
167+ },
168+
169+ "requires": []map[string]string{},
170+ }
171+ return results
172+}
173+
174+// normalize will replace the list with a map keyed by name for
175+// easier comparison.
176+func normalize(r map[string]map[string][]map[string]string) map[string]map[string]map[string]map[string]string {
177+ res := map[string]map[string]map[string]map[string]string{}
178+ for k1, v1 := range r {
179+ res[k1] = map[string]map[string]map[string]string{}
180+ for k2, v2 := range v1 {
181+ res[k1][k2] = map[string]map[string]string{}
182+ for _, v3 := range v2 {
183+ res[k1][k2][v3["name"]] = v3
184+ }
185+ }
186+ }
187+ return res
188+}
189+
190+func (s *suite) TestGetEndpointsAll(c *C) {
191+ s.setUpScenario(c)
192+ result, err := s.APIState.Client().GetEndpoints([]string{})
193+ c.Assert(err, IsNil)
194+ c.Assert(result, NotNil)
195+ c.Assert(result.Endpoints, NotNil)
196+ expected := make(map[string]map[string][]map[string]string)
197+ expected["wordpress"] = makeExpectedWordpress()
198+ expected["logging"] = makeExpectedLogging()
199+ expected["mysql"] = makeExpectedMysql()
200+ normalized := normalize(expected)
201+ obtained := normalize(result.Endpoints)
202+ c.Assert(obtained, DeepEquals, normalized)
203+}
204+
205+func (s *suite) TestGetEndpointsOne(c *C) {
206+ s.setUpScenario(c)
207+ result, err := s.APIState.Client().GetEndpoints([]string{"wordpress"})
208+ c.Assert(err, IsNil)
209+ c.Assert(result, NotNil)
210+ c.Assert(result.Endpoints, NotNil)
211+
212+ expected := make(map[string]map[string][]map[string]string)
213+ expected["wordpress"] = makeExpectedWordpress()
214+
215+ normalized := normalize(expected)
216+ obtained := normalize(result.Endpoints)
217+ c.Assert(obtained, DeepEquals, normalized)
218+}
219+
220+func (s *suite) TestGetEndpointsMost(c *C) {
221+ s.setUpScenario(c)
222+ result, err := s.APIState.Client().GetEndpoints([]string{"mysql", "wordpress"})
223+ c.Assert(err, IsNil)
224+ c.Assert(result, NotNil)
225+ c.Assert(result.Endpoints, NotNil)
226+
227+ expected := make(map[string]map[string][]map[string]string)
228+ expected["wordpress"] = makeExpectedWordpress()
229+ expected["mysql"] = makeExpectedMysql()
230+
231+ normalized := normalize(expected)
232+ obtained := normalize(result.Endpoints)
233+ c.Assert(obtained, DeepEquals, normalized)
234+}
235+
236+// This test will be thrown away, at least in part, once the stub code in
237+// state/megawatcher.go is implemented.
238+>>>>>>> MERGE-SOURCE
239 func (s *suite) TestClientWatchAll(c *C) {
240 // A very simple end-to-end test, because
241 // all the logic is tested elsewhere.
242
243=== modified file 'state/apiserver/apiserver.go'
244--- state/apiserver/apiserver.go 2013-03-21 09:32:47 +0000
245+++ state/apiserver/apiserver.go 2013-03-22 14:08:21 +0000
246@@ -435,6 +435,66 @@
247 return entity.SetAnnotations(args.Pairs)
248 }
249
250+// GetEndpoints returns the endpoints for the specified services. If
251+// no services are passed then endpoints for all services are returned.
252+func (c *srvClient) GetEndpoints(args params.GetEndpoints) (params.GetEndpointsResults, error) {
253+
254+ var results params.GetEndpointsResults
255+ var services []*state.Service
256+ srvState := c.root.srv.state
257+
258+ if len(args.Services) == 0 {
259+ var err error
260+ services, err = srvState.AllServices()
261+ if err != nil {
262+ return params.GetEndpointsResults{}, err
263+ }
264+ } else {
265+ for _, name := range args.Services {
266+ s, err := srvState.Service(name)
267+ if err != nil {
268+ return params.GetEndpointsResults{}, err
269+ }
270+ services = append(services, s)
271+ }
272+ }
273+ results.Endpoints = make(map[string]map[string][]map[string]string)
274+
275+ for _, svc := range services {
276+ charm, _, err := svc.Charm()
277+ if err != nil {
278+ return params.GetEndpointsResults{}, err
279+ }
280+ metadata := charm.Meta()
281+ svcName := svc.Name()
282+ results.Endpoints[svcName] = make(map[string][]map[string]string)
283+ results.Endpoints[svcName]["provides"] = flatten(metadata.Provides)
284+ results.Endpoints[svcName]["requires"] = flatten(metadata.Requires)
285+ }
286+
287+ return results, nil
288+
289+}
290+
291+func flatten(meta map[string]charm.Relation) []map[string]string {
292+ var results []map[string]string
293+ if meta == nil {
294+ return results
295+ }
296+ for key, value := range meta {
297+ rel := make(map[string]string)
298+ rel["name"] = key
299+ rel["interface"] = value.Interface
300+ rel["optional"] = strconv.FormatBool(value.Optional)
301+ // Note, "limit" is explicitly not returned, per discussion with
302+ // fwereade.
303+ // rel["limit"] = strconv.Itoa(value.Limit)
304+ rel["scope"] = string(value.Scope)
305+ results = append(results, rel)
306+ }
307+ return results
308+}
309+
310 // Login logs in with the provided credentials.
311 // All subsequent requests on the connection will
312 // act as the authenticated user.

Subscribers

People subscribed via source and target branches