-
Notifications
You must be signed in to change notification settings - Fork 180
Extension: Disk Config #256
Changes from all commits
5f7cf18
6935a9b
3315cf9
5f14f54
5b50549
80387a0
189c95c
0e5b92a
a70510a
3883af2
ae0ca65
9c24f6b
237aad6
d7814a3
5d68672
9e87a92
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package diskconfig | ||
|
||
import ( | ||
"errors" | ||
|
||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers" | ||
) | ||
|
||
// DiskConfig represents one of the two possible settings for the DiskConfig option when creating, | ||
// rebuilding, or resizing servers: Auto or Manual. | ||
type DiskConfig string | ||
|
||
const ( | ||
// Auto builds a server with a single partition the size of the target flavor disk and | ||
// automatically adjusts the filesystem to fit the entire partition. Auto may only be used with | ||
// images and servers that use a single EXT3 partition. | ||
Auto DiskConfig = "AUTO" | ||
|
||
// Manual builds a server using whatever partition scheme and filesystem are present in the source | ||
// image. If the target flavor disk is larger, the remaining space is left unpartitioned. This | ||
// enables images to have non-EXT3 filesystems, multiple partitions, and so on, and enables you | ||
// to manage the disk configuration. It also results in slightly shorter boot times. | ||
Manual DiskConfig = "MANUAL" | ||
) | ||
|
||
// ErrInvalidDiskConfig is returned if an invalid string is specified for a DiskConfig option. | ||
var ErrInvalidDiskConfig = errors.New("DiskConfig must be either diskconfig.Auto or diskconfig.Manual.") | ||
|
||
// Validate ensures that a DiskConfig contains an appropriate value. | ||
func (config DiskConfig) validate() error { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
switch config { | ||
case Auto, Manual: | ||
return nil | ||
default: | ||
return ErrInvalidDiskConfig | ||
} | ||
} | ||
|
||
// CreateOptsExt adds a DiskConfig option to the base CreateOpts. | ||
type CreateOptsExt struct { | ||
servers.CreateOptsBuilder | ||
|
||
// DiskConfig [optional] controls how the created server's disk is partitioned. | ||
DiskConfig DiskConfig | ||
} | ||
|
||
// ToServerCreateMap adds the diskconfig option to the base server creation options. | ||
func (opts CreateOptsExt) ToServerCreateMap() map[string]interface{} { | ||
base := opts.CreateOptsBuilder.ToServerCreateMap() | ||
|
||
serverMap := base["server"].(map[string]interface{}) | ||
serverMap["OS-DCF:diskConfig"] = string(opts.DiskConfig) | ||
|
||
return base | ||
} | ||
|
||
// RebuildOptsExt adds a DiskConfig option to the base RebuildOpts. | ||
type RebuildOptsExt struct { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great solution to this |
||
servers.RebuildOptsBuilder | ||
|
||
// DiskConfig [optional] controls how the rebuilt server's disk is partitioned. | ||
DiskConfig DiskConfig | ||
} | ||
|
||
// ToServerRebuildMap adds the diskconfig option to the base server rebuild options. | ||
func (opts RebuildOptsExt) ToServerRebuildMap() (map[string]interface{}, error) { | ||
err := opts.DiskConfig.validate() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
base, err := opts.RebuildOptsBuilder.ToServerRebuildMap() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
serverMap := base["rebuild"].(map[string]interface{}) | ||
serverMap["OS-DCF:diskConfig"] = string(opts.DiskConfig) | ||
|
||
return base, nil | ||
} | ||
|
||
// ResizeOptsExt adds a DiskConfig option to the base server resize options. | ||
type ResizeOptsExt struct { | ||
servers.ResizeOptsBuilder | ||
|
||
// DiskConfig [optional] controls how the resized server's disk is partitioned. | ||
DiskConfig DiskConfig | ||
} | ||
|
||
// ToServerResizeMap adds the diskconfig option to the base server creation options. | ||
func (opts ResizeOptsExt) ToServerResizeMap() (map[string]interface{}, error) { | ||
err := opts.DiskConfig.validate() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
base, err := opts.ResizeOptsBuilder.ToServerResizeMap() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
serverMap := base["resize"].(map[string]interface{}) | ||
serverMap["OS-DCF:diskConfig"] = string(opts.DiskConfig) | ||
|
||
return base, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package diskconfig | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers" | ||
th "github.com/rackspace/gophercloud/testhelper" | ||
) | ||
|
||
func TestCreateOpts(t *testing.T) { | ||
base := servers.CreateOpts{ | ||
Name: "createdserver", | ||
ImageRef: "asdfasdfasdf", | ||
FlavorRef: "performance1-1", | ||
} | ||
|
||
ext := CreateOptsExt{ | ||
CreateOptsBuilder: base, | ||
DiskConfig: Manual, | ||
} | ||
|
||
expected := ` | ||
{ | ||
"server": { | ||
"name": "createdserver", | ||
"imageRef": "asdfasdfasdf", | ||
"flavorRef": "performance1-1", | ||
"OS-DCF:diskConfig": "MANUAL" | ||
} | ||
} | ||
` | ||
th.CheckJSONEquals(t, expected, ext.ToServerCreateMap()) | ||
} | ||
|
||
func TestRebuildOpts(t *testing.T) { | ||
base := servers.RebuildOpts{ | ||
Name: "rebuiltserver", | ||
AdminPass: "swordfish", | ||
ImageID: "asdfasdfasdf", | ||
} | ||
|
||
ext := RebuildOptsExt{ | ||
RebuildOptsBuilder: base, | ||
DiskConfig: Auto, | ||
} | ||
|
||
actual, err := ext.ToServerRebuildMap() | ||
th.AssertNoErr(t, err) | ||
|
||
expected := ` | ||
{ | ||
"rebuild": { | ||
"name": "rebuiltserver", | ||
"imageRef": "asdfasdfasdf", | ||
"adminPass": "swordfish", | ||
"OS-DCF:diskConfig": "AUTO" | ||
} | ||
} | ||
` | ||
th.CheckJSONEquals(t, expected, actual) | ||
} | ||
|
||
func TestResizeOpts(t *testing.T) { | ||
base := servers.ResizeOpts{ | ||
FlavorRef: "performance1-8", | ||
} | ||
|
||
ext := ResizeOptsExt{ | ||
ResizeOptsBuilder: base, | ||
DiskConfig: Auto, | ||
} | ||
|
||
actual, err := ext.ToServerResizeMap() | ||
th.AssertNoErr(t, err) | ||
|
||
expected := ` | ||
{ | ||
"resize": { | ||
"flavorRef": "performance1-8", | ||
"OS-DCF:diskConfig": "AUTO" | ||
} | ||
} | ||
` | ||
th.CheckJSONEquals(t, expected, actual) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package diskconfig | ||
|
||
import ( | ||
"github.com/mitchellh/mapstructure" | ||
"github.com/rackspace/gophercloud" | ||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers" | ||
"github.com/rackspace/gophercloud/pagination" | ||
) | ||
|
||
func commonExtract(result gophercloud.Result) (*DiskConfig, error) { | ||
var resp struct { | ||
Server struct { | ||
DiskConfig string `mapstructure:"OS-DCF:diskConfig"` | ||
} `mapstructure:"server"` | ||
} | ||
|
||
err := mapstructure.Decode(result.Body, &resp) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
config := DiskConfig(resp.Server.DiskConfig) | ||
return &config, nil | ||
} | ||
|
||
// ExtractGet returns the disk configuration from a servers.Get call. | ||
func ExtractGet(result servers.GetResult) (*DiskConfig, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In several of the other services, we just have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is an extension, though. I was following @jamiehannaford's lead on naming here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, ok. I'm fine with having it like that. I'm just thinking that having 2 ways of writing an extraction will force the users to know if they're using an extension or not. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm inclined to agree. The logic is that you can perform one I think the extension mechanisms we're using need a little work, still - it's awkward to compose extensions and it's all a little too manual right now. I'm not sure what I'd rather see, though, and I think it'll need to wait for post-release. I'll try to articulate my thoughts about what I'd like to accomplish in a discussion issue, possibly after these next two weeks are over and we have 1.0 shipped 😄 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. Any changes can wait until a later release. |
||
return commonExtract(result.Result) | ||
} | ||
|
||
// ExtractUpdate returns the disk configuration from a servers.Update call. | ||
func ExtractUpdate(result servers.UpdateResult) (*DiskConfig, error) { | ||
return commonExtract(result.Result) | ||
} | ||
|
||
// ExtractRebuild returns the disk configuration from a servers.Rebuild call. | ||
func ExtractRebuild(result servers.RebuildResult) (*DiskConfig, error) { | ||
return commonExtract(result.Result) | ||
} | ||
|
||
// ExtractDiskConfig returns the DiskConfig setting for a specific server acquired from an | ||
// servers.ExtractServers call, while iterating through a Pager. | ||
func ExtractDiskConfig(page pagination.Page, index int) (*DiskConfig, error) { | ||
casted := page.(servers.ServerPage).Body | ||
|
||
type server struct { | ||
DiskConfig string `mapstructure:"OS-DCF:diskConfig"` | ||
} | ||
var response struct { | ||
Servers []server `mapstructure:"servers"` | ||
} | ||
|
||
err := mapstructure.Decode(casted, &response) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
config := DiskConfig(response.Servers[index].DiskConfig) | ||
return &config, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add in the
testing.Short()
bit here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added from your branch in the rebase 😉