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 }