fix(terraform): keep prior state when a virtual key update is rejected (#40512)

resourceKeyUpdate now calls d.Partial(true) on a failed /key/update so the SDK does not persist the rejected planned values into state, which made the next plan report no changes and silently dropped the update.

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
devin-ai-integration[bot] 2026-09-09 19:48:35 -07:00 committed by GitHub
parent 083ddefa92
commit fb603fdb6b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 42 additions and 0 deletions

View file

@ -315,6 +315,7 @@ func resourceKeyUpdate(ctx context.Context, d *schema.ResourceData, m interface{
metadata, err := plannedKeyMetadata(c, d)
if err != nil {
d.Partial(true)
return diag.FromErr(fmt.Errorf("error updating key: %s", err))
}
key.Metadata = metadata

View file

@ -319,6 +319,47 @@ func TestUpdateKeyOmitsEmptyBudgetDuration(t *testing.T) {
}
}
func TestResourceKeyUpdateFailureKeepsPriorState(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
if r.URL.Path == "/key/update" {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(`{"error":{"message":"Invalid budget_duration 'bad'"}}`))
return
}
w.Write([]byte(`{"key":"hash-1","info":{"key_alias":"demo","models":["fake-model"]}}`))
}))
defer srv.Close()
res := resourceKey()
priorData := newKeyResourceData(t, map[string]interface{}{
"key_alias": "demo",
"models": []interface{}{"fake-model"},
})
priorData.SetId("hash-1")
prior := priorData.State()
config := terraform.NewResourceConfigRaw(map[string]interface{}{
"key_alias": "demo",
"models": []interface{}{"fake-model"},
"budget_duration": "bad",
})
diff, err := res.Diff(context.Background(), prior, config, nil)
if err != nil {
t.Fatalf("diff failed: %v", err)
}
newState, diags := res.Apply(context.Background(), prior, diff, NewClient(srv.URL, "test-key", true))
if !diags.HasError() {
t.Fatal("apply succeeded, want the proxy's 400 surfaced as an error")
}
if got, ok := newState.Attributes["budget_duration"]; ok {
t.Errorf("failed update persisted budget_duration=%q into state, want it absent", got)
}
if newState.Attributes["key_alias"] != "demo" {
t.Errorf("prior key_alias lost from state: %v", newState.Attributes)
}
}
// /key/info nests the key's fields under "info"; GetKey must unwrap that
// envelope or reads map nothing back into state.
func TestGetKeyUnwrapsInfoEnvelope(t *testing.T) {