@@ -4,8 +4,12 @@ package ngrok
4
4
import (
5
5
"context"
6
6
"fmt"
7
+ "os"
8
+ "path/filepath"
9
+ "strings"
7
10
8
11
"golang.ngrok.com/ngrok/v2"
12
+ "gopkg.in/yaml.v2"
9
13
10
14
"github.com/stacklok/toolhive/pkg/logger"
11
15
)
@@ -17,31 +21,67 @@ type TunnelProvider struct {
17
21
18
22
// TunnelConfig holds configuration options for the ngrok tunnel provider.
19
23
type TunnelConfig struct {
20
- AuthToken string
21
- Domain string // Optional: specify custom domain
22
- DryRun bool
24
+ AuthToken string
25
+ URL string // Optional: specify custom URL
26
+ TrafficPolicy string // Optional: specify traffic policy
27
+ PoolingEnabled bool // Optional: enable pooling
28
+ DryRun bool
29
+ }
30
+
31
+ // loadTrafficPolicyFile reads a YAML file, ensures it's .yml/.yaml,
32
+ // validates its contents, and returns its text.
33
+ func loadTrafficPolicyFile (path string ) (string , error ) {
34
+ ext := strings .ToLower (filepath .Ext (path ))
35
+ if ext != ".yml" && ext != ".yaml" {
36
+ return "" , fmt .Errorf ("traffic policy file must be .yml or .yaml, got %q" , ext )
37
+ }
38
+
39
+ cleanPath := filepath .Clean (path )
40
+ b , err := os .ReadFile (cleanPath )
41
+ if err != nil {
42
+ return "" , fmt .Errorf ("reading traffic policy file: %w" , err )
43
+ }
44
+
45
+ var tmp any
46
+ if err := yaml .Unmarshal (b , & tmp ); err != nil {
47
+ return "" , fmt .Errorf ("invalid YAML in traffic policy file: %w" , err )
48
+ }
49
+
50
+ return string (b ), nil
23
51
}
24
52
25
53
// ParseConfig parses the configuration for the ngrok tunnel provider from a map.
26
54
func (p * TunnelProvider ) ParseConfig (raw map [string ]any ) error {
27
- token , ok := raw ["ngrok- auth-token" ].(string )
55
+ token , ok := raw ["auth-token" ].(string )
28
56
if ! ok || token == "" {
29
- return fmt .Errorf ("ngrok- auth-token is required" )
57
+ return fmt .Errorf ("auth-token is required" )
30
58
}
31
59
32
60
cfg := TunnelConfig {
33
61
AuthToken : token ,
34
62
}
35
63
36
- if domain , ok := raw ["ngrok-domain" ].(string ); ok {
37
- cfg .Domain = domain
64
+ // optional settings: url, traffic policy, pooling
65
+ if url , ok := raw ["url" ].(string ); ok {
66
+ cfg .URL = url
67
+ }
68
+ if path , ok := raw ["traffic-policy-file" ].(string ); ok && path != "" {
69
+ policyText , err := loadTrafficPolicyFile (path )
70
+ if err != nil {
71
+ return err
72
+ }
73
+ cfg .TrafficPolicy = policyText
74
+ }
75
+ if pooling , ok := raw ["pooling" ].(bool ); ok {
76
+ cfg .PoolingEnabled = pooling
38
77
}
39
78
40
79
p .config = cfg
41
80
42
81
if dr , ok := raw ["dry-run" ].(bool ); ok {
43
82
p .config .DryRun = dr
44
83
}
84
+
45
85
return nil
46
86
}
47
87
@@ -69,8 +109,14 @@ func (p *TunnelProvider) StartTunnel(ctx context.Context, name, targetURI string
69
109
endpointOpts := []ngrok.EndpointOption {
70
110
ngrok .WithDescription ("tunnel proxy for " + name ),
71
111
}
72
- if p .config .Domain != "" {
73
- endpointOpts = append (endpointOpts , ngrok .WithURL (p .config .Domain ))
112
+ if p .config .URL != "" {
113
+ endpointOpts = append (endpointOpts , ngrok .WithURL (p .config .URL ))
114
+ }
115
+ if p .config .TrafficPolicy != "" {
116
+ endpointOpts = append (endpointOpts , ngrok .WithTrafficPolicy (p .config .TrafficPolicy ))
117
+ }
118
+ if p .config .PoolingEnabled {
119
+ endpointOpts = append (endpointOpts , ngrok .WithPoolingEnabled (true ))
74
120
}
75
121
76
122
forwarder , err := agent .Forward (ctx ,
0 commit comments