I am trying to make SSH connections using golang.org/x/crypto/ssh and I am kinda surprised that I can't seem to find out how to timeout the NewSession function (I actually don't seen any way to timeout anything). When I try to connect to a server that is having issues, this just hangs for a very long time. I have written something to use select with a time.After but it just feels like a hack. Something I haven't tried yet is to keep the underlying net.Conn in my struct and just keep doing Conn.SetDeadline() calls. Haven't tried this yet because I don't know if the crypto/ssh library overrides this or anything like that.
Anyone have a good way to timeout dead servers with this library? Or does anyone know of a better library?
One way to handle this transparently with the ssh package, is to create a connection with an idle timeout via a custom net.Conn which sets deadlines for you. However, this will cause the background Reads on a connection to timeout, so we need to use ssh keepalives to keep the connection open. Depending on your use case, simply using ssh keepalives as an alert for a dead connection may suffice.
// Conn wraps a net.Conn, and sets a deadline for every read
// and write operation.
type Conn struct {
net.Conn
ReadTimeout time.Duration
WriteTimeout time.Duration
}
func (c *Conn) Read(b []byte) (int, error) {
err := c.Conn.SetReadDeadline(time.Now().Add(c.ReadTimeout))
if err != nil {
return 0, err
}
return c.Conn.Read(b)
}
func (c *Conn) Write(b []byte) (int, error) {
err := c.Conn.SetWriteDeadline(time.Now().Add(c.WriteTimeout))
if err != nil {
return 0, err
}
return c.Conn.Write(b)
}
You can then use net.DialTimeout or a net.Dialer to get the connection, wrap it in your Conn with timeouts, and pass it into ssh.NewClientConn.
func SSHDialTimeout(network, addr string, config *ssh.ClientConfig, timeout time.Duration) (*ssh.Client, error) {
conn, err := net.DialTimeout(network, addr, timeout)
if err != nil {
return nil, err
}
timeoutConn := &Conn{conn, timeout, timeout}
c, chans, reqs, err := ssh.NewClientConn(timeoutConn, addr, config)
if err != nil {
return nil, err
}
client := ssh.NewClient(c, chans, reqs)
// this sends keepalive packets every 2 seconds
// there's no useful response from these, so we can just abort if there's an error
go func() {
t := time.NewTicker(2 * time.Second)
defer t.Stop()
for range t.C {
_, _, err := client.Conn.SendRequest("keepalive#golang.org", true, nil)
if err != nil {
return
}
}
}()
return client, nil
}
Set the timeout on the ssh.ClientConfig.
cfg := ssh.ClientConfig{
User: "root",
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
},
HostKeyCallback: ssh.FixedHostKey(hostKey),
Timeout: 15 * time.Second, // max time to establish connection
}
ssh.Dial("tcp", ip+":22", &cfg)
When you call ssh.Dial, the timeout will be passed to net.DialTimeout.
Related
I am trying to implement a chat client with a function Listen() which is forever listening to incoming messages. In my exposed function I convert a channel by iterating over everything it sends and forwarding it with a callback by the receiver (eg Kotlin).
func (c *Chat) Listen(receiver MessageReceiver) error {
return c.auth.WithToken(func(token string) error {
ch, err := c.repo.Listen(token)
if err != nil {
return err
}
for msg := range ch {
receiver.Add(&Message{
From: msg.From,
Content: msg.Content,
})
}
return nil
})
}
When I call this in Kotlin I would like to transform it to a flow - but I have some problems with the load on the main thread - and is constantly getting messages about missing frames on the main thread.
fun listen() = channelFlow {
chat.listen {
trySendBlocking(Message(it.content, it.from, MessageType.IN))
}
}
Does anyone know how to deal with this type of usecase of gomobile (bind) ?
I have found a lot of golang tutorials on how to handle transactions, clean architecture, but none has satisfied my needs.
I have a go web application with a series of rest API each of one served by a three layers structure:
API
Service
Dao
DB
This is the main :
func main() {
DB = db.New()
dao = dao.New(DB)
service := service.New(dao)
server := &http.Server(....)
server.ListenAndServe()
}
Now, what I would realize is a transaction manager in the service layer.
Something like this:
type Service interface {
TransactionalExecute(fn func() error)
}
type ServiceImpl struct {
dao DaoImpl
}
func (service *ServiceImpl) TransactionalExecute(fn func() error) {
service.dao.TxBegin()
err := fn()
service.dao.TxEnd(err)
}
Dao should be like this:
type Dao interface {
TxBegin()
TxEnd(err error)
}
type DaoImpl struct {
DB db
tx *sql.Tx
}
func (dao *DaoImpl) TxBegin() {
dao.tx = dao.db.Begin()
}
func (dao *DaoImpl) TxEnd(err error) {
if p:= recover(); p != nil {
dao.tx.Rollback()
panic(p)
} else if err != nil {
dao.tx.Rollback()
} else {
dao.tx.Commit()
}
}
This kind of POC has two problems :
fn func() error parameter passed in the transactionalExecute() method must use the dao.Tx variable of the dao instance
This approach is not thread-safe : I am currently using gorilla mux and each http request will start a goroutine using a single instance of service, dao and DB. These instances are shared between multiple threads and is not safe. Anyway I was thinking about the use of mutex to access to the dao.tx variable but I am concerned about performances.
Any suggestion? Or different approaches to the problem?
Transactions should not really be part of DAO layer, it's a completely separate concern. A transaction is simply a single connection that executes all statements atomically. DAO, on the other hand, is a collection of database functions which use connections/transactions.
You can implement transactions and DAO nicely as two separate interfaces. Transaction can be an interface that is implemented both by sql.DB (connection pool from the standard library) and sql.Tx (transaction).
// Transaction represents both database connection pool and a single transaction/connection
type Transaction interface {
// ExecContext from database/sql library
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
// PrepareContext from database/sql library
PrepareContext(ctx context.Context, query string) (*sql.Stmt, error)
// QueryContext from database/sql library
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
// QueryRowContext from database/sql library
QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row
// QueryxContext from sqlx library
QueryxContext(ctx context.Context, query string, args ...interface{}) (*sqlx.Rows, error)
// QueryRowxContext from sqlx library
QueryRowxContext(ctx context.Context, query string, args ...interface{}) *sqlx.Row
// PreparexContext from sqlx library
PreparexContext(ctx context.Context, query string) (*sqlx.Stmt, error)
// GetContext from sqlx library
GetContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error
// SelectContext from sqlx library
SelectContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error
}
We use both database/sql and sqlx extended library for easier data scanning, you can just drop sqlx functions if you do not need them. Then a helper function for easier work:
// A TxFn is a function that will be called with an initialized `Transaction` object
// that can be used for executing statements and queries against a database.
type TxFn func(Transaction) error
// WithTransaction creates a new transaction and handles rollback/commit based on the
// error object returned by the `TxFn` or when it panics.
func WithTransaction(ctx context.Context, db *sqlx.DB, fn TxFn) error {
logger := ctxval.GetLogger(ctx)
tx, err := db.BeginTxx(ctx, &sql.TxOptions{
Isolation: sql.LevelDefault,
ReadOnly: false,
})
if err != nil {
logger.Error().Err(err).Msg("Cannot begin database transaction")
return fmt.Errorf("cannot begin transaction: %w", err)
}
defer func() {
if p := recover(); p != nil {
logger.Trace().Msg("Rolling database transaction back")
err := tx.Rollback()
if err != nil {
logger.Error().Err(err).Msg("Cannot rollback database transaction")
return
}
panic(p)
} else if err != nil {
logger.Trace().Msg("Rolling database transaction back")
err := tx.Rollback()
if err != nil {
logger.Error().Err(err).Msg("Cannot rollback database transaction")
return
}
} else {
logger.Trace().Msg("Committing database transaction")
err = tx.Commit()
if err != nil {
logger.Error().Err(err).Msg("Cannot rollback database transaction")
return
}
}
}()
logger.Trace().Msg("Starting database transaction")
err = fn(tx)
return err
}
Then you need a DAO interface, we have an initializer as a function that is then initialized by individual implementations, it takes context and transaction and returns DAO implementation:
var GetAccountDao func(ctx context.Context, tx Transaction) (AccountDao, error)
type AccountDao interface {
GetById(ctx context.Context, id uint64) (*models.Account, error)
GetByAccountNumber(ctx context.Context, number string) (*models.Account, error)
GetByOrgId(ctx context.Context, orgId string) (*models.Account, error)
List(ctx context.Context, limit, offset uint64) ([]*models.Account, error)
}
Implementation looks something like this:
const (
getAccountById = `SELECT * FROM accounts WHERE id = $1 LIMIT 1`
)
type accountDaoSqlx struct {
name string
getById *sqlx.Stmt
}
func getAccountDao(ctx context.Context, tx dao.Transaction) (dao.AccountDao, error) {
var err error
daoImpl := accountDaoSqlx{}
daoImpl.name = "account"
daoImpl.getById, err = tx.PreparexContext(ctx, getAccountById)
if err != nil {
return nil, NewPrepareStatementError(ctx, &daoImpl, getAccountById, err)
}
return &daoImpl, nil
}
func init() {
dao.GetAccountDao = getAccountDao
}
func (dao *accountDaoSqlx) GetById(ctx context.Context, id uint64) (*models.Account, error) {
query := getAccountById
stmt := dao.getById
result := &models.Account{}
err := stmt.GetContext(ctx, result, id)
if err != nil {
return nil, NewGetError(ctx, dao, query, err)
}
return result, nil
}
// etc
And example use in a "service" package (without and with transaction):
// import sqlx implementation which calls init()
_ "internal/dao/sqlx"
"internal/dao"
// etc
func CreatePubkey(w http.ResponseWriter, r *http.Request) {
payload := &payloads.PubkeyRequest{}
if err := render.Bind(r, payload); err != nil {
renderError(w, r, payloads.NewInvalidRequestError(r.Context(), err))
return
}
// example with transaction
err := dao.WithTransaction(r.Context(), db.DB, func(tx dao.Transaction) error {
pkDao, err := dao.GetPubkeyDao(r.Context(), tx)
if err != nil {
return payloads.NewInitializeDAOError(r.Context(), "pubkey DAO", err)
}
err = pkDao.Create(r.Context(), payload.Pubkey)
if err != nil {
return payloads.NewDAOError(r.Context(), "create pubkey", err)
}
// ... cut ...
return nil
})
// ignore errors etc
}
func ListPubkeys(w http.ResponseWriter, r *http.Request) {
// example without transaction
pubkeyDao, err := dao.GetPubkeyDao(r.Context(), db.DB)
if err != nil {
renderError(w, r, payloads.NewInitializeDAOError(r.Context(), "pubkey DAO", err))
return
}
pubkeys, err := pubkeyDao.List(r.Context(), 100, 0)
if err != nil {
renderError(w, r, payloads.NewDAOError(r.Context(), "list pubkeys", err))
return
}
if err := render.RenderList(w, r, payloads.NewPubkeyListResponse(pubkeys)); err != nil {
renderError(w, r, payloads.NewRenderError(r.Context(), "list pubkeys", err))
return
}
}
This is incomplete code I did cut details. Note I have both these interfaces in our dao package, these might be either separate or the same package.
One important thing to know: you must not share DAO implementation just like that (as in your snippet). The standard library sql.DB interface is actually a connection pool and it is thread safe (as documented), you can call as many Query/Exec/Etc function as you want, the driver will use one or more connection as needed.
Therefore the correct approach is to create new DAO type everytime a new request comes in, the connection pool will automatically acquire existing or new connection. More about this in the Go documentation.
you can modify the package dao to look like the following, which will make it thread-safe as func NewDao will be called in handler. you make this work you need to pass db *sql.DB from package to handler.
type Dao interface {
TxEnd(err error)
}
type DaoImpl struct {
tx *sql.Tx
db *sql.DB
txStared bool
}
func NewDao(db *sql.DB, txStared bool) Dao {
if txStared {
tx, _ := db.Begin()
return &DaoImpl{tx: tx, db: db, txStared: txStared}
}
return &DaoImpl{tx: nil, db: db, txStared: txStared}
}
func (dao *DaoImpl) TxEnd(err error) {
if !dao.txStared {
return
}
if p:= recover(); p != nil {
dao.tx.Rollback()
panic(p)
} else if err != nil {
dao.tx.Rollback()
} else {
dao.tx.Commit()
}
}
}
I recently started to play again with go, and my task at the moment is to implement a service (that will run on k8s) that should act as a gRPC server.
As per requirements, at the moment, to satisfy the readinessProbe on k8s, I need to implement an healthcheck endpoint for my service, and this should be tested: what I've done is:
func main() {
server := startHTTPServer()
defer server.Close()
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
<-c
log.Println("Stopped")
}
func startHTTPServer() *http.Server {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
server := &http.Server{
Addr: net.JoinHostPort("", strconv.Itoa(livelinessPort)),
}
go server.ListenAndServe()
return server
}
and I tested in this way, following this article:
func TestMain(m *testing.M) {
flag.Parse()
wg := setup()
result := m.Run()
shutdown(wg)
os.Exit(result)
}
func shutdown(wg *sync.WaitGroup) {
syscall.Kill(syscall.Getpid(), syscall.SIGINT)
wg.Wait()
}
func setup() *sync.WaitGroup{
os.Setenv("APP_ENV", EnvTest)
wg := &sync.WaitGroup{}
startMainProcess(wg)
return wg
}
func startMainProcess(wg *sync.WaitGroup) {
go func() {
wg.Add(1)
defer wg.Done()
main()
}()
}
func TestK8SHealth(t *testing.T) {
res, err := http.Get("http://:8080/")
if err != nil {
t.Errorf("Unexpected API error: %s", err)
return
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
t.Errorf("Unexpected status code: %d", res.StatusCode)
return
}
}
and so far so good:
$ go test
PASS
2018/08/13 09:23:16 Stopped
ok github.com/... 0.015s
Te problem is when I try to add the gRPC server in the main application, following gPRC go examples. I've added this to my main file
func main() {
[...]
startChecksServiceServer()
[...]
}
func startChecksServiceServer() *grpc.Server {
flag.Parse()
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", serverPort))
if err != nil {
log.Fatalf("Starting gPRC server error: failed to listen port %d: %v", serverPort, err)
}
grpcServer := grpc.NewServer()
pb.RegisterChecksServiceServer(grpcServer, &checksServiceServer{})
grpcServer.Serve(lis)
log.Println("Started grpc server")
return grpcServer
}
but now when I run the test, it fails after the actual test was run, when the interrupt signal is sent.
$ go test
PASS
signal: interrupt
FAIL github.com/... 0.016s
I made some tests and this is not depending by the actual test (as it pass with a test function that just return, and it's working while running the test suite using goLand:
GOROOT=/usr/local/go #gosetup
GOPATH=/Users/marco/Documents/projects/go #gosetup
/usr/local/go/bin/go test -c -i -o /private/var/folders/jh/znhv9f090yb7y390dckb23s00000gn/T/[...] #gosetup
/usr/local/go/bin/go tool test2json -t /private/var/folders/jh/znhv9f090yb7y390dckb23s00000gn/T/[...] -test.v -test.run
^TestK8SHealth$ #gosetup
=== RUN TestK8SHealth
--- PASS: TestK8SHealth (0.00s)
PASS
Process finished with exit code 1
--- EDIT
I found that the problem is around the listener. Having in the main function:
func main() {
net.Listen("tcp", fmt.Sprintf(":%d", serverPort))
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
<-c
log.Println("Stopped")
}
and a test with just a return (with the same setup of the previous one) will cause the test to fail:
$ go test -v
=== RUN Test2
--- PASS: Test2 (0.00s)
PASS
signal: interrupt
FAIL github.com/[...] 0.014s
grpcServer.Serve(lis) is a blocking call. So like what you did with HTTP server go server.ListenAndServe(), you need to run it in a goroutine. Could you please try if go grpcServer.Serve(lis) will make the test pass?
I am not sure about your new EDIT which indicates the problem is abut the listener. You can check whether net.Listen("tcp", fmt.Sprintf(":%d", serverPort)) returns or blocks.
I used to be able to connect to port 143 of a mail server like this:
c, err := imap.Dial(mailServer)
The code above connects to port 143 of the mailServer. Now I have a new mail server only accepts port 993. Looking at the Golang imap source code, the function DialTLS will connect to port 993. The signature of DialTLS looks like this:
func DialTLS(addr string, config *tls.Config) (c *Client, err error)
Now I don't know how to construct the *tls.Config. I Googled around, but didn't not find anything really useful. Can someone show me some example how to construct the *tls.Config?
I tried to pass in nil as the second parameter, it compiles, and I didn't get any runtime error. But it seems no new mails were fetched, even I believe there should be.
My fetch mail code looks like this:
// testimap
package main
import (
"bytes"
"code.google.com/p/go-imap/go1/imap"
"fmt"
"net/mail"
"time"
)
type Mail struct {
Subject string
Body string
From string
Uid uint32
}
func FetchMail(lastUid uint32) []*Mail {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
//
// Note: most of error handling code is omitted for brevity
//
var (
c *imap.Client
cmd *imap.Command
rsp *imap.Response
)
// Connect to the server
c, err := imap.DialTLS(mailServer, nil)
if err != nil {
fmt.Println(err)
}
// Remember to log out and close the connection when finished
defer c.Logout(30 * time.Second)
// Print server greeting (first response in the unilateral server data queue)
//fmt.Println("Server says hello:", c.Data[0].Info)
c.Data = nil
// Enable encryption, if supported by the server
if c.Caps["STARTTLS"] {
c.StartTLS(nil)
}
// Authenticate
if c.State() == imap.Login {
c.Login(mailSupportUser, mailSupportPw)
}
//// List all top-level mailboxes, wait for the command to finish
cmd, err = imap.Wait(c.List("", "%"))
if err != nil {
fmt.Println(err)
}
// Print mailbox information
//fmt.Println("\nTop-level mailboxes:")
//for _, rsp = range cmd.Data {
// fmt.Println("|--", rsp.MailboxInfo())
//}
// Check for new unilateral server data responses
//for _, rsp = range c.Data {
// fmt.Println("Server data:", rsp)
//}
c.Data = nil
// Open a mailbox (synchronous command - no need for imap.Wait)
c.Select("INBOX", true)
//fmt.Print("\nMailbox status:\n", c.Mailbox)
// Fetch the headers of the 10 most recent messages
set, err := imap.NewSeqSet(fmt.Sprint(lastUid, ":*"))
if err != nil {
fmt.Println(err)
}
//if c.Mailbox.Messages >= 10 {
// set.AddRange(c.Mailbox.Messages-9, c.Mailbox.Messages)
//} else {
// set.Add("1:*")
//}
cmd, err = c.UIDFetch(set, "RFC822.HEADER", "RFC822.TEXT")
if err != nil {
fmt.Println(err)
}
// Process responses while the command is running
//fmt.Println("\nMost recent messages:")
mails := make([]*Mail, 0, 10)
for cmd.InProgress() {
// Wait for the next response (no timeout)
c.Recv(-1)
// Process command data
for _, rsp = range cmd.Data {
if err != nil {
fmt.Println(err)
}
header := imap.AsBytes(rsp.MessageInfo().Attrs["RFC822.HEADER"])
uid := imap.AsNumber((rsp.MessageInfo().Attrs["UID"]))
body := imap.AsBytes(rsp.MessageInfo().Attrs["RFC822.TEXT"])
if msg, err := mail.ReadMessage(bytes.NewReader(header)); msg != nil {
if err != nil {
fmt.Println(err)
}
mail := &Mail{
Subject: msg.Header.Get("Subject"),
From: msg.Header.Get("FROM"),
Body: string(body),
Uid: uid,
}
if mail.Uid < lastUid {
continue
}
mails = append(mails, mail)
}
}
cmd.Data = nil
c.Data = nil
}
// Check command completion status
if rsp, err := cmd.Result(imap.OK); err != nil {
if err == imap.ErrAborted {
fmt.Println("Fetch command aborted")
} else {
fmt.Println("Fetch error:", rsp.Info)
}
}
fmt.Println(mails)
return mails
}
First off, you should be using the project's GitHub repo as the Google Code project stated development was moving to GitHub due to Google Code shutting down. It's a few commits ahead of the Google Code repo too, so you won't be getting any updates if you don't migrate to the GitHub repo.
Secondly, looking at the package's demo, passing nil to DialTLS as the TLS client seems to be the proper way to connect with a default TLS client.
From the information you've given, it seems like it may be an issue with your server accepting connections over that port. I would look into if the port is open to the client you're trying to connect from or if your IMAP server is even accepting TLS connections.
If you're absolutely sure it's not a server issue after further debugging, I would file an issue on the project's GitHub issue tracker to get help from people who are more familiar with the package, seeing that it's a third party package.
Is there an idiomatic Go way for abstracting resource allocation/deallocation? My initial guess is to abstract the allocation/deallocation in a higher-order function:
func withResource(f func(Resource)error) error {
// allocate resource
// defer free resource
return f(resource)
}
However, this line of thinking is borrowed directly from the functional paradigm and doesn't seem to align well with Go's largely imperative nature.
As a concrete example, running a daemon for the duration of a block of code is a recurring theme in my current project, so I've created a withDaemon function to abstract the commonality:
func withDaemon(
cmd *exec.Cmd,
f func(io.ReadCloser, io.ReadCloser, io.WriteCloser) error,
) error {
stdout, err := cmd.StdoutPipe()
if err != nil {
return fmt.Errorf("couldn't get stdout: %v", err)
}
stderr, err := cmd.StdoutPipe()
if err != nil {
return fmt.Errorf("couldn't get stderr: %v", err)
}
stdin, err := cmd.StdinPipe()
if err != nil {
return fmt.Errorf("couldn't get stdin: %v", err)
}
if err := cmd.Start(); err != nil {
return fmt.Errorf("failed to start: %v", err)
}
defer func() {
cmd.Process.Kill()
cmd.Wait()
}
return f(stdout, stderr, stdin)
}
I think that the idiomatic way would be to create a Daemon type, and to just use defer in the caller:
d := NewDaemon(...)
defer d.Stop()
doWhatever()