@@ -22,13 +22,16 @@ var logger = loggo.GetLogger("juju.charmstore")
22
22
// of Juju.
23
23
type Client interface {
24
24
BaseClient
25
- ResourcesClient
26
25
io.Closer
27
26
28
- // AsRepo returns a charm repo that wraps the client. If the model's
29
- // UUID is provided then it is associated with all of the repo's
30
- // interaction with the charm store.
31
- AsRepo (modelUUID string ) Repo
27
+ // WithMetadata returns a copy of the the client that will use the
28
+ // provided metadata during client requests.
29
+ WithMetadata (meta JujuMetadata ) (Client , error )
30
+
31
+ // Metadata returns the Juju metadata set on the client.
32
+ Metadata () JujuMetadata
33
+
34
+ // TODO(ericsnow) Add an "AsRepo() charmrepo.Interface" method.
32
35
}
33
36
34
37
// BaseClient exposes the functionality of the charm store, as provided
@@ -41,8 +44,14 @@ type Client interface {
41
44
// - UploadCharmWithRevision(id *charm.URL, ch charm.Charm, promulgatedRevision int) error
42
45
// - UploadBundleWithRevision()
43
46
type BaseClient interface {
44
- // TODO(ericsnow) Embed ResourcesClient here once the charm store
45
- // supports it.
47
+ ResourcesClient
48
+
49
+ // TODO(ericsnow) This should really be returning a type from
50
+ // charmrepo/csclient/params, but we don't have one yet.
51
+
52
+ // LatestRevisions returns the latest revision for each of the
53
+ // identified charms. The revisions in the provided URLs are ignored.
54
+ LatestRevisions ([]* charm.URL ) ([]charmrepo.CharmRevision , error )
46
55
47
56
// TODO(ericsnow) Replace use of Get with use of more specific API
48
57
// methods? We only use Get() for authorization on the Juju client
@@ -74,19 +83,50 @@ type ResourcesClient interface {
74
83
GetResource (cURL * charm.URL , resourceName string , revision int ) (io.ReadCloser , error )
75
84
}
76
85
77
- // client adapts csclient.Client to the needs of Juju.
78
- type client struct {
86
+ type baseClient struct {
79
87
* csclient.Client
80
88
fakeCharmStoreClient
89
+
90
+ asRepo func () * charmrepo.CharmStore
91
+ }
92
+
93
+ func newBaseClient (raw * csclient.Client ) * baseClient {
94
+ base := & baseClient {
95
+ Client : raw ,
96
+ }
97
+ base .asRepo = func () * charmrepo.CharmStore {
98
+ return charmrepo .NewCharmStoreFromClient (base .Client )
99
+ }
100
+ return base
101
+ }
102
+
103
+ // LatestRevisions implements BaseClient.
104
+ func (base baseClient ) LatestRevisions (cURLs []* charm.URL ) ([]charmrepo.CharmRevision , error ) {
105
+ // TODO(ericsnow) Fix this:
106
+ // We must use charmrepo.CharmStore since csclient.Client does not
107
+ // have the "Latest" method.
108
+ repo := base .asRepo ()
109
+ return repo .Latest (cURLs ... )
110
+ }
111
+
112
+ // TODO(ericsnow) Factor out a metadataClient type that embeds "client",
113
+ // and move the "meta" field there?
114
+
115
+ // client adapts csclient.Client to the needs of Juju.
116
+ type client struct {
117
+ BaseClient
81
118
io.Closer
119
+
120
+ newCopy func () * client
121
+ meta JujuMetadata
82
122
}
83
123
84
124
// NewClient returns a Juju charm store client for the given client
85
125
// config.
86
126
func NewClient (config csclient.Params ) Client {
87
127
base := csclient .New (config )
88
128
closer := ioutil .NopCloser (nil )
89
- return WrapBaseClient (base , closer )
129
+ return newClient (base , closer )
90
130
}
91
131
92
132
// NewDefaultClient returns a Juju charm store client using a default
@@ -100,36 +140,68 @@ func NewDefaultClient() Client {
100
140
// related to the client. If no closer is needed then pass in a no-op
101
141
// closer (e.g. ioutil.NopCloser).
102
142
func WrapBaseClient (base * csclient.Client , closer io.Closer ) Client {
103
- return & client {
104
- Client : base ,
105
- Closer : closer ,
143
+ return newClient (base , closer )
144
+ }
145
+
146
+ func newClient (base * csclient.Client , closer io.Closer ) * client {
147
+ c := & client {
148
+ BaseClient : newBaseClient (base ),
149
+ Closer : closer ,
150
+ }
151
+ c .newCopy = func () * client {
152
+ newBase := * base
153
+ copied := newClient (& newBase , closer )
154
+ copied .meta = c .meta
155
+ return copied
106
156
}
157
+ return c
107
158
}
108
159
109
- // AsRepo implements Client.
110
- func (client client ) AsRepo (envUUID string ) Repo {
111
- repo := charmrepo .NewCharmStoreFromClient (client .Client )
112
- if envUUID != "" {
113
- repo = repo .WithJujuAttrs (map [string ]string {
114
- "environment_uuid" : envUUID ,
115
- })
160
+ // WithMetadata returns a copy of the the client that will use the
161
+ // provided metadata during client requests.
162
+ func (c client ) WithMetadata (meta JujuMetadata ) (Client , error ) {
163
+ newClient := c .newCopy ()
164
+ newClient .meta = meta
165
+ // Note that we don't call meta.setOnClient() at this point.
166
+ // That is because not all charm store requests should include
167
+ // the metadata. The following do so:
168
+ // - LatestRevisions()
169
+ //
170
+ // If that changed then we would call meta.setOnClient() here.
171
+ // TODO(ericsnow) Use the metadata for *all* requests?
172
+ return newClient , nil
173
+ }
174
+
175
+ // Metadata implements Client.
176
+ func (c client ) Metadata () JujuMetadata {
177
+ // Note the value receiver, meaning that the returned metadata
178
+ // is a copy.
179
+ return c .meta
180
+ }
181
+
182
+ // LatestRevisions returns the latest revision for each of the
183
+ // identified charms. The revisions in the provided URLs are ignored.
184
+ // Note that this differs from BaseClient.LatestRevisions() exclusively
185
+ // due to taking into account Juju metadata (if any).
186
+ func (c * client ) LatestRevisions (cURLs []* charm.URL ) ([]charmrepo.CharmRevision , error ) {
187
+ if ! c .meta .IsZero () {
188
+ c = c .newCopy ()
189
+ if err := c .meta .setOnClient (c ); err != nil {
190
+ return nil , errors .Trace (err )
191
+ }
116
192
}
117
- return repo
193
+ return c . BaseClient . LatestRevisions ( cURLs )
118
194
}
119
195
120
196
// LatestCharmInfo returns the most up-to-date information about each
121
197
// of the identified charms at their latest revision. The revisions in
122
198
// the provided URLs are ignored.
123
- func LatestCharmInfo (client Client , modelUUID string , cURLs []* charm.URL ) ([]CharmInfoResult , error ) {
199
+ func LatestCharmInfo (client Client , cURLs []* charm.URL ) ([]CharmInfoResult , error ) {
124
200
now := time .Now ().UTC ()
125
201
126
- // We must use charmrepo.CharmStore since csclient.Client does not
127
- // have the "Latest" method.
128
- repo := client .AsRepo (modelUUID )
129
-
130
202
// Do a bulk call to get the revision info for all charms.
131
203
logger .Infof ("retrieving revision information for %d charms" , len (cURLs ))
132
- revResults , err := repo . Latest (cURLs ... )
204
+ revResults , err := client . LatestRevisions (cURLs )
133
205
if err != nil {
134
206
err = errors .Annotate (err , "while getting latest charm revision info" )
135
207
logger .Infof (err .Error ())
0 commit comments