github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/daemon/logger/splunk/splunkhecmock_test.go (about)

     1  package splunk // import "github.com/docker/docker/daemon/logger/splunk"
     2  
     3  import (
     4  	"compress/gzip"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io"
     9  	"net"
    10  	"net/http"
    11  	"sync"
    12  	"testing"
    13  )
    14  
    15  func (message *splunkMessage) EventAsString() (string, error) {
    16  	if val, ok := message.Event.(string); ok {
    17  		return val, nil
    18  	}
    19  	return "", fmt.Errorf("Cannot cast Event %v to string", message.Event)
    20  }
    21  
    22  func (message *splunkMessage) EventAsMap() (map[string]interface{}, error) {
    23  	if val, ok := message.Event.(map[string]interface{}); ok {
    24  		return val, nil
    25  	}
    26  	return nil, fmt.Errorf("Cannot cast Event %v to map", message.Event)
    27  }
    28  
    29  type HTTPEventCollectorMock struct {
    30  	tcpAddr     *net.TCPAddr
    31  	tcpListener *net.TCPListener
    32  
    33  	mu                  sync.Mutex
    34  	token               string
    35  	simulateServerError bool
    36  	blockingCtx         context.Context
    37  
    38  	test *testing.T
    39  
    40  	connectionVerified bool
    41  	gzipEnabled        *bool
    42  	messages           []*splunkMessage
    43  	numOfRequests      int
    44  }
    45  
    46  func NewHTTPEventCollectorMock(t *testing.T) *HTTPEventCollectorMock {
    47  	tcpAddr := &net.TCPAddr{IP: []byte{127, 0, 0, 1}, Port: 0, Zone: ""}
    48  	tcpListener, err := net.ListenTCP("tcp", tcpAddr)
    49  	if err != nil {
    50  		t.Fatal(err)
    51  	}
    52  	return &HTTPEventCollectorMock{
    53  		tcpAddr:             tcpAddr,
    54  		tcpListener:         tcpListener,
    55  		token:               "4642492F-D8BD-47F1-A005-0C08AE4657DF",
    56  		simulateServerError: false,
    57  		test:                t,
    58  		connectionVerified:  false}
    59  }
    60  
    61  func (hec *HTTPEventCollectorMock) simulateErr(b bool) {
    62  	hec.mu.Lock()
    63  	hec.simulateServerError = b
    64  	hec.mu.Unlock()
    65  }
    66  
    67  func (hec *HTTPEventCollectorMock) withBlock(ctx context.Context) {
    68  	hec.mu.Lock()
    69  	hec.blockingCtx = ctx
    70  	hec.mu.Unlock()
    71  }
    72  
    73  func (hec *HTTPEventCollectorMock) URL() string {
    74  	return "http://" + hec.tcpListener.Addr().String()
    75  }
    76  
    77  func (hec *HTTPEventCollectorMock) Serve() error {
    78  	return http.Serve(hec.tcpListener, hec)
    79  }
    80  
    81  func (hec *HTTPEventCollectorMock) Close() error {
    82  	return hec.tcpListener.Close()
    83  }
    84  
    85  func (hec *HTTPEventCollectorMock) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
    86  	var err error
    87  
    88  	hec.numOfRequests++
    89  
    90  	hec.mu.Lock()
    91  	simErr := hec.simulateServerError
    92  	ctx := hec.blockingCtx
    93  	hec.mu.Unlock()
    94  
    95  	if ctx != nil {
    96  		<-hec.blockingCtx.Done()
    97  	}
    98  
    99  	if simErr {
   100  		if request.Body != nil {
   101  			defer request.Body.Close()
   102  		}
   103  		writer.WriteHeader(http.StatusInternalServerError)
   104  		return
   105  	}
   106  
   107  	switch request.Method {
   108  	case http.MethodOptions:
   109  		// Verify that options method is getting called only once
   110  		if hec.connectionVerified {
   111  			hec.test.Errorf("Connection should not be verified more than once. Got second request with %s method.", request.Method)
   112  		}
   113  		hec.connectionVerified = true
   114  		writer.WriteHeader(http.StatusOK)
   115  	case http.MethodPost:
   116  		// Always verify that Driver is using correct path to HEC
   117  		if request.URL.String() != "/services/collector/event/1.0" {
   118  			hec.test.Errorf("Unexpected path %v", request.URL)
   119  		}
   120  		defer request.Body.Close()
   121  
   122  		if authorization, ok := request.Header["Authorization"]; !ok || authorization[0] != ("Splunk "+hec.token) {
   123  			hec.test.Error("Authorization header is invalid.")
   124  		}
   125  
   126  		gzipEnabled := false
   127  		if contentEncoding, ok := request.Header["Content-Encoding"]; ok && contentEncoding[0] == "gzip" {
   128  			gzipEnabled = true
   129  		}
   130  
   131  		if hec.gzipEnabled == nil {
   132  			hec.gzipEnabled = &gzipEnabled
   133  		} else if gzipEnabled != *hec.gzipEnabled {
   134  			// Nothing wrong with that, but we just know that Splunk Logging Driver does not do that
   135  			hec.test.Error("Driver should not change Content Encoding.")
   136  		}
   137  
   138  		var gzipReader *gzip.Reader
   139  		var reader io.Reader
   140  		if gzipEnabled {
   141  			gzipReader, err = gzip.NewReader(request.Body)
   142  			if err != nil {
   143  				hec.test.Fatal(err)
   144  			}
   145  			reader = gzipReader
   146  		} else {
   147  			reader = request.Body
   148  		}
   149  
   150  		// Read body
   151  		var body []byte
   152  		body, err = io.ReadAll(reader)
   153  		if err != nil {
   154  			hec.test.Fatal(err)
   155  		}
   156  
   157  		// Parse message
   158  		messageStart := 0
   159  		for i := 0; i < len(body); i++ {
   160  			if i == len(body)-1 || (body[i] == '}' && body[i+1] == '{') {
   161  				var message splunkMessage
   162  				err = json.Unmarshal(body[messageStart:i+1], &message)
   163  				if err != nil {
   164  					hec.test.Log(string(body[messageStart : i+1]))
   165  					hec.test.Fatal(err)
   166  				}
   167  				hec.messages = append(hec.messages, &message)
   168  				messageStart = i + 1
   169  			}
   170  		}
   171  
   172  		if gzipEnabled {
   173  			gzipReader.Close()
   174  		}
   175  
   176  		writer.WriteHeader(http.StatusOK)
   177  	default:
   178  		hec.test.Errorf("Unexpected HTTP method %s", http.MethodOptions)
   179  		writer.WriteHeader(http.StatusBadRequest)
   180  	}
   181  }