Files
kopia/cli/command_server_control_test.go
Nathan Baulch 657fda216a chore(ci): upgrade to golangci-lint 2.6.1 (#4973)
- upgrade to golangci-lint 2.6.1
- updates for gosec
- updates for govet
- updates for perfsprint
- updates modernize

Leaves out modernize:omitempty due to conflicts with tests
2025-11-11 21:27:10 -08:00

227 lines
8.7 KiB
Go

package cli_test
import (
"runtime"
"slices"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/kopia/kopia/internal/testutil"
"github.com/kopia/kopia/repo/blob/throttling"
"github.com/kopia/kopia/tests/testenv"
)
func TestServerControl(t *testing.T) {
env := testenv.NewCLITest(t, testenv.RepoFormatNotImportant, testenv.NewInProcRunner(t))
dir0 := testutil.TempDirectory(t)
dir1 := testutil.TempDirectory(t)
dir2 := testutil.TempDirectory(t)
dir3 := testutil.TempDirectory(t)
env.RunAndExpectSuccess(t, "repo", "create", "filesystem", "--path", env.RepoDir, "--override-username=another-user", "--override-hostname=another-host")
env.RunAndExpectSuccess(t, "snap", "create", dir0)
env.RunAndExpectSuccess(t, "repo", "connect", "filesystem", "--path", env.RepoDir, "--override-username=test-user", "--override-hostname=test-host")
env.RunAndExpectSuccess(t, "snap", "create", dir1)
env.RunAndExpectSuccess(t, "snap", "create", dir2)
serverStarted := make(chan struct{})
serverStopped := make(chan struct{})
var sp testutil.ServerParameters
go func() {
wait, _ := env.RunAndProcessStderr(t, sp.ProcessOutput,
"server", "start", "--insecure", "--random-server-control-password", "--address=127.0.0.1:0")
close(serverStarted)
wait()
close(serverStopped)
}()
select {
case <-serverStarted:
t.Logf("server started on %v", sp.BaseURL)
case <-time.After(5 * time.Second):
t.Fatalf("server did not start in time")
}
const (
pollFrequency = 100 * time.Millisecond
waitTimeout = 15 * time.Second
)
require.Eventually(t, func() bool {
lines := env.RunAndExpectSuccess(t, "server", "status", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword)
t.Logf("lines: %v", lines)
return hasLine(lines, "IDLE: test-user@test-host:"+dir1) && hasLine(lines, "IDLE: test-user@test-host:"+dir2)
}, waitTimeout, pollFrequency)
lines := env.RunAndExpectSuccess(t, "server", "status", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword, "--remote")
require.Len(t, lines, 3)
require.Contains(t, lines, "IDLE: test-user@test-host:"+dir1)
require.Contains(t, lines, "IDLE: test-user@test-host:"+dir2)
require.Contains(t, lines, "REMOTE: another-user@another-host:"+dir0)
// create snapshot outside of the server
env.RunAndExpectSuccess(t, "snap", "create", dir3)
env.RunAndExpectSuccess(t, "server", "refresh", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword)
require.Eventually(t, func() bool {
lines := env.RunAndExpectSuccess(t, "server", "status", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword, "--remote")
t.Logf("lines: %v", lines)
return hasLine(lines, "IDLE: test-user@test-host:"+dir3)
}, waitTimeout, pollFrequency)
env.RunAndExpectSuccess(t, "server", "flush", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword)
// trigger server snapshot
env.RunAndExpectSuccess(t, "server", "snapshot", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword, "--all")
env.RunAndExpectSuccess(t, "server", "snapshot", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword, dir1)
env.RunAndExpectFailure(t, "server", "snapshot", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword, "no-such-dir")
// neither dir nor --all specified
env.RunAndExpectFailure(t, "server", "snapshot", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword)
// cancel snapshot
env.RunAndExpectSuccess(t, "server", "cancel", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword, "--all")
env.RunAndExpectSuccess(t, "server", "pause", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword, dir1)
env.RunAndExpectSuccess(t, "server", "resume", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword, dir1)
env.RunAndExpectSuccess(t, "server", "throttle", "set", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword,
"--download-bytes-per-second=1000000000",
"--upload-bytes-per-second=2000000000",
"--read-requests-per-second=300",
"--write-requests-per-second=400",
"--list-requests-per-second=500",
"--concurrent-reads=300",
"--concurrent-writes=400",
)
require.Equal(t, []string{
"Max Download Speed: 1 GB/s",
"Max Upload Speed: 2 GB/s",
"Max Read Requests Per Second: 300",
"Max Write Requests Per Second: 400",
"Max List Requests Per Second: 500",
"Max Concurrent Reads: 300",
"Max Concurrent Writes: 400",
}, env.RunAndExpectSuccess(t, "server", "throttle", "get", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword))
env.RunAndExpectSuccess(t, "server", "throttle", "set", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword,
"--upload-bytes-per-second=unlimited",
"--write-requests-per-second=unlimited",
)
env.RunAndExpectFailure(t, "server", "throttle", "set", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword,
"--upload-bytes-per-second=-10",
)
require.Equal(t, []string{
"Max Download Speed: 1 GB/s",
"Max Upload Speed: (unlimited)",
"Max Read Requests Per Second: 300",
"Max Write Requests Per Second: (unlimited)",
"Max List Requests Per Second: 500",
"Max Concurrent Reads: 300",
"Max Concurrent Writes: 400",
}, env.RunAndExpectSuccess(t, "server", "throttle", "get", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword))
var limits throttling.Limits
testutil.MustParseJSONLines(t, env.RunAndExpectSuccess(t, "server", "throttle", "get", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword, "--json"), &limits)
require.Equal(t, throttling.Limits{
ReadsPerSecond: 300,
WritesPerSecond: 0,
ListsPerSecond: 500,
UploadBytesPerSecond: 0,
DownloadBytesPerSecond: 1e+09,
ConcurrentReads: 300,
ConcurrentWrites: 400,
}, limits)
env.RunAndExpectSuccess(t, "server", "shutdown", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword)
select {
case <-serverStopped:
t.Logf("server shut down")
case <-time.After(15 * time.Second):
t.Fatalf("server did not shutdown in time")
}
// this will fail since the server is down
env.RunAndExpectFailure(t, "server", "status", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword)
env.RunAndExpectFailure(t, "server", "flush", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword)
env.RunAndExpectFailure(t, "server", "refresh", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword)
env.RunAndExpectFailure(t, "server", "shutdown", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword)
}
func TestServerControlUDS(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip()
}
env := testenv.NewCLITest(t, testenv.RepoFormatNotImportant, testenv.NewInProcRunner(t))
dir0 := testutil.TempDirectory(t)
// the socket path must be < 108 bytes (linux) or 104 bytes (Mac), so can't use long tempdir
dir1 := testutil.TempDirectoryShort(t)
env.RunAndExpectSuccess(t, "repo", "create", "filesystem", "--path", env.RepoDir, "--override-username=another-user", "--override-hostname=another-host")
env.RunAndExpectSuccess(t, "snap", "create", dir0)
env.RunAndExpectSuccess(t, "repo", "connect", "filesystem", "--path", env.RepoDir, "--override-username=test-user", "--override-hostname=test-host")
serverStarted := make(chan struct{})
serverStopped := make(chan struct{})
var sp testutil.ServerParameters
go func() {
wait, _ := env.RunAndProcessStderr(t, sp.ProcessOutput,
"server", "start", "--insecure", "--random-server-control-password", "--address="+"unix:"+dir1+"/sock")
close(serverStarted)
wait()
close(serverStopped)
}()
select {
case <-serverStarted:
t.Logf("server started on %v", sp.BaseURL)
case <-time.After(5 * time.Second):
t.Fatalf("server did not start in time")
}
lines := env.RunAndExpectSuccess(t, "server", "status", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword, "--remote")
require.Len(t, lines, 1)
require.Contains(t, lines, "REMOTE: another-user@another-host:"+dir0)
env.RunAndExpectSuccess(t, "server", "shutdown", "--address", sp.BaseURL, "--server-control-password", sp.ServerControlPassword)
select {
case <-serverStopped:
t.Logf("server shut down")
case <-time.After(15 * time.Second):
t.Fatalf("server did not shutdown in time")
}
}
func hasLine(lines []string, lookFor string) bool {
return slices.Contains(lines, lookFor)
}