-
Notifications
You must be signed in to change notification settings - Fork 155
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
Introduce provider upgrade tests #2714
Conversation
Does the PR have any schema changes?Does the PR have any schema changes?Looking good! No breaking changes found. Maintainer note: consult the runbook for dealing with any breaking changes. |
f5e308b
to
47d921d
Compare
@iwahbe I've pondered sequencing here and I think if there's ongoing work to bring AWS repo under ci-mgmt perhaps this PR is not a good time to split examples and tests. I don't think we were tracking this before so here's a new issue: Once pulumi-aws is under ci-mgmt we can roll out examples/tests split everywhere including here, then move tests around. |
Let me confirm but I also think this is self-sufficient for recording v5 provider; the baseline version is currently written in the Pulumi.yaml Program file. So if doing something like adding example resources we should be able to re-record this baseline version from the main branch without having to checkout some earlier branch. |
examples/provider_upgrade_test.go
Outdated
} | ||
|
||
if isPureMethod(t, line) { | ||
line = ignoreStables(t, line) |
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 fine for now, but we should remember to make stables stable and then remove this, since this field is effectively no longer under test.
ambientProvider, _ := exec.LookPath(providerBinary) | ||
require.Emptyf(t, ambientProvider, "please remove the provider from PATH") |
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.
Instead of this, can we just set PULUMI_IGNORE_AMBIENT_PLUGINS=true
? It should accomplish the same thing without requiring destructive user action.
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.
Hmm. Not sure, I prefer this I think to fail loudly. Perhaps if we went PULUMI_IGNORE_AMBIENT_PLUGINS way I'd like another check to fail loudly if baseline recording is not on the expected version.
examples/provider_upgrade_test.go
Outdated
info := providerUpgradeInfo{} | ||
info.testCaseDir = filepath.Join("testdata", "resources") | ||
pyaml := filepath.Join(info.testCaseDir, "Pulumi.yaml") | ||
v := parseProviderVersion(t, pyaml) |
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.
We will eventually want to provision multiple providers. The obvious example is lots of tags
configurations. We can leave it for now and revisit.
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'd leave it, I'm not sure how to avoid copy-pasting resource configurations, perhaps we can parameterize the stacks somehow or resort to YAML munging. We might also need to break down this single stack into N stacks. I'd like to check in a reasonably working first version before making those extensions though.
examples/provider_upgrade_test.go
Outdated
pt.RunPulumiCommand("stack", "export", "--file", newStateFile) | ||
stackName := parseStackName(t, readFile(t, newStateFile)) | ||
fixedState := withUpdatedStackName(t, stackName, readFile(t, stateFile)) |
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.
We read the new state file twice, and write it once. Can we do this?
pt.RunPulumiCommand("stack", "export", "--file", newStateFile) | |
stackName := parseStackName(t, readFile(t, newStateFile)) | |
fixedState := withUpdatedStackName(t, stackName, readFile(t, stateFile)) | |
pt.RunPulumiCommand("stack", "export", "--file", newStateFile) | |
newStateFileBytes := readFile(t, newStateFile) | |
stackName := parseStackName(t, newStateFileBytes) | |
fixedState := withUpdatedStackName(t, stackName, newStateFileBytes) |
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.
Well no, I think in fact this would invalidate the test.. We want to read the pre-recorded stateFile (from stateFile variable). We're only reading the newStateFile as a workaround to determine currently allocated stack's name. I can factor it out into a function to make more obvious.
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 have many comments and questions, but nothing blocking. This seems like a useful tool, and something where we can safely land it and continue to improve
Makefile
Outdated
|
||
# Runs integration tests on the baseline version and updates testdata to record expected behavior. | ||
test.upgrade.record:: | ||
cd examples && PULUMI_ACCEPT=true go test -v -tags all -run TestProviderUpgradeRecord -timeout 2h |
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.
The Makefile feels like a non-ideal place to define a 2hr timeout. I would expect to define a timeout in the workflow for a CI job or just rely on the user for a timeout when running locally?
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 the state of the art in the Makefile already :) Just following existing practice.
examples/provider_upgrade_test.go
Outdated
) | ||
|
||
const ( | ||
acceptEnvVar = "PULUMI_ACCEPT" |
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.
The name of this variable is pretty generic for something that just toggles between record and compare modes.
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.
The name is consistent with similar tests in pulumi/pulumi and pulumi/pulumi-{yaml, java}.
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.
https://github.com/hexops/autogold defines custom -update flag which is an interesting approach we could use too. I'm just indeed opting for consistency with pulumi/pulumi where we use this strange PULUMI_ACCEPT var.
examples/provider_upgrade_test.go
Outdated
func TestProviderUpgradeRecord(t *testing.T) { | ||
if !accept { | ||
t.Skipf("Skipping; to record baselines set %s env var to true", acceptEnvVar) | ||
} |
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 wonder if this should just be a separate binary rather than a test? I can see how writing it as a test makes it easier to get a testing.T
for integration.ProgramTest(t, &test)
below, but it also means you have to have the extra env var to keep it from running most of the time. If it's not a huge amount of work to just make it run as a separate command, that could be a bit easier to maintain in the long term.
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.
We normally have this in a single test, that is always run in either a "record" or "replay" mode. We might want to do that here, since TestProviderUpgradeQuick
and TestProviderUpgradeRecord
interact via the file system.
Since it never makes sense to run both tests in 1 go, we might want to merge them here as well.
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.
Edited for it to sit in a single test. I do think refreshing belongs in the test world and not a separate binary, more idiomatic Go it seems, see https://github.com/hexops/autogold again.
|
||
func providerServer(t *testing.T) pulumirpc.ResourceProviderServer { | ||
ctx := context.Background() | ||
version.Version = "0.0.1" |
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.
probably just a go n00b type question, but I don't see where version is defined, or understand why you're setting it 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.
version.Version
is a global variable, normally set by the linker. We set it here because we expect that version.Version
is valid semver (which ""
is not).
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 can merge as is, and this will provider a useful backstop.
In the future, I think we should talk about the test structure. In particular, I don't understand why we have 3 test functions (with separate code) when I can only remember us wanting two tests.
Before we merge this PR, I would like all 3 tests to have a doc comment explaining what they are doing and how to run them (PULUMI_ACCEPT
, etc.).
examples/provider_upgrade_test.go
Outdated
func TestProviderUpgradeRecord(t *testing.T) { | ||
if !accept { | ||
t.Skipf("Skipping; to record baselines set %s env var to true", acceptEnvVar) | ||
} |
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.
We normally have this in a single test, that is always run in either a "record" or "replay" mode. We might want to do that here, since TestProviderUpgradeQuick
and TestProviderUpgradeRecord
interact via the file system.
Since it never makes sense to run both tests in 1 go, we might want to merge them here as well.
Partially fixes #2719
This PR adds coverage for testing provider upgrades from a baseline version (currently 5.42.0) to the in-source release candidate. The tests check that there are no upgrade or replace plans when upgrading the provider. This is verified on a few select resources.
Currently tested resources include:
The test relies on recording and replaying the behavior of the baseline version. There are two versions of the test, in-memory TestProviderUpgradeQuick version and full acceptance-style TestProviderUpgrade version. Full version is skipped for the moment with references to discovered issues.