Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions provider/provider_program_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ func TestZeroTrustAccessApplicationFromState(t *testing.T) {
pt.Preview(t)
}

func TestRuleSetHeadersUpgrade(t *testing.T) {
testUpgrade(
t, "test-programs/ruleset_headers/ruleset_headers_v5",
optproviderupgrade.NewSourcePath("test-programs/ruleset_headers"))
}

func TestAccRecordGo(t *testing.T) {
pt := testProgram(t, "test-programs/recordgo",
opttest.TestInPlace(), /* to use the parent directory's module */
Expand Down
22 changes: 20 additions & 2 deletions provider/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ func Provider() info.Provider {
PreStateUpgradeHook: func(
args info.PreStateUpgradeHookArgs,
) (int64, resource.PropertyMap, error) {
// v5 stored actionParameters.headers as an array of {name, operation, value, expression};
// v6 stores it as a MapNestedAttribute keyed by header name. See pulumi/pulumi-cloudflare#1172.
updateRuleHeaders := func(r resource.PropertyValue) resource.PropertyValue {
if !r.IsObject() {
return r
Expand All @@ -144,12 +146,28 @@ func Provider() info.Provider {
if !ok || !headers.IsArray() {
return r
}
if len(headers.ArrayValue()) == 0 {
arr := headers.ArrayValue()
if len(arr) == 0 {
delete(apCopy, "headers")
ruleCopy["actionParameters"] = resource.NewObjectProperty(apCopy)
return resource.NewObjectProperty(ruleCopy)
}
return r
headersMap := resource.PropertyMap{}
for _, h := range arr {
if !h.IsObject() {
return r
}
hObj := h.ObjectValue().Copy()
name, ok := hObj["name"]
if !ok || !name.IsString() {
return r
}
delete(hObj, "name")
headersMap[resource.PropertyKey(name.StringValue())] = resource.NewObjectProperty(hObj)
}
apCopy["headers"] = resource.NewObjectProperty(headersMap)
ruleCopy["actionParameters"] = resource.NewObjectProperty(apCopy)
return resource.NewObjectProperty(ruleCopy)
}

// This migrates the rules field from a map[string]string to a map[string][]string
Expand Down
26 changes: 26 additions & 0 deletions provider/test-programs/ruleset_headers/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: example-ruleset-headers
runtime: yaml

config:
cloudflare-zone-id:
type: string

resources:
headers-ruleset:
type: cloudflare:Ruleset
properties:
name: pulumi_cloudflare_test_headers
kind: zone
zoneId: ${cloudflare-zone-id}
phase: http_request_late_transform
rules:
- action: rewrite
actionParameters:
headers:
X-Pulumi-Test-Set:
operation: set
value: hello
X-Pulumi-Test-Remove:
operation: remove
enabled: true
expression: '(http.host eq "example.com")'
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: example-ruleset-headers
runtime: yaml

config:
cloudflare-zone-id:
type: string

resources:
headers-ruleset:
type: cloudflare:Ruleset
properties:
name: pulumi_cloudflare_test_headers
kind: zone
zoneId: ${cloudflare-zone-id}
phase: http_request_late_transform
rules:
- action: rewrite
actionParameters:
headers:
- name: X-Pulumi-Test-Set
operation: set
value: hello
- name: X-Pulumi-Test-Remove
operation: remove
enabled: true
expression: '(http.host eq "example.com")'

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
{
"version": 3,
"deployment": {
"manifest": {
"time": "2026-05-08T13:09:45.531431-07:00",
"magic": "d356bec6413f613372e5158db9b9441f6f60fd81ed9a5e4980d2b8af2faf06fd",
"version": "v3.228.0"
},
"secrets_providers": {
"type": "passphrase",
"state": {
"salt": "v1:1Ta9t6TL5ZQ=:v1:4AD66Vr4pqHB/Wd4:aQbSUW2njfHItss34OYleHtCyzwM5w=="
}
},
"resources": [
{
"urn": "urn:pulumi:test::example-ruleset-headers::pulumi:pulumi:Stack::example-ruleset-headers-test",
"custom": false,
"type": "pulumi:pulumi:Stack",
"created": "2026-05-08T20:09:44.060081Z",
"modified": "2026-05-08T20:09:44.060081Z",
"sourcePosition": "project:///../../../../../../../../../home/runner/work/pulumi-yaml/pulumi-yaml/pkg/server/server.go#322",
"stackTrace": [
{
"sourcePosition": "project:///../../../../../../../../../home/runner/work/pulumi-yaml/pulumi-yaml/pkg/server/server.go#322"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/go/pkg/mod/github.com/pulumi/pulumi/sdk/v3@v3.225.1/proto/go/language_grpc.pb.go#586"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/go/pkg/mod/github.com/pulumi/pulumi/sdk/v3@v3.225.1/go/common/util/rpcutil/interceptor.go#287"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/go/pkg/mod/google.golang.org/grpc@v1.79.1/server.go#1239"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/go/pkg/mod/github.com/grpc-ecosystem/grpc-opentracing@v0.0.0-20180507213350-8e809c8a8645/go/otgrpc/server.go#57"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/go/pkg/mod/google.golang.org/grpc@v1.79.1/server.go#1239"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/go/pkg/mod/github.com/pulumi/pulumi/sdk/v3@v3.225.1/go/common/util/rpcutil/interceptor.go#214"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/go/pkg/mod/google.golang.org/grpc@v1.79.1/server.go#1230"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/go/pkg/mod/github.com/pulumi/pulumi/sdk/v3@v3.225.1/proto/go/language_grpc.pb.go#588"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/go/pkg/mod/google.golang.org/grpc@v1.79.1/server.go#1426"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/go/pkg/mod/google.golang.org/grpc@v1.79.1/server.go#1830"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/go/pkg/mod/google.golang.org/grpc@v1.79.1/server.go#1061"
},
{
"sourcePosition": "project:///../../../../../../../../../opt/hostedtoolcache/go/1.25.6/x64/src/runtime/asm_arm64.s#1268"
}
]
},
{
"urn": "urn:pulumi:test::example-ruleset-headers::pulumi:providers:cloudflare::default",
"custom": true,
"id": "999b4fc8-198a-45e9-9436-813e8062da90",
"type": "pulumi:providers:cloudflare",
"inputs": {
"apiClientLogging": "false",
"maxBackoff": "30",
"minBackoff": "1",
"retries": "3",
"rps": "4"
},
"outputs": {
"apiClientLogging": "false",
"maxBackoff": "30",
"minBackoff": "1",
"retries": "3",
"rps": "4"
},
"created": "2026-05-08T20:09:44.093821Z",
"modified": "2026-05-08T20:09:44.093821Z"
},
{
"urn": "urn:pulumi:test::example-ruleset-headers::cloudflare:index/ruleset:Ruleset::headers-ruleset",
"custom": true,
"id": "e1f2f04f324a454998eed957f4e7ed42",
"type": "cloudflare:index/ruleset:Ruleset",
"inputs": {
"kind": "zone",
"name": "pulumi_cloudflare_test_headers",
"phase": "http_request_late_transform",
"rules": [
{
"action": "rewrite",
"actionParameters": {
"headers": [
{
"name": "X-Pulumi-Test-Set",
"operation": "set",
"value": "hello"
},
{
"name": "X-Pulumi-Test-Remove",
"operation": "remove"
}
]
},
"enabled": true,
"expression": "(http.host eq \"example.com\")"
}
],
"zoneId": "bd14943dfb0763ebef635e52db9a7d47"
},
"outputs": {
"__meta": "{\"schema_version\":\"1\"}",
"description": "",
"id": "e1f2f04f324a454998eed957f4e7ed42",
"kind": "zone",
"name": "pulumi_cloudflare_test_headers",
"phase": "http_request_late_transform",
"rules": [
{
"action": "rewrite",
"actionParameters": {
"algorithms": [],
"autominifies": [],
"headers": [
{
"name": "X-Pulumi-Test-Remove",
"operation": "remove"
},
{
"name": "X-Pulumi-Test-Set",
"operation": "set",
"value": "hello"
}
],
"responses": []
},
"description": "",
"enabled": true,
"expression": "(http.host eq \"example.com\")",
"id": "4a8cdc15e7854cd09a62733d68e2827c",
"ref": "4a8cdc15e7854cd09a62733d68e2827c"
}
],
"zoneId": "bd14943dfb0763ebef635e52db9a7d47"
},
"parent": "urn:pulumi:test::example-ruleset-headers::pulumi:pulumi:Stack::example-ruleset-headers-test",
"provider": "urn:pulumi:test::example-ruleset-headers::pulumi:providers:cloudflare::default::999b4fc8-198a-45e9-9436-813e8062da90",
"propertyDependencies": {
"kind": [],
"name": [],
"phase": [],
"rules": [],
"zoneId": []
},
"created": "2026-05-08T20:09:45.528481Z",
"modified": "2026-05-08T20:09:45.528481Z",
"sourcePosition": "project:///../../../../../../../../../home/runner/work/pulumi-yaml/pulumi-yaml/pkg/pulumiyaml/run.go#998",
"stackTrace": [
{
"sourcePosition": "project:///../../../../../../../../../home/runner/work/pulumi-yaml/pulumi-yaml/pkg/pulumiyaml/run.go#998"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/work/pulumi-yaml/pulumi-yaml/pkg/pulumiyaml/run.go#1222"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/work/pulumi-yaml/pulumi-yaml/pkg/pulumiyaml/run.go#1077"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/work/pulumi-yaml/pulumi-yaml/pkg/pulumiyaml/run.go#505"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/work/pulumi-yaml/pulumi-yaml/pkg/server/server.go#324"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/go/pkg/mod/github.com/pulumi/pulumi/sdk/v3@v3.225.1/go/pulumi/run.go#144"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/work/pulumi-yaml/pulumi-yaml/pkg/server/server.go#322"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/go/pkg/mod/github.com/pulumi/pulumi/sdk/v3@v3.225.1/proto/go/language_grpc.pb.go#586"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/go/pkg/mod/github.com/pulumi/pulumi/sdk/v3@v3.225.1/go/common/util/rpcutil/interceptor.go#287"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/go/pkg/mod/google.golang.org/grpc@v1.79.1/server.go#1239"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/go/pkg/mod/github.com/grpc-ecosystem/grpc-opentracing@v0.0.0-20180507213350-8e809c8a8645/go/otgrpc/server.go#57"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/go/pkg/mod/google.golang.org/grpc@v1.79.1/server.go#1239"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/go/pkg/mod/github.com/pulumi/pulumi/sdk/v3@v3.225.1/go/common/util/rpcutil/interceptor.go#214"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/go/pkg/mod/google.golang.org/grpc@v1.79.1/server.go#1230"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/go/pkg/mod/github.com/pulumi/pulumi/sdk/v3@v3.225.1/proto/go/language_grpc.pb.go#588"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/go/pkg/mod/google.golang.org/grpc@v1.79.1/server.go#1426"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/go/pkg/mod/google.golang.org/grpc@v1.79.1/server.go#1830"
},
{
"sourcePosition": "project:///../../../../../../../../../home/runner/go/pkg/mod/google.golang.org/grpc@v1.79.1/server.go#1061"
},
{
"sourcePosition": "project:///../../../../../../../../../opt/hostedtoolcache/go/1.25.6/x64/src/runtime/asm_arm64.s#1268"
}
]
}
],
"metadata": {}
}
}
Loading