Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tests: basic implementation for testing against vfkit #427

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,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 Expand Up @@ -59,6 +59,36 @@ var _ = ginkgo.Describe("port forwarding", func() {
gomega.Expect(string(out)).To(gomega.ContainSubstring("Hello from the host"))
})

ginkgo.It("should reach a http server on the host", func() {
ln, err := net.Listen("tcp", "127.0.0.1:9090")
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
defer ln.Close()

mux := http.NewServeMux()
mux.HandleFunc("/", func(writer http.ResponseWriter, _ *http.Request) {
_, _ = writer.Write([]byte("Hello from the host"))
})
go func() {
s := &http.Server{
Handler: mux,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
err := s.Serve(ln)
if err != nil {
log.Error(err)
}
}()

out, err := sshExec("curl http://host.containers.internal:9090")
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
gomega.Expect(string(out)).To(gomega.ContainSubstring("Hello from the host"))

out, err = sshExec("curl http://host.docker.internal:9090")
gomega.Expect(err).ShouldNot(gomega.HaveOccurred())
gomega.Expect(string(out)).To(gomega.ContainSubstring("Hello from the host"))
})

ginkgo.It("should reach a http server in the VM using dynamic port forwarding", func() {
_, err := net.Dial("tcp", "127.0.0.1:9090")
gomega.Expect(err).Should(gomega.HaveOccurred())
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
33 changes: 19 additions & 14 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,12 +62,13 @@ func (downloader *FcosDownload) DownloadImage() (string, error) {
}
}

uncompressedImage := ""
if uncompressedImage, err = Decompress(compressedImage); err != nil {
return "", err
}
if _, err := os.Stat(uncompressedImage); err == nil {
return uncompressedImage, nil
}
if err := Decompress(compressedImage, uncompressedImage); err != nil {
return "", err
}
return uncompressedImage, nil
}

Expand Down Expand Up @@ -91,7 +96,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 +112,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 +132,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 (
"fmt"
"net"
)

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

func IsHostPortAvailable(host string, port int) bool {
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", host, port))
if err != nil {
return false
}
listener.Close()
return true
}
Loading
Loading