@@ -16,11 +16,15 @@ limitations under the License.
16
16
package k8splugin
17
17
18
18
import (
19
+ "bufio"
19
20
"crypto/tls"
20
21
"crypto/x509"
21
22
"encoding/json"
22
23
"fmt"
23
24
log "github.com/Sirupsen/logrus"
25
+ "github.com/contiv/ofnet"
26
+ "golang.org/x/net/context"
27
+ "golang.org/x/net/context/ctxhttp"
24
28
"io/ioutil"
25
29
"net/http"
26
30
)
@@ -29,11 +33,42 @@ const (
29
33
nsURL = "/api/v1/namespaces/"
30
34
)
31
35
32
- // APIClient defines informatio needed for the k8s api client
36
+ // APIClient defines information needed for the k8s api client
33
37
type APIClient struct {
34
- baseURL string
35
- client * http.Client
36
- podCache podInfo
38
+ baseURL string
39
+ watchBase string
40
+ client * http.Client
41
+ podCache podInfo
42
+ }
43
+
44
+ // SvcWatchResp is the response to a service watch
45
+ type SvcWatchResp struct {
46
+ opcode string
47
+ errStr string
48
+ svcName string
49
+ svcSpec ofnet.ServiceSpec
50
+ }
51
+
52
+ // EpWatchResp is the response to service endpoints watch
53
+ type EpWatchResp struct {
54
+ opcode string
55
+ errStr string
56
+ svcName string
57
+ providers []string
58
+ }
59
+
60
+ type watchSvcStatus struct {
61
+ // The type of watch update contained in the message
62
+ Type string `json:"type"`
63
+ // Pod details
64
+ Object Service `json:"object"`
65
+ }
66
+
67
+ type watchSvcEpStatus struct {
68
+ // The type of watch update contained in the message
69
+ Type string `json:"type"`
70
+ // Pod details
71
+ Object Endpoints `json:"object"`
37
72
}
38
73
39
74
type podInfo struct {
@@ -46,6 +81,7 @@ type podInfo struct {
46
81
func NewAPIClient (serverURL , caFile , keyFile , certFile string ) * APIClient {
47
82
c := APIClient {}
48
83
c .baseURL = serverURL + "/api/v1/namespaces/"
84
+ c .watchBase = serverURL + "/api/v1/watch/"
49
85
50
86
// Read client cert
51
87
cert , err := tls .LoadX509KeyPair (certFile , keyFile )
@@ -172,3 +208,140 @@ func (c *APIClient) GetPodLabel(ns, name, label string) (string, error) {
172
208
log .Infof ("label %s not found in podSpec for %s.%s" , label , ns , name )
173
209
return "" , nil
174
210
}
211
+
212
+ // WatchServices watches the services object on the api server
213
+ func (c * APIClient ) WatchServices (respCh chan SvcWatchResp ) {
214
+ ctx , _ := context .WithCancel (context .Background ())
215
+
216
+ go func () {
217
+ // Make request to Kubernetes API
218
+ getURL := c .watchBase + "services"
219
+ req , err := http .NewRequest ("GET" , getURL , nil )
220
+ if err != nil {
221
+ respCh <- SvcWatchResp {opcode : "FATAL" , errStr : fmt .Sprintf ("Req %v" , err )}
222
+ return
223
+ }
224
+ res , err := ctxhttp .Do (ctx , c .client , req )
225
+ defer res .Body .Close ()
226
+ if err != nil {
227
+ respCh <- SvcWatchResp {opcode : "FATAL" , errStr : fmt .Sprintf ("Do %v" , err )}
228
+ return
229
+ }
230
+
231
+ var wss watchSvcStatus
232
+ reader := bufio .NewReader (res .Body )
233
+
234
+ // bufio.Reader.ReadBytes is blocking, so we watch for
235
+ // context timeout or cancellation in a goroutine
236
+ // and close the response body when see see it. The
237
+ // response body is also closed via defer when the
238
+ // request is made, but closing twice is OK.
239
+ go func () {
240
+ <- ctx .Done ()
241
+ res .Body .Close ()
242
+ }()
243
+
244
+ for {
245
+ line , err := reader .ReadBytes ('\n' )
246
+ if ctx .Err () != nil {
247
+ respCh <- SvcWatchResp {opcode : "ERROR" , errStr : fmt .Sprintf ("ctx %v" , err )}
248
+ return
249
+ }
250
+ if err != nil {
251
+ respCh <- SvcWatchResp {opcode : "ERROR" , errStr : fmt .Sprintf ("read %v" , err )}
252
+ return
253
+ }
254
+ if err := json .Unmarshal (line , & wss ); err != nil {
255
+ respCh <- SvcWatchResp {opcode : "WARN" , errStr : fmt .Sprintf ("unmarshal %v" , err )}
256
+ continue
257
+ }
258
+
259
+ //if wss.Object.ObjectMeta.Namespace != "default" {
260
+ // continue
261
+ //}
262
+ resp := SvcWatchResp {opcode : wss .Type }
263
+ resp .svcName = wss .Object .ObjectMeta .Name
264
+ sSpec := ofnet.ServiceSpec {}
265
+ sSpec .Ports = make ([]ofnet.PortSpec , 0 , 1 )
266
+ sSpec .IpAddress = wss .Object .Spec .ClusterIP
267
+ for _ , port := range wss .Object .Spec .Ports {
268
+ ps := ofnet.PortSpec {Protocol : string (port .Protocol ),
269
+ SvcPort : uint16 (port .Port ),
270
+ ProvPort : uint16 (port .TargetPort ),
271
+ }
272
+ sSpec .Ports = append (sSpec .Ports , ps )
273
+ }
274
+
275
+ resp .svcSpec = sSpec
276
+ log .Infof ("resp: %+v" , resp )
277
+
278
+ respCh <- resp
279
+ }
280
+ }()
281
+ }
282
+
283
+ // WatchSvcEps watches the service endpoints object
284
+ func (c * APIClient ) WatchSvcEps (respCh chan EpWatchResp ) {
285
+ ctx , _ := context .WithCancel (context .Background ())
286
+
287
+ go func () {
288
+ // Make request to Kubernetes API
289
+ getURL := c .watchBase + "endpoints"
290
+ req , err := http .NewRequest ("GET" , getURL , nil )
291
+ if err != nil {
292
+ respCh <- EpWatchResp {opcode : "FATAL" , errStr : fmt .Sprintf ("Req %v" , err )}
293
+ return
294
+ }
295
+ res , err := ctxhttp .Do (ctx , c .client , req )
296
+ defer res .Body .Close ()
297
+ if err != nil {
298
+ respCh <- EpWatchResp {opcode : "FATAL" , errStr : fmt .Sprintf ("Do %v" , err )}
299
+ return
300
+ }
301
+
302
+ var weps watchSvcEpStatus
303
+ reader := bufio .NewReader (res .Body )
304
+
305
+ // bufio.Reader.ReadBytes is blocking, so we watch for
306
+ // context timeout or cancellation in a goroutine
307
+ // and close the response body when see see it. The
308
+ // response body is also closed via defer when the
309
+ // request is made, but closing twice is OK.
310
+ go func () {
311
+ <- ctx .Done ()
312
+ res .Body .Close ()
313
+ }()
314
+
315
+ for {
316
+ line , err := reader .ReadBytes ('\n' )
317
+ if ctx .Err () != nil {
318
+ respCh <- EpWatchResp {opcode : "ERROR" , errStr : fmt .Sprintf ("ctx %v" , err )}
319
+ return
320
+ }
321
+ if err != nil {
322
+ respCh <- EpWatchResp {opcode : "ERROR" , errStr : fmt .Sprintf ("read %v" , err )}
323
+ return
324
+ }
325
+ if err := json .Unmarshal (line , & weps ); err != nil {
326
+ respCh <- EpWatchResp {opcode : "WARN" , errStr : fmt .Sprintf ("unmarshal %v" , err )}
327
+ continue
328
+ }
329
+ //if weps.Object.ObjectMeta.Namespace != "default" {
330
+ // continue
331
+ //}
332
+
333
+ resp := EpWatchResp {opcode : weps .Type }
334
+ resp .svcName = weps .Object .ObjectMeta .Name
335
+ resp .providers = make ([]string , 0 , 1 )
336
+ for _ , subset := range weps .Object .Subsets {
337
+ // TODO: handle partially ready providers
338
+ for _ , addr := range subset .Addresses {
339
+ resp .providers = append (resp .providers , addr .IP )
340
+ }
341
+ }
342
+
343
+ log .Infof ("kube ep watch: %v" , resp )
344
+ respCh <- resp
345
+ }
346
+ }()
347
+ }
0 commit comments