I'm experimenting with dynamic providers (wrapping...
# typescript
m
I'm experimenting with dynamic providers (wrapping some simple REST API calls) and I'm trying to avoid my resource being replaced every time I edit the provider code. Using
{ ingoreChanges: ['__provider']}
does not have to seem any effect, it still tries to replace the resource, with the only diff between "old" and "new" being some changes in the provider's code:
pulumi-nodejs:dynamic:Resource my-project create replacement [diff: ~__provider]
. I do understand that it makes sense to replace on provider changes in general, but I would like to control when that happens. The resource I create here is a container for other resources, and it's also a resource that cannot be deleted (just marked archived). I could not find anything in the docs or the Slack archive. Intuitively, I'd expect there to be some ID (e.g., a UUID) on the provider that I can change to signal that the provider is different now.
e
Have you implemented the
diff
method? I think if you return a diff result from that that isn't DIFF_UNKNOWN then it won't hit the default logic to replace
m
I have implemented a diff method:
Copy code
async diff(id: string, olds: MyProjectInputs, news: MyProjectInputs) {
    const changes: string[] = [];
    console.log('Olds:', olds);
    console.log('News:', news);
    if (olds.name !== news.name) {
      changes.push('name');
    }
    return { changes: changes.length > 0, replaces: [] };
  }
When I look at the console log, I see that "olds" and "news" contain the "__provider" key that is shown to be what has changed. The "name" has not changed, and would not require a replacement.
To me it looks like Pulumi makes a diff of the provider code. Hence my question whether there is, e.g., something like
Copy code
class MyProjectProvider implements pulumi.dynamic.ResourceProvider {
  private __provider_version = "123";
to signal that despite changes to the code, the provider should not be considered changed. (But I might be on the wrong track here entirely.)
I just found this in the implementation:
Copy code
// Note that we do not take any special action if the provider has changed. This allows a user to iterate on a
// dynamic provider's implementation. This does require some care on the part of the user: each iteration of a
// dynamic provider's implementation must be able to handle all state produced by prior iterations.
//
// Prior versions of the dynamic provider required that a dynamic resource be replaced any time its provider
// implementation changed. This made iteration painful, especially if the dynamic resource was managing a
// physical resource--in this case, the physical resource would be unnecessarily deleted and recreated each
// time the provider was updated.
This sounds exactly like what I want. Will investigate why I'm not seeing this behavior.
e
yeh this looks like it shouldn't replace from the provider code changing
odd
m
I might have bumped the version in package.json after the resource was first created. Unfortunately, GitHub has an outage right now, so my CI/CD doesn't run and I can't check it locally. I've reverted the version and will report back whether that solves it.
Hmm, now the first of my resources switched to in-place provider updates, which makes sense (so it shows
update [diff: ~__provider]
). I don't really understand what caused this change in behavior. The second one is still trying to replace the resource.
OK, so the root cause seems to be a mismatch in inputs and outputs. I looks like the provider is considered "changed so that resource requires replacement" when the inputs are missing from the outputs, or perhaps generally when the input to the resource's constructor change. I was able to fix the behavior by making sure all my inputs are part of the output and editing the state file to retroactively add the inputs to the outputs.
e
I'm a bit brain fogged from a cold today but that doesn't sound like it should behave that way, but if you've got a way to repro this and can raise a ticket with that we can hopefully fix this
👍 1
🙌 1
m
Somehow it works as I expect now... I'll be building more of these in the upcoming days, I'll try to reproduce it and share it. Right now, it seems like an issue with my initial implementation that led to a state that was not fully compatible with my improved implementation. The key was definitely getting the inputs, outputs, and types right.
🙌 1