Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 30057d4e59 | |||
| 75fd21e454 | |||
| 0d2f73d728 | |||
| 9b803aad2d | |||
| fb74addc1e | |||
| 7f89372cfa | |||
| 26b436463d |
Generated
+1
-1
@@ -83,7 +83,7 @@
|
|||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/problame/go-netssh"
|
name = "github.com/problame/go-netssh"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "ffa145d2506e222977205e7666a9722d6b9959ac"
|
revision = "53a2e445f8ace7ec678f2d8cdd9c1428dfef4562"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ RUN /tmp/lazy.sh devsetup
|
|||||||
# prepare volume mount of git checkout to /zrepl
|
# prepare volume mount of git checkout to /zrepl
|
||||||
RUN mkdir -p /go/src/github.com/zrepl/zrepl
|
RUN mkdir -p /go/src/github.com/zrepl/zrepl
|
||||||
RUN chmod -R 0777 /go
|
RUN chmod -R 0777 /go
|
||||||
|
RUN mkdir -p /.cache && chmod -R 0777 /.cache
|
||||||
WORKDIR /go/src/github.com/zrepl/zrepl
|
WORKDIR /go/src/github.com/zrepl/zrepl
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+20
-2
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/problame/go-netssh"
|
"github.com/problame/go-netssh"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SSHStdinserverConnecter struct {
|
type SSHStdinserverConnecter struct {
|
||||||
@@ -19,6 +20,8 @@ type SSHStdinserverConnecter struct {
|
|||||||
TransportOpenCommand []string `mapstructure:"transport_open_command"`
|
TransportOpenCommand []string `mapstructure:"transport_open_command"`
|
||||||
SSHCommand string `mapstructure:"ssh_command"`
|
SSHCommand string `mapstructure:"ssh_command"`
|
||||||
Options []string
|
Options []string
|
||||||
|
DialTimeout string `mapstructure:"dial_timeout"`
|
||||||
|
dialTimeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSSHStdinserverConnecter(i map[string]interface{}) (c *SSHStdinserverConnecter, err error) {
|
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
|
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
|
// TODO assert fields are filled
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -38,9 +50,15 @@ func (c *SSHStdinserverConnecter) Connect() (rwc io.ReadWriteCloser, err error)
|
|||||||
|
|
||||||
var endpoint netssh.Endpoint
|
var endpoint netssh.Endpoint
|
||||||
if err = copier.Copy(&endpoint, c); err != nil {
|
if err = copier.Copy(&endpoint, c); err != nil {
|
||||||
return
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
if rwc, err = netssh.Dial(context.TODO(), endpoint); err != nil {
|
|
||||||
err = errors.WithStack(err)
|
err = errors.WithStack(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-1
@@ -1,6 +1,7 @@
|
|||||||
.. |break_config| replace:: **[BREAK]**
|
.. |break_config| replace:: **[BREAK]**
|
||||||
.. |break| replace:: **[BREAK]**
|
.. |break| replace:: **[BREAK]**
|
||||||
.. |bugfix| replace:: [BUG]
|
.. |bugfix| replace:: [BUG]
|
||||||
|
.. |docs| replace:: [DOCS]
|
||||||
.. |feature| replace:: [FEATURE]
|
.. |feature| replace:: [FEATURE]
|
||||||
|
|
||||||
Changelog
|
Changelog
|
||||||
@@ -19,7 +20,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 <replication-downtime>`.
|
* Make sure to understand the meaning bookmarks have for :ref:`maximum replication downtime <replication-downtime>`.
|
||||||
* Example: :sampleconf:`pullbackup/productionhost.yml`
|
* 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.
|
* |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
|
* |feature| :issue:`10`: ``zrepl control status`` subcommand
|
||||||
|
|
||||||
@@ -34,6 +41,7 @@ Developers should consult the git commit log or GitHub issue tracker.
|
|||||||
* |bugfix| :issue:`8` and :issue:`56`: ``ssh+stdinserver`` transport properly reaps SSH child processes
|
* |bugfix| :issue:`8` and :issue:`56`: ``ssh+stdinserver`` transport properly reaps SSH child processes
|
||||||
* |bugfix| :commit:`cef63ac`: ``human`` format now prints non-string values correctly
|
* |bugfix| :commit:`cef63ac`: ``human`` format now prints non-string values correctly
|
||||||
* |bugfix| :issue:`26`: slow TCP outlets no longer block the daemon
|
* |bugfix| :issue:`26`: slow TCP outlets no longer block the daemon
|
||||||
|
* |docs| :issue:`64`: tutorial: document ``known_host`` file entry
|
||||||
|
|
||||||
0.0.2
|
0.0.2
|
||||||
-----
|
-----
|
||||||
|
|||||||
@@ -85,8 +85,9 @@ Connect Mode
|
|||||||
user: root
|
user: root
|
||||||
port: 22
|
port: 22
|
||||||
identity_file: /etc/zrepl/ssh/identity
|
identity_file: /etc/zrepl/ssh/identity
|
||||||
options: # optional
|
options: # optional, default [], `-o` arguments passed to ssh
|
||||||
- "Compression=on"
|
- "Compression=on"
|
||||||
|
dial_timeout: # optional, default 10s, max time.Duration until initial handshake is completed
|
||||||
|
|
||||||
The connecting zrepl daemon
|
The connecting zrepl daemon
|
||||||
|
|
||||||
@@ -101,10 +102,13 @@ The connecting zrepl daemon
|
|||||||
#. The remote user, host and port correspond to those configured.
|
#. The remote user, host and port correspond to those configured.
|
||||||
#. Further options can be specified using the ``options`` field, which appends each entry in the list to the command line using ``-o $entry``.
|
#. Further options can be specified using the ``options`` field, which appends each entry in the list to the command line using ``-o $entry``.
|
||||||
|
|
||||||
1. Wraps the pipe ends in an ``io.ReadWriteCloser`` and uses it for RPC.
|
#. Wraps the pipe ends in an ``io.ReadWriteCloser`` and uses it for RPC.
|
||||||
|
|
||||||
|
|
||||||
As discussed in the section above, the connecting zrepl daemon expects that ``zrepl stdinserver $client_identity`` is executed automatically via an ``authorized_keys`` file entry.
|
As discussed in the section above, the connecting zrepl daemon expects that ``zrepl stdinserver $client_identity`` is executed automatically via an ``authorized_keys`` file entry.
|
||||||
|
|
||||||
|
The ``known_hosts`` file used by the ssh command must contain an entry for the serving host, e.g., ``app-srv.example.com`` in the example above.
|
||||||
|
|
||||||
.. NOTE::
|
.. NOTE::
|
||||||
|
|
||||||
The environment variables of the underlying SSH process are cleared. ``$SSH_AUTH_SOCK`` will not be available.
|
The environment variables of the underlying SSH process are cleared. ``$SSH_AUTH_SOCK`` will not be available.
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ zrepl - ZFS replication
|
|||||||
Getting started
|
Getting started
|
||||||
~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
The :ref:`5 minute tutorial setup <tutorial>` gives you a first impression.
|
The :ref:`10 minutes tutorial setup <tutorial>` gives you a first impression.
|
||||||
|
|
||||||
Main Features
|
Main Features
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|||||||
+9
-2
@@ -60,7 +60,7 @@ Follow the :ref:`OS-specific installation instructions <installation>` and come
|
|||||||
Configure ``backup-srv``
|
Configure ``backup-srv``
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
We define a **pull job** named ``pull_app-srv`` in the |mainconfig|: ::
|
We define a **pull job** named ``pull_app-srv`` in the |mainconfig| on host ``backup-srv``: ::
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- name: pull_app-srv
|
- name: pull_app-srv
|
||||||
@@ -94,6 +94,13 @@ It uses the private key specified at ``connect.identity_file`` which we still ne
|
|||||||
Note that most use cases do not benefit from separate keypairs per remote endpoint.
|
Note that most use cases do not benefit from separate keypairs per remote endpoint.
|
||||||
Thus, it is sufficient to create one keypair and use it for all ``connect`` directives on one host.
|
Thus, it is sufficient to create one keypair and use it for all ``connect`` directives on one host.
|
||||||
|
|
||||||
|
zrepl uses ssh's default ``known_hosts`` file, which must contain a host identification entry for ``app-srv.example.com``.
|
||||||
|
If that entry does not already exist, we need to generate it.
|
||||||
|
Run the following command, compare the host fingerprints, and confirm with yes if they match.
|
||||||
|
You will not be able to get a shell with the identity file we just generated, which is fine. ::
|
||||||
|
|
||||||
|
ssh -i /etc/zrepl/ssh/identity root@app-srv.example.com
|
||||||
|
|
||||||
Learn more about :ref:`transport-ssh+stdinserver` transport and the :ref:`pull job <job-pull>` format.
|
Learn more about :ref:`transport-ssh+stdinserver` transport and the :ref:`pull job <job-pull>` format.
|
||||||
|
|
||||||
.. _tutorial-configure-app-srv:
|
.. _tutorial-configure-app-srv:
|
||||||
@@ -101,7 +108,7 @@ Learn more about :ref:`transport-ssh+stdinserver` transport and the :ref:`pull j
|
|||||||
Configure ``app-srv``
|
Configure ``app-srv``
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
We define a corresponding **source job** named ``pull_backup`` in the |mainconfig|: ::
|
We define a corresponding **source job** named ``pull_backup`` in the |mainconfig| on host ``app-srv``: ::
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- name: pull_backup
|
- name: pull_backup
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
package rpc
|
package rpc
|
||||||
|
|
||||||
import "fmt"
|
import "strconv"
|
||||||
|
|
||||||
const _DataType_name = "DataTypeNoneDataTypeControlDataTypeMarshaledJSONDataTypeOctets"
|
const _DataType_name = "DataTypeNoneDataTypeControlDataTypeMarshaledJSONDataTypeOctets"
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ var _DataType_index = [...]uint8{0, 12, 27, 48, 62}
|
|||||||
func (i DataType) String() string {
|
func (i DataType) String() string {
|
||||||
i -= 1
|
i -= 1
|
||||||
if i >= DataType(len(_DataType_index)-1) {
|
if i >= DataType(len(_DataType_index)-1) {
|
||||||
return fmt.Sprintf("DataType(%d)", i+1)
|
return "DataType(" + strconv.FormatInt(int64(i+1), 10) + ")"
|
||||||
}
|
}
|
||||||
return _DataType_name[_DataType_index[i]:_DataType_index[i+1]]
|
return _DataType_name[_DataType_index[i]:_DataType_index[i+1]]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
package rpc
|
package rpc
|
||||||
|
|
||||||
import "fmt"
|
import "strconv"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
_FrameType_name_0 = "FrameTypeHeaderFrameTypeDataFrameTypeTrailer"
|
_FrameType_name_0 = "FrameTypeHeaderFrameTypeDataFrameTypeTrailer"
|
||||||
@@ -11,7 +11,6 @@ const (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
_FrameType_index_0 = [...]uint8{0, 15, 28, 44}
|
_FrameType_index_0 = [...]uint8{0, 15, 28, 44}
|
||||||
_FrameType_index_1 = [...]uint8{0, 12}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (i FrameType) String() string {
|
func (i FrameType) String() string {
|
||||||
@@ -22,6 +21,6 @@ func (i FrameType) String() string {
|
|||||||
case i == 255:
|
case i == 255:
|
||||||
return _FrameType_name_1
|
return _FrameType_name_1
|
||||||
default:
|
default:
|
||||||
return fmt.Sprintf("FrameType(%d)", i)
|
return "FrameType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
package rpc
|
package rpc
|
||||||
|
|
||||||
import "fmt"
|
import "strconv"
|
||||||
|
|
||||||
const _Status_name = "StatusOKStatusRequestErrorStatusServerErrorStatusError"
|
const _Status_name = "StatusOKStatusRequestErrorStatusServerErrorStatusError"
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ var _Status_index = [...]uint8{0, 8, 26, 43, 54}
|
|||||||
func (i Status) String() string {
|
func (i Status) String() string {
|
||||||
i -= 1
|
i -= 1
|
||||||
if i >= Status(len(_Status_index)-1) {
|
if i >= Status(len(_Status_index)-1) {
|
||||||
return fmt.Sprintf("Status(%d)", i+1)
|
return "Status(" + strconv.FormatInt(int64(i+1), 10) + ")"
|
||||||
}
|
}
|
||||||
return _Status_name[_Status_index[i]:_Status_index[i+1]]
|
return _Status_name[_Status_index[i]:_Status_index[i+1]]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
package zfs
|
package zfs
|
||||||
|
|
||||||
import "fmt"
|
import "strconv"
|
||||||
|
|
||||||
const _Conflict_name = "ConflictIncrementalConflictAllRightConflictNoCommonAncestorConflictDiverged"
|
const _Conflict_name = "ConflictIncrementalConflictAllRightConflictNoCommonAncestorConflictDiverged"
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ var _Conflict_index = [...]uint8{0, 19, 35, 59, 75}
|
|||||||
|
|
||||||
func (i Conflict) String() string {
|
func (i Conflict) String() string {
|
||||||
if i < 0 || i >= Conflict(len(_Conflict_index)-1) {
|
if i < 0 || i >= Conflict(len(_Conflict_index)-1) {
|
||||||
return fmt.Sprintf("Conflict(%d)", i)
|
return "Conflict(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
}
|
}
|
||||||
return _Conflict_name[_Conflict_index[i]:_Conflict_index[i+1]]
|
return _Conflict_name[_Conflict_index[i]:_Conflict_index[i+1]]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user