19
28
// - UploadCharm(id *charm.URL, ch charm.Charm) (*charm.URL, error)
20
29
// - UploadCharmWithRevision(id *charm.URL, ch charm.Charm, promulgatedRevision int) error
21
30
// - UploadBundleWithRevision()
22
type Client interface {
23
// TODO(ericsnow) Replace use of Get with use of more specific API methods?
31
type BaseClient interface {
34
// TODO(ericsnow) This should really be returning a type from
35
// charmrepo/csclient/params, but we don't have one yet.
37
// LatestRevisions returns the latest revision for each of the
38
// identified charms. The revisions in the provided URLs are ignored.
39
LatestRevisions([]*charm.URL) ([]charmrepo.CharmRevision, error)
41
// TODO(ericsnow) Replace use of Get with use of more specific API
42
// methods? We only use Get() for authorization on the Juju client
25
45
// Get makes a GET request to the given path in the charm store. The
26
46
// path must have a leading slash, but must not include the host
41
65
// is streamed from the charm store. The charm's revision, if any,
42
66
// is ignored. If the identified resource is not in the charm store
43
67
// then errors.NotFound is returned.
44
GetResource(cURL *charm.URL, resourceName string, revision int) (io.ReadCloser, error)
68
GetResource(cURL *charm.URL, resourceName string, revision int) (charmresource.Resource, io.ReadCloser, error)
71
type baseClient struct {
74
asRepo func() *charmrepo.CharmStore
77
// TODO(ericsnow) Remove the fake methods once the charm store adds support.
79
// ListResources implements ResourcesClient.ListResources as a noop.
80
func (baseClient) ListResources(charmURLs []*charm.URL) ([][]charmresource.Resource, error) {
81
res := make([][]charmresource.Resource, len(charmURLs))
85
// GetResource implements ResourcesClient.GetResource as a noop.
86
func (baseClient) GetResource(cURL *charm.URL, resourceName string, revision int) (charmresource.Resource, io.ReadCloser, error) {
87
return charmresource.Resource{}, nil, errors.NotFoundf("resource %q", resourceName)
90
// TODO(ericsnow) We must make the Juju metadata available here since
91
// we must use charmrepo.NewCharmStore(), which doesn't give us an
94
func newBaseClient(raw *csclient.Client, config ClientConfig, meta JujuMetadata) *baseClient {
98
base.asRepo = func() *charmrepo.CharmStore {
99
// TODO(ericsnow) Use charmrepo.NewCharmStoreFromClient(), when available?
100
repo := charmrepo.NewCharmStore(config.NewCharmStoreParams)
101
return repo.WithJujuAttrs(meta.asAttrs())
106
// LatestRevisions implements BaseClient.
107
func (base baseClient) LatestRevisions(cURLs []*charm.URL) ([]charmrepo.CharmRevision, error) {
108
// TODO(ericsnow) Fix this:
109
// We must use charmrepo.CharmStore since csclient.Client does not
110
// have the "Latest" method.
111
repo := base.asRepo()
112
return repo.Latest(cURLs...)
115
// ClientConfig holds the configuration of a charm store client.
116
type ClientConfig struct {
117
charmrepo.NewCharmStoreParams
120
func (config ClientConfig) newCSClient() *csclient.Client {
121
return csclient.New(csclient.Params{
123
HTTPClient: config.HTTPClient,
124
VisitWebPage: config.VisitWebPage,
128
func (config ClientConfig) newCSRepo() *charmrepo.CharmStore {
129
return charmrepo.NewCharmStore(config.NewCharmStoreParams)
132
// TODO(ericsnow) Factor out a metadataClient type that embeds "client",
133
// and move the "meta" field there?
135
// Client adapts csclient.Client to the needs of Juju.
140
newCopy func() *Client
144
// NewClient returns a Juju charm store client for the given client
146
func NewClient(config ClientConfig) *Client {
147
base := config.newCSClient()
148
closer := ioutil.NopCloser(nil)
149
var meta JujuMetadata
150
return newClient(base, config, meta, closer)
153
func newClient(base *csclient.Client, config ClientConfig, meta JujuMetadata, closer io.Closer) *Client {
155
BaseClient: newBaseClient(base, config, meta),
159
c.newCopy = func() *Client {
160
newBase := *base // a copy
161
copied := newClient(&newBase, config, c.meta, closer)
167
// NewDefaultClient returns a Juju charm store client using a default
169
func NewDefaultClient() *Client {
170
return NewClient(ClientConfig{})
173
// WithMetadata returns a copy of the the client that will use the
174
// provided metadata during client requests.
175
func (c Client) WithMetadata(meta JujuMetadata) (*Client, error) {
176
newClient := c.newCopy()
177
newClient.meta = meta
178
// Note that we don't call meta.setOnClient() at this point.
179
// That is because not all charm store requests should include
180
// the metadata. The following do so:
181
// - LatestRevisions()
183
// If that changed then we would call meta.setOnClient() here.
184
// TODO(ericsnow) Use the metadata for *all* requests?
185
return newClient, nil
188
// Metadata returns a copy of the Juju metadata set on the client.
189
func (c Client) Metadata() JujuMetadata {
190
// Note the value receiver, meaning that the returned metadata
195
// LatestRevisions returns the latest revision for each of the
196
// identified charms. The revisions in the provided URLs are ignored.
197
// Note that this differs from BaseClient.LatestRevisions() exclusively
198
// due to taking into account Juju metadata (if any).
199
func (c *Client) LatestRevisions(cURLs []*charm.URL) ([]charmrepo.CharmRevision, error) {
200
if !c.meta.IsZero() {
202
if err := c.meta.setOnClient(c.BaseClient); err != nil {
203
return nil, errors.Trace(err)
206
return c.BaseClient.LatestRevisions(cURLs)