feat(internal)!: Add timeouts to all supported endpoints - #223
Conversation
0e3d7a9 to
fb3ca9e
Compare
|
I think we need to keep the sleeps so I'll drop the commit (in kraft we had also a 1s sleep after the creation commands or so) For the delete commit this was already done and merged so it can be dropped Next I'm doing the loop retry for 5 minutes |
fb3ca9e to
f021a98
Compare
7f8a6af to
f8cd9c4
Compare
There was a problem hiding this comment.
Pull request overview
This PR updates the Unikraft CLI’s instance lifecycle operations to use looping, bounded waits and explicit timeouts, aligning with newer platform API behavior (including the DestinationPort pointer change).
Changes:
- Update
platform.Service.DestinationPortcall sites to pass pointers. - Add client-side looping waits for instance start/stop/delete (with per-call max wait) and introduce
--waitduration flags forinstance start/instance stop. - Adjust instance create/autostart behavior and update tests/fixtures accordingly.
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/volimport/volimport.go | Pass DestinationPort by pointer for volimport service group creation. |
| internal/cmd/services.go | Pass DestinationPort by pointer when creating/patching service groups. |
| internal/cmd/instances.go | Add wait loops for start/stop/delete, add --wait flags, set API timeouts. |
| go.mod | Bump unikraft.com/cloud/sdk dependency version. |
| go.sum | Update checksums for the bumped SDK version. |
| cmd/unikraft/testdata/TestGolden/instances/help | Remove wait-timeout field from help golden output. |
| cmd/unikraft/instances_test.go | Enable start/stop test and add a start/stop test that uses --wait. |
Comments suppressed due to low confidence (2)
internal/cmd/instances.go:1649
- Same issue as
waitForInstancesToRun: ifWaitInstanceserrors andGetInstancesfails, no entries are added tonextPending, so the function can return nil even though some instances never reachedstopped. ReturngetErr(or keeprefspending) instead of silently ignoring it.
getResp, getErr := c.GetInstances(ctx, refs.NameOrUUIDs(), platform.GetInstancesOpts{})
if getErr == nil {
var stillPending multimetro.Keys
for _, inst := range getResp.Data.Instances {
if inst.Status != nil && *inst.Status != platform.ResponseStatusSuccess {
continue
}
switch inst.State {
case platform.InstanceStateStopped:
// Done, don't add to pending.
case platform.InstanceStateStopping, platform.InstanceStateDraining:
stillPending = append(stillPending, multimetro.Key{
Metro: c.Metro.Name,
Name: inst.Name,
UUID: inst.Uuid,
})
default:
return nil, fmt.Errorf("instance %q transitioned to unexpected state %q while waiting to stop", inst.Uuid, inst.State)
}
}
mu.Lock()
nextPending = append(nextPending, stillPending...)
mu.Unlock()
}
return refs, nil
})
cmd/unikraft/testdata/TestGolden/instances/help:64
- This golden help output was updated to remove
wait-timeout, but it does not reflect the newly added--waitflag oninstance start/instance stop(added ininternal/cmd/instances.go). Regenerate/update this fixture so the start/stop help sections include the--waitflag and its default, otherwise the golden help test will drift/fail.
zero.stateful, scale-to-zero.cooldown-time, scale-to-zero.notify-time
timing, timing.uptime, timing.boot-time, timing.net-time
restart, restart.policy, restart.start-count, restart.restart-count
autostart
replicas
features
vsock
template
stop, stop.reason, stop.origin, stop.errno, stop.exit-code
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
f8cd9c4 to
8ef1d5e
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 7 changed files in this pull request and generated 5 comments.
Comments suppressed due to low confidence (1)
internal/cmd/instances.go:1634
- Same issue as in waitForInstancesToRun: after a WaitInstances error, instances with non-success
Status(or missing from the response) are skipped and never added back tonextPending, which can make the wait loop return nil even though some instances never reached the stopped state. Iterate by index withrefsand handle non-success/missing responses explicitly (error or keep pending).
var stillPending multimetro.Keys
for _, inst := range getResp.Data.Instances {
if inst.Status != nil && *inst.Status != platform.ResponseStatusSuccess {
continue
}
8ef1d5e to
8fabf0f
Compare
|
@jedevc I don't really have an answer to how to cleanly configure the wait time for delete and create They are very abstract (and kinda weird to me) In kraft what I did is to not wait for Similar for the delete operation. I also have my doubts with that. It's fine to wait for the max API time for it to go away, but the operation is anyway async, so why should we make it synchronous? But if we do want to do it, then why don't we do it for all API operations, theoretically we can. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 7 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (1)
internal/cmd/instances.go:1635
- Same issue as wait-for-running: the GetInstances fallback iterates response items without ensuring a 1:1 correspondence with
refs. Missing/non-returned instances can be dropped frompendingKeys, causing the wait loop to finish while some instances are still stopping. Consider validating response length and iterating by index to handle missing/non-success items deterministically.
var stillPending multimetro.Keys
for _, inst := range getResp.Data.Instances {
if inst.Status != nil && *inst.Status != platform.ResponseStatusSuccess {
return nil, fmt.Errorf("instance %q has non-success status while waiting to stop", inst.Uuid)
}
switch inst.State {
8fabf0f to
0feec91
Compare
|
It's useful to make it synchronous for scripting and similar. If you delete an instance, but don't wait, and then try and delete a volume that was mounted you can get "still mounted" as an error. We should wait, so that when it returns it's all guaranteed to be definitely gone and all it's resources disposed of. |
|
It would be useful to have some sort of option to skip this synchronizing tbf - but imo it does make sense to synchronize as a default. |
|
Maybe this is more complex though. And needs some sort of visual indicator while it's waiting to start but after the instance has been created. |
|
Ah sorry, didn't think you'd get/answer to the notification 😓 The option is right now I believe to set the
Agreed, maybe some spinning thingy like the one in kraft that did |
|
The good part though is that 90% of cases finish in less than 1 second, and 99.9% of cases finish in less than 10s Above that it's safe to say that te rest will take less than 20s |
0feec91 to
4439568
Compare
18eb835 to
ef4681e
Compare
|
Keeping in draft until box is fixed, but can be reviewed @jedevc |
015a844 to
88714eb
Compare
|
@craciunoiuc can you revive this one plsss ❤️ It'll fix https://linear.app/unikraft/issue/TOOL-1134/vm-template-create-can-return-references-not-found. |
88714eb to
f30f45a
Compare
|
As discussed: if a call fails because of a timeout, just warn, and keep going. |
|
Ok we have a problem here then I revisited this and saw the comment If we try to continue it means we'll have to rearch how our usage of commands works. For example: when a create endpoint call errors out with a This means that in order to "continue" we need to do a Get when the call times out and try to backfill data on the create response which feels ugly? Anyway, have a rebase: |
f30f45a to
7b48c3e
Compare
|
Hmmmm, I thought a timeout would still return a partial success with some info. Does it not? If not, that does feel like a thing that the platform API should be updated to support. |
|
ok, so there is a partial success, I'm picking again up to add this |
|
Be aware of #400. Which adds some partial success stuff. |
7b48c3e to
44d7648
Compare
|
draft till I know 100% it works |
|
CI is being annoying, I'm investigating, but note that, stable will fail on that test till updated. I will take stable/prod out of it until bumped there also |
5d1bbff to
eb6fd43
Compare
| // Instances as the create call reported them, keyed by multimetro key. Used | ||
| // as a fallback when the follow-up Get cannot see them yet. | ||
| createdData := make(map[string]createdResource) |
There was a problem hiding this comment.
Can this ever happen? This is weird, we created something, but Get can't see it??? Even if that's an async operation going on, that feels weird from the API surface point-of-view.
It makes for a lot of complexity, when really, IMO, once something is returned as "it exists", the API should be externally consistent.
If this code path is actually being exercised, I think we need to report this to the platform team.
There was a problem hiding this comment.
Yes it can happen, I tested a case here: https://discord.com/channels/879723098881028167/1534550849299284129/1534897084212187290
A few examples:
- You create an instances with
delete-on-stopand it crashes/finishes on boot (deleted before GET) - You start an async operation that finishes after he timeout and GET
- You accidentally delete the instance you created before the GET happened (hard but doable)
- You used autokill and it got autokilled before the GET happened
and so on
I think there are plenty of cases where this can happen, resulting in the original bug report TOOL-1134.
I would really like to keep this fix (or an alternative to it) in.
There was a problem hiding this comment.
URGH you're not wrong. Okay fine, do we just get the UUID/name in the Get or is it the full object? Like, could we always just return that?
If not, I guess there's not much we can do, and we need this ugly hack.
There was a problem hiding this comment.
I think you meant in the Create not Get?
Yeah, you get the uuid/name but also some other info for some reason lol. I sent on discord but have here also:
metro: dev-ccraciunoiu1
name: nginx-57jo6
uuid: 98be47fc-b7d7-443b-aadb-4cafc6a84bb8
state: starting
service:
name: muddy-grass-3jjl1whm
uuid: 75f77131-753b-43a1-9646-5ea3684ec356
domains:
- fqdn: muddy-grass-3jjl1whm.dev-ccraciunoiu1.apw.unikraft.internal
So also the service information and the state
There was a problem hiding this comment.
But no memory or vcpus 🤔 weird. Yeah, fine I guess we can have this fallback 😞
| return multimetro.Keys(started), err | ||
| } | ||
|
|
||
| func stopInstances(ctx context.Context, g *group.Group[multimetro.MetroClient], keys multimetro.Keys, opts StopOpts) (multimetro.Keys, error) { |
There was a problem hiding this comment.
Wondering if we should set drain_timeout_ms? Or force? Or whatever.
Not really in scope here, but maybe worth thinking about, since start is now updated too.
There was a problem hiding this comment.
Hmm true, but I guess it would change functionality from default stop to:
- force stop if timeout is 0
- drain connections if timeout >0 or -1
This would change a bit how it works though. So maybe we leave force out of it for now and we only work around drain? Worth having a discussion on
eb6fd43 to
0151a1e
Compare
Mirrors what the platform is doing. Currently used only by the wait field so there is no lateral effect. Signed-off-by: Cezar Craciunoiu <cezar@unikraft.io>
Signed-off-by: Cezar Craciunoiu <cezar@unikraft.io>
Ensures that all endpoints that can wait, will wait the max time. To do this, it sets the timeout to -1, and if the request fails it will try to use the deprecated version with a retry. Switch is done in place in the request. Timeout errors are treated as success plus a warning. Signed-off-by: Cezar Craciunoiu <cezar@unikraft.io>
0151a1e to
548a381
Compare
Set timeouts in:
startdeletevolimportThe rest already had timeouts.
Also remove some hacks while refactoring.
Depends on: https://github.com/unikraft-cloud/platform/pull/937
Closes: TOOL-1134