first integration test implemented using testcontainers.
This commit is contained in:
parent
aa62cf6e4f
commit
8fe79e3c38
@ -1,4 +1,4 @@
|
||||
FROM alpine:3.20.2 as builder
|
||||
FROM alpine:3.20.2
|
||||
|
||||
RUN apk update && apk add go curl
|
||||
RUN mkdir -p /opt/converge/bin
|
||||
@ -16,17 +16,3 @@ COPY . /opt/converge/
|
||||
RUN templ generate
|
||||
RUN go build -ldflags "-linkmode 'external' -extldflags '-static'" -o bin ./cmd/...
|
||||
RUN GOOS=windows GOARCH=amd64 go build -o bin ./cmd/...
|
||||
|
||||
FROM scratch
|
||||
|
||||
COPY --from=builder /opt/converge/bin/converge /opt/converge/bin/
|
||||
COPY --from=builder /opt/converge/bin/agent \
|
||||
/opt/converge/bin/tcptows \
|
||||
/opt/converge/bin/wsproxy \
|
||||
/opt/converge/bin/agent.exe \
|
||||
/opt/converge/bin/tcptows.exe \
|
||||
/opt/converge/bin/wsproxy.exe \
|
||||
/opt/converge/static/
|
||||
COPY --from=builder /opt/converge/static/ /opt/converge/static/
|
||||
|
||||
ENTRYPOINT ["/opt/converge/bin/converge", "--static", "/opt/converge/static", "--downloads", "/opt/converge/static" ]
|
16
Dockerfile.prod
Normal file
16
Dockerfile.prod
Normal file
@ -0,0 +1,16 @@
|
||||
|
||||
ARG BASE_IMAGE
|
||||
FROM ${BASE_IMAGE} as builder
|
||||
FROM scratch
|
||||
|
||||
COPY --from=builder /opt/converge/bin/converge /opt/converge/bin/
|
||||
COPY --from=builder /opt/converge/bin/agent \
|
||||
/opt/converge/bin/tcptows \
|
||||
/opt/converge/bin/wsproxy \
|
||||
/opt/converge/bin/agent.exe \
|
||||
/opt/converge/bin/tcptows.exe \
|
||||
/opt/converge/bin/wsproxy.exe \
|
||||
/opt/converge/static/
|
||||
COPY --from=builder /opt/converge/static/ /opt/converge/static/
|
||||
|
||||
ENTRYPOINT ["/opt/converge/bin/converge", "--static", "/opt/converge/static", "--downloads", "/opt/converge/static" ]
|
4
Dockerfile.test
Normal file
4
Dockerfile.test
Normal file
@ -0,0 +1,4 @@
|
||||
ARG BASE_IMAGE
|
||||
FROM alpine:3.20.2
|
||||
RUN apk update && apk add go curl openssh && mkdir /test
|
||||
WORKDIR /test
|
11
Makefile
11
Makefile
@ -29,8 +29,11 @@ clean:
|
||||
|
||||
all: build buildwin
|
||||
|
||||
image:
|
||||
docker compose build
|
||||
images:
|
||||
docker compose build converge-builder
|
||||
docker compose build converge
|
||||
docker compose build converge-tester
|
||||
|
||||
push: image
|
||||
docker compose push
|
||||
push: images
|
||||
docker compose push converge
|
||||
docker compose push converge-tester
|
||||
|
@ -7,7 +7,7 @@ import (
|
||||
"git.wamblee.org/converge/pkg/agent/service"
|
||||
"git.wamblee.org/converge/pkg/agent/session"
|
||||
"git.wamblee.org/converge/pkg/comms"
|
||||
"git.wamblee.org/converge/pkg/support/iowrappers"
|
||||
"git.wamblee.org/converge/pkg/support/ioutils"
|
||||
"git.wamblee.org/converge/pkg/support/websocketutil"
|
||||
"github.com/gliderlabs/ssh"
|
||||
"github.com/gorilla/websocket"
|
||||
@ -37,7 +37,7 @@ func netCatServer(conn io.ReadWriter) {
|
||||
stdio := bufio.NewReadWriter(
|
||||
bufio.NewReaderSize(os.Stdin, 0),
|
||||
bufio.NewWriterSize(os.Stdout, 0))
|
||||
iowrappers.SynchronizeStreams("stdio -- ws", conn, stdio)
|
||||
ioutils.SynchronizeStreams("stdio -- ws", conn, stdio)
|
||||
}
|
||||
|
||||
type AgentService interface {
|
||||
@ -188,6 +188,7 @@ func main() {
|
||||
if len(args) != 1 {
|
||||
printHelp("")
|
||||
}
|
||||
shell := chooseShell(shells)
|
||||
|
||||
wsURL := args[0]
|
||||
url, err := url.Parse(wsURL)
|
||||
@ -221,7 +222,6 @@ func main() {
|
||||
wsConn := websocketutil.NewWebSocketConn(conn, false)
|
||||
defer wsConn.Close()
|
||||
|
||||
shell := chooseShell(shells)
|
||||
_, err = comms.AgentInitialization(wsConn, comms.NewEnvironmentInfo(shell))
|
||||
if err != nil {
|
||||
log.Printf("ERROR: %v", err)
|
||||
|
@ -3,7 +3,7 @@ package main
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"git.wamblee.org/converge/pkg/support/iowrappers"
|
||||
"git.wamblee.org/converge/pkg/support/ioutils"
|
||||
"git.wamblee.org/converge/pkg/support/websocketutil"
|
||||
"github.com/gorilla/websocket"
|
||||
"log"
|
||||
@ -39,7 +39,7 @@ func handleConnection(conn net.Conn, wsURL string, insecure bool) {
|
||||
wsConn := websocketutil.NewWebSocketConn(_wsConn, false)
|
||||
defer wsConn.Close()
|
||||
|
||||
iowrappers.SynchronizeStreams(wsURL+" -- localport", wsConn, conn)
|
||||
ioutils.SynchronizeStreams(wsURL+" -- localport", wsConn, conn)
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"git.wamblee.org/converge/pkg/comms"
|
||||
"git.wamblee.org/converge/pkg/support/iowrappers"
|
||||
"git.wamblee.org/converge/pkg/support/ioutils"
|
||||
"git.wamblee.org/converge/pkg/support/websocketutil"
|
||||
"github.com/gorilla/websocket"
|
||||
"log"
|
||||
@ -147,5 +147,5 @@ func main() {
|
||||
|
||||
dataConnection := wsConn
|
||||
|
||||
iowrappers.SynchronizeStreams(wsURL+" -- stdio", dataConnection, Stdio{})
|
||||
ioutils.SynchronizeStreams(wsURL+" -- stdio", dataConnection, Stdio{})
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.wamblee.org/converge/pkg/support/iowrappers"
|
||||
"git.wamblee.org/converge/pkg/support/ioutils"
|
||||
"git.wamblee.org/converge/pkg/support/websocketutil"
|
||||
"github.com/gorilla/websocket"
|
||||
"log"
|
||||
@ -42,5 +42,5 @@ func handleWebSocket(w http.ResponseWriter, r *http.Request, tcpConn net.Conn) {
|
||||
}
|
||||
defer wsConn.Close()
|
||||
|
||||
iowrappers.SynchronizeStreams("tcp -- ws", tcpConn, wsConn)
|
||||
ioutils.SynchronizeStreams("tcp -- ws", tcpConn, wsConn)
|
||||
}
|
||||
|
23
compose.yaml
23
compose.yaml
@ -1,9 +1,32 @@
|
||||
|
||||
services:
|
||||
|
||||
converge-builder:
|
||||
image: $REGISTRY/converge-builder:1.0
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.base
|
||||
profiles:
|
||||
- build_only
|
||||
|
||||
converge:
|
||||
image: $REGISTRY/converge:1.0
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.prod
|
||||
args:
|
||||
BASE_IMAGE: $REGISTRY/converge-builder:1.0
|
||||
ports:
|
||||
- 8000:8000
|
||||
|
||||
|
||||
converge-tester:
|
||||
image: $REGISTRY/converge-test:1.0
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.test
|
||||
args:
|
||||
BASE_IMAGE: $REGISTRY/converge-builder:1.0
|
||||
profiles:
|
||||
- build_only
|
||||
|
||||
|
43
go.mod
43
go.mod
@ -19,18 +19,59 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/containerd/containerd v1.7.18 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/cpuguy83/dockercfg v0.3.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/docker v27.1.1+incompatible // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/klauspost/compress v1.17.4 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||
github.com/moby/sys/user v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.48.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/shirou/gopsutil/v3 v3.23.12 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/testcontainers/testcontainers-go v0.33.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
118
go.sum
118
go.sum
@ -1,41 +1,108 @@
|
||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
github.com/ActiveState/termtest/conpty v0.5.0 h1:JLUe6YDs4Jw4xNPCU+8VwTpniYOGeKzQg4SM2YHQNA8=
|
||||
github.com/ActiveState/termtest/conpty v0.5.0/go.mod h1:LO4208FLsxw6DcNZ1UtuGUMW+ga9PFtX4ntv8Ymg9og=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/a-h/templ v0.2.747 h1:D0dQ2lxC3W7Dxl6fxQ/1zZHBQslSkTSvl5FxP/CfdKg=
|
||||
github.com/a-h/templ v0.2.747/go.mod h1:69ObQIbrcuwPCU32ohNaWce3Cb7qM5GMiqN1K+2yop4=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao=
|
||||
github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
|
||||
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
|
||||
github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E=
|
||||
github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
|
||||
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
|
||||
github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
|
||||
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
||||
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
||||
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
||||
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
||||
github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg=
|
||||
github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU=
|
||||
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
|
||||
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo=
|
||||
github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
||||
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||
@ -46,36 +113,82 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k
|
||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||
github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
|
||||
github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
|
||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
|
||||
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/testcontainers/testcontainers-go v0.33.0 h1:zJS9PfXYT5O0ZFXM2xxXfk4J5UMw/kRiISng037Gxdw=
|
||||
github.com/testcontainers/testcontainers-go v0.33.0/go.mod h1:W80YpTa8D5C3Yy16icheD01UTDu+LmXIA2Keo+jWtT8=
|
||||
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
|
||||
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
|
||||
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
|
||||
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
||||
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
|
||||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200428200454-593003d681fa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
@ -89,8 +202,13 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
220
integrationtest/integration_test.go
Normal file
220
integrationtest/integration_test.go
Normal file
@ -0,0 +1,220 @@
|
||||
package integrationtest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"git.wamblee.org/converge/pkg/support/ioutils"
|
||||
"git.wamblee.org/converge/pkg/support/sshsupport"
|
||||
"git.wamblee.org/converge/pkg/testsupport"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/testcontainers/testcontainers-go"
|
||||
"github.com/testcontainers/testcontainers-go/network"
|
||||
"github.com/testcontainers/testcontainers-go/wait"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
agentEntryPointTemplate = `
|
||||
curl -v http://converge:8000/downloads/agent > agent
|
||||
chmod 755 agent
|
||||
echo "{{.publicKey}}" > .authorized_keys
|
||||
cat .authorized_keys
|
||||
echo "HOSTNAME=$( hostname )"
|
||||
./agent --id abc ws://converge:8000
|
||||
tail -f /dev/null `
|
||||
|
||||
clientEntryPointTemplate = `
|
||||
curl -v http://converge:8000/downloads/wsproxy > wsproxy
|
||||
chmod 755 wsproxy
|
||||
echo "{{.privateKey}}" > id_rsa
|
||||
chmod 400 id_rsa
|
||||
echo "===================================================================================================="
|
||||
echo "
|
||||
hostname | sed 's/^/=============== HOSTNAME_OF_AGENT_ON_CLIENT=/g' " |
|
||||
ssh -tt -oStrictHostKeyChecking=no -i id_rsa -oProxyCommand="./wsproxy ws://converge:8000/client/abc" localhost
|
||||
echo "===================================================================================================="
|
||||
echo Ready
|
||||
sleep 1000000`
|
||||
)
|
||||
|
||||
var (
|
||||
convergeImage = fmt.Sprintf("%s/converge:1.0", getRegistry())
|
||||
agentImage = fmt.Sprintf("%s/converge-test:1.0", getRegistry())
|
||||
clientImage = agentImage
|
||||
)
|
||||
|
||||
func getRegistry() string {
|
||||
registry, ok := os.LookupEnv("REGISTRY")
|
||||
if ok {
|
||||
return registry
|
||||
}
|
||||
moduleDir := testsupport.GetGoModDIr()
|
||||
log.Printf("Go module directory %s", moduleDir)
|
||||
envFile := filepath.Join(moduleDir, ".env")
|
||||
file, err := os.Open(envFile)
|
||||
if err != nil {
|
||||
panic("Could not find .env file and REGISTRY env var is not set")
|
||||
}
|
||||
defer file.Close()
|
||||
props, err := ioutils.ReadPropertyFile(file)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error reading environment file '%v': %v", envFile, err))
|
||||
}
|
||||
registry, ok = props["REGISTRY"]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Could not find REGISTRY setting in property file '%v'", envFile))
|
||||
}
|
||||
return registry
|
||||
}
|
||||
|
||||
type IntegrationTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
ctx context.Context
|
||||
cancelFunc context.CancelFunc
|
||||
}
|
||||
|
||||
func Test_IntergrationTestSuite(t *testing.T) {
|
||||
suite.Run(t, &IntegrationTestSuite{})
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) SetupSuite() {
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TearDownSuite() {
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) SetupTest() {
|
||||
ctx, cancelFunc := testsupport.CreateTestContext(context.Background(), 10*time.Second)
|
||||
s.ctx = ctx
|
||||
s.cancelFunc = cancelFunc
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) TearDownTest() {
|
||||
s.cancelFunc()
|
||||
}
|
||||
|
||||
type ContainerLogger func(logtype string, msg string)
|
||||
|
||||
func logContainerOutput(container string, logtype string, msg string) {
|
||||
log.Printf("%s: %s: %s", container, logtype, msg)
|
||||
}
|
||||
|
||||
func containerLogger(container string) ContainerLogger {
|
||||
return func(logtype string, msg string) {
|
||||
logContainerOutput(container, logtype, msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (c ContainerLogger) Accept(log testcontainers.Log) {
|
||||
c(log.LogType, string(log.Content))
|
||||
}
|
||||
|
||||
func createLogConsumerConfig(container string) *testcontainers.LogConsumerConfig {
|
||||
return &testcontainers.LogConsumerConfig{
|
||||
Opts: []testcontainers.LogProductionOption{testcontainers.WithLogProductionTimeout(10 * time.Second)},
|
||||
Consumers: []testcontainers.LogConsumer{containerLogger(container)},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) runCmd(container testcontainers.Container, cmd []string) (int, string, error) {
|
||||
status, reader, err := container.Exec(s.ctx, cmd)
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
output, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
return status, string(output), nil
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) runCmdFailOnError(container testcontainers.Container, cmd []string) (int, string) {
|
||||
status, output, err := s.runCmd(container, cmd)
|
||||
s.Require().Nil(err)
|
||||
return status, output
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) defineContainer(net *testcontainers.DockerNetwork, hostname string,
|
||||
image string,
|
||||
waitForLog string,
|
||||
ports []string) testcontainers.GenericContainerRequest {
|
||||
|
||||
log.Printf("PORTS %v", ports)
|
||||
|
||||
request := testcontainers.GenericContainerRequest{
|
||||
ContainerRequest: testcontainers.ContainerRequest{
|
||||
Image: image,
|
||||
ExposedPorts: ports,
|
||||
Hostname: hostname,
|
||||
WaitingFor: wait.ForLog(waitForLog).WithStartupTimeout(10 * time.Second),
|
||||
LogConsumerCfg: createLogConsumerConfig(hostname),
|
||||
Networks: []string{net.Name},
|
||||
},
|
||||
Started: false,
|
||||
}
|
||||
return request
|
||||
}
|
||||
|
||||
func (s *IntegrationTestSuite) Test_SingleAgentAndClient() {
|
||||
|
||||
net, err := network.New(s.ctx)
|
||||
s.Nil(err)
|
||||
|
||||
// SSH key to use
|
||||
privateKey, publicKey, err := sshsupport.GeneratePrivatePublicKey(2048)
|
||||
s.Require().Nil(err)
|
||||
|
||||
// The server
|
||||
|
||||
converge, err := testcontainers.GenericContainer(s.ctx,
|
||||
s.defineContainer(net, "converge",
|
||||
convergeImage,
|
||||
"Rendez-vous server listening",
|
||||
[]string{"8000/tcp"}))
|
||||
s.Require().Nil(err)
|
||||
err = converge.Start(s.ctx)
|
||||
s.Require().Nil(err)
|
||||
port, err := converge.MappedPort(s.ctx, "8000/tcp")
|
||||
s.Require().Nil(err)
|
||||
log.Printf("External port %v", port)
|
||||
|
||||
// The agent
|
||||
|
||||
agentEntrypoint := testsupport.Template(agentEntryPointTemplate,
|
||||
map[string]string{"publicKey": publicKey})
|
||||
|
||||
agentRequest := s.defineContainer(net, "agent", agentImage,
|
||||
"starting ssh server, waiting for debug sessions", []string{})
|
||||
agentRequest.Entrypoint = []string{
|
||||
"sh", "-c", agentEntrypoint}
|
||||
agent, err := testcontainers.GenericContainer(s.ctx, agentRequest)
|
||||
s.Require().Nil(err)
|
||||
s.Require().Nil(agent.Start(s.ctx))
|
||||
|
||||
// The client
|
||||
|
||||
clientEntrypoint := testsupport.Template(clientEntryPointTemplate,
|
||||
map[string]string{"privateKey": privateKey})
|
||||
clientRequest := s.defineContainer(net, "client", clientImage,
|
||||
"Ready", []string{})
|
||||
clientRequest.Entrypoint = []string{
|
||||
"sh", "-c", clientEntrypoint}
|
||||
client, err := testcontainers.GenericContainer(s.ctx, clientRequest)
|
||||
s.Require().Nil(err)
|
||||
s.Require().Nil(client.Start(s.ctx))
|
||||
|
||||
input, err := client.Logs(s.ctx)
|
||||
s.Require().Nil(err)
|
||||
inputBytes, err := io.ReadAll(input)
|
||||
s.Require().Nil(err)
|
||||
s.True(strings.Contains(string(inputBytes), "HOSTNAME_OF_AGENT_ON_CLIENT=agent"))
|
||||
|
||||
log.Printf("Found registry: %v", getRegistry())
|
||||
}
|
@ -5,7 +5,7 @@ import (
|
||||
"git.wamblee.org/converge/pkg/agent/session"
|
||||
"git.wamblee.org/converge/pkg/agent/terminal"
|
||||
"git.wamblee.org/converge/pkg/comms"
|
||||
"git.wamblee.org/converge/pkg/support/iowrappers"
|
||||
"git.wamblee.org/converge/pkg/support/ioutils"
|
||||
"github.com/gliderlabs/ssh"
|
||||
"github.com/pkg/sftp"
|
||||
"io"
|
||||
@ -72,7 +72,7 @@ func (s *SshAgentService) sshServer() *ssh.Server {
|
||||
// For SSH we detect activity when there are writes to the shell that was spawned.
|
||||
// This means user input.
|
||||
activityDetector := NewWriteDetector(process.Pipe())
|
||||
iowrappers.SynchronizeStreams("shell -- ssh", activityDetector, sshSession)
|
||||
ioutils.SynchronizeStreams("shell -- ssh", activityDetector, sshSession)
|
||||
session.LogOut(sessionInfo.ClientId)
|
||||
// Using Kill() here will create defunct processes and in normal
|
||||
// circumstances Wait() will be the best because the process will be shutting
|
||||
|
@ -1,35 +0,0 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
func generateHostKey(filename string, bitSize int) error {
|
||||
if _, err := os.Stat(filename); !os.IsNotExist(err) {
|
||||
log.Printf("Reusing key file '%s'", filename)
|
||||
return nil
|
||||
}
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, bitSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
privateKeyPEM := &pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
|
||||
}
|
||||
|
||||
privateKeyFile, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer privateKeyFile.Close()
|
||||
|
||||
log.Printf("Generating key '%s'", filename)
|
||||
return pem.Encode(privateKeyFile, privateKeyPEM)
|
||||
}
|
@ -5,7 +5,7 @@ import (
|
||||
"git.wamblee.org/converge/pkg/comms"
|
||||
"git.wamblee.org/converge/pkg/models"
|
||||
"git.wamblee.org/converge/pkg/support/concurrency"
|
||||
iowrappers2 "git.wamblee.org/converge/pkg/support/iowrappers"
|
||||
iowrappers2 "git.wamblee.org/converge/pkg/support/ioutils"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
|
@ -7,7 +7,7 @@ import (
|
||||
"fmt"
|
||||
"git.wamblee.org/converge/pkg/comms"
|
||||
"git.wamblee.org/converge/pkg/models"
|
||||
"git.wamblee.org/converge/pkg/support/iowrappers"
|
||||
"git.wamblee.org/converge/pkg/support/ioutils"
|
||||
"git.wamblee.org/converge/pkg/testsupport"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"go.uber.org/goleak"
|
||||
@ -305,7 +305,7 @@ func (s *AdminTestSuite) Test_connectClientUnknownRendezVousId() {
|
||||
serverToClientRW, _ := testsupport.CreatePipe(s.ctx)
|
||||
|
||||
_, err := s.admin.AddClient(models.RendezVousId(publicId+"sothatisunknown"),
|
||||
iowrappers.NewSimpleReadWriteAddrCloser(serverToClientRW, testsupport.DummyRemoteAddr("remoteaddr")))
|
||||
ioutils.NewSimpleReadWriteAddrCloser(serverToClientRW, testsupport.DummyRemoteAddr("remoteaddr")))
|
||||
s.NotNil(err)
|
||||
|
||||
// verify state
|
||||
@ -354,7 +354,7 @@ func (s *AdminTestSuite) agentRegistration(agentToServerRW io.ReadWriteCloser) A
|
||||
func (s *AdminTestSuite) connectClient(publicId string, serverToClientRW io.ReadWriteCloser) any {
|
||||
// server
|
||||
clientConn, err := s.admin.AddClient(models.RendezVousId(publicId),
|
||||
iowrappers.NewSimpleReadWriteAddrCloser(serverToClientRW, testsupport.DummyRemoteAddr("remoteaddr")))
|
||||
ioutils.NewSimpleReadWriteAddrCloser(serverToClientRW, testsupport.DummyRemoteAddr("remoteaddr")))
|
||||
s.Nil(err)
|
||||
return clientConn
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
"git.wamblee.org/converge/pkg/comms"
|
||||
"git.wamblee.org/converge/pkg/models"
|
||||
"git.wamblee.org/converge/pkg/server/admin"
|
||||
iowrappers2 "git.wamblee.org/converge/pkg/support/iowrappers"
|
||||
iowrappers2 "git.wamblee.org/converge/pkg/support/ioutils"
|
||||
"io"
|
||||
"log"
|
||||
"time"
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"git.wamblee.org/converge/pkg/comms"
|
||||
"git.wamblee.org/converge/pkg/models"
|
||||
"git.wamblee.org/converge/pkg/support/iowrappers"
|
||||
"git.wamblee.org/converge/pkg/support/ioutils"
|
||||
"git.wamblee.org/converge/pkg/testsupport"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"go.uber.org/goleak"
|
||||
@ -110,7 +110,7 @@ func (agent *TestAgent) Register(s *MatchMakerTestSuite) error {
|
||||
|
||||
type TestClient struct {
|
||||
clientSideConn io.ReadWriteCloser
|
||||
serverSIdeConn iowrappers.ReadWriteAddrCloser
|
||||
serverSIdeConn ioutils.ReadWriteAddrCloser
|
||||
publicId models.RendezVousId
|
||||
clientId models.ClientId
|
||||
|
||||
@ -123,7 +123,7 @@ func NewTestClient(ctx context.Context) *TestClient {
|
||||
a, b := testsupport.CreatePipe(ctx)
|
||||
res := TestClient{
|
||||
clientSideConn: a,
|
||||
serverSIdeConn: iowrappers.NewSimpleReadWriteAddrCloser(b,
|
||||
serverSIdeConn: ioutils.NewSimpleReadWriteAddrCloser(b,
|
||||
testsupport.DummyRemoteAddr("remoteaddr")),
|
||||
}
|
||||
return &res
|
||||
|
@ -1,4 +1,4 @@
|
||||
package iowrappers
|
||||
package ioutils
|
||||
|
||||
import (
|
||||
"io"
|
@ -1,4 +1,4 @@
|
||||
package iowrappers
|
||||
package ioutils
|
||||
|
||||
import (
|
||||
"io"
|
1
pkg/support/ioutils/readwritercombiner.go
Normal file
1
pkg/support/ioutils/readwritercombiner.go
Normal file
@ -0,0 +1 @@
|
||||
package ioutils
|
@ -1,4 +1,4 @@
|
||||
package iowrappers
|
||||
package ioutils
|
||||
|
||||
import (
|
||||
"io"
|
40
pkg/support/ioutils/utils.go
Normal file
40
pkg/support/ioutils/utils.go
Normal file
@ -0,0 +1,40 @@
|
||||
package ioutils
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func FileExistsWithStats(filename string) (os.FileInfo, bool) {
|
||||
stats, err := os.Stat(filename)
|
||||
return stats, !os.IsNotExist(err)
|
||||
}
|
||||
|
||||
func FileExists(filename string) bool {
|
||||
_, err := os.Stat(filename)
|
||||
return !os.IsNotExist(err)
|
||||
}
|
||||
|
||||
func ReadPropertyFile(reader io.Reader) (map[string]string, error) {
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
props := make(map[string]string)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
commentCharIndex := strings.Index(line, "#")
|
||||
if commentCharIndex >= 0 {
|
||||
line = line[commentCharIndex+1:]
|
||||
}
|
||||
if equalIndex := strings.Index(line, "="); equalIndex >= 0 {
|
||||
key := strings.TrimSpace(line[:equalIndex])
|
||||
value := strings.TrimSpace(line[equalIndex+1:])
|
||||
props[key] = value
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return props, nil
|
||||
}
|
@ -1 +0,0 @@
|
||||
package iowrappers
|
65
pkg/support/sshsupport/ssh.go
Normal file
65
pkg/support/sshsupport/ssh.go
Normal file
@ -0,0 +1,65 @@
|
||||
package sshsupport
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func generateHostKey(filename string, bitSize int) error {
|
||||
if _, err := os.Stat(filename); !os.IsNotExist(err) {
|
||||
log.Printf("Reusing key file '%s'", filename)
|
||||
return nil
|
||||
}
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, bitSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
privateKeyPEM := &pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
|
||||
}
|
||||
|
||||
privateKeyFile, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer privateKeyFile.Close()
|
||||
|
||||
log.Printf("Generating key '%s'", filename)
|
||||
return pem.Encode(privateKeyFile, privateKeyPEM)
|
||||
}
|
||||
|
||||
func GeneratePrivatePublicKey(bitsize int) (privateKeyData string, publicKeyData string, err error) {
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, bitsize)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Encode private key to PEM format
|
||||
privateKeyPEM := &pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
|
||||
}
|
||||
buf := bytes.Buffer{}
|
||||
if err := pem.Encode(&buf, privateKeyPEM); err != nil {
|
||||
return "", "", fmt.Errorf("failed to write private key: %v", err)
|
||||
}
|
||||
privateKeyData = string(buf.String())
|
||||
|
||||
publicKey, err := ssh.NewPublicKey(&privateKey.PublicKey)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("failed to generate public key: %v", err)
|
||||
}
|
||||
|
||||
publicKeyData = strings.TrimSpace(string(ssh.MarshalAuthorizedKey(publicKey)))
|
||||
return privateKeyData, publicKeyData, nil
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package testsupport
|
||||
|
||||
import "git.wamblee.org/converge/pkg/support/iowrappers"
|
||||
import "git.wamblee.org/converge/pkg/support/ioutils"
|
||||
|
||||
type DummyRemoteAddr string
|
||||
|
||||
@ -16,6 +16,6 @@ func (r DummyRemoteAddr) String() string {
|
||||
// code under test reads the other side.
|
||||
|
||||
type BitPipe interface {
|
||||
Front() iowrappers.ReadWriteAddrCloser
|
||||
Back() iowrappers.ReadWriteAddrCloser
|
||||
Front() ioutils.ReadWriteAddrCloser
|
||||
Back() ioutils.ReadWriteAddrCloser
|
||||
}
|
||||
|
@ -1,16 +1,21 @@
|
||||
package testsupport
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"git.wamblee.org/converge/pkg/support/ioutils"
|
||||
"git.wamblee.org/converge/pkg/support/pprof"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
_ "runtime/pprof"
|
||||
"sync"
|
||||
"text/template"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -128,3 +133,32 @@ func CheckCondition(ctx context.Context, condition func() bool) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func Template(templateString string, data any) string {
|
||||
tmpl, err := template.New("dummy").Parse(templateString)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
buf := bytes.Buffer{}
|
||||
err = tmpl.Execute(&buf, data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func GetGoModDIr() string {
|
||||
curDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Unable to get the current directory %v", err))
|
||||
}
|
||||
for mod := filepath.Join(curDir, "go.mod"); !ioutils.FileExists(mod); mod = filepath.Join(curDir, "go.mod") {
|
||||
newCurDir := filepath.Dir(curDir)
|
||||
if newCurDir == curDir {
|
||||
panic("Could not find top-level directory of converge module")
|
||||
}
|
||||
curDir = newCurDir
|
||||
}
|
||||
|
||||
return curDir
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user