Skip to content

Commit 8c09eff

Browse files
author
Francisco de Paz Galan
authored
[bitnami/kubernetes-event-exporter] Add tests and publishing using VIB (#13198)
* [bitnami/kubernetes-event-exporter] Add tests and publishing using VIB Signed-off-by: FraPazGal <fdepaz@vmware.com> * Add asset to CD Signed-off-by: FraPazGal <fdepaz@vmware.com> * Add missing param type Signed-off-by: FraPazGal <fdepaz@vmware.com> * Apply suggestions Signed-off-by: FraPazGal <fdepaz@vmware.com> * Fix kubernetes exporter position in CD file Signed-off-by: FraPazGal <fdepaz@vmware.com> Signed-off-by: FraPazGal <fdepaz@vmware.com> Signed-off-by: Francisco de Paz Galan <fdepaz@vmware.com>
1 parent 0adb24d commit 8c09eff

File tree

11 files changed

+1080
-2
lines changed

11 files changed

+1080
-2
lines changed

.github/workflows/cd-pipeline.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ on: # rebuild any PRs and main branch changes
3636
- 'bitnami/keycloak/**'
3737
- 'bitnami/kibana/**'
3838
- 'bitnami/kong/**'
39+
- 'bitnami/kubernetes-event-exporter/**'
3940
- 'bitnami/logstash/**'
4041
- 'bitnami/magento/**'
4142
- 'bitnami/mariadb-galera/**'
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
module vib-test
2+
3+
go 1.19
4+
5+
require (
6+
github.com/onsi/ginkgo/v2 v2.2.0
7+
github.com/onsi/gomega v1.20.2
8+
k8s.io/api v0.25.2
9+
k8s.io/apimachinery v0.25.2
10+
k8s.io/client-go v0.25.2
11+
)
12+
13+
require (
14+
cloud.google.com/go v0.97.0 // indirect
15+
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
16+
github.com/Azure/go-autorest/autorest v0.11.27 // indirect
17+
github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect
18+
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
19+
github.com/Azure/go-autorest/logger v0.2.1 // indirect
20+
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
21+
github.com/PuerkitoBio/purell v1.1.1 // indirect
22+
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
23+
github.com/davecgh/go-spew v1.1.1 // indirect
24+
github.com/emicklei/go-restful/v3 v3.8.0 // indirect
25+
github.com/go-logr/logr v1.2.3 // indirect
26+
github.com/go-openapi/jsonpointer v0.19.5 // indirect
27+
github.com/go-openapi/jsonreference v0.19.5 // indirect
28+
github.com/go-openapi/swag v0.19.14 // indirect
29+
github.com/gogo/protobuf v1.3.2 // indirect
30+
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
31+
github.com/golang/protobuf v1.5.2 // indirect
32+
github.com/google/gnostic v0.5.7-v3refs // indirect
33+
github.com/google/go-cmp v0.5.8 // indirect
34+
github.com/google/gofuzz v1.1.0 // indirect
35+
github.com/imdario/mergo v0.3.6 // indirect
36+
github.com/josharian/intern v1.0.0 // indirect
37+
github.com/json-iterator/go v1.1.12 // indirect
38+
github.com/mailru/easyjson v0.7.6 // indirect
39+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
40+
github.com/modern-go/reflect2 v1.0.2 // indirect
41+
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
42+
github.com/spf13/pflag v1.0.5 // indirect
43+
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
44+
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
45+
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
46+
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
47+
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
48+
golang.org/x/text v0.3.7 // indirect
49+
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
50+
google.golang.org/appengine v1.6.7 // indirect
51+
google.golang.org/protobuf v1.28.0 // indirect
52+
gopkg.in/inf.v0 v0.9.1 // indirect
53+
gopkg.in/yaml.v2 v2.4.0 // indirect
54+
gopkg.in/yaml.v3 v3.0.1 // indirect
55+
k8s.io/klog/v2 v2.70.1 // indirect
56+
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
57+
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect
58+
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
59+
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
60+
sigs.k8s.io/yaml v1.2.0 // indirect
61+
)

.vib/kubernetes-event-exporter/ginkgo/go.sum

Lines changed: 672 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
package integration
2+
3+
import (
4+
"bufio"
5+
"context"
6+
"flag"
7+
"fmt"
8+
"io"
9+
"regexp"
10+
"testing"
11+
"time"
12+
13+
v1 "k8s.io/api/core/v1"
14+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
15+
cv1 "k8s.io/client-go/kubernetes/typed/core/v1"
16+
"k8s.io/client-go/rest"
17+
"k8s.io/client-go/tools/clientcmd"
18+
19+
. "github.com/onsi/ginkgo/v2"
20+
. "github.com/onsi/gomega"
21+
22+
// For client auth plugins
23+
_ "k8s.io/client-go/plugin/pkg/client/auth"
24+
)
25+
26+
const APP_NAME = "Kubernetes Event Exporter"
27+
28+
var kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
29+
var namespace = flag.String("namespace", "", "namespace where the resources are deployed")
30+
31+
func clusterConfigOrDie() *rest.Config {
32+
var config *rest.Config
33+
var err error
34+
35+
if *kubeconfig != "" {
36+
config, err = clientcmd.BuildConfigFromFlags("", *kubeconfig)
37+
} else {
38+
config, err = rest.InClusterConfig()
39+
}
40+
if err != nil {
41+
panic(err.Error())
42+
}
43+
44+
return config
45+
}
46+
47+
func createPodOrDie(ctx context.Context, c cv1.PodsGetter, name string, image string) *v1.Pod {
48+
podData := &v1.Pod{
49+
ObjectMeta: metav1.ObjectMeta{
50+
Namespace: *namespace,
51+
Name: name,
52+
},
53+
Spec: v1.PodSpec{
54+
Containers: []v1.Container{
55+
{
56+
Name: name,
57+
Image: image,
58+
},
59+
},
60+
},
61+
}
62+
result, err := c.Pods(*namespace).Create(ctx, podData, metav1.CreateOptions{})
63+
if err != nil {
64+
panic(fmt.Sprintf("There was an error creating the Pod: %s", err))
65+
}
66+
return result
67+
}
68+
69+
func getPodsByLabelOrDie(ctx context.Context, c cv1.PodsGetter, selector string) v1.PodList {
70+
output, err := c.Pods(*namespace).List(ctx, metav1.ListOptions{
71+
LabelSelector: selector,
72+
})
73+
if err != nil {
74+
panic(err.Error())
75+
}
76+
fmt.Printf("Obtained list of pods with label %q\n", selector)
77+
78+
return *output
79+
}
80+
81+
func getContainerLogsOrDie(ctx context.Context, c cv1.PodsGetter, podName string, container string) []string {
82+
var output []string
83+
tailLines := int64(50)
84+
85+
readCloser, err := c.Pods(*namespace).GetLogs(podName, &v1.PodLogOptions{
86+
Container: container,
87+
Follow: false,
88+
TailLines: &tailLines,
89+
}).Stream(ctx)
90+
if err != nil {
91+
panic(err.Error())
92+
}
93+
fmt.Printf("Obtained %q pod's logs\n", podName)
94+
95+
defer readCloser.Close()
96+
97+
scanner := bufio.NewScanner(interruptableReader{ctx, readCloser})
98+
for scanner.Scan() {
99+
output = append(output, scanner.Text())
100+
}
101+
if scanner.Err() != nil {
102+
panic(scanner.Err())
103+
}
104+
return output
105+
}
106+
107+
func containerLogsContainPattern(ctx context.Context, c cv1.PodsGetter, podLabel string, containerName string, pattern string) (bool, error) {
108+
var k8seePods v1.PodList
109+
var containerLogs []string
110+
111+
k8seePods = getPodsByLabelOrDie(ctx, c, podLabel)
112+
containerLogs = getContainerLogsOrDie(ctx, c, k8seePods.Items[0].GetName(), containerName)
113+
114+
return containsPattern(containerLogs, pattern)
115+
}
116+
117+
func retry(name string, attempts int, sleep time.Duration, f func() (bool, error)) (res bool, err error) {
118+
for i := 0; i < attempts; i++ {
119+
fmt.Printf("[retriable] operation %q executing now [attempt %d/%d]\n", name, (i + 1), attempts)
120+
res, err = f()
121+
if res {
122+
fmt.Printf("[retriable] operation %q succedeed [attempt %d/%d]\n", name, (i + 1), attempts)
123+
return res, err
124+
}
125+
fmt.Printf("[retriable] operation %q failed, sleeping for %q now...\n", name, sleep)
126+
time.Sleep(sleep)
127+
}
128+
fmt.Printf("[retriable] operation %q failed [attempt %d/%d]\n", name, attempts, attempts)
129+
return res, err
130+
}
131+
132+
type interruptableReader struct {
133+
ctx context.Context
134+
r io.Reader
135+
}
136+
137+
func (r interruptableReader) Read(p []byte) (int, error) {
138+
if err := r.ctx.Err(); err != nil {
139+
return 0, err
140+
}
141+
n, err := r.r.Read(p)
142+
if err != nil {
143+
return n, err
144+
}
145+
return n, r.ctx.Err()
146+
}
147+
148+
func containsPattern(haystack []string, pattern string) (bool, error) {
149+
var err error
150+
151+
for _, s := range haystack {
152+
match, err := regexp.MatchString(pattern, s)
153+
if match {
154+
return true, err
155+
}
156+
}
157+
return false, err
158+
}
159+
160+
func CheckRequirements() {
161+
if *namespace == "" {
162+
panic(fmt.Sprintf("The namespace where %s is deployed must be provided. Use the '--namespace' flag", APP_NAME))
163+
}
164+
}
165+
166+
func TestIntegration(t *testing.T) {
167+
RegisterFailHandler(Fail)
168+
CheckRequirements()
169+
RunSpecs(t, fmt.Sprintf("%s Integration Tests", APP_NAME))
170+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package integration
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
. "github.com/onsi/ginkgo/v2"
8+
. "github.com/onsi/gomega"
9+
10+
v1 "k8s.io/api/core/v1"
11+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
cv1 "k8s.io/client-go/kubernetes/typed/core/v1"
13+
14+
// For client auth plugins
15+
_ "k8s.io/client-go/plugin/pkg/client/auth"
16+
)
17+
18+
var _ = Describe("Kubernetes Event Exporter:", func() {
19+
var coreclient cv1.CoreV1Interface
20+
var ctx context.Context
21+
22+
BeforeEach(func() {
23+
coreclient = cv1.NewForConfigOrDie(clusterConfigOrDie())
24+
ctx = context.Background()
25+
})
26+
27+
When("a testing pod is created", func() {
28+
var resourceName string
29+
var createdPod *v1.Pod
30+
31+
BeforeEach(func() {
32+
resourceName = "kuard-" + *namespace
33+
createdPod = createPodOrDie(ctx, coreclient, resourceName, "gcr.io/kuar-demo/kuard-amd64:1")
34+
})
35+
36+
AfterEach(func() {
37+
// Not need to panic here if failed, the cluster is expected to clean up with the undeployment
38+
coreclient.Pods(*namespace).Delete(ctx, createdPod.GetName(), metav1.DeleteOptions{})
39+
})
40+
41+
Describe("the exporter logs", func() {
42+
It("shows its kubernetes events", func() {
43+
pattern := "msg=.*Created container kuard-" + *namespace
44+
podLabel := "app.kubernetes.io/name=kubernetes-event-exporter"
45+
containerName := "event-exporter"
46+
47+
containsPattern, _ := retry("containerLogsContainPattern", 5, 2*time.Second, func() (bool, error) {
48+
return containerLogsContainPattern(ctx, coreclient, podLabel, containerName, pattern)
49+
})
50+
Expect(containsPattern).To(BeTrue())
51+
})
52+
})
53+
})
54+
})
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
file:
2+
/data/config.yaml:
3+
exists: true
4+
filetype: symlink
5+
mode: "0777"
6+
owner: root
7+
contains:
8+
- /logLevel.*{{ .Vars.config.logLevel }}/
9+
/var/run/secrets/kubernetes.io/serviceaccount:
10+
exists: {{ .Vars.serviceAccount.automountServiceAccountToken }}
11+
filetype: directory
12+
mode: "3777"
13+
command:
14+
check-no-capabilities:
15+
exec: cat /proc/1/status
16+
exit-status: 0
17+
stdout:
18+
- "CapInh: 0000000000000000"
19+
- "CapPrm: 0000000000000000"
20+
- "CapEff: 0000000000000000"
21+
- "CapBnd: 0000000000000000"
22+
- "CapAmb: 0000000000000000"
23+
check-user-info:
24+
exec: id
25+
exit-status: 0
26+
stdout:
27+
- uid={{ .Vars.containerSecurityContext.runAsUser }}
28+
- /groups=.*{{ .Vars.podSecurityContext.fsGroup }}/
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
config:
2+
logLevel: debug
3+
serviceAccount:
4+
automountServiceAccountToken: true
5+
podSecurityContext:
6+
fsGroup: 1002
7+
containerSecurityContext:
8+
runAsUser: 1002

.vib/kubernetes-event-exporter/vib-publish.json

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,48 @@
1616
}
1717
]
1818
},
19+
"verify": {
20+
"context": {
21+
"resources": {
22+
"url": "{SHA_ARCHIVE}",
23+
"path": "/bitnami/kubernetes-event-exporter"
24+
},
25+
"runtime_parameters": "Y29uZmlnOgogIGxvZ0xldmVsOiBkZWJ1ZwogIGxvZ0Zvcm1hdDogcHJldHR5CiAgcmVjZWl2ZXJzOgogICAgLSBuYW1lOiAiZHVtcCIKICAgICAgZmlsZToKICAgICAgICBwYXRoOiAiL2Rldi9zdGRvdXQiCiAgICAgICAgbGF5b3V0OiB7fQogIHJvdXRlOgogICAgcm91dGVzOgogICAgICAtIG1hdGNoOgogICAgICAgICAgLSByZWNlaXZlcjogImR1bXAiCnJiYWM6CiAgY3JlYXRlOiB0cnVlCnNlcnZpY2VBY2NvdW50OgogIGNyZWF0ZTogdHJ1ZQogIGF1dG9tb3VudFNlcnZpY2VBY2NvdW50VG9rZW46IHRydWUKcG9kU2VjdXJpdHlDb250ZXh0OgogIGVuYWJsZWQ6IHRydWUKICBmc0dyb3VwOiAxMDAyCmNvbnRhaW5lclNlY3VyaXR5Q29udGV4dDoKICBlbmFibGVkOiB0cnVlCiAgcmVhZE9ubHlSb290RmlsZXN5c3RlbTogZmFsc2UKICBjYXBhYmlsaXRpZXM6CiAgICBhZGQ6IFtdCiAgICBkcm9wOgogICAgICAtIEFMTAogIHJ1bkFzVXNlcjogMTAwMg==",
26+
"target_platform": {
27+
"target_platform_id": "{VIB_ENV_ALTERNATIVE_TARGET_PLATFORM}",
28+
"size": {
29+
"name": "S4"
30+
}
31+
}
32+
},
33+
"actions": [
34+
{
35+
"action_id": "goss",
36+
"params": {
37+
"resources": {
38+
"path": "/.vib/kubernetes-event-exporter/goss"
39+
},
40+
"remote": {
41+
"workload": "deploy-kubernetes-event-exporter"
42+
},
43+
"vars_file": "vars.yaml"
44+
}
45+
},
46+
{
47+
"action_id": "ginkgo",
48+
"params": {
49+
"resources": {
50+
"path": "/.vib/kubernetes-event-exporter/ginkgo"
51+
52+
},
53+
"params": {
54+
"kubeconfig": "{{kubeconfig}}",
55+
"namespace": "{{namespace}}"
56+
}
57+
}
58+
}
59+
]
60+
},
1961
"publish": {
2062
"actions": [
2163
{

0 commit comments

Comments
 (0)