@@ -21,6 +21,7 @@ import (
21
21
"google.golang.org/grpc/credentials"
22
22
"crypto/tls"
23
23
"sort"
24
+ "github.com/Semior001/groxy/pkg/grpcx"
24
25
)
25
26
26
27
// File discovers the changes in routing rules from a file.
@@ -90,8 +91,8 @@ func (d *File) Events(ctx context.Context) <-chan string {
90
91
return res
91
92
}
92
93
93
- // Rules parses the file and returns the routing rules from it .
94
- func (d * File ) Rules ( context.Context ) ([] * discovery.Rule , error ) {
94
+ // State parses the file and returns the current state of the provider .
95
+ func (d * File ) State ( ctx context.Context ) (* discovery.State , error ) {
95
96
f , err := os .Open (d .FileName )
96
97
if err != nil {
97
98
return nil , fmt .Errorf ("open file: %w" , err )
@@ -100,70 +101,36 @@ func (d *File) Rules(context.Context) ([]*discovery.Rule, error) {
100
101
defer f .Close ()
101
102
102
103
var cfg Config
103
- if err = yaml .NewDecoder (f ).Decode (& cfg ); err != nil {
104
+ if err : = yaml .NewDecoder (f ).Decode (& cfg ); err != nil {
104
105
return nil , fmt .Errorf ("decode file: %w" , err )
105
106
}
106
107
107
108
if cfg .Version != "1" {
108
109
return nil , fmt .Errorf ("unsupported version: %s" , cfg .Version )
109
110
}
110
111
111
- parseRespond := func (r Respond ) (result * discovery.Mock , err error ) {
112
- result = & discovery.Mock {}
113
-
114
- if r .Metadata != nil {
115
- result .Header = metadata .New (r .Metadata .Header )
116
- result .Trailer = metadata .New (r .Metadata .Trailer )
117
- }
118
-
119
- switch {
120
- case r .Status != nil && r .Body != nil :
121
- return nil , fmt .Errorf ("can't set both status and body in rule" )
122
- case r .Status != nil :
123
- var code codes.Code
124
- if err = code .UnmarshalJSON ([]byte (fmt .Sprintf ("%q" , r .Status .Code ))); err != nil {
125
- return nil , fmt .Errorf ("unmarshal status code: %w" , err )
126
- }
127
- result .Status = status .New (code , r .Status .Message )
128
- case r .Body != nil :
129
- if result .Body , err = protodef .BuildMessage (* r .Body ); err != nil {
130
- return nil , fmt .Errorf ("build respond message: %w" , err )
131
- }
132
- default :
133
- return nil , fmt .Errorf ("empty response in rule" )
134
- }
135
-
136
- return result , nil
112
+ upstreams , err := d .upstreams (ctx , cfg )
113
+ if err != nil {
114
+ return nil , fmt .Errorf ("get upstreams: %w" , err )
137
115
}
138
116
139
- parseRule := func (r Rule ) (result discovery.Rule , err error ) {
140
- if r .Match .URI == "" {
141
- return discovery.Rule {}, fmt .Errorf ("empty URI in rule" )
142
- }
143
-
144
- result .Name = r .Match .URI
145
- if result .Match .URI , err = regexp .Compile (r .Match .URI ); err != nil {
146
- return discovery.Rule {}, fmt .Errorf ("compile URI regexp: %w" , err )
147
- }
148
-
149
- result .Match .IncomingMetadata = metadata .New (r .Match .Header )
150
-
151
- if r .Match .Body != nil {
152
- if result .Match .Message , err = protodef .BuildMessage (* r .Match .Body ); err != nil {
153
- return discovery.Rule {}, fmt .Errorf ("build request matcher message: %w" , err )
154
- }
155
- }
156
-
157
- if result .Mock , err = parseRespond (r .Respond ); err != nil {
158
- return discovery.Rule {}, fmt .Errorf ("parse respond: %w" , err )
159
- }
160
-
161
- return result , nil
117
+ rules , err := d .rules (cfg , upstreams )
118
+ if err != nil {
119
+ return nil , fmt .Errorf ("get rules: %w" , err )
162
120
}
163
121
122
+ return & discovery.State {
123
+ Name : d .Name (),
124
+ Rules : rules ,
125
+ Upstreams : upstreams ,
126
+ }, nil
127
+ }
128
+
129
+ // Rules parses the file and returns the routing rules from it.
130
+ func (d * File ) rules (cfg Config , upstreams []discovery.Upstream ) ([]* discovery.Rule , error ) {
164
131
rules := make ([]* discovery.Rule , 0 , len (cfg .Rules )+ 1 )
165
132
for idx , r := range cfg .Rules {
166
- rule , err := parseRule (r )
133
+ rule , err := parseRule (r , upstreams )
167
134
if err != nil {
168
135
return nil , fmt .Errorf ("parse rule #%d: %w" , idx , err )
169
136
}
@@ -172,37 +139,22 @@ func (d *File) Rules(context.Context) ([]*discovery.Rule, error) {
172
139
}
173
140
174
141
if cfg .NotMatched != nil {
142
+ mock , err := parseRespond (cfg .NotMatched )
143
+ if err != nil {
144
+ return nil , fmt .Errorf ("parse respond: %w" , err )
145
+ }
175
146
rule := discovery.Rule {
176
147
Name : "not matched" ,
177
148
Match : discovery.RequestMatcher {URI : regexp .MustCompile (".*" )},
178
- }
179
- if rule .Mock , err = parseRespond (* cfg .NotMatched ); err != nil {
180
- return nil , fmt .Errorf ("parse respond: %w" , err )
149
+ Mock : mock ,
181
150
}
182
151
rules = append (rules , & rule )
183
152
}
184
153
185
154
return rules , nil
186
155
}
187
156
188
- // Upstreams parses the file and returns the upstreams from it.
189
- func (d * File ) Upstreams (ctx context.Context ) ([]discovery.Upstream , error ) {
190
- f , err := os .Open (d .FileName )
191
- if err != nil {
192
- return nil , fmt .Errorf ("open file: %w" , err )
193
- }
194
-
195
- defer f .Close ()
196
-
197
- var cfg Config
198
- if err = yaml .NewDecoder (f ).Decode (& cfg ); err != nil {
199
- return nil , fmt .Errorf ("decode file: %w" , err )
200
- }
201
-
202
- if cfg .Version != "1" {
203
- return nil , fmt .Errorf ("unsupported version: %s" , cfg .Version )
204
- }
205
-
157
+ func (d * File ) upstreams (ctx context.Context , cfg Config ) ([]discovery.Upstream , error ) {
206
158
res := make ([]discovery.Upstream , 0 , len (cfg .Upstreams ))
207
159
for name , u := range cfg .Upstreams {
208
160
cred := insecure .NewCredentials ()
@@ -219,12 +171,15 @@ func (d *File) Upstreams(ctx context.Context) ([]discovery.Upstream, error) {
219
171
slog .String ("address" , u .Addr ),
220
172
slog .Bool ("tls" , u .TLS ))
221
173
222
- cc , err := grpc .DialContext (ctx , u .Addr , grpc .WithTransportCredentials (cred ))
174
+ cc , err := grpc .DialContext (ctx , u .Addr ,
175
+ grpc .WithTransportCredentials (cred ),
176
+ grpc .WithStreamInterceptor (grpcx .ClientLogInterceptor (slog .Default ())),
177
+ )
223
178
if err != nil {
224
179
return nil , fmt .Errorf ("dial upstream %q: %w" , name , err )
225
180
}
226
181
227
- res = append (res , discovery.NamedClosableClientConn {
182
+ res = append (res , discovery.ClientConn {
228
183
ConnName : name ,
229
184
ServeReflection : u .ServeReflection ,
230
185
ClientConn : cc ,
@@ -253,3 +208,74 @@ func (d *File) getModifTime(ctx context.Context) (modif time.Time, ok bool) {
253
208
254
209
return fi .ModTime (), true
255
210
}
211
+
212
+ func parseRule (r Rule , upstreams []discovery.Upstream ) (result discovery.Rule , err error ) {
213
+ if r .Match .URI == "" {
214
+ return discovery.Rule {}, fmt .Errorf ("empty URI in rule" )
215
+ }
216
+
217
+ result .Name = r .Match .URI
218
+ if result .Match .URI , err = regexp .Compile (r .Match .URI ); err != nil {
219
+ return discovery.Rule {}, fmt .Errorf ("compile URI regexp: %w" , err )
220
+ }
221
+
222
+ result .Match .IncomingMetadata = metadata .New (r .Match .Header )
223
+
224
+ if r .Match .Body != nil {
225
+ if result .Match .Message , err = protodef .BuildMessage (* r .Match .Body ); err != nil {
226
+ return discovery.Rule {}, fmt .Errorf ("build request matcher message: %w" , err )
227
+ }
228
+ }
229
+
230
+ if r .Forward != nil {
231
+ result .Forward = & discovery.Forward {Header : metadata .New (r .Forward .Header )}
232
+ for _ , up := range upstreams {
233
+ if up .Name () == r .Forward .Upstream {
234
+ result .Forward .Upstream = up
235
+ break
236
+ }
237
+ }
238
+ }
239
+
240
+ if result .Mock , err = parseRespond (r .Respond ); err != nil {
241
+ return discovery.Rule {}, fmt .Errorf ("parse respond: %w" , err )
242
+ }
243
+
244
+ if result .Mock != nil && result .Forward != nil {
245
+ return discovery.Rule {}, fmt .Errorf ("can't set both mock and forward in rule" )
246
+ }
247
+
248
+ return result , nil
249
+ }
250
+
251
+ func parseRespond (r * Respond ) (result * discovery.Mock , err error ) {
252
+ if r == nil {
253
+ return nil , nil
254
+ }
255
+
256
+ result = & discovery.Mock {}
257
+
258
+ if r .Metadata != nil {
259
+ result .Header = metadata .New (r .Metadata .Header )
260
+ result .Trailer = metadata .New (r .Metadata .Trailer )
261
+ }
262
+
263
+ switch {
264
+ case r .Status != nil && r .Body != nil :
265
+ return nil , fmt .Errorf ("can't set both status and body in rule" )
266
+ case r .Status != nil :
267
+ var code codes.Code
268
+ if err = code .UnmarshalJSON ([]byte (fmt .Sprintf ("%q" , r .Status .Code ))); err != nil {
269
+ return nil , fmt .Errorf ("unmarshal status code: %w" , err )
270
+ }
271
+ result .Status = status .New (code , r .Status .Message )
272
+ case r .Body != nil :
273
+ if result .Body , err = protodef .BuildMessage (* r .Body ); err != nil {
274
+ return nil , fmt .Errorf ("build respond message: %w" , err )
275
+ }
276
+ default :
277
+ return nil , fmt .Errorf ("empty response in rule" )
278
+ }
279
+
280
+ return result , nil
281
+ }
0 commit comments