@@ -2038,6 +2038,76 @@ func TestBackendLockedWithFile(t *testing.T) {
20382038 backend .TestBackendStateForceUnlock (t , b1 , b2 )
20392039}
20402040
2041+ func TestBackendLockedWithFile_ObjectLock_Compliance (t * testing.T ) {
2042+ testACC (t )
2043+ objectLockPreCheck (t )
2044+
2045+ ctx := context .TODO ()
2046+
2047+ bucketName := fmt .Sprintf ("terraform-remote-s3-test-%x" , time .Now ().Unix ())
2048+ keyName := "test/state"
2049+
2050+ b1 := backend .TestBackendConfig (t , New (), backend .TestWrapConfig (map [string ]interface {}{
2051+ "bucket" : bucketName ,
2052+ "key" : keyName ,
2053+ "encrypt" : true ,
2054+ "use_lockfile" : true ,
2055+ "region" : "us-west-2" ,
2056+ })).(* Backend )
2057+
2058+ b2 := backend .TestBackendConfig (t , New (), backend .TestWrapConfig (map [string ]interface {}{
2059+ "bucket" : bucketName ,
2060+ "key" : keyName ,
2061+ "encrypt" : true ,
2062+ "use_lockfile" : true ,
2063+ "region" : "us-west-2" ,
2064+ })).(* Backend )
2065+
2066+ createS3Bucket (ctx , t , b1 .s3Client , bucketName , b1 .awsConfig .Region ,
2067+ s3BucketWithVersioning ,
2068+ s3BucketWithObjectLock (s3types .ObjectLockRetentionModeCompliance ),
2069+ )
2070+ defer deleteS3Bucket (ctx , t , b1 .s3Client , bucketName , b1 .awsConfig .Region )
2071+
2072+ backend .TestBackendStateLocks (t , b1 , b2 )
2073+ backend .TestBackendStateForceUnlock (t , b1 , b2 )
2074+ }
2075+
2076+ func TestBackendLockedWithFile_ObjectLock_Governance (t * testing.T ) {
2077+ testACC (t )
2078+ objectLockPreCheck (t )
2079+
2080+ ctx := context .TODO ()
2081+
2082+ bucketName := fmt .Sprintf ("terraform-remote-s3-test-%x" , time .Now ().Unix ())
2083+ keyName := "test/state"
2084+
2085+ b1 := backend .TestBackendConfig (t , New (), backend .TestWrapConfig (map [string ]interface {}{
2086+ "bucket" : bucketName ,
2087+ "key" : keyName ,
2088+ "encrypt" : true ,
2089+ "use_lockfile" : true ,
2090+ "region" : "us-west-2" ,
2091+ })).(* Backend )
2092+
2093+ b2 := backend .TestBackendConfig (t , New (), backend .TestWrapConfig (map [string ]interface {}{
2094+ "bucket" : bucketName ,
2095+ "key" : keyName ,
2096+ "encrypt" : true ,
2097+ "use_lockfile" : true ,
2098+ "region" : "us-west-2" ,
2099+ })).(* Backend )
2100+
2101+ createS3Bucket (ctx , t , b1 .s3Client , bucketName , b1 .awsConfig .Region ,
2102+ s3BucketWithVersioning ,
2103+ s3BucketWithObjectLock (s3types .ObjectLockRetentionModeGovernance ),
2104+ )
2105+ defer deleteS3Bucket (ctx , t , b1 .s3Client , bucketName , b1 .awsConfig .Region )
2106+
2107+ backend .TestBackendStateLocks (t , b1 , b2 )
2108+ backend .TestBackendStateForceUnlock (t , b1 , b2 )
2109+ }
2110+
20412111func TestBackendLockedWithFileAndDynamoDB (t * testing.T ) {
20422112 testACC (t )
20432113
@@ -2158,6 +2228,116 @@ func TestBackend_LockFileCleanupOnDynamoDBLock(t *testing.T) {
21582228 }
21592229}
21602230
2231+ func TestBackend_LockFileCleanupOnDynamoDBLock_ObjectLock_Compliance (t * testing.T ) {
2232+ testACC (t )
2233+ objectLockPreCheck (t )
2234+
2235+ ctx := context .TODO ()
2236+
2237+ bucketName := fmt .Sprintf ("terraform-remote-s3-test-%x" , time .Now ().Unix ())
2238+ keyName := "test/state"
2239+
2240+ b1 := backend .TestBackendConfig (t , New (), backend .TestWrapConfig (map [string ]interface {}{
2241+ "bucket" : bucketName ,
2242+ "key" : keyName ,
2243+ "encrypt" : true ,
2244+ "use_lockfile" : false , // Only use DynamoDB
2245+ "dynamodb_table" : bucketName ,
2246+ "region" : "us-west-2" ,
2247+ })).(* Backend )
2248+
2249+ b2 := backend .TestBackendConfig (t , New (), backend .TestWrapConfig (map [string ]interface {}{
2250+ "bucket" : bucketName ,
2251+ "key" : keyName ,
2252+ "encrypt" : true ,
2253+ "use_lockfile" : true , // Use both DynamoDB and lockfile
2254+ "dynamodb_table" : bucketName ,
2255+ "region" : "us-west-2" ,
2256+ })).(* Backend )
2257+
2258+ createS3Bucket (ctx , t , b1 .s3Client , bucketName , b1 .awsConfig .Region ,
2259+ s3BucketWithVersioning ,
2260+ s3BucketWithObjectLock (s3types .ObjectLockRetentionModeCompliance ),
2261+ )
2262+ defer deleteS3Bucket (ctx , t , b1 .s3Client , bucketName , b1 .awsConfig .Region )
2263+ createDynamoDBTable (ctx , t , b1 .dynClient , bucketName )
2264+ defer deleteDynamoDBTable (ctx , t , b1 .dynClient , bucketName )
2265+
2266+ backend .TestBackendStateLocks (t , b1 , b2 )
2267+
2268+ // Attempt to retrieve the lock file from S3.
2269+ _ , err := b1 .s3Client .GetObject (ctx , & s3.GetObjectInput {
2270+ Bucket : aws .String (b1 .bucketName ),
2271+ Key : aws .String (b1 .keyName + ".tflock" ),
2272+ })
2273+ // We expect an error here, indicating that the lock file does not exist.
2274+ // The absence of the lock file is expected, as it should have been
2275+ // cleaned up following a failed lock acquisition due to `b1` already
2276+ // acquiring a DynamoDB lock.
2277+ if err != nil {
2278+ if ! IsA [* s3types.NoSuchKey ](err ) {
2279+ t .Fatalf ("unexpected error: %s" , err )
2280+ }
2281+ } else {
2282+ t .Fatalf ("expected error, got none" )
2283+ }
2284+ }
2285+
2286+ func TestBackend_LockFileCleanupOnDynamoDBLock_ObjectLock_Governance (t * testing.T ) {
2287+ testACC (t )
2288+ objectLockPreCheck (t )
2289+
2290+ ctx := context .TODO ()
2291+
2292+ bucketName := fmt .Sprintf ("terraform-remote-s3-test-%x" , time .Now ().Unix ())
2293+ keyName := "test/state"
2294+
2295+ b1 := backend .TestBackendConfig (t , New (), backend .TestWrapConfig (map [string ]interface {}{
2296+ "bucket" : bucketName ,
2297+ "key" : keyName ,
2298+ "encrypt" : true ,
2299+ "use_lockfile" : false , // Only use DynamoDB
2300+ "dynamodb_table" : bucketName ,
2301+ "region" : "us-west-2" ,
2302+ })).(* Backend )
2303+
2304+ b2 := backend .TestBackendConfig (t , New (), backend .TestWrapConfig (map [string ]interface {}{
2305+ "bucket" : bucketName ,
2306+ "key" : keyName ,
2307+ "encrypt" : true ,
2308+ "use_lockfile" : true , // Use both DynamoDB and lockfile
2309+ "dynamodb_table" : bucketName ,
2310+ "region" : "us-west-2" ,
2311+ })).(* Backend )
2312+
2313+ createS3Bucket (ctx , t , b1 .s3Client , bucketName , b1 .awsConfig .Region ,
2314+ s3BucketWithVersioning ,
2315+ s3BucketWithObjectLock (s3types .ObjectLockRetentionModeGovernance ),
2316+ )
2317+ defer deleteS3Bucket (ctx , t , b1 .s3Client , bucketName , b1 .awsConfig .Region )
2318+ createDynamoDBTable (ctx , t , b1 .dynClient , bucketName )
2319+ defer deleteDynamoDBTable (ctx , t , b1 .dynClient , bucketName )
2320+
2321+ backend .TestBackendStateLocks (t , b1 , b2 )
2322+
2323+ // Attempt to retrieve the lock file from S3.
2324+ _ , err := b1 .s3Client .GetObject (ctx , & s3.GetObjectInput {
2325+ Bucket : aws .String (b1 .bucketName ),
2326+ Key : aws .String (b1 .keyName + ".tflock" ),
2327+ })
2328+ // We expect an error here, indicating that the lock file does not exist.
2329+ // The absence of the lock file is expected, as it should have been
2330+ // cleaned up following a failed lock acquisition due to `b1` already
2331+ // acquiring a DynamoDB lock.
2332+ if err != nil {
2333+ if ! IsA [* s3types.NoSuchKey ](err ) {
2334+ t .Fatalf ("unexpected error: %s" , err )
2335+ }
2336+ } else {
2337+ t .Fatalf ("expected error, got none" )
2338+ }
2339+ }
2340+
21612341func TestBackend_LockDeletedOutOfBand (t * testing.T ) {
21622342 testACC (t )
21632343
0 commit comments