github.com/elfadel/cilium@v1.6.12/pkg/proxy/kafka_test.go (about) 1 // Copyright 2017 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // +build !privileged_tests 16 17 package proxy 18 19 import ( 20 "context" 21 "fmt" 22 "net" 23 "strconv" 24 "testing" 25 "time" 26 27 "github.com/cilium/cilium/common/addressing" 28 "github.com/cilium/cilium/pkg/completion" 29 "github.com/cilium/cilium/pkg/datapath" 30 "github.com/cilium/cilium/pkg/endpoint" 31 "github.com/cilium/cilium/pkg/endpoint/regeneration" 32 "github.com/cilium/cilium/pkg/endpointmanager" 33 "github.com/cilium/cilium/pkg/identity" 34 "github.com/cilium/cilium/pkg/identity/cache" 35 "github.com/cilium/cilium/pkg/lock" 36 "github.com/cilium/cilium/pkg/logging/logfields" 37 monitorAPI "github.com/cilium/cilium/pkg/monitor/api" 38 "github.com/cilium/cilium/pkg/policy" 39 "github.com/cilium/cilium/pkg/policy/api" 40 "github.com/cilium/cilium/pkg/proxy/logger" 41 "github.com/cilium/cilium/pkg/revert" 42 43 "github.com/optiopay/kafka" 44 "github.com/optiopay/kafka/proto" 45 "github.com/sirupsen/logrus" 46 47 . "gopkg.in/check.v1" 48 ) 49 50 // Hook up gocheck into the "go test" runner. 51 func Test(t *testing.T) { 52 TestingT(t) 53 } 54 55 type proxyTestSuite struct { 56 repo *policy.Repository 57 } 58 59 var _ = Suite(&proxyTestSuite{}) 60 61 func (s *proxyTestSuite) SetUpSuite(c *C) { 62 s.repo = policy.NewPolicyRepository() 63 } 64 65 func (s *proxyTestSuite) GetPolicyRepository() *policy.Repository { 66 return s.repo 67 } 68 69 func (s *proxyTestSuite) UpdateProxyRedirect(e regeneration.EndpointUpdater, l4 *policy.L4Filter, wg *completion.WaitGroup) (uint16, error, revert.FinalizeFunc, revert.RevertFunc) { 70 return 0, nil, nil, nil 71 } 72 73 func (s *proxyTestSuite) RemoveProxyRedirect(e regeneration.EndpointInfoSource, id string, wg *completion.WaitGroup) (error, revert.FinalizeFunc, revert.RevertFunc) { 74 return nil, nil, nil 75 } 76 77 func (s *proxyTestSuite) UpdateNetworkPolicy(e regeneration.EndpointUpdater, policy *policy.L4Policy, 78 proxyWaitGroup *completion.WaitGroup) (error, revert.RevertFunc) { 79 return nil, nil 80 } 81 82 func (s *proxyTestSuite) RemoveNetworkPolicy(e regeneration.EndpointInfoSource) {} 83 84 func (s *proxyTestSuite) QueueEndpointBuild(ctx context.Context, epID uint64) (func(), error) { 85 return nil, nil 86 } 87 88 func (s *proxyTestSuite) RemoveFromEndpointQueue(epID uint64) {} 89 90 func (s *proxyTestSuite) GetCompilationLock() *lock.RWMutex { 91 return nil 92 } 93 94 func (s *proxyTestSuite) SendNotification(typ monitorAPI.AgentNotification, text string) error { 95 return nil 96 } 97 98 func (s *proxyTestSuite) Datapath() datapath.Datapath { 99 return nil 100 } 101 102 func (s *proxyTestSuite) GetNodeSuffix() string { 103 return "" 104 } 105 106 func (s *proxyTestSuite) UpdateIdentities(added, deleted cache.IdentityCache) {} 107 108 type DummySelectorCacheUser struct{} 109 110 func (d *DummySelectorCacheUser) IdentitySelectionUpdated(selector policy.CachedSelector, selections, added, deleted []identity.NumericIdentity) { 111 } 112 113 var ( 114 localEndpointMock logger.EndpointUpdater = &proxyUpdaterMock{ 115 id: 1000, 116 ipv4: "10.0.0.1", 117 ipv6: "f00d::1", 118 labels: []string{"id.foo", "id.bar"}, 119 identity: identity.NumericIdentity(256), 120 } 121 122 dummySelectorCacheUser = &DummySelectorCacheUser{} 123 testSelectorCache = policy.NewSelectorCache(cache.IdentityCache{}) 124 125 wildcardCachedSelector, _ = testSelectorCache.AddIdentitySelector(dummySelectorCacheUser, api.WildcardEndpointSelector) 126 ) 127 128 // newTestBrokerConf returns BrokerConf with default configuration adjusted for 129 // tests 130 func newTestBrokerConf(clientID string) kafka.BrokerConf { 131 conf := kafka.NewBrokerConf(clientID) 132 conf.DialTimeout = 400 * time.Millisecond 133 conf.LeaderRetryLimit = 10 134 conf.LeaderRetryWait = 2 * time.Millisecond 135 return conf 136 } 137 138 type loggerMap struct{} 139 140 func fields(args ...interface{}) logrus.Fields { 141 fields := logrus.Fields{} 142 for i := 0; i+1 < len(args); i += 2 { 143 fields[args[i].(string)] = args[i+1] 144 } 145 return fields 146 } 147 148 func (loggerMap) Debug(msg string, args ...interface{}) { log.WithFields(fields(args...)).Debug(msg) } 149 func (loggerMap) Info(msg string, args ...interface{}) { log.WithFields(fields(args...)).Info(msg) } 150 func (loggerMap) Warn(msg string, args ...interface{}) { log.WithFields(fields(args...)).Warn(msg) } 151 func (loggerMap) Error(msg string, args ...interface{}) { log.WithFields(fields(args...)).Error(msg) } 152 153 var ( 154 proxyAddress = "127.0.0.1" 155 ) 156 157 type metadataTester struct { 158 host string 159 port uint16 160 topics map[string]bool 161 allowCreate bool 162 numGeneralFetches int 163 numSpecificFetches int 164 } 165 166 func newMetadataHandler(srv *Server, allowCreate bool, proxyPort uint16) *metadataTester { 167 tester := &metadataTester{ 168 host: proxyAddress, 169 port: proxyPort, 170 allowCreate: allowCreate, 171 topics: make(map[string]bool), 172 } 173 tester.topics["allowedTopic"] = true 174 tester.topics["disallowedTopic"] = true 175 return tester 176 } 177 178 func (m *metadataTester) NumGeneralFetches() int { 179 return m.numGeneralFetches 180 } 181 182 func (m *metadataTester) NumSpecificFetches() int { 183 return m.numSpecificFetches 184 } 185 186 func (m *metadataTester) Handler() RequestHandler { 187 return func(request Serializable) Serializable { 188 req := request.(*proto.MetadataReq) 189 190 if len(req.Topics) == 0 { 191 m.numGeneralFetches++ 192 } else { 193 m.numSpecificFetches++ 194 } 195 196 resp := &proto.MetadataResp{ 197 CorrelationID: req.CorrelationID, 198 Brokers: []proto.MetadataRespBroker{ 199 {NodeID: 1, Host: m.host, Port: int32(m.port)}, 200 }, 201 Topics: []proto.MetadataRespTopic{}, 202 } 203 204 wantsTopic := make(map[string]bool) 205 for _, topic := range req.Topics { 206 if m.allowCreate { 207 m.topics[topic] = true 208 } 209 wantsTopic[topic] = true 210 } 211 212 for topic := range m.topics { 213 // Return either all topics or only topics that they explicitly requested 214 _, explicitTopic := wantsTopic[topic] 215 if len(req.Topics) > 0 && !explicitTopic { 216 continue 217 } 218 219 resp.Topics = append(resp.Topics, proto.MetadataRespTopic{ 220 Name: topic, 221 Partitions: []proto.MetadataRespPartition{ 222 { 223 ID: 0, 224 Leader: 1, 225 Replicas: []int32{1}, 226 Isrs: []int32{1}, 227 }, 228 { 229 ID: 1, 230 Leader: 1, 231 Replicas: []int32{1}, 232 Isrs: []int32{1}, 233 }, 234 }, 235 }) 236 } 237 return resp 238 } 239 } 240 241 func (s *proxyTestSuite) TestKafkaRedirect(c *C) { 242 server := NewServer() 243 server.Start() 244 defer server.Close() 245 246 log.WithFields(logrus.Fields{ 247 "address": server.Address(), 248 }).Debug("Started kafka server") 249 250 pp := getProxyPort(policy.ParserTypeKafka, true) 251 c.Assert(pp.configured, Equals, false) 252 var err error 253 pp.proxyPort, err = allocatePort(pp.proxyPort, 10000, 20000) 254 c.Assert(err, IsNil) 255 c.Assert(pp.proxyPort, Not(Equals), 0) 256 pp.reservePort() 257 c.Assert(pp.configured, Equals, true) 258 259 proxyAddress := fmt.Sprintf("%s:%d", proxyAddress, uint16(pp.proxyPort)) 260 261 kafkaRule1 := api.PortRuleKafka{APIKey: "metadata", APIVersion: "0"} 262 c.Assert(kafkaRule1.Sanitize(), IsNil) 263 264 kafkaRule2 := api.PortRuleKafka{APIKey: "produce", APIVersion: "0", Topic: "allowedTopic"} 265 c.Assert(kafkaRule2.Sanitize(), IsNil) 266 267 // Insert a mock EP to the endpointmanager so that DefaultEndpointInfoRegistry may find 268 // the EP ID by the IP. 269 ep := endpoint.NewEndpointWithState(s, uint16(localEndpointMock.GetID()), endpoint.StateReady) 270 ipv4, err := addressing.NewCiliumIPv4("127.0.0.1") 271 c.Assert(err, IsNil) 272 ep.IPv4 = ipv4 273 ep.UpdateLogger(nil) 274 endpointmanager.Insert(ep) 275 defer endpointmanager.Remove(ep) 276 277 _, dstPortStr, err := net.SplitHostPort(server.Address()) 278 c.Assert(err, IsNil) 279 portInt, err := strconv.Atoi(dstPortStr) 280 c.Assert(err, IsNil) 281 r := newRedirect(localEndpointMock, pp, uint16(portInt)) 282 283 r.rules = policy.L7DataMap{ 284 wildcardCachedSelector: api.L7Rules{ 285 Kafka: []api.PortRuleKafka{kafkaRule1, kafkaRule2}, 286 }, 287 } 288 289 redir, err := createKafkaRedirect(r, kafkaConfiguration{ 290 lookupSrcID: func(mapname, remoteAddr, localAddr string, ingress bool) (uint32, error) { 291 return uint32(1000), nil 292 }, 293 // Disable use of SO_MARK, IP_TRANSPARENT for tests 294 testMode: true, 295 }, DefaultEndpointInfoRegistry) 296 c.Assert(err, IsNil) 297 defer redir.Close(nil) 298 299 log.WithFields(logrus.Fields{ 300 "address": proxyAddress, 301 }).Debug("Started kafka proxy") 302 303 server.Handle(MetadataRequest, newMetadataHandler(server, false, r.listener.proxyPort).Handler()) 304 305 broker, err := kafka.Dial([]string{proxyAddress}, newTestBrokerConf("tester")) 306 if err != nil { 307 c.Fatalf("cannot create broker: %s", err) 308 } 309 310 // setup producer 311 prodConf := kafka.NewProducerConf() 312 prodConf.RetryWait = time.Millisecond 313 prodConf.Logger = loggerMap{} 314 producer := broker.Producer(prodConf) 315 messages := []*proto.Message{ 316 {Value: []byte("first")}, 317 {Value: []byte("second")}, 318 } 319 320 // Start handling allowedTopic produce requests 321 server.Handle(ProduceRequest, func(request Serializable) Serializable { 322 req := request.(*proto.ProduceReq) 323 log.WithField(logfields.Request, logfields.Repr(req)).Debug("Handling req") 324 return &proto.ProduceResp{ 325 CorrelationID: req.CorrelationID, 326 Topics: []proto.ProduceRespTopic{ 327 { 328 Name: req.Topics[0].Name, 329 Partitions: []proto.ProduceRespPartition{ 330 { 331 ID: 0, 332 Offset: 5, 333 }, 334 }, 335 }, 336 }, 337 } 338 }) 339 340 // send a Produce request for an allowed topic 341 offset, err := producer.Produce("allowedTopic", 0, messages...) 342 c.Assert(err, IsNil) 343 c.Assert(offset, Equals, int64(5)) 344 345 // send a Produce request for disallowed topic 346 _, err = producer.Produce("disallowedTopic", 0, messages...) 347 c.Assert(err, Equals, proto.ErrTopicAuthorizationFailed) 348 349 log.Debug("Testing done, closing listen socket") 350 finalize, _ := redir.Close(nil) 351 finalize() 352 353 // In order to see in the logs that the connections get closed after the 354 // 1-minute timeout, uncomment this line: 355 // time.Sleep(2 * time.Minute) 356 }