feat(cmd): Add rollout mechanics when creating - #309
craciunoiuc wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Adds support for a --rollout mode to instance create, intended to replace existing instances in the same service group that use the same image “base name”, while also adjusting how service-group and volume identifiers are parsed during instance creation.
Changes:
- Add
--rolloutflag toinstance createand route execution through a newrunRolloutworkflow. - Modify parsing behavior for
--serviceand--volume-style inputs (notablyInstanceService.UnmarshalTextandInstanceVolume.UnmarshalText). - Remove the client-side “volume metro mismatch” validation during instance creation.
Comments suppressed due to low confidence (1)
internal/cmd/instances.go:783
- The volume creation path no longer validates
vol.Metroagainst the instance metro (unlike the service-group check below). If a volume is specified via structured input (--set volumes.0.metro=..., JSON/YAML, or if text parsing supportsmetro/...), the CLI will now proceed and fail later with a less actionable API error; consider restoring the metro-mismatch validation for volumes whenvol.Metrois set.
case "volumes":
for _, vol := range field.Create.Set.([]*InstanceVolume) {
reqVol := platform.CreateInstanceRequestVolume{
At: vol.At,
}
if vol.UUID != "" {
reqVol.Uuid = &vol.UUID
}
if vol.Name != "" {
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
926d9db to
ce661f7
Compare
8ccf7d3 to
c3adcf6
Compare
|
I reworked the implementation as you asked @jedevc Right now only instance rollout is implemented, but we can do other also I think |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
Comments suppressed due to low confidence (1)
internal/cmd/instances.go:904
- The volume link type (Link[Volume]) includes a Metro field, but the create path no longer validates that an explicitly metro-qualified volume matches the instance metro. Without this check, a user can specify e.g. "sfo/myvol" while creating in another metro and the metro will be silently ignored in the API request (only Name/UUID are sent). Consider restoring the metro-mismatch validation (similar to the service group check) or explicitly rejecting metro-qualified volume references here.
case "volumes":
for _, vol := range field.Create.Set.([]*InstanceVolume) {
reqVol := platform.CreateInstanceRequestVolume{
At: vol.At,
}
if vol.UUID != "" {
reqVol.Uuid = &vol.UUID
}
if vol.Name != "" {
reqVol.Name = &vol.Name
}
if vol.Size > 0 {
reqVol.SizeMb = new(uint64(vol.Size))
}
if vol.Readonly {
reqVol.Readonly = &vol.Readonly
}
req.Volumes = append(req.Volumes, reqVol)
}
143ed09 to
89cd18a
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 8 comments.
Comments suppressed due to low confidence (1)
internal/cmd/instances.go:1229
- Calling
Instance{}.Deletedirectly bypasses the sandbox wrappers used by resource commands, so--rolloutcan delete matching service instances even when they are not tracked by the active sandbox. This breaks sandbox isolation and can remove resources that normal delete commands would reject.
delErr := (Instance{}).Delete(ctx, []resource.Resource{old})
c68b276 to
144843c
Compare
|
something funky going on with tests, probably server is misbehaving. Will need to wait till I can make sure tests pass |
144843c to
a2e506a
Compare
|
One thing left to discuss here is if we're going for the loop-wait-retry logic If yes, then feel free to merge, if not, then we got to wait for the platform changes to go in I would say we leave as is because a rollout can take significantly more than a simple start or restart, si it's fine to have a more complex logic in place (at least conceptually) |
a2e506a to
d0260b3
Compare
|
Before merging the timeouts stuff here, could we get #223 in first? |
| if extra := len(toRemove) - len(created); extra > 0 { | ||
| var baseName string | ||
| for key, field := range resource.IterFields(fields) { | ||
| if key.String() == "name" && field.Create != nil && field.Create.Set != nil { |
There was a problem hiding this comment.
resource.GetFieldByPath instead of iteration.
|
A couple thoughts. There are two modes of rollout I think:
Does that make sense? I don't really know what to call these "modes", but I think we should have both of them. Also, as far as I can tell, the current implementation doesn't have the failure mode, where we restore back to the "known" good state. It assumes that every rollout is perfectly successful, which it may not be. |
|
for for I think we can do both, with With the small note that running a database in a service group is conceptually wrong so if we want to truly support database rollout we need to support non-servicegroup based rollout |
Hm, so how would you imagine database rollout working? I think service group still makes sense here - it's about the database service that you care about, but I'm curious why this is wrong.
Indeed, that's why I was suggesting creating them all, and only deleting once you're convinced of the health of the others. Similar to how rolling deployments work in k8s.
I think the safety of them is the same no? I don't actually know actually if the names |
|
Note, the platform doesn't really have a concept of instance health… this is sad. IMO, we should currently think of healthy as:
However, as we build the rollout flag, etc, we should consider that at some point, we would add a "real" healthcheck system (or add the ability to monitor health in new ways, like by checking logs, etc) |
|
Ok so I think the summary is:
|
|
Yup. For the last one, eventually we could do things like support rollout over tags. Might be worth planning what the CLI interface looks like? So it's nice and consistent for when we do. |
9111e2e to
78e9056
Compare
There was a problem hiding this comment.
🟡 Changes recommended
There are confirmed correctness issues in the new rollout field override helper and in create-time save/validation behavior when wrappers override resolved create patches.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 7/7 changed files
- Comments generated: 4
- Review effort level: Lite
78e9056 to
6182238
Compare
There was a problem hiding this comment.
🟡 Changes recommended
The rollout “instances up” gate currently relies on a single immediate state read-back (and uses time.After in a cancelable wait), which can make rollouts flaky and incorrectly unwind.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 7/7 changed files
- Comments generated: 2
- Review effort level: Lite
6182238 to
22ec0ba
Compare
22ec0ba to
227f40c
Compare
RunResources worked the fields to create out inline, so nothing outside could read them or add to them. A command needing a value before the create had to build the patch spec a second time, which runs any editor mode twice and hands the create different fields. The work moves into 'resolve', which keeps what it worked out, and 'CreateFields' and 'SetCreateFields' read and replace the create patches over that one resolution. Saving to a file stays ahead of every editor, which the 'xor' tag on those flags guarantees. Signed-off-by: Cezar Craciunoiu <cezar@unikraft.io>
Moving a service group onto a new image meant creating the new instances, watching their states and deleting the old ones by hand, with nothing to stop a half-finished swap from leaving the group short or running two images at once. The flag counts the group, creates that many instances in one request through replicas, and deletes the old set only once every new instance runs. A metro without room for both sets is refused up front, and new instances that do not run are deleted again. Signed-off-by: Cezar Craciunoiu <cezar@unikraft.io>
A rolling rollout runs both sets of instances at once, which a workload holding sole access to its volume cannot do, and it deletes the old set the moment the new one reports running, so an instance that falls over straight after takes the group with it. '--service-rollout=replace' stops the old instances and waits for them before it starts the new ones, and starts the old set again if the new one does not come up. '--rollout-healthy-after' holds both modes up and rechecks them, and autoscale groups are refused. Signed-off-by: Cezar Craciunoiu <cezar@unikraft.io>
227f40c to
6882f9f
Compare
Open to UI/UX suggestions, right now it prints every new instance that was rolledCompletely reworked, now, as discussed below, added extra:
rolling/replace, one starting + deleting, one stopping + starting + deleting--rollout-healthy-afterNotable ickies still open to suggestions:
instance.goitselfCloses: TOOL-794