diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 5e78e668..94583d8f 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -2,4 +2,6 @@ ### Bug Fixes +- Updating the timestamp of a one-time schedule will now cause a replacement, allowing schedules that have already run to be rescheduled correctly: [#455](https://github.com/pulumi/pulumi-pulumiservice/pull/455) + ### Miscellaneous diff --git a/examples/examples_yaml_test.go b/examples/examples_yaml_test.go index bca49b45..2c5c25ea 100644 --- a/examples/examples_yaml_test.go +++ b/examples/examples_yaml_test.go @@ -9,6 +9,7 @@ import ( "path" "strings" "testing" + "time" "github.com/google/uuid" "github.com/pulumi/pulumi/pkg/v3/testing/integration" @@ -20,6 +21,7 @@ import ( type Resource struct { Type string `yaml:"type"` Properties map[string]interface{} `yaml:"properties"` + Options map[string]interface{} `yaml:"options"` } type YamlProgram struct { Name string `yaml:"name"` @@ -248,14 +250,92 @@ func TestYamlWebhookExample(t *testing.T) { } func TestYamlSchedulesExample(t *testing.T) { - cwd := getCwd(t) - digits := generateRandomFiveDigits() - integration.ProgramTest(t, &integration.ProgramTestOptions{ - Dir: path.Join(cwd, ".", "yaml-schedules"), - StackName: "test-stack-" + digits, - Config: map[string]string{ - "digits": digits, - }, + + t.Run("Yaml Schedules Example", func(t *testing.T) { + cwd := getCwd(t) + digits := generateRandomFiveDigits() + integration.ProgramTest(t, &integration.ProgramTestOptions{ + Dir: path.Join(cwd, ".", "yaml-schedules"), + StackName: "test-stack-" + digits, + Config: map[string]string{ + "digits": digits, + }, + }) + }) + + t.Run("Schedules are replaced on timestamp update", func(t *testing.T) { + writeScheduleProgram := func(timestamp time.Time) string { + return writePulumiYaml(t, YamlProgram{ + Name: "yaml-schedule-reschedule", + Runtime: "yaml", + Resources: map[string]Resource{ + "settings": { + // deployment settings are required to be setup before schedules + Type: "pulumiservice:DeploymentSettings", + Properties: map[string]any{ + "organization": ServiceProviderTestOrg, + "project": "${pulumi.project}", + "stack": "${pulumi.stack}", + }, + }, + "deployment-schedule": { + Type: "pulumiservice:DeploymentSchedule", + Properties: map[string]any{ + "organization": ServiceProviderTestOrg, + "project": "${pulumi.project}", + "stack": "${pulumi.stack}", + "timestamp": timestamp.Format(time.RFC3339), + "pulumiOperation": "refresh", + }, + Options: map[string]any{ + "dependsOn": []string{"${settings}"}, + }, + }, + "ttl-schedule": { + Type: "pulumiservice:TtlSchedule", + Properties: map[string]any{ + "organization": ServiceProviderTestOrg, + "project": "${pulumi.project}", + "stack": "${pulumi.stack}", + "timestamp": timestamp.Format(time.RFC3339), + "deleteAfterDestroy": false, + }, + Options: map[string]any{ + "dependsOn": []string{"${settings}"}, + }, + }, + }, + }) + } + + // create some initial one-time schedules + initialDir := writeScheduleProgram(time.Now().Add(1 * time.Hour)) + // and then reschedule them + rescheduleDir := writeScheduleProgram(time.Now().Add(2 * time.Hour)) + + update := &strings.Builder{} + updateOut := io.MultiWriter(os.Stdout, update) + + integration.ProgramTest(t, &integration.ProgramTestOptions{ + StackName: "test-stack-" + generateRandomFiveDigits(), + Dir: initialDir, + Quick: true, + SkipRefresh: true, + EditDirs: []integration.EditDir{ + { + Dir: rescheduleDir, + Additive: true, + Stdout: updateOut, + Stderr: updateOut, + Verbose: true, + ExpectNoChanges: false, + }, + }, + }) + + // expect the update to cause schedule replacements + assert.Contains(t, update.String(), "deployment-schedule replaced") + assert.Contains(t, update.String(), "ttl-schedule replaced") }) } diff --git a/provider/cmd/pulumi-resource-pulumiservice/schema.json b/provider/cmd/pulumi-resource-pulumiservice/schema.json index e170d37c..d228171a 100644 --- a/provider/cmd/pulumi-resource-pulumiservice/schema.json +++ b/provider/cmd/pulumi-resource-pulumiservice/schema.json @@ -1254,7 +1254,8 @@ }, "timestamp": { "description": "The time at which the schedule should run, in ISO 8601 format. Eg: 2020-01-01T00:00:00Z. If you are supplying this, do not supply scheduleCron.", - "type": "string" + "type": "string", + "willReplaceOnChanges": true }, "pulumiOperation": { "description": "Which operation to run.", @@ -1291,7 +1292,8 @@ }, "timestamp": { "description": "The time at which the schedule should run, in ISO 8601 format. Eg: 2020-01-01T00:00:00Z. If you are supplying this, do not supply scheduleCron.", - "type": "string" + "type": "string", + "willReplaceOnChanges": true }, "pulumiOperation": { "description": "Which command to run.", @@ -1388,7 +1390,8 @@ }, "timestamp": { "description": "The time at which the schedule should run, in ISO 8601 format. Eg: 2020-01-01T00:00:00Z.", - "type": "string" + "type": "string", + "willReplaceOnChanges": true }, "deleteAfterDestroy": { "description": "True if the stack and all associated history and settings should be deleted.", @@ -1422,7 +1425,8 @@ }, "timestamp": { "description": "The time at which the schedule should run, in ISO 8601 format. Eg: 2020-01-01T00:00:00Z.", - "type": "string" + "type": "string", + "willReplaceOnChanges": true }, "deleteAfterDestroy": { "description": "True if the stack and all associated history and settings should be deleted.", diff --git a/provider/pkg/provider/deployment_schedules.go b/provider/pkg/provider/deployment_schedules.go index e25464f2..926530ea 100644 --- a/provider/pkg/provider/deployment_schedules.go +++ b/provider/pkg/provider/deployment_schedules.go @@ -167,6 +167,7 @@ func ScheduleSharedDiffMaps(olds resource.PropertyMap, news resource.PropertyMap "organization": true, "project": true, "stack": true, + "timestamp": true, } for k, v := range dd { if _, ok := replaceProperties[k]; ok {