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
|
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" ]
|
||||||
|
4
Makefile
4
Makefile
@ -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
|
||||||
|
|
||||||
|
@ -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
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
|
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 (
|
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")
|
||||||
|
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:
|
services:
|
||||||
|
|
||||||
converge:
|
converge:
|
||||||
image: your.repo.com/converge:1.0
|
image: $REGISTRY/converge:1.0
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
ports:
|
ports:
|
||||||
|
2
go.mod
2
go.mod
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user