github.com/grafana/synthetic-monitoring-agent@v0.64.2/internal/pusher/v2/errors_test.go (about)

     1  package v2
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"net/http"
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/grafana/synthetic-monitoring-agent/internal/pkg/prom"
    12  )
    13  
    14  func TestParsePushError(t *testing.T) {
    15  	for title, tc := range map[string]struct {
    16  		input    error
    17  		expected error
    18  		code     int
    19  	}{
    20  		"no error": {
    21  			input: nil,
    22  			expected: pushError{
    23  				kind: errKindNoError,
    24  			},
    25  			code: http.StatusOK,
    26  		},
    27  
    28  		"context canceled": {
    29  			input: context.Canceled,
    30  			expected: pushError{
    31  				kind: errKindTerminated,
    32  			},
    33  			code: 0,
    34  		},
    35  
    36  		"context deadline exceeded": {
    37  			input: context.DeadlineExceeded,
    38  			expected: pushError{
    39  				kind: errKindNetwork,
    40  			},
    41  			code: 0,
    42  		},
    43  
    44  		"other error is network": {
    45  			input: errors.New("something is wrong"),
    46  			expected: pushError{
    47  				kind: errKindNetwork,
    48  			},
    49  			code: 0,
    50  		},
    51  
    52  		"500 errors are temporary": {
    53  			input: &prom.HttpError{
    54  				StatusCode: 500,
    55  				Status:     "Internal Server Error",
    56  				Err:        errors.New(`something is wrong`),
    57  			},
    58  			expected: pushError{
    59  				kind: errKindNetwork,
    60  			},
    61  			code: 500,
    62  		},
    63  
    64  		"500 from grafana instance": {
    65  			input: &prom.HttpError{
    66  				StatusCode: 500,
    67  				Status:     "Internal Server Error",
    68  				Err:        errors.New(`It looks like there is an issue with this instance`),
    69  			},
    70  			expected: pushError{
    71  				kind: errKindTenant,
    72  			},
    73  			code: 500,
    74  		},
    75  
    76  		"400 errors are data errors": {
    77  			input: &prom.HttpError{
    78  				StatusCode: 400,
    79  				Status:     "Bad Request",
    80  				Err:        errors.New(`400 Bad Request: couldn't parse labels: 1:248: parse error: unexpected identifier "foo" in label set, expected "," or "}"`),
    81  			},
    82  			expected: pushError{
    83  				kind: errKindPayload,
    84  			},
    85  			code: 400,
    86  		},
    87  
    88  		"400 err-mimir-sample-out-of-order": {
    89  			input: &prom.HttpError{
    90  				StatusCode: 400,
    91  				Status:     "Bad Request",
    92  				Err:        errors.New(`400 Bad Request: failed pushing to ingester: user=275106: the sample has been rejected because another sample with a more recent timestamp has already been ingested and out-of-order samples are not allowed (err-mimir-sample-out-of-order). The affected sample has timestamp`),
    93  			},
    94  			expected: pushError{
    95  				kind: errKindPayload,
    96  			},
    97  			code: 400,
    98  		},
    99  
   100  		"400 err-mimir-max-series-per-user": {
   101  			input: &prom.HttpError{
   102  				StatusCode: 400,
   103  				Status:     "Bad Request",
   104  				Err:        errors.New(`400 Bad Request: failed pushing to ingester: user=1234: per-user series limit of 15000 exceeded (err-mimir-max-series-per-user). To adjust the related per-tenant limit, configure -ingester.max-global-series-per-user, or contact your service administrator.`),
   105  			},
   106  			expected: pushError{
   107  				kind: errKindFatal,
   108  			},
   109  			code: 400,
   110  		},
   111  
   112  		"429 generic": {
   113  			input: &prom.HttpError{
   114  				StatusCode: 429,
   115  				Status:     "Too Many Requests",
   116  				Err:        errors.New(`the request has been rejected because the tenant exceeded the request rate limit, set to 75 requests/s across all distributors with a maximum allowed burst of 750 (err-mimir-tenant-max-request-rate)`),
   117  			},
   118  			expected: pushError{
   119  				kind: errKindWait,
   120  			},
   121  			code: 429,
   122  		},
   123  
   124  		"429 loki ingest denied": {
   125  			input: &prom.HttpError{
   126  				StatusCode: 429,
   127  				Status:     "Too Many Requests",
   128  				Err:        errors.New(`Ingestion rate limit exceeded for user 1234 (limit: 0 bytes/sec) while attempting to ingest '7' lines totaling '1874' bytes, reduce log volume or contact your Loki administrator to see if the limit can be increased`),
   129  			},
   130  			expected: pushError{
   131  				kind: errKindFatal,
   132  			},
   133  			code: 429,
   134  		},
   135  
   136  		"429 loki active streams": {
   137  			input: &prom.HttpError{
   138  				StatusCode: 429,
   139  				Status:     "Too Many Requests",
   140  				Err:        errors.New(`Maximum active stream limit exceeded, reduce the number of active streams (reduce labels or reduce label values), or contact your Loki administrator to see if the limit can be increased, user: '1234'`),
   141  			},
   142  			expected: pushError{
   143  				kind: errKindLimit,
   144  			},
   145  			code: 429,
   146  		},
   147  
   148  		"404 Not Found": {
   149  			input: &prom.HttpError{
   150  				StatusCode: 404,
   151  				Status:     "Not Found",
   152  				Err:        errors.New("file not found"),
   153  			},
   154  			expected: pushError{
   155  				kind: errKindFatal,
   156  			},
   157  			code: 404,
   158  		},
   159  
   160  		"401 bad token": {
   161  			input: &prom.HttpError{
   162  				StatusCode: 401,
   163  				Status:     "Unauthorized",
   164  				Err:        errors.New(`{"status":"error","error":"authentication error: invalid token"}`),
   165  			},
   166  			expected: pushError{
   167  				kind: errKindTenant,
   168  			},
   169  			code: 401,
   170  		},
   171  
   172  		"403 Forbidden": {
   173  			input: &prom.HttpError{
   174  				StatusCode: 403,
   175  				Status:     "Forbidden",
   176  				Err:        errors.New(`Forbidden`),
   177  			},
   178  			expected: pushError{
   179  				kind: errKindFatal,
   180  			},
   181  			code: 403,
   182  		},
   183  
   184  		"300 response": {
   185  			input: &prom.HttpError{
   186  				StatusCode: 300,
   187  				Status:     "Multiple Choices",
   188  				Err:        errors.New("multiple choices"),
   189  			},
   190  			expected: pushError{
   191  				kind: errKindFatal,
   192  			},
   193  			code: 300,
   194  		},
   195  	} {
   196  		t.Run(title, func(t *testing.T) {
   197  			code, err := parsePublishError(tc.input)
   198  
   199  			var (
   200  				expectedErr = tc.expected
   201  				pErr        pushError
   202  			)
   203  			if errors.As(expectedErr, &pErr) {
   204  				pErr.inner = tc.input
   205  				expectedErr = pErr
   206  			}
   207  
   208  			require.Equal(t, expectedErr, err)
   209  			require.Equal(t, tc.code, code)
   210  		})
   211  	}
   212  }