github.com/rohankumardubey/proxyfs@v0.0.0-20210108201508-653efa9ab00e/proxyfsd/daemon_test.go (about)

     1  package proxyfsd
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"os"
     9  	"strconv"
    10  	"sync"
    11  	"testing"
    12  	"time"
    13  
    14  	"golang.org/x/sys/unix"
    15  
    16  	"github.com/swiftstack/ProxyFS/fs"
    17  	"github.com/swiftstack/ProxyFS/inode"
    18  	"github.com/swiftstack/ProxyFS/ramswift"
    19  )
    20  
    21  func TestMain(m *testing.M) {
    22  	mRunReturn := m.Run()
    23  	os.Exit(mRunReturn)
    24  }
    25  
    26  func TestDaemon(t *testing.T) {
    27  	var (
    28  		bytesWritten                          uint64
    29  		confMapStrings                        []string
    30  		confMapStringsWithAutoFormatTrueAdded []string
    31  		createdFileInodeNumber                inode.InodeNumber
    32  		err                                   error
    33  		errChan                               chan error
    34  		execArgs                              []string
    35  		ramswiftDoneChan                      chan bool
    36  		ramswiftSignalHandlerIsArmedWG        sync.WaitGroup
    37  		readData                              []byte
    38  		testVersion                           uint64
    39  		testVersionConfFile                   *os.File
    40  		testVersionConfFileName               string
    41  		toReadFileInodeNumber                 inode.InodeNumber
    42  		volumeHandle                          fs.VolumeHandle
    43  		wg                                    sync.WaitGroup
    44  	)
    45  
    46  	// Setup a ramswift instance leveraging test config
    47  
    48  	confMapStrings = []string{
    49  		"Stats.IPAddr=localhost",
    50  		"Stats.UDPPort=52184",
    51  		"Stats.BufferLength=100",
    52  		"Stats.MaxLatency=1s",
    53  
    54  		"StatsLogger.Period=0m",
    55  		"StatsLogger.Verbose=false",
    56  
    57  		"Logging.LogFilePath=/dev/null",
    58  		"Logging.LogToConsole=false",
    59  
    60  		"Peer:Peer0.PublicIPAddr=127.0.0.1",
    61  		"Peer:Peer0.PrivateIPAddr=127.0.0.1",
    62  		"Peer:Peer0.ReadCacheQuotaFraction=0.20",
    63  
    64  		"Cluster.WhoAmI=Peer0",
    65  		"Cluster.Peers=Peer0",
    66  		"Cluster.ServerGuid=a66488e9-a051-4ff7-865d-87bfb84cc2ae",
    67  		"Cluster.PrivateClusterUDPPort=18123",
    68  		"Cluster.UDPPacketSendSize=1400",
    69  		"Cluster.UDPPacketRecvSize=1500",
    70  		"Cluster.UDPPacketCapPerMessage=5",
    71  		"Cluster.HeartBeatDuration=1s",
    72  		"Cluster.HeartBeatMissLimit=3",
    73  		"Cluster.MessageQueueDepthPerPeer=4",
    74  		"Cluster.MaxRequestDuration=1s",
    75  		"Cluster.LivenessCheckRedundancy=2",
    76  
    77  		"HTTPServer.TCPPort=53461",
    78  
    79  		"SwiftClient.NoAuthIPAddr=127.0.0.1",
    80  		"SwiftClient.NoAuthTCPPort=35262",
    81  		"SwiftClient.Timeout=10s",
    82  		"SwiftClient.RetryLimit=1",
    83  		"SwiftClient.RetryLimitObject=1",
    84  		"SwiftClient.RetryDelay=10ms",
    85  		"SwiftClient.RetryDelayObject=10ms",
    86  		"SwiftClient.RetryExpBackoff=1.2",
    87  		"SwiftClient.RetryExpBackoffObject=2.0",
    88  		"SwiftClient.ChunkedConnectionPoolSize=64",
    89  		"SwiftClient.NonChunkedConnectionPoolSize=32",
    90  
    91  		"PhysicalContainerLayout:PhysicalContainerLayoutReplicated3Way.ContainerStoragePolicy=silver",
    92  		"PhysicalContainerLayout:PhysicalContainerLayoutReplicated3Way.ContainerNamePrefix=Replicated3Way_",
    93  		"PhysicalContainerLayout:PhysicalContainerLayoutReplicated3Way.ContainersPerPeer=10",
    94  		"PhysicalContainerLayout:PhysicalContainerLayoutReplicated3Way.MaxObjectsPerContainer=1000000",
    95  
    96  		"Volume:CommonVolume.FSID=1",
    97  		"Volume:CommonVolume.FUSEMountPointName=CommonMountPoint",
    98  		"Volume:CommonVolume.NFSExportClientMapList=CommonVolumeNFSClient0",
    99  		"Volume:CommonVolume.SMBShareName=CommonShare",
   100  		"Volume:CommonVolume.PrimaryPeer=Peer0",
   101  		"Volume:CommonVolume.AccountName=AUTH_CommonAccount",
   102  		"Volume:CommonVolume.CheckpointContainerName=.__checkpoint__",
   103  		"Volume:CommonVolume.CheckpointContainerStoragePolicy=gold",
   104  		"Volume:CommonVolume.CheckpointInterval=10s",
   105  		"Volume:CommonVolume.DefaultPhysicalContainerLayout=PhysicalContainerLayoutReplicated3Way",
   106  		"Volume:CommonVolume.MaxFlushSize=10485760",
   107  		"Volume:CommonVolume.MaxFlushTime=10s",
   108  		"Volume:CommonVolume.FileDefragmentChunkSize=10485760",
   109  		"Volume:CommonVolume.FileDefragmentChunkDelay=10ms",
   110  		"Volume:CommonVolume.NonceValuesToReserve=100",
   111  		"Volume:CommonVolume.MaxEntriesPerDirNode=32",
   112  		"Volume:CommonVolume.MaxExtentsPerFileNode=32",
   113  		"Volume:CommonVolume.MaxInodesPerMetadataNode=32",
   114  		"Volume:CommonVolume.MaxLogSegmentsPerMetadataNode=64",
   115  		"Volume:CommonVolume.MaxDirFileNodesPerMetadataNode=16",
   116  		"Volume:CommonVolume.MaxBytesInodeCache=100000",
   117  		"Volume:CommonVolume.InodeCacheEvictInterval=1s",
   118  		"Volume:CommonVolume.ActiveLeaseEvictLowLimit=5000",
   119  		"Volume:CommonVolume.ActiveLeaseEvictHighLimit=5010",
   120  
   121  		"NFSClientMap:CommonVolumeNFSClient0.ClientPattern=*",
   122  		"NFSClientMap:CommonVolumeNFSClient0.AccessMode=rw",
   123  		"NFSClientMap:CommonVolumeNFSClient0.RootSquash=no_root_squash",
   124  		"NFSClientMap:CommonVolumeNFSClient0.Secure=insecure",
   125  
   126  		"VolumeGroup:CommonVolumeGroup.VolumeList=CommonVolume",
   127  		"VolumeGroup:CommonVolumeGroup.VirtualIPAddr=",
   128  		"VolumeGroup:CommonVolumeGroup.PrimaryPeer=Peer0",
   129  		"VolumeGroup:CommonVolumeGroup.ReadCacheLineSize=1000000",
   130  		"VolumeGroup:CommonVolumeGroup.ReadCacheWeight=100",
   131  
   132  		"FSGlobals.VolumeGroupList=CommonVolumeGroup",
   133  		"FSGlobals.CheckpointHeaderConsensusAttempts=5",
   134  		"FSGlobals.MountRetryLimit=6",
   135  		"FSGlobals.MountRetryDelay=1s",
   136  		"FSGlobals.MountRetryExpBackoff=2",
   137  		"FSGlobals.LogCheckpointHeaderPosts=true",
   138  		"FSGlobals.TryLockBackoffMin=10ms",
   139  		"FSGlobals.TryLockBackoffMax=50ms",
   140  		"FSGlobals.TryLockSerializationThreshhold=5",
   141  		"FSGlobals.SymlinkMax=32",
   142  		"FSGlobals.CoalesceElementChunkSize=16",
   143  		"FSGlobals.InodeRecCacheEvictLowLimit=10000",
   144  		"FSGlobals.InodeRecCacheEvictHighLimit=10010",
   145  		"FSGlobals.LogSegmentRecCacheEvictLowLimit=10000",
   146  		"FSGlobals.LogSegmentRecCacheEvictHighLimit=10010",
   147  		"FSGlobals.BPlusTreeObjectCacheEvictLowLimit=10000",
   148  		"FSGlobals.BPlusTreeObjectCacheEvictHighLimit=10010",
   149  		"FSGlobals.DirEntryCacheEvictLowLimit=10000",
   150  		"FSGlobals.DirEntryCacheEvictHighLimit=10010",
   151  		"FSGlobals.FileExtentMapEvictLowLimit=10000",
   152  		"FSGlobals.FileExtentMapEvictHighLimit=10010",
   153  		"FSGlobals.EtcdEnabled=false",
   154  
   155  		"JSONRPCServer.TCPPort=12346",     // 12346 instead of 12345 so that test can run if proxyfsd is already running
   156  		"JSONRPCServer.FastTCPPort=32346", // ...and similarly here...
   157  		"JSONRPCServer.DataPathLogging=false",
   158  		"JSONRPCServer.MinLeaseDuration=250ms",
   159  		"JSONRPCServer.LeaseInterruptInterval=250ms",
   160  		"JSONRPCServer.LeaseInterruptLimit=20",
   161  
   162  		"RamSwiftInfo.MaxAccountNameLength=256",
   163  		"RamSwiftInfo.MaxContainerNameLength=256",
   164  		"RamSwiftInfo.MaxObjectNameLength=1024",
   165  		"RamSwiftInfo.AccountListingLimit=10000",
   166  		"RamSwiftInfo.ContainerListingLimit=10000",
   167  	}
   168  
   169  	testVersionConfFile, err = ioutil.TempFile(os.TempDir(), "proxyfsdTest_")
   170  	if nil != err {
   171  		t.Fatalf("ioutil.TempFile() failed: %v", err)
   172  	}
   173  	testVersionConfFileName = testVersionConfFile.Name()
   174  
   175  	_, err = testVersionConfFile.WriteString("[TestVersionSection]\n")
   176  	if nil != err {
   177  		t.Fatalf("testVersionConfFile.WriteString() [case 1] failed: %v", err)
   178  	}
   179  	_, err = testVersionConfFile.WriteString("Version: 1\n")
   180  	if nil != err {
   181  		t.Fatalf("testVersionConfFile.WriteString() [case 2] failed: %v", err)
   182  	}
   183  
   184  	err = testVersionConfFile.Close()
   185  	if nil != err {
   186  		t.Fatalf("testVersionConfFile.Close() [case 1] failed: %v", err)
   187  	}
   188  
   189  	ramswiftSignalHandlerIsArmedWG.Add(1)
   190  	ramswiftDoneChan = make(chan bool, 1)
   191  
   192  	go ramswift.Daemon(testVersionConfFileName, confMapStrings, &ramswiftSignalHandlerIsArmedWG, ramswiftDoneChan, unix.SIGINT)
   193  
   194  	ramswiftSignalHandlerIsArmedWG.Wait()
   195  
   196  	// Launch an instance of proxyfsd using above config with Volume:CommonVolume.AutoFormat=true
   197  
   198  	errChan = make(chan error, 1) // Must be buffered to avoid race
   199  
   200  	confMapStringsWithAutoFormatTrueAdded = append(confMapStrings, "Volume:CommonVolume.AutoFormat=true")
   201  	execArgs = append([]string{"daemon_test", "/dev/null"}, confMapStringsWithAutoFormatTrueAdded...)
   202  	go Daemon("/dev/null", confMapStringsWithAutoFormatTrueAdded, errChan, &wg,
   203  		execArgs, unix.SIGTERM, unix.SIGHUP)
   204  
   205  	err = <-errChan
   206  	if nil != err {
   207  		t.Fatalf("Daemon() startup failed [case 1a]: %v", err)
   208  	}
   209  
   210  	// Write to the volume (with no flush so that only time-based/restart flush is performed)
   211  
   212  	volumeHandle, err = fs.FetchVolumeHandleByVolumeName("CommonVolume")
   213  	if nil != err {
   214  		t.Fatalf("fs.FetchVolumeHandleByVolumeName() failed [case 1]: %v", err)
   215  	}
   216  
   217  	createdFileInodeNumber, err = volumeHandle.Create(
   218  		inode.InodeRootUserID,
   219  		inode.InodeGroupID(0),
   220  		nil,
   221  		inode.RootDirInodeNumber,
   222  		"TestFile",
   223  		inode.R_OK|inode.W_OK,
   224  	)
   225  	if nil != err {
   226  		t.Fatalf("fs.Create() failed: %v", err)
   227  	}
   228  
   229  	bytesWritten, err = volumeHandle.Write(
   230  		inode.InodeRootUserID,
   231  		inode.InodeGroupID(0),
   232  		nil,
   233  		createdFileInodeNumber,
   234  		0,
   235  		[]byte{0x00, 0x01, 0x02, 0x03},
   236  		nil,
   237  	)
   238  	if nil != err {
   239  		t.Fatalf("fs.Write() failed: %v", err)
   240  	}
   241  	if 4 != bytesWritten {
   242  		t.Fatalf("fs.Write() returned unexpected bytesWritten")
   243  	}
   244  
   245  	// Verify written data before restart
   246  
   247  	toReadFileInodeNumber, err = volumeHandle.Lookup(
   248  		inode.InodeRootUserID,
   249  		inode.InodeGroupID(0),
   250  		nil,
   251  		inode.RootDirInodeNumber,
   252  		"TestFile",
   253  	)
   254  	if nil != err {
   255  		t.Fatalf("fs.Lookup() failed [case 1]: %v", err)
   256  	}
   257  
   258  	readData, err = volumeHandle.Read(
   259  		inode.InodeRootUserID,
   260  		inode.InodeGroupID(0),
   261  		nil,
   262  		toReadFileInodeNumber,
   263  		0,
   264  		4,
   265  		nil,
   266  	)
   267  	if nil != err {
   268  		t.Fatalf("fs.Read() failed [case 1]: %v", err)
   269  	}
   270  	if 0 != bytes.Compare([]byte{0x00, 0x01, 0x02, 0x03}, readData) {
   271  		t.Fatalf("fs.Read() returned unexpected readData [case 1]")
   272  	}
   273  
   274  	// Send ourself a SIGTERM to signal normal termination of mainWithArgs()
   275  
   276  	unix.Kill(unix.Getpid(), unix.SIGTERM)
   277  
   278  	err = <-errChan
   279  
   280  	wg.Wait() // wait for services to go Down()
   281  
   282  	if nil != err {
   283  		t.Fatalf("Daemon() exited with error [case 1b]: == %v", err)
   284  	}
   285  
   286  	// Relaunch an instance of proxyfsd (without Volume:TestVolume.AutoFormat=true)
   287  
   288  	errChan = make(chan error, 1) // Must be buffered to avoid race
   289  
   290  	execArgs = append([]string{"daemon_test", testVersionConfFileName}, confMapStrings...)
   291  	go Daemon(testVersionConfFileName, confMapStrings, errChan, &wg,
   292  		execArgs, unix.SIGTERM, unix.SIGHUP)
   293  
   294  	err = <-errChan
   295  	if nil != err {
   296  		t.Fatalf("Daemon() startup failed [case 2a]: %v", err)
   297  	}
   298  
   299  	// Verify written data after restart
   300  
   301  	volumeHandle, err = fs.FetchVolumeHandleByVolumeName("CommonVolume")
   302  	if nil != err {
   303  		t.Fatalf("fs.FetchVolumeHandleByVolumeName() failed [case 2]: %v", err)
   304  	}
   305  
   306  	toReadFileInodeNumber, err = volumeHandle.Lookup(
   307  		inode.InodeRootUserID,
   308  		inode.InodeGroupID(0),
   309  		nil,
   310  		inode.RootDirInodeNumber,
   311  		"TestFile",
   312  	)
   313  	if nil != err {
   314  		t.Fatalf("fs.Lookup() failed [case 2]: %v", err)
   315  	}
   316  
   317  	readData, err = volumeHandle.Read(
   318  		inode.InodeRootUserID,
   319  		inode.InodeGroupID(0),
   320  		nil,
   321  		toReadFileInodeNumber,
   322  		0,
   323  		4,
   324  		nil,
   325  	)
   326  	if nil != err {
   327  		t.Fatalf("fs.Read() failed [case 2]: %v", err)
   328  	}
   329  	if 0 != bytes.Compare([]byte{0x00, 0x01, 0x02, 0x03}, readData) {
   330  		t.Fatalf("fs.Read() returned unexpected readData [case 2]")
   331  	}
   332  
   333  	// Verify [TestVersionSection]Version == 1
   334  
   335  	testVersion = fetchTestVersionSectionDotVersion(t)
   336  	if 1 != testVersion {
   337  		t.Fatalf("Before SIGHUP, fetchTestVersionSectionDotVersion() should have returned 1")
   338  	}
   339  
   340  	// Update testVersionConfFileName to bump [TestVersionSection]Version to 2
   341  
   342  	testVersionConfFile, err = os.OpenFile(testVersionConfFileName, os.O_TRUNC|os.O_WRONLY, 0)
   343  	if nil != err {
   344  		t.Fatalf("os.OpenFile() failed: %v", err)
   345  	}
   346  
   347  	_, err = testVersionConfFile.WriteString("[TestVersionSection]\n")
   348  	if nil != err {
   349  		t.Fatalf("testVersionConfFile.WriteString() [case 3] failed: %v", err)
   350  	}
   351  	_, err = testVersionConfFile.WriteString("Version: 2\n")
   352  	if nil != err {
   353  		t.Fatalf("testVersionConfFile.WriteString() [case 4] failed: %v", err)
   354  	}
   355  
   356  	err = testVersionConfFile.Close()
   357  	if nil != err {
   358  		t.Fatalf("testVersionConfFile.Close() [case 2] failed: %v", err)
   359  	}
   360  
   361  	// Send ourself a SIGHUP to signal reload of testVersionConfFileName
   362  	//   and wait a sufficient amount of time for the reload to complete
   363  
   364  	unix.Kill(unix.Getpid(), unix.SIGHUP)
   365  	time.Sleep(time.Second)
   366  
   367  	// Verify [TestVersionSection]Version == 2
   368  
   369  	testVersion = fetchTestVersionSectionDotVersion(t)
   370  	if 2 != testVersion {
   371  		t.Fatalf("After SIGHUP, fetchTestVersionSectionDotVersion() should have returned 2")
   372  	}
   373  
   374  	// Send ourself a SIGTERM to signal normal termination of mainWithArgs()
   375  
   376  	unix.Kill(unix.Getpid(), unix.SIGTERM)
   377  
   378  	err = <-errChan
   379  
   380  	wg.Wait() // wait for services to go Down()
   381  
   382  	if nil != err {
   383  		t.Fatalf("Daemon() exited with error [case 2b]: %v", err)
   384  	}
   385  
   386  	// Send ourself a SIGINT to also terminate ramswift
   387  
   388  	unix.Kill(unix.Getpid(), unix.SIGINT)
   389  
   390  	_ = <-ramswiftDoneChan
   391  
   392  	// Clean up
   393  
   394  	err = os.Remove(testVersionConfFileName)
   395  	if nil != err {
   396  		t.Fatalf("os.Remove(testVersionConfFileName) failed: %v", err)
   397  	}
   398  }
   399  
   400  func fetchTestVersionSectionDotVersion(t *testing.T) (version uint64) {
   401  	var (
   402  		body                                         []byte
   403  		bodySections                                 map[string]interface{}
   404  		err                                          error
   405  		ok                                           bool
   406  		resp                                         *http.Response
   407  		testVersionSection                           interface{}
   408  		testVersionSectionMap                        map[string]interface{}
   409  		testVersionSectionMapVersion                 interface{}
   410  		testVersionSectionMapVersionSlice            []interface{}
   411  		testVersionSectionMapVersionSliceElementZero interface{}
   412  		versionAsString                              string
   413  	)
   414  
   415  	resp, err = http.Get("http://127.0.0.1:53461/config")
   416  	if nil != err {
   417  		t.Fatalf("http.Get() failed: %v", err)
   418  	}
   419  	body, err = ioutil.ReadAll(resp.Body)
   420  	if nil != err {
   421  		t.Fatalf("ioutil.ReadAll() failed: %v", err)
   422  	}
   423  	err = resp.Body.Close()
   424  	if nil != err {
   425  		t.Fatalf("resp.Body.Close() failed: %v", err)
   426  	}
   427  	bodySections = make(map[string]interface{})
   428  	err = json.Unmarshal(body, &bodySections)
   429  	if nil != err {
   430  		t.Fatalf("json.Unmarshal() failed: %v", err)
   431  	}
   432  	testVersionSection, ok = bodySections["TestVersionSection"]
   433  	if !ok {
   434  		t.Fatalf("bodySections[\"TestVersionSection\"] not found")
   435  	}
   436  	testVersionSectionMap, ok = testVersionSection.(map[string]interface{})
   437  	if !ok {
   438  		t.Fatalf("testVersionSection.(map[string]interface{}) failed")
   439  	}
   440  	testVersionSectionMapVersion, ok = testVersionSectionMap["Version"]
   441  	if !ok {
   442  		t.Fatalf("testVersionSectionMap[\"Version\"] not found")
   443  	}
   444  	testVersionSectionMapVersionSlice, ok = testVersionSectionMapVersion.([]interface{})
   445  	if !ok {
   446  		t.Fatalf("testVersionSectionMapVersion.([]interface{}) failed")
   447  	}
   448  	if 1 != len(testVersionSectionMapVersionSlice) {
   449  		t.Fatalf("testVersionSectionMapVersionSlice should have a single element")
   450  	}
   451  	testVersionSectionMapVersionSliceElementZero = testVersionSectionMapVersionSlice[0]
   452  	versionAsString, ok = testVersionSectionMapVersionSliceElementZero.(string)
   453  	if !ok {
   454  		t.Fatalf("testVersionSectionMapVersionSliceElementZero.(string) failed")
   455  	}
   456  	version, err = strconv.ParseUint(versionAsString, 10, 64)
   457  	if nil != err {
   458  		t.Fatalf("strconv(versionAsString, 10, 64) failed: %v", err)
   459  	}
   460  
   461  	return
   462  }