cross compilation on windows working.
pty.Start() is not supported on windows
This commit is contained in:
parent
982977e796
commit
1ebee30c8c
@ -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" ]
|
||||
|
4
Makefile
4
Makefile
@ -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
|
||||
|
||||
|
@ -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
12
cmd/agent/agent_linux.go
Executable 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
7
cmd/agent/agent_windows.go
Executable file
@ -0,0 +1,7 @@
|
||||
package main
|
||||
|
||||
import "os"
|
||||
|
||||
func setWinsize(f *os.File, w, h int) {
|
||||
// Empty
|
||||
}
|
98
cmd/agent/open_process_windows.go
Normal file
98
cmd/agent/open_process_windows.go
Normal 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)
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
26
cmd/tcpclient/sshclient_linux.go
Normal file
26
cmd/tcpclient/sshclient_linux.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
7
cmd/tcpclient/sshclient_windows.go
Normal file
7
cmd/tcpclient/sshclient_windows.go
Normal file
@ -0,0 +1,7 @@
|
||||
package main
|
||||
|
||||
import "golang.org/x/crypto/ssh"
|
||||
|
||||
func handleWindowChange(session *ssh.Session, fd int) {
|
||||
// Empty
|
||||
}
|
@ -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")
|
||||
|
12
cmd/tcpserver/sshserver_linux.go
Normal file
12
cmd/tcpserver/sshserver_linux.go
Normal 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/tcpserver/sshserver_windows.go
Normal file
7
cmd/tcpserver/sshserver_windows.go
Normal file
@ -0,0 +1,7 @@
|
||||
package main
|
||||
|
||||
import "os"
|
||||
|
||||
func setWinsize(f *os.File, w, h int) {
|
||||
// Empty
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
services:
|
||||
|
||||
converge:
|
||||
image: your.repo.com/converge:1.0
|
||||
image: $REGISTRY/converge:1.0
|
||||
build:
|
||||
context: .
|
||||
ports:
|
||||
|
2
go.mod
2
go.mod
@ -1,6 +1,6 @@
|
||||
module converge
|
||||
|
||||
go 1.18
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/creack/pty v1.1.21
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user