diff --git a/api/go.mod b/api/go.mod index ccb28917..d7fdc65f 100644 --- a/api/go.mod +++ b/api/go.mod @@ -4,7 +4,7 @@ go 1.20 require ( github.com/fluxcd/pkg/apis/meta v1.1.2 - github.com/fluxcd/source-controller/api v1.0.1 + github.com/fluxcd/source-controller/api v1.1.0 k8s.io/apimachinery v0.27.4 sigs.k8s.io/controller-runtime v0.15.1 ) diff --git a/api/go.sum b/api/go.sum index cff25298..59e3cdea 100644 --- a/api/go.sum +++ b/api/go.sum @@ -4,8 +4,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fluxcd/pkg/apis/meta v1.1.2 h1:Unjo7hxadtB2dvGpeFqZZUdsjpRA08YYSBb7dF2WIAM= github.com/fluxcd/pkg/apis/meta v1.1.2/go.mod h1:BHQyRHCskGMEDf6kDGbgQ+cyiNpUHbLsCOsaMYM2maI= -github.com/fluxcd/source-controller/api v1.0.1 h1:nycylbNBnQd+EO4UHpqXqAQJ1cGAPxgBbrfERCQ1pp8= -github.com/fluxcd/source-controller/api v1.0.1/go.mod h1:rAY5FRFGZUKpIFNyYANHIgPgJPvbALBHWsq/zHw/cXQ= +github.com/fluxcd/source-controller/api v1.1.0 h1:JPtt9WTTqVNdJfPpea8q7fUWF/00kDihxbhISzcb0WE= +github.com/fluxcd/source-controller/api v1.1.0/go.mod h1:ZLkaUd1KQIjtLPCvO63Ni5zpnSTVBAkeRgFBzMItbDQ= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= diff --git a/go.mod b/go.mod index d119049e..9810fb33 100644 --- a/go.mod +++ b/go.mod @@ -20,12 +20,12 @@ require ( github.com/fluxcd/pkg/apis/acl v0.1.0 github.com/fluxcd/pkg/apis/event v0.5.2 github.com/fluxcd/pkg/apis/meta v1.1.2 - github.com/fluxcd/pkg/git v0.12.4 - github.com/fluxcd/pkg/git/gogit v0.12.1 - github.com/fluxcd/pkg/gittestserver v0.8.5 + github.com/fluxcd/pkg/git v0.13.0 + github.com/fluxcd/pkg/git/gogit v0.13.0 + github.com/fluxcd/pkg/gittestserver v0.8.6 github.com/fluxcd/pkg/runtime v0.42.0 - github.com/fluxcd/pkg/ssh v0.8.1 - github.com/fluxcd/source-controller/api v1.0.1 + github.com/fluxcd/pkg/ssh v0.8.2 + github.com/fluxcd/source-controller/api v1.1.0 github.com/go-git/go-billy/v5 v5.4.1 github.com/go-git/go-git/v5 v5.8.1 github.com/go-logr/logr v1.2.4 @@ -125,13 +125,13 @@ require ( go.starlark.net v0.0.0-20221028183056-acb66ad56dd2 // indirect go.uber.org/multierr v1.10.0 // indirect go.uber.org/zap v1.25.0 // indirect - golang.org/x/crypto v0.11.0 // indirect + golang.org/x/crypto v0.12.0 // indirect golang.org/x/mod v0.10.0 // indirect golang.org/x/net v0.13.0 // indirect golang.org/x/oauth2 v0.8.0 // indirect - golang.org/x/sys v0.10.0 // indirect - golang.org/x/term v0.10.0 // indirect - golang.org/x/text v0.11.0 // indirect + golang.org/x/sys v0.11.0 // indirect + golang.org/x/term v0.11.0 // indirect + golang.org/x/text v0.12.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.9.3 // indirect gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect diff --git a/go.sum b/go.sum index 1d59ee33..e119036a 100644 --- a/go.sum +++ b/go.sum @@ -59,7 +59,7 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0= +github.com/elazarl/goproxy v0.0.0-20230731152917-f99041a5c027 h1:1L0aalTpPz7YlMxETKpmQoWMBkeiuorElZIXoNmgiPE= github.com/emicklei/go-restful/v3 v3.10.0 h1:X4gma4HM7hFm6WMeAsTfqA0GOfdNoCzBIkHGoRLGXuM= github.com/emicklei/go-restful/v3 v3.10.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= @@ -89,20 +89,20 @@ github.com/fluxcd/pkg/apis/event v0.5.2 h1:WtnCOeWglf7wR3dpyiWxb1JtYkw1G5OXcERb1 github.com/fluxcd/pkg/apis/event v0.5.2/go.mod h1:5l6SSxVTkqrXrYjgEqAajOOHkl4x0TPocAuSdu+3AEs= github.com/fluxcd/pkg/apis/meta v1.1.2 h1:Unjo7hxadtB2dvGpeFqZZUdsjpRA08YYSBb7dF2WIAM= github.com/fluxcd/pkg/apis/meta v1.1.2/go.mod h1:BHQyRHCskGMEDf6kDGbgQ+cyiNpUHbLsCOsaMYM2maI= -github.com/fluxcd/pkg/git v0.12.4 h1:COuVYUL+gqMOYAm6oD32Vwcmy/8WVsT/nMk8ps0lpJI= -github.com/fluxcd/pkg/git v0.12.4/go.mod h1:rKB1puk7sbC4AYF1oZDBrkvu3cr0aibkd4I5yNbxSQg= -github.com/fluxcd/pkg/git/gogit v0.12.1 h1:06jzHOTntYN5xCSQvyFXtLXdqoP8crLh7VYgtXS9+wo= -github.com/fluxcd/pkg/git/gogit v0.12.1/go.mod h1:Z4Ysp8VifKTvWpjJMKncJsgb2iBqHuIeK80VGjlU41Y= -github.com/fluxcd/pkg/gittestserver v0.8.5 h1:EGqDF4240xPRgW1FFrQAs0Du7fZb8OGXC5qKDIqyXD8= -github.com/fluxcd/pkg/gittestserver v0.8.5/go.mod h1:SyGEh+OBzFpdlTWWqv3XBkiLB42Iu+mijfIQ4hPlEZQ= +github.com/fluxcd/pkg/git v0.13.0 h1:GcJfldYqw6ELf0vbTCV+iFZgSpK6HZBKx3yAvn1Dqfg= +github.com/fluxcd/pkg/git v0.13.0/go.mod h1:rKB1puk7sbC4AYF1oZDBrkvu3cr0aibkd4I5yNbxSQg= +github.com/fluxcd/pkg/git/gogit v0.13.0 h1:XCwfiB5qbz08djUgo0TII09zibH97Hn56v098pkFpns= +github.com/fluxcd/pkg/git/gogit v0.13.0/go.mod h1:V3g+UyIDSAOysg5KCpHhS+HXBUmNmmbNlVruWkpCJgY= +github.com/fluxcd/pkg/gittestserver v0.8.6 h1:YM8prVKB3LC9LBBe+a2p7l1BlfV9erXCgC1em9sbqW4= +github.com/fluxcd/pkg/gittestserver v0.8.6/go.mod h1:3abUQFRNlfBhn+BD+TI2lfXI/JkdntdQ99spSnItFk4= github.com/fluxcd/pkg/runtime v0.42.0 h1:a5DQ/f90YjoHBmiXZUpnp4bDSLORjInbmqP7K11L4uY= github.com/fluxcd/pkg/runtime v0.42.0/go.mod h1:p6A3xWVV8cKLLQW0N90GehKgGMMmbNYv+OSJ/0qB0vg= -github.com/fluxcd/pkg/ssh v0.8.1 h1:v35y7Ks/+ABWce8RcnrC7psVIhf3EdCUNFJi5+tYOps= -github.com/fluxcd/pkg/ssh v0.8.1/go.mod h1:M1ouDXuDG+QuhGB4JYEjCNCykNytLJGDhwKn9y4DEOE= +github.com/fluxcd/pkg/ssh v0.8.2 h1:WNfvTmnLnOUyXQDb8luSfmn1X0RIuhJBcKMFtKm6YsQ= +github.com/fluxcd/pkg/ssh v0.8.2/go.mod h1:ewbU9vakYYdGSX92qXhx6Kqi5tVQ3ppmGQakCX1R6Gw= github.com/fluxcd/pkg/version v0.2.2 h1:ZpVXECeLA5hIQMft11iLp6gN3cKcz6UNuVTQPw/bRdI= github.com/fluxcd/pkg/version v0.2.2/go.mod h1:NGnh/no8S6PyfCDxRFrPY3T5BUnqP48MxfxNRU0z8C0= -github.com/fluxcd/source-controller/api v1.0.1 h1:nycylbNBnQd+EO4UHpqXqAQJ1cGAPxgBbrfERCQ1pp8= -github.com/fluxcd/source-controller/api v1.0.1/go.mod h1:rAY5FRFGZUKpIFNyYANHIgPgJPvbALBHWsq/zHw/cXQ= +github.com/fluxcd/source-controller/api v1.1.0 h1:JPtt9WTTqVNdJfPpea8q7fUWF/00kDihxbhISzcb0WE= +github.com/fluxcd/source-controller/api v1.1.0/go.mod h1:ZLkaUd1KQIjtLPCvO63Ni5zpnSTVBAkeRgFBzMItbDQ= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= @@ -347,8 +347,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -423,16 +423,16 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= -golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= +golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -442,8 +442,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/internal/controller/imageupdateautomation_controller.go b/internal/controller/imageupdateautomation_controller.go index bda3c386..872663a3 100644 --- a/internal/controller/imageupdateautomation_controller.go +++ b/internal/controller/imageupdateautomation_controller.go @@ -31,6 +31,7 @@ import ( "github.com/ProtonMail/go-crypto/openpgp" securejoin "github.com/cyphar/filepath-securejoin" extgogit "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing/transport" "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -256,9 +257,20 @@ func (r *ImageUpdateAutomationReconciler) Reconcile(ctx context.Context, req ctr tracelog.Info("using push branch from $ref.branch", "branch", pushBranch) } - debuglog.Info("attempting to clone git repository", "gitrepository", originName, "ref", checkoutRef, "working", tmp) + authOpts, err := r.getAuthOpts(ctx, &origin) + if err != nil { + return failWithError(err) + } + var proxyOpts *transport.ProxyOptions + if origin.Spec.ProxySecretRef != nil { + proxyOpts, err = r.getProxyOpts(ctx, origin.Spec.ProxySecretRef.Name, origin.GetNamespace()) + if err != nil { + return failWithError(err) + } + } - gitClient, err := r.constructGitClient(ctx, &origin, tmp, switchBranch) + clientOpts := r.getGitClientOpts(authOpts.Transport, proxyOpts, switchBranch) + gitClient, err := gogit.NewClient(tmp, authOpts, clientOpts...) if err != nil { return failWithError(err) } @@ -279,6 +291,7 @@ func (r *ImageUpdateAutomationReconciler) Reconcile(ctx context.Context, req ctr // Use the git operations timeout for the repo. cloneCtx, cancel := context.WithTimeout(ctx, origin.Spec.Timeout.Duration) defer cancel() + debuglog.Info("attempting to clone git repository", "gitrepository", originName, "ref", checkoutRef, "working", tmp) if _, err := gitClient.Clone(cloneCtx, origin.Spec.URL, opts); err != nil { return failWithError(err) } @@ -506,6 +519,31 @@ func intervalOrDefault(auto *imagev1.ImageUpdateAutomation) time.Duration { return auto.Spec.Interval.Duration } +func (r *ImageUpdateAutomationReconciler) getGitClientOpts(gitTransport git.TransportType, proxyOpts *transport.ProxyOptions, + diffPushBranch bool) []gogit.ClientOption { + clientOpts := []gogit.ClientOption{gogit.WithDiskStorage()} + if gitTransport == git.HTTP { + clientOpts = append(clientOpts, gogit.WithInsecureCredentialsOverHTTP()) + } + + if proxyOpts != nil { + clientOpts = append(clientOpts, gogit.WithProxy(*proxyOpts)) + } + + // If the push branch is different from the checkout ref, we need to + // have all the references downloaded at clone time, to ensure that + // SwitchBranch will have access to the target branch state. fluxcd/flux2#3384 + // + // To always overwrite the push branch, the feature gate + // GitAllBranchReferences can be set to false, which will cause + // the SwitchBranch operation to ignore the remote branch state. + allReferences := r.features[features.GitAllBranchReferences] + if diffPushBranch { + clientOpts = append(clientOpts, gogit.WithSingleBranch(!allReferences)) + } + return clientOpts +} + // automationsForGitRepo fetches all the automations that refer to a // particular source.GitRepository object. func (r *ImageUpdateAutomationReconciler) automationsForGitRepo(ctx context.Context, obj client.Object) []reconcile.Request { @@ -541,20 +579,17 @@ func (r *ImageUpdateAutomationReconciler) automationsForImagePolicy(ctx context. return reqs } +// getAuthOpts fetches the secret containing the auth options (if specified), +// constructs a git.AuthOptions object using those options along with the provided +// repository's URL and returns it. func (r *ImageUpdateAutomationReconciler) getAuthOpts(ctx context.Context, repository *sourcev1.GitRepository) (*git.AuthOptions, error) { var data map[string][]byte + var err error if repository.Spec.SecretRef != nil { - name := types.NamespacedName{ - Namespace: repository.GetNamespace(), - Name: repository.Spec.SecretRef.Name, - } - - secret := &corev1.Secret{} - err := r.Client.Get(ctx, name, secret) + data, err = r.getSecretData(ctx, repository.Spec.SecretRef.Name, repository.GetNamespace()) if err != nil { - return nil, fmt.Errorf("failed to get secret '%s': %w", name.String(), err) + return nil, fmt.Errorf("failed to get auth secret '%s/%s': %w", repository.GetNamespace(), repository.Spec.SecretRef.Name, err) } - data = secret.Data } u, err := url.Parse(repository.Spec.URL) @@ -570,36 +605,37 @@ func (r *ImageUpdateAutomationReconciler) getAuthOpts(ctx context.Context, repos return opts, nil } -// constructGitClient constructs and returns a new gogit client. -func (r *ImageUpdateAutomationReconciler) constructGitClient(ctx context.Context, - origin *sourcev1.GitRepository, repoDir string, switchBranch bool) (*gogit.Client, error) { - authOpts, err := r.getAuthOpts(ctx, origin) +// getProxyOpts fetches the secret containing the proxy settings, constructs a +// transport.ProxyOptions object using those settings and then returns it. +func (r *ImageUpdateAutomationReconciler) getProxyOpts(ctx context.Context, proxySecretName, + proxySecretNamespace string) (*transport.ProxyOptions, error) { + proxyData, err := r.getSecretData(ctx, proxySecretName, proxySecretNamespace) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to get proxy secret '%s/%s': %w", proxySecretNamespace, proxySecretName, err) } - - clientOpts := []gogit.ClientOption{gogit.WithDiskStorage()} - if authOpts.Transport == git.HTTP { - clientOpts = append(clientOpts, gogit.WithInsecureCredentialsOverHTTP()) + address, ok := proxyData["address"] + if !ok { + return nil, fmt.Errorf("invalid proxy secret '%s/%s': key 'address' is missing", proxySecretNamespace, proxySecretName) } - // If the push branch is different from the checkout ref, we need to - // have all the references downloaded at clone time, to ensure that - // SwitchBranch will have access to the target branch state. fluxcd/flux2#3384 - // - // To always overwrite the push branch, the feature gate - // GitAllBranchReferences can be set to false, which will cause - // the SwitchBranch operation to ignore the remote branch state. - allReferences := r.features[features.GitAllBranchReferences] - if switchBranch { - clientOpts = append(clientOpts, gogit.WithSingleBranch(!allReferences)) + proxyOpts := &transport.ProxyOptions{ + URL: string(address), + Username: string(proxyData["username"]), + Password: string(proxyData["password"]), } + return proxyOpts, nil +} - gitClient, err := gogit.NewClient(repoDir, authOpts, clientOpts...) - if err != nil { +func (r *ImageUpdateAutomationReconciler) getSecretData(ctx context.Context, name, namespace string) (map[string][]byte, error) { + key := types.NamespacedName{ + Namespace: namespace, + Name: name, + } + var secret corev1.Secret + if err := r.Client.Get(ctx, key, &secret); err != nil { return nil, err } - return gitClient, nil + return secret.Data, nil } // getSigningEntity retrieves an OpenPGP entity referenced by the diff --git a/internal/controller/update_test.go b/internal/controller/update_test.go index 3cccc876..81a49aff 100644 --- a/internal/controller/update_test.go +++ b/internal/controller/update_test.go @@ -39,6 +39,7 @@ import ( "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/cache" "github.com/go-git/go-git/v5/plumbing/object" + "github.com/go-git/go-git/v5/plumbing/transport" "github.com/go-git/go-git/v5/storage/filesystem" "github.com/go-logr/logr" . "github.com/onsi/gomega" @@ -55,6 +56,7 @@ import ( imagev1_reflect "github.com/fluxcd/image-reflector-controller/api/v1beta2" "github.com/fluxcd/pkg/apis/acl" "github.com/fluxcd/pkg/apis/meta" + "github.com/fluxcd/pkg/git" "github.com/fluxcd/pkg/git/gogit" "github.com/fluxcd/pkg/git/gogit/fs" "github.com/fluxcd/pkg/gittestserver" @@ -158,8 +160,10 @@ func TestImageUpdateAutomationReconciler_deleteBeforeFinalizer(t *testing.T) { EventRecorder: record.NewFakeRecorder(32), } // NOTE: Only a real API server responds with an error in this scenario. - _, err := r.Reconcile(ctx, ctrl.Request{NamespacedName: client.ObjectKeyFromObject(imageUpdate)}) - g.Expect(err).NotTo(HaveOccurred()) + g.Eventually(func() error { + _, err := r.Reconcile(ctx, ctrl.Request{NamespacedName: client.ObjectKeyFromObject(imageUpdate)}) + return err + }, timeout).Should(Succeed()) } func TestImageAutomationReconciler_commitMessage(t *testing.T) { @@ -989,6 +993,124 @@ func TestImageAutomationReconciler_defaulting(t *testing.T) { To(Equal(&imagev1.UpdateStrategy{Strategy: imagev1.UpdateStrategySetters})) } +func TestImageUpdateAutomationReconciler_getProxyOpts(t *testing.T) { + invalidProxy := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "invalid-proxy", + Namespace: "default", + }, + Data: map[string][]byte{ + "url": []byte("https://example.com"), + }, + } + validProxy := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "valid-proxy", + Namespace: "default", + }, + Data: map[string][]byte{ + "address": []byte("https://example.com"), + "username": []byte("user"), + "password": []byte("pass"), + }, + } + + clientBuilder := fakeclient.NewClientBuilder(). + WithScheme(testEnv.GetScheme()). + WithObjects(invalidProxy, validProxy) + + r := &ImageUpdateAutomationReconciler{ + Client: clientBuilder.Build(), + } + + tests := []struct { + name string + secret string + err string + proxyOpts *transport.ProxyOptions + }{ + { + name: "non-existent secret", + secret: "non-existent", + err: "failed to get proxy secret 'default/non-existent': ", + }, + { + name: "invalid proxy secret", + secret: "invalid-proxy", + err: "invalid proxy secret 'default/invalid-proxy': key 'address' is missing", + }, + { + name: "valid proxy secret", + secret: "valid-proxy", + proxyOpts: &transport.ProxyOptions{ + URL: "https://example.com", + Username: "user", + Password: "pass", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + opts, err := r.getProxyOpts(context.TODO(), tt.secret, "default") + if opts != nil { + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(opts).To(Equal(tt.proxyOpts)) + } else { + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring(tt.err)) + } + }) + } +} + +func TestImageAutomationReconciler_getGitClientOpts(t *testing.T) { + tests := []struct { + name string + gitTransport git.TransportType + proxyOpts *transport.ProxyOptions + diffPushBranch bool + clientOptsN int + }{ + { + name: "default client opts", + gitTransport: git.HTTPS, + clientOptsN: 1, + }, + { + name: "http transport adds insecure credentials client opt", + gitTransport: git.HTTP, + clientOptsN: 2, + }, + { + name: "http transport and providing proxy options adds insecure crednetials and proxy client opt", + gitTransport: git.HTTP, + proxyOpts: &transport.ProxyOptions{}, + clientOptsN: 3, + }, + { + name: "push branch different from checkout branch adds single branch client opt", + gitTransport: git.HTTPS, + diffPushBranch: true, + clientOptsN: 2, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + r := &ImageUpdateAutomationReconciler{ + features: map[string]bool{ + features.GitAllBranchReferences: true, + }, + } + clientOpts := r.getGitClientOpts(tt.gitTransport, tt.proxyOpts, tt.diffPushBranch) + g.Expect(len(clientOpts)).To(Equal(tt.clientOptsN)) + }) + } +} + func checkoutBranch(repo *extgogit.Repository, branch string) error { wt, err := repo.Worktree() if err != nil {