Skip to content

Commit

Permalink
feat: add conditional matcher
Browse files Browse the repository at this point in the history
  • Loading branch information
mhrabovcin committed May 16, 2023
1 parent a08d1a1 commit efa8afc
Show file tree
Hide file tree
Showing 8 changed files with 266 additions and 1 deletion.
1 change: 1 addition & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ golang 1.20.2
golangci-lint 1.51.2
pre-commit 3.1.1
goreleaser 1.16.1
mockery 2.27.1
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ export DOCKER_REGISTRY ?= ghcr.io
export DOCKERHUB_ORG ?= mhrabovcin
export GIT_TREE_STATE ?=

.PHONY: gogenerate
gogenerate:
go generate ./...

.PHONY: test
test: tools.gotestsum
test: tools.gotestsum gogenerate
gotestsum --format pkgname --junitfile unit-tests.xml --jsonfile test.json -- -coverprofile=cover.out ./... && \
go tool cover -func=cover.out

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/ulikunitz/xz v0.5.9 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
golang.org/x/net v0.7.0 // indirect
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
Expand Down
55 changes: 55 additions & 0 deletions pkg/rewriter/conditional.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package rewriter

import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
)

type conditionalRewriter struct {
rewriter ResourceRewriter
condition Condition
}

var _ ResourceRewriter = (*conditionalRewriter)(nil)

// When executes provided rewriter only if condition matches
func When(condition Condition, rewriter ResourceRewriter) ResourceRewriter {
return &conditionalRewriter{
rewriter: rewriter,
condition: condition,
}
}

func (r *conditionalRewriter) BeforeImport(u *unstructured.Unstructured) error {
if r.condition(u) {
return r.rewriter.BeforeImport(u)
}
return nil
}

func (r *conditionalRewriter) BeforeServing(u *unstructured.Unstructured) error {
if r.condition(u) {
return r.rewriter.BeforeServing(u)
}
return nil
}

// Condition is function that returns bool with condition match result.
type Condition func(*unstructured.Unstructured) bool

// MatchGVK checks if resource matches given GroupVersionKind
func MatchGVK(gvk schema.GroupVersionKind) Condition {
return func(u *unstructured.Unstructured) bool {
apiVersion, kind := gvk.ToAPIVersionAndKind()

if u.GetAPIVersion() != apiVersion {
return false
}

if u.GetKind() != kind {
return false
}

return true
}
}
80 changes: 80 additions & 0 deletions pkg/rewriter/conditional_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package rewriter_test

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"

"github.com/mhrabovcin/troubleshoot-live/pkg/rewriter"
"github.com/mhrabovcin/troubleshoot-live/pkg/rewriter/mocks"
)

var (
anyUnstructured = mock.MatchedBy(func(_ *unstructured.Unstructured) bool { return true })
matchAny = func(u *unstructured.Unstructured) bool { return true }
matchNone = func(u *unstructured.Unstructured) bool { return false }
)

func TestWhen_Matching(t *testing.T) {
rrMock := mocks.NewResourceRewriter(t)
rrMock.EXPECT().BeforeImport(anyUnstructured).Return(nil).Once()
rrMock.EXPECT().BeforeServing(anyUnstructured).Return(nil).Once()
assert.NoError(t, rewriter.When(matchAny, rrMock).BeforeImport(&unstructured.Unstructured{}))
assert.NoError(t, rewriter.When(matchAny, rrMock).BeforeServing(&unstructured.Unstructured{}))
}

func TestWhen_Matching_Error(t *testing.T) {
rrMock := mocks.NewResourceRewriter(t)
rrMock.EXPECT().BeforeImport(anyUnstructured).Return(fmt.Errorf("test")).Once()
rrMock.EXPECT().BeforeServing(anyUnstructured).Return(fmt.Errorf("serving")).Once()
assert.ErrorContains(t, rewriter.When(matchAny, rrMock).BeforeImport(&unstructured.Unstructured{}), "test")
assert.ErrorContains(t, rewriter.When(matchAny, rrMock).BeforeServing(&unstructured.Unstructured{}), "serving")
}

func TestWhen_NoMatch(t *testing.T) {
rrMock := mocks.NewResourceRewriter(t)
assert.NoError(t, rewriter.When(matchNone, rrMock).BeforeImport(&unstructured.Unstructured{}))
assert.NoError(t, rewriter.When(matchNone, rrMock).BeforeServing(&unstructured.Unstructured{}))
}

func TestCondition_MatchGVK(t *testing.T) {
testCases := []struct {
config schema.GroupVersionKind
apiVersion string
kind string
expected bool
}{
{
config: schema.FromAPIVersionAndKind("v1", "Pod"),
apiVersion: "v1",
kind: "Pod",
expected: true,
},
{
config: schema.FromAPIVersionAndKind("scheduling.k8s.io/v1beta1", "PriorityClass"),
apiVersion: "scheduling.k8s.io/v1",
kind: "PriorityClass",
expected: false,
},
{
config: schema.FromAPIVersionAndKind("scheduling.k8s.io/v1", "PriorityClass"),
apiVersion: "scheduling.k8s.io/v1",
kind: "PriorityClassDifferent",
expected: false,
},
}

for _, tc := range testCases {
t.Run(fmt.Sprintf("%s:%s/%s", tc.config, tc.apiVersion, tc.kind), func(tt *testing.T) {
condition := rewriter.MatchGVK(tc.config)
u := &unstructured.Unstructured{}
u.SetAPIVersion(tc.apiVersion)
u.SetKind(tc.kind)
assert.Equal(tt, tc.expected, condition(u))
})
}
}
121 changes: 121 additions & 0 deletions pkg/rewriter/mocks/ResourceRewriter.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pkg/rewriter/rewriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ func annotationForField(fieldPath ...string) string {
return annotationForOriginalValue(strings.Join(fieldPath, "."))
}

//go:generate mockery --name ResourceRewriter --with-expecter

// ResourceRewriter prepares object for saving on import and rewrites the object
// before its returned back from proxy server.
type ResourceRewriter interface {
Expand Down

0 comments on commit efa8afc

Please sign in to comment.