Skip to content

Commit ea8e5f8

Browse files
committed
Start of integration tests.
1 parent b4e93e3 commit ea8e5f8

2 files changed

Lines changed: 168 additions & 2 deletions

File tree

internal/controller/polledrepository_controller.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ import (
3030
ctrl "sigs.k8s.io/controller-runtime"
3131
"sigs.k8s.io/controller-runtime/pkg/client"
3232
"sigs.k8s.io/controller-runtime/pkg/predicate"
33-
"sigs.k8s.io/controller-runtime/pkg/reconcile"
3433

3534
pollingv1 "github.com/gitops-tools/gitpoller-controller/api/v1alpha1"
3635
"github.com/gitops-tools/gitpoller-controller/pkg/git"
@@ -64,6 +63,7 @@ type PolledRepositoryReconciler struct {
6463
// Reconcile is part of the main Kubernetes reconciliation loop which aims to
6564
// move the current state of the cluster closer to the desired state.
6665
func (r *PolledRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
66+
6767
reqLogger := logr.FromContextOrDiscard(ctx)
6868
reqLogger.Info("reconciling PolledRepository")
6969

@@ -85,13 +85,20 @@ func (r *PolledRepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Req
8585

8686
authToken, err := r.authTokenForRepo(ctx, reqLogger, req.Namespace, repo)
8787
if err != nil {
88+
// TODO: Patch this!
89+
repo.Status.LastError = err.Error()
8890
reqLogger.Error(err, "Getting the auth token failed")
89-
return reconcile.Result{}, fmt.Errorf("failed to get auth token: %w", err)
91+
if err := r.Client.Status().Update(ctx, &repo); err != nil {
92+
reqLogger.Error(err, "unable to update Repository status")
93+
}
94+
// TODO: improve the error
95+
return ctrl.Result{}, err
9096
}
9197

9298
// TODO: handle pollerFactory returning nil/error
9399
newStatus, commit, err := r.PollerFactory(r.HTTPClient, &repo, endpoint, authToken).Poll(ctx, repoName, repo.Status.PollStatus)
94100
if err != nil {
101+
// TODO: Patch this!
95102
repo.Status.LastError = err.Error()
96103
reqLogger.Error(err, "repository poll failed")
97104
if err := r.Client.Status().Update(ctx, &repo); err != nil {
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/*
2+
Copyright 2024.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package controller
18+
19+
import (
20+
"context"
21+
"encoding/json"
22+
"net/http"
23+
"net/http/httptest"
24+
"os"
25+
"path/filepath"
26+
"testing"
27+
"time"
28+
29+
pollingv1 "github.com/gitops-tools/gitpoller-controller/api/v1alpha1"
30+
"github.com/gitops-tools/gitpoller-controller/pkg/cloudevents"
31+
"github.com/gitops-tools/gitpoller-controller/pkg/git"
32+
"github.com/gitops-tools/gitpoller-controller/test/utils"
33+
"github.com/google/go-cmp/cmp"
34+
"github.com/onsi/gomega"
35+
corev1 "k8s.io/api/core/v1"
36+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
37+
"k8s.io/apimachinery/pkg/runtime"
38+
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
39+
ctrl "sigs.k8s.io/controller-runtime"
40+
"sigs.k8s.io/controller-runtime/pkg/client"
41+
"sigs.k8s.io/controller-runtime/pkg/envtest"
42+
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
43+
)
44+
45+
const (
46+
timeout = 5 * time.Second
47+
)
48+
49+
func TestPolledRepositoryController(t *testing.T) {
50+
if os.Getenv("KUBEBUILDER_ASSETS") == "" {
51+
t.Skip("Not setup for envtest correctly please set KUBEBUILDER_ASSETS")
52+
}
53+
54+
scheme := runtime.NewScheme()
55+
utils.AssertNoError(t, clientgoscheme.AddToScheme(scheme))
56+
utils.AssertNoError(t, pollingv1.AddToScheme(scheme))
57+
58+
testEnv := &envtest.Environment{
59+
CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
60+
ErrorIfCRDPathMissing: true,
61+
}
62+
63+
cfg, err := testEnv.Start()
64+
if err != nil {
65+
t.Fatalf("failed to start test environment: %s", err)
66+
}
67+
68+
t.Cleanup(func() {
69+
if err := testEnv.Stop(); err != nil {
70+
t.Fatalf("failed to stop test environment: %s", err)
71+
}
72+
})
73+
74+
k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{
75+
Scheme: scheme,
76+
Metrics: metricsserver.Options{
77+
BindAddress: "0", // Do not start the metrics server.
78+
},
79+
})
80+
utils.AssertNoError(t, err)
81+
82+
k8sClient := k8sManager.GetClient()
83+
84+
if err := (&PolledRepositoryReconciler{
85+
Client: k8sClient,
86+
Scheme: scheme,
87+
HTTPClient: http.DefaultClient,
88+
PollerFactory: func(cl *http.Client, repo *pollingv1.PolledRepository, endpoint, token string) git.CommitPoller {
89+
return MakeCommitPoller(cl, repo, endpoint, token)
90+
},
91+
EventDispatcher: cloudevents.CloudEventDispatcher{},
92+
}).SetupWithManager(k8sManager); err != nil {
93+
t.Fatalf("Failed to start PolledRepositoryReconciler: %v", err)
94+
}
95+
96+
ctx, cancel := context.WithCancel(context.Background())
97+
defer cancel()
98+
99+
go func() {
100+
utils.AssertNoError(t, k8sManager.Start(ctx))
101+
}()
102+
103+
<-k8sManager.Elected()
104+
105+
utils.AssertNoError(t, k8sClient.Create(context.Background(), &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "testing"}}))
106+
107+
t.Run("polling a github repository", func(t *testing.T) {
108+
dispatches := make(chan map[string]any, 1)
109+
ts := httptest.NewServer(http.HandlerFunc(notificationHandler(dispatches)))
110+
t.Cleanup(ts.Close)
111+
112+
repo := newPolledRepository(withEndpoint(ts.URL))
113+
repoKey := client.ObjectKeyFromObject(repo)
114+
115+
utils.AssertNoError(t, k8sClient.Create(context.Background(), repo))
116+
t.Cleanup(func() {
117+
deleteObject(t, k8sClient, repo)
118+
})
119+
120+
gomega.NewWithT(t).Eventually(func() string {
121+
utils.AssertNoError(t, client.IgnoreNotFound(k8sClient.Get(context.Background(), repoKey, repo)))
122+
return repo.Status.PollStatus.SHA
123+
}, timeout).Should(gomega.Not(gomega.Equal("")))
124+
125+
received := <-dispatches
126+
127+
if diff := cmp.Diff(repo.Status.PollStatus.SHA, received["sha"]); diff != "" {
128+
t.Errorf("incorrect notification: diff -want +got\n%s", diff)
129+
}
130+
})
131+
}
132+
133+
func deleteObject(t *testing.T, cl client.Client, obj client.Object) {
134+
t.Helper()
135+
if err := cl.Delete(context.TODO(), obj); err != nil {
136+
t.Fatal(err)
137+
}
138+
}
139+
140+
func notificationHandler(dispatches chan<- map[string]any) func(http.ResponseWriter, *http.Request) {
141+
return func(w http.ResponseWriter, req *http.Request) {
142+
decoder := json.NewDecoder(req.Body)
143+
// TODO: This could do so much more, but we have tests for the
144+
// CloudEvent dispatcher so maybe not?
145+
result := map[string]any{}
146+
if err := decoder.Decode(&result); err != nil {
147+
http.Error(w, "failed to decode", http.StatusBadRequest)
148+
return
149+
}
150+
151+
dispatches <- result
152+
}
153+
}
154+
155+
func withEndpoint(s string) func(*pollingv1.PolledRepository) {
156+
return func(r *pollingv1.PolledRepository) {
157+
r.Spec.Endpoint = s
158+
}
159+
}

0 commit comments

Comments
 (0)