Skip to content

Commit eee48fa

Browse files
committed
feat: jd changesets
1 parent f10f84c commit eee48fa

11 files changed

Lines changed: 1591 additions & 151 deletions

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ require (
3131
golang.org/x/crypto v0.53.0
3232
golang.org/x/mod v0.37.0
3333
golang.org/x/sync v0.21.0
34+
google.golang.org/grpc v1.81.1
3435
gopkg.in/yaml.v3 v3.0.1
3536
)
3637

@@ -320,7 +321,6 @@ require (
320321
golang.org/x/tools v0.45.0 // indirect
321322
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect
322323
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
323-
google.golang.org/grpc v1.81.1 // indirect
324324
google.golang.org/protobuf v1.36.11 // indirect
325325
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
326326
gopkg.in/inf.v0 v0.9.1 // indirect

jd/changesets/jobs.go

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package changesets
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
7+
jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job"
8+
9+
cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment"
10+
fwops "github.com/smartcontractkit/chainlink-deployments-framework/operations"
11+
12+
jdops "github.com/smartcontractkit/cld-changesets/jd/operations"
13+
)
14+
15+
var (
16+
_ cldf.ChangeSetV2[ProposeJobsInput] = ProposeJobsChangeset{}
17+
_ cldf.ChangeSetV2[RevokeJobsInput] = RevokeJobsChangeset{}
18+
_ cldf.ChangeSetV2[DeleteJobsInput] = DeleteJobsChangeset{}
19+
)
20+
21+
type JobSpec = jdops.JobSpec
22+
type ProposeJobsInput = jdops.ProposeJobsInput
23+
type RevokeJobsInput = jdops.RevokeJobsInput
24+
type DeleteJobsInput = jdops.DeleteJobsInput
25+
26+
// ProposeJobsChangeset proposes a batch of job specs to nodes matched by label.
27+
type ProposeJobsChangeset struct{}
28+
29+
func (ProposeJobsChangeset) VerifyPreconditions(_ cldf.Environment, cfg ProposeJobsInput) error {
30+
if cfg.Domain == "" {
31+
return errors.New("domain is required")
32+
}
33+
if len(cfg.Jobs) == 0 {
34+
return errors.New("no jobs provided")
35+
}
36+
for i, j := range cfg.Jobs {
37+
if j.JobspecTOML == "" {
38+
return fmt.Errorf("job[%d]: jobspec_toml is required", i)
39+
}
40+
if len(j.NodeLabels) == 0 {
41+
return fmt.Errorf("job[%d]: node_labels is required", i)
42+
}
43+
for k, v := range j.NodeLabels {
44+
if k == "" || v == "" {
45+
return fmt.Errorf("job[%d]: node_labels key and value must be non-empty (key=%q value=%q)", i, k, v)
46+
}
47+
}
48+
}
49+
50+
return nil
51+
}
52+
53+
func (ProposeJobsChangeset) Apply(e cldf.Environment, input ProposeJobsInput) (cldf.ChangesetOutput, error) {
54+
deps := jdops.JDOpDeps{Offchain: e.Offchain, EnvName: e.Name}
55+
seqReport, err := fwops.ExecuteSequence(e.OperationsBundle, jdops.SeqJDProposeJobs, deps, input)
56+
reports := append(seqReport.ExecutionReports, seqReport.ToGenericReport())
57+
58+
return cldf.ChangesetOutput{Reports: reports}, err
59+
}
60+
61+
// RevokeJobsChangeset revokes jobs.
62+
type RevokeJobsChangeset struct{}
63+
64+
func (RevokeJobsChangeset) VerifyPreconditions(_ cldf.Environment, cfg RevokeJobsInput) error {
65+
if len(cfg.JobIDs) == 0 {
66+
return errors.New("no job_ids provided")
67+
}
68+
seen := make(map[string]struct{}, len(cfg.JobIDs))
69+
for _, id := range cfg.JobIDs {
70+
if id == "" {
71+
return errors.New("job id cannot be empty")
72+
}
73+
if _, ok := seen[id]; ok {
74+
return fmt.Errorf("duplicate job id %q", id)
75+
}
76+
seen[id] = struct{}{}
77+
}
78+
79+
return nil
80+
}
81+
82+
func (RevokeJobsChangeset) Apply(e cldf.Environment, input RevokeJobsInput) (cldf.ChangesetOutput, error) {
83+
deps := jdops.JDOpDeps{Offchain: e.Offchain, EnvName: e.Name}
84+
seqReport, err := fwops.ExecuteSequence(e.OperationsBundle, jdops.SeqJDRevokeJobs, deps, input)
85+
reports := append(seqReport.ExecutionReports, seqReport.ToGenericReport())
86+
87+
return cldf.ChangesetOutput{Reports: reports}, err
88+
}
89+
90+
// DeleteJobsChangeset deletes jobs.
91+
type DeleteJobsChangeset struct{}
92+
93+
func (DeleteJobsChangeset) VerifyPreconditions(env cldf.Environment, cfg DeleteJobsInput) error {
94+
if len(cfg.JobIDs) == 0 {
95+
return errors.New("no job_ids provided")
96+
}
97+
seen := make(map[string]struct{}, len(cfg.JobIDs))
98+
for _, id := range cfg.JobIDs {
99+
if id == "" {
100+
return errors.New("job id cannot be empty")
101+
}
102+
if _, ok := seen[id]; ok {
103+
return fmt.Errorf("duplicate job id %q", id)
104+
}
105+
seen[id] = struct{}{}
106+
}
107+
108+
jobs, err := env.Offchain.ListJobs(env.GetContext(), &jobv1.ListJobsRequest{
109+
Filter: &jobv1.ListJobsRequest_Filter{Ids: cfg.JobIDs, IncludeDeleted: true},
110+
})
111+
if err != nil {
112+
return fmt.Errorf("failed to list jobs: %w", err)
113+
}
114+
for _, j := range jobs.Jobs {
115+
if j.DeletedAt != nil {
116+
return fmt.Errorf("job %q is already deleted", j.Id)
117+
}
118+
}
119+
if len(jobs.Jobs) != len(cfg.JobIDs) {
120+
found := make([]string, 0, len(jobs.Jobs))
121+
for _, j := range jobs.Jobs {
122+
found = append(found, j.Id)
123+
}
124+
125+
return fmt.Errorf("not all jobs found: requested %v, found %v", cfg.JobIDs, found)
126+
}
127+
128+
return nil
129+
}
130+
131+
func (DeleteJobsChangeset) Apply(e cldf.Environment, input DeleteJobsInput) (cldf.ChangesetOutput, error) {
132+
deps := jdops.JDOpDeps{Offchain: e.Offchain, EnvName: e.Name}
133+
seqReport, err := fwops.ExecuteSequence(e.OperationsBundle, jdops.SeqJDDeleteJobs, deps, input)
134+
reports := append(seqReport.ExecutionReports, seqReport.ToGenericReport())
135+
136+
return cldf.ChangesetOutput{Reports: reports}, err
137+
}

0 commit comments

Comments
 (0)