github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/backend/remote-state/oss/client_test.go (about) 1 package oss 2 3 import ( 4 "fmt" 5 "strings" 6 "testing" 7 "time" 8 9 "bytes" 10 "crypto/md5" 11 12 "github.com/hashicorp/terraform/internal/backend" 13 "github.com/hashicorp/terraform/internal/states/remote" 14 "github.com/hashicorp/terraform/internal/states/statefile" 15 "github.com/hashicorp/terraform/internal/states/statemgr" 16 ) 17 18 // NOTE: Before running this testcase, please create a OTS instance called 'tf-oss-remote' 19 var RemoteTestUsedOTSEndpoint = "https://tf-oss-remote.cn-hangzhou.ots.aliyuncs.com" 20 21 func TestRemoteClient_impl(t *testing.T) { 22 var _ remote.Client = new(RemoteClient) 23 var _ remote.ClientLocker = new(RemoteClient) 24 } 25 26 func TestRemoteClient(t *testing.T) { 27 testACC(t) 28 bucketName := fmt.Sprintf("tf-remote-oss-test-%x", time.Now().Unix()) 29 path := "testState" 30 31 b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ 32 "bucket": bucketName, 33 "prefix": path, 34 "encrypt": true, 35 })).(*Backend) 36 37 createOSSBucket(t, b.ossClient, bucketName) 38 defer deleteOSSBucket(t, b.ossClient, bucketName) 39 40 state, err := b.StateMgr(backend.DefaultStateName) 41 if err != nil { 42 t.Fatal(err) 43 } 44 45 remote.TestClient(t, state.(*remote.State).Client) 46 } 47 48 func TestRemoteClientLocks(t *testing.T) { 49 testACC(t) 50 bucketName := fmt.Sprintf("tf-remote-oss-test-%x", time.Now().Unix()) 51 tableName := fmt.Sprintf("tfRemoteTestForce%x", time.Now().Unix()) 52 path := "testState" 53 54 b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ 55 "bucket": bucketName, 56 "prefix": path, 57 "encrypt": true, 58 "tablestore_table": tableName, 59 "tablestore_endpoint": RemoteTestUsedOTSEndpoint, 60 })).(*Backend) 61 62 b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ 63 "bucket": bucketName, 64 "prefix": path, 65 "encrypt": true, 66 "tablestore_table": tableName, 67 "tablestore_endpoint": RemoteTestUsedOTSEndpoint, 68 })).(*Backend) 69 70 createOSSBucket(t, b1.ossClient, bucketName) 71 defer deleteOSSBucket(t, b1.ossClient, bucketName) 72 createTablestoreTable(t, b1.otsClient, tableName) 73 defer deleteTablestoreTable(t, b1.otsClient, tableName) 74 75 s1, err := b1.StateMgr(backend.DefaultStateName) 76 if err != nil { 77 t.Fatal(err) 78 } 79 80 s2, err := b2.StateMgr(backend.DefaultStateName) 81 if err != nil { 82 t.Fatal(err) 83 } 84 85 remote.TestRemoteLocks(t, s1.(*remote.State).Client, s2.(*remote.State).Client) 86 } 87 88 // verify that the backend can handle more than one state in the same table 89 func TestRemoteClientLocks_multipleStates(t *testing.T) { 90 testACC(t) 91 bucketName := fmt.Sprintf("tf-remote-oss-test-force-%x", time.Now().Unix()) 92 tableName := fmt.Sprintf("tfRemoteTestForce%x", time.Now().Unix()) 93 path := "testState" 94 95 b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ 96 "bucket": bucketName, 97 "prefix": path, 98 "encrypt": true, 99 "tablestore_table": tableName, 100 "tablestore_endpoint": RemoteTestUsedOTSEndpoint, 101 })).(*Backend) 102 103 b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ 104 "bucket": bucketName, 105 "prefix": path, 106 "encrypt": true, 107 "tablestore_table": tableName, 108 "tablestore_endpoint": RemoteTestUsedOTSEndpoint, 109 })).(*Backend) 110 111 createOSSBucket(t, b1.ossClient, bucketName) 112 defer deleteOSSBucket(t, b1.ossClient, bucketName) 113 createTablestoreTable(t, b1.otsClient, tableName) 114 defer deleteTablestoreTable(t, b1.otsClient, tableName) 115 116 s1, err := b1.StateMgr("s1") 117 if err != nil { 118 t.Fatal(err) 119 } 120 if _, err := s1.Lock(statemgr.NewLockInfo()); err != nil { 121 t.Fatal("failed to get lock for s1:", err) 122 } 123 124 // s1 is now locked, s2 should not be locked as it's a different state file 125 s2, err := b2.StateMgr("s2") 126 if err != nil { 127 t.Fatal(err) 128 } 129 if _, err := s2.Lock(statemgr.NewLockInfo()); err != nil { 130 t.Fatal("failed to get lock for s2:", err) 131 } 132 } 133 134 // verify that we can unlock a state with an existing lock 135 func TestRemoteForceUnlock(t *testing.T) { 136 testACC(t) 137 bucketName := fmt.Sprintf("tf-remote-oss-test-force-%x", time.Now().Unix()) 138 tableName := fmt.Sprintf("tfRemoteTestForce%x", time.Now().Unix()) 139 path := "testState" 140 141 b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ 142 "bucket": bucketName, 143 "prefix": path, 144 "encrypt": true, 145 "tablestore_table": tableName, 146 "tablestore_endpoint": RemoteTestUsedOTSEndpoint, 147 })).(*Backend) 148 149 b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ 150 "bucket": bucketName, 151 "prefix": path, 152 "encrypt": true, 153 "tablestore_table": tableName, 154 "tablestore_endpoint": RemoteTestUsedOTSEndpoint, 155 })).(*Backend) 156 157 createOSSBucket(t, b1.ossClient, bucketName) 158 defer deleteOSSBucket(t, b1.ossClient, bucketName) 159 createTablestoreTable(t, b1.otsClient, tableName) 160 defer deleteTablestoreTable(t, b1.otsClient, tableName) 161 162 // first test with default 163 s1, err := b1.StateMgr(backend.DefaultStateName) 164 if err != nil { 165 t.Fatal(err) 166 } 167 168 info := statemgr.NewLockInfo() 169 info.Operation = "test" 170 info.Who = "clientA" 171 172 lockID, err := s1.Lock(info) 173 if err != nil { 174 t.Fatal("unable to get initial lock:", err) 175 } 176 177 // s1 is now locked, get the same state through s2 and unlock it 178 s2, err := b2.StateMgr(backend.DefaultStateName) 179 if err != nil { 180 t.Fatal("failed to get default state to force unlock:", err) 181 } 182 183 if err := s2.Unlock(lockID); err != nil { 184 t.Fatal("failed to force-unlock default state") 185 } 186 187 // now try the same thing with a named state 188 // first test with default 189 s1, err = b1.StateMgr("test") 190 if err != nil { 191 t.Fatal(err) 192 } 193 194 info = statemgr.NewLockInfo() 195 info.Operation = "test" 196 info.Who = "clientA" 197 198 lockID, err = s1.Lock(info) 199 if err != nil { 200 t.Fatal("unable to get initial lock:", err) 201 } 202 203 // s1 is now locked, get the same state through s2 and unlock it 204 s2, err = b2.StateMgr("test") 205 if err != nil { 206 t.Fatal("failed to get named state to force unlock:", err) 207 } 208 209 if err = s2.Unlock(lockID); err != nil { 210 t.Fatal("failed to force-unlock named state") 211 } 212 } 213 214 func TestRemoteClient_clientMD5(t *testing.T) { 215 testACC(t) 216 217 bucketName := fmt.Sprintf("tf-remote-oss-test-%x", time.Now().Unix()) 218 tableName := fmt.Sprintf("tfRemoteTestForce%x", time.Now().Unix()) 219 path := "testState" 220 221 b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ 222 "bucket": bucketName, 223 "prefix": path, 224 "tablestore_table": tableName, 225 "tablestore_endpoint": RemoteTestUsedOTSEndpoint, 226 })).(*Backend) 227 228 createOSSBucket(t, b.ossClient, bucketName) 229 defer deleteOSSBucket(t, b.ossClient, bucketName) 230 createTablestoreTable(t, b.otsClient, tableName) 231 defer deleteTablestoreTable(t, b.otsClient, tableName) 232 233 s, err := b.StateMgr(backend.DefaultStateName) 234 if err != nil { 235 t.Fatal(err) 236 } 237 client := s.(*remote.State).Client.(*RemoteClient) 238 239 sum := md5.Sum([]byte("test")) 240 241 if err := client.putMD5(sum[:]); err != nil { 242 t.Fatal(err) 243 } 244 245 getSum, err := client.getMD5() 246 if err != nil { 247 t.Fatal(err) 248 } 249 250 if !bytes.Equal(getSum, sum[:]) { 251 t.Fatalf("getMD5 returned the wrong checksum: expected %x, got %x", sum[:], getSum) 252 } 253 254 if err := client.deleteMD5(); err != nil { 255 t.Fatal(err) 256 } 257 258 if getSum, err := client.getMD5(); err == nil { 259 t.Fatalf("expected getMD5 error, got none. checksum: %x", getSum) 260 } 261 } 262 263 // verify that a client won't return a state with an incorrect checksum. 264 func TestRemoteClient_stateChecksum(t *testing.T) { 265 testACC(t) 266 267 bucketName := fmt.Sprintf("tf-remote-oss-test-%x", time.Now().Unix()) 268 tableName := fmt.Sprintf("tfRemoteTestForce%x", time.Now().Unix()) 269 path := "testState" 270 271 b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ 272 "bucket": bucketName, 273 "prefix": path, 274 "tablestore_table": tableName, 275 "tablestore_endpoint": RemoteTestUsedOTSEndpoint, 276 })).(*Backend) 277 278 createOSSBucket(t, b1.ossClient, bucketName) 279 defer deleteOSSBucket(t, b1.ossClient, bucketName) 280 createTablestoreTable(t, b1.otsClient, tableName) 281 defer deleteTablestoreTable(t, b1.otsClient, tableName) 282 283 s1, err := b1.StateMgr(backend.DefaultStateName) 284 if err != nil { 285 t.Fatal(err) 286 } 287 client1 := s1.(*remote.State).Client 288 289 // create an old and new state version to persist 290 s := statemgr.TestFullInitialState() 291 sf := &statefile.File{State: s} 292 var oldState bytes.Buffer 293 if err := statefile.Write(sf, &oldState); err != nil { 294 t.Fatal(err) 295 } 296 sf.Serial++ 297 var newState bytes.Buffer 298 if err := statefile.Write(sf, &newState); err != nil { 299 t.Fatal(err) 300 } 301 302 // Use b2 without a tablestore_table to bypass the lock table to write the state directly. 303 // client2 will write the "incorrect" state, simulating oss eventually consistency delays 304 b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{ 305 "bucket": bucketName, 306 "prefix": path, 307 })).(*Backend) 308 s2, err := b2.StateMgr(backend.DefaultStateName) 309 if err != nil { 310 t.Fatal(err) 311 } 312 client2 := s2.(*remote.State).Client 313 314 // write the new state through client2 so that there is no checksum yet 315 if err := client2.Put(newState.Bytes()); err != nil { 316 t.Fatal(err) 317 } 318 319 // verify that we can pull a state without a checksum 320 if _, err := client1.Get(); err != nil { 321 t.Fatal(err) 322 } 323 324 // write the new state back with its checksum 325 if err := client1.Put(newState.Bytes()); err != nil { 326 t.Fatal(err) 327 } 328 329 // put an empty state in place to check for panics during get 330 if err := client2.Put([]byte{}); err != nil { 331 t.Fatal(err) 332 } 333 334 // remove the timeouts so we can fail immediately 335 origTimeout := consistencyRetryTimeout 336 origInterval := consistencyRetryPollInterval 337 defer func() { 338 consistencyRetryTimeout = origTimeout 339 consistencyRetryPollInterval = origInterval 340 }() 341 consistencyRetryTimeout = 0 342 consistencyRetryPollInterval = 0 343 344 // fetching an empty state through client1 should now error out due to a 345 // mismatched checksum. 346 if _, err := client1.Get(); !strings.HasPrefix(err.Error(), errBadChecksumFmt[:80]) { 347 t.Fatalf("expected state checksum error: got %s", err) 348 } 349 350 // put the old state in place of the new, without updating the checksum 351 if err := client2.Put(oldState.Bytes()); err != nil { 352 t.Fatal(err) 353 } 354 355 // fetching the wrong state through client1 should now error out due to a 356 // mismatched checksum. 357 if _, err := client1.Get(); !strings.HasPrefix(err.Error(), errBadChecksumFmt[:80]) { 358 t.Fatalf("expected state checksum error: got %s", err) 359 } 360 361 // update the state with the correct one after we Get again 362 testChecksumHook = func() { 363 if err := client2.Put(newState.Bytes()); err != nil { 364 t.Fatal(err) 365 } 366 testChecksumHook = nil 367 } 368 369 consistencyRetryTimeout = origTimeout 370 371 // this final Get will fail to fail the checksum verification, the above 372 // callback will update the state with the correct version, and Get should 373 // retry automatically. 374 if _, err := client1.Get(); err != nil { 375 t.Fatal(err) 376 } 377 }