Documentation
¶
Overview ¶
Package uploadfun fans out local files to multiple FTP/FTPS/SFTP endpoints concurrently, with retry and an event stream, for unattended automation rather than interactive use.
Index ¶
- Constants
- func Upload(ctx context.Context, files []string, endpoints []Endpoint, opts Options) <-chan UploadEvent
- type DryRunEvent
- type Duration
- type Endpoint
- type EndpointDoneEvent
- type EndpointGivenUpEvent
- type EndpointUnreachableEvent
- type FileErrorEvent
- type FileStartEvent
- type FileSuccessEvent
- type Options
- type OverwriteMode
- type ProgressEvent
- type Protocol
- type UploadEvent
Constants ¶
const ( DefaultAttempts = 3 DefaultRetryDelay = 2 * time.Second DefaultConnectTimeout = 30 * time.Second DefaultStallTimeout = 5 * time.Minute // DefaultMaxConsecutiveConnectFailures is a floor, not a multiple of // Attempts: even Attempts: 1 gets a few tries across the batch before // being written off, rather than quitting after one connect blip. DefaultMaxConsecutiveConnectFailures = 3 )
Defaults applied by LoadConfig to any endpoint (or the config file as a whole) that doesn't set its own value.
Variables ¶
This section is empty.
Functions ¶
func Upload ¶
func Upload( ctx context.Context, files []string, endpoints []Endpoint, opts Options, ) <-chan UploadEvent
Upload fans out files to endpoints, one goroutine per endpoint, retrying per Endpoint.Attempts/RetryDelay. The channel closes once every worker finishes; canceling ctx doesn't stop in-flight transfers. The caller must keep receiving from the channel until it closes - each worker sends on it unbuffered, so if the caller stops ranging early (e.g. returning on the first error), every worker blocks forever on its next send.
Types ¶
type DryRunEvent ¶
type DryRunEvent struct {
Endpoint string `json:"endpoint"`
// Files is how many files a real run would upload to this endpoint;
// meaningful only when Err is nil.
Files int `json:"files"`
// Err is set if connecting, authenticating, or the write probe failed;
// nil means the endpoint is reachable and writable and Files reflects
// the planned upload.
Err error `json:"-"`
}
DryRunEvent reports the outcome of a --dry-run preflight for one endpoint: connect and authenticate to prove it's reachable, probe the target directory for writability, then report how many files a real run would upload. Exactly one is sent per endpoint when Options.DryRun is set, replacing the per-file events.
type Duration ¶
Duration wraps time.Duration so an event's timing field serializes to JSON as a seconds number rounded to two decimals - matching the text output and friendlier than the raw nanosecond count a plain time.Duration would marshal to.
func (Duration) MarshalJSON ¶
type Endpoint ¶
type Endpoint struct {
Name string
Protocol Protocol
Host string
Port int
Username string
Password string
// PrivateKey is a path to an SSH private key file, used by the sftp
// protocol as an alternative to Password.
PrivateKey string
Overwrite OverwriteMode
// InsecureSkipVerify disables TLS certificate verification for the
// ftps protocol. Only for self-signed/test servers - it accepts any
// certificate, so never enable it against a production endpoint.
InsecureSkipVerify bool
Attempts int
RetryDelay time.Duration
ConnectTimeout time.Duration
// StallTimeout bounds how long a transfer may go without forward
// progress; zero disables idle-stall protection.
StallTimeout time.Duration
// MaxConsecutiveConnectFailures bounds how many connect failures in a
// row this endpoint tolerates across the whole batch before the rest
// of the files are skipped as unreachable, independent of Attempts.
MaxConsecutiveConnectFailures int
}
Endpoint is one remote destination to upload to, fully resolved (global config defaults already merged in) by LoadConfig.
func LoadConfig ¶
LoadConfig reads and validates a YAML endpoint config, resolving ${ENV_VAR} interpolation and global-default/per-endpoint-override merging. It collects every error instead of stopping at the first.
type EndpointDoneEvent ¶
type EndpointDoneEvent struct {
Endpoint string `json:"endpoint"`
Succeeded int `json:"succeeded"`
Failed int `json:"failed"`
// Elapsed is the endpoint worker's total wall-clock time across the
// whole batch, including connects, retries, and backoff.
Elapsed Duration `json:"durationSec"`
}
EndpointDoneEvent reports that one endpoint's worker has finished (uploaded or given up on) every file and disconnected.
type EndpointGivenUpEvent ¶
type EndpointGivenUpEvent struct {
Endpoint string `json:"endpoint"`
Reason string `json:"reason"`
SkippedFiles []string `json:"skippedFiles"`
}
EndpointGivenUpEvent reports that one endpoint's worker abandoned the rest of the batch after hitting an unrecoverable transfer error (for example, an SFTP permission-denied response), covering every skipped file in a single event.
type EndpointUnreachableEvent ¶
type EndpointUnreachableEvent struct {
Endpoint string `json:"endpoint"`
ConsecutiveFailures int `json:"consecutiveFailures"`
SkippedFiles []string `json:"skippedFiles"`
}
EndpointUnreachableEvent reports that, after ConsecutiveFailures connect failures in a row, one endpoint's worker gave up on the rest of the batch, covering every skipped file in a single event.
type FileErrorEvent ¶
type FileErrorEvent struct {
Endpoint string `json:"endpoint"`
File string `json:"file"`
Attempt int `json:"attempt"`
Reason string `json:"reason"`
// Err is the underlying error; excluded from JSON output since the
// error interface carries no exported fields worth serializing (its
// message is already captured in Reason).
Err error `json:"-"`
}
FileErrorEvent reports a single failed attempt (upload or verification) for a file on one endpoint. Attempt is 1-based; further attempts follow up to the endpoint's Attempts budget before the file is given up on.
type FileStartEvent ¶
type FileStartEvent struct {
Endpoint string `json:"endpoint"`
File string `json:"file"`
Attempt int `json:"attempt"`
}
FileStartEvent reports that an endpoint worker is about to attempt one file - emitted once per attempt, before delete/upload/verify, so consumers know a (possibly long) transfer is underway before the first ProgressEvent arrives.
type FileSuccessEvent ¶
type FileSuccessEvent struct {
Endpoint string `json:"endpoint"`
File string `json:"file"`
// VerifyMethod describes what verification was performed ("size",
// "size+hash"), or "" if disabled (NoVerify).
VerifyMethod string `json:"verifyMethod,omitempty"`
// Elapsed is the wall-clock time of the successful attempt's work -
// delete (if any), upload, and verification - excluding earlier failed
// attempts and their retry backoff.
Elapsed Duration `json:"durationSec"`
}
FileSuccessEvent reports that a file was uploaded (and, unless NoVerify, verified) successfully on one endpoint.
type Options ¶
type Options struct {
// NoVerify disables the post-upload size/hash verification that's on
// by default.
NoVerify bool
// DryRun connects and authenticates per endpoint, verifies the target
// directory is writable by round-tripping a throwaway probe file, and
// reports how many files a real run would upload - without touching any
// of the actual files being sent.
DryRun bool
}
Options controls behavior of Upload that isn't per-endpoint config.
type OverwriteMode ¶
type OverwriteMode string
OverwriteMode controls how an existing remote file with the same name is handled before upload.
const ( // OverwriteDeleteFirst deletes any existing remote file before // uploading. It's the default: it avoids servers that reject a PUT // over an existing file. OverwriteDeleteFirst OverwriteMode = "delete-first" // OverwriteDirect uploads straight over any existing remote file, // avoiding the brief window where the remote file doesn't exist at // all between delete and re-upload. OverwriteDirect OverwriteMode = "direct" )
type ProgressEvent ¶
type ProgressEvent struct {
Endpoint string `json:"endpoint"`
File string `json:"file"`
BytesSent int64 `json:"bytesSent"`
TotalBytes int64 `json:"totalBytes"`
}
ProgressEvent reports byte-level upload progress for one file on one endpoint. It is always emitted; consumers that don't want progress detail (like the CLI's non-verbose modes) simply ignore it.
type UploadEvent ¶
type UploadEvent interface {
// contains filtered or unexported methods
}
UploadEvent is the vocabulary of events sent on the channel returned by Upload; consumers type-switch on it to distinguish the event kinds below.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
uploadfun
command
Command uploadfun is the CLI entry point for the uploadfun library.
|
Command uploadfun is the CLI entry point for the uploadfun library. |
|
internal
|
|
|
testdocker
Package testdocker provides minimal Docker container lifecycle helpers shared by this repo's integration tests (gated behind the "integration" build tag in their own packages).
|
Package testdocker provides minimal Docker container lifecycle helpers shared by this repo's integration tests (gated behind the "integration" build tag in their own packages). |
|
testservers
Package testservers starts the real FTP/FTPS/SFTP servers this repo's integration tests (internal/transport and the root package) run against, on top of internal/testdocker's generic container lifecycle helpers.
|
Package testservers starts the real FTP/FTPS/SFTP servers this repo's integration tests (internal/transport and the root package) run against, on top of internal/testdocker's generic container lifecycle helpers. |
|
transport
Package transport holds the unexported FTP/FTPS/SFTP protocol implementations.
|
Package transport holds the unexported FTP/FTPS/SFTP protocol implementations. |