Skip to content

Commit 8e4245c

Browse files
authored
Merge pull request #160 from devops-rob/fix/134-upgrade-state-nil
Fix UpgradeState panic when prior state is nil
2 parents 66eaafc + b651f4c commit 8e4245c

3 files changed

Lines changed: 106 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 2.5.3
2+
3+
BUG FIXES:
4+
5+
- Fix panic upgrading `terracurl_request` state from provider v1.x when `UpgradeResourceState` receives nil prior state. Closes #134.
6+
17
## 2.5.2
28

39
BUG FIXES:

internal/provider/curl_resource.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,13 +1020,24 @@ func (r *CurlResource) UpgradeState(ctx context.Context) map[int64]resource.Stat
10201020

10211021
var oldState CurlResourceModel
10221022

1023-
// First, try the normal state extraction
1024-
diags := req.State.Get(ctx, &oldState)
1023+
var diags diag.Diagnostics
1024+
useRawState := req.State == nil
10251025

1026-
if diags.HasError() {
1027-
tflog.Info(ctx, "Direct state extraction failed, attempting to extract values from raw state", map[string]interface{}{
1028-
"errors": diags.Errors(),
1029-
})
1026+
if !useRawState {
1027+
diags = req.State.Get(ctx, &oldState)
1028+
if diags.HasError() {
1029+
useRawState = true
1030+
}
1031+
}
1032+
1033+
if useRawState {
1034+
if req.State == nil {
1035+
tflog.Info(ctx, "Prior state is nil, attempting to extract values from raw state")
1036+
} else {
1037+
tflog.Info(ctx, "Direct state extraction failed, attempting to extract values from raw state", map[string]interface{}{
1038+
"errors": diags.Errors(),
1039+
})
1040+
}
10301041

10311042
// Try to extract what we can from the raw state
10321043
if req.RawState != nil && len(req.RawState.JSON) > 0 {

internal/provider/curl_resource_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1762,3 +1762,86 @@ func TestCurlResource_StateUpgrade_EmptyDestroyParameters(t *testing.T) {
17621762
t.Errorf("Expected id to be preserved, got '%s'", upgradedState.Id.ValueString())
17631763
}
17641764
}
1765+
1766+
func TestCurlResource_StateUpgrade_NilRequestState(t *testing.T) {
1767+
ctx := context.Background()
1768+
r := &CurlResource{}
1769+
1770+
upgraders := r.UpgradeState(ctx)
1771+
upgrader, ok := upgraders[0]
1772+
if !ok {
1773+
t.Fatal("No upgrader found for version 0")
1774+
}
1775+
1776+
// v1.2-style state JSON from issue #134 (provider SDK -> framework upgrade).
1777+
rawStateJSON := `{
1778+
"id": "example-get-call",
1779+
"name": "example-get-call",
1780+
"url": "https://jsonplaceholder.typicode.com/posts/1",
1781+
"method": "GET",
1782+
"headers": {"Content-Type": "application/json"},
1783+
"response_codes": ["200", "201"],
1784+
"response": "{\"userId\":1,\"id\":1,\"title\":\"sunt aut facere\",\"body\":\"quia et suscipit\"}",
1785+
"status_code": "200"
1786+
}`
1787+
1788+
rUpgrade := &CurlResource{}
1789+
schemaResp := &resource2.SchemaResponse{}
1790+
rUpgrade.Schema(ctx, resource2.SchemaRequest{}, schemaResp)
1791+
schemaVar := schemaResp.Schema
1792+
1793+
req := resource2.UpgradeStateRequest{
1794+
State: nil,
1795+
RawState: &tfprotov6.RawState{
1796+
JSON: []byte(rawStateJSON),
1797+
},
1798+
}
1799+
1800+
resp := &resource2.UpgradeStateResponse{
1801+
State: tfsdk.State{
1802+
Schema: schemaVar,
1803+
},
1804+
}
1805+
1806+
upgrader.StateUpgrader(ctx, req, resp)
1807+
if resp.Diagnostics.HasError() {
1808+
t.Fatalf("upgrade failed: %v", resp.Diagnostics)
1809+
}
1810+
1811+
var upgradedState CurlResourceModel
1812+
diags := resp.State.Get(ctx, &upgradedState)
1813+
if diags.HasError() {
1814+
t.Fatalf("error getting upgraded state: %v", diags)
1815+
}
1816+
1817+
if upgradedState.Id.ValueString() != "example-get-call" {
1818+
t.Errorf("Expected id 'example-get-call', got '%s'", upgradedState.Id.ValueString())
1819+
}
1820+
if upgradedState.Name.ValueString() != "example-get-call" {
1821+
t.Errorf("Expected name 'example-get-call', got '%s'", upgradedState.Name.ValueString())
1822+
}
1823+
if upgradedState.Url.ValueString() != "https://jsonplaceholder.typicode.com/posts/1" {
1824+
t.Errorf("Expected url preserved, got '%s'", upgradedState.Url.ValueString())
1825+
}
1826+
if upgradedState.Method.ValueString() != "GET" {
1827+
t.Errorf("Expected method 'GET', got '%s'", upgradedState.Method.ValueString())
1828+
}
1829+
if upgradedState.Headers.IsNull() {
1830+
t.Error("Expected headers to be preserved")
1831+
}
1832+
if upgradedState.ResponseCodes.IsNull() {
1833+
t.Error("Expected response_codes to be preserved")
1834+
}
1835+
if !upgradedState.SkipRead.ValueBool() {
1836+
t.Error("Expected skip_read to be set to true in v1 upgrade")
1837+
}
1838+
if !upgradedState.ReadUrl.IsNull() {
1839+
t.Error("Expected read_url to be null in v1 upgrade")
1840+
}
1841+
if !upgradedState.ReadResponseCodes.IsNull() {
1842+
t.Error("Expected read_response_codes to be null in v1 upgrade")
1843+
}
1844+
if upgradedState.ResponseSensitive.IsNull() || upgradedState.ResponseSensitive.ValueBool() {
1845+
t.Error("Expected response_sensitive to default to false")
1846+
}
1847+
}

0 commit comments

Comments
 (0)