-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge latest changes from feature-resources.
- Loading branch information
Showing
35 changed files
with
851 additions
and
256 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// Copyright 2016 Canonical Ltd. | ||
// Licensed under the AGPLv3, see LICENCE file for details. | ||
|
||
package charmcmd | ||
|
||
import ( | ||
"github.com/juju/cmd" | ||
) | ||
|
||
var charmDoc = ` | ||
"juju charm" is the the juju CLI equivalent of the "charm" command used | ||
by charm authors, though only applicable functionality is mirrored. | ||
` | ||
|
||
const charmPurpose = "interact with charms" | ||
|
||
// Command is the top-level command wrapping all backups functionality. | ||
type Command struct { | ||
cmd.SuperCommand | ||
} | ||
|
||
// NewSuperCommand returns a new charm super-command. | ||
func NewSuperCommand() *Command { | ||
charmCmd := &Command{ | ||
SuperCommand: *cmd.NewSuperCommand( | ||
cmd.SuperCommandParams{ | ||
Name: "charm", | ||
Doc: charmDoc, | ||
UsagePrefix: "juju", | ||
Purpose: charmPurpose, | ||
}, | ||
), | ||
} | ||
spec := newCharmstoreSpec() | ||
|
||
// Sub-commands may be registered directly here, like so: | ||
//charmCmd.Register(newXXXCommand(spec)) | ||
|
||
// ...or externally via RegisterSubCommand(). | ||
for _, newSubCommand := range registeredSubCommands { | ||
command := newSubCommand(spec) | ||
charmCmd.Register(command) | ||
} | ||
|
||
return charmCmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// Copyright 2016 Canonical Ltd. | ||
// Licensed under the AGPLv3, see LICENCE file for details. | ||
|
||
package charmcmd_test | ||
|
||
import ( | ||
"github.com/juju/testing" | ||
jc "github.com/juju/testing/checkers" | ||
gc "gopkg.in/check.v1" | ||
|
||
"github.com/juju/juju/cmd/juju/charmcmd" | ||
) | ||
|
||
type CharmSuite struct { | ||
testing.IsolationSuite | ||
} | ||
|
||
var _ = gc.Suite(&CharmSuite{}) | ||
|
||
func (s *CharmSuite) SetUpTest(c *gc.C) { | ||
s.IsolationSuite.SetUpTest(c) | ||
} | ||
|
||
// TODO(ericsnow) Copy some tests from cmd/juju/commands/main_test.go? | ||
|
||
func (s *CharmSuite) Test(c *gc.C) { | ||
return | ||
// TODO(ericsnow) Finish! | ||
chCmd := charmcmd.NewSuperCommand() | ||
|
||
c.Check(chCmd, jc.DeepEquals, nil) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// Copyright 2016 Canonical Ltd. | ||
// Licensed under the AGPLv3, see LICENCE file for details. | ||
|
||
package charmcmd_test | ||
|
||
import ( | ||
"testing" | ||
|
||
gc "gopkg.in/check.v1" | ||
) | ||
|
||
func TestPackage(t *testing.T) { | ||
gc.TestingT(t) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
// Copyright 2016 Canonical Ltd. | ||
// Licensed under the AGPLv3, see LICENCE file for details. | ||
|
||
package charmcmd | ||
|
||
import ( | ||
"io" | ||
"net/http" | ||
"os" | ||
|
||
"github.com/juju/errors" | ||
"github.com/juju/persistent-cookiejar" | ||
"gopkg.in/juju/charmrepo.v2-unstable" | ||
"gopkg.in/juju/charmrepo.v2-unstable/csclient" | ||
"gopkg.in/macaroon-bakery.v1/httpbakery" | ||
) | ||
|
||
// TODO(ericsnow) Factor out code from cmd/juju/commands/common.go and | ||
// cmd/envcmd/base.go into cmd/charmstore.go and cmd/apicontext.go. Then | ||
// use those here instead of copy-and-pasting here. | ||
|
||
// CharmstoreClient exposes the functionality of the charm store client. | ||
type CharmstoreClient interface { | ||
io.Closer | ||
} | ||
|
||
/////////////////// | ||
// The charmstoreSpec code is based loosely on code in cmd/juju/commands/deploy.go. | ||
|
||
// CharmstoreSpec provides the functionality needed to open a charm | ||
// store client. | ||
type CharmstoreSpec interface { | ||
// Connect connects to the specified charm store. | ||
Connect() (CharmstoreClient, error) | ||
} | ||
|
||
type charmstoreSpec struct { | ||
params charmrepo.NewCharmStoreParams | ||
} | ||
|
||
// newCharmstoreSpec creates a new charm store spec with default | ||
// settings. | ||
func newCharmstoreSpec() CharmstoreSpec { | ||
return &charmstoreSpec{ | ||
params: charmrepo.NewCharmStoreParams{ | ||
//URL: We use the default. | ||
//HTTPClient: We set it later. | ||
VisitWebPage: httpbakery.OpenWebBrowser, | ||
}, | ||
} | ||
} | ||
|
||
// Connect implements CharmstoreSpec. | ||
func (cs charmstoreSpec) Connect() (CharmstoreClient, error) { | ||
params, apiContext, err := cs.connect() | ||
if err != nil { | ||
return nil, errors.Trace(err) | ||
} | ||
|
||
baseClient := csclient.New(csclient.Params{ | ||
URL: params.URL, | ||
HTTPClient: params.HTTPClient, | ||
VisitWebPage: params.VisitWebPage, | ||
}) | ||
|
||
csClient := &charmstoreClient{ | ||
Client: baseClient, | ||
apiContext: apiContext, | ||
} | ||
return csClient, nil | ||
} | ||
|
||
// TODO(ericsnow) Also add charmstoreSpec.Repo() -> charmrepo.Interface? | ||
|
||
func (cs charmstoreSpec) connect() (charmrepo.NewCharmStoreParams, *apiContext, error) { | ||
apiContext, err := newAPIContext() | ||
if err != nil { | ||
return charmrepo.NewCharmStoreParams{}, nil, errors.Trace(err) | ||
} | ||
|
||
params := cs.params // a copy | ||
params.HTTPClient = apiContext.HTTPClient() | ||
return params, apiContext, nil | ||
} | ||
|
||
/////////////////// | ||
// charmstoreClient is based loosely on cmd/juju/commands/common.go. | ||
|
||
type charmstoreClient struct { | ||
*csclient.Client | ||
*apiContext | ||
} | ||
|
||
// Close implements io.Closer. | ||
func (cs *charmstoreClient) Close() error { | ||
return cs.apiContext.Close() | ||
} | ||
|
||
/////////////////// | ||
// For the most part, apiContext is copied directly from cmd/envcmd/base.go. | ||
|
||
// newAPIContext returns a new api context, which should be closed | ||
// when done with. | ||
func newAPIContext() (*apiContext, error) { | ||
jar, err := cookiejar.New(&cookiejar.Options{ | ||
Filename: cookieFile(), | ||
}) | ||
if err != nil { | ||
return nil, errors.Trace(err) | ||
} | ||
client := httpbakery.NewClient() | ||
client.Jar = jar | ||
client.VisitWebPage = httpbakery.OpenWebBrowser | ||
|
||
return &apiContext{ | ||
jar: jar, | ||
client: client, | ||
}, nil | ||
} | ||
|
||
// apiContext is a convenience type that can be embedded wherever | ||
// we need an API connection. | ||
// It also stores a bakery bakery client allowing the API | ||
// to be used using macaroons to authenticate. It stores | ||
// obtained macaroons and discharges in a cookie jar file. | ||
type apiContext struct { | ||
jar *cookiejar.Jar | ||
client *httpbakery.Client | ||
} | ||
|
||
// Close saves the embedded cookie jar. | ||
func (c *apiContext) Close() error { | ||
if err := c.jar.Save(); err != nil { | ||
return errors.Annotatef(err, "cannot save cookie jar") | ||
} | ||
return nil | ||
} | ||
|
||
// HTTPClient returns an http.Client that contains the loaded | ||
// persistent cookie jar. | ||
func (ctx *apiContext) HTTPClient() *http.Client { | ||
return ctx.client.Client | ||
} | ||
|
||
// cookieFile returns the path to the cookie used to store authorization | ||
// macaroons. The returned value can be overridden by setting the | ||
// JUJU_COOKIEFILE or GO_COOKIEFILE environment variables. | ||
func cookieFile() string { | ||
if file := os.Getenv("JUJU_COOKIEFILE"); file != "" { | ||
return file | ||
} | ||
return cookiejar.DefaultCookieFile() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// Copyright 2016 Canonical Ltd. | ||
// Licensed under the AGPLv3, see LICENCE file for details. | ||
|
||
package charmcmd | ||
|
||
import ( | ||
"github.com/juju/cmd" | ||
"github.com/juju/errors" | ||
) | ||
|
||
var registeredSubCommands []func(CharmstoreSpec) cmd.Command | ||
|
||
// RegisterSubCommand adds the provided func to the set of those that | ||
// will be called when the juju command runs. Each returned command will | ||
// be registered with the identified "juju" sub-supercommand. | ||
func RegisterSubCommand(newCommand func(CharmstoreSpec) cmd.Command) { | ||
registeredSubCommands = append(registeredSubCommands, newCommand) | ||
} | ||
|
||
// NewCommandBase returns a new CommandBase. | ||
func NewCommandBase(spec CharmstoreSpec) *CommandBase { | ||
return &CommandBase{ | ||
spec: newCharmstoreSpec(), | ||
} | ||
} | ||
|
||
// CommandBase is the type that should be embedded in "juju charm" | ||
// sub-commands. | ||
type CommandBase struct { | ||
cmd.CommandBase | ||
spec CharmstoreSpec | ||
} | ||
|
||
// Connect implements CommandBase. | ||
func (c *CommandBase) Connect() (CharmstoreClient, error) { | ||
if c.spec == nil { | ||
return nil, errors.Errorf("missing charm store spec") | ||
} | ||
client, err := c.spec.Connect() | ||
if err != nil { | ||
return nil, errors.Trace(err) | ||
} | ||
|
||
return client, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.