Skip to content

Commit

Permalink
enable passed one-time scheduled actions to be rescheduled (#455)
Browse files Browse the repository at this point in the history
One-time schedules are [soft-deleted after
running](https://github.com/pulumi/pulumi-service/blob/186e4d4249d635f8f928f416c371c739ae0b52b5/cmd/service/model/schedules.go#L457),
which means updating their schedule doesn't work because the action is
never actually picked up again by the scheduler. (Prior to
pulumi/pulumi-service#24133 the update would
appear to succeed, but the scheduled action would just silently never
run. Now at least you get an error indicating the schedule was deleted.)

This PR enables rescheduling to work correctly by forcing the schedule
to be replaced when its timestamp is updated.

Partially addresses #449
  • Loading branch information
nyobe authored Dec 9, 2024
1 parent d8143d5 commit 70aebb0
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 12 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG_PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
96 changes: 88 additions & 8 deletions examples/examples_yaml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"path"
"strings"
"testing"
"time"

"github.com/google/uuid"
"github.com/pulumi/pulumi/pkg/v3/testing/integration"
Expand All @@ -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"`
Expand Down Expand Up @@ -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")
})
}

Expand Down
12 changes: 8 additions & 4 deletions provider/cmd/pulumi-resource-pulumiservice/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down Expand Up @@ -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.",
Expand Down Expand Up @@ -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.",
Expand Down Expand Up @@ -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.",
Expand Down
1 change: 1 addition & 0 deletions provider/pkg/provider/deployment_schedules.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 70aebb0

Please sign in to comment.