cross compilation on windows working.

pty.Start() is not supported on windows
This commit is contained in:
Erik Brakkee 2024-07-22 19:34:26 +02:00
parent 982977e796
commit 1ebee30c8c
16 changed files with 204 additions and 52 deletions

View File

@ -7,11 +7,18 @@ WORKDIR /opt/converge
RUN go mod download
COPY . /opt/converge/
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/docs/
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/docs/
COPY --from=builder /opt/converge/static/ /opt/converge/docs/
ENTRYPOINT ["/opt/converge/bin/converge", "/opt/converge/docs" ]

View File

@ -13,6 +13,10 @@ build: vet
mkdir -p bin
go build -o bin ./cmd/...
buildwin:
mkdir -p bin
GOOS=windows GOARCH=amd64 go build -o bin ./cmd/...
clean:
rm -rf bin

View File

@ -6,7 +6,11 @@ import (
"converge/pkg/iowrappers"
"converge/pkg/websocketutil"
"fmt"
"github.com/creack/pty"
"github.com/gliderlabs/ssh"
"github.com/gorilla/websocket"
"github.com/hashicorp/yamux"
"github.com/pkg/sftp"
"io"
"log"
"net"
@ -14,14 +18,7 @@ import (
"os"
"os/exec"
"strings"
"syscall"
"time"
"unsafe"
"github.com/creack/pty"
"github.com/gliderlabs/ssh"
"github.com/hashicorp/yamux"
"github.com/pkg/sftp"
_ "embed"
)
@ -55,11 +52,6 @@ func passwordAuth(ctx ssh.Context, password string) bool {
return ctx.User() == "abc" && password == "123"
}
func setWinsize(f *os.File, w, h int) {
syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(syscall.TIOCSWINSZ),
uintptr(unsafe.Pointer(&struct{ h, w, x, y uint16 }{uint16(h), uint16(w), 0, 0})))
}
func sshServer(hostKeyFile string, shellCommand string) *ssh.Server {
ssh.Handle(func(s ssh.Session) {

12
cmd/agent/agent_linux.go Executable file
View File

@ -0,0 +1,12 @@
package main
import (
"os"
"syscall"
"unsafe"
)
func setWinsize(f *os.File, w, h int) {
syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(syscall.TIOCSWINSZ),
uintptr(unsafe.Pointer(&struct{ h, w, x, y uint16 }{uint16(h), uint16(w), 0, 0})))
}

7
cmd/agent/agent_windows.go Executable file
View File

@ -0,0 +1,7 @@
package main
import "os"
func setWinsize(f *os.File, w, h int) {
// Empty
}

View File

@ -0,0 +1,98 @@
package main
import (
"fmt"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
func example() {
// Create pipes for stdin and stdout
var stdInRead, stdInWrite, stdOutRead, stdOutWrite windows.Handle
sa := &windows.SecurityAttributes{Length: uint32(unsafe.Sizeof(windows.SecurityAttributes{})), InheritHandle: 1}
err := windows.CreatePipe(&stdInRead, &stdInWrite, sa, 0)
if err != nil {
fmt.Println("Error creating stdin pipe:", err)
return
}
defer windows.CloseHandle(stdInRead)
defer windows.CloseHandle(stdInWrite)
err = windows.CreatePipe(&stdOutRead, &stdOutWrite, sa, 0)
if err != nil {
fmt.Println("Error creating stdout pipe:", err)
return
}
defer windows.CloseHandle(stdOutRead)
defer windows.CloseHandle(stdOutWrite)
// Set the pipe to non-blocking mode
mode := uint32(windows.PIPE_NOWAIT)
err = windows.SetNamedPipeHandleState(stdInWrite, &mode, nil, nil)
if err != nil {
fmt.Println("Error setting stdin pipe to non-blocking:", err)
return
}
err = windows.SetNamedPipeHandleState(stdOutRead, &mode, nil, nil)
if err != nil {
fmt.Println("Error setting stdout pipe to non-blocking:", err)
return
}
// Prepare process startup info
si := &windows.StartupInfo{
Cb: uint32(unsafe.Sizeof(windows.StartupInfo{})),
Flags: windows.STARTF_USESTDHANDLES,
StdInput: stdInRead,
StdOutput: stdOutWrite,
StdErr: stdOutWrite,
}
pi := &windows.ProcessInformation{}
// Create the process
cmd := "cmd.exe"
err = windows.CreateProcess(
nil,
syscall.StringToUTF16Ptr(cmd),
nil,
nil,
true,
0,
nil,
nil,
si,
pi,
)
if err != nil {
fmt.Println("Error creating process:", err)
return
}
defer windows.CloseHandle(pi.Process)
defer windows.CloseHandle(pi.Thread)
// Write to the process
message := "echo Hello, World!\r\n"
var written uint32
err = windows.WriteFile(stdInWrite, []byte(message), &written, nil)
if err != nil {
fmt.Println("Error writing to process:", err)
return
}
// Read from the process
buffer := make([]byte, 1024)
var read uint32
err = windows.ReadFile(stdOutRead, buffer, &read, nil)
if err != nil && err != windows.ERROR_NO_DATA {
fmt.Println("Error reading from process:", err)
return
}
fmt.Printf("Output: %s", buffer[:read])
// Wait for the process to finish
windows.WaitForSingleObject(pi.Process, windows.INFINITE)
}

View File

@ -116,19 +116,3 @@ func runSSHClient(user, password string, netConn net.Conn) error {
return nil
}
func handleWindowChange(session *ssh.Session, fd int) {
sigwinchCh := make(chan os.Signal, 1)
signal.Notify(sigwinchCh, syscall.SIGWINCH)
for range sigwinchCh {
width, height, err := term.GetSize(fd)
if err != nil {
log.Printf("Failed to get window size: %v\n", err)
continue
}
if err := session.WindowChange(height, width); err != nil {
log.Printf("Failed to send window change request: %v\n", err)
}
}
}

View File

@ -0,0 +1,26 @@
package main
import (
"golang.org/x/crypto/ssh"
"golang.org/x/term"
"log"
"os"
"os/signal"
"syscall"
)
func handleWindowChange(session *ssh.Session, fd int) {
sigwinchCh := make(chan os.Signal, 1)
signal.Notify(sigwinchCh, syscall.SIGWINCH)
for range sigwinchCh {
width, height, err := term.GetSize(fd)
if err != nil {
log.Printf("Failed to get window size: %v\n", err)
continue
}
if err := session.WindowChange(height, width); err != nil {
log.Printf("Failed to send window change request: %v\n", err)
}
}
}

View File

@ -0,0 +1,7 @@
package main
import "golang.org/x/crypto/ssh"
func handleWindowChange(session *ssh.Session, fd int) {
// Empty
}

View File

@ -2,16 +2,13 @@ package main
import (
"fmt"
"github.com/creack/pty"
"github.com/gliderlabs/ssh"
"github.com/pkg/sftp"
"io"
"log"
"os"
"os/exec"
"syscall"
"unsafe"
"github.com/creack/pty"
"github.com/gliderlabs/ssh"
"github.com/pkg/sftp"
)
func SftpHandler(sess ssh.Session) {
@ -40,11 +37,6 @@ func passwordAuth(ctx ssh.Context, password string) bool {
return ctx.User() == "abc" && password == "123"
}
func setWinsize(f *os.File, w, h int) {
syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(syscall.TIOCSWINSZ),
uintptr(unsafe.Pointer(&struct{ h, w, x, y uint16 }{uint16(h), uint16(w), 0, 0})))
}
func main() {
ssh.Handle(func(s ssh.Session) {
cmd := exec.Command("bash")

View File

@ -0,0 +1,12 @@
package main
import (
"os"
"syscall"
"unsafe"
)
func setWinsize(f *os.File, w, h int) {
syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), uintptr(syscall.TIOCSWINSZ),
uintptr(unsafe.Pointer(&struct{ h, w, x, y uint16 }{uint16(h), uint16(w), 0, 0})))
}

View File

@ -0,0 +1,7 @@
package main
import "os"
func setWinsize(f *os.File, w, h int) {
// Empty
}

View File

@ -2,7 +2,7 @@
services:
converge:
image: your.repo.com/converge:1.0
image: $REGISTRY/converge:1.0
build:
context: .
ports:

4
go.mod
View File

@ -1,6 +1,6 @@
module converge
module converge
go 1.18
go 1.21
require (
github.com/creack/pty v1.1.21

View File

@ -30,6 +30,8 @@ type AgentState struct {
// map of unique session id to a session
sessions map[int]*AgentSession
agentUsed bool
}
type AgentSession struct {
@ -61,6 +63,7 @@ func ConfigureAgent(advanceWarningTime, agentExpiryTime, tickerInterval time.Dur
tickerInterval: tickerInterval,
ticker: time.NewTicker(tickerInterval),
sessions: make(map[int]*AgentSession),
agentUsed: false,
}
go func() {
@ -97,6 +100,7 @@ func Login(sessionId int, sshSession ssh.Session) {
sshSession: sshSession,
}
state.sessions[sessionId] = &agentSession
state.agentUsed = true
LogStatus()
}
@ -178,7 +182,7 @@ func check() {
}
}
if !fileExists(holdFilename) && len(state.sessions) == 0 {
if state.agentUsed && !fileExists(holdFilename) && len(state.sessions) == 0 {
log.Printf("All clients disconnected and no '%s' file found, exiting", holdFilename)
os.Exit(0)
}

View File

@ -17,7 +17,7 @@
<h1>About</h1>
<p>
Converge is a utility for troubleshooting builds on continuous integration serves.
Converge is a utility for troubleshooting builds on continuous integration servers.
It solves a common problem where the cause of job failure is difficult to determine.
This is complicated further by the fact that build jobs are usually run on a build
farm where there is no access to the build agents or in more modern envrionments when
@ -28,7 +28,7 @@
With Converge it is possible to get remote shell access to such jobs. This works
by configuring the build job to connect to a Converge server using an agent program.
The agent program can be downloaded from within the CI job using curl or wget.
Next, an end-use can connect to the Converge server, a rendez-vous server, that connects
Next, an end-user can connect to the Converge server, a rendez-vous server, that connects
the client and server together.
</p>
@ -44,12 +44,12 @@
<p>
There is a timeout mechanism in the agent such that jobs do not hang indefinitely waiting
for a connection. This mechanism is useful to make sure build agents do not wait
indefinitely for a user session. By default, the agent exits with status 0 when
for a connection. This mechanism is useful to make sure build agents do not keep
build agents occupied for a long time. By default, the agent exits with status 0 when
the first client exits after logging in. This behavior as well as general expiry can be
controlled from within a shell session by touching a .hold file. After logging in, the
user can control expiry of the session as instructed by messages in the ssh session.
Then the timeout of a session is near the user is informed about this with messages
When the timeout of a session is near the user is informed about this with messages
in the shell.
</p>
@ -69,8 +69,8 @@
Above, ID is a unique id
for the job. This should not conflict with other ids.
This connects the agent to the converge server. Clients can now connect to converge
to establish a connection to the CI job through converge.
This connects the agent to the converge server. Clients can now connect to the Converge
server to establish a connection to the CI job through converge.
</p>
<h2>Local clients: using ssh proxy command </h2>
@ -84,7 +84,7 @@
server.</p>
<p>
Next step is to run a local SSH of SFTP client:
Next step is to run a local SSH or SFTP client:
</p>
<pre>