Skip to content

Commit 7f032af

Browse files
committed
Merge branch 'develop' into feature/PLEX-2611-cre-don2don-accept-ocr-attestatin
# Conflicts: # deployment/go.mod # integration-tests/go.mod # integration-tests/load/go.mod # system-tests/lib/go.mod
2 parents d7e09e2 + b5c8b5c commit 7f032af

96 files changed

Lines changed: 4302 additions & 891 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"chainlink": patch
3+
---
4+
5+
#internal
6+
Forward-port the Aptos mock streams trigger compatibility path onto `develop` behind explicit local capability opt-in.

.github/workflows/build-publish.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,45 @@ jobs:
239239
}
240240
JSON
241241
242+
emit-release-image-tagged-event:
243+
name: Emit release image tagged event
244+
needs: [ checks ]
245+
if: needs.checks.outputs.is-release == 'true'
246+
runs-on: ubuntu-latest
247+
permissions:
248+
contents: write
249+
steps:
250+
- name: Compute image tag
251+
id: compute-image-tag
252+
shell: bash
253+
env:
254+
GIT_TAG: ${{ github.ref_name }}
255+
run: |
256+
# Compute image tag by removing 'v' prefix
257+
# Examples:
258+
# v2.34.0 -> 2.34.0
259+
tag_without_v="${GIT_TAG#v}"
260+
echo "image-tag=$tag_without_v" | tee -a "$GITHUB_OUTPUT"
261+
- name: Emit release image tagged event
262+
env:
263+
DOCKER_IMAGE_TAG: ${{ steps.compute-image-tag.outputs.image-tag }}
264+
GITHUB_REF_NAME: ${{ github.ref_name }}
265+
GH_TOKEN: ${{ github.token }}
266+
run: |
267+
gh api \
268+
--method POST \
269+
-H "Accept: application/vnd.github+json" \
270+
"/repos/${GITHUB_REPOSITORY}/dispatches" \
271+
--input - <<JSON
272+
{
273+
"event_type": "release-image-tagged",
274+
"client_payload": {
275+
"chainlink_image_tag": "${DOCKER_IMAGE_TAG}",
276+
"chainlink_version": "${GITHUB_REF_NAME}"
277+
}
278+
}
279+
JSON
280+
242281
# Notify Slack channel for new git tags associated with pre-releases.
243282
# Final release notifications originate from the release coordinator repo.
244283
slack-notify:

.github/workflows/ci-core.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ jobs:
206206
matrix:
207207
type:
208208
- cmd: go_core_tests
209-
os: runs-on=${{ github.run_id }}-unit/cpu=48/ram=96/family=c6i/spot=false/image=ubuntu24-full-x64/extras=s3-cache+tmpfs
209+
os: runs-on=${{ github.run_id }}-unit/cpu=48/ram=96/family=c6id+c5ad/spot=false/image=ubuntu24-full-x64/extras=s3-cache
210210
should-run: ${{ needs.filter.outputs.should-run-core-tests }}
211211
trunk-auto-quarantine: "true"
212212

.github/workflows/cre-system-tests.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ jobs:
8686
8787
# Add list of tests with certain topologies
8888
PER_TEST_TOPOLOGIES_JSON=${PER_TEST_TOPOLOGIES_JSON:-'{
89+
"Test_CRE_V2_Suite_Bucket_B": [
90+
{"topology":"workflow-gateway-capabilities","configs":"configs/workflow-gateway-capabilities-don.toml"},
91+
{"topology":"workflow-gateway-capabilities-vault-jwt_auth-enabled","configs":"configs/workflow-gateway-capabilities-don-vault-jwt_auth-enabled.toml"}
92+
],
8993
"Test_CRE_V2_Aptos_Suite": [
9094
{"topology":"workflow-gateway-aptos","configs":"configs/workflow-gateway-don-aptos.toml"}
9195
],

.github/workflows/post-build-publish.yml

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: "Post Build, Sign and Publish Chainlink"
22
on:
3-
# # to be used with post release tests
3+
# # to be used with pre-release tests
44
repository_dispatch:
55
types: [ pre-release-image-published ]
66

@@ -29,6 +29,7 @@ jobs:
2929
fi
3030
echo "Trigger validated successfully"
3131
# Upgrade compatibility tests
32+
# Include rc and beta releases, since we will be upgrading from them on canary servers
3233
df1-compat:
3334
uses: ./.github/workflows/devenv-compat.yml
3435
name: Data Feeds v1 Upgrade Test
@@ -42,9 +43,6 @@ jobs:
4243
upgrade-nodes: "3"
4344
node-name-template: "don-node%d"
4445
versions-back: "3"
45-
# uncomment once we have tested this pipeline with beta/rc releases
46-
# exclude-refs: "beta,rc,ccip"
47-
# remove when ^^ is uncommented
4846
exclude-refs: "ccip"
4947
working-directory: "devenv"
5048
logs-directory: "./devenv/tests/ocr2/logs"
@@ -65,9 +63,6 @@ jobs:
6563
upgrade-nodes: "3"
6664
node-name-template: "workflow-node%d"
6765
versions-back: "3"
68-
# uncomment once we have tested this pipeline with beta/rc releases
69-
# exclude-refs: "beta,rc,ccip"
70-
# remove when ^^ is uncommented
7166
exclude-refs: "ccip"
7267
working-directory: "./system-tests/tests"
7368
logs-directory: "./system-tests/tests/smoke/cre/logs"
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
name: "Post Release Image Tagged"
2+
on:
3+
# to be used with post release tests
4+
repository_dispatch:
5+
types: [ release-image-tagged ]
6+
7+
permissions: {}
8+
9+
jobs:
10+
validate-trigger:
11+
runs-on: ubuntu-latest
12+
permissions:
13+
contents: read
14+
steps:
15+
- name: Validate trigger
16+
env:
17+
CHAINLINK_IMAGE_TAG: ${{ github.event.client_payload.chainlink_image_tag }}
18+
CHAINLINK_VERSION: ${{ github.event.client_payload.chainlink_version }}
19+
run: |
20+
echo "Image Tag: $CHAINLINK_IMAGE_TAG"
21+
echo "Chainlink Version: $CHAINLINK_VERSION"
22+
if [ -z "$CHAINLINK_IMAGE_TAG" ]; then
23+
echo "Chainlink image tag is required, but 'client_payload.chainlink_image_tag' is empty"
24+
exit 1
25+
fi
26+
if [ -z "$CHAINLINK_VERSION" ]; then
27+
echo "Chainlink Version is required, but 'client_payload.chainlink_version' is empty"
28+
exit 1
29+
fi
30+
echo "Trigger validated successfully"
31+
32+
# Upgrade compatibility tests
33+
# Run on images excluding beta and release candidate versions
34+
df1-compat:
35+
uses: ./.github/workflows/devenv-compat.yml
36+
name: Data Feeds v1 Upgrade Test
37+
permissions:
38+
id-token: write
39+
contents: read
40+
with:
41+
buildcmd: "just cli"
42+
envcmd: "cl u env.toml,products/ocr2/basic.toml"
43+
testcmd: "cl test ocr2 TestSmoke/rounds"
44+
upgrade-nodes: "3"
45+
node-name-template: "don-node%d"
46+
versions-back: "3"
47+
exclude-refs: "beta,rc,ccip"
48+
working-directory: "devenv"
49+
logs-directory: "./devenv/tests/ocr2/logs"
50+
ctf-log-level: "debug"
51+
strip-image-suffix: "v"
52+
secrets: inherit
53+
54+
cre-compat:
55+
uses: ./.github/workflows/devenv-compat.yml
56+
name: CRE Upgrade Test
57+
permissions:
58+
id-token: write
59+
contents: read
60+
with:
61+
buildcmd: "echo nothing-to-build" # added to prevent `just cli` from being used (default buildcmd if not provided)
62+
envcmd: "go run -C ../../core/scripts/cre/environment . env start"
63+
testcmd: "go test -v -run Test_Upgrade_Suite ./smoke/cre"
64+
upgrade-nodes: "3"
65+
node-name-template: "workflow-node%d"
66+
versions-back: "3"
67+
exclude-refs: "beta,rc,ccip"
68+
working-directory: "./system-tests/tests"
69+
logs-directory: "./system-tests/tests/smoke/cre/logs"
70+
ctf-log-level: "debug"
71+
strip-image-suffix: "v"
72+
secrets: inherit
73+
# Do not run other tests, since they were already run on the pre-release image

core/capabilities/fakes/manual_cron_trigger.go

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"time"
88

9+
"github.com/go-co-op/gocron/v2"
910
"google.golang.org/protobuf/types/known/timestamppb"
1011

1112
"github.com/smartcontractkit/chainlink-common/pkg/capabilities"
@@ -25,6 +26,7 @@ var _ cronserver.CronCapability = (*ManualCronTriggerService)(nil)
2526
const ServiceName = "CronTriggerService"
2627
const ID = "cron-trigger@1.0.0"
2728
const defaultFastestScheduleIntervalSeconds = 1
29+
const allowSeconds = true
2830

2931
var manualCronTriggerInfo = capabilities.MustNewCapabilityInfo(
3032
ID,
@@ -43,19 +45,28 @@ type ManualCronTriggerService struct {
4345
callbackCh map[string]chan capabilities.TriggerAndId[*crontypedapi.Payload]
4446
legacyCallbackCh chan capabilities.TriggerAndId[*crontypedapi.LegacyPayload] //nolint:staticcheck // LegacyPayload intentionally used for backward compatibility
4547
workflowIDs map[string]string // triggerID -> workflowID mapping
48+
triggerConfigs map[string]*crontypedapi.Config
49+
scheduler gocron.Scheduler
4650
}
4751

48-
func NewManualCronTriggerService(parentLggr logger.Logger) *ManualCronTriggerService {
52+
func NewManualCronTriggerService(parentLggr logger.Logger) (*ManualCronTriggerService, error) {
4953
lggr := logger.Named(parentLggr, "CronTriggerService") // ManualCronTriggerService
5054

55+
scheduler, err := gocron.NewScheduler()
56+
if err != nil {
57+
return nil, fmt.Errorf("failed to create cron scheduler: %w", err)
58+
}
59+
5160
return &ManualCronTriggerService{
5261
CapabilityInfo: manualCronTriggerInfo,
5362
config: ManualCronConfig{FastestScheduleIntervalSeconds: 1},
5463
lggr: lggr,
5564
callbackCh: make(map[string]chan capabilities.TriggerAndId[*crontypedapi.Payload]),
5665
legacyCallbackCh: make(chan capabilities.TriggerAndId[*crontypedapi.LegacyPayload]), //nolint:staticcheck // LegacyPayload intentionally used for backward compatibility
5766
workflowIDs: make(map[string]string),
58-
}
67+
triggerConfigs: make(map[string]*crontypedapi.Config),
68+
scheduler: scheduler,
69+
}, nil
5970
}
6071

6172
func (f *ManualCronTriggerService) Initialise(ctx context.Context, dependencies core.StandardCapabilitiesDependencies) error {
@@ -75,17 +86,17 @@ func (f *ManualCronTriggerService) Initialise(ctx context.Context, dependencies
7586

7687
f.config = cronConfig
7788

78-
err := f.Start(ctx)
79-
if err != nil {
89+
if err := f.Start(ctx); err != nil {
8090
return fmt.Errorf("error when starting trigger service: %w", err)
8191
}
8292

8393
return nil
8494
}
8595

8696
func (f *ManualCronTriggerService) RegisterTrigger(ctx context.Context, triggerID string, metadata capabilities.RequestMetadata, input *crontypedapi.Config) (<-chan capabilities.TriggerAndId[*crontypedapi.Payload], caperrors.Error) {
87-
f.callbackCh[triggerID] = make(chan capabilities.TriggerAndId[*crontypedapi.Payload])
97+
f.callbackCh[triggerID] = make(chan capabilities.TriggerAndId[*crontypedapi.Payload], 1)
8898
f.workflowIDs[triggerID] = metadata.WorkflowID
99+
f.triggerConfigs[triggerID] = input
89100
return f.callbackCh[triggerID], nil
90101
}
91102

@@ -105,7 +116,28 @@ func (f *ManualCronTriggerService) AckEvent(ctx context.Context, triggerID strin
105116
return nil
106117
}
107118

108-
func (f *ManualCronTriggerService) ManualTrigger(ctx context.Context, triggerID string, scheduledExecutionTime time.Time) error {
119+
func (f *ManualCronTriggerService) ManualTrigger(ctx context.Context, triggerID string, skipWait <-chan struct{}) error {
120+
config, exists := f.triggerConfigs[triggerID]
121+
if !exists {
122+
return fmt.Errorf(`trigger config "%s" not found`, triggerID)
123+
}
124+
125+
jobFired := make(chan struct{}, 1)
126+
job, err := f.scheduler.NewJob(
127+
gocron.CronJob(config.Schedule, allowSeconds),
128+
gocron.NewTask(func() {
129+
defer close(jobFired)
130+
jobFired <- struct{}{}
131+
}),
132+
)
133+
if err != nil {
134+
return fmt.Errorf("failed to create cron job: %w", err)
135+
}
136+
scheduledExecutionTime, err := job.NextRun()
137+
if err != nil {
138+
return fmt.Errorf("failed to get next scheduled execution time: %w", err)
139+
}
140+
109141
f.lggr.Debugf("ManualTrigger: %s", scheduledExecutionTime.Format(time.RFC3339Nano))
110142

111143
triggerEvent := f.createManualTriggerEvent(scheduledExecutionTime)
@@ -128,16 +160,22 @@ func (f *ManualCronTriggerService) ManualTrigger(ctx context.Context, triggerID
128160
f.lggr.Errorw("failed to emit trigger execution started event", "err", err)
129161
}
130162

131-
go func() {
132-
select {
133-
case f.callbackCh[triggerID] <- triggerEvent:
134-
// Successfully sent trigger response
135-
case <-ctx.Done():
136-
// Context cancelled, cleanup goroutine
137-
f.lggr.Debug("ManualTrigger goroutine cancelled due to context cancellation")
138-
}
163+
defer func() {
164+
_ = f.scheduler.RemoveJob(job.ID())
139165
}()
140166

167+
// Either wait for cron scheduler or skip wait signal
168+
select {
169+
case <-skipWait:
170+
break
171+
case <-jobFired:
172+
break
173+
case <-ctx.Done():
174+
return ctx.Err()
175+
}
176+
177+
// Sent trigger response
178+
f.callbackCh[triggerID] <- triggerEvent
141179
return nil
142180
}
143181

@@ -161,11 +199,15 @@ func (f *ManualCronTriggerService) createManualTriggerEvent(scheduledExecutionTi
161199

162200
func (f *ManualCronTriggerService) Start(ctx context.Context) error {
163201
f.lggr.Debugw("Starting ManualCronTriggerService")
202+
f.scheduler.Start()
164203
return nil
165204
}
166205

167206
func (f *ManualCronTriggerService) Close() error {
168207
f.lggr.Debug("Closing ManualCronTriggerService")
208+
if err := f.scheduler.Shutdown(); err != nil {
209+
f.lggr.Errorw("failed to close scheduler", "err", err)
210+
}
169211
return nil
170212
}
171213

core/capabilities/registry.go

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -83,25 +83,36 @@ func NewRegistry(lggr logger.Logger) *Registry {
8383
// interface. It is used when ExternalCapabilitiesRegistry is not available.
8484
type TestMetadataRegistry struct {
8585
core.UnimplementedCapabilitiesRegistryMetadata
86+
// WorkflowDONF allows local CRE to override the synthetic workflow DON fault
87+
// tolerance for compatibility paths that still expect a multi-signer shape.
88+
WorkflowDONF uint8
8689
}
8790

91+
const (
92+
testWorkflowDONID = 1
93+
testWorkflowDONConfigVersion = 1
94+
)
95+
8896
func (t *TestMetadataRegistry) LocalNode(ctx context.Context) (capabilities.Node, error) {
8997
peerID := p2ptypes.PeerID{}
90-
workflowDON := capabilities.DON{
91-
ID: 1,
92-
ConfigVersion: 1,
98+
return capabilities.Node{
99+
PeerID: &peerID,
100+
WorkflowDON: newTestWorkflowDON(peerID, t.WorkflowDONF),
101+
CapabilityDONs: []capabilities.DON{},
102+
}, nil
103+
}
104+
105+
func newTestWorkflowDON(peerID p2ptypes.PeerID, faultTolerance uint8) capabilities.DON {
106+
return capabilities.DON{
107+
ID: testWorkflowDONID,
108+
ConfigVersion: testWorkflowDONConfigVersion,
93109
Members: []p2ptypes.PeerID{
94110
peerID,
95111
},
96-
F: 0,
112+
F: faultTolerance,
97113
IsPublic: false,
98114
AcceptsWorkflows: true,
99115
}
100-
return capabilities.Node{
101-
PeerID: &peerID,
102-
WorkflowDON: workflowDON,
103-
CapabilityDONs: []capabilities.DON{},
104-
}, nil
105116
}
106117

107118
func (t *TestMetadataRegistry) NodeByPeerID(ctx context.Context, _ p2ptypes.PeerID) (capabilities.Node, error) {

0 commit comments

Comments
 (0)