-
Notifications
You must be signed in to change notification settings - Fork 27
Resource Managers: durable metering for the CRE and other products #2158
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a081b4f
9e647b7
e3e374b
2823ae9
8205ca4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -85,6 +85,12 @@ const ( | |
| envTelemetryPrometheusBridgeEnabled = "CL_TELEMETRY_PROMETHEUS_BRIDGE_ENABLED" | ||
| envTelemetryPrometheusBridgePrefixes = "CL_TELEMETRY_PROMETHEUS_BRIDGE_PREFIXES" | ||
| envTelemetryLogCompressor = "CL_TELEMETRY_LOG_COMPRESSOR" | ||
| envMeterRecordsEnabled = "CL_METER_RECORDS_ENABLED" | ||
| envMeterSnapshotsEnabled = "CL_METER_SNAPSHOTS_ENABLED" | ||
| envMeteringProduct = "CL_METERING_PRODUCT" | ||
| envMeteringEnvironment = "CL_METERING_ENVIRONMENT" | ||
| envMeteringZone = "CL_METERING_ZONE" | ||
| envMeteringNodeID = "CL_METERING_NODE_ID" | ||
|
|
||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note to reader, DonID isn't here b/c a node can belong to many DONs; it is the responsibility of the loop to properly receive its DonID from |
||
| envChipIngressEndpoint = "CL_CHIP_INGRESS_ENDPOINT" | ||
| envChipIngressInsecureConnection = "CL_CHIP_INGRESS_INSECURE_CONNECTION" | ||
|
|
@@ -171,6 +177,24 @@ type EnvConfig struct { | |
| TelemetryPrometheusBridgeEnabled bool | ||
| TelemetryPrometheusBridgePrefixes []string | ||
| TelemetryLogCompressor string | ||
| MeterRecordsEnabled bool | ||
| MeterSnapshotsEnabled bool | ||
|
|
||
| // MeteringProduct / MeteringEnvironment / MeteringZone / MeteringNodeID are | ||
| // the static deployment+node identity dimensions used as coarse | ||
| // metering/billing rollup dimensions. They are resolved once from node | ||
| // config by the host and delivered to every LOOP plugin over the env, the | ||
| // same channel as the metering toggles above (rather than the | ||
| // standard-capabilities boundary). Any may be empty if the host did not | ||
| // provide it. | ||
| // | ||
| // MeteringNodeID is the node's logical name (e.g. "clp-cre-wf-zone-a-1"), | ||
| // not the CSA public key; the CSA key rides emitted events separately as the | ||
| // node_csa_key attribute. | ||
| MeteringProduct string | ||
| MeteringEnvironment string | ||
| MeteringZone string | ||
| MeteringNodeID string | ||
|
|
||
| TracingEnabled bool | ||
| TracingCollectorTarget string | ||
|
|
@@ -264,6 +288,12 @@ func (e *EnvConfig) AsCmdEnv() (env []string) { | |
| add(envTelemetryPrometheusBridgeEnabled, strconv.FormatBool(e.TelemetryPrometheusBridgeEnabled)) | ||
| add(envTelemetryPrometheusBridgePrefixes, strings.Join(e.TelemetryPrometheusBridgePrefixes, ",")) | ||
| add(envTelemetryLogCompressor, e.TelemetryLogCompressor) | ||
| add(envMeterRecordsEnabled, strconv.FormatBool(e.MeterRecordsEnabled)) | ||
| add(envMeterSnapshotsEnabled, strconv.FormatBool(e.MeterSnapshotsEnabled)) | ||
| add(envMeteringProduct, e.MeteringProduct) | ||
| add(envMeteringEnvironment, e.MeteringEnvironment) | ||
| add(envMeteringZone, e.MeteringZone) | ||
| add(envMeteringNodeID, e.MeteringNodeID) | ||
|
|
||
| add(envChipIngressEndpoint, e.ChipIngressEndpoint) | ||
| add(envChipIngressInsecureConnection, strconv.FormatBool(e.ChipIngressInsecureConnection)) | ||
|
|
@@ -518,6 +548,20 @@ func (e *EnvConfig) parse() error { | |
| e.CRESettings = os.Getenv(envCRESettings) | ||
| e.CRESettingsDefault = os.Getenv(envCRESettingsDefault) | ||
|
|
||
| e.MeterRecordsEnabled, err = getBool(envMeterRecordsEnabled) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to parse %s: %w", envMeterRecordsEnabled, err) | ||
| } | ||
| e.MeterSnapshotsEnabled, err = getBool(envMeterSnapshotsEnabled) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to parse %s: %w", envMeterSnapshotsEnabled, err) | ||
| } | ||
|
|
||
| e.MeteringProduct = os.Getenv(envMeteringProduct) | ||
| e.MeteringEnvironment = os.Getenv(envMeteringEnvironment) | ||
| e.MeteringZone = os.Getenv(envMeteringZone) | ||
| e.MeteringNodeID = os.Getenv(envMeteringNodeID) | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| package resourcemanager | ||
|
|
||
| import ( | ||
| "math/big" | ||
| "strconv" | ||
|
|
||
| meteringpb "github.com/smartcontractkit/chainlink-protos/metering/go" | ||
| ) | ||
|
|
||
| // UtilizationFields identifies one billed utilization dimension. | ||
| type UtilizationFields struct { | ||
| ResourceType string | ||
| ResourceID string | ||
| EventID string | ||
| OrgID string | ||
| } | ||
|
|
||
| // NewUtilization builds a Utilization with int64 quantity encoded as a decimal | ||
| // string value. | ||
| func NewUtilization(value int64, fields UtilizationFields) *meteringpb.Utilization { | ||
| return NewUtilizationString(strconv.FormatInt(value, 10), fields) | ||
| } | ||
|
|
||
| // NewUtilizationString builds a Utilization from a pre-formatted numeric string | ||
| // value. | ||
| func NewUtilizationString(value string, fields UtilizationFields) *meteringpb.Utilization { | ||
| return &meteringpb.Utilization{ | ||
| Value: value, | ||
| ResourceType: fields.ResourceType, | ||
| ResourceId: fields.ResourceID, | ||
| EventId: fields.EventID, | ||
| OrgId: fields.OrgID, | ||
| } | ||
| } | ||
|
|
||
| // NewUtilizationBig builds a Utilization from an arbitrary-precision integer. | ||
| func NewUtilizationBig(value *big.Int, fields UtilizationFields) *meteringpb.Utilization { | ||
| if value == nil { | ||
| return NewUtilizationString("0", fields) | ||
| } | ||
| return NewUtilizationString(value.String(), fields) | ||
| } | ||
|
|
||
| // NewUtilizationFloat builds a Utilization from a floating-point value. | ||
| func NewUtilizationFloat(value float64, fields UtilizationFields) *meteringpb.Utilization { | ||
| return NewUtilizationString(strconv.FormatFloat(value, 'g', -1, 64), fields) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package resourcemanager | ||
|
|
||
| import "strings" | ||
|
|
||
| // OwnerLabel returns the canonical form of the "owner" label, used by all | ||
| // producers: a leading "0x"/"0X" prefix is stripped and the remainder is | ||
| // lowercased. Downstream joins on the owner label depend on every producer | ||
| // emitting exactly this form, so producers must not write the owner label | ||
| // any other way. | ||
| func OwnerLabel(owner string) string { | ||
| if strings.HasPrefix(owner, "0x") || strings.HasPrefix(owner, "0X") { | ||
| owner = owner[2:] | ||
| } | ||
| return strings.ToLower(owner) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| package resourcemanager | ||
|
|
||
| import ( | ||
| "testing" | ||
|
|
||
| "github.com/stretchr/testify/assert" | ||
| ) | ||
|
|
||
| func TestOwnerLabel(t *testing.T) { | ||
| tests := []struct { | ||
| name string | ||
| owner string | ||
| want string | ||
| }{ | ||
| {name: "lowercase 0x prefix stripped", owner: "0xabc123def", want: "abc123def"}, | ||
| {name: "uppercase 0X prefix stripped", owner: "0Xabc123def", want: "abc123def"}, | ||
| {name: "no prefix unchanged", owner: "abc123def", want: "abc123def"}, | ||
| {name: "mixed case lowercased", owner: "0xAbC123dEF", want: "abc123def"}, | ||
| {name: "mixed case without prefix lowercased", owner: "AbC123dEF", want: "abc123def"}, | ||
| {name: "empty string", owner: "", want: ""}, | ||
| {name: "prefix only", owner: "0x", want: ""}, | ||
| {name: "only one prefix stripped", owner: "0x0Xabc", want: "0xabc"}, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| assert.Equal(t, tt.want, OwnerLabel(tt.owner)) | ||
| }) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note to reader: changes made here instead of pkg/beholder as we are trying to leave beholder as is without accruing much more complexity, and durableemitter pkg is completely owned by foundations, so is a cleaner division of responsibility
cc @pkcll