-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[KEP-0009] feat: add expression based assertions (#576)
This PR adds CEL-expression based assertions to `TestAsserts`. See https://github.com/kudobuilder/kuttl/blob/main/keps/0009-expression-based-assertions.md for more details. Signed-off-by: Kumar Mallikarjuna <[email protected]>
- Loading branch information
1 parent
69968d7
commit 947aa91
Showing
19 changed files
with
844 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package v1beta1 | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"strings" | ||
|
||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
"k8s.io/apimachinery/pkg/runtime/schema" | ||
"k8s.io/apimachinery/pkg/types" | ||
) | ||
|
||
var ( | ||
errAPIVersionInvalid = errors.New("apiVersion not of the format (<group>/)<version>") | ||
errKindNotSpecified = errors.New("kind not specified") | ||
errNameNotSpecified = errors.New("name not specified") | ||
errRefNotSpecified = errors.New("ref not specified") | ||
) | ||
|
||
func (t *TestResourceRef) BuildResourceReference() (namespacedName types.NamespacedName, referencedResource *unstructured.Unstructured) { | ||
referencedResource = &unstructured.Unstructured{} | ||
apiVersionSplit := strings.Split(t.APIVersion, "/") | ||
gvk := schema.GroupVersionKind{ | ||
Version: apiVersionSplit[len(apiVersionSplit)-1], | ||
Kind: t.Kind, | ||
} | ||
if len(apiVersionSplit) > 1 { | ||
gvk.Group = apiVersionSplit[0] | ||
} | ||
referencedResource.SetGroupVersionKind(gvk) | ||
|
||
namespacedName = types.NamespacedName{ | ||
Namespace: t.Namespace, | ||
Name: t.Name, | ||
} | ||
|
||
return | ||
} | ||
|
||
func (t *TestResourceRef) Validate() error { | ||
apiVersionSplit := strings.Split(t.APIVersion, "/") | ||
switch { | ||
case t.APIVersion == "" || len(apiVersionSplit) > 2: | ||
return errAPIVersionInvalid | ||
case t.Kind == "": | ||
return errKindNotSpecified | ||
case t.Name == "": | ||
return errNameNotSpecified | ||
case t.Ref == "": | ||
return errRefNotSpecified | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (t *TestResourceRef) String() string { | ||
return fmt.Sprintf( | ||
"apiVersion=%v, kind=%v, namespace=%v, name=%v, ref=%v", | ||
t.APIVersion, | ||
t.Kind, | ||
t.Namespace, | ||
t.Name, | ||
t.Ref, | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
package v1beta1 | ||
|
||
import ( | ||
"reflect" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
"k8s.io/apimachinery/pkg/runtime/schema" | ||
"k8s.io/apimachinery/pkg/types" | ||
) | ||
|
||
func TestValidate(t *testing.T) { | ||
testCases := []struct { | ||
name string | ||
testResourceRef TestResourceRef | ||
errored bool | ||
expectedError error | ||
}{ | ||
{ | ||
name: "apiVersion is not specified", | ||
testResourceRef: TestResourceRef{ | ||
Kind: "Pod", | ||
Namespace: "test", | ||
Name: "test-pod", | ||
Ref: "testPod", | ||
}, | ||
errored: true, | ||
expectedError: errAPIVersionInvalid, | ||
}, | ||
{ | ||
name: "apiVersion is invalid", | ||
testResourceRef: TestResourceRef{ | ||
APIVersion: "x/y/z", | ||
Kind: "Pod", | ||
Namespace: "test", | ||
Name: "test-pod", | ||
Ref: "testPod", | ||
}, | ||
errored: true, | ||
expectedError: errAPIVersionInvalid, | ||
}, | ||
{ | ||
name: "apiVersion is valid and group is vacuous", | ||
testResourceRef: TestResourceRef{ | ||
APIVersion: "v1", | ||
Kind: "Pod", | ||
Namespace: "test", | ||
Name: "test-pod", | ||
Ref: "testPod", | ||
}, | ||
errored: false, | ||
}, | ||
{ | ||
name: "apiVersion has both group name and version", | ||
testResourceRef: TestResourceRef{ | ||
APIVersion: "apps/v1", | ||
Kind: "Deployment", | ||
Namespace: "test", | ||
Name: "test-deployment", | ||
Ref: "testDeployment", | ||
}, | ||
errored: false, | ||
}, | ||
{ | ||
name: "kind is not specified", | ||
testResourceRef: TestResourceRef{ | ||
APIVersion: "apps/v1", | ||
Namespace: "test", | ||
Name: "test-deployment", | ||
Ref: "testDeployment", | ||
}, | ||
errored: true, | ||
expectedError: errKindNotSpecified, | ||
}, | ||
{ | ||
name: "name is not specified", | ||
testResourceRef: TestResourceRef{ | ||
APIVersion: "apps/v1", | ||
Kind: "Deployment", | ||
Namespace: "test", | ||
Ref: "testDeployment", | ||
}, | ||
errored: true, | ||
expectedError: errNameNotSpecified, | ||
}, | ||
{ | ||
name: "ref is not specified", | ||
testResourceRef: TestResourceRef{ | ||
APIVersion: "apps/v1", | ||
Kind: "Deployment", | ||
Namespace: "test", | ||
Name: "test-deployment", | ||
}, | ||
errored: true, | ||
expectedError: errRefNotSpecified, | ||
}, | ||
{ | ||
name: "all attributes are present and valid", | ||
testResourceRef: TestResourceRef{ | ||
APIVersion: "apps/v1", | ||
Kind: "Deployment", | ||
Namespace: "test", | ||
Name: "test-deployment", | ||
Ref: "testDeployment", | ||
}, | ||
errored: false, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
err := tc.testResourceRef.Validate() | ||
if !tc.errored { | ||
assert.NoError(t, err) | ||
} else { | ||
assert.ErrorIs(t, err, tc.expectedError) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestBuildResourceReference(t *testing.T) { | ||
buildObject := func(gvk schema.GroupVersionKind) *unstructured.Unstructured { | ||
obj := &unstructured.Unstructured{} | ||
obj.SetGroupVersionKind(gvk) | ||
return obj | ||
} | ||
|
||
testCases := []struct { | ||
name string | ||
testResourceRef TestResourceRef | ||
namespacedName types.NamespacedName | ||
resourceReference *unstructured.Unstructured | ||
}{ | ||
{ | ||
name: "group name is vacuous", | ||
testResourceRef: TestResourceRef{ | ||
APIVersion: "v1", | ||
Kind: "Pod", | ||
Namespace: "test", | ||
Name: "test-pod", | ||
Ref: "testPod", | ||
}, | ||
namespacedName: types.NamespacedName{ | ||
Namespace: "test", | ||
Name: "test-pod", | ||
}, | ||
resourceReference: buildObject(schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"}), | ||
}, | ||
{ | ||
name: "group name is present", | ||
testResourceRef: TestResourceRef{ | ||
APIVersion: "apps/v1", | ||
Kind: "Deployment", | ||
Namespace: "test", | ||
Name: "test-deployment", | ||
Ref: "testDeployment", | ||
}, | ||
namespacedName: types.NamespacedName{ | ||
Namespace: "test", | ||
Name: "test-deployment", | ||
}, | ||
resourceReference: buildObject(schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"}), | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
namspacedName, referencedResource := tc.testResourceRef.BuildResourceReference() | ||
assert.Equal(t, tc.namespacedName, namspacedName) | ||
assert.True( | ||
t, | ||
reflect.DeepEqual(tc.resourceReference, referencedResource), | ||
"constructed unstructured reference does not match, expected '%s', got '%s'", | ||
tc.resourceReference, | ||
referencedResource, | ||
) | ||
}) | ||
} | ||
} |
Oops, something went wrong.