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

test/functional: Add flag for container layer paths, remove containerd dependence #1536

Merged
merged 4 commits into from
Nov 15, 2022
Merged
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
7 changes: 2 additions & 5 deletions test/functional/lcow_container_bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
package functional

import (
"context"
"testing"

ctrdoci "github.com/containerd/containerd/oci"
Expand All @@ -15,7 +14,6 @@ import (
"github.com/Microsoft/hcsshim/osversion"

"github.com/Microsoft/hcsshim/test/internal/cmd"
"github.com/Microsoft/hcsshim/test/internal/constants"
"github.com/Microsoft/hcsshim/test/internal/container"
"github.com/Microsoft/hcsshim/test/internal/layers"
"github.com/Microsoft/hcsshim/test/internal/oci"
Expand All @@ -27,9 +25,8 @@ func BenchmarkLCOW_Container(b *testing.B) {
requireFeatures(b, featureLCOW, featureContainer)
require.Build(b, osversion.RS5)

ctx, _, client := newContainerdClient(context.Background(), b)
ls := layers.FromImage(ctx, b, client, constants.ImageLinuxAlpineLatest,
constants.PlatformLinux, constants.SnapshotterLinux)
ctx := namespacedContext()
ls := linuxImageLayers(ctx, b)

// Create a new uvm per benchmark in case any left over state lingers

Expand Down
20 changes: 6 additions & 14 deletions test/functional/lcow_container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
package functional

import (
"context"
"strings"
"testing"

Expand All @@ -13,7 +12,6 @@ import (
"github.com/Microsoft/hcsshim/osversion"

"github.com/Microsoft/hcsshim/test/internal/cmd"
"github.com/Microsoft/hcsshim/test/internal/constants"
"github.com/Microsoft/hcsshim/test/internal/container"
"github.com/Microsoft/hcsshim/test/internal/layers"
"github.com/Microsoft/hcsshim/test/internal/oci"
Expand All @@ -25,10 +23,8 @@ func TestLCOW_ContainerLifecycle(t *testing.T) {
requireFeatures(t, featureLCOW, featureContainer)
require.Build(t, osversion.RS5)

ctx, _, client := newContainerdClient(context.Background(), t)
ls := layers.FromImage(ctx, t, client, constants.ImageLinuxAlpineLatest,
constants.PlatformLinux, constants.SnapshotterLinux)

ctx := namespacedContext()
ls := linuxImageLayers(ctx, t)
opts := defaultLCOWOptions(t)
vm := uvm.CreateAndStartLCOWFromOpts(ctx, t, opts)

Expand Down Expand Up @@ -81,10 +77,8 @@ func TestLCOW_ContainerIO(t *testing.T) {
requireFeatures(t, featureLCOW, featureContainer)
require.Build(t, osversion.RS5)

ctx, _, client := newContainerdClient(context.Background(), t)
ls := layers.FromImage(ctx, t, client, constants.ImageLinuxAlpineLatest,
constants.PlatformLinux, constants.SnapshotterLinux)

ctx := namespacedContext()
ls := linuxImageLayers(ctx, t)
opts := defaultLCOWOptions(t)
cache := layers.CacheFile(ctx, t, "")
vm := uvm.CreateAndStartLCOWFromOpts(ctx, t, opts)
Expand Down Expand Up @@ -125,10 +119,8 @@ func TestLCOW_ContainerExec(t *testing.T) {
requireFeatures(t, featureLCOW, featureContainer)
require.Build(t, osversion.RS5)

ctx, _, client := newContainerdClient(context.Background(), t)
ls := layers.FromImage(ctx, t, client, constants.ImageLinuxAlpineLatest,
constants.PlatformLinux, constants.SnapshotterLinux)

ctx := namespacedContext()
ls := linuxImageLayers(ctx, t)
opts := defaultLCOWOptions(t)
vm := uvm.CreateAndStartLCOWFromOpts(ctx, t, opts)

Expand Down
8 changes: 2 additions & 6 deletions test/functional/lcow_networking_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
package functional

import (
"context"
"fmt"
"strings"
"testing"
Expand All @@ -15,7 +14,6 @@ import (
"github.com/Microsoft/hcsshim/osversion"

"github.com/Microsoft/hcsshim/test/internal/cmd"
"github.com/Microsoft/hcsshim/test/internal/constants"
"github.com/Microsoft/hcsshim/test/internal/container"
"github.com/Microsoft/hcsshim/test/internal/layers"
"github.com/Microsoft/hcsshim/test/internal/oci"
Expand Down Expand Up @@ -123,10 +121,8 @@ func TestLCOW_IPv6_Assignment(t *testing.T) {
t.Fatalf("network attachment: %v", err)
}

ctx, _, client := newContainerdClient(context.Background(), t)
ls := layers.FromImage(ctx, t, client, constants.ImageLinuxAlpineLatest,
constants.PlatformLinux, constants.SnapshotterLinux)

ctx := namespacedContext()
ls := linuxImageLayers(ctx, t)
opts := defaultLCOWOptions(t)
vm := uvm.CreateAndStartLCOWFromOpts(ctx, t, opts)

Expand Down
8 changes: 3 additions & 5 deletions test/functional/lcow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import (

testutilities "github.com/Microsoft/hcsshim/test/internal"
testcmd "github.com/Microsoft/hcsshim/test/internal/cmd"
"github.com/Microsoft/hcsshim/test/internal/layers"
"github.com/Microsoft/hcsshim/test/internal/require"
testuvm "github.com/Microsoft/hcsshim/test/internal/uvm"
)
Expand Down Expand Up @@ -165,8 +164,7 @@ func TestLCOWSimplePodScenario(t *testing.T) {
require.Build(t, osversion.RS5)
requireFeatures(t, featureLCOW, featureContainer)

//nolint:staticcheck // SA1019: TODO: replace `LayerFolders`
alpineLayers := layers.LayerFolders(t, "alpine")
layers := linuxImageLayers(context.Background(), t)

cacheDir := t.TempDir()
cacheFile := filepath.Join(cacheDir, "cache.vhdx")
Expand Down Expand Up @@ -201,7 +199,7 @@ func TestLCOWSimplePodScenario(t *testing.T) {
t.Fatal(err)
}
c1Spec := testutilities.GetDefaultLinuxSpec(t)
c1Folders := append(alpineLayers, c1ScratchDir)
c1Folders := append(layers, c1ScratchDir)
c1Spec.Windows.LayerFolders = c1Folders
c1Spec.Process.Args = []string{"echo", "hello", "lcow", "container", "one"}
c1Opts := &hcsoci.CreateOptions{
Expand All @@ -214,7 +212,7 @@ func TestLCOWSimplePodScenario(t *testing.T) {
t.Fatal(err)
}
c2Spec := testutilities.GetDefaultLinuxSpec(t)
c2Folders := append(alpineLayers, c2ScratchDir)
c2Folders := append(layers, c2ScratchDir)
c2Spec.Windows.LayerFolders = c2Folders
c2Spec.Process.Args = []string{"echo", "hello", "lcow", "container", "two"}
c2Opts := &hcsoci.CreateOptions{
Expand Down
140 changes: 97 additions & 43 deletions test/functional/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@ import (
"log"
"os"
"os/exec"
"regexp"
"strconv"
"strings"
"testing"
"time"

"github.com/containerd/containerd"
"github.com/containerd/containerd/namespaces"
"github.com/sirupsen/logrus"

"github.com/Microsoft/hcsshim/internal/cow"
Expand All @@ -28,15 +27,34 @@ import (
"github.com/Microsoft/hcsshim/internal/uvm"
"github.com/Microsoft/hcsshim/internal/winapi"

testctrd "github.com/Microsoft/hcsshim/test/internal/containerd"
"github.com/Microsoft/hcsshim/test/internal/constants"
testflag "github.com/Microsoft/hcsshim/test/internal/flag"
"github.com/Microsoft/hcsshim/test/internal/layers"
"github.com/Microsoft/hcsshim/test/internal/require"
"github.com/Microsoft/hcsshim/test/internal/util"
testuvm "github.com/Microsoft/hcsshim/test/internal/uvm"
)

// owner field for uVMs.
const hcsOwner = "hcsshim-functional-tests"

var (
alpineImagePaths = &layers.LazyImageLayers{
helsaawy marked this conversation as resolved.
Show resolved Hide resolved
Image: constants.ImageLinuxAlpineLatest,
Platform: constants.PlatformLinux,
}
//TODO: pick appropriate image based on OS build
nanoserverImagePaths = &layers.LazyImageLayers{
Image: constants.ImageWindowsNanoserverLTSC2022,
Platform: constants.PlatformWindows,
}
// wcow tests originally used busyboxw; cannot find image on docker or mcr
servercoreImagePaths = &layers.LazyImageLayers{
Image: constants.ImageWindowsServercoreLTSC2022,
Platform: constants.PlatformWindows,
}
)

const (
featureLCOW = "LCOW"
featureWCOW = "WCOW"
Expand All @@ -63,16 +81,25 @@ var allFeatures = []string{
featureVPMEM,
}

// todo: use a new containerd namespace and then nuke everything in it

var (
debug bool
pauseDurationOnCreateContainerFailure time.Duration

flagFeatures = testflag.NewFeatureFlag(allFeatures)
flagContainerdAddress = flag.String("ctr-address", "tcp://127.0.0.1:2376", "`address` for containerd's GRPC server")
flagContainerdNamespace = flag.String("ctr-namespace", "k8s.io", "containerd `namespace`")
flagLinuxBootFilesPath = flag.String("linux-bootfiles", "",
flagPauseAfterCreateContainerFailure time.Duration

flagFeatures = testflag.NewFeatureFlag(allFeatures)
flagDebug = flag.Bool("debug",
os.Getenv("HCSSHIM_FUNCTIONAL_TESTS_DEBUG") != "",
"set logging level to debug [%HCSSHIM_FUNCTIONAL_TESTS_DEBUG%]")
flagContainerdNamespace = flag.String("ctr-namespace", hcsOwner,
"containerd `namespace` to use when creating OCI specs")
flagLCOWLayerPaths = testflag.NewStringSlice("lcow-layer-paths",
"comma separated list of image layer `paths` to use as LCOW container rootfs. "+
"If empty, \""+alpineImagePaths.Image+"\" will be pulled and unpacked.")
//nolint:unused // will be used when WCOW tests are updated
flagWCOWLayerPaths = testflag.NewStringSlice("wcow-layer-paths",
"comma separated list of image layer `paths` to use as WCOW uVM and container rootfs. "+
"If empty, \""+nanoserverImagePaths.Image+"\" will be pulled and unpacked.")
flagLayerTempDir = flag.String("layer-temp-dir", "",
"`directory` to unpack image layers to, if not provided. Leave empty to use os.TempDir.")
flagLinuxBootFilesPath = flag.String("linux-bootfiles", "",
"override default `path` for LCOW uVM boot files (rootfs.vhd, initrd.img, kernel, and vmlinux)")
)

Expand All @@ -81,20 +108,15 @@ func init() {
log.Fatal("tests must be run in an elevated context")
}

if _, ok := os.LookupEnv("HCSSHIM_FUNCTIONAL_TESTS_DEBUG"); ok {
debug = true
}
flag.BoolVar(&debug, "debug", debug, "set logging level to debug [%HCSSHIM_FUNCTIONAL_TESTS_DEBUG%]")

// This allows for debugging a utility VM.
if s := os.Getenv("HCSSHIM_FUNCTIONAL_TESTS_PAUSE_ON_CREATECONTAINER_FAIL_IN_MINUTES"); s != "" {
if t, err := strconv.Atoi(s); err == nil {
pauseDurationOnCreateContainerFailure = time.Duration(t) * time.Minute
flagPauseAfterCreateContainerFailure = time.Duration(t) * time.Minute
}
}
flag.DurationVar(&pauseDurationOnCreateContainerFailure,
flag.DurationVar(&flagPauseAfterCreateContainerFailure,
"container-creation-failure-pause",
pauseDurationOnCreateContainerFailure,
flagPauseAfterCreateContainerFailure,
"the number of minutes to wait after a container creation failure to try again "+
"[%HCSSHIM_FUNCTIONAL_TESTS_PAUSE_ON_CREATECONTAINER_FAIL_IN_MINUTES%]")
}
Expand All @@ -103,18 +125,23 @@ func TestMain(m *testing.M) {
flag.Parse()

lvl := logrus.WarnLevel
if debug {
if *flagDebug {
lvl = logrus.DebugLevel
}
logrus.SetLevel(lvl)
logrus.SetFormatter(&logrus.TextFormatter{FullTimestamp: true})
logrus.Infof("using features %q", flagFeatures.S.Strings())

images := []*layers.LazyImageLayers{alpineImagePaths, nanoserverImagePaths, servercoreImagePaths}
for _, l := range images {
l.TempPath = *flagLayerTempDir
}

e := m.Run()

// close any uVMs that escaped
cmdStr := `foreach ($vm in Get-ComputeProcess -Owner '` + hcsOwner + `') ` +
`{ Write-Output $vm.Id ; Stop-ComputeProcess -Force -Id $vm.Id }`
cmdStr := ` foreach ($vm in Get-ComputeProcess -Owner '` + hcsOwner +
`') { Write-Output "uVM $($vm.Id) was left running" ; Stop-ComputeProcess -Force -Id $vm.Id } `
cmd := exec.Command("powershell.exe", "-NoLogo", " -NonInteractive", "-Command", cmdStr)
o, err := cmd.CombinedOutput()
s := string(o)
Expand All @@ -124,17 +151,23 @@ func TestMain(m *testing.M) {
logrus.Warningf("cleaned up left over uVMs: %s", strings.Split(s, "\r\n"))
}

// delete downloaded layers; cant use defer, since os.exit does not run them
for _, l := range images {
// just ignore errors: they are logged, and no other cleanup possible
_ = l.Close(context.Background())
}
katiewasnothere marked this conversation as resolved.
Show resolved Hide resolved

os.Exit(e)
}

func CreateContainerTestWrapper(ctx context.Context, options *hcsoci.CreateOptions) (cow.Container, *resources.Resources, error) {
if pauseDurationOnCreateContainerFailure != 0 {
if flagPauseAfterCreateContainerFailure != 0 {
options.DoNotReleaseResourcesOnFailure = true
}
s, r, err := hcsoci.CreateContainer(ctx, options)
if err != nil {
logrus.Warnf("Test is pausing for %s for debugging CreateContainer failure", pauseDurationOnCreateContainerFailure)
time.Sleep(pauseDurationOnCreateContainerFailure)
logrus.Warnf("Test is pausing for %s for debugging CreateContainer failure", flagPauseAfterCreateContainerFailure)
time.Sleep(flagPauseAfterCreateContainerFailure)
_ = resources.ReleaseResources(ctx, r, options.HostingSystem, true)
}

Expand All @@ -146,36 +179,57 @@ func requireFeatures(tb testing.TB, features ...string) {
require.Features(tb, flagFeatures.S, features...)
}

func getContainerdOptions() testctrd.ContainerdClientOptions {
return testctrd.ContainerdClientOptions{
Address: *flagContainerdAddress,
Namespace: *flagContainerdNamespace,
func defaultLCOWOptions(tb testing.TB) *uvm.OptionsLCOW {
tb.Helper()
opts := testuvm.DefaultLCOWOptions(tb, util.CleanName(tb.Name()), hcsOwner)
if p := *flagLinuxBootFilesPath; p != "" {
opts.BootFilesPath = p
}
return opts
}

func newContainerdClient(ctx context.Context, tb testing.TB) (context.Context, context.CancelFunc, *containerd.Client) {
//nolint:deadcode,unused // will be used when WCOW tests are updated
func defaultWCOWOptions(tb testing.TB) *uvm.OptionsWCOW {
tb.Helper()
return getContainerdOptions().NewClient(ctx, tb)
return uvm.NewDefaultOptionsWCOW(util.CleanName(tb.Name()), hcsOwner)
}

func defaultLCOWOptions(tb testing.TB) *uvm.OptionsLCOW {
// linuxImageLayers returns image layer paths appropriate for use as a container rootfs.
// If layer paths were provided on the command line, they are returned.
// Otherwise, it pulls an appropriate image.
func linuxImageLayers(ctx context.Context, tb testing.TB) []string {
tb.Helper()
opts := testuvm.DefaultLCOWOptions(tb, cleanName(tb.Name()), hcsOwner)
if p := *flagLinuxBootFilesPath; p != "" {
opts.BootFilesPath = p
if ss := flagLCOWLayerPaths.S.Strings(); len(ss) > 0 {
return ss
}
return opts
return alpineImagePaths.Layers(ctx, tb)
}

// windowsImageLayers returns image layer paths appropriate for use as a uVM or container rootfs.
// If layer paths were provided on the command line, they are returned.
// Otherwise, it pulls an appropriate image.
//
//nolint:deadcode,unused // will be used when WCOW tests are updated
func defaultWCOWOptions(tb testing.TB) *uvm.OptionsWCOW {
func windowsImageLayers(ctx context.Context, tb testing.TB) []string {
tb.Helper()
opts := uvm.NewDefaultOptionsWCOW(cleanName(tb.Name()), hcsOwner)
return opts
if ss := flagWCOWLayerPaths.S.Strings(); len(ss) > 0 {
return ss
}
return nanoserverImagePaths.Layers(ctx, tb)
}

var _nameRegex = regexp.MustCompile(`[\\\/\s]`)
// windowsServercoreImageLayers returns image layer paths for Windows servercore.
//
// See [windowsImageLayers] for more.
//
//nolint:unused // will be used when WCOW tests are updated
func windowsServercoreImageLayers(ctx context.Context, tb testing.TB) []string {
tb.Helper()
return servercoreImagePaths.Layers(ctx, tb)
}

func cleanName(n string) string {
return _nameRegex.ReplaceAllString(n, "")
// namespacedContext returns a [context.Context] with the provided namespace added via
// [github.com/containerd/containerd/namespaces.WithNamespace].
func namespacedContext() context.Context {
return namespaces.WithNamespace(context.Background(), *flagContainerdNamespace)
}
Loading