Skip to content

Commit

Permalink
destroy-controller: report num storage remaining
Browse files Browse the repository at this point in the history
Update the Controller.ModelStatus API to include
information about volumes and filesystems, which
we'll use in the destroy-controller command to
report to the user in case they don't supply
the --destroy-storage or --release-storage flags.
  • Loading branch information
axw committed Jul 28, 2017
1 parent 487d261 commit b4fa55d
Show file tree
Hide file tree
Showing 12 changed files with 386 additions and 71 deletions.
18 changes: 18 additions & 0 deletions api/base/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ type ModelStatus struct {
HostedMachineCount int
ServiceCount int
Machines []Machine
Volumes []Volume
Filesystems []Filesystem
}

// Machine holds information about a machine in a juju model.
Expand Down Expand Up @@ -77,3 +79,19 @@ type UserInfo struct {
LastConnection *time.Time
Access string
}

// Volume holds information about a volume in a juju model.
type Volume struct {
Id string
ProviderId string
Status string
Detachable bool
}

// Filesystem holds information about a filesystem in a juju model.
type Filesystem struct {
Id string
ProviderId string
Status string
Detachable bool
}
22 changes: 22 additions & 0 deletions api/common/modelstatus.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,35 @@ func (c *ModelStatusAPI) ModelStatus(tags ...names.ModelTag) ([]base.ModelStatus
return nil, errors.Annotatef(err, "OwnerTag %q at position %d", r.OwnerTag, i)
}

volumes := make([]base.Volume, len(r.Volumes))
for i, in := range r.Volumes {
volumes[i] = base.Volume{
Id: in.Id,
ProviderId: in.ProviderId,
Status: in.Status,
Detachable: in.Detachable,
}
}

filesystems := make([]base.Filesystem, len(r.Filesystems))
for i, in := range r.Filesystems {
filesystems[i] = base.Filesystem{
Id: in.Id,
ProviderId: in.ProviderId,
Status: in.Status,
Detachable: in.Detachable,
}
}

results[i] = base.ModelStatus{
UUID: model.Id(),
Life: string(r.Life),
Owner: owner.Id(),
HostedMachineCount: r.HostedMachineCount,
ServiceCount: r.ApplicationCount,
TotalMachineCount: len(r.Machines),
Volumes: volumes,
Filesystems: filesystems,
}
results[i].Machines = make([]base.Machine, len(r.Machines))
for j, mm := range r.Machines {
Expand Down
18 changes: 18 additions & 0 deletions apiserver/common/modelmanagerinterface.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ type ModelManagerBackend interface {
UserAccess(names.UserTag, names.Tag) (permission.UserAccess, error)
AllMachines() (machines []Machine, err error)
AllApplications() (applications []Application, err error)
AllFilesystems() ([]state.Filesystem, error)
AllVolumes() ([]state.Volume, error)
ControllerUUID() string
ControllerTag() names.ControllerTag
Export() (description.Model, error)
Expand Down Expand Up @@ -215,6 +217,22 @@ func (st modelManagerStateShim) AllApplications() ([]Application, error) {
return all, nil
}

func (st modelManagerStateShim) AllFilesystems() ([]state.Filesystem, error) {
model, err := st.State.IAASModel()
if err != nil {
return nil, err
}
return model.AllFilesystems()
}

func (st modelManagerStateShim) AllVolumes() ([]state.Volume, error) {
model, err := st.State.IAASModel()
if err != nil {
return nil, err
}
return model.AllVolumes()
}

// BackendPool provides access to a pool of ModelManagerBackends.
type BackendPool interface {
Get(modelUUID string) (ModelManagerBackend, func(), error)
Expand Down
65 changes: 65 additions & 0 deletions apiserver/common/modelstatus.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/juju/juju/apiserver/facade"
"github.com/juju/juju/apiserver/params"
"github.com/juju/juju/state"
)

// ModelStatusAPI implements the ModelStatus() API.
Expand Down Expand Up @@ -95,12 +96,76 @@ func (c *ModelStatusAPI) modelStatus(tag string) (params.ModelStatus, error) {
return status, errors.Trace(err)
}

volumes, err := st.AllVolumes()
if err != nil {
return status, errors.Trace(err)
}
modelVolumes := ModelVolumeInfo(volumes)

filesystems, err := st.AllFilesystems()
if err != nil {
return status, errors.Trace(err)
}
modelFilesystems := ModelFilesystemInfo(filesystems)

return params.ModelStatus{
ModelTag: tag,
OwnerTag: model.Owner().String(),
Life: params.Life(model.Life().String()),
HostedMachineCount: len(hostedMachines),
ApplicationCount: len(applications),
Machines: modelMachines,
Volumes: modelVolumes,
Filesystems: modelFilesystems,
}, nil
}

// ModelFilesystemInfo returns information about filesystems in the model.
func ModelFilesystemInfo(in []state.Filesystem) []params.ModelFilesystemInfo {
out := make([]params.ModelFilesystemInfo, len(in))
for i, in := range in {
var statusString string
status, err := in.Status()
if err != nil {
statusString = err.Error()
} else {
statusString = string(status.Status)
}
var providerId string
if info, err := in.Info(); err == nil {
providerId = info.FilesystemId
}
out[i] = params.ModelFilesystemInfo{
Id: in.Tag().Id(),
ProviderId: providerId,
Status: statusString,
Detachable: in.Detachable(),
}
}
return out
}

// ModelVolumeInfo returns information about volumes in the model.
func ModelVolumeInfo(in []state.Volume) []params.ModelVolumeInfo {
out := make([]params.ModelVolumeInfo, len(in))
for i, in := range in {
var statusString string
status, err := in.Status()
if err != nil {
statusString = err.Error()
} else {
statusString = string(status.Status)
}
var providerId string
if info, err := in.Info(); err == nil {
providerId = info.VolumeId
}
out[i] = params.ModelVolumeInfo{
Id: in.Tag().Id(),
ProviderId: providerId,
Status: statusString,
Detachable: in.Detachable(),
}
}
return out
}
68 changes: 67 additions & 1 deletion apiserver/common/modelstatus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package common_test

import (
"github.com/juju/errors"
"github.com/juju/loggo"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"
Expand All @@ -13,9 +14,15 @@ import (
"github.com/juju/juju/apiserver/facades/client/controller"
"github.com/juju/juju/apiserver/params"
apiservertesting "github.com/juju/juju/apiserver/testing"
"github.com/juju/juju/constraints"
"github.com/juju/juju/environs"
"github.com/juju/juju/environs/config"
"github.com/juju/juju/instance"
"github.com/juju/juju/provider/dummy"
"github.com/juju/juju/state"
statetesting "github.com/juju/juju/state/testing"
"github.com/juju/juju/storage"
"github.com/juju/juju/storage/provider"
"github.com/juju/juju/testing"
"github.com/juju/juju/testing/factory"
)
Expand All @@ -36,6 +43,9 @@ func (s *modelStatusSuite) SetUpTest(c *gc.C) {
s.InitialConfig = testing.CustomModelConfig(c, testing.Attrs{
"name": "controller",
})
s.NewPolicy = func(*state.State) state.Policy {
return statePolicy{}
}

s.StateSuite.SetUpTest(c)
s.resources = common.NewResources()
Expand Down Expand Up @@ -124,9 +134,28 @@ func (s *modelStatusSuite) TestModelStatus(c *gc.C) {
Jobs: []state.MachineJob{state.JobManageModel},
Characteristics: &instance.HardwareCharacteristics{CpuCores: &eight},
InstanceId: "id-4",
Volumes: []state.MachineVolumeParams{{
Volume: state.VolumeParams{
Pool: "modelscoped",
Size: 123,
},
}},
})
s.Factory.MakeMachine(c, &factory.MachineParams{
Jobs: []state.MachineJob{state.JobHostUnits}, InstanceId: "id-5"})
Jobs: []state.MachineJob{state.JobHostUnits},
InstanceId: "id-5",
Filesystems: []state.MachineFilesystemParams{{
Filesystem: state.FilesystemParams{
Pool: "modelscoped",
Size: 123,
},
}, {
Filesystem: state.FilesystemParams{
Pool: "machinescoped",
Size: 123,
},
}},
})
s.Factory.MakeApplication(c, &factory.ApplicationParams{
Charm: s.Factory.MakeCharm(c, nil),
})
Expand Down Expand Up @@ -163,6 +192,14 @@ func (s *modelStatusSuite) TestModelStatus(c *gc.C) {
{Id: "0", Hardware: &params.MachineHardware{Cores: &eight}, InstanceId: "id-4", Status: "pending", WantsVote: true},
{Id: "1", Hardware: stdHw, InstanceId: "id-5", Status: "pending"},
},
Volumes: []params.ModelVolumeInfo{{
Id: "0", Status: "pending", Detachable: true,
}},
Filesystems: []params.ModelFilesystemInfo{{
Id: "0", Status: "pending", Detachable: true,
}, {
Id: "1/1", Status: "pending", Detachable: false,
}},
}, {
ModelTag: hostedModelTag,
HostedMachineCount: 2,
Expand All @@ -175,3 +212,32 @@ func (s *modelStatusSuite) TestModelStatus(c *gc.C) {
},
}})
}

type statePolicy struct{}

func (statePolicy) Prechecker() (environs.InstancePrechecker, error) {
return nil, errors.NotImplementedf("Prechecker")
}

func (statePolicy) ConfigValidator() (config.Validator, error) {
return nil, errors.NotImplementedf("ConfigValidator")
}

func (statePolicy) ConstraintsValidator() (constraints.Validator, error) {
return nil, errors.NotImplementedf("ConstraintsValidator")
}

func (statePolicy) InstanceDistributor() (instance.Distributor, error) {
return nil, errors.NotImplementedf("InstanceDistributor")
}

func (statePolicy) StorageProviderRegistry() (storage.ProviderRegistry, error) {
return storage.ChainedProviderRegistry{
dummy.StorageProviders(),
provider.CommonStorageProviders(),
}, nil
}

func (statePolicy) ProviderConfigSchemaSource() (config.ConfigSchemaSource, error) {
return nil, errors.NotImplementedf("ConfigSchemaSource")
}
10 changes: 10 additions & 0 deletions apiserver/facades/client/modelmanager/modelinfo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,16 @@ func (st *mockState) AllApplications() ([]common.Application, error) {
return nil, st.NextErr()
}

func (st *mockState) AllVolumes() ([]state.Volume, error) {
st.MethodCall(st, "AllVolumes")
return nil, st.NextErr()
}

func (st *mockState) AllFilesystems() ([]state.Filesystem, error) {
st.MethodCall(st, "AllFilesystems")
return nil, st.NextErr()
}

func (st *mockState) IsControllerAdmin(user names.UserTag) (bool, error) {
st.MethodCall(st, "IsControllerAdmin", user)
if st.controllerModel == nil {
Expand Down
14 changes: 8 additions & 6 deletions apiserver/params/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,14 @@ type RemoveBlocksArgs struct {

// ModelStatus holds information about the status of a juju model.
type ModelStatus struct {
ModelTag string `json:"model-tag"`
Life Life `json:"life"`
HostedMachineCount int `json:"hosted-machine-count"`
ApplicationCount int `json:"application-count"`
OwnerTag string `json:"owner-tag"`
Machines []ModelMachineInfo `json:"machines,omitempty"`
ModelTag string `json:"model-tag"`
Life Life `json:"life"`
HostedMachineCount int `json:"hosted-machine-count"`
ApplicationCount int `json:"application-count"`
OwnerTag string `json:"owner-tag"`
Machines []ModelMachineInfo `json:"machines,omitempty"`
Volumes []ModelVolumeInfo `json:"volumes,omitempty"`
Filesystems []ModelFilesystemInfo `json:"filesystems,omitempty"`
}

// ModelStatusResults holds status information about a group of models.
Expand Down
16 changes: 16 additions & 0 deletions apiserver/params/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,22 @@ type MachineHardware struct {
AvailabilityZone *string `json:"availability-zone,omitempty"`
}

// ModelVolumeInfo holds information about a volume in a model.
type ModelVolumeInfo struct {
Id string `json:"id"`
ProviderId string `json:"provider-id,omitempty"`
Status string `json:"status,omitempty"`
Detachable bool `json:"detachable,omitempty"`
}

// ModelFilesystemInfo holds information about a filesystem in a model.
type ModelFilesystemInfo struct {
Id string `json:"id"`
ProviderId string `json:"provider-id,omitempty"`
Status string `json:"status,omitempty"`
Detachable bool `json:"detachable,omitempty"`
}

// ModelUserInfo holds information on a user who has access to a
// model. Owners of a model can see this information for all users
// who have access, so it should not include sensitive information.
Expand Down
Loading

0 comments on commit b4fa55d

Please sign in to comment.