88 "context"
99 "errors"
1010 "fmt"
11+ "time"
1112
1213 "github.com/fluxcd/pkg/apis/meta"
1314 "github.com/fluxcd/pkg/runtime/conditions"
@@ -18,22 +19,26 @@ import (
1819 "k8s.io/apimachinery/pkg/runtime"
1920 "k8s.io/apimachinery/pkg/types"
2021 ctrl "sigs.k8s.io/controller-runtime"
22+ "sigs.k8s.io/controller-runtime/pkg/builder"
2123 "sigs.k8s.io/controller-runtime/pkg/client"
2224 "sigs.k8s.io/controller-runtime/pkg/log"
25+ "sigs.k8s.io/controller-runtime/pkg/predicate"
2326
2427 ocmv1 "github.com/open-component-model/ocm-controller/api/v1alpha1"
2528
2629 "github.com/open-component-model/git-controller/apis/delivery/v1alpha1"
2730 mpasv1alpha1 "github.com/open-component-model/git-controller/apis/mpas/v1alpha1"
28- providers "github.com/open-component-model/git-controller/pkg"
31+ "github.com/open-component-model/git-controller/pkg"
32+ "github.com/open-component-model/git-controller/pkg/providers"
2933)
3034
3135// SyncReconciler reconciles a Sync object
3236type SyncReconciler struct {
3337 client.Client
3438 Scheme * runtime.Scheme
3539
36- Git providers.Git
40+ Git pkg.Git
41+ Provider providers.Provider
3742}
3843
3944//+kubebuilder:rbac:groups=delivery.ocm.software,resources=syncs,verbs=get;list;watch;create;update;patch;delete
@@ -53,7 +58,7 @@ func (r *SyncReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.
5358 )
5459
5560 log := log .FromContext (ctx )
56- log . V ( 4 ). Info ( "starting reconcile loop for snapshot" )
61+
5762 obj := & v1alpha1.Sync {}
5863 if err := r .Get (ctx , req .NamespacedName , obj ); err != nil {
5964 if apierrors .IsNotFound (err ) {
@@ -139,6 +144,8 @@ func (r *SyncReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.
139144 return ctrl.Result {}, retErr
140145 }
141146
147+ log .V (4 ).Info ("found target snapshot" )
148+
142149 repository := & mpasv1alpha1.Repository {}
143150 if err := r .Get (ctx , types.NamespacedName {
144151 Namespace : obj .Namespace ,
@@ -150,6 +157,8 @@ func (r *SyncReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.
150157 return ctrl.Result {}, retErr
151158 }
152159
160+ log .V (4 ).Info ("found target repository" )
161+
153162 authSecret := & corev1.Secret {}
154163 if err := r .Get (ctx , types.NamespacedName {
155164 Namespace : obj .Namespace ,
@@ -161,16 +170,35 @@ func (r *SyncReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.
161170 return ctrl.Result {}, retErr
162171 }
163172
173+ log .V (4 ).Info ("found authentication secret" )
174+
175+ baseBranch := obj .Spec .CommitTemplate .BaseBranch
176+ if baseBranch == "" {
177+ baseBranch = "main"
178+ }
179+ targetBranch := obj .Spec .CommitTemplate .TargetBranch
180+ if targetBranch == "" && obj .Spec .AutomaticPullRequestCreation {
181+ targetBranch = fmt .Sprintf ("branch-%d" , time .Now ().Unix ())
182+ } else if targetBranch == "" && ! obj .Spec .AutomaticPullRequestCreation {
183+ retErr = fmt .Errorf ("branch cannot be empty if automatic pull request creation is not enabled" )
184+ conditions .MarkFalse (obj , meta .ReadyCondition , v1alpha1 .GitRepositoryPushFailedReason , retErr .Error ())
185+
186+ return ctrl.Result {}, retErr
187+ }
188+
189+ log .Info ("preparing to push snapshot content" , "base" , baseBranch , "target" , targetBranch )
190+
164191 // trim any trailing `/` and then just add.
165192 log .V (4 ).Info ("crafting artifact URL to download from" , "url" , snapshot .Status .RepositoryURL )
166- opts := & providers.PushOptions {
167- URL : repository .GetRepositoryURL (),
168- Message : obj .Spec .CommitTemplate .Message ,
169- Name : obj .Spec .CommitTemplate .Name ,
170- Email : obj .Spec .CommitTemplate .Email ,
171- Snapshot : snapshot ,
172- Branch : obj .Spec .Branch ,
173- SubPath : obj .Spec .SubPath ,
193+ opts := & pkg.PushOptions {
194+ URL : repository .GetRepositoryURL (),
195+ Message : obj .Spec .CommitTemplate .Message ,
196+ Name : obj .Spec .CommitTemplate .Name ,
197+ Email : obj .Spec .CommitTemplate .Email ,
198+ Snapshot : snapshot ,
199+ BaseBranch : baseBranch ,
200+ TargetBranch : targetBranch ,
201+ SubPath : obj .Spec .SubPath ,
174202 }
175203
176204 r .parseAuthSecret (authSecret , opts )
@@ -183,27 +211,42 @@ func (r *SyncReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.
183211 return ctrl.Result {}, retErr
184212 }
185213
214+ log .Info ("target content pushed with digest" , "base" , baseBranch , "target" , targetBranch , "digest" , digest )
215+
186216 obj .Status .Digest = digest
187217
218+ if obj .Spec .AutomaticPullRequestCreation {
219+ log .Info ("automatic pull-request creation is enabled, preparing to create a pull request" )
220+
221+ if err := r .Provider .CreatePullRequest (ctx , targetBranch , * obj , * repository ); err != nil {
222+ retErr = fmt .Errorf ("failed to create pull request: %w" , err )
223+ conditions .MarkFalse (obj , meta .ReadyCondition , v1alpha1 .CreatePullRequestFailedReason , retErr .Error ())
224+
225+ return ctrl.Result {}, retErr
226+ }
227+ }
228+
188229 // Remove any stale Ready condition, most likely False, set above. Its value
189230 // is derived from the overall result of the reconciliation in the deferred
190231 // block at the very end.
191232 conditions .Delete (obj , meta .ReadyCondition )
192233
234+ log .Info ("successfully reconciled sync object" )
235+
193236 return result , retErr
194237}
195238
196239// SetupWithManager sets up the controller with the Manager.
197240func (r * SyncReconciler ) SetupWithManager (mgr ctrl.Manager ) error {
198241 return ctrl .NewControllerManagedBy (mgr ).
199- For (& v1alpha1.Sync {}).
242+ For (& v1alpha1.Sync {}, builder . WithPredicates (predicate. GenerationChangedPredicate {}) ).
200243 Complete (r )
201244}
202245
203- func (r * SyncReconciler ) parseAuthSecret (secret * corev1.Secret , opts * providers .PushOptions ) {
246+ func (r * SyncReconciler ) parseAuthSecret (secret * corev1.Secret , opts * pkg .PushOptions ) {
204247 if _ , ok := secret .Data ["identity" ]; ok {
205- opts .Auth = & providers .Auth {
206- SSH : & providers .SSH {
248+ opts .Auth = & pkg .Auth {
249+ SSH : & pkg .SSH {
207250 PemBytes : secret .Data ["identity" ],
208251 User : string (secret .Data ["username" ]),
209252 Password : string (secret .Data ["password" ]),
@@ -212,8 +255,8 @@ func (r *SyncReconciler) parseAuthSecret(secret *corev1.Secret, opts *providers.
212255 return
213256 }
214257 // default to basic auth.
215- opts .Auth = & providers .Auth {
216- BasicAuth : & providers .BasicAuth {
258+ opts .Auth = & pkg .Auth {
259+ BasicAuth : & pkg .BasicAuth {
217260 Username : string (secret .Data ["username" ]),
218261 Password : string (secret .Data ["password" ]),
219262 },
0 commit comments