Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rework label selector and resource identifier type #3

Merged
merged 3 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 30 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,22 +141,17 @@ matches some expectation:
allows you to override the `defaults.namespace` value from the test scenario.
* `kube`: (optional) an object containing actions and assertions the test takes
against the Kubernetes API server.
* `kube.get`: (optional) string containing either a resource specifier (e.g.
`pods`, `po/nginx` or a file path to a YAML manifest containing resources
that will be read from the Kubernetes API server.
* `kube.get`: (optional) string or object containing a resource identifier
(e.g. `pods`, `po/nginx` or label selector for resources that will be read
from the Kubernetes API server.
* `kube.create`: (optional) string containing either a file path to a YAML
manifest or a string of raw YAML containing the resource(s) to create.
* `kube.apply`: (optional) string containing either a file path to a YAML
manifest or a string of raw YAML containing the resource(s) for which
`gdt-kube` will perform a Kubernetes Apply call.
* `kube.delete`: (optional) string containing either a resource specifier (e.g.
`pods`, `po/nginx` or a file path to a YAML manifest containing resources
that will be deleted.
* `kube.with`: (optional) object containing selectors with which to filter
`get` and `delete` operations.
* `kube.with.labels`: (optional) `map[string]string` containing the label keys
and values to use in constructing an equality label selector (for all listed
labels)
* `kube.delete`: (optional) string or object containing either a resource
identifier (e.g. `pods`, `po/nginx` , a file path to a YAML manifest, or a
label selector for resources that will be deleted.
* `assert`: (optional) object containing assertions to make about the
action performed by the test.
* `assert.error`: (optional) string to match a returned error from the
Expand Down Expand Up @@ -236,14 +231,34 @@ Testing that there are two Pods having the label `app:nginx`:
```yaml
name: list-pods-with-labels
tests:
# You can use the shortcut kube.get
- name: verify-pods-with-app-nginx-label
kube.get:
type: pods
labels:
app: nginx
assert:
len: 2
# Or the long-form kube:get
- name: verify-pods-with-app-nginx-label
kube:
get: pods
with:
get:
type: pods
labels:
app: nginx
assert:
len: 2
# Like "kube.get", you can pass a label selector for "kube.delete"
- kube.delete:
type: pods
labels:
app: nginx
# And you can use the long-form kube:delete as well
- kube:
delete:
type: pods
labels:
app: nginx
```

Testing that a Pod with the name `nginx` exists by the specified timeout
Expand All @@ -253,9 +268,8 @@ the timeout):
```yaml
name: test-nginx-pod-exists-within-1-minute
tests:
- kube:
get: pods/nginx
timeout: 1m
- kube.get: pods/nginx
timeout: 1m
```

Testing creation and subsequent fetch then delete of a Pod, specifying the Pod
Expand Down
40 changes: 40 additions & 0 deletions action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Use and distribution licensed under the Apache license version 2.
//
// See the COPYING file in the root project directory for full text.

package kube

// Action describes the the Kubernetes-specific action that is performed by the
// test.
type Action struct {
// Create is a string containing a file path or raw YAML content describing
// a Kubernetes resource to call `kubectl create` with.
Create string `yaml:"create,omitempty"`
// Apply is a string containing a file path or raw YAML content describing
// a Kubernetes resource to call `kubectl apply` with.
Apply string `yaml:"apply,omitempty"`
// Delete is a string or object containing arguments to `kubectl delete`.
//
// It must be one of the following:
//
// - a file path to a manifest that will be read and the resources
// described in the manifest will be deleted
// - a resource kind or kind alias, e.g. "pods", "po", followed by one of
// the following:
// * a space or `/` character followed by the resource name to delete
// only a resource with that name.
// - an object with a `type` and optional `labels` field containing a label
// selector that should be used to select that `type` of resource.
Delete *ResourceIdentifierOrFile `yaml:"delete,omitempty"`
// Get is a string or object containing arguments to `kubectl get`.
//
// It must be one of the following:
//
// - a string with a resource kind or kind alias, e.g. "pods", "po",
// followed by one of the following:
// * a space or `/` character followed by the resource name to get only a
// resource with that name.
// - an object with a `type` and optional `labels` field containing a label
// selector that should be used to select that `type` of resource.
Get *ResourceIdentifier `yaml:"get,omitempty"`
}
5 changes: 0 additions & 5 deletions connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,6 @@ func (s *Spec) Config(ctx context.Context) (*rest.Config, error) {
).ClientConfig()
}

type groupVersion struct {
group string
version string
}

// connection is a struct containing a discovery client and a dynamic client
// that the Spec uses to communicate with Kubernetes.
type connection struct {
Expand Down
47 changes: 33 additions & 14 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,6 @@ var (
"or a string with embedded YAML",
gdterrors.ErrParse,
)
// ErrMoreThanOneShortcut is returned when the test author included
// more than one shortcut (e.g. `kube.create` or `kube.apply`) in the same
// test spec.
ErrMoreThanOneShortcut = fmt.Errorf(
"%w: you may only specify a single shortcut field (e.g. "+
"`kube.create` or `kube.apply`",
gdterrors.ErrParse,
)
// ErrEitherShortcutOrKubeSpec is returned when the test author
// included both a shortcut (e.g. `kube.create` or `kube.apply`) AND the
// long-form `kube` object in the same test spec.
Expand Down Expand Up @@ -128,6 +120,24 @@ var (
)
)

// EitherShortcutOrKubeSpecAt returns ErrEitherShortcutOrKubeSpec for a given
// YAML node
func EitherShortcutOrKubeSpecAt(node *yaml.Node) error {
return fmt.Errorf(
"%w at line %d, column %d",
ErrEitherShortcutOrKubeSpec, node.Line, node.Column,
)
}

// MoreThanOneKubeActionAt returns ErrMoreThanOneKubeAction for a given YAML
// node
func MoreThanOneKubeActionAt(node *yaml.Node) error {
return fmt.Errorf(
"%w at line %d, column %d",
ErrMoreThanOneKubeAction, node.Line, node.Column,
)
}

// ExpectedMapOrYAMLStringAt returns ErrExpectedMapOrYAMLString for a given
// YAML node
func ExpectedMapOrYAMLStringAt(node *yaml.Node) error {
Expand All @@ -144,22 +154,31 @@ func KubeConfigNotFound(path string) error {

// InvalidResourceSpecifier returns ErrResourceSpecifier for a given
// supplied resource specifier.
func InvalidResourceSpecifier(subject string) error {
return fmt.Errorf("%w: %s", ErrResourceSpecifierInvalid, subject)
func InvalidResourceSpecifier(subject string, node *yaml.Node) error {
return fmt.Errorf(
"%w: %s at line %d, column %d",
ErrResourceSpecifierInvalid, subject, node.Line, node.Column,
)
}

// InvalidResourceSpecifierOrFilepath returns
// ErrResourceSpecifierOrFilepath for a given supplied subject.
func InvalidResourceSpecifierOrFilepath(subject string) error {
func InvalidResourceSpecifierOrFilepath(
subject string, node *yaml.Node,
) error {
return fmt.Errorf(
"%w: %s", ErrResourceSpecifierInvalidOrFilepath, subject,
"%w: %s at line %d, column %d",
ErrResourceSpecifierInvalidOrFilepath, subject, node.Line, node.Column,
)
}

// InvalidWithLabels returns ErrWithLabels with an error containing more
// context.
func InvalidWithLabels(err error) error {
return fmt.Errorf("%w: %s", ErrWithLabelsInvalid, err)
func InvalidWithLabels(err error, node *yaml.Node) error {
return fmt.Errorf(
"%w: %s at line %d, column %d",
ErrWithLabelsInvalid, err, node.Line, node.Column,
)
}

// ResourceUnknown returns ErrRuntimeResourceUnknown for a given kind
Expand Down
30 changes: 17 additions & 13 deletions eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ func (s *Spec) Eval(ctx context.Context, t *testing.T) *result.Result {
}
var res *result.Result
t.Run(s.Title(), func(t *testing.T) {
if s.Kube.Get != "" {
if s.Kube.Get != nil {
res = s.get(ctx, t, c)
}
if s.Kube.Create != "" {
res = s.create(ctx, t, c)
}
if s.Kube.Delete != "" {
if s.Kube.Delete != nil {
res = s.delete(ctx, t, c)
}
if s.Kube.Apply != "" {
Expand Down Expand Up @@ -83,7 +83,7 @@ func (s *Spec) get(
t *testing.T,
c *connection,
) *result.Result {
kind, name := splitKindName(s.Kube.Get)
kind, name := s.Kube.Get.KindName()
gvk := schema.GroupVersionKind{
Kind: kind,
}
Expand Down Expand Up @@ -146,12 +146,10 @@ func (s *Spec) doList(
namespace string,
) gdttypes.Assertions {
opts := metav1.ListOptions{}
with := s.Kube.With
if with != nil {
if with.Labels != nil {
// We already validated the label selector during parse-time
opts.LabelSelector = labels.Set(with.Labels).String()
}
withlabels := s.Kube.Get.Labels()
if withlabels != nil {
// We already validated the label selector during parse-time
opts.LabelSelector = labels.Set(withlabels).String()
}
list, err := c.client.Resource(res).Namespace(namespace).List(
ctx, opts,
Expand Down Expand Up @@ -346,8 +344,8 @@ func (s *Spec) delete(
t *testing.T,
c *connection,
) *result.Result {
if probablyFilePath(s.Kube.Delete) {
path := s.Kube.Delete
if s.Kube.Delete.FilePath() != "" {
path := s.Kube.Delete.FilePath()
f, err := os.Open(path)
if err != nil {
// This should never happen because we check during parse time
Expand Down Expand Up @@ -385,7 +383,7 @@ func (s *Spec) delete(
return result.New()
}

kind, name := splitKindName(s.Kube.Delete)
kind, name := s.Kube.Delete.KindName()
gvk := schema.GroupVersionKind{
Kind: kind,
}
Expand Down Expand Up @@ -428,10 +426,16 @@ func (s *Spec) doDeleteCollection(
res schema.GroupVersionResource,
namespace string,
) *result.Result {
listOpts := metav1.ListOptions{}
withlabels := s.Kube.Delete.Labels()
if withlabels != nil {
// We already validated the label selector during parse-time
listOpts.LabelSelector = labels.Set(withlabels).String()
}
err := c.client.Resource(res).Namespace(namespace).DeleteCollection(
ctx,
metav1.DeleteOptions{},
metav1.ListOptions{},
listOpts,
)
a := newAssertions(s.Assert, err, nil)
return result.New(result.WithFailures(a.Failures()...))
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.19

require (
github.com/cenkalti/backoff/v4 v4.2.1
github.com/gdt-dev/gdt v1.1.1
github.com/gdt-dev/gdt v1.2.1
github.com/samber/lo v1.38.1
github.com/stretchr/testify v1.8.4
gopkg.in/yaml.v3 v3.0.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww=
github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4=
github.com/gdt-dev/gdt v1.1.1 h1:863WjQr2Oa+1eVKJspw1SRqW72S0Y3UydN0j8WwPYJU=
github.com/gdt-dev/gdt v1.1.1/go.mod h1:StnyGjC/67u59La2u6fh3HwW9MmodVhKdXcLlkgvNSY=
github.com/gdt-dev/gdt v1.2.1 h1:tNIpBPLatk8Rb0YFSK+FOzKIhHPYgmLpXQL8qottNcI=
github.com/gdt-dev/gdt v1.2.1/go.mod h1:StnyGjC/67u59La2u6fh3HwW9MmodVhKdXcLlkgvNSY=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
Expand Down
Loading