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

Add ability to handle partition match with the machine allocation constraint map response. #75

Merged
merged 3 commits into from
Sep 19, 2018

Conversation

blakerouse
Copy link

MAAS 2.5 has added the ability for tags to match a partition and not just a block device when performing allocation with storage constraints.

This change allows this to on MAAS 2.5, while also not breaking versions before. This adds a new interface StorageDevice that can be either a partition or a block device.

Copy link
Member

@wallyworld wallyworld left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few initial thoughts before lunch - I'm not sure of the need to change the map values from int to interface{}/int/float64

blockdevice.go Outdated
@@ -77,6 +89,14 @@ func (b *blockdevice) Size() uint64 {
return b.size
}

// FileSystem implements BlockDevice.
func (b *blockdevice) FileSystem() FileSystem {
if b.filesystem == nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can just return b.filesystem here
no need for if {}

blockdevice.go Outdated
"partitions": schema.List(schema.StringMap(schema.Any())),
}
checker := schema.FieldMap(fields, nil)
defaults := schema.Defaults{
"uuid": "",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you even need to specify a default given that above you have:
"uuid": schema.OneOf(schema.Nil(""), schema.String()),

eg model and id_path are processed the same was as uuid and don't need a default to be set

blockdevice.go Outdated
@@ -157,17 +182,26 @@ func blockdevice_2_0(source map[string]interface{}) (*blockdevice, error) {
// From here we know that the map returned from the schema coercion
// contains fields of the right type.

var filesystem *filesystem
if fsSource := valid["filesystem"]; fsSource != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be better:

if fsSource, ok := valid["filesystem"].(map[string]interface{}); ok {
filesystem, err = filesystem2_0(fsSource)

@@ -41,6 +42,11 @@ func (*blockdeviceSuite) TestReadBlockDevices(c *gc.C) {
partition := partitions[0]
c.Check(partition.ID(), gc.Equals, 1)
c.Check(partition.UsedFor(), gc.Equals, "ext4 formatted filesystem mounted at /")

fs := blockdevice.FileSystem()
c.Assert(fs, gc.NotNil)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a check for the actual content

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Type() and MountPoint() are checked below? Is there something different your looking for?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, what's below does the job

@@ -89,7 +95,13 @@ var blockdevicesResponse = `
"size": 8581545984
}
],
"filesystem": null,
"filesystem": {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should retain this "WithNulls" test and add a new one without

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This diff is confusing I still have the filesystem is nil in the WithNulls. I added another check to ensure that filesystem stays nil in the WithNulls.

controller.go Outdated
}

if interfaceMatches, found := constraintsMap["interfaces"]; found {
matches := convertConstraintMatches(interfaceMatches)
for label, ids := range matches {
interfaces := make([]Interface, len(ids))
for index, id := range ids {
iface := machine.Interface(id)
iface := machine.Interface(id.(int))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is dangerous given the change above to schema.Any() as it could panic
Why was the change to schema.Any() needed?

controller.go Outdated
return empty, NewDeserializationError("constraint match storage %q: %d does not match a block device for the machine", label, id)
storageDevices := make([]StorageDevice, len(ids))
for index, storageId := range ids {
if id, ok := storageId.(float64); ok {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be very careful about type asserts like this as they will panic if the value is the wrong type.
We had int previously and now flot64 here. It seems like you are catering for a json marshall/unmarshal use case?
Maybe you want to use a cast float64(storageId)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I now understand that if we have a number it's a block device and a string is a partition. Can we add a comment to explain this concept.

blockdevice.go Outdated
"partitions": schema.List(schema.StringMap(schema.Any())),
}
checker := schema.FieldMap(fields, nil)
defaults := schema.Defaults{}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can just revert to the previous code here

controller.go Outdated
@@ -931,15 +932,15 @@ func parseAllocateConstraintsResponse(source interface{}, machine *machine) (Con
constraintsMap := valid["constraints_by_type"].(map[string]interface{})
result := ConstraintMatches{
Interfaces: make(map[string][]Interface),
Storage: make(map[string][]BlockDevice),
Storage: make(map[string][]StorageDevice),
}

if interfaceMatches, found := constraintsMap["interfaces"]; found {
matches := convertConstraintMatches(interfaceMatches)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we are processing the "interfaces" map here.
The "interfaces" schema defines a map of string-> int list.
We should have a version of convertConstraintMatches() which returns the correct type so we don't need to use id.(int)

controller.go Outdated
} else if id, ok := storageId.(string); ok {
// Should link to a partition.
const partPrefix = "partition:"
if strings.HasPrefix(id, partPrefix) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To save indentation, do the check for the expected prefix and exit.
Then the rest of the stuff can be done without indenting

controller.go Outdated
}
}
return result, nil
}

func convertConstraintMatches(source interface{}) map[string][]int {
func convertConstraintMatches(source interface{}) map[string][]interface{} {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As stated above, maybe 2 versions of this to return the expected typed map and error if the values don't conform

interfaces.go Outdated
@@ -234,6 +234,9 @@ type Machine interface {
// BlockDevice returns the block device for the machine that matches the
// id specified. If there is no match, nil is returned.
BlockDevice(id int) BlockDevice
// Partition returns the partition for the machine that matches the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit pic - blank line before comments

Copy link
Member

@wallyworld wallyworld left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good, main issue is tweaking how the data is parsed to better handle the different type values

@blakerouse
Copy link
Author

Thanks for the review.

I have updated the branch to cleanup the areas you called out. The Any and the .(int) casting has been removed for interfaces as request. Hopefully its good enough to land now, ;-)

@wallyworld
Copy link
Member

$$merge$$

@Veebers
Copy link

Veebers commented Sep 19, 2018

!!build!!

2 similar comments
@Veebers
Copy link

Veebers commented Sep 19, 2018

!!build!!

@Veebers
Copy link

Veebers commented Sep 19, 2018

!!build!!

@Veebers
Copy link

Veebers commented Sep 19, 2018

$$merge$$

@jujubot jujubot merged commit 8a8cec7 into juju:master Sep 19, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants