forked from openshift/origin
-
Notifications
You must be signed in to change notification settings - Fork 1
/
copyrsync.go
133 lines (120 loc) · 4.02 KB
/
copyrsync.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package rsync
import (
"bytes"
"errors"
"fmt"
"io"
"strings"
"github.com/golang/glog"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
kerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/sets"
kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
cmdutil "github.com/openshift/origin/pkg/cmd/util"
)
// rsyncStrategy implements the rsync copy strategy
// The rsync strategy calls the local rsync command directly and passes the OpenShift
// CLI rsh command as the remote shell command for rsync. It requires that rsync be
// present in both the client machine and the remote container.
type rsyncStrategy struct {
Flags []string
RshCommand string
LocalExecutor executor
RemoteExecutor executor
podChecker podChecker
}
// rshExcludeFlags are flags that are passed to oc rsync, and should not be passed on to the underlying command being invoked via oc rsh.
var rshExcludeFlags = sets.NewString("delete", "strategy", "quiet", "include", "exclude", "progress", "no-perms", "watch", "compress")
func newRsyncStrategy(f kcmdutil.Factory, c *cobra.Command, o *RsyncOptions) (copyStrategy, error) {
// Determine the rsh command to pass to the local rsync command
rshCmd := cmdutil.SiblingCommand(c, "rsh")
// Append all original flags to rsh command
c.Flags().Visit(func(flag *pflag.Flag) {
if rshExcludeFlags.Has(flag.Name) {
return
}
rshCmd = append(rshCmd, fmt.Sprintf("--%s=%s", flag.Name, flag.Value.String()))
})
rshCmdStr := strings.Join(rsyncEscapeCommand(rshCmd), " ")
glog.V(4).Infof("Rsh command: %s", rshCmdStr)
remoteExec, err := newRemoteExecutor(f, o)
if err != nil {
return nil, err
}
// The blocking-io flag is used to resolve a sync issue when
// copying from the pod to the local machine
flags := []string{"--blocking-io"}
flags = append(flags, rsyncDefaultFlags...)
flags = append(flags, rsyncFlagsFromOptions(o)...)
podName := o.Source.PodName
if o.Source.Local() {
podName = o.Destination.PodName
}
client, err := f.ClientSet()
if err != nil {
return nil, err
}
return &rsyncStrategy{
Flags: flags,
RshCommand: rshCmdStr,
RemoteExecutor: remoteExec,
LocalExecutor: newLocalExecutor(),
podChecker: podAPIChecker{client, o.Namespace, podName},
}, nil
}
func (r *rsyncStrategy) Copy(source, destination *pathSpec, out, errOut io.Writer) error {
glog.V(3).Infof("Copying files with rsync")
cmd := append([]string{"rsync"}, r.Flags...)
cmd = append(cmd, "-e", r.RshCommand, source.RsyncPath(), destination.RsyncPath())
errBuf := &bytes.Buffer{}
err := r.LocalExecutor.Execute(cmd, nil, out, errBuf)
if isExitError(err) {
// Check if pod exists
if podCheckErr := r.podChecker.CheckPod(); podCheckErr != nil {
return podCheckErr
}
// Determine whether rsync is present in the pod container
testRsyncErr := checkRsync(r.RemoteExecutor)
if testRsyncErr != nil {
return strategySetupError("rsync not available in container")
}
}
io.Copy(errOut, errBuf)
return err
}
func (r *rsyncStrategy) Validate() error {
errs := []error{}
if len(r.RshCommand) == 0 {
errs = append(errs, errors.New("rsh command must be provided"))
}
if r.LocalExecutor == nil {
errs = append(errs, errors.New("local executor must not be nil"))
}
if r.RemoteExecutor == nil {
errs = append(errs, errors.New("remote executor must not be nil"))
}
if len(errs) > 0 {
return kerrors.NewAggregate(errs)
}
return nil
}
// rsyncEscapeCommand wraps each element of the command in double quotes
// if it contains any of the following: single quote, double quote, or space
// It also replaces every pre-existing double quote character in the element with a pair.
// Example: " -> ""
func rsyncEscapeCommand(command []string) []string {
var escapedCommand []string
for _, val := range command {
needsQuoted := strings.ContainsAny(val, `'" `)
if needsQuoted {
val = strings.Replace(val, `"`, `""`, -1)
val = `"` + val + `"`
}
escapedCommand = append(escapedCommand, val)
}
return escapedCommand
}
func (r *rsyncStrategy) String() string {
return "rsync"
}