diff --git a/pkg/profiles/profiles.go b/pkg/profiles/profiles.go index b2038ef6ac553..348fcefd40033 100644 --- a/pkg/profiles/profiles.go +++ b/pkg/profiles/profiles.go @@ -38,6 +38,8 @@ var ( minStatus uint32 = 100 maxStatus uint32 = 599 + errNilMatchAll = fmt.Errorf("null condition \"all\"") + errNilMatchAny = fmt.Errorf("null condition \"any\"") errRequestMatchField = errors.New("A request match must have a field set") errResponseMatchField = errors.New("A response match must have a field set") ) @@ -66,6 +68,9 @@ func Validate(data []byte) error { } for _, route := range serviceProfile.Spec.Routes { + if route == nil { + return fmt.Errorf("ServiceProfile %q has a null route", serviceProfile.Name) + } if route.Name == "" { return fmt.Errorf("ServiceProfile %q has a route with no name", serviceProfile.Name) } @@ -83,6 +88,9 @@ func Validate(data []byte) error { return fmt.Errorf("ServiceProfile %q has a route with an invalid condition: %w", serviceProfile.Name, err) } for _, rc := range route.ResponseClasses { + if rc == nil { + return fmt.Errorf("ServiceProfile %q has a null response class", serviceProfile.Name) + } if rc.Condition == nil { return fmt.Errorf("ServiceProfile %q has a response class with no condition", serviceProfile.Name) } @@ -119,6 +127,9 @@ func ValidateRequestMatch(reqMatch *sp.RequestMatch) error { if reqMatch.All != nil { matchKindSet = true for _, child := range reqMatch.All { + if child == nil { + return errNilMatchAll + } err := ValidateRequestMatch(child) if err != nil { return err @@ -128,6 +139,9 @@ func ValidateRequestMatch(reqMatch *sp.RequestMatch) error { if reqMatch.Any != nil { matchKindSet = true for _, child := range reqMatch.Any { + if child == nil { + return errNilMatchAny + } err := ValidateRequestMatch(child) if err != nil { return err @@ -162,6 +176,9 @@ func ValidateResponseMatch(rspMatch *sp.ResponseMatch) error { if rspMatch.All != nil { matchKindSet = true for _, child := range rspMatch.All { + if child == nil { + return errNilMatchAll + } err := ValidateResponseMatch(child) if err != nil { return err @@ -171,6 +188,9 @@ func ValidateResponseMatch(rspMatch *sp.ResponseMatch) error { if rspMatch.Any != nil { matchKindSet = true for _, child := range rspMatch.Any { + if child == nil { + return errNilMatchAny + } err := ValidateResponseMatch(child) if err != nil { return err diff --git a/pkg/profiles/profiles_test.go b/pkg/profiles/profiles_test.go index d33b39467ae1e..1eb00565e2c58 100644 --- a/pkg/profiles/profiles_test.go +++ b/pkg/profiles/profiles_test.go @@ -396,6 +396,137 @@ spec: method: GET pathRegex: /route-1`, }, + { + err: errors.New(`ServiceProfile "name.ns.svc.cluster.local" has a null route`), + sp: `apiVersion: linkerd.io/v1alpha2 +kind: ServiceProfile +metadata: + name: name.ns.svc.cluster.local + namespace: linkerd-ns +spec: + routes: + -`, + }, + { + err: errors.New(`ServiceProfile "name.ns.svc.cluster.local" has a route with an invalid condition: null condition "all"`), + 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 + all: + -`, + }, + { + err: errors.New(`ServiceProfile "name.ns.svc.cluster.local" has a route with an invalid condition: null condition "any"`), + 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 + any: + -`, + }, + { + err: errors.New(`ServiceProfile "name.ns.svc.cluster.local" has a null response class`), + 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: + -`, + }, + { + err: errors.New(`ServiceProfile "name.ns.svc.cluster.local" has a response class with an invalid condition: null condition "all"`), + 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: + -`, + }, + { + err: errors.New(`ServiceProfile "name.ns.svc.cluster.local" has a response class with an invalid condition: null condition "any"`), + 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 + any: + -`, + }, + { + 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, + timeout: 1ns`, + }, + { + err: errors.New(`ServiceProfile "name.ns.svc.cluster.local" has a route with an invalid timeout: time: invalid duration "one-second"`), + 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, + timeout: one-second`, + }, } for id, exp := range expectations {