feat: Add templates to create working release (#422)

* Add templates

* Move API structs to codersdk

* Back to green tests!

* It all works, but now with tea! 🧋

* It works!

* Add cancellation to provisionerd

* Tests pass!

* Add deletion of workspaces and projects

* Fix agent lock

* Add clog

* Fix linting errors

* Remove unused CLI tests

* Rename daemon to start

* Fix leaking command

* Fix promptui test

* Update agent connection frequency

* Skip login tests on Windows

* Increase tunnel connect timeout

* Fix templater

* Lower test requirements

* Fix embed

* Disable promptui tests for Windows

* Fix write newline

* Fix PTY write newline

* Fix CloseReader

* Fix compilation on Windows

* Fix linting error

* Remove bubbletea

* Cleanup readwriter

* Use embedded templates instead of serving over API

* Move templates to examples

* Improve workspace create flow

* Fix Windows build

* Fix tests

* Fix linting errors

* Fix untar with extracting max size

* Fix newline char
This commit is contained in:
Kyle Carberry
2022-03-22 13:17:50 -06:00
committed by GitHub
parent 2818b3ce6d
commit c451f4e685
138 changed files with 7314 additions and 2331 deletions
+72 -71
View File
@@ -8,6 +8,7 @@ import (
"fmt"
"io"
"net"
"os"
"os/exec"
"os/user"
"sync"
@@ -85,6 +86,76 @@ type server struct {
sshServer *ssh.Server
}
func (s *server) run(ctx context.Context) {
var peerListener *peerbroker.Listener
var err error
// An exponential back-off occurs when the connection is failing to dial.
// This is to prevent server spam in case of a coderd outage.
for retrier := retry.New(50*time.Millisecond, 10*time.Second); retrier.Wait(ctx); {
peerListener, err = s.clientDialer(ctx, s.options)
if err != nil {
if errors.Is(err, context.Canceled) {
return
}
if s.isClosed() {
return
}
s.options.Logger.Warn(context.Background(), "failed to dial", slog.Error(err))
continue
}
s.options.Logger.Debug(context.Background(), "connected")
break
}
select {
case <-ctx.Done():
return
default:
}
for {
conn, err := peerListener.Accept()
if err != nil {
if s.isClosed() {
return
}
s.options.Logger.Debug(ctx, "peer listener accept exited; restarting connection", slog.Error(err))
s.run(ctx)
return
}
s.closeMutex.Lock()
s.connCloseWait.Add(1)
s.closeMutex.Unlock()
go s.handlePeerConn(ctx, conn)
}
}
func (s *server) handlePeerConn(ctx context.Context, conn *peer.Conn) {
go func() {
<-conn.Closed()
s.connCloseWait.Done()
}()
for {
channel, err := conn.Accept(ctx)
if err != nil {
if errors.Is(err, peer.ErrClosed) || s.isClosed() {
return
}
s.options.Logger.Debug(ctx, "accept channel from peer connection", slog.Error(err))
return
}
switch channel.Protocol() {
case "ssh":
s.sshServer.HandleConn(channel.NetConn())
default:
s.options.Logger.Warn(ctx, "unhandled protocol from channel",
slog.F("protocol", channel.Protocol()),
slog.F("label", channel.Label()),
)
}
}
}
func (s *server) init(ctx context.Context) {
// Clients' should ignore the host key when connecting.
// The agent needs to authenticate with coderd to SSH,
@@ -197,7 +268,7 @@ func (*server) handleSSHSession(session ssh.Session) error {
}()
cmd := exec.CommandContext(session.Context(), command, args...)
cmd.Env = session.Environ()
cmd.Env = append(os.Environ(), session.Environ()...)
sshPty, windowSize, isPty := session.Pty()
if isPty {
@@ -244,76 +315,6 @@ func (*server) handleSSHSession(session ssh.Session) error {
return nil
}
func (s *server) run(ctx context.Context) {
var peerListener *peerbroker.Listener
var err error
// An exponential back-off occurs when the connection is failing to dial.
// This is to prevent server spam in case of a coderd outage.
for retrier := retry.New(50*time.Millisecond, 10*time.Second); retrier.Wait(ctx); {
peerListener, err = s.clientDialer(ctx, s.options)
if err != nil {
if errors.Is(err, context.Canceled) {
return
}
if s.isClosed() {
return
}
s.options.Logger.Warn(context.Background(), "failed to dial", slog.Error(err))
continue
}
s.options.Logger.Debug(context.Background(), "connected")
break
}
select {
case <-ctx.Done():
return
default:
}
for {
conn, err := peerListener.Accept()
if err != nil {
if s.isClosed() {
return
}
s.options.Logger.Debug(ctx, "peer listener accept exited; restarting connection", slog.Error(err))
s.run(ctx)
return
}
s.closeMutex.Lock()
s.connCloseWait.Add(1)
s.closeMutex.Unlock()
go s.handlePeerConn(ctx, conn)
}
}
func (s *server) handlePeerConn(ctx context.Context, conn *peer.Conn) {
go func() {
<-conn.Closed()
s.connCloseWait.Done()
}()
for {
channel, err := conn.Accept(ctx)
if err != nil {
if errors.Is(err, peer.ErrClosed) || s.isClosed() {
return
}
s.options.Logger.Debug(ctx, "accept channel from peer connection", slog.Error(err))
return
}
switch channel.Protocol() {
case "ssh":
s.sshServer.HandleConn(channel.NetConn())
default:
s.options.Logger.Warn(ctx, "unhandled protocol from channel",
slog.F("protocol", channel.Protocol()),
slog.F("label", channel.Label()),
)
}
}
}
// isClosed returns whether the API is closed or not.
func (s *server) isClosed() bool {
select {
+6
View File
@@ -1,6 +1,12 @@
package usershell
import "os/exec"
// Get returns the command prompt binary name.
func Get(username string) (string, error) {
_, err := exec.LookPath("powershell.exe")
if err == nil {
return "powershell.exe", nil
}
return "cmd.exe", nil
}