Skip to content

Commit c92cc92

Browse files
authored
DataStreams: Fix LLO job collision (#17611)
* Allow one LLO job per don, not one LLO job in total. * The bootstrap LLO job should have an informative name. * Ensure DonIdentifier() produces results compatible with JD labels. * Fix test.
1 parent 04802cd commit c92cc92

File tree

6 files changed

+88
-5
lines changed

6 files changed

+88
-5
lines changed

deployment/data-streams/changeset/jd_distribute_llo_jobs.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ func generateBootstrapProposals(ctx context.Context, e deployment.Environment, c
116116
Value: pointer.To(devenv.LabelNodeTypeValueBootstrap),
117117
Op: ptypes.SelectorOp_EQ,
118118
},
119+
{
120+
Key: utils.DonIdentifier(cfg.Filter.DONID, cfg.Filter.DONName),
121+
Op: ptypes.SelectorOp_EXIST,
122+
},
119123
})
120124
if err != nil {
121125
return nil, fmt.Errorf("failed to get externalJobID: %w", err)
@@ -124,6 +128,7 @@ func generateBootstrapProposals(ctx context.Context, e deployment.Environment, c
124128
bootstrapSpec := jobs.NewBootstrapSpec(
125129
cfg.ConfiguratorAddress,
126130
cfg.Filter.DONID,
131+
cfg.Filter.DONName,
127132
jobs.RelayTypeEVM,
128133
jobs.RelayConfig{
129134
ChainID: chainID,
@@ -211,6 +216,10 @@ func generateOracleProposals(ctx context.Context, e deployment.Environment, cfg
211216
Value: pointer.To(devenv.LabelNodeTypeValuePlugin),
212217
Op: ptypes.SelectorOp_EQ,
213218
},
219+
{
220+
Key: utils.DonIdentifier(cfg.Filter.DONID, cfg.Filter.DONName),
221+
Op: ptypes.SelectorOp_EXIST,
222+
},
214223
})
215224
if err != nil {
216225
return nil, fmt.Errorf("failed to get externalJobID: %w", err)

deployment/data-streams/changeset/jd_distribute_llo_jobs_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ donID = 1
103103
servers = {'mercury-pipeline-testnet-producer.TEST.cldev.cloud:1340' = '0000005187b1498c0ccb2e56d5ee8040a03a4955822ed208749b474058fc3f9c'}
104104
`
105105

106-
bootstrapSpec := `name = 'bootstrap'
106+
bootstrapSpec := `name = 'don | 1'
107107
type = 'bootstrap'
108108
schemaVersion = 1
109109
contractID = '0x4170ed0880ac9a755fd29b2688956bd959f923f4'

deployment/data-streams/jobs/bootstrap.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package jobs
22

33
import (
4+
"fmt"
5+
46
"github.com/google/uuid"
57
"github.com/pelletier/go-toml/v2"
68
)
@@ -37,13 +39,13 @@ type RelayConfig struct {
3739
FromBlock uint64 `toml:"fromBlock,omitempty"`
3840
}
3941

40-
func NewBootstrapSpec(contractID string, donID uint64, relay RelayType, relayConfig RelayConfig, externalJobID uuid.UUID) *BootstrapSpec {
42+
func NewBootstrapSpec(contractID string, donID uint64, donName string, relay RelayType, relayConfig RelayConfig, externalJobID uuid.UUID) *BootstrapSpec {
4143
if externalJobID == uuid.Nil {
4244
externalJobID = uuid.New()
4345
}
4446
return &BootstrapSpec{
4547
Base: Base{
46-
Name: "bootstrap",
48+
Name: fmt.Sprintf("%s | %d", donName, donID),
4749
Type: JobSpecTypeBootstrap,
4850
SchemaVersion: 1,
4951
ExternalJobID: externalJobID,

deployment/data-streams/jobs/bootstrap_test.go

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,63 @@ chainID = '42161'
1919
fromBlock = 283806260
2020
`
2121

22-
func Test_Bootstrap(t *testing.T) {
22+
func TestNewBootstrapSpec(t *testing.T) {
23+
t.Parallel()
24+
25+
externalJobID := uuid.New()
26+
27+
tests := []struct {
28+
name string
29+
contractID string
30+
donID uint64
31+
donName string
32+
relay RelayType
33+
relayConfig RelayConfig
34+
externalJobID uuid.UUID
35+
want *BootstrapSpec
36+
}{
37+
{
38+
name: "success",
39+
contractID: "0x01",
40+
donID: 123,
41+
donName: "don-123",
42+
relay: RelayTypeEVM,
43+
relayConfig: RelayConfig{
44+
ChainID: "234",
45+
FromBlock: 345,
46+
},
47+
externalJobID: externalJobID,
48+
want: &BootstrapSpec{
49+
Base: Base{
50+
Name: "don-123 | 123",
51+
Type: JobSpecTypeBootstrap,
52+
SchemaVersion: 1,
53+
ExternalJobID: externalJobID,
54+
},
55+
ContractID: "0x01",
56+
DonID: 123,
57+
Relay: RelayTypeEVM,
58+
RelayConfig: RelayConfig{
59+
ChainID: "234",
60+
FromBlock: 345,
61+
}},
62+
},
63+
}
64+
65+
for _, tc := range tests {
66+
t.Run(tc.name, func(t *testing.T) {
67+
got := NewBootstrapSpec(tc.contractID, tc.donID, tc.donName, tc.relay, tc.relayConfig, tc.externalJobID)
68+
if got == nil {
69+
t.Fatal("got nil")
70+
}
71+
if *got != *tc.want {
72+
t.Errorf("got %v, want %v", got, tc.want)
73+
}
74+
})
75+
}
76+
}
77+
78+
func TestMarshalTOML(t *testing.T) {
2379
t.Parallel()
2480

2581
bootstrapSpec := BootstrapSpec{

deployment/data-streams/utils/identifiers.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@ package utils
22

33
import (
44
"fmt"
5+
"regexp"
56
)
67

78
const (
89
ProductLabel = "data-streams"
910
)
1011

1112
// DonIdentifier generates a unique identifier for a DON based on its ID and name.
13+
// All non-alphanumeric characters are replaced with underscores due to the limiting requirements of
14+
// Job Distributor label keys.
1215
func DonIdentifier(donID uint64, donName string) string {
13-
return fmt.Sprintf("don-%d-%s", donID, donName)
16+
cleanDONName := regexp.MustCompile(`[^a-zA-Z0-9]+`).ReplaceAllString(donName, "_")
17+
return fmt.Sprintf("don-%d-%s", donID, cleanDONName)
1418
}

deployment/environment/devenv/don.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,18 @@ import (
2323
"github.com/smartcontractkit/chainlink-protos/job-distributor/v1/shared/ptypes"
2424
)
2525

26+
// All label keys:
27+
// * must be non-empty,
28+
// * must be 63 characters or less,
29+
// * must begin and end with an alphanumeric character ([a-z0-9A-Z]),
30+
// * could contain dashes (-), underscores (_), dots (.), and alphanumerics between.
31+
//
32+
// All label values:
33+
// * must be 63 characters or less (can be empty),
34+
// * unless empty, must begin and end with an alphanumeric character ([a-z0-9A-Z]),
35+
// * could contain dashes (-), underscores (_), dots (.), and alphanumerics between.
36+
//
37+
// Source: https://github.com/smartcontractkit/job-distributor/blob/main/pkg/entities/labels.go
2638
const (
2739
LabelNodeTypeKey = "type"
2840
LabelNodeTypeValueBootstrap = "bootstrap"

0 commit comments

Comments
 (0)