diff --git a/pkg/internal/tests/cross-tests/upgrade_state_check_test.go b/pkg/internal/tests/cross-tests/upgrade_state_check_test.go index 05b86490a..ad8dac39b 100644 --- a/pkg/internal/tests/cross-tests/upgrade_state_check_test.go +++ b/pkg/internal/tests/cross-tests/upgrade_state_check_test.go @@ -72,6 +72,9 @@ type upgradeStateTestCase struct { // would always pick the provider version recorded in the state to perform operations against. So these checks // are possibly moot, but might be useful in the future if Pulumi behavior around refresh changes. ExperimentalPulumiRefresh bool + + // Do not auto-generate an Update function to test resources that do not support Update. + DoesNotSupportUpdate bool } type upgradeStateTestPhase string @@ -164,6 +167,15 @@ func instrumentCustomizeDiff(t *testing.T, tc upgradeStateTestCase) upgradeState } func instrumentUpdate(t *testing.T, tc upgradeStateTestCase) upgradeStateTestCase { + if tc.DoesNotSupportUpdate { + r1 := *tc.Resource1 + require.Nilf(t, r1.UpdateContext, "Resource1.UpdateContext is not compatible with DoesNotSupportUpdate") + r2 := *tc.Resource2 + require.Nilf(t, r2.UpdateContext, "Resource2.UpdateContext is not compatible with DoesNotSupportUpdate") + + return tc + } + r2 := *tc.Resource2 require.Nilf(t, r2.UpdateContext, "Resource2.UpdateContext cannot yet be set in tests") @@ -388,15 +400,17 @@ func runUpgradeStateTestTF(t T, tc upgradeStateTestCase) []upgradeStateTrace { tracker.phase = previewPhase plan, err := tfd2.writePlanErr(t, tc.Resource2.Schema, defRtype, rname, inputs2, lifecycleArgs{}) if tc.ExpectFailure { - require.Errorf(t, err, "refresh should have failed") + require.Errorf(t, err, "plan should have failed") return tracker.trace } - require.NoErrorf(t, err, "refresh should not have failed") + require.NoErrorf(t, err, "plan should not have failed") + t.Logf("plan.StdOut: %s", plan.StdOut) t.Logf("#### apply (similar to update)") tracker.phase = updatePhase - err = tfd2.driver.ApplyPlan(t, plan) + applyStdout, err := tfd2.driver.ApplyPlanReturnStdOut(t, plan) require.NoError(t, err) + t.Logf("applyStdout: %s", string(applyStdout)) return tracker.trace } diff --git a/pkg/internal/tests/cross-tests/upgrade_state_cross_test.go b/pkg/internal/tests/cross-tests/upgrade_state_cross_test.go index 9b1caf6e1..e99e0d81a 100644 --- a/pkg/internal/tests/cross-tests/upgrade_state_cross_test.go +++ b/pkg/internal/tests/cross-tests/upgrade_state_cross_test.go @@ -1065,3 +1065,68 @@ func TestUpgrade_Downgrading(t *testing.T) { autogold.Expect([]upgradeStateTrace{}).Equal(t, result.pulumiUpgrades) }) } + +// See https://github.com/pulumi/pulumi-gcp/issues/3236 +func TestUpgrade_FieldsAddedButUpdateNotSupported(t *testing.T) { + t.Parallel() + skipUnlessLinux(t) + //skipUnlessDeltasEnabled(t) + + sch1 := map[string]*schema.Schema{ + "name": { + Required: true, + ForceNew: true, + Type: schema.TypeString, + }, + } + + sch2 := map[string]*schema.Schema{ + "name": { + Required: true, + ForceNew: true, + Type: schema.TypeString, + }, + "as_paths": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "as_lists": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + "path_segment_type": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + } + + res1 := &schema.Resource{Schema: sch1} + res2 := &schema.Resource{Schema: sch2} + + configVal := func() map[string]any { + return map[string]any{ + "name": "resource-1", + } + } + + t.Run("same", func(t *testing.T) { + result := runUpgradeStateTest(t, upgradeStateTestCase{ + Resource1: res1, + Resource2: res2, + Inputs1: configVal(), + Inputs2: configVal(), + + DoesNotSupportUpdate: true, + SkipSchemaVersionAfterUpdateCheck: true, + }) + + autogold.Expect((*map[string]int)(nil)).Equal(t, result.pulumiUpResult.Summary.ResourceChanges) + }) +} diff --git a/pkg/internal/tests/pulcheck/pulcheck.go b/pkg/internal/tests/pulcheck/pulcheck.go index b2124a637..3acd407c6 100644 --- a/pkg/internal/tests/pulcheck/pulcheck.go +++ b/pkg/internal/tests/pulcheck/pulcheck.go @@ -99,12 +99,12 @@ func WithValidProvider(t T, tfp *schema.Provider) *schema.Provider { // This is necessary to work around a bug in the TF SDK schema validation where some resources which // do need an Update method will instead be flagged as not needing an Update method. // See https://github.com/pulumi/pulumi-terraform-bridge/pull/2723#issuecomment-2541518646 - if !resourceNeedsUpdate(r) { - r.Schema["update_prop"] = &schema.Schema{ - Type: schema.TypeString, - Optional: true, - } - } + // if !resourceNeedsUpdate(r) { + // r.Schema["update_prop"] = &schema.Schema{ + // Type: schema.TypeString, + // Optional: true, + // } + // } if r.ReadContext == nil { r.ReadContext = func(_ context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { @@ -130,13 +130,13 @@ func WithValidProvider(t T, tfp *schema.Provider) *schema.Provider { // Because of the no-op update_prop property added above, all resources will now // need an Update method. - if r.UpdateContext == nil { - r.UpdateContext = func( - ctx context.Context, rd *schema.ResourceData, i interface{}, - ) diag.Diagnostics { - return diag.Diagnostics{} - } - } + // if r.UpdateContext == nil { + // r.UpdateContext = func( + // ctx context.Context, rd *schema.ResourceData, i interface{}, + // ) diag.Diagnostics { + // return diag.Diagnostics{} + // } + // } } require.NoError(t, tfp.InternalValidate()) diff --git a/pkg/tests/tfcheck/tfcheck.go b/pkg/tests/tfcheck/tfcheck.go index c229865f6..8be7e89ac 100644 --- a/pkg/tests/tfcheck/tfcheck.go +++ b/pkg/tests/tfcheck/tfcheck.go @@ -135,11 +135,11 @@ func (d *TFDriver) Write(t pulcheck.T, program string) { require.NoErrorf(t, err, "writing test.tf") } -// Plans an operation. This sets -refresh=false which is not the default behavior in Terraform but it better matches +// Plans an operation. This sets which is not the default behavior in Terraform but it better matches // the target behavior that bridged providers currently emulate. func (d *TFDriver) Plan(t pulcheck.T) (*TFPlan, error) { planFile := filepath.Join(d.cwd, "test.tfplan") - planStdoutBytes, err := d.execTf(t, "plan", "-refresh=false", "-out", planFile, "-no-color") + planStdoutBytes, err := d.execTf(t, "plan", "-out", planFile, "-no-color") if err != nil { return nil, err } @@ -152,20 +152,24 @@ func (d *TFDriver) Plan(t pulcheck.T) (*TFPlan, error) { return &tp, nil } -// Executes apply. This sets -refresh=false which is not the default behavior in Terraform but it better matches the +// Executes apply. This sets which is not the default behavior in Terraform but it better matches the // target behavior that bridged providers currently emulate. Unlike [ApplyPlan] this operation will recompute the plan // on the fly. func (d *TFDriver) Apply(t pulcheck.T) error { - _, err := d.execTf(t, "apply", "-auto-approve", "-refresh=false") + _, err := d.execTf(t, "apply", "-auto-approve") return err } // Apply a given plan. func (d *TFDriver) ApplyPlan(t pulcheck.T, plan *TFPlan) error { - _, err := d.execTf(t, "apply", "-auto-approve", "-refresh=false", plan.PlanFile) + _, err := d.execTf(t, "apply", "-auto-approve", plan.PlanFile) return err } +func (d *TFDriver) ApplyPlanReturnStdOut(t pulcheck.T, plan *TFPlan) ([]byte, error) { + return d.execTf(t, "apply", "-auto-approve", plan.PlanFile) +} + func (d *TFDriver) Refresh(t pulcheck.T) error { _, err := d.execTf(t, "refresh") return err diff --git a/pkg/tfgen/source.go b/pkg/tfgen/source.go index 37fb941cf..b4ab59408 100644 --- a/pkg/tfgen/source.go +++ b/pkg/tfgen/source.go @@ -159,8 +159,8 @@ func getRepoPath(gitHost string, org string, provider string, version string) (_ command.Dir = curWd output, err := command.CombinedOutput() if err != nil { - msg := "error running 'go mod download -json' in %q dir for module: %w\n\nOutput: %s" - return "", fmt.Errorf(msg, curWd, err, output) + msg := "error running 'go mod download -json %q' in %q dir for module: %w\n\nOutput: %s" + return "", fmt.Errorf(msg, moduleCoordinates, curWd, err, output) } target := struct { @@ -170,7 +170,8 @@ func getRepoPath(gitHost string, org string, provider string, version string) (_ }{} if err := json.Unmarshal(output, &target); err != nil { - return "", fmt.Errorf("error parsing output of 'go mod download -json' for module: %w", err) + return "", fmt.Errorf("error parsing output of 'go mod download -json %q' for module: %w", + moduleCoordinates, err) } if target.Error != "" {