Skip to content

Commit e6fa281

Browse files
authored
Merge pull request #6 from sygmaprotocol/mmuftic/add-env-support
feat: add ENV support for configuration
2 parents 3e99e2b + 1757493 commit e6fa281

16 files changed

+251
-154
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
.idea
88

9-
config.yml
9+
config.json
1010
config_*
1111

1212
prometheus

README.md

Lines changed: 58 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -39,47 +39,69 @@ go test -v ./...
3939
For local development and testing, you can run the application with:
4040

4141
```console
42-
DEBUG=true go run . --config config.yml
42+
DEBUG=true go run . --config config.json
4343
```
4444

45-
## Configuration
45+
Additionally, to load configuration from an environment variable, use the `--env` flag. Ensure the `GATEWAY_CONFIG` environment variable is set with the main configuration data.
4646

47-
A main YAML configuration (`config.yml`) specifies the metrics server port and multiple gateways, each with its own `.yml` configuration file:
47+
```console
48+
DEBUG=true go run . --env
49+
```
4850

49-
```yaml
50-
metrics:
51-
port: 9090 # Port for Prometheus metrics, served on /metrics and /
51+
## Configuration
5252

53-
gateways:
54-
- config-file: "config_holesky.yml"
55-
name: "Holesky gateway"
56-
- config-file: "config_sepolia.yml"
57-
name: "Sepolia gateway"
53+
The main configuration has been updated to use JSON format (`config.json`). It specifies the metrics server port and multiple gateways, each with its own JSON configuration file:
54+
55+
```json
56+
{
57+
"metrics": {
58+
"port": 9090
59+
},
60+
"port": 4000,
61+
"gateways": [
62+
{
63+
"configFile": "config_holesky.json",
64+
"name": "Holesky gateway"
65+
},
66+
{
67+
"configFile": "config_sepolia.json",
68+
"name": "Sepolia gateway"
69+
}
70+
]
71+
}
5872
```
5973

60-
Each `.yml` configuration file for the gateways can specify detailed settings for proxy behavior, health checks, and target node providers. Here is an example of what these individual gateway configuration files can contain:
61-
62-
```yaml
63-
proxy:
64-
port: "3000" # Port for RPC gateway
65-
upstreamTimeout: "1s" # When is a request considered timed out
66-
67-
healthChecks:
68-
interval: "5s" # How often to perform health checks
69-
timeout: "1s" # Timeout duration for health checks
70-
failureThreshold: 2 # Failed checks until a target is marked unhealthy
71-
successThreshold: 1 # Successes required to mark a target healthy again
72-
73-
targets: # Failover order is determined by the list order
74-
- name: "Cloudflare"
75-
connection:
76-
http:
77-
url: "https://cloudflare-eth.com"
78-
- name: "Alchemy"
79-
connection:
80-
http:
81-
url: "https://alchemy.com/rpc/<apikey>"
74+
Each JSON configuration file for the gateways can specify detailed settings for proxy behavior, health checks, and target node providers. Here is an example of what these individual gateway configuration files might contain:
75+
76+
```json
77+
{
78+
"proxy": {
79+
"port": "3000",
80+
"upstreamTimeout": "1s"
81+
},
82+
"healthChecks": {
83+
"interval": "5s",
84+
"timeout": "1s",
85+
"failureThreshold": 2,
86+
"successThreshold": 1
87+
},
88+
"targets": [
89+
{
90+
"name": "Cloudflare",
91+
"connection": {
92+
"http": {
93+
"url": "https://cloudflare-eth.com"
94+
}
95+
}
96+
},
97+
{
98+
"name": "Alchemy",
99+
"connection": {
100+
"http": {
101+
"url": "https://alchemy.com/rpc/<apikey>"
102+
}
103+
}
104+
}
105+
]
106+
}
82107
```
83-
84-
Any of these configuration files can be also loaded from URL if one is provided as path.
85-
---

example_config.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"metrics": {
3+
"port": 9090
4+
},
5+
"gateways": [
6+
{
7+
"configFile": "config_holesky.json",
8+
"name": "Holesky gateway"
9+
},
10+
{
11+
"configFile": "config_sepolia.json",
12+
"name": "Sepolia gateway"
13+
}
14+
]
15+
}

example_config.yml

Lines changed: 0 additions & 10 deletions
This file was deleted.

example_network_config.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"proxy": {
3+
"port": "3000",
4+
"upstreamTimeout": "1s"
5+
},
6+
"healthChecks": {
7+
"interval": "5s",
8+
"timeout": "1s",
9+
"failureThreshold": 2,
10+
"successThreshold": 1
11+
},
12+
"targets": [
13+
{
14+
"name": "Cloudflare",
15+
"connection": {
16+
"http": {
17+
"url": "https://cloudflare-eth.com"
18+
}
19+
}
20+
},
21+
{
22+
"name": "Alchemy",
23+
"connection": {
24+
"http": {
25+
"url": "https://alchemy.com/rpc/<apikey>"
26+
}
27+
}
28+
}
29+
]
30+
}

example_network_config.yml

Lines changed: 0 additions & 20 deletions
This file was deleted.

internal/metrics/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
package metrics
22

33
type Config struct {
4-
Port uint `yaml:"port"`
4+
Port uint `json:"port"`
55
}

internal/proxy/config.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
package proxy
22

33
import (
4-
"time"
4+
"github.com/sygmaprotocol/rpc-gateway/internal/util"
55
)
66

77
type HealthCheckConfig struct {
8-
Interval time.Duration `yaml:"interval"`
9-
Timeout time.Duration `yaml:"timeout"`
10-
FailureThreshold uint `yaml:"failureThreshold"`
11-
SuccessThreshold uint `yaml:"successThreshold"`
8+
Interval util.DurationUnmarshalled `json:"interval"`
9+
Timeout util.DurationUnmarshalled `json:"timeout"`
10+
FailureThreshold uint `json:"failureThreshold"`
11+
SuccessThreshold uint `json:"successThreshold"`
1212
}
1313

1414
type ProxyConfig struct { // nolint:revive
15-
Path string `yaml:"path"`
16-
UpstreamTimeout time.Duration `yaml:"upstreamTimeout"`
15+
Path string `json:"path"`
16+
UpstreamTimeout util.DurationUnmarshalled `json:"upstreamTimeout"`
1717
}
1818

1919
// This struct is temporary. It's about to keep the input interface clean and simple.

internal/proxy/healthchecker.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"sync"
88
"time"
99

10+
"github.com/sygmaprotocol/rpc-gateway/internal/util"
11+
1012
"github.com/ethereum/go-ethereum/common/hexutil"
1113
"github.com/ethereum/go-ethereum/rpc"
1214
)
@@ -21,16 +23,16 @@ type HealthCheckerConfig struct {
2123
Logger *slog.Logger
2224

2325
// How often to check health.
24-
Interval time.Duration `yaml:"healthcheckInterval"`
26+
Interval util.DurationUnmarshalled `json:"interval"`
2527

2628
// How long to wait for responses before failing
27-
Timeout time.Duration `yaml:"healthcheckTimeout"`
29+
Timeout util.DurationUnmarshalled `json:"timeout"`
2830

2931
// Try FailureThreshold times before marking as unhealthy
30-
FailureThreshold uint `yaml:"healthcheckInterval"`
32+
FailureThreshold uint `yaml:"failureThreshold"`
3133

3234
// Minimum consecutive successes required to mark as healthy
33-
SuccessThreshold uint `yaml:"healthcheckInterval"`
35+
SuccessThreshold uint `yaml:"successThreshold"`
3436
}
3537

3638
type HealthChecker struct {
@@ -93,6 +95,7 @@ func (h *HealthChecker) checkBlockNumber(c context.Context) (uint64, error) {
9395
// want to perform an eth_call to make sure eth_call requests are also succeding
9496
// as blockNumber can be either cached or routed to a different service on the
9597
// RPC provider's side.
98+
// nolint: unused
9699
func (h *HealthChecker) checkGasLimit(c context.Context) (uint64, error) {
97100
gasLimit, err := performGasLeftCall(c, h.httpClient, h.config.URL)
98101
if err != nil {
@@ -117,7 +120,7 @@ func (h *HealthChecker) CheckAndSetHealth() {
117120
}
118121

119122
func (h *HealthChecker) checkAndSetBlockNumberHealth() {
120-
c, cancel := context.WithTimeout(context.Background(), h.config.Timeout)
123+
c, cancel := context.WithTimeout(context.Background(), time.Duration(h.config.Timeout))
121124
defer cancel()
122125

123126
// TODO
@@ -135,8 +138,9 @@ func (h *HealthChecker) checkAndSetBlockNumberHealth() {
135138
h.blockNumber = blockNumber
136139
}
137140

141+
// nolint: unused
138142
func (h *HealthChecker) checkAndSetGasLeftHealth() {
139-
c, cancel := context.WithTimeout(context.Background(), h.config.Timeout)
143+
c, cancel := context.WithTimeout(context.Background(), time.Duration(h.config.Timeout))
140144
defer cancel()
141145

142146
gasLimit, err := h.checkGasLimit(c)
@@ -154,7 +158,7 @@ func (h *HealthChecker) checkAndSetGasLeftHealth() {
154158
func (h *HealthChecker) Start(c context.Context) {
155159
h.CheckAndSetHealth()
156160

157-
ticker := time.NewTicker(h.config.Interval)
161+
ticker := time.NewTicker(time.Duration(h.config.Interval))
158162
defer ticker.Stop()
159163

160164
for {

internal/proxy/healthchecker_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"testing"
88
"time"
99

10+
"github.com/sygmaprotocol/rpc-gateway/internal/util"
11+
1012
"github.com/caitlinelfring/go-env-default"
1113
"github.com/stretchr/testify/assert"
1214
)
@@ -18,8 +20,8 @@ func TestBasicHealthchecker(t *testing.T) {
1820

1921
healtcheckConfig := HealthCheckerConfig{
2022
URL: env.GetDefault("RPC_GATEWAY_NODE_URL_1", "https://cloudflare-eth.com"),
21-
Interval: 1 * time.Second,
22-
Timeout: 2 * time.Second,
23+
Interval: util.DurationUnmarshalled(1 * time.Second),
24+
Timeout: util.DurationUnmarshalled(2 * time.Second),
2325
FailureThreshold: 1,
2426
SuccessThreshold: 1,
2527
Logger: slog.New(slog.NewTextHandler(os.Stderr, nil)),

0 commit comments

Comments
 (0)