Skip to content

Commit 657bf76

Browse files
authored
CLOUDP-151976: Create test coverage report (#831)
* Add test coverage * Update tests * Run multinamespace operator via code
1 parent d3f0ace commit 657bf76

32 files changed

+681
-549
lines changed

.github/codecov.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
comment: false
2+
3+
github_checks:
4+
annotations: false

.github/workflows/test-e2e.yml

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ jobs:
177177
echo "k8s_platform=$platform" >> $GITHUB_OUTPUT
178178
- name: Create k8s Kind Cluster
179179
if: ${{ steps.properties.outputs.k8s_platform == 'kind' && !env.ACT }}
180-
uses: helm/kind-action@v1.4.0
180+
uses: helm/kind-action@v1.5.0
181181
with:
182182
version: v0.11.1
183183
config: test/e2e/config/kind.yaml
@@ -193,6 +193,10 @@ jobs:
193193
wget https://github.com/operator-framework/operator-sdk/releases/download/v1.22.0/operator-sdk_linux_amd64 -q
194194
chmod +x operator-sdk_linux_amd64 && sudo mv operator-sdk_linux_amd64 /usr/local/bin/operator-sdk
195195
operator-sdk version
196+
- name: Install CRDs if needed
197+
if: ${{ !( matrix.test == 'helm-update' || matrix.test == 'helm-wide' || matrix.test == 'bundle-test' ) }}
198+
run: |
199+
kubectl apply -f deploy/crds
196200
- name: Run e2e test
197201
env:
198202
MCLI_PUBLIC_API_KEY: ${{ secrets.ATLAS_PUBLIC_KEY }}
@@ -221,17 +225,26 @@ jobs:
221225
run: |
222226
helm version
223227
go version
224-
228+
225229
go install github.com/onsi/ginkgo/v2/ginkgo@v2.6.1 && \
226230
go install github.com/onsi/gomega/...
231+
232+
cd test/e2e
227233
228234
# no `long-run`, no `broken` tests. `Long-run` tests run as a separate job
229235
[[ $TEST_NAME == 'long-run' ]] && filter='long-run && !broken' || filter="$TEST_NAME"' && !long-run && !broken' && \
230-
echo 'Running: ginkgo --label-filter="${filter}" --timeout 120m --nodes=10 -v test/e2e/' && \
231-
ginkgo --label-filter="${filter}" --timeout 120m --nodes=10 -v test/e2e/
236+
echo 'Running: ginkgo --label-filter="${filter}" --timeout 120m --nodes=10 --cover --v' && \
237+
ginkgo --label-filter="${filter}" --timeout 120m --nodes=10 --cover --v --coverpkg=github.com/mongodb/mongodb-atlas-kubernetes/pkg/...
232238
- name: Upload operator logs
233239
if: ${{ failure() }}
234240
uses: actions/upload-artifact@v3
235241
with:
236242
name: logs
237243
path: test/e2e/output/**
244+
- name: Upload test results to codecov.io
245+
if: ${{ success() }}
246+
uses: codecov/codecov-action@v3
247+
with:
248+
files: test/e2e/coverprofile.out
249+
name: ${{ matrix.test }}
250+
verbose: true

.github/workflows/test-int.yml

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,36 @@ jobs:
4242
~/.cache/go-build
4343
~/go/pkg/mod
4444
key: ${{ runner.os }}-build-${{ hashFiles('**/go.sum') }}
45-
46-
- name: Run testing
47-
uses: ./.github/actions/int-test
45+
- name: Setup Go
46+
uses: actions/setup-go@v3
4847
with:
48+
go-version-file: "${{ github.workspace }}/go.mod"
49+
- name: Run testing
50+
env:
4951
ATLAS_ORG_ID: ${{ secrets.ATLAS_ORG_ID }}
5052
ATLAS_PUBLIC_KEY: ${{ secrets.ATLAS_PUBLIC_KEY }}
5153
ATLAS_PRIVATE_KEY: ${{ secrets.ATLAS_PRIVATE_KEY }}
5254
TEST_NAME: ${{ matrix.test }}
53-
TEST_PATH: ${{ matrix.path }}
5455
PARALLEL_NODES: ${{ matrix.nodes }}
56+
GO111MODULE: on
57+
GINKGO_EDITOR_INTEGRATION: "true"
58+
run: |
59+
60+
# Download binaries for envtests (api-server, etcd)
61+
sudo curl -Lo setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/v0.8.0/hack/setup-envtest.sh && \
62+
sudo mkdir -p /usr/local/kubebuilder/bin && \
63+
sudo /bin/bash -c "source setup-envtest.sh && fetch_envtest_tools /usr/local/kubebuilder"
64+
65+
go install github.com/onsi/ginkgo/v2/ginkgo@v2.6.1 && \
66+
go install github.com/onsi/gomega/...
67+
68+
cd ${{ matrix.path }}
69+
ginkgo --label-filter="${TEST_NAME}" --timeout 80m --v --nodes="${PARALLEL_NODES}" --cover --coverpkg=github.com/mongodb/mongodb-atlas-kubernetes/pkg/...
70+
71+
- name: Upload coverage to Codecov
72+
uses: codecov/codecov-action@v3
73+
with:
74+
name: ${{ matrix.test }}
75+
files: ${{ matrix.path }}/coverprofile.out
76+
verbose: true
77+

.github/workflows/test-unit.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,11 @@ jobs:
3838
- run: go version
3939

4040
- name: Run testing
41-
run: CGO_ENABLED=0 go test -v $(go list ./pkg/...)
41+
run: CGO_ENABLED=0 go test -v $(go list ./pkg/...) -coverprofile=coverage.out
42+
43+
- name: Upload coverage to Codecov
44+
uses: codecov/codecov-action@v3
45+
with:
46+
name: unit-tests
47+
files: coverage.out
48+
verbose: true

.github/workflows/test.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,27 @@ jobs:
2828
steps:
2929
- name: allowed message
3030
run: echo "Allowed to run"
31+
- name: check Github action bot comment
32+
if: github.event_name == 'pull_request'
33+
uses: peter-evans/find-comment@v2
34+
id: find-bot-comment
35+
with:
36+
issue-number: ${{ github.event.pull_request.number }}
37+
comment-author: github-actions[bot]
38+
body-includes: 'https://app.codecov.io/github/mongodb/mongodb-atlas-kubernetes/commit'
39+
- name: edit comment if exists
40+
if: github.event_name == 'pull_request' && steps.find-bot-comment.outputs.comment-id != ''
41+
uses: peter-evans/create-or-update-comment@v2
42+
with:
43+
edit-mode: replace
44+
comment-id: ${{ steps.find-bot-comment.outputs.comment-id }}
45+
body: https://app.codecov.io/github/mongodb/mongodb-atlas-kubernetes/commit/${{ github.event.pull_request.head.sha }}
46+
- name: comment PR
47+
if: github.event_name == 'pull_request' && steps.find-bot-comment.outputs.comment-id == ''
48+
env:
49+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
50+
run: |
51+
gh pr comment ${{ github.event.pull_request.number }} -R mongodb/mongodb-atlas-kubernetes -b "https://app.codecov.io/github/mongodb/mongodb-atlas-kubernetes/commit/${{ github.event.pull_request.head.sha }}"
3152
3253
unit-tests:
3354
needs: allowed

test/e2e/actions/deploy/deploy_operator.go

Lines changed: 31 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -2,169 +2,46 @@
22
package deploy
33

44
import (
5+
"context"
56
"fmt"
6-
"os"
7-
"path/filepath"
8-
"strings"
97
"time"
108

11-
"github.com/mongodb/mongodb-atlas-kubernetes/test/e2e/k8s"
12-
13-
"github.com/mongodb/mongodb-atlas-kubernetes/pkg/api/v1/status"
14-
15-
corev1 "k8s.io/api/core/v1"
16-
17-
"github.com/mongodb/mongodb-atlas-kubernetes/test/e2e/actions/kube"
18-
19-
"k8s.io/apimachinery/pkg/types"
20-
21-
"github.com/mongodb/mongodb-atlas-kubernetes/test/e2e/api/atlas"
22-
239
. "github.com/onsi/ginkgo/v2"
2410
. "github.com/onsi/gomega"
11+
corev1 "k8s.io/api/core/v1"
12+
"k8s.io/apimachinery/pkg/types"
13+
"sigs.k8s.io/controller-runtime/pkg/client"
2514

26-
kubecli "github.com/mongodb/mongodb-atlas-kubernetes/test/e2e/cli/kubecli"
27-
"github.com/mongodb/mongodb-atlas-kubernetes/test/e2e/cli/kustomize"
15+
"github.com/mongodb/mongodb-atlas-kubernetes/pkg/api/v1/status"
16+
"github.com/mongodb/mongodb-atlas-kubernetes/test/e2e/actions/kube"
2817
"github.com/mongodb/mongodb-atlas-kubernetes/test/e2e/config"
18+
"github.com/mongodb/mongodb-atlas-kubernetes/test/e2e/k8s"
2919
"github.com/mongodb/mongodb-atlas-kubernetes/test/e2e/model"
3020
"github.com/mongodb/mongodb-atlas-kubernetes/test/e2e/utils"
3121
)
3222

33-
// prepareNamespaceOperatorResources create copy of `/deploy/namespaced` folder with kustomization file for overriding namespace
34-
func prepareNamespaceOperatorResources(input model.UserInputs) {
35-
fullPath := input.GetOperatorFolder()
36-
os.Mkdir(fullPath, os.ModePerm)
37-
utils.CopyFile(config.DefaultNamespacedCRDConfig, filepath.Join(fullPath, "crds.yaml"))
38-
utils.CopyFile(config.DefaultNamespacedOperatorConfig, filepath.Join(fullPath, "namespaced-config.yaml"))
39-
data := []byte(
40-
"namespace: " + input.Namespace + "\n" +
41-
"resources:" + "\n" +
42-
"- crds.yaml" + "\n" +
43-
"- namespaced-config.yaml",
44-
)
45-
utils.SaveToFile(filepath.Join(fullPath, "kustomization.yaml"), data)
46-
}
47-
48-
// CopyKustomizeNamespaceOperator create copy of `/deploy/namespaced` folder with kustomization file for overriding namespace
49-
func prepareWideOperatorResources(input model.UserInputs) {
50-
fullPath := input.GetOperatorFolder()
51-
os.Mkdir(fullPath, os.ModePerm)
52-
utils.CopyFile(config.DefaultClusterWideCRDConfig, filepath.Join(fullPath, "crds.yaml"))
53-
utils.CopyFile(config.DefaultClusterWideOperatorConfig, filepath.Join(fullPath, "clusterwide-config.yaml"))
54-
}
55-
56-
// CopyKustomizeNamespaceOperator create copy of `/deploy/namespaced` folder with kustomization file for overriding namespace
57-
func prepareMultiNamespaceOperatorResources(input model.UserInputs, watchedNamespaces []string) {
58-
fullPath := input.GetOperatorFolder()
59-
err := os.Mkdir(fullPath, os.ModePerm)
60-
Expect(err).ShouldNot(HaveOccurred())
61-
utils.CopyFile(config.DefaultClusterWideCRDConfig, filepath.Join(fullPath, "crds.yaml"))
62-
utils.CopyFile(config.DefaultClusterWideOperatorConfig, filepath.Join(fullPath, "multinamespace-config.yaml"))
63-
namespaces := strings.Join(watchedNamespaces, ",")
64-
patchWatch := []byte(
65-
"apiVersion: apps/v1\n" +
66-
"kind: Deployment\n" +
67-
"metadata:\n" +
68-
" name: mongodb-atlas-operator\n" +
69-
"spec:\n" +
70-
" template:\n" +
71-
" spec:\n" +
72-
" containers:\n" +
73-
" - name: manager\n" +
74-
" env:\n" +
75-
" - name: WATCH_NAMESPACE\n" +
76-
" value: \"" + namespaces + "\"",
77-
)
78-
err = utils.SaveToFile(filepath.Join(fullPath, "patch.yaml"), patchWatch)
79-
Expect(err).ShouldNot(HaveOccurred())
80-
kustomization := []byte(
81-
"resources:\n" +
82-
"- multinamespace-config.yaml\n" +
83-
"patches:\n" +
84-
"- path: patch.yaml\n" +
85-
" target:\n" +
86-
" group: apps\n" +
87-
" version: v1\n" +
88-
" kind: Deployment\n" +
89-
" name: mongodb-atlas-operator",
90-
)
91-
err = utils.SaveToFile(filepath.Join(fullPath, "kustomization.yaml"), kustomization)
92-
Expect(err).ShouldNot(HaveOccurred())
93-
}
94-
95-
func NamespacedOperator(data *model.TestDataProvider) {
96-
prepareNamespaceOperatorResources(data.Resources)
97-
By("Deploy namespaced Operator\n", func() {
98-
kubecli.Apply("-k", data.Resources.GetOperatorFolder())
99-
CheckOperatorRunning(data, data.Resources.Namespace)
100-
})
101-
}
102-
103-
func CheckOperatorRunning(data *model.TestDataProvider, namespace string) {
104-
By("Check Operator is running", func() {
105-
Eventually(
106-
func(g Gomega) string {
107-
status, err := k8s.GetPodStatus(data.Context, data.K8SClient, namespace)
108-
g.Expect(err).ShouldNot(HaveOccurred())
109-
return status
110-
},
111-
"5m", "3s",
112-
).Should(Equal("Running"), "The operator should successfully run")
113-
})
114-
}
115-
116-
func ClusterWideOperator(data *model.TestDataProvider) {
117-
prepareWideOperatorResources(data.Resources)
118-
By("Deploy clusterwide Operator \n", func() {
119-
kubecli.Apply("-k", data.Resources.GetOperatorFolder())
120-
CheckOperatorRunning(data, config.DefaultOperatorNS)
121-
})
122-
}
123-
12423
func MultiNamespaceOperator(data *model.TestDataProvider, watchNamespace []string) {
125-
prepareMultiNamespaceOperatorResources(data.Resources, watchNamespace)
12624
By("Deploy multinamespaced Operator \n", func() {
127-
kustomOperatorPath := data.Resources.GetOperatorFolder() + "/final.yaml"
128-
utils.SaveToFile(kustomOperatorPath, kustomize.Build(data.Resources.GetOperatorFolder()))
129-
kubecli.Apply(kustomOperatorPath)
130-
CheckOperatorRunning(data, config.DefaultOperatorNS)
131-
})
132-
}
133-
134-
func DeleteProject(testData *model.TestDataProvider) {
135-
By("Delete Project", func() {
136-
projectId := testData.Project.Status.ID
137-
Expect(testData.K8SClient.Get(testData.Context, types.NamespacedName{Name: testData.Project.Name, Namespace: testData.Project.Namespace}, testData.Project)).Should(Succeed(), "Get project failed")
138-
Expect(testData.K8SClient.Delete(testData.Context, testData.Project)).Should(Succeed(), "Delete project failed")
139-
aClient := atlas.GetClientOrFail()
140-
Eventually(func(g Gomega) bool {
141-
return aClient.IsProjectExists(g, projectId)
142-
}).WithTimeout(5*time.Minute).WithPolling(20*time.Second).Should(BeTrue(), "Project was not deleted in Atlas")
143-
})
144-
}
145-
146-
func DeleteUsers(testData *model.TestDataProvider) {
147-
By("Delete Users", func() {
148-
for _, user := range testData.Users {
149-
Expect(testData.K8SClient.Get(testData.Context, types.NamespacedName{Name: user.Name, Namespace: user.Namespace}, user)).Should(Succeed(), "Get user failed")
150-
Expect(testData.K8SClient.Delete(testData.Context, user)).Should(Succeed(), "Delete user failed")
151-
}
152-
})
153-
}
154-
155-
func DeleteInitialDeployments(testData *model.TestDataProvider) {
156-
By("Delete initial deployments", func() {
157-
for _, deployment := range testData.InitialDeployments {
158-
projectId := testData.Project.Status.ID
159-
deploymentName := deployment.Spec.DeploymentSpec.Name
160-
Expect(testData.K8SClient.Get(testData.Context, types.NamespacedName{Name: deployment.Name,
161-
Namespace: testData.Resources.Namespace}, deployment)).Should(Succeed(), "Get deployment failed")
162-
Expect(testData.K8SClient.Delete(testData.Context, deployment)).Should(Succeed(), "Deployment %s was not deleted", deployment.Name)
163-
aClient := atlas.GetClientOrFail()
164-
Eventually(func() bool {
165-
return aClient.IsDeploymentExist(projectId, deploymentName)
166-
}).WithTimeout(15*time.Minute).WithPolling(20*time.Second).Should(BeFalse(), "Deployment should be deleted in Atlas")
25+
watchNamespaceMap := make(map[string]bool, len(watchNamespace))
26+
for _, ns := range watchNamespace {
27+
watchNamespaceMap[ns] = true
16728
}
29+
mgr, err := k8s.RunOperator(&k8s.Config{
30+
Namespace: config.DefaultOperatorNS,
31+
GlobalAPISecret: client.ObjectKey{
32+
Namespace: config.DefaultOperatorNS,
33+
Name: config.DefaultOperatorGlobalKey,
34+
},
35+
WatchedNamespaces: watchNamespaceMap,
36+
LogDir: "logs",
37+
})
38+
Expect(err).Should(Succeed())
39+
ctx := context.Background()
40+
go func(ctx context.Context) {
41+
err = mgr.Start(ctx)
42+
Expect(err).Should(Succeed(), "Operator should be started")
43+
}(ctx)
44+
data.ManagerContext = ctx
16845
})
16946
}
17047

@@ -175,8 +52,11 @@ func CreateProject(testData *model.TestDataProvider) {
17552
By(fmt.Sprintf("Deploy Project %s", testData.Project.GetName()), func() {
17653
err := testData.K8SClient.Create(testData.Context, testData.Project)
17754
Expect(err).ShouldNot(HaveOccurred(), "Project %s was not created", testData.Project.GetName())
178-
Eventually(kube.ProjectReadyCondition(testData)).WithTimeout(5*time.Minute).WithPolling(20*time.Second).
179-
Should(Not(Equal("False")), "Project %s should be ready", testData.Project.GetName())
55+
Eventually(func(g Gomega) {
56+
condition, _ := k8s.GetProjectStatusCondition(testData.Context, testData.K8SClient, status.ReadyType,
57+
testData.Resources.Namespace, testData.Project.GetName())
58+
g.Expect(condition).Should(Equal("True"))
59+
}).Should(Succeed(), "Project %s was not created", testData.Project.GetName())
18060
})
18161
By(fmt.Sprintf("Wait for Project %s", testData.Project.GetName()), func() {
18262
Eventually(func() bool {

0 commit comments

Comments
 (0)