Skip to content

Commit ade77ec

Browse files
author
Paulo Gomes
authored
Merge pull request #944 from fluxcd/git-refactoring
Refactor Git operations and introduce `go-git` support for Azure DevOps and AWS CodeCommit
2 parents a9a85b2 + e87997c commit ade77ec

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+127
-7293
lines changed

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ ARG GO_VERSION=1.19
33
ARG XX_VERSION=1.1.2
44

55
ARG LIBGIT2_IMG=ghcr.io/fluxcd/golang-with-libgit2-only
6-
ARG LIBGIT2_TAG=v0.3.0
6+
ARG LIBGIT2_TAG=v0.4.0
77

88
FROM ${LIBGIT2_IMG}:${LIBGIT2_TAG} AS libgit2-libs
99

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ TAG ?= latest
44

55
# Base image used to build the Go binary
66
LIBGIT2_IMG ?= ghcr.io/fluxcd/golang-with-libgit2-only
7-
LIBGIT2_TAG ?= v0.3.0
7+
LIBGIT2_TAG ?= v0.4.0
88

99
# Allows for defining additional Go test args, e.g. '-tags integration'.
1010
GO_TEST_ARGS ?= -race

controllers/gitrepository_controller.go

+46-45
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"errors"
2222
"fmt"
23+
"net/url"
2324
"os"
2425
"path/filepath"
2526
"strings"
@@ -41,6 +42,9 @@ import (
4142
"sigs.k8s.io/controller-runtime/pkg/ratelimiter"
4243

4344
"github.com/fluxcd/pkg/apis/meta"
45+
"github.com/fluxcd/pkg/git"
46+
"github.com/fluxcd/pkg/git/gogit"
47+
"github.com/fluxcd/pkg/git/libgit2"
4448
"github.com/fluxcd/pkg/runtime/conditions"
4549
helper "github.com/fluxcd/pkg/runtime/controller"
4650
"github.com/fluxcd/pkg/runtime/events"
@@ -54,8 +58,6 @@ import (
5458
sreconcile "github.com/fluxcd/source-controller/internal/reconcile"
5559
"github.com/fluxcd/source-controller/internal/reconcile/summarize"
5660
"github.com/fluxcd/source-controller/internal/util"
57-
"github.com/fluxcd/source-controller/pkg/git"
58-
"github.com/fluxcd/source-controller/pkg/git/strategy"
5961
)
6062

6163
// gitRepositoryReadyCondition contains the information required to summarize a
@@ -440,9 +442,7 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
440442
conditions.Delete(obj, sourcev1.SourceVerifiedCondition)
441443
}
442444

443-
// Configure authentication strategy to access the source
444-
var authOpts *git.AuthOptions
445-
var err error
445+
var authData map[string][]byte
446446
if obj.Spec.SecretRef != nil {
447447
// Attempt to retrieve secret
448448
name := types.NamespacedName{
@@ -459,20 +459,27 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
459459
// Return error as the world as observed may change
460460
return sreconcile.ResultEmpty, e
461461
}
462+
authData = secret.Data
463+
}
462464

463-
// Configure strategy with secret
464-
authOpts, err = git.AuthOptionsFromSecret(obj.Spec.URL, &secret)
465-
} else {
466-
// Set the minimal auth options for valid transport.
467-
authOpts, err = git.AuthOptionsWithoutSecret(obj.Spec.URL)
465+
u, err := url.Parse(obj.Spec.URL)
466+
if err != nil {
467+
e := serror.NewStalling(
468+
fmt.Errorf("failed to parse url '%s': %w", obj.Spec.URL, err),
469+
sourcev1.URLInvalidReason,
470+
)
471+
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, e.Err.Error())
472+
return sreconcile.ResultEmpty, e
468473
}
474+
475+
// Configure authentication strategy to access the source
476+
authOpts, err := git.NewAuthOptions(*u, authData)
469477
if err != nil {
470478
e := serror.NewGeneric(
471-
fmt.Errorf("failed to configure auth strategy for Git implementation '%s': %w", obj.Spec.GitImplementation, err),
479+
fmt.Errorf("failed to configure authentication options: %w", err),
472480
sourcev1.AuthenticationFailedReason,
473481
)
474482
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, e.Err.Error())
475-
// Return error as the contents of the secret may change
476483
return sreconcile.ResultEmpty, e
477484
}
478485

@@ -725,59 +732,52 @@ func (r *GitRepositoryReconciler) reconcileInclude(ctx context.Context,
725732
func (r *GitRepositoryReconciler) gitCheckout(ctx context.Context,
726733
obj *sourcev1.GitRepository, authOpts *git.AuthOptions, dir string, optimized bool) (*git.Commit, error) {
727734
// Configure checkout strategy.
728-
checkoutOpts := git.CheckoutOptions{RecurseSubmodules: obj.Spec.RecurseSubmodules}
735+
cloneOpts := git.CloneOptions{
736+
RecurseSubmodules: obj.Spec.RecurseSubmodules,
737+
ShallowClone: true,
738+
}
729739
if ref := obj.Spec.Reference; ref != nil {
730-
checkoutOpts.Branch = ref.Branch
731-
checkoutOpts.Commit = ref.Commit
732-
checkoutOpts.Tag = ref.Tag
733-
checkoutOpts.SemVer = ref.SemVer
740+
cloneOpts.Branch = ref.Branch
741+
cloneOpts.Commit = ref.Commit
742+
cloneOpts.Tag = ref.Tag
743+
cloneOpts.SemVer = ref.SemVer
734744
}
735745

736746
// Only if the object has an existing artifact in storage, attempt to
737747
// short-circuit clone operation. reconcileStorage has already verified
738748
// that the artifact exists.
739749
if optimized && conditions.IsTrue(obj, sourcev1.ArtifactInStorageCondition) {
740750
if artifact := obj.GetArtifact(); artifact != nil {
741-
checkoutOpts.LastRevision = artifact.Revision
751+
cloneOpts.LastObservedCommit = artifact.Revision
742752
}
743753
}
744754

745755
gitCtx, cancel := context.WithTimeout(ctx, obj.Spec.Timeout.Duration)
746756
defer cancel()
747757

748-
checkoutStrategy, err := strategy.CheckoutStrategyForImplementation(gitCtx,
749-
git.Implementation(obj.Spec.GitImplementation), checkoutOpts)
758+
var gitReader git.RepositoryReader
759+
var err error
760+
761+
switch obj.Spec.GitImplementation {
762+
case sourcev1.LibGit2Implementation:
763+
gitReader, err = libgit2.NewClient(dir, authOpts)
764+
case sourcev1.GoGitImplementation:
765+
gitReader, err = gogit.NewClient(dir, authOpts)
766+
default:
767+
err = fmt.Errorf("invalid Git implementation: %s", obj.Spec.GitImplementation)
768+
}
750769
if err != nil {
751770
// Do not return err as recovery without changes is impossible.
752-
e := &serror.Stalling{
753-
Err: fmt.Errorf("failed to configure checkout strategy for Git implementation '%s': %w", obj.Spec.GitImplementation, err),
754-
Reason: sourcev1.GitOperationFailedReason,
755-
}
771+
e := serror.NewStalling(
772+
fmt.Errorf("failed to create Git client for implementation '%s': %w", obj.Spec.GitImplementation, err),
773+
sourcev1.GitOperationFailedReason,
774+
)
756775
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, e.Err.Error())
757776
return nil, e
758777
}
778+
defer gitReader.Close()
759779

760-
// this is needed only for libgit2, due to managed transport.
761-
if obj.Spec.GitImplementation == sourcev1.LibGit2Implementation {
762-
// We set the TransportOptionsURL of this set of authentication options here by constructing
763-
// a unique URL that won't clash in a multi tenant environment. This unique URL is used by
764-
// libgit2 managed transports. This enables us to bypass the inbuilt credentials callback in
765-
// libgit2, which is inflexible and unstable.
766-
if strings.HasPrefix(obj.Spec.URL, "http") {
767-
authOpts.TransportOptionsURL = fmt.Sprintf("http://%s/%s/%d", obj.Name, obj.UID, obj.Generation)
768-
} else if strings.HasPrefix(obj.Spec.URL, "ssh") {
769-
authOpts.TransportOptionsURL = fmt.Sprintf("ssh://%s/%s/%d", obj.Name, obj.UID, obj.Generation)
770-
} else {
771-
e := &serror.Stalling{
772-
Err: fmt.Errorf("git repository URL '%s' has invalid transport type, supported types are: http, https, ssh", obj.Spec.URL),
773-
Reason: sourcev1.URLInvalidReason,
774-
}
775-
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, e.Err.Error())
776-
return nil, e
777-
}
778-
}
779-
780-
commit, err := checkoutStrategy.Checkout(gitCtx, dir, obj.Spec.URL, authOpts)
780+
commit, err := gitReader.Clone(gitCtx, obj.Spec.URL, cloneOpts)
781781
if err != nil {
782782
e := serror.NewGeneric(
783783
fmt.Errorf("failed to checkout and determine revision: %w", err),
@@ -786,6 +786,7 @@ func (r *GitRepositoryReconciler) gitCheckout(ctx context.Context,
786786
conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, e.Err.Error())
787787
return nil, e
788788
}
789+
789790
return commit, nil
790791
}
791792

controllers/gitrepository_controller_test.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,13 @@ import (
5656
"github.com/fluxcd/pkg/ssh"
5757
"github.com/fluxcd/pkg/testserver"
5858

59+
"github.com/fluxcd/pkg/git"
60+
"github.com/fluxcd/pkg/git/libgit2/transport"
5961
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
6062
serror "github.com/fluxcd/source-controller/internal/error"
6163
"github.com/fluxcd/source-controller/internal/features"
6264
sreconcile "github.com/fluxcd/source-controller/internal/reconcile"
6365
"github.com/fluxcd/source-controller/internal/reconcile/summarize"
64-
"github.com/fluxcd/source-controller/pkg/git"
65-
"github.com/fluxcd/source-controller/pkg/git/libgit2/managed"
6666
)
6767

6868
const (
@@ -502,7 +502,7 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) {
502502
EventRecorder: record.NewFakeRecorder(32),
503503
Storage: testStorage,
504504
features: features.FeatureGates(),
505-
Libgit2TransportInitialized: managed.Enabled,
505+
Libgit2TransportInitialized: transport.Enabled,
506506
}
507507

508508
for _, i := range testGitImplementations {
@@ -731,7 +731,7 @@ func TestGitRepositoryReconciler_reconcileSource_checkoutStrategy(t *testing.T)
731731
EventRecorder: record.NewFakeRecorder(32),
732732
Storage: testStorage,
733733
features: features.FeatureGates(),
734-
Libgit2TransportInitialized: managed.Enabled,
734+
Libgit2TransportInitialized: transport.Enabled,
735735
}
736736

737737
for _, tt := range tests {
@@ -1404,7 +1404,7 @@ func TestGitRepositoryReconciler_verifyCommitSignature(t *testing.T) {
14041404
},
14051405
wantErr: true,
14061406
assertConditions: []metav1.Condition{
1407-
*conditions.FalseCondition(sourcev1.SourceVerifiedCondition, "InvalidCommitSignature", "signature verification of commit 'shasum' failed: failed to verify commit with any of the given key rings"),
1407+
*conditions.FalseCondition(sourcev1.SourceVerifiedCondition, "InvalidCommitSignature", "signature verification of commit 'shasum' failed: unable to verify commit with any of the given key rings"),
14081408
},
14091409
},
14101410
{
@@ -1599,7 +1599,7 @@ func TestGitRepositoryReconciler_ConditionsUpdate(t *testing.T) {
15991599
EventRecorder: record.NewFakeRecorder(32),
16001600
Storage: testStorage,
16011601
features: features.FeatureGates(),
1602-
Libgit2TransportInitialized: managed.Enabled,
1602+
Libgit2TransportInitialized: transport.Enabled,
16031603
}
16041604

16051605
key := client.ObjectKeyFromObject(obj)

controllers/ocirepository_controller_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import (
5757
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
5858

5959
"github.com/fluxcd/pkg/apis/meta"
60+
"github.com/fluxcd/pkg/git"
6061
"github.com/fluxcd/pkg/oci"
6162
"github.com/fluxcd/pkg/runtime/conditions"
6263
conditionscheck "github.com/fluxcd/pkg/runtime/conditions/check"
@@ -66,7 +67,6 @@ import (
6667
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
6768
serror "github.com/fluxcd/source-controller/internal/error"
6869
sreconcile "github.com/fluxcd/source-controller/internal/reconcile"
69-
"github.com/fluxcd/source-controller/pkg/git"
7070
)
7171

7272
func TestOCIRepository_Reconcile(t *testing.T) {

controllers/suite_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
ctrl "sigs.k8s.io/controller-runtime"
3838

3939
dcontext "github.com/distribution/distribution/v3/context"
40+
"github.com/fluxcd/pkg/git/libgit2/transport"
4041
"github.com/fluxcd/pkg/runtime/controller"
4142
"github.com/fluxcd/pkg/runtime/testenv"
4243
"github.com/fluxcd/pkg/testserver"
@@ -47,13 +48,12 @@ import (
4748
dockerRegistry "github.com/distribution/distribution/v3/registry"
4849
_ "github.com/distribution/distribution/v3/registry/auth/htpasswd"
4950
_ "github.com/distribution/distribution/v3/registry/storage/driver/inmemory"
50-
git2go "github.com/libgit2/git2go/v33"
51+
git2go "github.com/libgit2/git2go/v34"
5152

5253
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
5354
"github.com/fluxcd/source-controller/internal/cache"
5455
"github.com/fluxcd/source-controller/internal/features"
5556
"github.com/fluxcd/source-controller/internal/helm/registry"
56-
"github.com/fluxcd/source-controller/pkg/git/libgit2/managed"
5757
// +kubebuilder:scaffold:imports
5858
)
5959

@@ -237,7 +237,7 @@ func TestMain(m *testing.M) {
237237
panic(fmt.Sprintf("Failed to create a test registry server: %v", err))
238238
}
239239

240-
if err = managed.InitManagedTransport(); err != nil {
240+
if err = transport.InitManagedTransport(); err != nil {
241241
panic(fmt.Sprintf("Failed to initialize libgit2 managed transport: %v", err))
242242
}
243243

@@ -247,7 +247,7 @@ func TestMain(m *testing.M) {
247247
Metrics: testMetricsH,
248248
Storage: testStorage,
249249
features: features.FeatureGates(),
250-
Libgit2TransportInitialized: managed.Enabled,
250+
Libgit2TransportInitialized: transport.Enabled,
251251
}).SetupWithManager(testEnv); err != nil {
252252
panic(fmt.Sprintf("Failed to start GitRepositoryReconciler: %v", err))
253253
}

0 commit comments

Comments
 (0)