Skip to content

Commit

Permalink
Merge pull request #10148 from alromeros/port-forward-vmexport
Browse files Browse the repository at this point in the history
Add port-forward functionalities to virtctl vmexport
  • Loading branch information
kubevirt-bot committed Aug 17, 2023
2 parents 7765946 + 6f2d4c2 commit c709e13
Show file tree
Hide file tree
Showing 17 changed files with 921 additions and 102 deletions.
13 changes: 13 additions & 0 deletions pkg/virtctl/memorydump/memorydump.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ const (
createClaimArg = "create-claim"
storageClassArg = "storage-class"
accessModeArg = "access-mode"
portForwardArg = "port-forward"
localPortArg = "local-port"

configName = "config"
filesystemOverhead = cdiv1.Percent("0.055")
Expand All @@ -61,6 +63,8 @@ const (
var (
claimName string
createClaim bool
portForward bool
localPort string
storageClass string
accessMode string
outputFile string
Expand Down Expand Up @@ -113,6 +117,8 @@ func NewMemoryDumpCommand(clientConfig clientcmd.ClientConfig) *cobra.Command {
cmd.SetUsageTemplate(templates.UsageTemplate())
cmd.Flags().StringVar(&claimName, claimNameArg, "", "pvc name to contain the memory dump")
cmd.Flags().BoolVar(&createClaim, createClaimArg, false, "Create the pvc that will conatin the memory dump")
cmd.Flags().BoolVar(&portForward, portForwardArg, false, "Configure and set port-forward in a random port to download the memory dump")
cmd.Flags().StringVar(&localPort, localPortArg, "0", "Specify port for port-forward")
cmd.Flags().StringVar(&storageClass, storageClassArg, "", "The storage class for the PVC.")
cmd.Flags().StringVar(&accessMode, accessModeArg, "", "The access mode for the PVC.")
cmd.Flags().StringVar(&outputFile, "output", "", "Specifies the output path of the memory dump to be downloaded.")
Expand Down Expand Up @@ -327,7 +333,14 @@ func downloadMemoryDump(namespace, vmName string, virtClient kubecli.KubevirtCli
Namespace: namespace,
Name: vmexportName,
ExportSource: exportSource,
PortForward: portForward,
LocalPort: localPort,
}

if portForward {
vmExportInfo.ServiceURL = fmt.Sprintf("127.0.0.1:%s", localPort)
}

// User wants the output in a file, create
output, err := os.Create(vmExportInfo.OutputFile)
if err != nil {
Expand Down
42 changes: 42 additions & 0 deletions pkg/virtctl/memorydump/memorydump_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package memorydump_test
import (
"context"
"fmt"
"io"
"net/http"
"net/http/httptest"
"strings"
"sync"
"time"

Expand Down Expand Up @@ -363,6 +365,16 @@ var _ = Describe("MemoryDump", func() {
vmexport.SetHTTPClientCreator(func(*http.Transport, bool) *http.Client {
return server.Client()
})
vmexport.SetPortForwarder(func(client kubecli.KubevirtClient, pod k8sv1.Pod, namespace string, ports []string, stopChan, readyChan chan struct{}, portChan chan uint16) error {
readyChan <- struct{}{}
portChan <- uint16(5432)
return nil
})
})

AfterEach(func() {
vmexport.SetDefaultPortForwarder()
vmexport.SetDefaultHTTPClientCreator()
})

It("should get memory dump and call download memory dump", func() {
Expand Down Expand Up @@ -401,6 +413,36 @@ var _ = Describe("MemoryDump", func() {
Expect(cmd.Execute()).To(Succeed())
})

DescribeTable("should call download memory dump with port-forward", func(commandAndArgs []string) {
vmexport.HandleHTTPRequest = func(client kubecli.KubevirtClient, vmexport *exportv1.VirtualMachineExport, downloadUrl string, insecure bool, exportURL string, headers map[string]string) (*http.Response, error) {
Expect(downloadUrl).To(Equal("https://127.0.0.1:5432"))
resp := http.Response{
StatusCode: http.StatusOK,
Body: io.NopCloser(strings.NewReader("data")),
}
return &resp, nil
}
memorydump.WaitMemoryDumpComplete = waitForMemoryDumpDefault
vme := utils.VMExportSpecPVC(vmexportName, k8smetav1.NamespaceDefault, claimName, secretName)
vme.Status = utils.GetVMEStatus([]exportv1.VirtualMachineExportVolume{
{
Name: claimName,
Formats: utils.GetExportVolumeFormat(server.URL, exportv1.KubeVirtGz),
},
}, secretName)
vme.Status.Links.Internal = vme.Status.Links.External
utils.HandleSecretGet(coreClient, secretName)
utils.HandleVMExportCreate(vmExportClient, vme)
utils.HandleServiceGet(coreClient, fmt.Sprintf("virt-export-%s", vme.Name), 443)
utils.HandlePodList(coreClient, fmt.Sprintf("virt-export-pod-%s", vme.Name))
cmd := clientcmd.NewVirtctlCommand(commandAndArgs...)
Expect(cmd.Execute()).To(Succeed())
},
Entry("with default port-forward", []string{"memory-dump", "download", "testvm", outputFileFlag, "--port-forward"}),
Entry("with port-forward specifying local port", []string{"memory-dump", "download", "testvm", outputFileFlag, "--port-forward", "--local-port", "5432"}),
Entry("with port-forward specifying default number on local port", []string{"memory-dump", "download", "testvm", outputFileFlag, "--port-forward", "--local-port", "0"}),
)

It("should fail download memory dump if not completed succesfully", func() {
memorydump.WaitMemoryDumpComplete = waitForMemoryDumpErr

Expand Down
56 changes: 56 additions & 0 deletions pkg/virtctl/utils/test_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,62 @@ func HandleSecretGet(k8sClient *fakek8sclient.Clientset, secretName string) {
})
}

func HandleServiceGet(k8sClient *fakek8sclient.Clientset, serviceName string, port int) {
k8sClient.Fake.PrependReactor("get", "services", func(action testing.Action) (handled bool, obj runtime.Object, err error) {
get, ok := action.(testing.GetAction)
Expect(ok).To(BeTrue())
Expect(get.GetNamespace()).To(Equal(k8smetav1.NamespaceDefault))
Expect(get.GetName()).To(Equal(serviceName))
return true, &v1.Service{
ObjectMeta: k8smetav1.ObjectMeta{
Name: serviceName,
Namespace: k8smetav1.NamespaceDefault,
},
Spec: v1.ServiceSpec{
Ports: []v1.ServicePort{
{
Name: "export",
Port: int32(port),
},
},
},
}, nil
})
}

func HandlePodList(k8sClient *fakek8sclient.Clientset, podName string) {
podList := &v1.PodList{
ListMeta: k8smetav1.ListMeta{
ResourceVersion: "1",
},
Items: []v1.Pod{
{
ObjectMeta: k8smetav1.ObjectMeta{
Name: podName,
Namespace: k8smetav1.NamespaceDefault,
},
Spec: v1.PodSpec{},
},
},
}

k8sClient.Fake.PrependReactor("list", "pods", func(action testing.Action) (handled bool, obj runtime.Object, err error) {
list, ok := action.(testing.ListAction)
Expect(ok).To(BeTrue())
Expect(list.GetNamespace()).To(Equal(k8smetav1.NamespaceDefault))
return true, podList, nil
})
}

func HandleVMExportDelete(client *kubevirtfake.Clientset, name string) {
client.Fake.PrependReactor("delete", "virtualmachineexports", func(action testing.Action) (handled bool, obj runtime.Object, err error) {
delete, ok := action.(testing.DeleteAction)
Expect(ok).To(BeTrue())
Expect(delete.GetName()).To(Equal(name))
return true, nil, nil
})
}

func GetExportVolumeFormat(url string, format exportv1.ExportVolumeFormat) []exportv1.VirtualMachineExportVolumeFormat {
return []exportv1.VirtualMachineExportVolumeFormat{
{
Expand Down
4 changes: 4 additions & 0 deletions pkg/virtctl/vmexport/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@ go_library(
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
"//vendor/k8s.io/client-go/tools/portforward:go_default_library",
"//vendor/k8s.io/client-go/transport/spdy:go_default_library",
"//vendor/k8s.io/kubectl/pkg/util:go_default_library",
],
)

Expand Down

0 comments on commit c709e13

Please sign in to comment.