Skip to content

Commit a3cbe6e

Browse files
authored
Merge pull request #606 from pjbgf/managed-transport-libgit130
Experimental managed transport for libgit2 operations
2 parents 9bbcd09 + 115040e commit a3cbe6e

File tree

11 files changed

+1207
-5
lines changed

11 files changed

+1207
-5
lines changed

controllers/gitrepository_controller.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import (
5050
"github.com/fluxcd/source-controller/internal/reconcile/summarize"
5151
"github.com/fluxcd/source-controller/internal/util"
5252
"github.com/fluxcd/source-controller/pkg/git"
53+
"github.com/fluxcd/source-controller/pkg/git/libgit2/managed"
5354
"github.com/fluxcd/source-controller/pkg/git/strategy"
5455
"github.com/fluxcd/source-controller/pkg/sourceignore"
5556
)
@@ -369,10 +370,37 @@ func (r *GitRepositoryReconciler) reconcileSource(ctx context.Context,
369370
return sreconcile.ResultEmpty, e
370371
}
371372

373+
repositoryURL := obj.Spec.URL
374+
// managed GIT transport only affects the libgit2 implementation
375+
if managed.Enabled() && obj.Spec.GitImplementation == sourcev1.LibGit2Implementation {
376+
// At present only HTTP connections have the ability to define remote options.
377+
// Although this can be easily extended by ensuring that the fake URL below uses the
378+
// target ssh scheme, and the libgit2/managed/ssh.go pulls that information accordingly.
379+
//
380+
// This is due to the fact the key libgit2 remote callbacks do not take place for HTTP
381+
// whilst most still work for SSH.
382+
if strings.HasPrefix(repositoryURL, "http") {
383+
// Due to the lack of the callback feature, a fake target URL is created to allow
384+
// for the smart sub transport be able to pick the options specific for this
385+
// GitRepository object.
386+
// The URL should use unique information that do not collide in a multi tenant
387+
// deployment.
388+
repositoryURL = fmt.Sprintf("http://%s/%s/%d", obj.Name, obj.UID, obj.Generation)
389+
managed.AddTransportOptions(repositoryURL,
390+
managed.TransportOptions{
391+
TargetURL: obj.Spec.URL,
392+
CABundle: authOpts.CAFile,
393+
})
394+
395+
// We remove the options from memory, to avoid accumulating unused options over time.
396+
defer managed.RemoveTransportOptions(repositoryURL)
397+
}
398+
}
399+
372400
// Checkout HEAD of reference in object
373401
gitCtx, cancel := context.WithTimeout(ctx, obj.Spec.Timeout.Duration)
374402
defer cancel()
375-
c, err := checkoutStrategy.Checkout(gitCtx, dir, obj.Spec.URL, authOpts)
403+
c, err := checkoutStrategy.Checkout(gitCtx, dir, repositoryURL, authOpts)
376404
if err != nil {
377405
e := &serror.Event{
378406
Err: fmt.Errorf("failed to checkout and determine revision: %w", err),

hack/ci/e2e.sh

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,19 @@ echo "Run large Git repo tests"
139139
kubectl -n source-system apply -f "${ROOT_DIR}/config/testdata/git/large-repo.yaml"
140140
kubectl -n source-system wait gitrepository/large-repo-go-git --for=condition=ready --timeout=2m15s
141141
kubectl -n source-system wait gitrepository/large-repo-libgit2 --for=condition=ready --timeout=2m15s
142+
143+
144+
# Test experimental libgit2 transport. Any tests against the default transport must
145+
# either run before this, or patch the deployment again to disable this, as once enabled
146+
# only the managed transport will be used.
147+
kubectl -n source-system patch deployment source-controller \
148+
--patch '{"spec": {"template": {"spec": {"containers": [{"name": "manager","env": [{"name": "EXPERIMENTAL_GIT_TRANSPORT", "value": "true"}]}]}}}}'
149+
150+
# wait until the patch took effect and the new source-controller is running
151+
sleep 20s
152+
153+
kubectl -n source-system wait --for=condition=ready --timeout=1m -l app=source-controller pod
154+
155+
echo "Re-run large libgit2 repo test with managed transport"
156+
kubectl -n source-system wait gitrepository/large-repo-libgit2 --for=condition=ready --timeout=2m15s
157+
kubectl -n source-system exec deploy/source-controller -- printenv | grep EXPERIMENTAL_GIT_TRANSPORT=true

main.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import (
4545
sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
4646
"github.com/fluxcd/source-controller/controllers"
4747
"github.com/fluxcd/source-controller/internal/helm"
48+
"github.com/fluxcd/source-controller/pkg/git/libgit2/managed"
4849
// +kubebuilder:scaffold:imports
4950
)
5051

@@ -226,6 +227,10 @@ func main() {
226227
startFileServer(storage.BasePath, storageAddr, setupLog)
227228
}()
228229

230+
if managed.Enabled() {
231+
managed.InitManagedTransport()
232+
}
233+
229234
setupLog.Info("starting manager")
230235
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
231236
setupLog.Error(err, "problem running manager")

pkg/git/libgit2/checkout.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"github.com/fluxcd/pkg/version"
3232

3333
"github.com/fluxcd/source-controller/pkg/git"
34+
"github.com/fluxcd/source-controller/pkg/git/libgit2/managed"
3435
)
3536

3637
// CheckoutStrategyForOptions returns the git.CheckoutStrategy for the given
@@ -72,7 +73,7 @@ func (c *CheckoutBranch) Checkout(ctx context.Context, path, url string, opts *g
7273
CheckoutBranch: c.Branch,
7374
})
7475
if err != nil {
75-
return nil, fmt.Errorf("unable to clone '%s': %w", url, gitutil.LibGit2Error(err))
76+
return nil, fmt.Errorf("unable to clone '%s': %w", managed.EffectiveURL(url), gitutil.LibGit2Error(err))
7677
}
7778
defer repo.Free()
7879
head, err := repo.Head()
@@ -101,7 +102,7 @@ func (c *CheckoutTag) Checkout(ctx context.Context, path, url string, opts *git.
101102
},
102103
})
103104
if err != nil {
104-
return nil, fmt.Errorf("unable to clone '%s': %w", url, gitutil.LibGit2Error(err))
105+
return nil, fmt.Errorf("unable to clone '%s': %w", managed.EffectiveURL(url), gitutil.LibGit2Error(err))
105106
}
106107
defer repo.Free()
107108
cc, err := checkoutDetachedDwim(repo, c.Tag)
@@ -125,7 +126,7 @@ func (c *CheckoutCommit) Checkout(ctx context.Context, path, url string, opts *g
125126
},
126127
})
127128
if err != nil {
128-
return nil, fmt.Errorf("unable to clone '%s': %w", url, gitutil.LibGit2Error(err))
129+
return nil, fmt.Errorf("unable to clone '%s': %w", managed.EffectiveURL(url), gitutil.LibGit2Error(err))
129130
}
130131
defer repo.Free()
131132
oid, err := git2go.NewOid(c.Commit)
@@ -157,7 +158,7 @@ func (c *CheckoutSemVer) Checkout(ctx context.Context, path, url string, opts *g
157158
},
158159
})
159160
if err != nil {
160-
return nil, fmt.Errorf("unable to clone '%s': %w", url, gitutil.LibGit2Error(err))
161+
return nil, fmt.Errorf("unable to clone '%s': %w", managed.EffectiveURL(url), gitutil.LibGit2Error(err))
161162
}
162163
defer repo.Free()
163164

pkg/git/libgit2/managed/flag.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
Copyright 2022 The Flux authors
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 managed
18+
19+
import (
20+
"os"
21+
"strings"
22+
)
23+
24+
// Enabled defines whether the use of Managed Transport should be enabled.
25+
// This is only affects git operations that uses libgit2 implementation.
26+
//
27+
// True is returned when the environment variable `EXPERIMENTAL_GIT_TRANSPORT`
28+
// is detected with the value of `true` or `1`.
29+
func Enabled() bool {
30+
if v, ok := os.LookupEnv("EXPERIMENTAL_GIT_TRANSPORT"); ok {
31+
return strings.ToLower(v) == "true" || v == "1"
32+
}
33+
return false
34+
}

0 commit comments

Comments
 (0)