~juju-qa/ubuntu/xenial/juju/xenial-2.0-beta3

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/cmd/juju/charmcmd/store.go

  • Committer: Martin Packman
  • Date: 2016-03-30 19:31:08 UTC
  • mfrom: (1.1.41)
  • Revision ID: martin.packman@canonical.com-20160330193108-h9iz3ak334uk0z5r
Merge new upstream source 2.0~beta3

Show diffs side-by-side

added added

removed removed

Lines of Context:
4
4
package charmcmd
5
5
 
6
6
import (
7
 
        "io"
8
 
        "net/http"
9
 
        "os"
10
 
 
 
7
        "github.com/juju/cmd"
11
8
        "github.com/juju/errors"
12
 
        "github.com/juju/persistent-cookiejar"
13
9
        "gopkg.in/juju/charmrepo.v2-unstable"
14
 
        "gopkg.in/juju/charmrepo.v2-unstable/csclient"
15
 
        "gopkg.in/macaroon-bakery.v1/httpbakery"
 
10
 
 
11
        "github.com/juju/juju/charmstore"
 
12
        "github.com/juju/juju/cmd/modelcmd"
16
13
)
17
14
 
18
 
// TODO(ericsnow) Factor out code from cmd/juju/commands/common.go and
19
 
// cmd/envcmd/base.go into cmd/charmstore.go and cmd/apicontext.go. Then
20
 
// use those here instead of copy-and-pasting here.
21
 
 
22
 
// CharmstoreClient exposes the functionality of the charm store client.
23
 
type CharmstoreClient interface {
24
 
        // TODO(ericsnow) Embed github.com/juju/juju/charmstore.Client.
25
 
        io.Closer
26
 
}
27
 
 
28
15
///////////////////
29
16
// The charmstoreSpec code is based loosely on code in cmd/juju/commands/deploy.go.
30
17
 
32
19
// store client.
33
20
type CharmstoreSpec interface {
34
21
        // Connect connects to the specified charm store.
35
 
        Connect() (CharmstoreClient, error)
 
22
        Connect(ctx *cmd.Context) (*charmstore.Client, error)
36
23
}
37
24
 
38
 
type charmstoreSpec struct {
39
 
        params charmrepo.NewCharmStoreParams
40
 
}
 
25
type charmstoreSpec struct{}
41
26
 
42
27
// newCharmstoreSpec creates a new charm store spec with default
43
28
// settings.
44
29
func newCharmstoreSpec() CharmstoreSpec {
45
 
        return &charmstoreSpec{
46
 
                params: charmrepo.NewCharmStoreParams{
47
 
                        //URL:        We use the default.
48
 
                        //HTTPClient: We set it later.
49
 
                        VisitWebPage: httpbakery.OpenWebBrowser,
 
30
        return &charmstoreSpec{}
 
31
}
 
32
 
 
33
// Connect implements CharmstoreSpec.
 
34
func (cs charmstoreSpec) Connect(ctx *cmd.Context) (*charmstore.Client, error) {
 
35
        // Note that creating the API context in Connect is technically
 
36
        // wrong, as it means we'll be creating the bakery context
 
37
        // (and reading/writing the cookies) each time it's called.
 
38
        // TODO(ericsnow) Use modelcmd.ModelCommandBase instead.
 
39
        apiContext, err := modelcmd.NewAPIContext(ctx)
 
40
        if err != nil {
 
41
                return nil, errors.Trace(err)
 
42
        }
 
43
        client := charmstore.NewClient(charmstore.ClientConfig{
 
44
                charmrepo.NewCharmStoreParams{
 
45
                        BakeryClient: apiContext.BakeryClient,
50
46
                },
51
 
        }
52
 
}
53
 
 
54
 
// Connect implements CharmstoreSpec.
55
 
func (cs charmstoreSpec) Connect() (CharmstoreClient, error) {
56
 
        params, apiContext, err := cs.connect()
57
 
        if err != nil {
58
 
                return nil, errors.Trace(err)
59
 
        }
60
 
 
61
 
        baseClient := csclient.New(csclient.Params{
62
 
                URL:          params.URL,
63
 
                HTTPClient:   params.HTTPClient,
64
 
                VisitWebPage: params.VisitWebPage,
65
 
        })
66
 
 
67
 
        csClient := &charmstoreClient{
68
 
                Client:     baseClient,
69
 
                apiContext: apiContext,
70
 
        }
71
 
        return csClient, nil
72
 
}
73
 
 
74
 
// TODO(ericsnow) Also add charmstoreSpec.Repo() -> charmrepo.Interface?
75
 
 
76
 
func (cs charmstoreSpec) connect() (charmrepo.NewCharmStoreParams, *apiContext, error) {
77
 
        apiContext, err := newAPIContext()
78
 
        if err != nil {
79
 
                return charmrepo.NewCharmStoreParams{}, nil, errors.Trace(err)
80
 
        }
81
 
 
82
 
        params := cs.params // a copy
83
 
        params.HTTPClient = apiContext.HTTPClient()
84
 
        return params, apiContext, nil
85
 
}
86
 
 
87
 
///////////////////
88
 
// charmstoreClient is based loosely on cmd/juju/commands/common.go.
89
 
 
90
 
type charmstoreClient struct {
91
 
        *csclient.Client
92
 
        *apiContext
93
 
}
94
 
 
95
 
// Close implements io.Closer.
96
 
func (cs *charmstoreClient) Close() error {
97
 
        return cs.apiContext.Close()
98
 
}
99
 
 
100
 
///////////////////
101
 
// For the most part, apiContext is copied directly from cmd/envcmd/base.go.
102
 
 
103
 
// newAPIContext returns a new api context, which should be closed
104
 
// when done with.
105
 
func newAPIContext() (*apiContext, error) {
106
 
        jar, err := cookiejar.New(&cookiejar.Options{
107
 
                Filename: cookieFile(),
108
 
        })
109
 
        if err != nil {
110
 
                return nil, errors.Trace(err)
111
 
        }
112
 
        client := httpbakery.NewClient()
113
 
        client.Jar = jar
114
 
        client.VisitWebPage = httpbakery.OpenWebBrowser
115
 
 
116
 
        return &apiContext{
117
 
                jar:    jar,
118
 
                client: client,
119
 
        }, nil
120
 
}
121
 
 
122
 
// apiContext is a convenience type that can be embedded wherever
123
 
// we need an API connection.
124
 
// It also stores a bakery bakery client allowing the API
125
 
// to be used using macaroons to authenticate. It stores
126
 
// obtained macaroons and discharges in a cookie jar file.
127
 
type apiContext struct {
128
 
        jar    *cookiejar.Jar
129
 
        client *httpbakery.Client
130
 
}
131
 
 
132
 
// Close saves the embedded cookie jar.
133
 
func (c *apiContext) Close() error {
134
 
        if err := c.jar.Save(); err != nil {
135
 
                return errors.Annotatef(err, "cannot save cookie jar")
136
 
        }
137
 
        return nil
138
 
}
139
 
 
140
 
// HTTPClient returns an http.Client that contains the loaded
141
 
// persistent cookie jar.
142
 
func (ctx *apiContext) HTTPClient() *http.Client {
143
 
        return ctx.client.Client
144
 
}
145
 
 
146
 
// cookieFile returns the path to the cookie used to store authorization
147
 
// macaroons. The returned value can be overridden by setting the
148
 
// JUJU_COOKIEFILE or GO_COOKIEFILE environment variables.
149
 
func cookieFile() string {
150
 
        if file := os.Getenv("JUJU_COOKIEFILE"); file != "" {
151
 
                return file
152
 
        }
153
 
        return cookiejar.DefaultCookieFile()
 
47
        })
 
48
        client.Closer = apiContext
 
49
 
 
50
        return client, nil
154
51
}