Skip to content

Commit

Permalink
Support for LCOW tests and benchmarks
Browse files Browse the repository at this point in the history
Updated go-winio to include bugfixes for closing hvsockets, specifically
for writing (which is needed by `internal\cmd` to signal that the stdin
stream has finished.

Expose or add certain functionality needed by tests and benchmarks both
in the functional directory and uploaded directly onto a Linux UVM.
For example, ability to access an hcsv2 containers init process and ID,
as well creating and deleting network namespaces on the Linux UVM.

Added more functionality to `internal\tools\uvmboot`, to include adding
SCSI VHD and file mounts, as well as disabling the time sync, and setting
the uVM security policy.

Added winapi call to check if process is elevated, to allow returning a
clear error message before attempting to start uVMs.

Split out `internal\guest\runtime\runc\runc.go` into `runc.go`,
`container.go` and `process.go`.

Reorganized makefile to read from top to bottom, and added additional
files to LCOW rootfs that include the time stamp of of the vhd creation,
the image build date, and its full name (pulled from a
`*.testdata.json` file in the LSG release, that appears to be
the only location of that information).

Signed-off-by: Hamza El-Saawy <hamzaelsaawy@microsoft.com>
  • Loading branch information
helsaawy committed Apr 13, 2022
1 parent 8af6c33 commit 75f96e0
Show file tree
Hide file tree
Showing 59 changed files with 1,628 additions and 994 deletions.
12 changes: 10 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ service/pkg/
*.img
*.vhd
*.tar.gz
*.tar

# Make stuff
.rootfs-done
Expand All @@ -32,9 +33,16 @@ rootfs/*
rootfs-conv/*
*.o
/build/

deps/*
out/*

# test results
test/results

# ninja build
.ninja_log
build.ninja

# go workspaces
go.work
go.work.sum
go.work.sum
38 changes: 22 additions & 16 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ ifeq "$(GOMODVENDOR)" "1"
GO_FLAGS_EXTRA += -mod=vendor
endif
GO_BUILD:=CGO_ENABLED=$(CGO_ENABLED) $(GO) build $(GO_FLAGS) $(GO_FLAGS_EXTRA)
GO_TEST_BUILD:=CGO_ENABLED=$(CGO_ENABLED) $(GO) test $(GO_FLAGS) $(GO_FLAGS_EXTRA) -c

SRCROOT=$(dir $(abspath $(firstword $(MAKEFILE_LIST))))
# additional directories to search for rule prerequisites and targets
VPATH=$(SRCROOT)

# The link aliases for gcstools
GCS_TOOLS=\
Expand All @@ -40,33 +43,38 @@ out/rootfs.vhd: out/rootfs.tar.gz bin/cmd/tar2ext4
gzip -f -d ./out/rootfs.tar.gz
bin/cmd/tar2ext4 -vhd -i ./out/rootfs.tar -o $@

out/rootfs.tar.gz: out/initrd.img
rm -rf rootfs-conv
mkdir rootfs-conv
gunzip -c out/initrd.img | (cd rootfs-conv && cpio -imd)
tar -zcf $@ -C rootfs-conv .
rm -rf rootfs-conv

out/initrd.img: $(BASE) out/delta.tar.gz $(SRCROOT)/hack/catcpio.sh
$(SRCROOT)/hack/catcpio.sh "$(BASE)" out/delta.tar.gz > out/initrd.img.uncompressed
gzip -c out/initrd.img.uncompressed > $@
rm out/initrd.img.uncompressed

out/delta.tar.gz: bin/init bin/vsockexec bin/cmd/gcs bin/cmd/gcstools bin/cmd/hooks/wait-paths Makefile
@mkdir -p out
rm -rf rootfs
mkdir -p rootfs/bin/
mkdir -p rootfs/info/
cp bin/init rootfs/
cp bin/vsockexec rootfs/bin/
cp bin/cmd/gcs rootfs/bin/
cp bin/cmd/gcstools rootfs/bin/
cp bin/cmd/hooks/wait-paths rootfs/bin/
for tool in $(GCS_TOOLS); do ln -s gcstools rootfs/bin/$$tool; done
git -C $(SRCROOT) rev-parse HEAD > rootfs/gcs.commit && \
git -C $(SRCROOT) rev-parse --abbrev-ref HEAD > rootfs/gcs.branch
git -C $(SRCROOT) rev-parse HEAD > rootfs/info/gcs.commit && \
git -C $(SRCROOT) rev-parse --abbrev-ref HEAD > rootfs/info/gcs.branch && \
date --iso-8601=minute --utc > rootfs/info/tar.date
$(if $(realpath $(subst .tar,.testdata.json,$(BASE))), \
jq -r '.IMAGE_NAME' $(subst .tar,.testdata.json,$(BASE)) 2>/dev/null > rootfs/info/image.name && \
jq -r '.DATETIME' $(subst .tar,.testdata.json,$(BASE)) 2>/dev/null > rootfs/info/build.date )
tar -zcf $@ -C rootfs .
rm -rf rootfs

out/rootfs.tar.gz: out/initrd.img
rm -rf rootfs-conv
mkdir rootfs-conv
gunzip -c out/initrd.img | (cd rootfs-conv && cpio -imd)
tar -zcf $@ -C rootfs-conv .
rm -rf rootfs-conv

out/initrd.img: $(BASE) out/delta.tar.gz $(SRCROOT)/hack/catcpio.sh
$(SRCROOT)/hack/catcpio.sh "$(BASE)" out/delta.tar.gz > out/initrd.img.uncompressed
gzip -c out/initrd.img.uncompressed > $@
rm out/initrd.img.uncompressed

-include deps/cmd/gcs.gomake
-include deps/cmd/gcstools.gomake
-include deps/cmd/hooks/wait-paths.gomake
Expand All @@ -84,8 +92,6 @@ out/initrd.img: $(BASE) out/delta.tar.gz $(SRCROOT)/hack/catcpio.sh
@/bin/echo -e '-include $(@:%.gomake=%.godeps)' >> $@.new
mv $@.new $@

VPATH=$(SRCROOT)

bin/vsockexec: vsockexec/vsockexec.o vsockexec/vsock.o
@mkdir -p bin
$(CC) $(LDFLAGS) -o $@ $^
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.17

require (
github.com/BurntSushi/toml v0.3.1
github.com/Microsoft/go-winio v0.4.17
github.com/Microsoft/go-winio v0.5.2
github.com/cenkalti/backoff/v4 v4.1.1
github.com/containerd/cgroups v1.0.1
github.com/containerd/console v1.0.2
Expand Down
3 changes: 2 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/Microsoft/go-winio v0.4.17 h1:iT12IBVClFevaf8PuVyi3UmZOVh4OqnaLxDTW2O6j3w=
github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ=
Expand Down
2 changes: 1 addition & 1 deletion hcn/doc.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// Package hcn is a shim for the Host Compute Networking (HCN) service, which manages networking for Windows Server
// containers and Hyper-V containers. Previous to RS5, HCN was referred to as Host Networking Service (HNS).
// containers and Hyper-V containers. Prior to RS5, HCN was referred to as Host Networking Service (HNS).
package hcn
8 changes: 4 additions & 4 deletions hcn/hcnnamespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,11 +296,11 @@ func GetNamespaceContainerIds(namespaceId string) ([]string, error) {
var containerIds []string
for _, resource := range namespace.Resources {
if resource.Type == "Container" {
var contaienrResource NamespaceResourceContainer
if err := json.Unmarshal([]byte(resource.Data), &contaienrResource); err != nil {
var containerResource NamespaceResourceContainer
if err := json.Unmarshal([]byte(resource.Data), &containerResource); err != nil {
return nil, err
}
containerIds = append(containerIds, contaienrResource.Id)
containerIds = append(containerIds, containerResource.Id)
}
}
return containerIds, nil
Expand Down Expand Up @@ -377,7 +377,7 @@ func (namespace *HostComputeNamespace) Sync() error {
}
shimPath := runhcs.VMPipePath(cfg.HostUniqueID)
if err := runhcs.IssueVMRequest(shimPath, &req); err != nil {
// The shim is likey gone. Simply ignore the sync as if it didn't exist.
// The shim is likely gone. Simply ignore the sync as if it didn't exist.
if perr, ok := err.(*os.PathError); ok && perr.Err == syscall.ERROR_FILE_NOT_FOUND {
// Remove the reg key there is no point to try again
_ = cfg.Remove()
Expand Down
16 changes: 15 additions & 1 deletion internal/guest/runtime/hcsv2/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,24 @@ func (c *Container) ExecProcess(ctx context.Context, process *oci.Process, conSe
return pid, nil
}

// InitProcess returns the container's init process
func (c *Container) InitProcess() Process {
//todo: thread a context to this function call
logrus.WithFields(logrus.Fields{
logfields.ContainerID: c.id,
}).Info("opengcs::Container::InitProcess")

return c.initProcess
}

// GetProcess returns the Process with the matching 'pid'. If the 'pid' does
// not exit returns error.
func (c *Container) GetProcess(pid uint32) (Process, error) {
//todo: thread a context to this function call
logrus.WithFields(logrus.Fields{
logfields.ContainerID: c.id,
logfields.ProcessID: pid,
}).Info("opengcs::Container::GetProcesss")
}).Info("opengcs::Container::GetProcess")
if c.initProcess.pid == pid {
return c.initProcess, nil
}
Expand Down Expand Up @@ -219,3 +229,7 @@ func (c *Container) GetStats(ctx context.Context) (*v1.Metrics, error) {
func (c *Container) modifyContainerConstraints(ctx context.Context, rt guestrequest.RequestType, cc *guestresource.LCOWContainerConstraints) (err error) {
return c.Update(ctx, cc.Linux)
}

func (c *Container) ID() string {
return c.id
}
10 changes: 5 additions & 5 deletions internal/guest/runtime/hcsv2/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ func getNetworkNamespace(id string) (*namespace, error) {
return ns, nil
}

// getOrAddNetworkNamespace returns the namespace found by `id` or creates a new
// GetOrAddNetworkNamespace returns the namespace found by `id` or creates a new
// one and assigns `id.
func getOrAddNetworkNamespace(id string) *namespace {
func GetOrAddNetworkNamespace(id string) *namespace {
id = strings.ToLower(id)

namespaceSync.Lock()
Expand All @@ -69,8 +69,8 @@ func getOrAddNetworkNamespace(id string) *namespace {
return ns
}

// removeNetworkNamespace removes the in-memory `namespace` found by `id`.
func removeNetworkNamespace(ctx context.Context, id string) (err error) {
// RemoveNetworkNamespace removes the in-memory `namespace` found by `id`.
func RemoveNetworkNamespace(ctx context.Context, id string) (err error) {
_, span := trace.StartSpan(ctx, "hcsv2::removeNetworkNamespace")
defer span.End()
defer func() { oc.SetSpanStatus(span, err) }()
Expand Down Expand Up @@ -123,7 +123,7 @@ func (n *namespace) AssignContainerPid(ctx context.Context, pid int) (err error)
defer n.m.Unlock()

if n.pid != 0 {
return errors.Errorf("previously assigned container pid: %d", n.pid)
return errors.Errorf("previously assigned container pid to network namespace %q: %d", n.id, n.pid)
}

n.pid = pid
Expand Down
26 changes: 13 additions & 13 deletions internal/guest/runtime/hcsv2/network_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

func Test_getNetworkNamespace_NotExist(t *testing.T) {
defer func() {
err := removeNetworkNamespace(context.Background(), t.Name())
err := RemoveNetworkNamespace(context.Background(), t.Name())
if err != nil {
t.Errorf("failed to remove ns with error: %v", err)
}
Expand All @@ -29,13 +29,13 @@ func Test_getNetworkNamespace_NotExist(t *testing.T) {

func Test_getNetworkNamespace_PreviousExist(t *testing.T) {
defer func() {
err := removeNetworkNamespace(context.Background(), t.Name())
err := RemoveNetworkNamespace(context.Background(), t.Name())
if err != nil {
t.Errorf("failed to remove ns with error: %v", err)
}
}()

ns1 := getOrAddNetworkNamespace(t.Name())
ns1 := GetOrAddNetworkNamespace(t.Name())
if ns1 == nil {
t.Fatal("namespace ns1 should not be nil")
}
Expand All @@ -50,43 +50,43 @@ func Test_getNetworkNamespace_PreviousExist(t *testing.T) {

func Test_getOrAddNetworkNamespace_NotExist(t *testing.T) {
defer func() {
err := removeNetworkNamespace(context.Background(), t.Name())
err := RemoveNetworkNamespace(context.Background(), t.Name())
if err != nil {
t.Errorf("failed to remove ns with error: %v", err)
}
}()

ns := getOrAddNetworkNamespace(t.Name())
ns := GetOrAddNetworkNamespace(t.Name())
if ns == nil {
t.Fatalf("namespace should not be nil")
}
}

func Test_getOrAddNetworkNamespace_PreviousExist(t *testing.T) {
defer func() {
err := removeNetworkNamespace(context.Background(), t.Name())
err := RemoveNetworkNamespace(context.Background(), t.Name())
if err != nil {
t.Errorf("failed to remove ns with error: %v", err)
}
}()

ns1 := getOrAddNetworkNamespace(t.Name())
ns2 := getOrAddNetworkNamespace(t.Name())
ns1 := GetOrAddNetworkNamespace(t.Name())
ns2 := GetOrAddNetworkNamespace(t.Name())
if ns1 != ns2 {
t.Fatalf("ns1 %+v != ns2 %+v", ns1, ns2)
}
}

func Test_removeNetworkNamespace_NotExist(t *testing.T) {
err := removeNetworkNamespace(context.Background(), t.Name())
err := RemoveNetworkNamespace(context.Background(), t.Name())
if err != nil {
t.Fatalf("failed to remove non-existing ns with error: %v", err)
}
}

func Test_removeNetworkNamespace_HasAdapters(t *testing.T) {
defer func() {
err := removeNetworkNamespace(context.Background(), t.Name())
err := RemoveNetworkNamespace(context.Background(), t.Name())
if err != nil {
t.Errorf("failed to remove ns with error: %v", err)
}
Expand All @@ -96,7 +96,7 @@ func Test_removeNetworkNamespace_HasAdapters(t *testing.T) {
networkInstanceIDToName = nsOld
}()

ns := getOrAddNetworkNamespace(t.Name())
ns := GetOrAddNetworkNamespace(t.Name())

networkInstanceIDToName = func(ctx context.Context, id string, _ bool) (string, error) {
return "/dev/sdz", nil
Expand All @@ -105,15 +105,15 @@ func Test_removeNetworkNamespace_HasAdapters(t *testing.T) {
if err != nil {
t.Fatalf("failed to add adapter: %v", err)
}
err = removeNetworkNamespace(context.Background(), t.Name())
err = RemoveNetworkNamespace(context.Background(), t.Name())
if err == nil {
t.Fatal("should have failed to delete namespace with adapters")
}
err = ns.RemoveAdapter(context.Background(), "test")
if err != nil {
t.Fatalf("failed to remove adapter: %v", err)
}
err = removeNetworkNamespace(context.Background(), t.Name())
err = RemoveNetworkNamespace(context.Background(), t.Name())
if err != nil {
t.Fatalf("should not have failed to delete empty namepace got: %v", err)
}
Expand Down
6 changes: 5 additions & 1 deletion internal/guest/runtime/hcsv2/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ type containerProcess struct {
// (runtime.Process).Wait() call returns, and exitCode has been updated.
exitWg sync.WaitGroup

// Used to allow addtion/removal to the writersWg after an initial wait has
// Used to allow addition/removal to the writersWg after an initial wait has
// already been issued. It is not safe to call Add/Done without holding this
// lock.
writersSyncRoot sync.Mutex
Expand All @@ -67,6 +67,8 @@ type containerProcess struct {
writersCalled bool
}

var _ Process = &containerProcess{}

// newProcess returns a containerProcess struct that has been initialized with
// an outstanding wait for process exit, and post exit an outstanding wait for
// process cleanup to release all resources once at least 1 waiter has
Expand Down Expand Up @@ -262,6 +264,8 @@ type externalProcess struct {
remove func(pid int)
}

var _ Process = &containerProcess{}

func (ep *externalProcess) Kill(ctx context.Context, signal syscall.Signal) error {
if err := syscall.Kill(int(ep.cmd.Process.Pid), signal); err != nil {
if err == syscall.ESRCH {
Expand Down
2 changes: 1 addition & 1 deletion internal/guest/runtime/hcsv2/standalone_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func setupStandaloneContainerSpec(ctx context.Context, id string, spec *oci.Spec

// Write resolv.conf
if !isInMounts("/etc/resolv.conf", spec.Mounts) {
ns := getOrAddNetworkNamespace(getNetworkNamespaceID(spec))
ns := GetOrAddNetworkNamespace(getNetworkNamespaceID(spec))
var searches, servers []string
for _, n := range ns.Adapters() {
if len(n.DNSSuffix) > 0 {
Expand Down
Loading

0 comments on commit 75f96e0

Please sign in to comment.