-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Implement ResourceProvider#StreamInvoke
#3424
Conversation
d1ef371
to
2528fda
Compare
@pgavlin I'm going to mark this as ready for review because I think it's directionally correct. LMK what you think. |
sdk/nodejs/runtime/invoke.ts
Outdated
opts: InvokeOptions = {}, | ||
): AsyncIterable<any> { | ||
if (opts.async) { | ||
throw Error("streamInvoke does not support async mode"); |
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.
Seems like streamInvoke
only supports async mode?
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! On purpose. By definition it's an AsyncIterable
.
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.
Perfect. I don't think we need to throw in this case, then--opts.async
is essentially meaningless, so we might as well ignore it entirely.
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 still think we shouldn't throw 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.
oops missed this. will fix tonight.
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.
Ok I just made streamInvoke
async natively.
2528fda
to
bda2d76
Compare
Just an update on this change—we've decided that we need to be able to cancel a const deployments = pulumi.runtime.streamInvoke("kubernetes:kubernetes:watch", {
group: "apps",
version: "v1",
kind: "Deployment",
});
for await (const { object: deployment, type } of deployments) {
console.log(`${deployment.metadata!.name} was ${type}`);
break;
}
deployments.close(); // <-- Signals to the resource provider to stop the invoke. The code will be done today, but it's probably best, given that people are trying to leave so they can go to the end-of-sprint unwinder, to check this in on Monday. |
5752852
to
6e4d838
Compare
I've just added the ability to "cancel" a // `streamInvoke` to retrieve all updates to any `Deployment`, enumerate 1
// update from the stream, then `cancel` giving the Kubernetes provider to
// clean up and close gracefully.
const deployments = await streamInvoke("kubernetes:kubernetes:watch", {
group: "apps", version: "v1", kind: "Deployment",
break;
});
for await (const d of deployments) {
break;
}
deployments.cancel(); The corresponding "graceful shutdown" code in the Kubernetes provider (which you can see in pulumi/pulumi-kubernetes#858) looks like the following snippet, and specifically in the last for {
select {
case <-k.canceler.context.Done():
//
// `kubeProvider#Cancel` was called. Terminate the `StreamInvoke` RPC, free all
// resources, and exit without error.
//
watch.Stop()
return nil
case event := <-watch.ResultChan():
//
// Kubernetes resource was updated. Publish resource update back to user.
//
resp, err := plugin.MarshalProperties(
resource.NewPropertyMapFromMap(
map[string]interface{}{
"type": event.Type,
"object": event.Object.(*unstructured.Unstructured).Object,
}),
plugin.MarshalOptions{})
if err != nil {
return err
}
err = server.Send(&pulumirpc.InvokeResponse{Return: resp})
if err != nil {
return err
}
case <-server.Context().Done():
//
// gRPC stream was cancelled. Usually this happens in the language provider, e.g.,
// in the call to `cancel` below. Terminate the `StreamInvoke` RPC, free all
// resources, and exit without error.
//
// const deployments = await streamInvoke("kubernetes:kubernetes:watch", {
// group: "apps", version: "v1", kind: "Deployment",
// });
// deployments.cancel();
//
watch.Stop()
return nil
}
} |
6e4d838
to
14d2bf1
Compare
14d2bf1
to
fa6a4b9
Compare
} | ||
|
||
// Check properties that failed verification. | ||
var failures []CheckFailure |
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.
This is a bit weird--do we expect to only get these on the first message?
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.
Yeah, it's weird. We're streaming InvokeResponse
back, but because we don't have a separate, first-class structured error channel it seems like you have to cram the error conditions into the message type—which we do both here and in Invoke
. So you end up with this weird thing.
We could just take this out. It's not clear to me which is better.
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.
Eh, I think we can leave it for now. Maybe add a note with what you described above.
03f5510
to
370e8d1
Compare
The @pulumi/pulumi TypScript SDK exposes `streamInvoke`, which returns a (potentially infinite) stream of responses. This currently is _assumed_ to be infinite, in that there is no way to signal cancellation, and prevents Pulumi from being able to clean up when we're finished using the results of the `streamInvoke`. This commit will introduce a `StreamInvokeResult` type, which is an `AsyncIterable` that also exposes a `cancel` function, whih does just this. Use it like this: // `streamInvoke` to retrieve all updates to any `Deployment`, enumerate 0 // updates from the stream, then `cancel` giving the Kubernetes provider to // clean up and close gracefully. const deployments = await streamInvoke("kubernetes:kubernetes:watch", { group: "apps", version: "v1", kind: "Deployment", break; }); deployments.cancel();
370e8d1
to
671c035
Compare
No description provided.