libgit2: fix gitlab redirection for HTTP
Gitlab only supports HTTP redirection for GET operations, and fails POST operations targeting a repository without the .git suffix. Fixes: https://github.com/fluxcd/image-automation-controller/issues/379 Signed-off-by: Paulo Gomes <paulo.gomes@weave.works>
This commit is contained in:
parent
2a52056594
commit
b764bdbeaa
|
@ -52,6 +52,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
pool "github.com/fluxcd/source-controller/internal/transport"
|
pool "github.com/fluxcd/source-controller/internal/transport"
|
||||||
|
@ -59,6 +60,13 @@ import (
|
||||||
git2go "github.com/libgit2/git2go/v33"
|
git2go "github.com/libgit2/git2go/v33"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var actionSuffixes = []string{
|
||||||
|
"/info/refs?service=git-upload-pack",
|
||||||
|
"/git-upload-pack",
|
||||||
|
"/info/refs?service=git-receive-pack",
|
||||||
|
"/git-receive-pack",
|
||||||
|
}
|
||||||
|
|
||||||
// registerManagedHTTP registers a Go-native implementation of an
|
// registerManagedHTTP registers a Go-native implementation of an
|
||||||
// HTTP(S) transport that doesn't rely on any lower-level libraries
|
// HTTP(S) transport that doesn't rely on any lower-level libraries
|
||||||
// such as OpenSSL.
|
// such as OpenSSL.
|
||||||
|
@ -152,12 +160,40 @@ func (t *httpSmartSubtransport) Action(transportOptionsURL string, action git2go
|
||||||
|
|
||||||
return http.ErrUseLastResponse
|
return http.ErrUseLastResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Some Git servers (i.e. Gitlab) only support redirection on the GET operations.
|
||||||
|
// Therefore, on the initial GET operation we update the target URL to include the
|
||||||
|
// new target, so the subsequent actions include the correct target URL.
|
||||||
|
// Example of this is trying to access a Git repository without the .git suffix.
|
||||||
|
if req.Response != nil && req.Response.StatusCode == http.StatusMovedPermanently {
|
||||||
|
if newURL, err := req.Response.Location(); err == nil && newURL != nil {
|
||||||
|
if strings.EqualFold(newURL.Host, req.URL.Host) && strings.EqualFold(newURL.Port(), req.URL.Port()) {
|
||||||
|
opts, _ := getTransportOptions(transportOptionsURL)
|
||||||
|
if opts == nil {
|
||||||
|
opts = &TransportOptions{}
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.TargetURL = trimActionSuffix(newURL.String())
|
||||||
|
AddTransportOptions(transportOptionsURL, *opts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return stream, nil
|
return stream, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func trimActionSuffix(url string) string {
|
||||||
|
newUrl := url
|
||||||
|
for _, s := range actionSuffixes {
|
||||||
|
newUrl = strings.TrimSuffix(newUrl, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newUrl
|
||||||
|
}
|
||||||
|
|
||||||
func createClientRequest(targetURL string, action git2go.SmartServiceAction,
|
func createClientRequest(targetURL string, action git2go.SmartServiceAction,
|
||||||
t *http.Transport, authOpts *git.AuthOptions) (*http.Client, *http.Request, error) {
|
t *http.Transport, authOpts *git.AuthOptions) (*http.Client, *http.Request, error) {
|
||||||
var req *http.Request
|
var req *http.Request
|
||||||
|
|
|
@ -200,26 +200,81 @@ func TestHTTPManagedTransport_E2E(t *testing.T) {
|
||||||
repo.Free()
|
repo.Free()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHTTPManagedTransport_HandleRedirect(t *testing.T) {
|
func TestTrimActionSuffix(t *testing.T) {
|
||||||
g := NewWithT(t)
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
inURL string
|
||||||
|
wantURL string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "ignore other suffixes",
|
||||||
|
inURL: "https://gitlab/repo/podinfo.git/somethingelse",
|
||||||
|
wantURL: "https://gitlab/repo/podinfo.git/somethingelse",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "trim /info/refs?service=git-upload-pack",
|
||||||
|
inURL: "https://gitlab/repo/podinfo.git/info/refs?service=git-upload-pack",
|
||||||
|
wantURL: "https://gitlab/repo/podinfo.git",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "trim /git-upload-pack",
|
||||||
|
inURL: "https://gitlab/repo/podinfo.git/git-upload-pack",
|
||||||
|
wantURL: "https://gitlab/repo/podinfo.git",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "trim /info/refs?service=git-receive-pack",
|
||||||
|
inURL: "https://gitlab/repo/podinfo.git/info/refs?service=git-receive-pack",
|
||||||
|
wantURL: "https://gitlab/repo/podinfo.git",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "trim /git-receive-pack",
|
||||||
|
inURL: "https://gitlab/repo/podinfo.git/git-receive-pack",
|
||||||
|
wantURL: "https://gitlab/repo/podinfo.git",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
tmpDir := t.TempDir()
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
g := NewWithT(t)
|
||||||
|
|
||||||
|
gotURL := trimActionSuffix(tt.inURL)
|
||||||
|
g.Expect(gotURL).To(Equal(tt.wantURL))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHTTPManagedTransport_HandleRedirect(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
repoURL string
|
||||||
|
}{
|
||||||
|
{name: "http to https", repoURL: "http://github.com/stefanprodan/podinfo"},
|
||||||
|
{name: "handle gitlab redirect", repoURL: "https://gitlab.com/stefanprodan/podinfo"},
|
||||||
|
}
|
||||||
|
|
||||||
// Force managed transport to be enabled
|
// Force managed transport to be enabled
|
||||||
InitManagedTransport(logr.Discard())
|
InitManagedTransport(logr.Discard())
|
||||||
|
|
||||||
id := "http://obj-id"
|
for _, tt := range tests {
|
||||||
AddTransportOptions(id, TransportOptions{
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
TargetURL: "http://github.com/stefanprodan/podinfo",
|
g := NewWithT(t)
|
||||||
})
|
|
||||||
|
|
||||||
// GitHub will cause a 301 and redirect to https
|
tmpDir := t.TempDir()
|
||||||
repo, err := git2go.Clone(id, tmpDir, &git2go.CloneOptions{
|
|
||||||
CheckoutOptions: git2go.CheckoutOptions{
|
|
||||||
Strategy: git2go.CheckoutForce,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
g.Expect(err).ToNot(HaveOccurred())
|
id := "http://obj-id"
|
||||||
repo.Free()
|
AddTransportOptions(id, TransportOptions{
|
||||||
|
TargetURL: tt.repoURL,
|
||||||
|
})
|
||||||
|
|
||||||
|
// GitHub will cause a 301 and redirect to https
|
||||||
|
repo, err := git2go.Clone(id, tmpDir, &git2go.CloneOptions{
|
||||||
|
CheckoutOptions: git2go.CheckoutOptions{
|
||||||
|
Strategy: git2go.CheckoutForce,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
g.Expect(err).ToNot(HaveOccurred())
|
||||||
|
repo.Free()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue