Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add rsync client and another constructor for rsync server #14

Merged
merged 5 commits into from
Mar 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
476 changes: 476 additions & 0 deletions transfer/rsync/client.go

Large diffs are not rendered by default.

565 changes: 565 additions & 0 deletions transfer/rsync/client_test.go

Large diffs are not rendered by default.

213 changes: 213 additions & 0 deletions transfer/rsync/command_options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
package rsync

import (
"fmt"
errorsutil "k8s.io/apimachinery/pkg/util/errors"
"regexp"
"strings"
)

const (
optRecursive = "--recursive"
optSymLinks = "--links"
optPermissions = "--perms"
optModTimes = "--times"
optDeviceFiles = "--devices"
optSpecialFiles = "--specials"
optOwner = "--owner"
optGroup = "--group"
optHardLinks = "--hard-links"
optPartial = "--partial"
optDelete = "--delete"
optBwLimit = "--bwlimit=%d"
optInfo = "--info=%s"
optHumanReadable = "--human-readable"
optLogFile = "--log-file=%s"
)

const (
logFileStdOut = "/dev/stdout"
)

type Applier interface {
ApplyTo(options *CommandOptions) error
}

// CommandOptions defines options that can be customized in the Rsync command
type CommandOptions struct {
Recursive bool
SymLinks bool
Permissions bool
ModTimes bool
DeviceFiles bool
SpecialFiles bool
Groups bool
Owners bool
HardLinks bool
Delete bool
Partial bool
BwLimit *int
HumanReadable bool
LogFile string
Info []string
Extras []string
}

// AsRsyncCommandOptions returns validated rsync options and validation errors as two lists
func (c *CommandOptions) AsRsyncCommandOptions() ([]string, error) {
var errs []error
opts := []string{}
if c.Recursive {
opts = append(opts, optRecursive)
}
if c.SymLinks {
opts = append(opts, optSymLinks)
}
if c.Permissions {
opts = append(opts, optPermissions)
}
if c.DeviceFiles {
opts = append(opts, optDeviceFiles)
}
if c.SpecialFiles {
opts = append(opts, optSpecialFiles)
}
if c.ModTimes {
opts = append(opts, optModTimes)
}
if c.Owners {
opts = append(opts, optOwner)
}
if c.Groups {
opts = append(opts, optGroup)
}
if c.HardLinks {
opts = append(opts, optHardLinks)
}
if c.Delete {
opts = append(opts, optDelete)
}
if c.Partial {
opts = append(opts, optPartial)
}
if c.BwLimit != nil {
if *c.BwLimit > 0 {
opts = append(opts,
fmt.Sprintf(optBwLimit, *c.BwLimit))
} else {
errs = append(errs, fmt.Errorf("rsync bwlimit value must be a positive integer"))
}
}
if c.HumanReadable {
opts = append(opts, optHumanReadable)
}
if c.LogFile != "" {
opts = append(opts, fmt.Sprintf(optLogFile, c.LogFile))
}
if len(c.Info) > 0 {
validatedOptions, err := filterRsyncInfoOptions(c.Info)
errs = append(errs, err)
opts = append(opts,
fmt.Sprintf(
optInfo, strings.Join(validatedOptions, ",")))
}
if len(c.Extras) > 0 {
extraOpts, err := filterRsyncExtraOptions(c.Extras)
errs = append(errs, err)
opts = append(opts, extraOpts...)
}
return opts, errorsutil.NewAggregate(errs)
}

func filterRsyncInfoOptions(options []string) (validatedOptions []string, err error) {
var errs []error
r := regexp.MustCompile(`^[A-Z]+\d?$`)
for _, opt := range options {
if r.MatchString(opt) {
validatedOptions = append(validatedOptions, strings.TrimSpace(opt))
} else {
errs = append(errs, fmt.Errorf("invalid value %s for Rsync option --info", opt))
}
}
return validatedOptions, errorsutil.NewAggregate(errs)
}

func filterRsyncExtraOptions(options []string) (validatedOptions []string, err error) {
var errs []error
r := regexp.MustCompile(`^\-{1,2}([a-z0-9]+\-){0,}?[a-z0-9]+$`)
for _, opt := range options {
if r.MatchString(opt) {
validatedOptions = append(validatedOptions, opt)
} else {
errs = append(errs, fmt.Errorf("invalid Rsync option %s", opt))
}
}
return validatedOptions, errorsutil.NewAggregate(errs)
}

func rsyncCommandDefaultOptions() []Applier {
return []Applier{
ArchiveFiles(true),
StandardProgress(true),
}
}

func (c *CommandOptions) Apply(opts ...Applier) error {
errs := []error{}
for _, opt := range opts {
if err := opt.ApplyTo(c); err != nil {
errs = append(errs, err)
}
}
return errorsutil.NewAggregate(errs)
}

func rsyncCommandWithDefaultFlags() ([]string, error) {
c := CommandOptions{}
defaultOptions := rsyncCommandDefaultOptions()
err := c.Apply(defaultOptions...)
if err != nil {
return nil, err
}
return c.AsRsyncCommandOptions()
}

type ArchiveFiles bool

func (a ArchiveFiles) ApplyTo(opts *CommandOptions) error {
opts.Recursive = bool(a)
opts.SymLinks = bool(a)
opts.Permissions = bool(a)
opts.ModTimes = bool(a)
opts.Groups = bool(a)
opts.Owners = bool(a)
opts.DeviceFiles = bool(a)
opts.SpecialFiles = bool(a)
return nil
}

type PreserveOwnership bool

func (p PreserveOwnership) ApplyTo(opts *CommandOptions) error {
opts.Owners = bool(p)
opts.Groups = bool(p)
return nil
}

type StandardProgress bool

func (s StandardProgress) ApplyTo(opts *CommandOptions) error {
opts.Info = []string{
"COPY2", "DEL2", "REMOVE2", "SKIP2", "FLIST2", "PROGRESS2", "STATS2",
}
opts.HumanReadable = true
opts.LogFile = logFileStdOut
return nil
}

type DeleteDestination bool

func (d DeleteDestination) ApplyTo(opts *CommandOptions) error {
opts.Delete = bool(d)
return nil
}
19 changes: 11 additions & 8 deletions transfer/rsync/rsync.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ const (
)

const (
rsyncImage = "quay.io/konveyor/rsync-transfer:latest"
rsyncConfig = "backube-rsync-config"
rsyncSecretPrefix = "backube-rsync"
rsyncServiceAccount = "backube-rsync-sa"
rsyncRole = "backube-rsync-role"
rsyncRoleBinding = "backube-rsync-rolebinding"
rsyncdLogDir = "rsyncd-logs"
rsyncdLogDirPath = "/var/log/rsyncd/"
rsyncImage = "quay.io/konveyor/rsync-transfer:latest"
rsyncConfig = "backube-rsync-config"
rsyncSecretPrefix = "backube-rsync"
rsyncServiceAccount = "backube-rsync-sa"
rsyncRole = "backube-rsync-role"
rsyncPassword = "backube-rsync-password"
rsyncPasswordKey = "RSYNC_PASSWORD"
rsyncCommunicationMountPath = "/usr/share/rsync"
rsyncRoleBinding = "backube-rsync-rolebinding"
rsyncdLogDir = "rsyncd-logs"
rsyncdLogDirPath = "/var/log/rsyncd/"
)

// applyPodOptions take a PodSpec and PodOptions, applies
Expand Down
Loading