github.com/opencontainers/runtime-tools@v0.9.0/validation/linux_ns_nopath/linux_ns_nopath.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"runtime"
     8  
     9  	"github.com/mndrix/tap-go"
    10  	rspec "github.com/opencontainers/runtime-spec/specs-go"
    11  	"github.com/opencontainers/runtime-tools/specerror"
    12  	"github.com/opencontainers/runtime-tools/validation/util"
    13  )
    14  
    15  func printDiag(t *tap.T, diagActual, diagExpected, diagNsType string, errNs error) {
    16  	specErr := specerror.NewError(specerror.NSNewNSWithoutPath,
    17  		errNs, rspec.Version)
    18  	diagnostic := map[string]string{
    19  		"actual":         diagActual,
    20  		"expected":       diagExpected,
    21  		"namespace type": diagNsType,
    22  		"level":          specErr.(*specerror.Error).Err.Level.String(),
    23  		"reference":      specErr.(*specerror.Error).Err.Reference,
    24  	}
    25  	t.YAML(diagnostic)
    26  }
    27  
    28  func testNamespaceNoPath(t *tap.T) error {
    29  	var errNs error
    30  	diagActual := ""
    31  	diagExpected := ""
    32  	diagNsType := ""
    33  
    34  	// To be able to print out diagnostics for all kinds of error cases
    35  	// at the end of the tests, we make use of defer function. To do that,
    36  	// each error handling routine should set diagActual, diagExpected,
    37  	// diagNsType, and errNs, before returning an error.
    38  	defer func() {
    39  		if errNs != nil {
    40  			printDiag(t, diagActual, diagExpected, diagNsType, errNs)
    41  		}
    42  	}()
    43  
    44  	hostNsPath := fmt.Sprintf("/proc/%d/ns", os.Getpid())
    45  	hostNsInodes := map[string]string{}
    46  
    47  	for _, nsName := range util.ProcNamespaces {
    48  		nsPathAbs := filepath.Join(hostNsPath, nsName)
    49  		nsInode, err := os.Readlink(nsPathAbs)
    50  		if err != nil {
    51  			errNs = fmt.Errorf("cannot resolve symlink %q: %v", nsPathAbs, err)
    52  			diagActual = fmt.Sprintf("err == %v", errNs)
    53  			diagExpected = "err == nil"
    54  			diagNsType = nsName
    55  			return errNs
    56  		}
    57  		hostNsInodes[nsName] = nsInode
    58  	}
    59  
    60  	g, err := util.GetDefaultGenerator()
    61  	if err != nil {
    62  		errNs = fmt.Errorf("cannot get the default generator: %v", err)
    63  		diagActual = fmt.Sprintf("err == %v", errNs)
    64  		diagExpected = "err == nil"
    65  		// NOTE: we don't have a namespace type
    66  		return errNs
    67  	}
    68  
    69  	// As the namespaces, cgroups and user, are not set by GetDefaultGenerator(),
    70  	// others are set by default. We just set them explicitly to avoid confusion.
    71  	g.AddOrReplaceLinuxNamespace("cgroup", "")
    72  	g.AddOrReplaceLinuxNamespace("ipc", "")
    73  	g.AddOrReplaceLinuxNamespace("mount", "")
    74  	g.AddOrReplaceLinuxNamespace("network", "")
    75  	g.AddOrReplaceLinuxNamespace("pid", "")
    76  	g.AddOrReplaceLinuxNamespace("user", "")
    77  	g.AddOrReplaceLinuxNamespace("uts", "")
    78  
    79  	// For user namespaces, we need to set uid/gid maps to create a container
    80  	g.AddLinuxUIDMapping(uint32(1000), uint32(0), uint32(1000))
    81  	g.AddLinuxGIDMapping(uint32(1000), uint32(0), uint32(1000))
    82  
    83  	err = util.RuntimeOutsideValidate(g, t, func(config *rspec.Spec, t *tap.T, state *rspec.State) error {
    84  		containerNsPath := fmt.Sprintf("/proc/%d/ns", state.Pid)
    85  
    86  		for _, nsName := range util.ProcNamespaces {
    87  			nsPathAbs := filepath.Join(containerNsPath, nsName)
    88  			nsInode, err := os.Readlink(nsPathAbs)
    89  			if err != nil {
    90  				errNs = fmt.Errorf("cannot resolve symlink %q: %v", nsPathAbs, err)
    91  				diagActual = fmt.Sprintf("err == %v", errNs)
    92  				diagExpected = "err == nil"
    93  				diagNsType = nsName
    94  				return errNs
    95  			}
    96  
    97  			t.Ok(hostNsInodes[nsName] != nsInode, fmt.Sprintf("create namespace %s without path", nsName))
    98  			if hostNsInodes[nsName] == nsInode {
    99  				// NOTE: for such inode match cases, we should print out diagnostics
   100  				// for each case, not only at the end of tests. So we should simply
   101  				// call once printDiag(), then continue testing next namespaces.
   102  				// Thus we don't need to set diagActual, diagExpected, diagNsType, etc.
   103  				printDiag(t, nsInode, fmt.Sprintf("!= %s", hostNsInodes[nsName]), nsName,
   104  					fmt.Errorf("both namespaces for %s have the same inode %s", nsName, nsInode))
   105  				continue
   106  			}
   107  		}
   108  
   109  		return nil
   110  	})
   111  	if err != nil {
   112  		errNs = fmt.Errorf("cannot run validation tests: %v", err)
   113  	}
   114  
   115  	return errNs
   116  }
   117  
   118  func main() {
   119  	t := tap.New()
   120  	t.Header(0)
   121  
   122  	if "linux" != runtime.GOOS {
   123  		t.Skip(1, fmt.Sprintf("linux-specific namespace test"))
   124  	}
   125  
   126  	err := testNamespaceNoPath(t)
   127  	if err != nil {
   128  		t.Fail(err.Error())
   129  	}
   130  
   131  	t.AutoPlan()
   132  }