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 RUN go mod download
COPY . /opt/converge/ COPY . /opt/converge/
RUN go build -ldflags "-linkmode 'external' -extldflags '-static'" -o bin ./cmd/... RUN go build -ldflags "-linkmode 'external' -extldflags '-static'" -o bin ./cmd/...
RUN GOOS=windows GOARCH=amd64 go build -o bin ./cmd/...
FROM scratch FROM scratch
COPY --from=builder /opt/converge/bin/converge /opt/converge/bin/ 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/ COPY --from=builder /opt/converge/static/ /opt/converge/docs/
ENTRYPOINT ["/opt/converge/bin/converge", "/opt/converge/docs" ] ENTRYPOINT ["/opt/converge/bin/converge", "/opt/converge/docs" ]

View File

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

View File

@ -6,7 +6,11 @@ import (
"converge/pkg/iowrappers" "converge/pkg/iowrappers"
"converge/pkg/websocketutil" "converge/pkg/websocketutil"
"fmt" "fmt"
"github.com/creack/pty"
"github.com/gliderlabs/ssh"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/hashicorp/yamux"
"github.com/pkg/sftp"
"io" "io"
"log" "log"
"net" "net"
@ -14,14 +18,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"strings" "strings"
"syscall"
"time" "time"
"unsafe"
"github.com/creack/pty"
"github.com/gliderlabs/ssh"
"github.com/hashicorp/yamux"
"github.com/pkg/sftp"
_ "embed" _ "embed"
) )
@ -55,11 +52,6 @@ func passwordAuth(ctx ssh.Context, password string) bool {
return ctx.User() == "abc" && password == "123" 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 { func sshServer(hostKeyFile string, shellCommand string) *ssh.Server {
ssh.Handle(func(s ssh.Session) { 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 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 ( import (
"fmt" "fmt"
"github.com/creack/pty"
"github.com/gliderlabs/ssh"
"github.com/pkg/sftp"
"io" "io"
"log" "log"
"os" "os"
"os/exec" "os/exec"
"syscall"
"unsafe"
"github.com/creack/pty"
"github.com/gliderlabs/ssh"
"github.com/pkg/sftp"
) )
func SftpHandler(sess ssh.Session) { func SftpHandler(sess ssh.Session) {
@ -40,11 +37,6 @@ func passwordAuth(ctx ssh.Context, password string) bool {
return ctx.User() == "abc" && password == "123" 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() { func main() {
ssh.Handle(func(s ssh.Session) { ssh.Handle(func(s ssh.Session) {
cmd := exec.Command("bash") 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: services:
converge: converge:
image: your.repo.com/converge:1.0 image: $REGISTRY/converge:1.0
build: build:
context: . context: .
ports: ports:

2
go.mod
View File

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

View File

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

View File

@ -17,7 +17,7 @@
<h1>About</h1> <h1>About</h1>
<p> <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. 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 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 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 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. 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. 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. the client and server together.
</p> </p>
@ -44,12 +44,12 @@
<p> <p>
There is a timeout mechanism in the agent such that jobs do not hang indefinitely waiting 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 for a connection. This mechanism is useful to make sure build agents do not keep
indefinitely for a user session. By default, the agent exits with status 0 when 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 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 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. 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. in the shell.
</p> </p>
@ -69,8 +69,8 @@
Above, ID is a unique id Above, ID is a unique id
for the job. This should not conflict with other ids. for the job. This should not conflict with other ids.
This connects the agent to the converge server. Clients can now connect to converge This connects the agent to the converge server. Clients can now connect to the Converge
to establish a connection to the CI job through converge. server to establish a connection to the CI job through converge.
</p> </p>
<h2>Local clients: using ssh proxy command </h2> <h2>Local clients: using ssh proxy command </h2>
@ -84,7 +84,7 @@
server.</p> server.</p>
<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> </p>
<pre> <pre>