github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/client/allocwatcher/alloc_watcher_unix_test.go (about)

     1  //go:build !windows
     2  // +build !windows
     3  
     4  package allocwatcher
     5  
     6  import (
     7  	"archive/tar"
     8  	"bytes"
     9  	"context"
    10  	"fmt"
    11  	"io"
    12  	"io/ioutil"
    13  	"os"
    14  	"path/filepath"
    15  	"syscall"
    16  	"testing"
    17  
    18  	"github.com/hashicorp/nomad/ci"
    19  	ctestutil "github.com/hashicorp/nomad/client/testutil"
    20  	"github.com/hashicorp/nomad/helper/testlog"
    21  )
    22  
    23  // TestPrevAlloc_StreamAllocDir_Ok asserts that streaming a tar to an alloc dir
    24  // works.
    25  func TestPrevAlloc_StreamAllocDir_Ok(t *testing.T) {
    26  	ci.Parallel(t)
    27  	ctestutil.RequireRoot(t)
    28  
    29  	dir := t.TempDir()
    30  
    31  	// Create foo/
    32  	fooDir := filepath.Join(dir, "foo")
    33  	if err := os.Mkdir(fooDir, 0777); err != nil {
    34  		t.Fatalf("err: %v", err)
    35  	}
    36  
    37  	// Change ownership of foo/ to test #3702 (any non-root user is fine)
    38  	const uid, gid = 1, 1
    39  	if err := os.Chown(fooDir, uid, gid); err != nil {
    40  		t.Fatalf("err : %v", err)
    41  	}
    42  
    43  	dirInfo, err := os.Stat(fooDir)
    44  	if err != nil {
    45  		t.Fatalf("err: %v", err)
    46  	}
    47  
    48  	// Create foo/bar
    49  	f, err := os.Create(filepath.Join(fooDir, "bar"))
    50  	if err != nil {
    51  		t.Fatalf("err: %v", err)
    52  	}
    53  	if _, err := f.WriteString("123"); err != nil {
    54  		t.Fatalf("err: %v", err)
    55  	}
    56  	if err := f.Chmod(0644); err != nil {
    57  		t.Fatalf("err: %v", err)
    58  	}
    59  	fInfo, err := f.Stat()
    60  	if err != nil {
    61  		t.Fatalf("err: %v", err)
    62  	}
    63  	f.Close()
    64  
    65  	// Create foo/baz -> bar symlink
    66  	if err := os.Symlink("bar", filepath.Join(dir, "foo", "baz")); err != nil {
    67  		t.Fatalf("err: %v", err)
    68  	}
    69  	linkInfo, err := os.Lstat(filepath.Join(dir, "foo", "baz"))
    70  	if err != nil {
    71  		t.Fatalf("err: %v", err)
    72  	}
    73  
    74  	buf := new(bytes.Buffer)
    75  	tw := tar.NewWriter(buf)
    76  
    77  	walkFn := func(path string, fileInfo os.FileInfo, err error) error {
    78  		// filepath.Walk passes in an error
    79  		if err != nil {
    80  			return fmt.Errorf("error from filepath.Walk(): %s", err)
    81  		}
    82  		// Include the path of the file name relative to the alloc dir
    83  		// so that we can put the files in the right directories
    84  		link := ""
    85  		if fileInfo.Mode()&os.ModeSymlink != 0 {
    86  			target, err := os.Readlink(path)
    87  			if err != nil {
    88  				return fmt.Errorf("error reading symlink: %v", err)
    89  			}
    90  			link = target
    91  		}
    92  		hdr, err := tar.FileInfoHeader(fileInfo, link)
    93  		if err != nil {
    94  			return fmt.Errorf("error creating file header: %v", err)
    95  		}
    96  		hdr.Name = fileInfo.Name()
    97  		tw.WriteHeader(hdr)
    98  
    99  		// If it's a directory or symlink we just write the header into the tar
   100  		if fileInfo.IsDir() || (fileInfo.Mode()&os.ModeSymlink != 0) {
   101  			return nil
   102  		}
   103  
   104  		// Write the file into the archive
   105  		file, err := os.Open(path)
   106  		if err != nil {
   107  			return err
   108  		}
   109  		defer file.Close()
   110  
   111  		if _, err := io.Copy(tw, file); err != nil {
   112  			return err
   113  		}
   114  
   115  		return nil
   116  	}
   117  
   118  	if err := filepath.Walk(dir, walkFn); err != nil {
   119  		t.Fatalf("err: %v", err)
   120  	}
   121  	tw.Close()
   122  
   123  	dir1 := t.TempDir()
   124  
   125  	rc := ioutil.NopCloser(buf)
   126  	prevAlloc := &remotePrevAlloc{logger: testlog.HCLogger(t)}
   127  	if err := prevAlloc.streamAllocDir(context.Background(), rc, dir1); err != nil {
   128  		t.Fatalf("err: %v", err)
   129  	}
   130  
   131  	// Ensure foo is present
   132  	fi, err := os.Stat(filepath.Join(dir1, "foo"))
   133  	if err != nil {
   134  		t.Fatalf("err: %v", err)
   135  	}
   136  	if fi.Mode() != dirInfo.Mode() {
   137  		t.Fatalf("mode: %v", fi.Mode())
   138  	}
   139  	stat := fi.Sys().(*syscall.Stat_t)
   140  	if stat.Uid != uid || stat.Gid != gid {
   141  		t.Fatalf("foo/ has incorrect ownership: expected %d:%d found %d:%d",
   142  			uid, gid, stat.Uid, stat.Gid)
   143  	}
   144  
   145  	fi1, err := os.Stat(filepath.Join(dir1, "bar"))
   146  	if err != nil {
   147  		t.Fatalf("err: %v", err)
   148  	}
   149  	if fi1.Mode() != fInfo.Mode() {
   150  		t.Fatalf("mode: %v", fi1.Mode())
   151  	}
   152  
   153  	fi2, err := os.Lstat(filepath.Join(dir1, "baz"))
   154  	if err != nil {
   155  		t.Fatalf("err: %v", err)
   156  	}
   157  	if fi2.Mode() != linkInfo.Mode() {
   158  		t.Fatalf("mode: %v", fi2.Mode())
   159  	}
   160  }