Add e2e test for Custom Resource support (#338)

Signed-off-by: Prasad Ghangal <prasad.ghangal@gmail.com>

##### ISSUE TYPE
 - Feature Pull Request

##### SUMMARY

- Add e2e test for Custom Resource support
- Refactor test package

Fixes #334
This commit is contained in:
Prasad Ghangal 2020-09-13 10:51:59 +05:30 committed by GitHub
parent a64b24d8b6
commit 8f95320ab8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 109 additions and 105 deletions

1
go.mod
View File

@ -50,6 +50,7 @@ require (
k8s.io/apimachinery v0.17.0
k8s.io/client-go v0.17.0
k8s.io/kubectl v0.17.0
k8s.io/sample-controller v0.17.0
)
go 1.13

7
go.sum
View File

@ -24,6 +24,7 @@ github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
github.com/Masterminds/squirrel v1.1.0 h1:baP1qLdoQCeTw3ifCdOq2dkYc6vGcmRdaociKLbEJXs=
github.com/Masterminds/squirrel v1.1.0/go.mod h1:yaPeOnPG5ZRwL9oKdTsO/prlkPbXWZlRVMQ/gGlzIuA=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46 h1:lsxEuwrXEAokXB9qhlbKWPpo3KMLZQ5WB5WLQRW1uq0=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
@ -116,6 +117,7 @@ github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDA
github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
@ -233,6 +235,7 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
@ -456,12 +459,10 @@ k8s.io/api v0.17.0 h1:H9d/lw+VkZKEVIUc8F3wgiQ+FUXTTr21M87jXLU7yqM=
k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI=
k8s.io/apimachinery v0.17.0 h1:xRBnuie9rXcPxUkDizUsGvPf1cnlZCFu210op7J7LJo=
k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
k8s.io/apimachinery v0.19.0 h1:gjKnAda/HZp5k4xQYjL0K/Yb66IvNqjthCb03QlKpaQ=
k8s.io/cli-runtime v0.17.0 h1:XEuStbJBHCQlEKFyTQmceDKEWOSYHZkcYWKp3SsQ9Hk=
k8s.io/cli-runtime v0.17.0/go.mod h1:1E5iQpMODZq2lMWLUJELwRu2MLWIzwvMgDBpn3Y81Qo=
k8s.io/client-go v0.17.0 h1:8QOGvUGdqDMFrm9sD6IUFl256BcffynGoe80sxgTEDg=
k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k=
k8s.io/client-go v11.0.0+incompatible h1:LBbX2+lOwY9flffWlJM7f1Ct8V2SRNiMRDFeiwnJo9o=
k8s.io/code-generator v0.17.0/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s=
k8s.io/component-base v0.17.0 h1:BnDFcmBDq+RPpxXjmuYnZXb59XNN9CaFrX8ba9+3xrA=
k8s.io/component-base v0.17.0/go.mod h1:rKuRAokNMY2nn2A6LP/MiwpoaMRHpfRnrPaUJJj1Yoc=
@ -476,6 +477,8 @@ k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKf
k8s.io/kubectl v0.17.0 h1:xD4EWlL+epc/JTO1gvSjmV9yiYF0Z2wiHK2DIek6URY=
k8s.io/kubectl v0.17.0/go.mod h1:jIPrUAW656Vzn9wZCCe0PC+oTcu56u2HgFD21Xbfk1s=
k8s.io/metrics v0.17.0/go.mod h1:EH1D3YAwN6d7bMelrElnLhLg72l/ERStyv2SIQVt6Do=
k8s.io/sample-controller v0.17.0 h1:XWXk7bDFuU2IA2GSsvFOGICh6JFYDdsyZjw8D4ADRPo=
k8s.io/sample-controller v0.17.0/go.mod h1:/qDFKdxOwcu3XSKxR0u3WsUt4sQ0oTtKrkXGdct70RE=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=

View File

@ -26,13 +26,15 @@ import (
"strings"
"testing"
"github.com/infracloudio/botkube/pkg/config"
"github.com/infracloudio/botkube/pkg/execute"
"github.com/infracloudio/botkube/test/e2e/utils"
"github.com/nlopes/slack"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/infracloudio/botkube/pkg/config"
"github.com/infracloudio/botkube/pkg/execute"
"github.com/infracloudio/botkube/test/e2e/utils"
)
type botkubeCommand struct {
@ -159,7 +161,8 @@ func (c *context) testNotifierCommand(t *testing.T) {
// Create pod and verify that BotKube is not sending notifications
pod := utils.CreateObjects{
Kind: "pod",
GVR: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"},
Kind: "Pod",
Namespace: "test",
Specs: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "test-pod-notifier"}},
ExpectedSlackMessage: utils.SlackMessage{

7
test/e2e/env/env.go vendored
View File

@ -26,9 +26,6 @@ import (
"testing"
"time"
"github.com/infracloudio/botkube/pkg/config"
"github.com/infracloudio/botkube/test/e2e/utils"
"github.com/infracloudio/botkube/test/webhook"
"github.com/nlopes/slack"
"github.com/nlopes/slack/slacktest"
"k8s.io/apimachinery/pkg/runtime"
@ -36,6 +33,10 @@ import (
"k8s.io/client-go/dynamic"
"k8s.io/client-go/dynamic/fake"
kubeFake "k8s.io/client-go/kubernetes/fake"
"github.com/infracloudio/botkube/pkg/config"
"github.com/infracloudio/botkube/test/e2e/utils"
"github.com/infracloudio/botkube/test/webhook"
)
// TestEnv to store objects required for e2e testing

View File

@ -23,15 +23,17 @@ import (
"encoding/json"
"testing"
"github.com/infracloudio/botkube/pkg/notify"
"github.com/infracloudio/botkube/test/e2e/env"
"github.com/infracloudio/botkube/test/e2e/utils"
"github.com/nlopes/slack"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
networkV1beta1 "k8s.io/api/networking/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/intstr"
"github.com/infracloudio/botkube/pkg/notify"
"github.com/infracloudio/botkube/test/e2e/env"
"github.com/infracloudio/botkube/test/e2e/utils"
)
type context struct {
@ -43,7 +45,8 @@ func (c *context) testFilters(t *testing.T) {
// Test cases
tests := map[string]utils.CreateObjects{
"test ImageTagChecker filter": {
Kind: "pod",
GVR: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"},
Kind: "Pod",
Namespace: "test",
Specs: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "nginx-pod", Labels: map[string]string{"env": "test"}}, Spec: v1.PodSpec{Containers: []v1.Container{{Name: "nginx", Image: "nginx:latest"}}}},
ExpectedSlackMessage: utils.SlackMessage{
@ -57,7 +60,8 @@ func (c *context) testFilters(t *testing.T) {
},
"test PodLabelChecker filter": {
Kind: "pod",
GVR: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"},
Kind: "Pod",
Namespace: "test",
Specs: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod-wo-label"}},
ExpectedSlackMessage: utils.SlackMessage{
@ -71,7 +75,8 @@ func (c *context) testFilters(t *testing.T) {
},
"test IngressValidator filter": {
Kind: "ingress",
GVR: schema.GroupVersionResource{Group: "networking.k8s.io", Version: "v1beta1", Resource: "ingresses"},
Kind: "Ingress",
Namespace: "test",
Specs: &networkV1beta1.Ingress{ObjectMeta: metav1.ObjectMeta{Name: "ingress-with-service"}, Spec: networkV1beta1.IngressSpec{Rules: []networkV1beta1.IngressRule{{IngressRuleValue: networkV1beta1.IngressRuleValue{HTTP: &networkV1beta1.HTTPIngressRuleValue{Paths: []networkV1beta1.HTTPIngressPath{{Path: "testpath", Backend: networkV1beta1.IngressBackend{ServiceName: "test-service", ServicePort: intstr.FromInt(80)}}}}}}}}},
ExpectedSlackMessage: utils.SlackMessage{

View File

@ -24,15 +24,18 @@ import (
"fmt"
"testing"
"github.com/nlopes/slack"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
samplev1alpha1 "k8s.io/sample-controller/pkg/apis/samplecontroller/v1alpha1"
"github.com/infracloudio/botkube/pkg/config"
"github.com/infracloudio/botkube/pkg/notify"
"github.com/infracloudio/botkube/pkg/utils"
"github.com/infracloudio/botkube/test/e2e/env"
testutils "github.com/infracloudio/botkube/test/e2e/utils"
"github.com/nlopes/slack"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type context struct {
@ -44,7 +47,8 @@ func (c *context) testCreateResource(t *testing.T) {
// Test cases
tests := map[string]testutils.CreateObjects{
"create pod in configured namespace": {
Kind: "pod",
GVR: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"},
Kind: "Pod",
Namespace: "test",
Specs: &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "test-pod"}},
ExpectedSlackMessage: testutils.SlackMessage{
@ -57,7 +61,8 @@ func (c *context) testCreateResource(t *testing.T) {
},
},
"create service in configured namespace": {
Kind: "service",
GVR: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "services"},
Kind: "Service",
Namespace: "test",
Specs: &v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "test-service"}},
ExpectedSlackMessage: testutils.SlackMessage{
@ -70,7 +75,8 @@ func (c *context) testCreateResource(t *testing.T) {
},
},
"create a namespace": {
Kind: "namespace",
GVR: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"},
Kind: "Namespace",
Specs: &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "test-namespace"}},
ExpectedSlackMessage: testutils.SlackMessage{
Attachments: []slack.Attachment{{Color: "good", Title: "v1/namespaces created", Fields: []slack.AttachmentField{{Value: "Namespace *test-namespace* has been created in *test-cluster-1* cluster\n", Short: false}}, Footer: "BotKube"}},
@ -81,6 +87,20 @@ func (c *context) testCreateResource(t *testing.T) {
Summary: "Namespace *test-namespace* has been created in *test-cluster-1* cluster\n",
},
},
"create a foo CR": {
GVR: schema.GroupVersionResource{Group: "samplecontroller.k8s.io", Version: "v1alpha1", Resource: "foos"},
Kind: "Foo",
Namespace: "test",
Specs: &samplev1alpha1.Foo{ObjectMeta: metav1.ObjectMeta{Name: "test-foo"}},
ExpectedSlackMessage: testutils.SlackMessage{
Attachments: []slack.Attachment{{Color: "good", Title: "samplecontroller.k8s.io/v1alpha1/foos created", Fields: []slack.AttachmentField{{Value: "Foo *test/test-foo* has been created in *test-cluster-1* cluster\n", Short: false}}, Footer: "BotKube"}},
},
ExpectedWebhookPayload: testutils.WebhookPayload{
EventMeta: notify.EventMeta{Kind: "Foo", Name: "test-foo", Namespace: "test", Cluster: "test-cluster-1"},
EventStatus: notify.EventStatus{Type: "create", Level: "info", Reason: "", Error: ""},
Summary: "Foo *test/test-foo* has been created in *test-cluster-1* cluster\n",
},
},
}
for name, test := range tests {
@ -108,8 +128,19 @@ func (c *context) testCreateResource(t *testing.T) {
assert.Equal(t, test.ExpectedWebhookPayload.EventStatus, lastSeenPayload.EventStatus)
assert.Equal(t, test.ExpectedWebhookPayload.Summary, lastSeenPayload.Summary)
}
isAllowed := utils.AllowedEventKindsMap[utils.EventKind{Resource: fmt.Sprintf("v1/%vs", test.Kind), Namespace: "all", EventType: config.CreateEvent}] ||
utils.AllowedEventKindsMap[utils.EventKind{Resource: fmt.Sprintf("v1/%vs", test.Kind), Namespace: test.Namespace, EventType: config.CreateEvent}]
resource := fmt.Sprintf("%s/%s/%s", test.GVR.Group, test.GVR.Version, test.GVR.Resource)
if test.GVR.Group == "" {
resource = fmt.Sprintf("%s/%s", test.GVR.Version, test.GVR.Resource)
}
isAllowed := utils.AllowedEventKindsMap[utils.EventKind{
Resource: resource,
Namespace: "all",
EventType: config.CreateEvent}] ||
utils.AllowedEventKindsMap[utils.EventKind{
Resource: resource,
Namespace: test.Namespace,
EventType: config.CreateEvent}]
assert.Equal(t, isAllowed, true)
})
}

View File

@ -20,27 +20,19 @@
package utils
import (
"strings"
"testing"
"github.com/nlopes/slack"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/infracloudio/botkube/pkg/config"
"github.com/infracloudio/botkube/pkg/notify"
"github.com/infracloudio/botkube/pkg/utils"
)
var (
podGVR = utils.ParseResourceArg("v1/pods")
serviceGVR = utils.ParseResourceArg("v1/services")
ingressGVR = utils.ParseResourceArg("networking.k8s.io/v1beta1/ingresses")
namespaceGVR = utils.ParseResourceArg("v1/namespaces")
)
// SlackMessage structure
type SlackMessage struct {
Text string
@ -56,6 +48,7 @@ type WebhookPayload struct {
// CreateObjects stores specs for creating a k8s fake object and expected Slack response
type CreateObjects struct {
GVR schema.GroupVersionResource
Kind string
Namespace string
Specs runtime.Object
@ -66,60 +59,17 @@ type CreateObjects struct {
// CreateResource with fake client
func CreateResource(t *testing.T, obj CreateObjects) {
switch obj.Kind {
case "pod":
// convert the runtime.Object to unstructured.Unstructured
s := unstructured.Unstructured{}
k, ok := runtime.DefaultUnstructuredConverter.ToUnstructured(obj.Specs)
if ok != nil {
t.Fatalf("Failed to convert pod object into unstructured")
}
s.Object = k
s.SetGroupVersionKind(podGVR.GroupVersion().WithKind(strings.Title(strings.ToLower(obj.Kind))))
_, err := utils.DynamicKubeClient.Resource(podGVR).Namespace(obj.Namespace).Create(&s, v1.CreateOptions{})
if err != nil {
t.Fatalf("Failed to create pod: %v", err)
}
case "service":
s := unstructured.Unstructured{}
k, ok := runtime.DefaultUnstructuredConverter.ToUnstructured(obj.Specs)
if ok != nil {
t.Fatalf("Failed to convert pod object into unstructured")
}
s.Object = k
s.SetGroupVersionKind(serviceGVR.GroupVersion().WithKind(strings.Title(strings.ToLower(obj.Kind))))
_, err := utils.DynamicKubeClient.Resource(serviceGVR).Namespace(obj.Namespace).Create(&s, v1.CreateOptions{})
if err != nil {
t.Fatalf("Failed to create service: %v", err)
}
case "ingress":
s := unstructured.Unstructured{}
k, ok := runtime.DefaultUnstructuredConverter.ToUnstructured(obj.Specs)
if ok != nil {
t.Fatalf("Failed to convert pod object into unstructured")
}
s.Object = k
s.SetGroupVersionKind(ingressGVR.GroupVersion().WithKind(strings.Title(strings.ToLower(obj.Kind))))
_, err := utils.DynamicKubeClient.Resource(ingressGVR).Namespace(obj.Namespace).Create(&s, v1.CreateOptions{})
if err != nil {
t.Fatalf("Failed to create ingress: %v", err)
}
case "namespace":
s := unstructured.Unstructured{}
k, ok := runtime.DefaultUnstructuredConverter.ToUnstructured(obj.Specs)
if ok != nil {
t.Fatalf("Failed to convert pod object into unstructured")
}
s.Object = k
s.SetGroupVersionKind(namespaceGVR.GroupVersion().WithKind(strings.Title(strings.ToLower(obj.Kind))))
_, err := utils.DynamicKubeClient.Resource(namespaceGVR).Namespace(obj.Namespace).Create(&s, v1.CreateOptions{})
if err != nil {
t.Fatalf("Failed to create namespace: %v", err)
}
default:
t.Fatalf("CreateResource method is not defined for resource %s", obj.Kind)
// convert the runtime.Object to unstructured.Unstructured
s := unstructured.Unstructured{}
k, ok := runtime.DefaultUnstructuredConverter.ToUnstructured(obj.Specs)
if ok != nil {
t.Fatalf("Failed to convert pod object into unstructured")
}
s.Object = k
s.SetGroupVersionKind(obj.GVR.GroupVersion().WithKind(obj.Kind))
// Create resource
_, err := utils.DynamicKubeClient.Resource(obj.GVR).Namespace(obj.Namespace).Create(&s, v1.CreateOptions{})
if err != nil {
t.Fatalf("Failed to create %s: %v", obj.GVR.Resource, err)
}
}

View File

@ -20,7 +20,7 @@
## test_config.yaml for Integration Testing
## Resources you want to watch
resources:
- name: v1/pods # Name of the resources e.g pod, deployment, ingress, etc.
- name: v1/pods # Name of the resources e.g pod, deployment, ingress, etc.
namespaces: # List of namespaces, "all" will watch all the namespaces
include:
- all
@ -198,6 +198,16 @@ resources:
- create
- delete
- error
- name: samplecontroller.k8s.io/v1alpha1/foos
namespaces:
include:
- all
ignore:
-
events:
- create
- delete
- error
# Check true if you want to receive recommendations
# about the best practices for the created resource
recommendations: true