Skip to content
This repository has been archived by the owner on Jul 17, 2023. It is now read-only.

Commit

Permalink
Merge pull request #1181 from cloudfoundry/chores/matcher-cleanup
Browse files Browse the repository at this point in the history
Chores/matcher cleanup
  • Loading branch information
andrewedstrom authored Jan 13, 2020
2 parents 2b5f6a8 + f6ef8bc commit 9ccfc69
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 56 deletions.
Empty file added k8s/templates/values/values.yml
Empty file.
52 changes: 52 additions & 0 deletions k8s/test/container_matcher_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package k8s_test

import (
"fmt"
"github.com/onsi/gomega"
"github.com/onsi/gomega/format"
"github.com/onsi/gomega/gstruct"
"github.com/onsi/gomega/types"
coreV1 "k8s.io/api/core/v1"
)

type ContainerMatcher struct {
fields map[string]types.GomegaMatcher

container *coreV1.Container
executed types.GomegaMatcher
}

func NewContainerMatcher() *ContainerMatcher {
return &ContainerMatcher{map[string]types.GomegaMatcher{}, nil, nil}
}

func (matcher *ContainerMatcher) WithName(name string) *ContainerMatcher {
matcher.fields["Name"] = gomega.Equal(name)

return matcher
}

func (matcher *ContainerMatcher) Match(actual interface{}) (bool, error) {
container, ok := actual.(coreV1.Container)
if !ok {
return false, fmt.Errorf("Expected a container. Got\n%s", format.Object(actual, 1))
}

matcher.container = &container
matcher.executed = gstruct.MatchFields(gstruct.IgnoreExtras, matcher.fields)
return matcher.executed.Match(container)
}

func (matcher *ContainerMatcher) FailureMessage(actual interface{}) string {
return fmt.Sprintf(
"At least one container should match: \n%s",
matcher.executed.FailureMessage(&matcher.container),
)
}

func (matcher *ContainerMatcher) NegatedFailureMessage(actual interface{}) string {
return fmt.Sprintf(
"No container should match: \n%s",
matcher.executed.FailureMessage(&matcher.container),
)
}
48 changes: 48 additions & 0 deletions k8s/test/deployment_matcher_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package k8s_test

import (
"fmt"
"github.com/onsi/gomega/format"
"github.com/onsi/gomega/types"
appV1 "k8s.io/api/apps/v1"
)

type PodMatcherConfig func(*PodMatcher)

type DeploymentMatcher struct {
pod *PodMatcher

executed types.GomegaMatcher
}

func RepresentingDeployment() *DeploymentMatcher {
return &DeploymentMatcher{NewPodMatcher(), nil}
}

func (matcher *DeploymentMatcher) WithPodMatching(config PodMatcherConfig) *DeploymentMatcher {
config(matcher.pod)

return matcher
}

func (matcher *DeploymentMatcher) Match(actual interface{}) (bool, error) {
deployment, ok := actual.(*appV1.Deployment)
if !ok {
return false, fmt.Errorf("Expected a deployment. Got\n%s", format.Object(actual, 1))
}

matcher.executed = matcher.pod
if pass, err := matcher.pod.Match(deployment.Spec.Template); !pass || err != nil {
return pass, err
}

return true, nil
}

func (matcher *DeploymentMatcher) FailureMessage(actual interface{}) string {
return matcher.executed.FailureMessage(actual)
}

func (matcher *DeploymentMatcher) NegatedFailureMessage(actual interface{}) string {
return matcher.executed.NegatedFailureMessage(actual)
}
12 changes: 10 additions & 2 deletions k8s/test/deployment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,17 @@ var _ = Describe("Deployment", func() {
valuesPath = pathToTemplate(filepath.Join("values", "values.yml"))
})

It("Constructs the YAML from a set of files", func() {
It("Renders a deployment for the UAA", func() {
ctx := NewRenderingContext(deploymentPath, valuesPath)

Expect(ctx).To(ProduceYAML(RepresentingContainer("uaa")))
Expect(ctx).To(
ProduceYAML(
RepresentingDeployment().WithPodMatching(func(pod *PodMatcher) {
pod.WithContainerMatching(func(container *ContainerMatcher) {
container.WithName("uaa")
})
}),
),
)
})
})
55 changes: 55 additions & 0 deletions k8s/test/pod_matcher_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package k8s_test

import (
"fmt"
"github.com/onsi/gomega"
"github.com/onsi/gomega/format"
"github.com/onsi/gomega/types"
coreV1 "k8s.io/api/core/v1"
)

type ContainerMatcherConfig func(*ContainerMatcher)

type PodMatcher struct {
containers []types.GomegaMatcher

executed types.GomegaMatcher
}

func NewPodMatcher() *PodMatcher {
return &PodMatcher{[]types.GomegaMatcher{}, nil}
}

func (matcher *PodMatcher) WithContainerMatching(config ContainerMatcherConfig) *PodMatcher {
container := NewContainerMatcher()
config(container)
matcher.containers = append(matcher.containers, container)

return matcher
}

func (matcher *PodMatcher) Match(actual interface{}) (bool, error) {
pod, ok := actual.(coreV1.PodTemplateSpec)
if !ok {
return false, fmt.Errorf("Expected pod. Got\n%s", format.Object(actual, 1))
}

for _, container := range matcher.containers {
contains := gomega.ContainElement(container)

matcher.executed = container
if pass, err := contains.Match(pod.Spec.Containers); !pass || err != nil {
return pass, err
}
}

return true, nil
}

func (matcher *PodMatcher) FailureMessage(actual interface{}) string {
return matcher.executed.FailureMessage(actual)
}

func (matcher *PodMatcher) NegatedFailureMessage(actual interface{}) string {
return matcher.executed.NegatedFailureMessage(actual)
}
70 changes: 16 additions & 54 deletions k8s/test/matchers_test.go → k8s/test/produce_yaml_matcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import (
"github.com/onsi/gomega/gbytes"
"github.com/onsi/gomega/gexec"
"github.com/onsi/gomega/types"
appV1 "k8s.io/api/apps/v1"
coreV1 "k8s.io/api/core/v1"
"k8s.io/client-go/kubernetes/scheme"
"os/exec"
)
Expand All @@ -28,11 +26,12 @@ func NewRenderingContext(templates ...string) RenderingContext {
}

type ProduceYAMLMatcher struct {
matcher types.GomegaMatcher
matcher types.GomegaMatcher
rendered string
}

func ProduceYAML(matcher types.GomegaMatcher) *ProduceYAMLMatcher {
return &ProduceYAMLMatcher{matcher}
return &ProduceYAMLMatcher{matcher, ""}
}

func (matcher *ProduceYAMLMatcher) Match(actual interface{}) (bool, error) {
Expand All @@ -46,6 +45,7 @@ func (matcher *ProduceYAMLMatcher) Match(actual interface{}) (bool, error) {
return false, err
}

matcher.rendered = string(session.Out.Contents())
obj, err := parseYAML(session.Out)
if err != nil {
return false, err
Expand All @@ -55,11 +55,21 @@ func (matcher *ProduceYAMLMatcher) Match(actual interface{}) (bool, error) {
}

func (matcher *ProduceYAMLMatcher) FailureMessage(actual interface{}) string {
return matcher.matcher.FailureMessage(actual)
msg := fmt.Sprintf(
"There is a problem with this YAML:\n\n%s\n\n%s",
matcher.rendered,
matcher.matcher.FailureMessage(actual),
)
return msg
}

func (matcher *ProduceYAMLMatcher) NegatedFailureMessage(actual interface{}) string {
return matcher.matcher.NegatedFailureMessage(actual)
msg := fmt.Sprintf(
"There is a problem with this YAML:\n\n%s\n\n%s",
matcher.rendered,
matcher.matcher.NegatedFailureMessage(actual),
)
return msg
}

func renderWithData(templates []string, data map[string]string) (*gexec.Session, error) {
Expand Down Expand Up @@ -90,51 +100,3 @@ func parseYAML(yaml *gbytes.Buffer) (interface{}, error) {

return obj, nil
}

type ContainerExpectation func(coreV1.Container) error

type RepresentingContainerMatcher struct {
name string
tests []ContainerExpectation
err error
}

func RepresentingContainer(name string) *RepresentingContainerMatcher {
return &RepresentingContainerMatcher{name, nil, nil}
}

func (matcher *RepresentingContainerMatcher) Match(actual interface{}) (bool, error) {
deployment, ok := actual.(*appV1.Deployment)
if !ok {
return false, fmt.Errorf("RepresentingContainer must be passed a deployment. Got\n%s", format.Object(actual, 1))
}

var selected *coreV1.Container
for _, c := range deployment.Spec.Template.Spec.Containers {
if c.Name == matcher.name {
selected = &c
}
}

if selected == nil {
matcher.err = fmt.Errorf("Expected container named %s, but did not find one", matcher.name)
return false, nil
}

for _, test := range matcher.tests {
if err := test(*selected); err != nil {
matcher.err = err
return false, nil
}
}

return true, nil
}

func (matcher *RepresentingContainerMatcher) FailureMessage(actual interface{}) string {
return fmt.Sprintf("Container did not match expectation: %v", matcher.err)
}

func (matcher *RepresentingContainerMatcher) NegatedFailureMessage(actual interface{}) string {
return fmt.Sprintf("Container should not to match expectation: %v", matcher.err)
}

0 comments on commit 9ccfc69

Please sign in to comment.