Skip to content

Commit 82ba464

Browse files
authored
ensure controllers have a chance to run at least once (#128)
* ensure controllers have a chance to run at least once Signed-off-by: Manabu McCloskey <[email protected]>
1 parent 6f9f0a8 commit 82ba464

File tree

10 files changed

+249
-53
lines changed

10 files changed

+249
-53
lines changed

api/v1alpha1/custom_package_types.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,17 @@ type CustomPackageList struct {
2323

2424
// CustomPackageSpec controls the installation of the custom applications.
2525
type CustomPackageSpec struct {
26-
// Replicate specifies whether to replicate remote or local contents to the local gitea server.
27-
// +kubebuilder:default:=false
28-
Replicate bool `json:"replicate"`
26+
ArgoCD ArgoCDPackageSpec `json:"argoCD,omitempty"`
2927
// GitServerURL specifies the base URL for the git server for API calls.
3028
// for example, https://gitea.cnoe.localtest.me:8443
31-
GitServerURL string `json:"gitServerURL"`
29+
GitServerURL string `json:"gitServerURL"`
30+
GitServerAuthSecretRef SecretReference `json:"gitServerAuthSecretRef"`
3231
// InternalGitServeURL specifies the base URL for the git server accessible within the cluster.
3332
// for example, http://my-gitea-http.gitea.svc.cluster.local:3000
34-
InternalGitServeURL string `json:"internalGitServeURL"`
35-
GitServerAuthSecretRef SecretReference `json:"gitServerAuthSecretRef"`
36-
37-
ArgoCD ArgoCDPackageSpec `json:"argoCD,omitempty"`
33+
InternalGitServeURL string `json:"internalGitServeURL"`
34+
// Replicate specifies whether to replicate remote or local contents to the local gitea server.
35+
// +kubebuilder:default:=false
36+
Replicate bool `json:"replicate"`
3837
}
3938

4039
type ArgoCDPackageSpec struct {

api/v1alpha1/gitrepository_types.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
)
66

77
type GitRepositorySpec struct {
8-
Source GitRepositorySource `json:"source,omitempty"`
98
// GitURL is the base URL of Git server used for API calls.
109
// +kubebuilder:validation:Required
1110
// +kubebuilder:validation:Pattern=`^https?:\/\/.+$`
@@ -14,7 +13,8 @@ type GitRepositorySpec struct {
1413
InternalGitURL string `json:"internalGitURL"`
1514
// SecretRef is the reference to secret that contain Git server credentials
1615
// +kubebuilder:validation:Optional
17-
SecretRef SecretReference `json:"secretRef"`
16+
SecretRef SecretReference `json:"secretRef"`
17+
Source GitRepositorySource `json:"source,omitempty"`
1818
}
1919

2020
type GitRepositorySource struct {
@@ -54,9 +54,8 @@ type GitRepositoryStatus struct {
5454
InternalGitRepositoryUrl string `json:"internalGitRepositoryUrl"`
5555
// Path is the path within the repository that contains the files.
5656
// +kubebuilder:validation:Optional
57-
Path string `json:"path"`
58-
59-
Synced bool `json:"synced"`
57+
Path string `json:"path"`
58+
Synced bool `json:"synced"`
6059
}
6160

6261
// +kubebuilder:object:root=true

api/v1alpha1/localbuild_types.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ import (
77
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
88
)
99

10+
const (
11+
// LastObservedCLIStartTimeAnnotation indicates when the controller acted on a resource.
12+
LastObservedCLIStartTimeAnnotation = "cnoe.io/last-observed-cli-start-time"
13+
// CliStartTimeAnnotation indicates when the CLI was invoked.
14+
CliStartTimeAnnotation = "cnoe.io/cli-start-time"
15+
FieldManager = "idpbuilder"
16+
)
17+
1018
// ArgoPackageConfigSpec Allows for configuration of the ArgoCD Installation.
1119
// If no fields are specified then the binary embedded resources will be used to intall ArgoCD.
1220
type ArgoPackageConfigSpec struct {

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/build/build.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package build
33
import (
44
"context"
55
"fmt"
6-
76
"github.com/cnoe-io/idpbuilder/api/v1alpha1"
87
"github.com/cnoe-io/idpbuilder/globals"
98
"github.com/cnoe-io/idpbuilder/pkg/controllers"
@@ -16,6 +15,7 @@ import (
1615
"sigs.k8s.io/controller-runtime/pkg/client"
1716
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
1817
"sigs.k8s.io/controller-runtime/pkg/manager"
18+
"time"
1919
)
2020

2121
var (
@@ -142,15 +142,20 @@ func (b *Build) Run(ctx context.Context, recreateCluster bool) error {
142142
return err
143143
}
144144

145-
// Create localbuild resource
146145
localBuild := v1alpha1.Localbuild{
147146
ObjectMeta: metav1.ObjectMeta{
148147
Name: b.name,
149148
},
150149
}
151150

151+
cliStartTime := time.Now().Format(time.RFC3339Nano)
152+
152153
setupLog.Info("Creating localbuild resource")
153154
_, err = controllerutil.CreateOrUpdate(ctx, kubeClient, &localBuild, func() error {
155+
if localBuild.ObjectMeta.Annotations == nil {
156+
localBuild.ObjectMeta.Annotations = map[string]string{}
157+
}
158+
localBuild.ObjectMeta.Annotations[v1alpha1.CliStartTimeAnnotation] = cliStartTime
154159
localBuild.Spec = v1alpha1.LocalbuildSpec{
155160
PackageConfigs: v1alpha1.PackageConfigsSpec{
156161
Argo: v1alpha1.ArgoPackageConfigSpec{
@@ -160,7 +165,6 @@ func (b *Build) Run(ctx context.Context, recreateCluster bool) error {
160165
Enabled: true,
161166
},
162167
GitConfig: v1alpha1.GitConfigSpec{
163-
// hint: for the old behavior, replace Type value below with globals.GitServerResourcename()
164168
Type: globals.GiteaResourceName(),
165169
},
166170
CustomPackageDirs: b.customPackageDirs,

pkg/controllers/custompackage/controller.go

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package custompackage
33
import (
44
"context"
55
"fmt"
6+
"github.com/cnoe-io/idpbuilder/pkg/util"
67
"os"
78
"path/filepath"
89
"strings"
@@ -54,10 +55,16 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
5455

5556
func (r *Reconciler) postProcessReconcile(ctx context.Context, req ctrl.Request, pkg *v1alpha1.CustomPackage) {
5657
logger := log.FromContext(ctx)
58+
5759
err := r.Status().Update(ctx, pkg)
5860
if err != nil {
5961
logger.Error(err, "failed updating repo status")
6062
}
63+
64+
err = util.UpdateSyncAnnotation(ctx, r.Client, pkg)
65+
if err != nil {
66+
logger.Error(err, "failed updating repo annotation")
67+
}
6168
}
6269

6370
// create an in-cluster repository CR, update the application spec, then apply
@@ -173,21 +180,30 @@ func (r *Reconciler) reconcileGitRepo(ctx context.Context, resource *v1alpha1.Cu
173180
Name: repoName,
174181
Namespace: resource.Namespace,
175182
},
176-
Spec: v1alpha1.GitRepositorySpec{
183+
}
184+
185+
cliStartTime, _ := util.GetCLIStartTimeAnnotationValue(resource.ObjectMeta.Annotations)
186+
187+
_, err := controllerutil.CreateOrUpdate(ctx, r.Client, repo, func() error {
188+
if err := controllerutil.SetControllerReference(resource, repo, r.Scheme); err != nil {
189+
return err
190+
}
191+
192+
if repo.ObjectMeta.Annotations == nil {
193+
repo.ObjectMeta.Annotations = make(map[string]string)
194+
}
195+
util.SetCLIStartTimeAnnotationValue(repo.ObjectMeta.Annotations, cliStartTime)
196+
197+
repo.Spec = v1alpha1.GitRepositorySpec{
177198
Source: v1alpha1.GitRepositorySource{
178199
Type: "local",
179200
Path: absPath,
180201
},
181202
GitURL: resource.Spec.GitServerURL,
182203
InternalGitURL: resource.Spec.InternalGitServeURL,
183204
SecretRef: resource.Spec.GitServerAuthSecretRef,
184-
},
185-
}
186-
187-
_, err := controllerutil.CreateOrUpdate(ctx, r.Client, repo, func() error {
188-
if err := controllerutil.SetControllerReference(resource, repo, r.Scheme); err != nil {
189-
return err
190205
}
206+
191207
return nil
192208
})
193209
// it's possible for an application to specify the same directory multiple times in the spec.

pkg/controllers/gitrepository/controller.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,12 @@ func (r *RepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Request)
9797
return ctrl.Result{}, client.IgnoreNotFound(err)
9898
}
9999

100+
defer r.postProcessReconcile(ctx, req, &gitRepo)
100101
if !r.shouldProcess(gitRepo) {
101102
return ctrl.Result{Requeue: false}, nil
102103
}
103104

104105
logger.Info("reconciling GitRepository", "name", req.Name, "namespace", req.Namespace)
105-
defer r.postProcessReconcile(ctx, req, &gitRepo)
106-
107106
result, err := r.reconcileGitRepo(ctx, &gitRepo)
108107
if err != nil {
109108
r.Recorder.Event(&gitRepo, "Warning", "reconcile error", err.Error())
@@ -116,16 +115,22 @@ func (r *RepositoryReconciler) Reconcile(ctx context.Context, req ctrl.Request)
116115

117116
func (r *RepositoryReconciler) postProcessReconcile(ctx context.Context, req ctrl.Request, repo *v1alpha1.GitRepository) {
118117
logger := log.FromContext(ctx)
118+
119119
err := r.Status().Update(ctx, repo)
120120
if err != nil {
121121
logger.Error(err, "failed updating repo status")
122122
}
123+
124+
err = util.UpdateSyncAnnotation(ctx, r.Client, repo)
125+
if err != nil {
126+
logger.Error(err, "failed updating repo annotation")
127+
}
123128
}
124129

125130
func (r *RepositoryReconciler) reconcileGitRepo(ctx context.Context, repo *v1alpha1.GitRepository) (ctrl.Result, error) {
126131
logger := log.FromContext(ctx)
127132
logger.Info("reconciling", "name", repo.Name, "dir", repo.Spec.Source)
128-
133+
repo.Status.Synced = false
129134
tr := &http.Transport{
130135
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
131136
}
@@ -147,13 +152,14 @@ func (r *RepositoryReconciler) reconcileGitRepo(ctx context.Context, repo *v1alp
147152
if err != nil {
148153
return ctrl.Result{Requeue: true, RequeueAfter: requeueTime}, fmt.Errorf("failed to create or update repo %w", err)
149154
}
150-
repo.Status.ExternalGitRepositoryUrl = giteaRepo.CloneURL
151-
repo.Status.InternalGitRepositoryUrl = getRepositoryURL(repo.Namespace, repo.Name, repo.Spec.InternalGitURL)
152155

153156
err = r.reconcileRepoContent(ctx, repo, giteaRepo)
154157
if err != nil {
155158
return ctrl.Result{Requeue: true, RequeueAfter: requeueTime}, fmt.Errorf("failed to reconcile repo content %w", err)
156159
}
160+
161+
repo.Status.ExternalGitRepositoryUrl = giteaRepo.CloneURL
162+
repo.Status.InternalGitRepositoryUrl = getRepositoryURL(repo.Namespace, repo.Name, repo.Spec.InternalGitURL)
157163
repo.Status.Synced = true
158164
return ctrl.Result{Requeue: true, RequeueAfter: requeueTime}, nil
159165
}

pkg/controllers/gitrepository/controller_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"os"
88
"path/filepath"
99
"reflect"
10+
ctrl "sigs.k8s.io/controller-runtime"
1011
"testing"
1112
"time"
1213

@@ -60,6 +61,7 @@ type testCase struct {
6061

6162
type fakeClient struct {
6263
client.Client
64+
patchObj client.Object
6365
}
6466

6567
func (f *fakeClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object) error {
@@ -71,6 +73,23 @@ func (f *fakeClient) Get(ctx context.Context, key client.ObjectKey, obj client.O
7173
return nil
7274
}
7375

76+
func (f *fakeClient) Status() client.StatusWriter {
77+
return fakeStatusWriter{}
78+
}
79+
80+
func (f *fakeClient) Patch(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.PatchOption) error {
81+
f.patchObj = obj
82+
return nil
83+
}
84+
85+
type fakeStatusWriter struct {
86+
client.StatusWriter
87+
}
88+
89+
func (f fakeStatusWriter) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error {
90+
return nil
91+
}
92+
7493
func setUpLocalRepo() (string, string, error) {
7594
repoDir, err := os.MkdirTemp("", fmt.Sprintf("test"))
7695
if err != nil {
@@ -377,6 +396,41 @@ func TestGitRepositoryReconcile(t *testing.T) {
377396
if !reflect.DeepEqual(v.input.Status, v.expect.resource) {
378397
t.Fatalf("objects not equal")
379398
}
399+
380400
})
381401
}
382402
}
403+
404+
func TestGitRepositoryPostReconcile(t *testing.T) {
405+
c := fakeClient{}
406+
reconciler := RepositoryReconciler{
407+
Client: &c,
408+
}
409+
testTime := time.Now().Format(time.RFC3339Nano)
410+
repo := v1alpha1.GitRepository{
411+
ObjectMeta: metav1.ObjectMeta{
412+
Name: "test",
413+
Namespace: "test",
414+
Annotations: map[string]string{
415+
v1alpha1.CliStartTimeAnnotation: testTime,
416+
},
417+
},
418+
}
419+
420+
reconciler.postProcessReconcile(context.Background(), ctrl.Request{}, &repo)
421+
annotations := c.patchObj.GetAnnotations()
422+
v, ok := annotations[v1alpha1.LastObservedCLIStartTimeAnnotation]
423+
if !ok {
424+
t.Fatalf("expected annotation not found: %s", v1alpha1.LastObservedCLIStartTimeAnnotation)
425+
}
426+
if v != testTime {
427+
t.Fatalf("annotation values does not match")
428+
}
429+
430+
repo.Annotations[v1alpha1.LastObservedCLIStartTimeAnnotation] = "abc"
431+
reconciler.postProcessReconcile(context.Background(), ctrl.Request{}, &repo)
432+
v = annotations[v1alpha1.LastObservedCLIStartTimeAnnotation]
433+
if v != testTime {
434+
t.Fatalf("annotation values does not match")
435+
}
436+
}

0 commit comments

Comments
 (0)