Support copy file from node to local host or between nodes.
parent
78dec3b851
commit
a88aec8b46
|
@ -25,7 +25,6 @@ import (
|
||||||
pt "path"
|
pt "path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"k8s.io/klog/v2"
|
|
||||||
"k8s.io/minikube/pkg/minikube/assets"
|
"k8s.io/minikube/pkg/minikube/assets"
|
||||||
"k8s.io/minikube/pkg/minikube/command"
|
"k8s.io/minikube/pkg/minikube/command"
|
||||||
"k8s.io/minikube/pkg/minikube/exit"
|
"k8s.io/minikube/pkg/minikube/exit"
|
||||||
|
@ -36,72 +35,46 @@ import (
|
||||||
"k8s.io/minikube/pkg/minikube/reason"
|
"k8s.io/minikube/pkg/minikube/reason"
|
||||||
)
|
)
|
||||||
|
|
||||||
// placeholders for flag values
|
type remotePath struct {
|
||||||
var (
|
node string
|
||||||
srcPath string
|
path string
|
||||||
dstPath string
|
}
|
||||||
dstNode string
|
|
||||||
)
|
|
||||||
|
|
||||||
// cpCmd represents the cp command, similar to docker cp
|
// cpCmd represents the cp command, similar to docker cp
|
||||||
var cpCmd = &cobra.Command{
|
var cpCmd = &cobra.Command{
|
||||||
Use: "cp <source file path> <target node name>:<target file absolute path>",
|
Use: "cp <source node name>:<source file path> <target node name>:<target file absolute path>",
|
||||||
Short: "Copy the specified file into minikube",
|
Short: "Copy the specified file into minikube",
|
||||||
Long: "Copy the specified file into minikube, it will be saved at path <target file absolute path> in your minikube.\n" +
|
Long: `Copy the specified file into minikube, it will be saved at path <target file absolute path> in your minikube.
|
||||||
"Example Command : \"minikube cp a.txt /home/docker/b.txt\"\n" +
|
Default target node controlplane and If <source node name> is omitted, It will trying to copy from host.
|
||||||
" \"minikube cp a.txt minikube-m02:/home/docker/b.txt\"\n",
|
|
||||||
|
Example Command : "minikube cp a.txt /home/docker/b.txt" +
|
||||||
|
"minikube cp a.txt minikube-m02:/home/docker/b.txt"
|
||||||
|
"minikube cp minikube-m01:a.txt minikube-m02:/home/docker/b.txt"`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
if len(args) != 2 {
|
if len(args) != 2 {
|
||||||
exit.Message(reason.Usage, `Please specify the path to copy:
|
exit.Message(reason.Usage, `Please specify the path to copy:
|
||||||
minikube cp <source file path> <target file absolute path> (example: "minikube cp a/b.txt /copied.txt")`)
|
minikube cp <source file path> <target file absolute path> (example: "minikube cp a/b.txt /copied.txt")`)
|
||||||
}
|
}
|
||||||
|
|
||||||
srcPath = args[0]
|
src := newRemotePath(args[0])
|
||||||
dstPath = args[1]
|
dst := newRemotePath(args[1])
|
||||||
|
validateArgs(src, dst)
|
||||||
// if destination path is not a absolute path, trying to parse with <node>:<abs path> format
|
|
||||||
if !strings.HasPrefix(dstPath, "/") {
|
|
||||||
if sp := strings.SplitN(dstPath, ":", 2); len(sp) == 2 {
|
|
||||||
dstNode = sp[0]
|
|
||||||
dstPath = sp[1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
validateArgs(srcPath, dstPath)
|
|
||||||
|
|
||||||
fa, err := assets.NewFileAsset(srcPath, pt.Dir(dstPath), pt.Base(dstPath), "0644")
|
|
||||||
if err != nil {
|
|
||||||
out.ErrLn("%v", errors.Wrap(err, "getting file asset"))
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
if err := fa.Close(); err != nil {
|
|
||||||
klog.Warningf("error closing the file %s: %v", fa.GetSourcePath(), err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
co := mustload.Running(ClusterFlagValue())
|
co := mustload.Running(ClusterFlagValue())
|
||||||
var runner command.Runner
|
var runner command.Runner
|
||||||
if dstNode == "" {
|
|
||||||
|
if dst.node != "" {
|
||||||
|
runner = remoteCommandRunner(&co, dst.node)
|
||||||
|
} else if src.node == "" {
|
||||||
|
// if node name not explicitly specfied in both of source and target,
|
||||||
|
// consider target is controlpanel node for backward compatibility.
|
||||||
runner = co.CP.Runner
|
runner = co.CP.Runner
|
||||||
} else {
|
} else {
|
||||||
n, _, err := node.Retrieve(*co.Config, dstNode)
|
runner = command.NewExecRunner(false)
|
||||||
if err != nil {
|
|
||||||
exit.Message(reason.GuestNodeRetrieve, "Node {{.nodeName}} does not exist.", out.V{"nodeName": dstNode})
|
|
||||||
}
|
|
||||||
|
|
||||||
h, err := machine.GetHost(co.API, *co.Config, *n)
|
|
||||||
if err != nil {
|
|
||||||
exit.Error(reason.GuestLoadHost, "Error getting host", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
runner, err = machine.CommandRunner(h)
|
|
||||||
if err != nil {
|
|
||||||
exit.Error(reason.InternalCommandRunner, "Failed to get command runner", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = runner.Copy(fa); err != nil {
|
fa := copyableFile(&co, src, dst)
|
||||||
|
if err := runner.Copy(fa); err != nil {
|
||||||
exit.Error(reason.InternalCommandRunner, fmt.Sprintf("Fail to copy file %s", fa.GetSourcePath()), err)
|
exit.Error(reason.InternalCommandRunner, fmt.Sprintf("Fail to copy file %s", fa.GetSourcePath()), err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -110,24 +83,84 @@ var cpCmd = &cobra.Command{
|
||||||
func init() {
|
func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateArgs(srcPath string, dstPath string) {
|
// split path to node name and file path
|
||||||
if srcPath == "" {
|
func newRemotePath(path string) *remotePath {
|
||||||
exit.Message(reason.Usage, "Source {{.path}} can not be empty", out.V{"path": srcPath})
|
// if destination path is not a absolute path, trying to parse with <node>:<abs path> format
|
||||||
|
sp := strings.SplitN(path, ":", 2)
|
||||||
|
if len(sp) == 2 && len(sp[0]) > 0 && !strings.Contains(sp[0], "/") && strings.HasPrefix(sp[1], "/") {
|
||||||
|
return &remotePath{node: sp[0], path: sp[1]}
|
||||||
}
|
}
|
||||||
|
|
||||||
if dstPath == "" {
|
return &remotePath{node: "", path: path}
|
||||||
exit.Message(reason.Usage, "Target {{.path}} can not be empty", out.V{"path": dstPath})
|
}
|
||||||
|
|
||||||
|
func remoteCommandRunner(co *mustload.ClusterController, nodeName string) command.Runner {
|
||||||
|
n, _, err := node.Retrieve(*co.Config, nodeName)
|
||||||
|
if err != nil {
|
||||||
|
exit.Message(reason.GuestNodeRetrieve, "Node {{.nodeName}} does not exist.", out.V{"nodeName": nodeName})
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := os.Stat(srcPath); err != nil {
|
h, err := machine.GetHost(co.API, *co.Config, *n)
|
||||||
|
if err != nil {
|
||||||
|
out.ErrLn("%v", errors.Wrap(err, "getting host"))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
runner, err := machine.CommandRunner(h)
|
||||||
|
if err != nil {
|
||||||
|
out.ErrLn("%v", errors.Wrap(err, "getting command runner"))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return runner
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyableFile(co *mustload.ClusterController, src, dst *remotePath) assets.CopyableFile {
|
||||||
|
// get assets.CopyableFile from minikube node
|
||||||
|
if src.node != "" {
|
||||||
|
runner := remoteCommandRunner(co, src.node)
|
||||||
|
f, err := runner.ReadableFile(src.path)
|
||||||
|
if err != nil {
|
||||||
|
out.ErrLn("%v", errors.Wrapf(err, "getting file from %s node", src.node))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fakeWriter := func(_ []byte) (n int, err error) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return assets.NewBaseCopyableFile(f, fakeWriter, pt.Dir(dst.path), pt.Base(dst.path))
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(src.path); err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
exit.Message(reason.HostPathMissing, "Cannot find directory {{.path}} for copy", out.V{"path": srcPath})
|
exit.Message(reason.HostPathMissing, "Cannot find directory {{.path}} for copy", out.V{"path": src})
|
||||||
} else {
|
} else {
|
||||||
exit.Error(reason.HostPathStat, "stat failed", err)
|
exit.Error(reason.HostPathStat, "stat failed", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.HasPrefix(dstPath, "/") {
|
fa, err := assets.NewFileAsset(src.path, pt.Dir(dst.path), pt.Base(dst.path), "0644")
|
||||||
exit.Message(reason.Usage, `<target file absolute path> must be an absolute Path. Relative Path is not allowed (example: "/home/docker/copied.txt")`)
|
if err != nil {
|
||||||
|
out.ErrLn("%v", errors.Wrap(err, "getting file asset"))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fa
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateArgs(src, dst *remotePath) {
|
||||||
|
if src.path == "" {
|
||||||
|
exit.Message(reason.Usage, "Source {{.path}} can not be empty", out.V{"path": src.path})
|
||||||
|
}
|
||||||
|
|
||||||
|
if dst.path == "" {
|
||||||
|
exit.Message(reason.Usage, "Target {{.path}} can not be empty", out.V{"path": dst.path})
|
||||||
|
}
|
||||||
|
|
||||||
|
// if node name not explicitly specfied in both of source and target,
|
||||||
|
// consider target node is controlpanel for backward compatibility.
|
||||||
|
if src.node == "" && dst.node == "" && !strings.HasPrefix(dst.path, "/") {
|
||||||
|
exit.Message(reason.Usage, `Target <remote file path> must be an absolute Path. Relative Path is not allowed (example: "minikube:/home/docker/copied.txt")`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
Copyright 2021 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParsePath(t *testing.T) {
|
||||||
|
var passedCases = []struct {
|
||||||
|
path string
|
||||||
|
expectedNode string
|
||||||
|
expectedPath string
|
||||||
|
}{
|
||||||
|
|
||||||
|
{"", "", ""},
|
||||||
|
{":", "", ":"},
|
||||||
|
{":/a", "", ":/a"},
|
||||||
|
{":a", "", ":a"},
|
||||||
|
{"minikube:", "", "minikube:"},
|
||||||
|
{"minikube:./a", "", "minikube:./a"},
|
||||||
|
{"minikube:a", "", "minikube:a"},
|
||||||
|
{"minikube::a", "", "minikube::a"},
|
||||||
|
{"./a", "", "./a"},
|
||||||
|
{"./a/b", "", "./a/b"},
|
||||||
|
{"a", "", "a"},
|
||||||
|
{"a/b", "", "a/b"},
|
||||||
|
{"/a", "", "/a"},
|
||||||
|
{"/a/b", "", "/a/b"},
|
||||||
|
{"./:a/b", "", "./:a/b"},
|
||||||
|
{"c:\\a", "", "c:\\a"},
|
||||||
|
{"c:\\a\\b", "", "c:\\a\\b"},
|
||||||
|
{"minikube:/a", "minikube", "/a"},
|
||||||
|
{"minikube:/a/b", "minikube", "/a/b"},
|
||||||
|
{"minikube:/a/b:c", "minikube", "/a/b:c"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range passedCases {
|
||||||
|
rp := newRemotePath(c.path)
|
||||||
|
expected := remotePath{
|
||||||
|
node: c.expectedNode,
|
||||||
|
path: c.expectedPath,
|
||||||
|
}
|
||||||
|
if *rp != expected {
|
||||||
|
t.Errorf("parsePath \"%s\" expected: %q, got: %q", c.path, expected, *rp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -35,23 +36,68 @@ import (
|
||||||
// MemorySource is the source name used for in-memory copies
|
// MemorySource is the source name used for in-memory copies
|
||||||
const MemorySource = "memory"
|
const MemorySource = "memory"
|
||||||
|
|
||||||
// CopyableFile is something that can be copied
|
// ReadableFile is something that can be read
|
||||||
type CopyableFile interface {
|
type ReadableFile interface {
|
||||||
io.Reader
|
io.Reader
|
||||||
io.Writer
|
|
||||||
GetLength() int
|
GetLength() int
|
||||||
SetLength(int)
|
|
||||||
GetSourcePath() string
|
GetSourcePath() string
|
||||||
GetTargetPath() string
|
|
||||||
|
|
||||||
GetTargetDir() string
|
|
||||||
GetTargetName() string
|
|
||||||
GetPermissions() string
|
GetPermissions() string
|
||||||
GetModTime() (time.Time, error)
|
GetModTime() (time.Time, error)
|
||||||
Seek(int64, int) (int64, error)
|
Seek(int64, int) (int64, error)
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CopyableFile is something that can be copied
|
||||||
|
type CopyableFile interface {
|
||||||
|
ReadableFile
|
||||||
|
|
||||||
|
io.Writer
|
||||||
|
SetLength(int)
|
||||||
|
GetTargetPath() string
|
||||||
|
GetTargetDir() string
|
||||||
|
GetTargetName() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type writeFn func(d []byte) (n int, err error)
|
||||||
|
type BaseCopyableFile struct {
|
||||||
|
ReadableFile
|
||||||
|
|
||||||
|
writer writeFn
|
||||||
|
length int
|
||||||
|
targetDir string
|
||||||
|
targetName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *BaseCopyableFile) Write(d []byte) (n int, err error) {
|
||||||
|
return r.writer(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *BaseCopyableFile) SetLength(length int) {
|
||||||
|
r.length = length
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *BaseCopyableFile) GetTargetPath() string {
|
||||||
|
return filepath.Join(r.GetTargetDir(), r.GetTargetName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *BaseCopyableFile) GetTargetDir() string {
|
||||||
|
return r.targetDir
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *BaseCopyableFile) GetTargetName() string {
|
||||||
|
return r.targetName
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBaseCopyableFile(source ReadableFile, writer writeFn, targetDir, targetName string) *BaseCopyableFile {
|
||||||
|
return &BaseCopyableFile{
|
||||||
|
ReadableFile: source,
|
||||||
|
writer: writer,
|
||||||
|
targetDir: targetDir,
|
||||||
|
targetName: targetName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// BaseAsset is the base asset class
|
// BaseAsset is the base asset class
|
||||||
type BaseAsset struct {
|
type BaseAsset struct {
|
||||||
SourcePath string
|
SourcePath string
|
||||||
|
|
|
@ -80,6 +80,9 @@ type Runner interface {
|
||||||
|
|
||||||
// Remove is a convenience method that runs a command to remove a file
|
// Remove is a convenience method that runs a command to remove a file
|
||||||
Remove(assets.CopyableFile) error
|
Remove(assets.CopyableFile) error
|
||||||
|
|
||||||
|
// ReadableFile open a remote file for reading
|
||||||
|
ReadableFile(sourcePath string) (assets.ReadableFile, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command returns a human readable command string that does not induce eye fatigue
|
// Command returns a human readable command string that does not induce eye fatigue
|
||||||
|
|
|
@ -219,3 +219,7 @@ func (e *execRunner) Remove(f assets.CopyableFile) error {
|
||||||
}
|
}
|
||||||
return os.Remove(dst)
|
return os.Remove(dst)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *execRunner) ReadableFile(sourcePath string) (assets.ReadableFile, error) {
|
||||||
|
return nil, fmt.Errorf("execRunner does not support ReadableFile - you could be the first to add it")
|
||||||
|
}
|
||||||
|
|
|
@ -161,6 +161,10 @@ func (f *FakeCommandRunner) Remove(file assets.CopyableFile) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FakeCommandRunner) ReadableFile(sourcePath string) (assets.ReadableFile, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// SetFileToContents stores the file to contents map for the FakeCommandRunner
|
// SetFileToContents stores the file to contents map for the FakeCommandRunner
|
||||||
func (f *FakeCommandRunner) SetFileToContents(fileToContents map[string]string) {
|
func (f *FakeCommandRunner) SetFileToContents(fileToContents map[string]string) {
|
||||||
for k, v := range fileToContents {
|
for k, v := range fileToContents {
|
||||||
|
|
|
@ -139,6 +139,10 @@ func (k *kicRunner) WaitCmd(sc *StartedCmd) (*RunResult, error) {
|
||||||
return nil, fmt.Errorf("kicRunner does not support WaitCmd - you could be the first to add it")
|
return nil, fmt.Errorf("kicRunner does not support WaitCmd - you could be the first to add it")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (k *kicRunner) ReadableFile(sourcePath string) (assets.ReadableFile, error) {
|
||||||
|
return nil, fmt.Errorf("kicRunner does not support ReadableFile - you could be the first to add it")
|
||||||
|
}
|
||||||
|
|
||||||
// Copy copies a file and its permissions
|
// Copy copies a file and its permissions
|
||||||
func (k *kicRunner) Copy(f assets.CopyableFile) error {
|
func (k *kicRunner) Copy(f assets.CopyableFile) error {
|
||||||
dst := path.Join(path.Join(f.GetTargetDir(), f.GetTargetName()))
|
dst := path.Join(path.Join(f.GetTargetDir(), f.GetTargetName()))
|
||||||
|
|
|
@ -44,7 +44,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// SSHRunner runs commands through SSH.
|
// SSHRunner runs commands through SSH.
|
||||||
//
|
|
||||||
// It implements the CommandRunner interface.
|
// It implements the CommandRunner interface.
|
||||||
type SSHRunner struct {
|
type SSHRunner struct {
|
||||||
d drivers.Driver
|
d drivers.Driver
|
||||||
|
@ -52,6 +52,49 @@ type SSHRunner struct {
|
||||||
s *ssh.Session
|
s *ssh.Session
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type sshReadableFile struct {
|
||||||
|
length int
|
||||||
|
sourcePath string
|
||||||
|
permissions string
|
||||||
|
sess *ssh.Session
|
||||||
|
modTime time.Time
|
||||||
|
reader io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLength returns lentgh of file
|
||||||
|
func (s *sshReadableFile) GetLength() int {
|
||||||
|
return s.length
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSourcePath returns asset name
|
||||||
|
func (s *sshReadableFile) GetSourcePath() string {
|
||||||
|
return s.sourcePath
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPermissions returns permissions
|
||||||
|
func (s *sshReadableFile) GetPermissions() string {
|
||||||
|
return s.permissions
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sshReadableFile) GetModTime() (time.Time, error) {
|
||||||
|
return s.modTime, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sshReadableFile) Read(p []byte) (int, error) {
|
||||||
|
if s.GetLength() == 0 {
|
||||||
|
return 0, fmt.Errorf("attempted read from a 0 length asset")
|
||||||
|
}
|
||||||
|
return s.reader.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sshReadableFile) Seek(offset int64, whence int) (int64, error) {
|
||||||
|
return 0, fmt.Errorf("Seek is not implemented for sshReadableFile")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sshReadableFile) Close() error {
|
||||||
|
return s.sess.Close()
|
||||||
|
}
|
||||||
|
|
||||||
// NewSSHRunner returns a new SSHRunner that will run commands
|
// NewSSHRunner returns a new SSHRunner that will run commands
|
||||||
// through the ssh.Client provided.
|
// through the ssh.Client provided.
|
||||||
func NewSSHRunner(d drivers.Driver) *SSHRunner {
|
func NewSSHRunner(d drivers.Driver) *SSHRunner {
|
||||||
|
@ -455,3 +498,55 @@ func (s *SSHRunner) CopyFrom(f assets.CopyableFile) error {
|
||||||
}
|
}
|
||||||
return g.Wait()
|
return g.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SSHRunner) ReadableFile(sourcePath string) (assets.ReadableFile, error) {
|
||||||
|
klog.V(4).Infof("NewsshReadableFile: %s -> %s", sourcePath)
|
||||||
|
|
||||||
|
if !strings.HasPrefix(sourcePath, "/") {
|
||||||
|
return nil, fmt.Errorf("sourcePath must be an absolute Path. Relative Path is not allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// get file size and modtime of the destination
|
||||||
|
rr, err := s.RunCmd(exec.Command("stat", "-c", "%#a %s %y", sourcePath))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout := strings.TrimSpace(rr.Stdout.String())
|
||||||
|
outputs := strings.SplitN(stdout, " ", 3)
|
||||||
|
|
||||||
|
permission := outputs[0]
|
||||||
|
size, err := strconv.Atoi(outputs[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
modTime, err := time.Parse(layout, outputs[2])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sess, err := s.session()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "NewSession")
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := sess.StdoutPipe()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "StdOutPipe")
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := fmt.Sprintf("cat %s", sourcePath)
|
||||||
|
if err := sess.Start(cmd); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &sshReadableFile{
|
||||||
|
length: size,
|
||||||
|
sourcePath: sourcePath,
|
||||||
|
permissions: permission,
|
||||||
|
reader: r,
|
||||||
|
modTime: modTime,
|
||||||
|
sess: sess,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
|
@ -69,6 +69,8 @@ type CommandRunner interface {
|
||||||
CopyFrom(assets.CopyableFile) error
|
CopyFrom(assets.CopyableFile) error
|
||||||
// Remove is a convenience method that runs a command to remove a file
|
// Remove is a convenience method that runs a command to remove a file
|
||||||
Remove(assets.CopyableFile) error
|
Remove(assets.CopyableFile) error
|
||||||
|
|
||||||
|
ReadableFile(sourcePath string) (assets.ReadableFile, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manager is a common interface for container runtimes
|
// Manager is a common interface for container runtimes
|
||||||
|
|
|
@ -244,6 +244,10 @@ func (f *FakeRunner) Remove(assets.CopyableFile) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *FakeRunner) ReadableFile(sourcePath string) (assets.ReadableFile, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (f *FakeRunner) dockerPs(args []string) (string, error) {
|
func (f *FakeRunner) dockerPs(args []string) (string, error) {
|
||||||
// ps -a --filter="name=apiserver" --format="{{.ID}}"
|
// ps -a --filter="name=apiserver" --format="{{.ID}}"
|
||||||
if args[1] == "-a" && strings.HasPrefix(args[2], "--filter") {
|
if args[1] == "-a" && strings.HasPrefix(args[2], "--filter") {
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
@ -1555,7 +1556,22 @@ func validateCpCmd(ctx context.Context, t *testing.T, profile string) {
|
||||||
// docs: Run `minikube cp ...` to copy a file to the minikube node
|
// docs: Run `minikube cp ...` to copy a file to the minikube node
|
||||||
// docs: Run `minikube ssh sudo cat ...` to print out the copied file within minikube
|
// docs: Run `minikube ssh sudo cat ...` to print out the copied file within minikube
|
||||||
// docs: make sure the file is correctly copied
|
// docs: make sure the file is correctly copied
|
||||||
testCpCmd(ctx, t, profile, "")
|
|
||||||
|
srcPath := cpTestLocalPath()
|
||||||
|
dstPath := cpTestMinikubePath()
|
||||||
|
|
||||||
|
// copy to node
|
||||||
|
testCpCmd(ctx, t, profile, "", srcPath, "", dstPath)
|
||||||
|
|
||||||
|
// copy from node
|
||||||
|
tmpDir, err := ioutil.TempDir("", "mk_test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
|
tmpPath := filepath.Join(tmpDir, "cp-test.txt")
|
||||||
|
testCpCmd(ctx, t, profile, profile, dstPath, "", tmpPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateMySQL validates a minimalist MySQL deployment
|
// validateMySQL validates a minimalist MySQL deployment
|
||||||
|
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -519,18 +520,38 @@ func cpTestLocalPath() string {
|
||||||
return filepath.Join(*testdataDir, "cp-test.txt")
|
return filepath.Join(*testdataDir, "cp-test.txt")
|
||||||
}
|
}
|
||||||
|
|
||||||
// testCpCmd ensures copy functionality into minikube instance.
|
func cpTestReadText(ctx context.Context, t *testing.T, profile, node, path string) string {
|
||||||
func testCpCmd(ctx context.Context, t *testing.T, profile string, node string) {
|
|
||||||
srcPath := cpTestLocalPath()
|
|
||||||
dstPath := cpTestMinikubePath()
|
|
||||||
|
|
||||||
cpArgv := []string{"-p", profile, "cp", srcPath}
|
|
||||||
if node == "" {
|
if node == "" {
|
||||||
cpArgv = append(cpArgv, dstPath)
|
expected, err := ioutil.ReadFile(path)
|
||||||
} else {
|
if err != nil {
|
||||||
cpArgv = append(cpArgv, fmt.Sprintf("%s:%s", node, dstPath))
|
t.Errorf("failed to read test file 'testdata/cp-test.txt' : %v", err)
|
||||||
|
}
|
||||||
|
return string(expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sshArgv := []string{"-p", profile, "ssh", "-n", node, fmt.Sprintf("sudo cat %s", path)}
|
||||||
|
rr, err := Run(t, exec.CommandContext(ctx, Target(), sshArgv...))
|
||||||
|
if ctx.Err() == context.DeadlineExceeded {
|
||||||
|
t.Errorf("failed to run command by deadline. exceeded timeout : %s", rr.Command())
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("failed to run an cp command. args %q : %v", rr.Command(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return rr.Stdout.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func cpTestMergePath(node, path string) string {
|
||||||
|
if node == "" {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s:%s", node, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// testCpCmd ensures copy functionality into minikube instance.
|
||||||
|
func testCpCmd(ctx context.Context, t *testing.T, profile string, srcNode, srcPath, dstNode, dstPath string) {
|
||||||
|
cpArgv := []string{"-p", profile, "cp", cpTestMergePath(srcNode, srcPath), cpTestMergePath(dstNode, dstPath)}
|
||||||
rr, err := Run(t, exec.CommandContext(ctx, Target(), cpArgv...))
|
rr, err := Run(t, exec.CommandContext(ctx, Target(), cpArgv...))
|
||||||
if ctx.Err() == context.DeadlineExceeded {
|
if ctx.Err() == context.DeadlineExceeded {
|
||||||
t.Errorf("failed to run command by deadline. exceeded timeout : %s", rr.Command())
|
t.Errorf("failed to run command by deadline. exceeded timeout : %s", rr.Command())
|
||||||
|
@ -539,26 +560,15 @@ func testCpCmd(ctx context.Context, t *testing.T, profile string, node string) {
|
||||||
t.Errorf("failed to run an cp command. args %q : %v", rr.Command(), err)
|
t.Errorf("failed to run an cp command. args %q : %v", rr.Command(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sshArgv := []string{"-p", profile, "ssh"}
|
expected := cpTestReadText(ctx, t, profile, srcNode, srcPath)
|
||||||
if node != "" {
|
var copiedText string
|
||||||
sshArgv = append(sshArgv, "-n", node)
|
if srcNode == "" && dstNode == "" {
|
||||||
}
|
copiedText = cpTestReadText(ctx, t, profile, profile, dstPath)
|
||||||
sshArgv = append(sshArgv, fmt.Sprintf("sudo cat %s", dstPath))
|
} else {
|
||||||
|
copiedText = cpTestReadText(ctx, t, profile, dstNode, dstPath)
|
||||||
rr, err = Run(t, exec.CommandContext(ctx, Target(), sshArgv...))
|
|
||||||
if ctx.Err() == context.DeadlineExceeded {
|
|
||||||
t.Errorf("failed to run command by deadline. exceeded timeout : %s", rr.Command())
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to run an cp command. args %q : %v", rr.Command(), err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expected, err := os.ReadFile(srcPath)
|
if diff := cmp.Diff(expected, copiedText); diff != "" {
|
||||||
if err != nil {
|
|
||||||
t.Errorf("failed to read test file 'testdata/cp-test.txt' : %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if diff := cmp.Diff(string(expected), rr.Stdout.String()); diff != "" {
|
|
||||||
t.Errorf("/testdata/cp-test.txt content mismatch (-want +got):\n%s", diff)
|
t.Errorf("/testdata/cp-test.txt content mismatch (-want +got):\n%s", diff)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,11 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -177,11 +180,30 @@ func validateCopyFileWithMultiNode(ctx context.Context, t *testing.T, profile st
|
||||||
t.Errorf("failed to decode json from status: args %q: %v", rr.Command(), err)
|
t.Errorf("failed to decode json from status: args %q: %v", rr.Command(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range statuses {
|
tmpDir, err := ioutil.TempDir("", "mk_cp_test")
|
||||||
if s.Worker {
|
if err != nil {
|
||||||
testCpCmd(ctx, t, profile, s.Name)
|
t.Fatal(err)
|
||||||
} else {
|
}
|
||||||
testCpCmd(ctx, t, profile, "")
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
|
srcPath := cpTestLocalPath()
|
||||||
|
dstPath := cpTestMinikubePath()
|
||||||
|
|
||||||
|
for _, n := range statuses {
|
||||||
|
// copy local to node
|
||||||
|
testCpCmd(ctx, t, profile, "", srcPath, n.Name, dstPath)
|
||||||
|
|
||||||
|
// copy back from node to lcoal
|
||||||
|
tmpPath := filepath.Join(tmpDir, fmt.Sprintf("cp-test_%s.txt", n.Name))
|
||||||
|
testCpCmd(ctx, t, profile, n.Name, dstPath, "", tmpPath)
|
||||||
|
|
||||||
|
// copy node to node
|
||||||
|
for _, n2 := range statuses {
|
||||||
|
if n.Name == n2.Name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fp := filepath.Join("/home/docker", fmt.Sprintf("cp-test_%s_%s.txt", n.Name, n2.Name))
|
||||||
|
testCpCmd(ctx, t, profile, n.Name, dstPath, n2.Name, fp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
"--kvm-numa-count range is 1-8": "",
|
"--kvm-numa-count range is 1-8": "",
|
||||||
"--network flag is only valid with the docker/podman and KVM drivers, it will be ignored": "",
|
"--network flag is only valid with the docker/podman and KVM drivers, it will be ignored": "",
|
||||||
"127.0.0.1": "",
|
"127.0.0.1": "",
|
||||||
"\u003ctarget file absolute path\u003e must be an absolute Path. Relative Path is not allowed (example: \"/home/docker/copied.txt\")": "",
|
|
||||||
"==\u003e Audit \u003c==": "",
|
"==\u003e Audit \u003c==": "",
|
||||||
"==\u003e Last Start \u003c==": "",
|
"==\u003e Last Start \u003c==": "",
|
||||||
"A VPN or firewall is interfering with HTTP access to the minikube VM. Alternatively, try a different VM driver: https://minikube.sigs.k8s.io/docs/start/": "",
|
"A VPN or firewall is interfering with HTTP access to the minikube VM. Alternatively, try a different VM driver: https://minikube.sigs.k8s.io/docs/start/": "",
|
||||||
|
@ -598,6 +597,7 @@
|
||||||
"System only has {{.size}}MiB available, less than the required {{.req}}MiB for Kubernetes": "",
|
"System only has {{.size}}MiB available, less than the required {{.req}}MiB for Kubernetes": "",
|
||||||
"Tag images": "",
|
"Tag images": "",
|
||||||
"Tag to apply to the new image (optional)": "",
|
"Tag to apply to the new image (optional)": "",
|
||||||
|
"Target \u003cremote file path\u003e must be an absolute Path. Relative Path is not allowed (example: \"minikube:/home/docker/copied.txt\")": "",
|
||||||
"Target directory {{.path}} must be an absolute path": "",
|
"Target directory {{.path}} must be an absolute path": "",
|
||||||
"Target {{.path}} can not be empty": "",
|
"Target {{.path}} can not be empty": "",
|
||||||
"Test docs have been saved at - {{.path}}": "",
|
"Test docs have been saved at - {{.path}}": "",
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
"--kvm-numa-count range is 1-8": "",
|
"--kvm-numa-count range is 1-8": "",
|
||||||
"--network flag is only valid with the docker/podman and KVM drivers, it will be ignored": "",
|
"--network flag is only valid with the docker/podman and KVM drivers, it will be ignored": "",
|
||||||
"127.0.0.1": "",
|
"127.0.0.1": "",
|
||||||
"\u003ctarget file absolute path\u003e must be an absolute Path. Relative Path is not allowed (example: \"/home/docker/copied.txt\")": "",
|
|
||||||
"==\u003e Audit \u003c==": "",
|
"==\u003e Audit \u003c==": "",
|
||||||
"==\u003e Last Start \u003c==": "",
|
"==\u003e Last Start \u003c==": "",
|
||||||
"A VPN or firewall is interfering with HTTP access to the minikube VM. Alternatively, try a different VM driver: https://minikube.sigs.k8s.io/docs/start/": "Una VPN o cortafuegos está interfiriendo con el acceso HTTP a la máquina virtual de minikube. Alternativamente prueba otro controlador: https://minikube.sigs.k8s.io/docs/start/",
|
"A VPN or firewall is interfering with HTTP access to the minikube VM. Alternatively, try a different VM driver: https://minikube.sigs.k8s.io/docs/start/": "Una VPN o cortafuegos está interfiriendo con el acceso HTTP a la máquina virtual de minikube. Alternativamente prueba otro controlador: https://minikube.sigs.k8s.io/docs/start/",
|
||||||
|
@ -604,6 +603,7 @@
|
||||||
"System only has {{.size}}MiB available, less than the required {{.req}}MiB for Kubernetes": "",
|
"System only has {{.size}}MiB available, less than the required {{.req}}MiB for Kubernetes": "",
|
||||||
"Tag images": "",
|
"Tag images": "",
|
||||||
"Tag to apply to the new image (optional)": "",
|
"Tag to apply to the new image (optional)": "",
|
||||||
|
"Target \u003cremote file path\u003e must be an absolute Path. Relative Path is not allowed (example: \"minikube:/home/docker/copied.txt\")": "",
|
||||||
"Target directory {{.path}} must be an absolute path": "",
|
"Target directory {{.path}} must be an absolute path": "",
|
||||||
"Target {{.path}} can not be empty": "",
|
"Target {{.path}} can not be empty": "",
|
||||||
"Test docs have been saved at - {{.path}}": "",
|
"Test docs have been saved at - {{.path}}": "",
|
||||||
|
|
|
@ -567,6 +567,7 @@
|
||||||
"System only has {{.size}}MiB available, less than the required {{.req}}MiB for Kubernetes": "Le système n'a que {{.size}} Mio disponibles, moins que les {{.req}} Mio requis pour Kubernetes",
|
"System only has {{.size}}MiB available, less than the required {{.req}}MiB for Kubernetes": "Le système n'a que {{.size}} Mio disponibles, moins que les {{.req}} Mio requis pour Kubernetes",
|
||||||
"Tag images": "Marquer des images",
|
"Tag images": "Marquer des images",
|
||||||
"Tag to apply to the new image (optional)": "Tag à appliquer à la nouvelle image (facultatif)",
|
"Tag to apply to the new image (optional)": "Tag à appliquer à la nouvelle image (facultatif)",
|
||||||
|
"Target \u003cremote file path\u003e must be an absolute Path. Relative Path is not allowed (example: \"minikube:/home/docker/copied.txt\")": "",
|
||||||
"Target directory {{.path}} must be an absolute path": "Le répertoire cible {{.path}} doit être un chemin absolu",
|
"Target directory {{.path}} must be an absolute path": "Le répertoire cible {{.path}} doit être un chemin absolu",
|
||||||
"Target {{.path}} can not be empty": "La cible {{.path}} ne peut pas être vide",
|
"Target {{.path}} can not be empty": "La cible {{.path}} ne peut pas être vide",
|
||||||
"Test docs have been saved at - {{.path}}": "Les documents de test ont été enregistrés à - {{.path}}",
|
"Test docs have been saved at - {{.path}}": "Les documents de test ont été enregistrés à - {{.path}}",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
|
||||||
"\"The '{{.minikube_addon}}' addon is disabled": "'{{.minikube_addon}}' アドオンが無効です",
|
"\"The '{{.minikube_addon}}' addon is disabled": "'{{.minikube_addon}}' アドオンが無効です",
|
||||||
"\"{{.context}}\" context has been updated to point to {{.hostname}}:{{.port}}": "「{{.context}}」コンテキストが更新されて、{{.hostname}}:{{.port}} を指すようになりました",
|
"\"{{.context}}\" context has been updated to point to {{.hostname}}:{{.port}}": "「{{.context}}」コンテキストが更新されて、{{.hostname}}:{{.port}} を指すようになりました",
|
||||||
"\"{{.machineName}}\" does not exist, nothing to stop": "「{{.machineName}}」は存在しません。停止対象がありません",
|
"\"{{.machineName}}\" does not exist, nothing to stop": "「{{.machineName}}」は存在しません。停止対象がありません",
|
||||||
|
@ -598,6 +599,7 @@
|
||||||
"System only has {{.size}}MiB available, less than the required {{.req}}MiB for Kubernetes": "",
|
"System only has {{.size}}MiB available, less than the required {{.req}}MiB for Kubernetes": "",
|
||||||
"Tag images": "イメージのタグ付与",
|
"Tag images": "イメージのタグ付与",
|
||||||
"Tag to apply to the new image (optional)": "新しいイメージに適用するタグ (任意)",
|
"Tag to apply to the new image (optional)": "新しいイメージに適用するタグ (任意)",
|
||||||
|
"Target \u003cremote file path\u003e must be an absolute Path. Relative Path is not allowed (example: \"minikube:/home/docker/copied.txt\")": "",
|
||||||
"Target directory {{.path}} must be an absolute path": "ターゲットディレクトリ {{.path}} は絶対パスでなければなりません。",
|
"Target directory {{.path}} must be an absolute path": "ターゲットディレクトリ {{.path}} は絶対パスでなければなりません。",
|
||||||
"Target {{.path}} can not be empty": "ターゲット {{.path}} は空にできません",
|
"Target {{.path}} can not be empty": "ターゲット {{.path}} は空にできません",
|
||||||
"Test docs have been saved at - {{.path}}": "テストドキュメントは {{.path}} に保存されました",
|
"Test docs have been saved at - {{.path}}": "テストドキュメントは {{.path}} に保存されました",
|
||||||
|
@ -1001,4 +1003,4 @@
|
||||||
"{{.profile}} profile is not valid: {{.err}}": "",
|
"{{.profile}} profile is not valid: {{.err}}": "",
|
||||||
"{{.type}} is not yet a supported filesystem. We will try anyways!": "{{.type}} はまだサポートされていなファイルシステムです。とにかくやってみます!",
|
"{{.type}} is not yet a supported filesystem. We will try anyways!": "{{.type}} はまだサポートされていなファイルシステムです。とにかくやってみます!",
|
||||||
"{{.url}} is not accessible: {{.error}}": "{{.url}} はアクセス可能ではありません。 {{.error}}"
|
"{{.url}} is not accessible: {{.error}}": "{{.url}} はアクセス可能ではありません。 {{.error}}"
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
"--kvm-numa-count range is 1-8": "--kvm-numa-count 범위는 1부터 8입니다",
|
"--kvm-numa-count range is 1-8": "--kvm-numa-count 범위는 1부터 8입니다",
|
||||||
"--network flag is only valid with the docker/podman and KVM drivers, it will be ignored": "",
|
"--network flag is only valid with the docker/podman and KVM drivers, it will be ignored": "",
|
||||||
"127.0.0.1": "",
|
"127.0.0.1": "",
|
||||||
"\u003ctarget file absolute path\u003e must be an absolute Path. Relative Path is not allowed (example: \"/home/docker/copied.txt\")": "",
|
|
||||||
"==\u003e Audit \u003c==": "",
|
"==\u003e Audit \u003c==": "",
|
||||||
"==\u003e Last Start \u003c==": "",
|
"==\u003e Last Start \u003c==": "",
|
||||||
"A VPN or firewall is interfering with HTTP access to the minikube VM. Alternatively, try a different VM driver: https://minikube.sigs.k8s.io/docs/start/": "",
|
"A VPN or firewall is interfering with HTTP access to the minikube VM. Alternatively, try a different VM driver: https://minikube.sigs.k8s.io/docs/start/": "",
|
||||||
|
@ -619,6 +618,7 @@
|
||||||
"System only has {{.size}}MiB available, less than the required {{.req}}MiB for Kubernetes": "",
|
"System only has {{.size}}MiB available, less than the required {{.req}}MiB for Kubernetes": "",
|
||||||
"Tag images": "",
|
"Tag images": "",
|
||||||
"Tag to apply to the new image (optional)": "",
|
"Tag to apply to the new image (optional)": "",
|
||||||
|
"Target \u003cremote file path\u003e must be an absolute Path. Relative Path is not allowed (example: \"minikube:/home/docker/copied.txt\")": "",
|
||||||
"Target directory {{.path}} must be an absolute path": "타겟 폴더 {{.path}} 는 절대 경로여야 합니다",
|
"Target directory {{.path}} must be an absolute path": "타겟 폴더 {{.path}} 는 절대 경로여야 합니다",
|
||||||
"Target {{.path}} can not be empty": "",
|
"Target {{.path}} can not be empty": "",
|
||||||
"Test docs have been saved at - {{.path}}": "",
|
"Test docs have been saved at - {{.path}}": "",
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
"--kvm-numa-count range is 1-8": "",
|
"--kvm-numa-count range is 1-8": "",
|
||||||
"--network flag is only valid with the docker/podman and KVM drivers, it will be ignored": "",
|
"--network flag is only valid with the docker/podman and KVM drivers, it will be ignored": "",
|
||||||
"127.0.0.1": "",
|
"127.0.0.1": "",
|
||||||
"\u003ctarget file absolute path\u003e must be an absolute Path. Relative Path is not allowed (example: \"/home/docker/copied.txt\")": "",
|
|
||||||
"==\u003e Audit \u003c==": "==\u003e Audyt \u003c==",
|
"==\u003e Audit \u003c==": "==\u003e Audyt \u003c==",
|
||||||
"==\u003e Last Start \u003c==": "==\u003e Ostatni start \u003c==",
|
"==\u003e Last Start \u003c==": "==\u003e Ostatni start \u003c==",
|
||||||
"A VPN or firewall is interfering with HTTP access to the minikube VM. Alternatively, try a different VM driver: https://minikube.sigs.k8s.io/docs/start/": "VPN lub zapora sieciowa przeszkadza w komunikacji protokołem HTTP z maszyną wirtualną minikube. Spróbuj użyć innego sterownika: https://minikube.sigs.k8s.io/docs/start/",
|
"A VPN or firewall is interfering with HTTP access to the minikube VM. Alternatively, try a different VM driver: https://minikube.sigs.k8s.io/docs/start/": "VPN lub zapora sieciowa przeszkadza w komunikacji protokołem HTTP z maszyną wirtualną minikube. Spróbuj użyć innego sterownika: https://minikube.sigs.k8s.io/docs/start/",
|
||||||
|
@ -619,6 +618,7 @@
|
||||||
"System only has {{.size}}MiB available, less than the required {{.req}}MiB for Kubernetes": "",
|
"System only has {{.size}}MiB available, less than the required {{.req}}MiB for Kubernetes": "",
|
||||||
"Tag images": "",
|
"Tag images": "",
|
||||||
"Tag to apply to the new image (optional)": "",
|
"Tag to apply to the new image (optional)": "",
|
||||||
|
"Target \u003cremote file path\u003e must be an absolute Path. Relative Path is not allowed (example: \"minikube:/home/docker/copied.txt\")": "",
|
||||||
"Target directory {{.path}} must be an absolute path": "",
|
"Target directory {{.path}} must be an absolute path": "",
|
||||||
"Target {{.path}} can not be empty": "",
|
"Target {{.path}} can not be empty": "",
|
||||||
"Test docs have been saved at - {{.path}}": "",
|
"Test docs have been saved at - {{.path}}": "",
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
"--kvm-numa-count range is 1-8": "",
|
"--kvm-numa-count range is 1-8": "",
|
||||||
"--network flag is only valid with the docker/podman and KVM drivers, it will be ignored": "",
|
"--network flag is only valid with the docker/podman and KVM drivers, it will be ignored": "",
|
||||||
"127.0.0.1": "",
|
"127.0.0.1": "",
|
||||||
"\u003ctarget file absolute path\u003e must be an absolute Path. Relative Path is not allowed (example: \"/home/docker/copied.txt\")": "",
|
|
||||||
"==\u003e Audit \u003c==": "",
|
"==\u003e Audit \u003c==": "",
|
||||||
"==\u003e Last Start \u003c==": "",
|
"==\u003e Last Start \u003c==": "",
|
||||||
"A VPN or firewall is interfering with HTTP access to the minikube VM. Alternatively, try a different VM driver: https://minikube.sigs.k8s.io/docs/start/": "",
|
"A VPN or firewall is interfering with HTTP access to the minikube VM. Alternatively, try a different VM driver: https://minikube.sigs.k8s.io/docs/start/": "",
|
||||||
|
@ -567,6 +566,7 @@
|
||||||
"System only has {{.size}}MiB available, less than the required {{.req}}MiB for Kubernetes": "",
|
"System only has {{.size}}MiB available, less than the required {{.req}}MiB for Kubernetes": "",
|
||||||
"Tag images": "",
|
"Tag images": "",
|
||||||
"Tag to apply to the new image (optional)": "",
|
"Tag to apply to the new image (optional)": "",
|
||||||
|
"Target \u003cremote file path\u003e must be an absolute Path. Relative Path is not allowed (example: \"minikube:/home/docker/copied.txt\")": "",
|
||||||
"Target directory {{.path}} must be an absolute path": "",
|
"Target directory {{.path}} must be an absolute path": "",
|
||||||
"Target {{.path}} can not be empty": "",
|
"Target {{.path}} can not be empty": "",
|
||||||
"Test docs have been saved at - {{.path}}": "",
|
"Test docs have been saved at - {{.path}}": "",
|
||||||
|
|
|
@ -25,7 +25,6 @@
|
||||||
"--kvm-numa-count range is 1-8": "",
|
"--kvm-numa-count range is 1-8": "",
|
||||||
"--network flag is only valid with the docker/podman and KVM drivers, it will be ignored": "",
|
"--network flag is only valid with the docker/podman and KVM drivers, it will be ignored": "",
|
||||||
"127.0.0.1": "",
|
"127.0.0.1": "",
|
||||||
"\u003ctarget file absolute path\u003e must be an absolute Path. Relative Path is not allowed (example: \"/home/docker/copied.txt\")": "",
|
|
||||||
"==\u003e Audit \u003c==": "",
|
"==\u003e Audit \u003c==": "",
|
||||||
"==\u003e Last Start \u003c==": "",
|
"==\u003e Last Start \u003c==": "",
|
||||||
"A VPN or firewall is interfering with HTTP access to the minikube VM. Alternatively, try a different VM driver: https://minikube.sigs.k8s.io/docs/start/": "VPN 或者防火墙正在干扰对 minikube 虚拟机的 HTTP 访问。或者,您可以使用其它的虚拟机驱动:https://minikube.sigs.k8s.io/docs/start/",
|
"A VPN or firewall is interfering with HTTP access to the minikube VM. Alternatively, try a different VM driver: https://minikube.sigs.k8s.io/docs/start/": "VPN 或者防火墙正在干扰对 minikube 虚拟机的 HTTP 访问。或者,您可以使用其它的虚拟机驱动:https://minikube.sigs.k8s.io/docs/start/",
|
||||||
|
@ -703,6 +702,7 @@
|
||||||
"System only has {{.size}}MiB available, less than the required {{.req}}MiB for Kubernetes": "",
|
"System only has {{.size}}MiB available, less than the required {{.req}}MiB for Kubernetes": "",
|
||||||
"Tag images": "",
|
"Tag images": "",
|
||||||
"Tag to apply to the new image (optional)": "",
|
"Tag to apply to the new image (optional)": "",
|
||||||
|
"Target \u003cremote file path\u003e must be an absolute Path. Relative Path is not allowed (example: \"minikube:/home/docker/copied.txt\")": "",
|
||||||
"Target directory {{.path}} must be an absolute path": "",
|
"Target directory {{.path}} must be an absolute path": "",
|
||||||
"Target {{.path}} can not be empty": "",
|
"Target {{.path}} can not be empty": "",
|
||||||
"Test docs have been saved at - {{.path}}": "",
|
"Test docs have been saved at - {{.path}}": "",
|
||||||
|
|
Loading…
Reference in New Issue