...

Source file src/github.com/linkerd/linkerd2/pkg/profiles/profiles_test.go

Documentation: github.com/linkerd/linkerd2/pkg/profiles

     1  package profiles
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"testing"
     7  )
     8  
     9  type spExp struct {
    10  	err error
    11  	sp  string
    12  }
    13  
    14  func TestValidate(t *testing.T) {
    15  	expectations := []spExp{
    16  		{
    17  			err: nil,
    18  			sp: `apiVersion: linkerd.io/v1alpha2
    19  kind: ServiceProfile
    20  metadata:
    21    name: name.ns.svc.cluster.local
    22    namespace: linkerd-ns
    23  spec:
    24    routes:
    25    - name: name-1
    26      condition:
    27        method: GET
    28        pathRegex: /route-1`,
    29  		},
    30  		{
    31  			err: nil,
    32  			sp: `apiVersion: linkerd.io/v1alpha2
    33  kind: ServiceProfile
    34  metadata:
    35    name: name.ns.svc.cluster.local
    36    namespace: linkerd-ns
    37  spec:
    38    retryBudget:
    39      minRetriesPerSecond: 5
    40      retryRatio: 0.2
    41      ttl: 10ms
    42    routes:
    43    - name: name-1
    44      condition:
    45        method: GET
    46        pathRegex: /route-1
    47        any:
    48        - all:
    49          - method: POST
    50          - pathRegex: '/authors/\d+'
    51        - all:
    52          - not:
    53              method: DELETE
    54          - pathRegex: /info.txt
    55      responseClasses:
    56      - condition:
    57          status:
    58            min: 500
    59            max: 599
    60          all:
    61          - status:
    62              min: 500
    63              max: 599
    64          - not:
    65              status:
    66                min: 503`,
    67  		},
    68  		{
    69  			err: errors.New("ServiceProfile \"^.^\" has invalid name: a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')"),
    70  			sp: `apiVersion: linkerd.io/v1alpha2
    71  kind: ServiceProfile
    72  metadata:
    73    name: ^.^
    74    namespace: linkerd-ns
    75  spec:
    76    routes:
    77    - name: name-1
    78      condition:
    79        method: GET
    80        pathRegex: /route-1`,
    81  		},
    82  		{
    83  			err: errors.New("failed to validate ServiceProfile: error unmarshaling JSON: while decoding JSON: json: unknown field \"foo\""),
    84  			sp: `apiVersion: linkerd.io/v1alpha2
    85  kind: ServiceProfile
    86  metadata:
    87    name: name.ns.svc.cluster.local
    88    namespace: linkerd-ns
    89  spec:
    90    foo: bar
    91    routes:
    92    - name: name-1
    93      condition:
    94        method: GET
    95        pathRegex: /route-1`,
    96  		},
    97  		{
    98  			err: errors.New("failed to validate ServiceProfile: error unmarshaling JSON: while decoding JSON: json: unknown field \"foo\""),
    99  			sp: `apiVersion: linkerd.io/v1alpha2
   100  kind: ServiceProfile
   101  metadata:
   102    name: name.ns.svc.cluster.local
   103    namespace: linkerd-ns
   104  spec:
   105    routes:
   106    - name: name-1
   107      foo: bar
   108      condition:
   109        method: GET
   110        pathRegex: /route-1`,
   111  		},
   112  		{
   113  			err: errors.New("failed to validate ServiceProfile: error unmarshaling JSON: while decoding JSON: json: unknown field \"foo\""),
   114  			sp: `apiVersion: linkerd.io/v1alpha2
   115  kind: ServiceProfile
   116  metadata:
   117    name: name.ns.svc.cluster.local
   118    namespace: linkerd-ns
   119  spec:
   120    routes:
   121    - name: name-1
   122      condition:
   123        foo: bar
   124        method: GET
   125        pathRegex: /route-1`,
   126  		},
   127  		{
   128  			err: errors.New("ServiceProfile \"name.ns.svc.cluster.local\" has a route with no condition"),
   129  			sp: `apiVersion: linkerd.io/v1alpha2
   130  kind: ServiceProfile
   131  metadata:
   132    name: name.ns.svc.cluster.local
   133    namespace: linkerd-ns
   134  spec:
   135    routes:
   136    - name: name-1`,
   137  		},
   138  		{
   139  			err: errors.New("ServiceProfile \"name.ns.svc.cluster.local\" has a route with no name"),
   140  			sp: `apiVersion: linkerd.io/v1alpha2
   141  kind: ServiceProfile
   142  metadata:
   143    name: name.ns.svc.cluster.local
   144    namespace: linkerd-ns
   145  spec:
   146    routes:
   147    - condition:
   148        method: GET
   149        pathRegex: /route-1`,
   150  		},
   151  		{
   152  			err: errors.New("failed to validate ServiceProfile: error unmarshaling JSON: while decoding JSON: json: unknown field \"foo\""),
   153  			sp: `apiVersion: linkerd.io/v1alpha2
   154  kind: ServiceProfile
   155  metadata:
   156    name: name.ns.svc.cluster.local
   157    namespace: linkerd-ns
   158  spec:
   159    routes:
   160    - name: name-1
   161      condition:
   162        foo: bar
   163        method: GET
   164        pathRegex: /route-1
   165        not:
   166          method: GET`,
   167  		},
   168  		{
   169  			err: errors.New("ServiceProfile \"name.ns.svc.cluster.local\" has a route with no condition"),
   170  			sp: `apiVersion: linkerd.io/v1alpha2
   171  kind: ServiceProfile
   172  metadata:
   173    name: name.ns.svc.cluster.local
   174    namespace: linkerd-ns
   175  spec:
   176    routes:
   177    - name: name-1
   178      condition:`,
   179  		},
   180  		{
   181  			err: errors.New("ServiceProfile \"name.ns.svc.cluster.local\" has a route with an invalid condition: A request match must have a field set"),
   182  			sp: `apiVersion: linkerd.io/v1alpha2
   183  kind: ServiceProfile
   184  metadata:
   185    name: name.ns.svc.cluster.local
   186    namespace: linkerd-ns
   187  spec:
   188    routes:
   189    - name: name-1
   190      condition:
   191        method:`,
   192  		},
   193  		{
   194  			err: errors.New("ServiceProfile \"name.ns.svc.cluster.local\" has a response class with no condition"),
   195  			sp: `apiVersion: linkerd.io/v1alpha2
   196  kind: ServiceProfile
   197  metadata:
   198    name: name.ns.svc.cluster.local
   199    namespace: linkerd-ns
   200  spec:
   201    routes:
   202    - name: name-1
   203      condition:
   204        method: GET
   205        pathRegex: /route-1
   206      responseClasses:
   207      - condition:`,
   208  		},
   209  		{
   210  			err: errors.New("ServiceProfile \"name.ns.svc.cluster.local\" has a response class with an invalid condition: A response match must have a field set"),
   211  			sp: `apiVersion: linkerd.io/v1alpha2
   212  kind: ServiceProfile
   213  metadata:
   214    name: name.ns.svc.cluster.local
   215    namespace: linkerd-ns
   216  spec:
   217    routes:
   218    - name: name-1
   219      condition:
   220        method: GET
   221        pathRegex: /route-1
   222      responseClasses:
   223      - condition:
   224          status:
   225            min: 500
   226            max: 599
   227          all:
   228          - status:`,
   229  		},
   230  		{
   231  			err: errors.New("ServiceProfile \"name.ns.svc.cluster.local\" has a response class with an invalid condition: Range maximum must be between 100 and 599, inclusive"),
   232  			sp: `apiVersion: linkerd.io/v1alpha2
   233  kind: ServiceProfile
   234  metadata:
   235    name: name.ns.svc.cluster.local
   236    namespace: linkerd-ns
   237  spec:
   238    routes:
   239    - name: name-1
   240      condition:
   241        method: GET
   242        pathRegex: /route-1
   243      responseClasses:
   244      - condition:
   245          status:
   246            min: 500
   247            max: 600`,
   248  		},
   249  		{
   250  			err: errors.New("ServiceProfile \"name.ns.svc.cluster.local\" has a response class with an invalid condition: Range maximum cannot be smaller than minimum"),
   251  			sp: `apiVersion: linkerd.io/v1alpha2
   252  kind: ServiceProfile
   253  metadata:
   254    name: name.ns.svc.cluster.local
   255    namespace: linkerd-ns
   256  spec:
   257    routes:
   258    - name: name-1
   259      condition:
   260        method: GET
   261        pathRegex: /route-1
   262      responseClasses:
   263      - condition:
   264          status:
   265            min: 500
   266            max: 599
   267          all:
   268          - status:
   269              min: 500
   270              max: 599
   271          - not:
   272              status:
   273                min: 300
   274                max: 200`,
   275  		},
   276  		{
   277  			err: errors.New("ServiceProfile \"name.ns.svc.cluster.local\" has a response class with an invalid condition: Range minimum must be between 100 and 599, inclusive"),
   278  			sp: `apiVersion: linkerd.io/v1alpha2
   279  kind: ServiceProfile
   280  metadata:
   281    name: name.ns.svc.cluster.local
   282    namespace: linkerd-ns
   283  spec:
   284    routes:
   285    - name: name-1
   286      condition:
   287        method: GET
   288        pathRegex: /route-1
   289      responseClasses:
   290      - condition:
   291          status:
   292            min: 500
   293            max: 599
   294          all:
   295          - status:
   296              min: 500
   297              max: 599
   298          - not:
   299              status:
   300                min: 1`,
   301  		},
   302  		{
   303  			err: errors.New("failed to validate ServiceProfile: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal bool into Go struct field Range.spec.routes.responseClasses.condition.all.not.status.min of type uint32"),
   304  			sp: `apiVersion: linkerd.io/v1alpha2
   305  kind: ServiceProfile
   306  metadata:
   307    name: name.ns.svc.cluster.local
   308    namespace: linkerd-ns
   309  spec:
   310    routes:
   311    - name: name-1
   312      condition:
   313        method: GET
   314        pathRegex: /route-1
   315      responseClasses:
   316      - condition:
   317          status:
   318            min: 500
   319            max: 599
   320          all:
   321          - status:
   322              min: 500
   323              max: 599
   324          - not:
   325              status:
   326                min: false`,
   327  		},
   328  		{
   329  			err: errors.New("ServiceProfile \"name.ns.svc.cluster.local\" RetryBudget missing TTL field"),
   330  			sp: `apiVersion: linkerd.io/v1alpha2
   331  kind: ServiceProfile
   332  metadata:
   333    name: name.ns.svc.cluster.local
   334    namespace: linkerd-ns
   335  spec:
   336    retryBudget:
   337      minRetriesPerSecond: 5
   338      retryRatio: 0.2
   339    routes:
   340    - name: name-1
   341      condition:
   342        method: GET
   343        pathRegex: /route-1`,
   344  		},
   345  		{
   346  			err: errors.New("ServiceProfile \"name.ns.svc.cluster.local\" RetryBudget: time: invalid duration \"foo\""),
   347  			sp: `apiVersion: linkerd.io/v1alpha2
   348  kind: ServiceProfile
   349  metadata:
   350    name: name.ns.svc.cluster.local
   351    namespace: linkerd-ns
   352  spec:
   353    retryBudget:
   354      minRetriesPerSecond: 5
   355      retryRatio: 0.2
   356      ttl: foo
   357    routes:
   358    - name: name-1
   359      condition:
   360        method: GET
   361        pathRegex: /route-1`,
   362  		},
   363  		{
   364  			err: errors.New("ServiceProfile \"name.ns.svc.cluster.local\" RetryBudget RetryRatio must be non-negative: -0.200000"),
   365  			sp: `apiVersion: linkerd.io/v1alpha2
   366  kind: ServiceProfile
   367  metadata:
   368    name: name.ns.svc.cluster.local
   369    namespace: linkerd-ns
   370  spec:
   371    retryBudget:
   372      minRetriesPerSecond: 5
   373      retryRatio: -0.2
   374      ttl: 10s
   375    routes:
   376    - name: name-1
   377      condition:
   378        method: GET
   379        pathRegex: /route-1`,
   380  		},
   381  		{
   382  			err: errors.New("failed to validate ServiceProfile: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal number -5 into Go struct field RetryBudget.spec.retryBudget.minRetriesPerSecond of type uint32"),
   383  			sp: `apiVersion: linkerd.io/v1alpha2
   384  kind: ServiceProfile
   385  metadata:
   386    name: name.ns.svc.cluster.local
   387    namespace: linkerd-ns
   388  spec:
   389    retryBudget:
   390      minRetriesPerSecond: -5
   391      retryRatio: 0.2
   392      ttl: 10s
   393    routes:
   394    - name: name-1
   395      condition:
   396        method: GET
   397        pathRegex: /route-1`,
   398  		},
   399  	}
   400  
   401  	for id, exp := range expectations {
   402  		exp := exp // pin
   403  		t.Run(fmt.Sprintf("%d", id), func(t *testing.T) {
   404  			err := Validate([]byte(exp.sp))
   405  			if err != nil || exp.err != nil {
   406  				if (err == nil && exp.err != nil) ||
   407  					(err != nil && exp.err == nil) ||
   408  					(err.Error() != exp.err.Error()) {
   409  					t.Fatalf("Unexpected error (Expected: %s, Got: %s)", exp.err, err)
   410  				}
   411  			}
   412  		})
   413  	}
   414  }
   415  

View as plain text