Skip to content

Commit

Permalink
Merge pull request #427 from lstocchi/test-vfkit
Browse files Browse the repository at this point in the history
Tests: basic implementation for testing against vfkit
  • Loading branch information
openshift-merge-bot[bot] authored Jan 9, 2025
2 parents 5f09250 + e897a4a commit fe2f2d9
Show file tree
Hide file tree
Showing 16 changed files with 454 additions and 107 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ jobs:

- name: Test
run: |
sudo -s -u ${USER} bash -c 'make test'
sudo -s -u ${USER} bash -c 'make test-linux'
- uses: actions/upload-artifact@v4
if: always()
Expand Down
10 changes: 7 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ cross: $(TOOLS_BINDIR)/makefat
test-companion:
GOOS=linux go build -ldflags "$(LDFLAGS)" -o bin/test-companion ./cmd/test-companion

.PHONY: test
test: gvproxy test-companion
go test -timeout 20m -v ./...
.PHONY: test-linux
test-linux: gvproxy test-companion
go test -timeout 20m -v ./test-qemu

.PHONY: test-mac
test-mac: gvproxy
go test -timeout 20m -v ./test-vfkit
45 changes: 45 additions & 0 deletions test-qemu/basic_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package e2eqemu

import (
"github.com/containers/gvisor-tap-vsock/pkg/types"
e2e "github.com/containers/gvisor-tap-vsock/test"
"github.com/onsi/ginkgo"
"github.com/onsi/gomega"
)

var _ = ginkgo.Describe("connectivity with qemu", func() {
e2e.BasicConnectivityTests(e2e.BasicTestProps{
SSHExec: sshExec,
})
})

var _ = ginkgo.Describe("dns with qemu", func() {
e2e.BasicDNSTests(e2e.BasicTestProps{
SSHExec: sshExec,
Sock: sock,
})
})

var _ = ginkgo.Describe("command-line format", func() {
ginkgo.It("should convert Command to command line format", func() {
command := types.NewGvproxyCommand()
command.AddEndpoint("unix:///tmp/network.sock")
command.Debug = true
command.AddQemuSocket("tcp://0.0.0.0:1234")
command.PidFile = "~/gv-pidfile.txt"
command.LogFile = "~/gv.log"
command.AddForwardUser("demouser")

cmd := command.ToCmdline()
gomega.Expect(cmd).To(gomega.Equal([]string{
"-listen", "unix:///tmp/network.sock",
"-debug",
"-mtu", "1500",
"-ssh-port", "2222",
"-listen-qemu", "tcp://0.0.0.0:1234",
"-forward-user", "demouser",
"-pid-file", "~/gv-pidfile.txt",
"-log-file", "~/gv.log",
}))
})
})
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package e2e
package e2eqemu

import (
"fmt"
Expand Down
2 changes: 1 addition & 1 deletion test/efi_generic.go → test-qemu/efi_generic.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//go:build !(darwin && arm64)

package e2e
package e2eqemu

func efiArgs() (string, error) {
return "", nil
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package e2e
package e2eqemu

import (
"context"
Expand Down
35 changes: 8 additions & 27 deletions test/suite_test.go → test-qemu/suite_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package e2e
package e2eqemu

import (
"flag"
Expand All @@ -13,9 +13,10 @@ import (
"testing"
"time"

e2e_utils "github.com/containers/gvisor-tap-vsock/test-utils"

"github.com/onsi/ginkgo"
"github.com/onsi/gomega"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)

Expand Down Expand Up @@ -63,15 +64,15 @@ func init() {
var _ = ginkgo.BeforeSuite(func() {
gomega.Expect(os.MkdirAll(filepath.Join(tmpDir, "disks"), os.ModePerm)).Should(gomega.Succeed())

downloader, err := NewFcosDownloader(filepath.Join(tmpDir, "disks"))
downloader, err := e2e_utils.NewFcosDownloader(filepath.Join(tmpDir, "disks"))
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
qemuImage, err := downloader.DownloadImage()
qemuImage, err := downloader.DownloadImage("qemu", "qcow2.xz")
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())

publicKey, err := createSSHKeys()
publicKey, err := e2e_utils.CreateSSHKeys(publicKeyFile, privateKeyFile)
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())

err = CreateIgnition(ignFile, publicKey, ignitionUser, ignitionPasswordHash)
err = e2e_utils.CreateIgnition(ignFile, publicKey, ignitionUser, ignitionPasswordHash)
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())

outer:
Expand Down Expand Up @@ -154,7 +155,7 @@ outer:
})

func qemuExecutable() string {
qemuBinaries := []string{"qemu-kvm", fmt.Sprintf("qemu-system-%s", coreosArch())}
qemuBinaries := []string{"qemu-kvm", fmt.Sprintf("qemu-system-%s", e2e_utils.CoreosArch())}
for _, binary := range qemuBinaries {
path, err := exec.LookPath(binary)
if err == nil && path != "" {
Expand Down Expand Up @@ -182,26 +183,6 @@ func qemuArgs() string {
return fmt.Sprintf("-machine %s,accel=%s:tcg -smp 4 -cpu host ", machine, accel)
}

func createSSHKeys() (string, error) {
_ = os.Remove(publicKeyFile)
_ = os.Remove(privateKeyFile)
err := exec.Command("ssh-keygen", "-N", "", "-t", "ed25519", "-f", privateKeyFile).Run()
if err != nil {
return "", errors.Wrap(err, "Could not generate ssh keys")
}

return readPublicKey()
}

func readPublicKey() (string, error) {
publicKey, err := os.ReadFile(publicKeyFile)
if err != nil {
return "", nil
}

return strings.TrimSpace(string(publicKey)), nil
}

func scp(src, dst string) error {
sshCmd := exec.Command("scp",
"-o", "UserKnownHostsFile=/dev/null",
Expand Down
32 changes: 17 additions & 15 deletions test/fcos.go → test-utils/fcos.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package e2e
package e2eutils

import (
"os"
Expand Down Expand Up @@ -27,6 +27,11 @@ type fcosDownloadInfo struct {
Sha256Sum string
}

type ArtifactFormat struct {
Artifact string
Format string
}

func NewFcosDownloader(dataDir string) (*FcosDownload, error) {
return &FcosDownload{
DataDir: dataDir,
Expand All @@ -38,14 +43,13 @@ func imageName(info *fcosDownloadInfo) string {
return urlSplit[len(urlSplit)-1]
}

func (downloader *FcosDownload) DownloadImage() (string, error) {
info, err := getFCOSDownload()
func (downloader *FcosDownload) DownloadImage(artifactType string, formatType string) (string, error) {
info, err := getFCOSDownload(artifactType, formatType)
if err != nil {
return "", err
}

compressedImage := filepath.Join(downloader.DataDir, imageName(info))
uncompressedImage := strings.TrimSuffix(filepath.Join(filepath.Dir(compressedImage), imageName(info)), ".xz")

// check if the latest image is already present
ok, err := downloader.updateAvailable(info, compressedImage)
Expand All @@ -58,10 +62,8 @@ func (downloader *FcosDownload) DownloadImage() (string, error) {
}
}

if _, err := os.Stat(uncompressedImage); err == nil {
return uncompressedImage, nil
}
if err := Decompress(compressedImage, uncompressedImage); err != nil {
uncompressedImage := ""
if uncompressedImage, err = Decompress(compressedImage); err != nil {
return "", err
}
return uncompressedImage, nil
Expand Down Expand Up @@ -91,7 +93,7 @@ func (downloader *FcosDownload) updateAvailable(info *fcosDownloadInfo, compress

// as of 2024-05-28, these are the 4 architectures available in
// curl https://builds.coreos.fedoraproject.org/streams/next.json
func coreosArch() string {
func CoreosArch() string {
switch runtime.GOARCH {
case "amd64":
return "x86_64"
Expand All @@ -107,7 +109,7 @@ func coreosArch() string {

// This should get Exported and stay put as it will apply to all fcos downloads
// getFCOS parses fedoraCoreOS's stream and returns the image download URL and the release version
func getFCOSDownload() (*fcosDownloadInfo, error) {
func getFCOSDownload(artifactType string, formatType string) (*fcosDownloadInfo, error) {
streamurl := fedoracoreos.GetStreamURL(fedoracoreos.StreamNext)
resp, err := http.Get(streamurl.String())
if err != nil {
Expand All @@ -127,27 +129,27 @@ func getFCOSDownload() (*fcosDownloadInfo, error) {
if err := json.Unmarshal(body, &fcosstable); err != nil {
return nil, err
}
arch, ok := fcosstable.Architectures[coreosArch()]
arch, ok := fcosstable.Architectures[CoreosArch()]
if !ok {
return nil, fmt.Errorf("unable to pull VM image: no targetArch in stream")
}
artifacts := arch.Artifacts
if artifacts == nil {
return nil, fmt.Errorf("unable to pull VM image: no artifact in stream")
}
qemu, ok := artifacts["qemu"]
artifact, ok := artifacts[artifactType]
if !ok {
return nil, fmt.Errorf("unable to pull VM image: no qemu artifact in stream")
}
formats := qemu.Formats
formats := artifact.Formats
if formats == nil {
return nil, fmt.Errorf("unable to pull VM image: no formats in stream")
}
qcow, ok := formats["qcow2.xz"]
format, ok := formats[formatType]
if !ok {
return nil, fmt.Errorf("unable to pull VM image: no qcow2.xz format in stream")
}
disk := qcow.Disk
disk := format.Disk
if disk == nil {
return nil, fmt.Errorf("unable to pull VM image: no disk in stream")
}
Expand Down
2 changes: 1 addition & 1 deletion test/ignition.go → test-utils/ignition.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package e2e
package e2eutils

import (
"encoding/json"
Expand Down
2 changes: 1 addition & 1 deletion test/ignition_schema.go → test-utils/ignition_schema.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package e2e
package e2eutils

// Taken from https://github.com/coreos/ignition/blob/master/config/v3_2/types/schema.go

Expand Down
19 changes: 19 additions & 0 deletions test-utils/port.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package e2eutils

import (
"net"
"strconv"
)

func IsPortAvailable(port int) bool {
return IsHostPortAvailable("127.0.0.1", port)
}

func IsHostPortAvailable(host string, port int) bool {
listener, err := net.Listen("tcp", net.JoinHostPort(host, strconv.Itoa(port)))
if err != nil {
return false
}
listener.Close()
return true
}
61 changes: 54 additions & 7 deletions test/pull.go → test-utils/pull.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package e2e
package e2eutils

import (
"compress/gzip"
"fmt"
"io"
"net/http"
Expand Down Expand Up @@ -48,18 +49,37 @@ func DownloadVMImage(downloadURL string, localImagePath string) error {
return nil
}

func Decompress(localPath, uncompressedPath string) error {
func Decompress(localPath string) (string, error) {
uncompressedPath := ""
if strings.HasSuffix(localPath, ".xz") {
uncompressedPath = strings.TrimSuffix(localPath, ".xz")
} else if strings.HasSuffix(localPath, ".gz") {
uncompressedPath = strings.TrimSuffix(localPath, ".gz")
}

if uncompressedPath == "" {
return "", fmt.Errorf("unsupported compression for %s", localPath)
}

// we remove the uncompressed file if already exists. Maybe it has been used earlier and can affect the tests result
os.Remove(uncompressedPath)

uncompressedFileWriter, err := os.OpenFile(uncompressedPath, os.O_CREATE|os.O_RDWR, 0600)
if err != nil {
return err
return "", err
}

if !strings.HasSuffix(localPath, ".xz") {
return fmt.Errorf("unsupported compression for %s", localPath)
fmt.Printf("Extracting %s\n", localPath)
if strings.HasSuffix(localPath, ".xz") {
err = decompressXZ(localPath, uncompressedFileWriter)
} else {
err = decompressGZ(localPath, uncompressedFileWriter)
}

fmt.Printf("Extracting %s\n", localPath)
return decompressXZ(localPath, uncompressedFileWriter)
if err != nil {
return "", err
}
return uncompressedPath, nil
}

// Will error out if file without .xz already exists
Expand All @@ -79,3 +99,30 @@ func decompressXZ(src string, output io.Writer) error {
}()
return cmd.Run()
}

func decompressGZ(src string, output io.Writer) error {
file, err := os.Open(src)
if err != nil {
return err
}
defer file.Close()

// Create a gzip reader
reader, err := gzip.NewReader(file)
if err != nil {
return err
}
defer reader.Close()

for {
_, err := io.CopyN(output, reader, 1024)
if err != nil {
if err == io.EOF {
break
}
return err
}
}

return nil
}
Loading

0 comments on commit fe2f2d9

Please sign in to comment.