diff --git a/Gopkg.lock b/Gopkg.lock index 9ffab2c..072b256 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -83,7 +83,7 @@ branch = "master" name = "github.com/problame/go-netssh" packages = ["."] - revision = "ffa145d2506e222977205e7666a9722d6b9959ac" + revision = "984ce91f575c75c4d5d6c7e74660d2c837571496" [[projects]] branch = "master" diff --git a/cmd/config_connect.go b/cmd/config_connect.go index 60bbc8d..b6d8435 100644 --- a/cmd/config_connect.go +++ b/cmd/config_connect.go @@ -9,6 +9,7 @@ import ( "github.com/mitchellh/mapstructure" "github.com/pkg/errors" "github.com/problame/go-netssh" + "time" ) type SSHStdinserverConnecter struct { @@ -19,6 +20,8 @@ type SSHStdinserverConnecter struct { TransportOpenCommand []string `mapstructure:"transport_open_command"` SSHCommand string `mapstructure:"ssh_command"` Options []string + DialTimeout string `mapstructure:"dial_timeout"` + dialTimeout time.Duration } func parseSSHStdinserverConnecter(i map[string]interface{}) (c *SSHStdinserverConnecter, err error) { @@ -29,6 +32,15 @@ func parseSSHStdinserverConnecter(i map[string]interface{}) (c *SSHStdinserverCo return nil, err } + if c.DialTimeout != "" { + c.dialTimeout, err = time.ParseDuration(c.DialTimeout) + if err != nil { + return nil, errors.Wrap(err, "cannot parse dial_timeout") + } + } else { + c.dialTimeout = 10 * time.Second + } + // TODO assert fields are filled return @@ -38,9 +50,15 @@ func (c *SSHStdinserverConnecter) Connect() (rwc io.ReadWriteCloser, err error) var endpoint netssh.Endpoint if err = copier.Copy(&endpoint, c); err != nil { - return + return nil, errors.WithStack(err) } - if rwc, err = netssh.Dial(context.TODO(), endpoint); err != nil { + var dialCtx context.Context + dialCtx, dialCancel := context.WithTimeout(context.TODO(), c.dialTimeout) // context.TODO tied to error handling below + defer dialCancel() + if rwc, err = netssh.Dial(dialCtx, endpoint); err != nil { + if err == context.DeadlineExceeded { + err = errors.Errorf("dial_timeout of %s exceeded", c.dialTimeout) + } err = errors.WithStack(err) return } diff --git a/docs/changelog.rst b/docs/changelog.rst index 6704a96..d93dc55 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -19,7 +19,13 @@ Developers should consult the git commit log or GitHub issue tracker. * Make sure to understand the meaning bookmarks have for :ref:`maximum replication downtime `. * Example: :sampleconf:`pullbackup/productionhost.yml` -* |break| :commit:`ccd062e`: both sides of a replication setup must be updated and restarted. Otherwise the connecting side will hang and not time out. +* |break| :commit:`ccd062e`: ``ssh+stdinserver`` transport: changed protocol requires daemon restart on both sides + + * The delicate procedure of talking to the serving-side zrepl daemon via the stdinserver proxy command now has better error handling. + * This includes handshakes between client+proxy and client + remote daemo, which is not implemented in previous versions of zrepl. + * The connecting side will therefore time out, with the message ``dial_timeout of 10s exceeded``. + * Both sides of a replication setup must be updated and restarted. Otherwise the connecting side will hang and not time out. + * |break_config| :commit:`2bfcfa5`: first outlet in ``global.logging`` is now used for logging meta-errors, for example problems encountered when writing to other outlets. * |feature| :issue:`10`: ``zrepl control status`` subcommand diff --git a/docs/configuration/transports.rst b/docs/configuration/transports.rst index 94ae799..18a30fb 100644 --- a/docs/configuration/transports.rst +++ b/docs/configuration/transports.rst @@ -85,8 +85,9 @@ Connect Mode user: root port: 22 identity_file: /etc/zrepl/ssh/identity - options: # optional + options: # optional, default [], `-o` arguments passed to ssh - "Compression=on" + dial_timeout: # optional, default 10s, max time.Duration until initial handshake is completed The connecting zrepl daemon