Skip to content

Commit

Permalink
Merge pull request #10198 from anencore94/minikube-scp
Browse files Browse the repository at this point in the history
Add minikube cp command as new feature
  • Loading branch information
medyagh authored Mar 23, 2021
2 parents a05f887 + 109b1e0 commit 3e869e0
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 0 deletions.
93 changes: 93 additions & 0 deletions cmd/minikube/cmd/cp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
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 (
"github.com/pkg/errors"
"github.com/spf13/cobra"

"os"
pt "path"
"path/filepath"

"k8s.io/minikube/pkg/minikube/assets"
"k8s.io/minikube/pkg/minikube/exit"
"k8s.io/minikube/pkg/minikube/mustload"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
)

// placeholders for flag values
var (
srcPath string
dstPath string
)

// cpCmd represents the cp command, similar to docker cp
var cpCmd = &cobra.Command{
Use: "cp <source file path> <target file absolute path>",
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" +
"Example Command : \"minikube cp a.txt /home/docker/b.txt\"\n",
Run: func(cmd *cobra.Command, args []string) {
if len(args) != 2 {
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")`)
}

srcPath = args[0]
dstPath = args[1]
validateArgs(srcPath, dstPath)

co := mustload.Running(ClusterFlagValue())
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)
}

if err = co.CP.Runner.Copy(fa); err != nil {
out.ErrLn("%v", errors.Wrap(err, "copying file"))
os.Exit(1)
}
},
}

func init() {
}

func validateArgs(srcPath string, dstPath string) {
if srcPath == "" {
exit.Message(reason.Usage, "Source {{.path}} can not be empty", out.V{"path": srcPath})
}

if dstPath == "" {
exit.Message(reason.Usage, "Target {{.path}} can not be empty", out.V{"path": dstPath})
}

if _, err := os.Stat(srcPath); err != nil {
if os.IsNotExist(err) {
exit.Message(reason.HostPathMissing, "Cannot find directory {{.path}} for copy", out.V{"path": srcPath})
} else {
exit.Error(reason.HostPathStat, "stat failed", err)
}
}

if !filepath.IsAbs(dstPath) {
exit.Message(reason.Usage, `<target file absolute path> must be an absolute Path. Relative Path is not allowed (example: "/home/docker/copied.txt")`)
}
}
1 change: 1 addition & 0 deletions cmd/minikube/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ func init() {
sshCmd,
kubectlCmd,
nodeCmd,
cpCmd,
},
},
{
Expand Down
43 changes: 43 additions & 0 deletions site/content/en/docs/commands/cp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
title: "cp"
description: >
Copy the specified file into minikube
---


## minikube cp

Copy the specified file into minikube

### Synopsis

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"


```shell
minikube cp <source file path> <target file absolute path> [flags]
```

### Options inherited from parent commands

```
--add_dir_header If true, adds the file directory to the header of the log messages
--alsologtostderr log to standard error as well as files
-b, --bootstrapper string The name of the cluster bootstrapper that will set up the Kubernetes cluster. (default "kubeadm")
-h, --help
--log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0)
--log_dir string If non-empty, write log files in this directory
--log_file string If non-empty, use this log file
--log_file_max_size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800)
--logtostderr log to standard error instead of files
--one_output If true, only write logs to their native severity level (vs also writing to each lower severity level
-p, --profile string The name of the minikube VM being used. This can be set to allow having multiple instances of minikube independently. (default "minikube")
--skip_headers If true, avoid header prefixes in the log messages
--skip_log_headers If true, avoid headers when opening log files
--stderrthreshold severity logs at or above this threshold go to stderr (default 2)
--user string Specifies the user executing the operation. Useful for auditing operations executed by 3rd party tools. Defaults to the operating system username.
-v, --v Level number for the log level verbosity
--vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging
```

29 changes: 29 additions & 0 deletions test/integration/functional_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ func TestFunctional(t *testing.T) {
{"PersistentVolumeClaim", validatePersistentVolumeClaim},
{"TunnelCmd", validateTunnelCmd},
{"SSHCmd", validateSSHCmd},
{"CpCmd", validateCpCmd},
{"MySQL", validateMySQL},
{"FileSync", validateFileSync},
{"CertSync", validateCertSync},
Expand Down Expand Up @@ -1176,6 +1177,34 @@ func validateSSHCmd(ctx context.Context, t *testing.T, profile string) {
}
}

// validateCpCmd asserts basic "cp" command functionality
func validateCpCmd(ctx context.Context, t *testing.T, profile string) {
if NoneDriver() {
t.Skipf("skipping: cp is unsupported by none driver")
}

cpPath := filepath.Join(*testdataDir, "cp-test.txt")
rr, err := Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "cp", cpPath, "/home/docker/hello_cp.txt"))
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 := "Test file for checking file cp process"
rr, err = Run(t, exec.CommandContext(ctx, Target(), "-p", profile, "ssh", "cat /home/docker/hello_cp.txt"))
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)
}
if diff := cmp.Diff(expected, rr.Stdout.String()); diff != "" {
t.Errorf("/testdata/cp-test.txt content mismatch (-want +got):\n%s", diff)
}
}

// validateMySQL validates a minimalist MySQL deployment
func validateMySQL(ctx context.Context, t *testing.T, profile string) {
if arm64Platform() {
Expand Down
1 change: 1 addition & 0 deletions test/integration/testdata/cp-test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Test file for checking file cp process

0 comments on commit 3e869e0

Please sign in to comment.