-
Notifications
You must be signed in to change notification settings - Fork 2k
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
CSI: allow updates to volumes on re-registration #12167
Conversation
fd88e6f
to
2311782
Compare
2311782
to
38cc30d
Compare
38cc30d
to
7311d87
Compare
7311d87
to
d148732
Compare
d148732
to
ea88557
Compare
Clarify that this state store method is used for everything, not just for the `Register` RPC.
ea88557
to
294ef74
Compare
CSI `CreateVolume` RPC is idempotent given that the topology, capabilities, and parameters are unchanged. CSI volumes have many user-defined fields that are immutable once set, and many fields that are not user-settable. Update the `Register` RPC so that updating a volume via the API merges onto any existing volume without touching Nomad-controlled fields, while validating it with the same strict requirements expected for idempotent `CreateVolume` RPCs.
294ef74
to
2c23e25
Compare
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.
LGMT.
I added a couple of comments, but I don't know if they are actually problems.
s.CSIVolumeDenormalize(nil, old.Copy()) | ||
if old.InUse() { |
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.
Does passing a Copy
to CSIVolumeDenormalize
means that old
is not denormalized at this point?
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.
Yes! We denormalize-on-read rather than having to push volume updates on every allocation status update. The volume only gets written with those denormalized updates on claim transitions (which happens in either csi_hook.go
on the client or in volumewatcher
on the leader).
nomad/structs/csi.go
Outdated
for _, otherTopo := range other.RequestedTopologies.Required { | ||
if !otherTopo.MatchFound(v.Topologies) { | ||
err = errors.New( | ||
"volume topology requirement update was not compatible with existing topology") | ||
break | ||
} | ||
} | ||
for _, otherTopo := range other.RequestedTopologies.Preferred { | ||
if !otherTopo.MatchFound(v.Topologies) { | ||
err = errors.New( | ||
"volume topology preference update was not compatible with existing topology") | ||
break | ||
} | ||
} |
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.
If topologies are immutable, do we need to check in the other direction as well? Meaning, can an update request reduce the scope of the topologies?
I wrote a test case like this and it fails because it doesn't return an error"
{
name: "invalid topology update - missing",
v: &CSIVolume{
Topologies: []*CSITopology{
{Segments: map[string]string{"rack": "R1"}},
{Segments: map[string]string{"rack": "R3"}},
},
},
update: &CSIVolume{
RequestedTopologies: &CSITopologyRequest{
Required: []*CSITopology{
{Segments: map[string]string{"rack": "R1"}},
},
},
},
expected: "volume topology request update was not compatible with existing topology",
expectFn: func(t *testing.T, v *CSIVolume) {
require.Len(t, v.Topologies, 2)
},
},
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.
I think we're ok, but I should definitely add a test covering this case that asserts the expected behavior so that it's not only "accidentally ok".
The TopologyRequirement
message in the CreateVolume spec has examples, but the high-level description is this:
If requisite is specified, the provisioned volume MUST be accessible from at least one of the requisite topologies.
The easiest example is to assume we're creating an AWS EBS volume. These are accessible within single availability zone. So suppose I provide the following topology request:
topology_request {
required {
topology { segments { "topology.ebs.csi.aws.com/zone" = "us-east-1a" } }
topology { segments { "topology.ebs.csi.aws.com/zone" = "us-east-1b" } }
}
}
The plugin can create the volume in either us-east-1a
or us-east-1b
, and the resulting volume object topology will have topology like this:
&CSIVolume{
Topologies: []*CSITopology{
{Segments: map[string]string{"topology.ebs.csi.aws.com/zone": "us-east-1a"},
},
},
}
So what we're trying to do here is to allow user to update their request to be:
topology_request {
required {
topology { segments { "topology.ebs.csi.aws.com/zone" = "us-east-1a" } }
}
}
Because that request could have been sent and resulted in the same topology.
All that being said, having described all that I'm wondering whether it really makes sense to let users change the topology_request
at all if it can't result in a changed topology. This feels like something we'll write in the docs and then folks will open issues about anyways because it's confusing behavior.
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.
Hm, that behavior is even more confusing because you can't reduce your request to:
topology_request {}
I think I'm going to change this so that once Topologies
is set by the plugin, you can't change the TopologyRequest
at all. That's going to have less weird edge cases.
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.
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.
LGTM
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.
Nice, thanks for the explanation.
I'm going to lock this pull request because it has been closed for 120 days ⏳. This helps our maintainers find and focus on the active contributions. |
Fixes #11786 (as described in #11786 (comment))
The CSI
CreateVolume
RPC is idempotent given that the topology,capabilities, and parameters are unchanged. CSI volumes have many
user-defined fields that are immutable once set, and many fields that
are not user-settable.
Update the
Register
RPC so that updating a volume via the API mergesonto any existing volume without touching Nomad-controlled fields,
while validating it with the same strict requirements expected for
idempotent
CreateVolume
RPCs.Note to reviewers: I've broken out two small refactoring commits, so this is likely best reviewed commit-by-commit.