linkerd2/pkg/profiles/profiles_test.go

424 lines
9.9 KiB
Go

package profiles
import (
"errors"
"fmt"
"testing"
)
type spExp struct {
err error
sp string
}
func TestValidate(t *testing.T) {
expectations := []spExp{
{
err: nil,
sp: `apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: name.ns.svc.cluster.local
namespace: linkerd-ns
spec:
routes:
- name: name-1
condition:
method: GET
pathRegex: /route-1`,
},
{
err: nil,
sp: `apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: name.ns.svc.cluster.local
namespace: linkerd-ns
spec:
retryBudget:
minRetriesPerSecond: 5
retryRatio: 0.2
ttl: 10ms
routes:
- name: name-1
condition:
method: GET
pathRegex: /route-1
any:
- all:
- method: POST
- pathRegex: '/authors/\d+'
- all:
- not:
method: DELETE
- pathRegex: /info.txt
responseClasses:
- condition:
status:
min: 500
max: 599
all:
- status:
min: 500
max: 599
- not:
status:
min: 503`,
},
{
err: errors.New("ServiceProfile \"^.^\" has invalid name: a DNS-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])?)*')"),
sp: `apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: ^.^
namespace: linkerd-ns
spec:
routes:
- name: name-1
condition:
method: GET
pathRegex: /route-1`,
},
{
err: errors.New("failed to validate ServiceProfile: error unmarshaling JSON: while decoding JSON: json: unknown field \"foo\""),
sp: `apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: name.ns.svc.cluster.local
namespace: linkerd-ns
spec:
foo: bar
routes:
- name: name-1
condition:
method: GET
pathRegex: /route-1`,
},
{
err: errors.New("failed to validate ServiceProfile: error unmarshaling JSON: while decoding JSON: json: unknown field \"foo\""),
sp: `apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: name.ns.svc.cluster.local
namespace: linkerd-ns
spec:
routes:
- name: name-1
foo: bar
condition:
method: GET
pathRegex: /route-1`,
},
{
err: errors.New("failed to validate ServiceProfile: error unmarshaling JSON: while decoding JSON: json: unknown field \"foo\""),
sp: `apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: name.ns.svc.cluster.local
namespace: linkerd-ns
spec:
routes:
- name: name-1
condition:
foo: bar
method: GET
pathRegex: /route-1`,
},
{
err: errors.New("ServiceProfile \"name.ns.svc.cluster.local\" has no routes"),
sp: `apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: name.ns.svc.cluster.local
namespace: linkerd-ns
spec:`,
},
{
err: errors.New("ServiceProfile \"name.ns.svc.cluster.local\" has a route with no condition"),
sp: `apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: name.ns.svc.cluster.local
namespace: linkerd-ns
spec:
routes:
- name: name-1`,
},
{
err: errors.New("ServiceProfile \"name.ns.svc.cluster.local\" has a route with no name"),
sp: `apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: name.ns.svc.cluster.local
namespace: linkerd-ns
spec:
routes:
- condition:
method: GET
pathRegex: /route-1`,
},
{
err: errors.New("failed to validate ServiceProfile: error unmarshaling JSON: while decoding JSON: json: unknown field \"foo\""),
sp: `apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: name.ns.svc.cluster.local
namespace: linkerd-ns
spec:
routes:
- name: name-1
condition:
foo: bar
method: GET
pathRegex: /route-1
not:
method: GET`,
},
{
err: errors.New("ServiceProfile \"name.ns.svc.cluster.local\" has a route with no condition"),
sp: `apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: name.ns.svc.cluster.local
namespace: linkerd-ns
spec:
routes:
- name: name-1
condition:`,
},
{
err: errors.New("ServiceProfile \"name.ns.svc.cluster.local\" has a route with an invalid condition: A request match must have a field set"),
sp: `apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: name.ns.svc.cluster.local
namespace: linkerd-ns
spec:
routes:
- name: name-1
condition:
method:`,
},
{
err: errors.New("ServiceProfile \"name.ns.svc.cluster.local\" has a response class with no condition"),
sp: `apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: name.ns.svc.cluster.local
namespace: linkerd-ns
spec:
routes:
- name: name-1
condition:
method: GET
pathRegex: /route-1
responseClasses:
- condition:`,
},
{
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"),
sp: `apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: name.ns.svc.cluster.local
namespace: linkerd-ns
spec:
routes:
- name: name-1
condition:
method: GET
pathRegex: /route-1
responseClasses:
- condition:
status:
min: 500
max: 599
all:
- status:`,
},
{
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"),
sp: `apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: name.ns.svc.cluster.local
namespace: linkerd-ns
spec:
routes:
- name: name-1
condition:
method: GET
pathRegex: /route-1
responseClasses:
- condition:
status:
min: 500
max: 600`,
},
{
err: errors.New("ServiceProfile \"name.ns.svc.cluster.local\" has a response class with an invalid condition: Range maximum cannot be smaller than minimum"),
sp: `apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: name.ns.svc.cluster.local
namespace: linkerd-ns
spec:
routes:
- name: name-1
condition:
method: GET
pathRegex: /route-1
responseClasses:
- condition:
status:
min: 500
max: 599
all:
- status:
min: 500
max: 599
- not:
status:
min: 300
max: 200`,
},
{
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"),
sp: `apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: name.ns.svc.cluster.local
namespace: linkerd-ns
spec:
routes:
- name: name-1
condition:
method: GET
pathRegex: /route-1
responseClasses:
- condition:
status:
min: 500
max: 599
all:
- status:
min: 500
max: 599
- not:
status:
min: 1`,
},
{
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"),
sp: `apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: name.ns.svc.cluster.local
namespace: linkerd-ns
spec:
routes:
- name: name-1
condition:
method: GET
pathRegex: /route-1
responseClasses:
- condition:
status:
min: 500
max: 599
all:
- status:
min: 500
max: 599
- not:
status:
min: false`,
},
{
err: errors.New("ServiceProfile \"name.ns.svc.cluster.local\" RetryBudget missing TTL field"),
sp: `apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: name.ns.svc.cluster.local
namespace: linkerd-ns
spec:
retryBudget:
minRetriesPerSecond: 5
retryRatio: 0.2
routes:
- name: name-1
condition:
method: GET
pathRegex: /route-1`,
},
{
err: errors.New("ServiceProfile \"name.ns.svc.cluster.local\" RetryBudget: time: invalid duration foo"),
sp: `apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: name.ns.svc.cluster.local
namespace: linkerd-ns
spec:
retryBudget:
minRetriesPerSecond: 5
retryRatio: 0.2
ttl: foo
routes:
- name: name-1
condition:
method: GET
pathRegex: /route-1`,
},
{
err: errors.New("ServiceProfile \"name.ns.svc.cluster.local\" RetryBudget RetryRatio must be non-negative: -0.200000"),
sp: `apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: name.ns.svc.cluster.local
namespace: linkerd-ns
spec:
retryBudget:
minRetriesPerSecond: 5
retryRatio: -0.2
ttl: 10s
routes:
- name: name-1
condition:
method: GET
pathRegex: /route-1`,
},
{
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"),
sp: `apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: name.ns.svc.cluster.local
namespace: linkerd-ns
spec:
retryBudget:
minRetriesPerSecond: -5
retryRatio: 0.2
ttl: 10s
routes:
- name: name-1
condition:
method: GET
pathRegex: /route-1`,
},
}
for id, exp := range expectations {
exp := exp // pin
t.Run(fmt.Sprintf("%d", id), func(t *testing.T) {
err := Validate([]byte(exp.sp))
if err != nil || exp.err != nil {
if (err == nil && exp.err != nil) ||
(err != nil && exp.err == nil) ||
(err.Error() != exp.err.Error()) {
t.Fatalf("Unexpected error (Expected: %s, Got: %s)", exp.err, err)
}
}
})
}
}