Skip to content

Commit

Permalink
Client Facade and CLI changes for export bundle
Browse files Browse the repository at this point in the history
  • Loading branch information
vinu2003 committed Jul 23, 2018
1 parent 161d387 commit ec05eb2
Show file tree
Hide file tree
Showing 7 changed files with 394 additions and 0 deletions.
50 changes: 50 additions & 0 deletions api/bundle/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2018 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// Package bundle provides access to the bundle api facade.
// This facade contains api calls that are specific to bundles.

package bundle

import (
"github.com/juju/errors"
"github.com/juju/loggo"

"github.com/juju/juju/api/base"
"github.com/juju/juju/apiserver/params"
)

var logger = loggo.GetLogger("juju.api.bundle")

// Client allows access to the bundle API end point.
type Client struct {
base.ClientFacade
st base.APICallCloser
facade base.FacadeCaller
}

// NewClient creates a new client for accessing the bundle api.
func NewClient(st base.APICallCloser) *Client {
frontend, backend := base.NewClientFacade(st, "Bundle")
return &Client{
ClientFacade: frontend,
st: st,
facade: backend}
}

// ExportBundle exports the current model configuration.
func (c *Client) ExportBundle() (string, error) {
var result params.StringResult
if bestVer := c.BestAPIVersion(); bestVer < 2 {
return "", errors.Errorf("command not supported on v%d", bestVer)
}

if err := c.facade.FacadeCall("ExportBundle", nil, &result); err != nil {
return "", errors.Trace(err)
}

if len(result.Result) == 0 {
return "", errors.Errorf("result obtained is incorrect")
}
return result.Result, nil
}
92 changes: 92 additions & 0 deletions api/bundle/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright 2018 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package bundle_test

import (
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"

basetesting "github.com/juju/juju/api/base/testing"
"github.com/juju/juju/api/bundle"
"github.com/juju/juju/apiserver/params"
coretesting "github.com/juju/juju/testing"
)

type bundleMockSuite struct {
coretesting.BaseSuite
bundleClient *bundle.Client
}

var _ = gc.Suite(&bundleMockSuite{})

func newClient(f basetesting.APICallerFunc, ver int) *bundle.Client {
return bundle.NewClient(basetesting.BestVersionCaller{f, ver})
}

func (s *bundleMockSuite) TestFailExportBundlev1(c *gc.C) {
client := newClient(
func(objType string,
version int,
id,
request string,
args,
response interface{},
) error {
c.Check(objType, gc.Equals, "Bundle")
c.Check(id, gc.Equals, "")
c.Check(request, gc.Equals, "ExportBundle")
c.Assert(args, gc.Equals, nil)
result := response.(*params.StringResult)
result.Result = ""
return nil
}, 1,
)
result, err := client.ExportBundle()
c.Assert(err, gc.ErrorMatches, "command not supported on v1")
c.Assert(result, jc.DeepEquals, "")
}

func (s *bundleMockSuite) TestExportBundlev2(c *gc.C) {
client := newClient(
func(objType string, version int,
id,
request string,
args,
response interface{},
) error {
c.Check(objType, gc.Equals, "Bundle")
c.Check(id, gc.Equals, "")
c.Check(request, gc.Equals, "ExportBundle")
c.Assert(args, gc.Equals, nil)
result := response.(*params.StringResult)
result.Result = "applications:\n " +
"ubuntu:\n " +
"charm: cs:trusty/ubuntu\n " +
"series: trusty\n " +
"num_units: 1\n " +
"to:\n " +
"- \"0\"\n " +
"options:\n " +
"key: value\n" +
"series: xenial\n" +
"relations:\n" +
"- []\n"
return nil
}, 2,
)
result, err := client.ExportBundle()
c.Assert(err, jc.ErrorIsNil)
c.Assert(result, jc.DeepEquals, "applications:\n "+
"ubuntu:\n "+
"charm: cs:trusty/ubuntu\n "+
"series: trusty\n "+
"num_units: 1\n "+
"to:\n "+
"- \"0\"\n "+
"options:\n "+
"key: value\n"+
"series: xenial\n"+
"relations:\n"+
"- []\n")
}
14 changes: 14 additions & 0 deletions api/bundle/package_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2018 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package bundle_test

import (
"testing"

gc "gopkg.in/check.v1"
)

func TestAll(t *testing.T) {
gc.TestingT(t)
}
1 change: 1 addition & 0 deletions cmd/juju/commands/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ func registerCommands(r commandRegistry, ctx *cmd.Context) {
if featureflag.Enabled(feature.DeveloperMode) {
r.Register(model.NewDumpCommand())
r.Register(model.NewDumpDBCommand())
r.Register(model.NewExportBundleCommand())
}

// Manage and control actions
Expand Down
7 changes: 7 additions & 0 deletions cmd/juju/model/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ func NewDumpDBCommandForTest(api DumpDBAPI, store jujuclient.ClientStore) cmd.Co
return modelcmd.Wrap(cmd)
}

// NewDumpCommandForTest returns a DumpCommand with the api provided as specified.
func NewExportBundleCommandForTest(api ExportBundleModelAPI, store jujuclient.ClientStore) cmd.Command {
cmd := &exportBundleCommand{api: api}
cmd.SetClientStore(store)
return modelcmd.Wrap(cmd)
}

// NewDestroyCommandForTest returns a DestroyCommand with the api provided as specified.
func NewDestroyCommandForTest(
api DestroyModelAPI,
Expand Down
117 changes: 117 additions & 0 deletions cmd/juju/model/exportbundle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copyright 2018 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package model

import (
"fmt"
"os"

"github.com/juju/cmd"
"github.com/juju/errors"
"github.com/juju/gnuflag"

"github.com/juju/juju/api/bundle"
"github.com/juju/juju/cmd/modelcmd"
"github.com/juju/juju/cmd/output"
)

// NewExportBundleCommand returns a fully constructed export bundle command.
func NewExportBundleCommand() cmd.Command {
return modelcmd.Wrap(&exportBundleCommand{})
}

type exportBundleCommand struct {
modelcmd.ModelCommandBase
out cmd.Output
api ExportBundleModelAPI
// name of the charm bundle file.
Filename string
}

const exportBundleHelpDoc = `
Exports the current model configuration into a YAML file.
Examples:
juju export-bundle
If --filename is not used, the bundle is displayed in stdout.
`

// Info implements Command.
func (c *exportBundleCommand) Info() *cmd.Info {
return &cmd.Info{
Name: "export-bundle",
Purpose: "Exports the current model configuration in a charm bundle.",
Doc: exportBundleHelpDoc,
}
}

// SetFlags implements Command.
func (c *exportBundleCommand) SetFlags(f *gnuflag.FlagSet) {
c.ModelCommandBase.SetFlags(f)
c.out.AddFlags(f, "yaml", output.DefaultFormatters)
f.StringVar(&c.Filename, "filename", "", "Export Model")
}

// Init implements Command.
func (c *exportBundleCommand) Init(args []string) error {
return cmd.CheckEmpty(args)
}

// ExportBundleAPI specifies the used function calls of the ModelManager.
type ExportBundleModelAPI interface {
Close() error
BestAPIVersion() int
ExportBundle() (string, error)
}

func (c *exportBundleCommand) getAPI() (ExportBundleModelAPI, error) {
if c.api != nil {
return c.api, nil
}

api, err := c.NewAPIRoot()
if err != nil {
return nil, errors.Trace(err)
}
return bundle.NewClient(api), nil
}

// Run implements Command.
func (c *exportBundleCommand) Run(ctx *cmd.Context) error {
client, err := c.getAPI()
if err != nil {
return err
}
defer client.Close()

var result string
if client.BestAPIVersion() > 1 {
result, err = client.ExportBundle()
if err != nil {
return err
}
}

if c.Filename != "" {
filename := c.Filename + ".yaml"
file, err := os.Create(filename)
if err != nil {
return errors.Annotate(err, "while creating local file")
}
defer file.Close()

// Write out the result.
_, err = file.WriteString(result)
if err != nil {
return errors.Annotate(err, "while copying in local file")
}

// Print the local filename.
fmt.Fprintln(ctx.Stdout, "Bundle successfully exported to", filename)
} else {
c.out.Write(ctx, result)
}
return nil
}
Loading

0 comments on commit ec05eb2

Please sign in to comment.