Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Import storage review fixes #7646

Merged
merged 2 commits into from
Jul 18, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/storage/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ func (c *Client) Detach(storageIds []string) ([]params.ErrorResult, error) {
}

// Import imports storage into the model.
func (c *Client) ImportStorage(
func (c *Client) Import(
kind storage.StorageKind,
storagePool string,
storageProviderId string,
Expand Down
61 changes: 61 additions & 0 deletions api/storage/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/juju/juju/api/storage"
"github.com/juju/juju/apiserver/common"
"github.com/juju/juju/apiserver/params"
jujustorage "github.com/juju/juju/storage"
"github.com/juju/juju/testing"
)

Expand Down Expand Up @@ -771,3 +772,63 @@ func (s *storageMockSuite) TestAttachArityMismatch(c *gc.C) {
_, err := client.Attach("foo/0", []string{"bar/1", "baz/2"})
c.Check(err, gc.ErrorMatches, `expected 2 result\(s\), got 3`)
}

func (s *storageMockSuite) TestImport(c *gc.C) {
apiCaller := basetesting.APICallerFunc(
func(objType string,
version int,
id, request string,
a, result interface{},
) error {
c.Check(objType, gc.Equals, "Storage")
c.Check(id, gc.Equals, "")
c.Check(request, gc.Equals, "Import")
c.Check(a, jc.DeepEquals, params.BulkImportStorageParams{[]params.ImportStorageParams{{
Kind: params.StorageKindBlock,
Pool: "foo",
ProviderId: "bar",
StorageName: "baz",
}}})
c.Assert(result, gc.FitsTypeOf, &params.ImportStorageResults{})
results := result.(*params.ImportStorageResults)
results.Results = []params.ImportStorageResult{{
Result: &params.ImportStorageDetails{
StorageTag: "storage-qux-0",
},
}}
return nil
},
)
client := storage.NewClient(apiCaller)
storageTag, err := client.Import(jujustorage.StorageKindBlock, "foo", "bar", "baz")
c.Assert(err, jc.ErrorIsNil)
c.Assert(storageTag, gc.Equals, names.NewStorageTag("qux/0"))
}

func (s *storageMockSuite) TestImportError(c *gc.C) {
apiCaller := basetesting.APICallerFunc(
func(objType string, version int, id, request string, a, result interface{}) error {
results := result.(*params.ImportStorageResults)
results.Results = []params.ImportStorageResult{{
Error: &params.Error{Message: "qux"},
}}
return nil
},
)
client := storage.NewClient(apiCaller)
_, err := client.Import(jujustorage.StorageKindBlock, "foo", "bar", "baz")
c.Check(err, gc.ErrorMatches, "qux")
}

func (s *storageMockSuite) TestImportArityMismatch(c *gc.C) {
apiCaller := basetesting.APICallerFunc(
func(objType string, version int, id, request string, a, result interface{}) error {
results := result.(*params.ImportStorageResults)
results.Results = []params.ImportStorageResult{{}, {}}
return nil
},
)
client := storage.NewClient(apiCaller)
_, err := client.Import(jujustorage.StorageKindBlock, "foo", "bar", "baz")
c.Check(err, gc.ErrorMatches, `expected 1 result, got 2`)
}
4 changes: 2 additions & 2 deletions apiserver/facades/client/storage/shim.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ type storageAccess interface {
// identified unit.
UnitStorageAttachments(names.UnitTag) ([]state.StorageAttachment, error)

// ImportFilesystem imports an existing filesystem into the model.
ImportFilesystem(f state.FilesystemInfo, v *state.VolumeInfo, storageName string) (names.StorageTag, error)
// AddExistingFilesystem imports an existing filesystem into the model.
AddExistingFilesystem(f state.FilesystemInfo, v *state.VolumeInfo, storageName string) (names.StorageTag, error)
}

var getState = func(st *state.State) storageAccess {
Expand Down
2 changes: 1 addition & 1 deletion apiserver/facades/client/storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -1005,7 +1005,7 @@ func (a *APIv4) importFilesystem(
filesystemInfo.Size = info.Size
}

storageTag, err := a.storage.ImportFilesystem(filesystemInfo, volumeInfo, arg.StorageName)
storageTag, err := a.storage.AddExistingFilesystem(filesystemInfo, volumeInfo, arg.StorageName)
if err != nil {
return nil, errors.Trace(err)
}
Expand Down
14 changes: 13 additions & 1 deletion cmd/juju/storage/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/juju/cmd"
"github.com/juju/errors"

apistorage "github.com/juju/juju/api/storage"
"github.com/juju/juju/cmd/modelcmd"
"github.com/juju/juju/jujuclient"
"github.com/juju/juju/storage"
Expand Down Expand Up @@ -43,7 +44,8 @@ type NewStorageImporterFunc func(*StorageCommandBase) (StorageImporter, error)
// NewStorageImporter returns a new StorageImporter,
// given a StorageCommandBase.
func NewStorageImporter(cmd *StorageCommandBase) (StorageImporter, error) {
return cmd.NewStorageAPI()
api, err := cmd.NewStorageAPI()
return apiStorageImporter{api}, err
}

const (
Expand Down Expand Up @@ -152,3 +154,13 @@ type StorageImporter interface {
storagePool, storageProviderId, storageName string,
) (names.StorageTag, error)
}

type apiStorageImporter struct {
*apistorage.Client
}

func (a apiStorageImporter) ImportStorage(
kind storage.StorageKind, storagePool, storageProviderId, storageName string,
) (names.StorageTag, error) {
return a.Import(kind, storagePool, storageProviderId, storageName)
}
93 changes: 60 additions & 33 deletions state/filesystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package state
import (
"fmt"
"path"
"regexp"
"strings"
"time"

Expand Down Expand Up @@ -780,43 +781,19 @@ func removeFilesystemOps(st *State, filesystem Filesystem, assert interface{}) (
return ops, nil
}

// ImportFilesystem imports an existing, already-provisioned
// AddExistingFilesystem imports an existing, already-provisioned
// filesystem into the model. The model will start out with
// the status "detached". The filesystem and associated backing
// volume (if any) will be associated with the given storage
// name, with the allocated storage tag being returned.
func (st *State) ImportFilesystem(
func (st *State) AddExistingFilesystem(
info FilesystemInfo,
backingVolume *VolumeInfo,
storageName string,
) (_ names.StorageTag, err error) {
defer errors.DeferredAnnotatef(&err, "cannot import filesystem")
if info.Pool == "" {
return names.StorageTag{}, errors.NotValidf("empty pool name")
}
if backingVolume == nil {
if info.FilesystemId == "" {
return names.StorageTag{}, errors.NotValidf("empty filesystem ID")
}
} else {
if info.FilesystemId != "" {
return names.StorageTag{}, errors.NotValidf("non-empty filesystem ID with backing volume")
}
if backingVolume.VolumeId == "" {
return names.StorageTag{}, errors.NotValidf("empty backing volume ID")
}
if backingVolume.Pool != info.Pool {
return names.StorageTag{}, errors.Errorf(
"volume pool %q does not match filesystem pool %q",
backingVolume.Pool, info.Pool,
)
}
if backingVolume.Size != info.Size {
return names.StorageTag{}, errors.Errorf(
"volume size %d does not match filesystem size %d",
backingVolume.Size, info.Size,
)
}
defer errors.DeferredAnnotatef(&err, "cannot add existing filesystem")
if err := validateAddExistingFilesystem(st, info, backingVolume, storageName); err != nil {
return names.StorageTag{}, errors.Trace(err)
}
storageId, err := newStorageInstanceId(st, storageName)
if err != nil {
Expand Down Expand Up @@ -860,6 +837,60 @@ func (st *State) ImportFilesystem(
return storageTag, nil
}

var storageNameRE = regexp.MustCompile(names.StorageNameSnippet)

func validateAddExistingFilesystem(
st *State,
info FilesystemInfo,
backingVolume *VolumeInfo,
storageName string,
) error {
if !storage.IsValidPoolName(info.Pool) {
return errors.NotValidf("pool name %q", info.Pool)
}
if !storageNameRE.MatchString(storageName) {
return errors.NotValidf("storage name %q", storageName)
}
if backingVolume == nil {
if info.FilesystemId == "" {
return errors.NotValidf("empty filesystem ID")
}
} else {
if info.FilesystemId != "" {
return errors.NotValidf("non-empty filesystem ID with backing volume")
}
if backingVolume.VolumeId == "" {
return errors.NotValidf("empty backing volume ID")
}
if backingVolume.Pool != info.Pool {
return errors.Errorf(
"volume pool %q does not match filesystem pool %q",
backingVolume.Pool, info.Pool,
)
}
if backingVolume.Size != info.Size {
return errors.Errorf(
"volume size %d does not match filesystem size %d",
backingVolume.Size, info.Size,
)
}
}
_, provider, err := poolStorageProvider(st, info.Pool)
if err != nil {
return errors.Trace(err)
}
if !provider.Supports(storage.StorageKindFilesystem) {
if backingVolume == nil {
return errors.New("backing volume info missing")
}
} else {
if backingVolume != nil {
return errors.New("unexpected volume info")
}
}
return nil
}

// filesystemAttachmentId returns a filesystem attachment document ID,
// given the corresponding filesystem name and machine ID.
func filesystemAttachmentId(machineId, filesystemId string) string {
Expand Down Expand Up @@ -946,10 +977,6 @@ func (st *State) addFilesystemOps(params FilesystemParams, machineId string) ([]
}
volumeId = volumeTag.Id()
ops = append(ops, volumeOps...)
} else {
if params.volumeInfo != nil {
return nil, names.FilesystemTag{}, names.VolumeTag{}, errors.Errorf("unexpected volume info")
}
}

statusDoc := statusDoc{
Expand Down
34 changes: 17 additions & 17 deletions state/filesystem_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1124,13 +1124,13 @@ func (s *FilesystemStateSuite) TestFilesystemAttachmentLocationConflict(c *gc.C)
`mount point "/srv/within" for "data" storage`)
}

func (s *FilesystemStateSuite) TestImportFilesystem(c *gc.C) {
func (s *FilesystemStateSuite) TestAddExistingFilesystem(c *gc.C) {
fsInfoIn := state.FilesystemInfo{
Pool: "modelscoped",
Size: 123,
FilesystemId: "foo",
}
storageTag, err := s.State.ImportFilesystem(fsInfoIn, nil, "pgdata")
storageTag, err := s.State.AddExistingFilesystem(fsInfoIn, nil, "pgdata")
c.Assert(err, jc.ErrorIsNil)
c.Assert(storageTag, gc.Equals, names.NewStorageTag("pgdata/0"))

Expand All @@ -1145,16 +1145,16 @@ func (s *FilesystemStateSuite) TestImportFilesystem(c *gc.C) {
c.Assert(fsStatus.Status, gc.Equals, status.Detached)
}

func (s *FilesystemStateSuite) TestImportFilesystemEmptyFilesystemId(c *gc.C) {
func (s *FilesystemStateSuite) TestAddExistingFilesystemEmptyFilesystemId(c *gc.C) {
fsInfoIn := state.FilesystemInfo{
Pool: "modelscoped",
Size: 123,
}
_, err := s.State.ImportFilesystem(fsInfoIn, nil, "pgdata")
c.Assert(err, gc.ErrorMatches, "cannot import filesystem: empty filesystem ID not valid")
_, err := s.State.AddExistingFilesystem(fsInfoIn, nil, "pgdata")
c.Assert(err, gc.ErrorMatches, "cannot add existing filesystem: empty filesystem ID not valid")
}

func (s *FilesystemStateSuite) TestImportFilesystemVolumeBacked(c *gc.C) {
func (s *FilesystemStateSuite) TestAddExistingFilesystemVolumeBacked(c *gc.C) {
fsInfoIn := state.FilesystemInfo{
Pool: "modelscoped-block",
Size: 123,
Expand All @@ -1164,15 +1164,15 @@ func (s *FilesystemStateSuite) TestImportFilesystemVolumeBacked(c *gc.C) {
Size: 123,
VolumeId: "foo",
}
storageTag, err := s.State.ImportFilesystem(fsInfoIn, &volInfoIn, "pgdata")
storageTag, err := s.State.AddExistingFilesystem(fsInfoIn, &volInfoIn, "pgdata")
c.Assert(err, jc.ErrorIsNil)
c.Assert(storageTag, gc.Equals, names.NewStorageTag("pgdata/0"))

filesystem, err := s.State.StorageInstanceFilesystem(storageTag)
c.Assert(err, jc.ErrorIsNil)
fsInfoOut, err := filesystem.Info()
c.Assert(err, jc.ErrorIsNil)
fsInfoIn.FilesystemId = "filesystem-0" // set by ImportFilesystem
fsInfoIn.FilesystemId = "filesystem-0" // set by AddExistingFilesystem
c.Assert(fsInfoOut, jc.DeepEquals, fsInfoIn)

fsStatus, err := filesystem.Status()
Expand All @@ -1190,17 +1190,17 @@ func (s *FilesystemStateSuite) TestImportFilesystemVolumeBacked(c *gc.C) {
c.Assert(volStatus.Status, gc.Equals, status.Detached)
}

func (s *FilesystemStateSuite) TestImportFilesystemVolumeBackedVolumeInfoMissing(c *gc.C) {
func (s *FilesystemStateSuite) TestAddExistingFilesystemVolumeBackedVolumeInfoMissing(c *gc.C) {
fsInfo := state.FilesystemInfo{
Pool: "modelscoped-block",
Size: 123,
FilesystemId: "foo",
}
_, err := s.State.ImportFilesystem(fsInfo, nil, "pgdata")
c.Assert(err, gc.ErrorMatches, "cannot import filesystem: backing volume info missing")
_, err := s.State.AddExistingFilesystem(fsInfo, nil, "pgdata")
c.Assert(err, gc.ErrorMatches, "cannot add existing filesystem: backing volume info missing")
}

func (s *FilesystemStateSuite) TestImportFilesystemVolumeBackedFilesystemIdSupplied(c *gc.C) {
func (s *FilesystemStateSuite) TestAddExistingFilesystemVolumeBackedFilesystemIdSupplied(c *gc.C) {
fsInfo := state.FilesystemInfo{
Pool: "modelscoped-block",
Size: 123,
Expand All @@ -1211,11 +1211,11 @@ func (s *FilesystemStateSuite) TestImportFilesystemVolumeBackedFilesystemIdSuppl
Size: 123,
VolumeId: "foo",
}
_, err := s.State.ImportFilesystem(fsInfo, &volInfo, "pgdata")
c.Assert(err, gc.ErrorMatches, "cannot import filesystem: non-empty filesystem ID with backing volume not valid")
_, err := s.State.AddExistingFilesystem(fsInfo, &volInfo, "pgdata")
c.Assert(err, gc.ErrorMatches, "cannot add existing filesystem: non-empty filesystem ID with backing volume not valid")
}

func (s *FilesystemStateSuite) TestImportFilesystemVolumeBackedEmptyVolumeId(c *gc.C) {
func (s *FilesystemStateSuite) TestAddExistingFilesystemVolumeBackedEmptyVolumeId(c *gc.C) {
fsInfo := state.FilesystemInfo{
Pool: "modelscoped-block",
Size: 123,
Expand All @@ -1224,8 +1224,8 @@ func (s *FilesystemStateSuite) TestImportFilesystemVolumeBackedEmptyVolumeId(c *
Pool: "modelscoped-block",
Size: 123,
}
_, err := s.State.ImportFilesystem(fsInfo, &volInfo, "pgdata")
c.Assert(err, gc.ErrorMatches, "cannot import filesystem: empty backing volume ID not valid")
_, err := s.State.AddExistingFilesystem(fsInfo, &volInfo, "pgdata")
c.Assert(err, gc.ErrorMatches, "cannot add existing filesystem: empty backing volume ID not valid")
}

func (s *FilesystemStateSuite) setupFilesystemAttachment(c *gc.C, pool string) (state.Filesystem, *state.Machine) {
Expand Down