333 lines
9.1 KiB
Go
333 lines
9.1 KiB
Go
/*
|
|
Copyright 2022 The Flux authors
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package managed
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/fluxcd/pkg/gittestserver"
|
|
"github.com/fluxcd/pkg/ssh"
|
|
"github.com/fluxcd/source-controller/pkg/git"
|
|
"github.com/go-logr/logr"
|
|
|
|
git2go "github.com/libgit2/git2go/v33"
|
|
. "github.com/onsi/gomega"
|
|
"gotest.tools/assert"
|
|
)
|
|
|
|
func TestHttpAction_CreateClientRequest(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
url string
|
|
expectedUrl string
|
|
expectedMethod string
|
|
action git2go.SmartServiceAction
|
|
opts *TransportOptions
|
|
transport *http.Transport
|
|
wantedErr error
|
|
}{
|
|
{
|
|
name: "Uploadpack: no changes when no options found",
|
|
url: "https://sometarget/abc",
|
|
expectedUrl: "https://sometarget/abc/git-upload-pack",
|
|
expectedMethod: "POST",
|
|
action: git2go.SmartServiceActionUploadpack,
|
|
transport: &http.Transport{},
|
|
opts: nil,
|
|
wantedErr: nil,
|
|
},
|
|
{
|
|
name: "UploadpackLs: no changes when no options found",
|
|
url: "https://sometarget/abc",
|
|
expectedUrl: "https://sometarget/abc/info/refs?service=git-upload-pack",
|
|
expectedMethod: "GET",
|
|
action: git2go.SmartServiceActionUploadpackLs,
|
|
transport: &http.Transport{},
|
|
opts: nil,
|
|
wantedErr: nil,
|
|
},
|
|
{
|
|
name: "Receivepack: no changes when no options found",
|
|
url: "https://sometarget/abc",
|
|
expectedUrl: "https://sometarget/abc/git-receive-pack",
|
|
expectedMethod: "POST",
|
|
action: git2go.SmartServiceActionReceivepack,
|
|
transport: &http.Transport{},
|
|
opts: nil,
|
|
wantedErr: nil,
|
|
},
|
|
{
|
|
name: "ReceivepackLs: no changes when no options found",
|
|
url: "https://sometarget/abc",
|
|
expectedUrl: "https://sometarget/abc/info/refs?service=git-receive-pack",
|
|
expectedMethod: "GET",
|
|
action: git2go.SmartServiceActionReceivepackLs,
|
|
transport: &http.Transport{},
|
|
opts: nil,
|
|
wantedErr: nil,
|
|
},
|
|
{
|
|
name: "override URL via options",
|
|
url: "https://initial-target/abc",
|
|
expectedUrl: "https://final-target/git-upload-pack",
|
|
expectedMethod: "POST",
|
|
action: git2go.SmartServiceActionUploadpack,
|
|
transport: &http.Transport{},
|
|
opts: &TransportOptions{
|
|
TargetURL: "https://final-target",
|
|
},
|
|
wantedErr: nil,
|
|
},
|
|
{
|
|
name: "error when no http.transport provided",
|
|
url: "https://initial-target/abc",
|
|
expectedUrl: "",
|
|
expectedMethod: "",
|
|
action: git2go.SmartServiceActionUploadpack,
|
|
transport: nil,
|
|
opts: nil,
|
|
wantedErr: fmt.Errorf("failed to create client: transport cannot be nil"),
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.opts != nil {
|
|
AddTransportOptions(tt.url, *tt.opts)
|
|
}
|
|
|
|
_, req, err := createClientRequest(tt.url, tt.action, tt.transport)
|
|
if tt.wantedErr != nil {
|
|
if tt.wantedErr.Error() != err.Error() {
|
|
t.Errorf("wanted: %v got: %v", tt.wantedErr, err)
|
|
}
|
|
} else {
|
|
assert.Equal(t, req.URL.String(), tt.expectedUrl)
|
|
assert.Equal(t, req.Method, tt.expectedMethod)
|
|
}
|
|
|
|
if tt.opts != nil {
|
|
RemoveTransportOptions(tt.url)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOptions(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
registerOpts bool
|
|
url string
|
|
opts TransportOptions
|
|
expectOpts bool
|
|
expectedOpts *TransportOptions
|
|
}{
|
|
{
|
|
name: "return registered option",
|
|
registerOpts: true,
|
|
url: "https://target/?123",
|
|
opts: TransportOptions{},
|
|
expectOpts: true,
|
|
expectedOpts: &TransportOptions{},
|
|
},
|
|
{
|
|
name: "match registered options",
|
|
registerOpts: true,
|
|
url: "https://target/?876",
|
|
opts: TransportOptions{
|
|
TargetURL: "https://new-target/321",
|
|
CABundle: []byte{123, 213, 132},
|
|
},
|
|
expectOpts: true,
|
|
expectedOpts: &TransportOptions{
|
|
TargetURL: "https://new-target/321",
|
|
CABundle: []byte{123, 213, 132},
|
|
},
|
|
},
|
|
{
|
|
name: "ignore when options not registered",
|
|
registerOpts: false,
|
|
url: "",
|
|
opts: TransportOptions{},
|
|
expectOpts: false,
|
|
expectedOpts: nil,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.registerOpts {
|
|
AddTransportOptions(tt.url, tt.opts)
|
|
}
|
|
|
|
opts, found := transportOptions(tt.url)
|
|
if tt.expectOpts != found {
|
|
t.Errorf("%s: wanted %v got %v", tt.name, tt.expectOpts, found)
|
|
}
|
|
|
|
if tt.expectOpts {
|
|
if reflect.DeepEqual(opts, *tt.expectedOpts) {
|
|
t.Errorf("%s: wanted %v got %v", tt.name, *tt.expectedOpts, opts)
|
|
}
|
|
}
|
|
|
|
if tt.registerOpts {
|
|
RemoveTransportOptions(tt.url)
|
|
}
|
|
|
|
if _, found = transportOptions(tt.url); found {
|
|
t.Errorf("%s: option for %s was not removed", tt.name, tt.url)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFlagStatus(t *testing.T) {
|
|
if Enabled() {
|
|
t.Errorf("experimental transport should not be enabled by default")
|
|
}
|
|
|
|
os.Setenv("EXPERIMENTAL_GIT_TRANSPORT", "true")
|
|
if !Enabled() {
|
|
t.Errorf("experimental transport should be enabled when env EXPERIMENTAL_GIT_TRANSPORT=true")
|
|
}
|
|
|
|
os.Setenv("EXPERIMENTAL_GIT_TRANSPORT", "1")
|
|
if !Enabled() {
|
|
t.Errorf("experimental transport should be enabled when env EXPERIMENTAL_GIT_TRANSPORT=1")
|
|
}
|
|
|
|
os.Setenv("EXPERIMENTAL_GIT_TRANSPORT", "somethingelse")
|
|
if Enabled() {
|
|
t.Errorf("experimental transport should be enabled only when env EXPERIMENTAL_GIT_TRANSPORT is 1 or true but was enabled for 'somethingelse'")
|
|
}
|
|
|
|
os.Unsetenv("EXPERIMENTAL_GIT_TRANSPORT")
|
|
if Enabled() {
|
|
t.Errorf("experimental transport should not be enabled when env EXPERIMENTAL_GIT_TRANSPORT is not present")
|
|
}
|
|
}
|
|
|
|
func TestManagedTransport_E2E(t *testing.T) {
|
|
g := NewWithT(t)
|
|
|
|
server, err := gittestserver.NewTempGitServer()
|
|
g.Expect(err).ToNot(HaveOccurred())
|
|
defer os.RemoveAll(server.Root())
|
|
|
|
user := "test-user"
|
|
pasword := "test-pswd"
|
|
server.Auth(user, pasword)
|
|
server.KeyDir(filepath.Join(server.Root(), "keys"))
|
|
|
|
err = server.ListenSSH()
|
|
g.Expect(err).ToNot(HaveOccurred())
|
|
|
|
err = server.StartHTTP()
|
|
g.Expect(err).ToNot(HaveOccurred())
|
|
defer server.StopHTTP()
|
|
|
|
go func() {
|
|
server.StartSSH()
|
|
}()
|
|
defer server.StopSSH()
|
|
|
|
// Force managed transport to be enabled
|
|
InitManagedTransport(logr.Discard())
|
|
|
|
repoPath := "test.git"
|
|
err = server.InitRepo("../testdata/git/repo", git.DefaultBranch, repoPath)
|
|
g.Expect(err).ToNot(HaveOccurred())
|
|
|
|
tmpDir, _ := os.MkdirTemp("", "test")
|
|
defer os.RemoveAll(tmpDir)
|
|
|
|
// Test HTTP transport
|
|
|
|
// Use a fake-url and force it to be overriden by the smart transport.
|
|
// This was the way found to ensure that the built-in transport was not used.
|
|
httpAddress := "http://fake-url"
|
|
AddTransportOptions(httpAddress, TransportOptions{
|
|
TargetURL: server.HTTPAddress() + "/" + repoPath,
|
|
})
|
|
|
|
repo, err := git2go.Clone(httpAddress, tmpDir, &git2go.CloneOptions{
|
|
FetchOptions: git2go.FetchOptions{
|
|
RemoteCallbacks: git2go.RemoteCallbacks{
|
|
CredentialsCallback: func(url, username_from_url string, allowed_types git2go.CredentialType) (*git2go.Credential, error) {
|
|
return git2go.NewCredentialUserpassPlaintext(user, pasword)
|
|
},
|
|
},
|
|
},
|
|
CheckoutOptions: git2go.CheckoutOptions{
|
|
Strategy: git2go.CheckoutForce,
|
|
},
|
|
})
|
|
g.Expect(err).ToNot(HaveOccurred())
|
|
repo.Free()
|
|
|
|
tmpDir2, _ := os.MkdirTemp("", "test")
|
|
defer os.RemoveAll(tmpDir2)
|
|
|
|
kp, err := ssh.NewEd25519Generator().Generate()
|
|
g.Expect(err).ToNot(HaveOccurred())
|
|
|
|
// Test SSH transport
|
|
sshAddress := server.SSHAddress() + "/" + repoPath
|
|
repo, err = git2go.Clone(sshAddress, tmpDir2, &git2go.CloneOptions{
|
|
FetchOptions: git2go.FetchOptions{
|
|
RemoteCallbacks: git2go.RemoteCallbacks{
|
|
CredentialsCallback: func(url, username_from_url string, allowed_types git2go.CredentialType) (*git2go.Credential, error) {
|
|
return git2go.NewCredentialSSHKeyFromMemory("git", "", string(kp.PrivateKey), "")
|
|
},
|
|
},
|
|
},
|
|
CheckoutOptions: git2go.CheckoutOptions{
|
|
Strategy: git2go.CheckoutForce,
|
|
},
|
|
})
|
|
|
|
g.Expect(err).ToNot(HaveOccurred())
|
|
repo.Free()
|
|
}
|
|
|
|
func TestManagedTransport_HandleRedirect(t *testing.T) {
|
|
g := NewWithT(t)
|
|
|
|
tmpDir, _ := os.MkdirTemp("", "test")
|
|
defer os.RemoveAll(tmpDir)
|
|
|
|
// Force managed transport to be enabled
|
|
InitManagedTransport(logr.Discard())
|
|
|
|
// GitHub will cause a 301 and redirect to https
|
|
repo, err := git2go.Clone("http://github.com/stefanprodan/podinfo", tmpDir, &git2go.CloneOptions{
|
|
FetchOptions: git2go.FetchOptions{},
|
|
CheckoutOptions: git2go.CheckoutOptions{
|
|
Strategy: git2go.CheckoutForce,
|
|
},
|
|
})
|
|
|
|
g.Expect(err).ToNot(HaveOccurred())
|
|
repo.Free()
|
|
}
|