diff --git a/Dockerfile b/Dockerfile.base similarity index 50% rename from Dockerfile rename to Dockerfile.base index 4f186e4..1a674ad 100644 --- a/Dockerfile +++ b/Dockerfile.base @@ -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" ] diff --git a/Dockerfile.prod b/Dockerfile.prod new file mode 100644 index 0000000..b5cf261 --- /dev/null +++ b/Dockerfile.prod @@ -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" ] diff --git a/Dockerfile.test b/Dockerfile.test new file mode 100644 index 0000000..83669f9 --- /dev/null +++ b/Dockerfile.test @@ -0,0 +1,4 @@ +ARG BASE_IMAGE +FROM alpine:3.20.2 +RUN apk update && apk add go curl openssh && mkdir /test +WORKDIR /test diff --git a/Makefile b/Makefile index 1ae1c95..51ef925 100644 --- a/Makefile +++ b/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 diff --git a/cmd/agent/agent.go b/cmd/agent/agent.go index 0bd1d86..b831018 100755 --- a/cmd/agent/agent.go +++ b/cmd/agent/agent.go @@ -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) diff --git a/cmd/tcptows/tcptows.go b/cmd/tcptows/tcptows.go index 153d9e7..70707cd 100644 --- a/cmd/tcptows/tcptows.go +++ b/cmd/tcptows/tcptows.go @@ -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() { diff --git a/cmd/wsproxy/wsproxy.go b/cmd/wsproxy/wsproxy.go index 5407bf0..45c7ef8 100644 --- a/cmd/wsproxy/wsproxy.go +++ b/cmd/wsproxy/wsproxy.go @@ -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{}) } diff --git a/cmd/wstotcp/wstotcp.go b/cmd/wstotcp/wstotcp.go index adbd82e..020959f 100644 --- a/cmd/wstotcp/wstotcp.go +++ b/cmd/wstotcp/wstotcp.go @@ -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) } diff --git a/compose.yaml b/compose.yaml index 71ed4b8..9953d23 100644 --- a/compose.yaml +++ b/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 + diff --git a/go.mod b/go.mod index 46d95e1..10f4d85 100755 --- a/go.mod +++ b/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 diff --git a/go.sum b/go.sum index 7c5edba..526069d 100755 --- a/go.sum +++ b/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= diff --git a/integrationtest/integration_test.go b/integrationtest/integration_test.go new file mode 100644 index 0000000..cfd77f1 --- /dev/null +++ b/integrationtest/integration_test.go @@ -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()) +} diff --git a/pkg/agent/service/sshservice.go b/pkg/agent/service/sshservice.go index 54b50aa..0f32c02 100644 --- a/pkg/agent/service/sshservice.go +++ b/pkg/agent/service/sshservice.go @@ -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 diff --git a/pkg/agent/session/ssh.go b/pkg/agent/session/ssh.go deleted file mode 100644 index 46e4a29..0000000 --- a/pkg/agent/session/ssh.go +++ /dev/null @@ -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) -} diff --git a/pkg/server/admin/admin.go b/pkg/server/admin/admin.go index 1a91f92..848e0b5 100644 --- a/pkg/server/admin/admin.go +++ b/pkg/server/admin/admin.go @@ -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" diff --git a/pkg/server/admin/admin_test.go b/pkg/server/admin/admin_test.go index 5d84ee1..f39b9b7 100644 --- a/pkg/server/admin/admin_test.go +++ b/pkg/server/admin/admin_test.go @@ -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 } diff --git a/pkg/server/matchmaker/matchmaker.go b/pkg/server/matchmaker/matchmaker.go index 1e457b8..ed6e276 100644 --- a/pkg/server/matchmaker/matchmaker.go +++ b/pkg/server/matchmaker/matchmaker.go @@ -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" diff --git a/pkg/server/matchmaker/matchmaker_test.go b/pkg/server/matchmaker/matchmaker_test.go index 706ee1c..408d55e 100644 --- a/pkg/server/matchmaker/matchmaker_test.go +++ b/pkg/server/matchmaker/matchmaker_test.go @@ -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 diff --git a/pkg/support/iowrappers/readwriteaddrcloser.go b/pkg/support/ioutils/readwriteaddrcloser.go similarity index 97% rename from pkg/support/iowrappers/readwriteaddrcloser.go rename to pkg/support/ioutils/readwriteaddrcloser.go index b46df5d..9c7dc7b 100644 --- a/pkg/support/iowrappers/readwriteaddrcloser.go +++ b/pkg/support/ioutils/readwriteaddrcloser.go @@ -1,4 +1,4 @@ -package iowrappers +package ioutils import ( "io" diff --git a/pkg/support/iowrappers/readwriteaddrcombiner.go b/pkg/support/ioutils/readwriteaddrcombiner.go similarity index 97% rename from pkg/support/iowrappers/readwriteaddrcombiner.go rename to pkg/support/ioutils/readwriteaddrcombiner.go index 16c755f..c16a629 100644 --- a/pkg/support/iowrappers/readwriteaddrcombiner.go +++ b/pkg/support/ioutils/readwriteaddrcombiner.go @@ -1,4 +1,4 @@ -package iowrappers +package ioutils import ( "io" diff --git a/pkg/support/ioutils/readwritercombiner.go b/pkg/support/ioutils/readwritercombiner.go new file mode 100644 index 0000000..7e514bc --- /dev/null +++ b/pkg/support/ioutils/readwritercombiner.go @@ -0,0 +1 @@ +package ioutils diff --git a/pkg/support/iowrappers/synchronizestreams.go b/pkg/support/ioutils/synchronizestreams.go similarity index 97% rename from pkg/support/iowrappers/synchronizestreams.go rename to pkg/support/ioutils/synchronizestreams.go index 8a01a8f..a3c7415 100644 --- a/pkg/support/iowrappers/synchronizestreams.go +++ b/pkg/support/ioutils/synchronizestreams.go @@ -1,4 +1,4 @@ -package iowrappers +package ioutils import ( "io" diff --git a/pkg/support/ioutils/utils.go b/pkg/support/ioutils/utils.go new file mode 100644 index 0000000..b123873 --- /dev/null +++ b/pkg/support/ioutils/utils.go @@ -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 +} diff --git a/pkg/support/iowrappers/readwritercombiner.go b/pkg/support/iowrappers/readwritercombiner.go deleted file mode 100644 index 34c2edb..0000000 --- a/pkg/support/iowrappers/readwritercombiner.go +++ /dev/null @@ -1 +0,0 @@ -package iowrappers diff --git a/pkg/support/sshsupport/ssh.go b/pkg/support/sshsupport/ssh.go new file mode 100644 index 0000000..ac2f582 --- /dev/null +++ b/pkg/support/sshsupport/ssh.go @@ -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 +} diff --git a/pkg/testsupport/bitpipe.go b/pkg/testsupport/bitpipe.go index a2ebf0c..1bd2741 100644 --- a/pkg/testsupport/bitpipe.go +++ b/pkg/testsupport/bitpipe.go @@ -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 } diff --git a/pkg/testsupport/utils.go b/pkg/testsupport/utils.go index 00edd23..fa8eb23 100644 --- a/pkg/testsupport/utils.go +++ b/pkg/testsupport/utils.go @@ -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 +}