github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/builtin/providers/digitalocean/resource_digitalocean_droplet_test.go (about) 1 package digitalocean 2 3 import ( 4 "context" 5 "fmt" 6 "strconv" 7 "strings" 8 "testing" 9 10 "github.com/digitalocean/godo" 11 "github.com/hashicorp/terraform/helper/acctest" 12 "github.com/hashicorp/terraform/helper/resource" 13 "github.com/hashicorp/terraform/terraform" 14 ) 15 16 func TestAccDigitalOceanDroplet_Basic(t *testing.T) { 17 var droplet godo.Droplet 18 rInt := acctest.RandInt() 19 20 resource.Test(t, resource.TestCase{ 21 PreCheck: func() { testAccPreCheck(t) }, 22 Providers: testAccProviders, 23 CheckDestroy: testAccCheckDigitalOceanDropletDestroy, 24 Steps: []resource.TestStep{ 25 { 26 Config: testAccCheckDigitalOceanDropletConfig_basic(rInt), 27 Check: resource.ComposeTestCheckFunc( 28 testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), 29 testAccCheckDigitalOceanDropletAttributes(&droplet), 30 resource.TestCheckResourceAttr( 31 "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), 32 resource.TestCheckResourceAttr( 33 "digitalocean_droplet.foobar", "size", "512mb"), 34 resource.TestCheckResourceAttr( 35 "digitalocean_droplet.foobar", "price_hourly", "0.00744"), 36 resource.TestCheckResourceAttr( 37 "digitalocean_droplet.foobar", "price_monthly", "5"), 38 resource.TestCheckResourceAttr( 39 "digitalocean_droplet.foobar", "image", "centos-7-x64"), 40 resource.TestCheckResourceAttr( 41 "digitalocean_droplet.foobar", "region", "nyc3"), 42 resource.TestCheckResourceAttr( 43 "digitalocean_droplet.foobar", "user_data", "foobar"), 44 ), 45 }, 46 }, 47 }) 48 } 49 50 func TestAccDigitalOceanDroplet_WithID(t *testing.T) { 51 var droplet godo.Droplet 52 rInt := acctest.RandInt() 53 // TODO: not hardcode this as it will change over time 54 centosID := 22995941 55 56 resource.Test(t, resource.TestCase{ 57 PreCheck: func() { testAccPreCheck(t) }, 58 Providers: testAccProviders, 59 CheckDestroy: testAccCheckDigitalOceanDropletDestroy, 60 Steps: []resource.TestStep{ 61 { 62 Config: testAccCheckDigitalOceanDropletConfig_withID(centosID, rInt), 63 Check: resource.ComposeTestCheckFunc( 64 testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), 65 ), 66 }, 67 }, 68 }) 69 } 70 func TestAccDigitalOceanDroplet_withSSH(t *testing.T) { 71 var droplet godo.Droplet 72 rInt := acctest.RandInt() 73 74 resource.Test(t, resource.TestCase{ 75 PreCheck: func() { testAccPreCheck(t) }, 76 Providers: testAccProviders, 77 CheckDestroy: testAccCheckDigitalOceanDropletDestroy, 78 Steps: []resource.TestStep{ 79 { 80 Config: testAccCheckDigitalOceanDropletConfig_withSSH(rInt), 81 Check: resource.ComposeTestCheckFunc( 82 testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), 83 testAccCheckDigitalOceanDropletAttributes(&droplet), 84 resource.TestCheckResourceAttr( 85 "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), 86 resource.TestCheckResourceAttr( 87 "digitalocean_droplet.foobar", "size", "512mb"), 88 resource.TestCheckResourceAttr( 89 "digitalocean_droplet.foobar", "image", "centos-7-x64"), 90 resource.TestCheckResourceAttr( 91 "digitalocean_droplet.foobar", "region", "nyc3"), 92 resource.TestCheckResourceAttr( 93 "digitalocean_droplet.foobar", "user_data", "foobar"), 94 ), 95 }, 96 }, 97 }) 98 } 99 100 func TestAccDigitalOceanDroplet_Update(t *testing.T) { 101 var droplet godo.Droplet 102 rInt := acctest.RandInt() 103 104 resource.Test(t, resource.TestCase{ 105 PreCheck: func() { testAccPreCheck(t) }, 106 Providers: testAccProviders, 107 CheckDestroy: testAccCheckDigitalOceanDropletDestroy, 108 Steps: []resource.TestStep{ 109 { 110 Config: testAccCheckDigitalOceanDropletConfig_basic(rInt), 111 Check: resource.ComposeTestCheckFunc( 112 testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), 113 testAccCheckDigitalOceanDropletAttributes(&droplet), 114 resource.TestCheckResourceAttr( 115 "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), 116 ), 117 }, 118 119 { 120 Config: testAccCheckDigitalOceanDropletConfig_RenameAndResize(rInt), 121 Check: resource.ComposeTestCheckFunc( 122 testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), 123 testAccCheckDigitalOceanDropletRenamedAndResized(&droplet), 124 resource.TestCheckResourceAttr( 125 "digitalocean_droplet.foobar", "name", fmt.Sprintf("baz-%d", rInt)), 126 resource.TestCheckResourceAttr( 127 "digitalocean_droplet.foobar", "size", "1gb"), 128 resource.TestCheckResourceAttr( 129 "digitalocean_droplet.foobar", "disk", "30"), 130 ), 131 }, 132 }, 133 }) 134 } 135 136 func TestAccDigitalOceanDroplet_ResizeWithOutDisk(t *testing.T) { 137 var droplet godo.Droplet 138 rInt := acctest.RandInt() 139 140 resource.Test(t, resource.TestCase{ 141 PreCheck: func() { testAccPreCheck(t) }, 142 Providers: testAccProviders, 143 CheckDestroy: testAccCheckDigitalOceanDropletDestroy, 144 Steps: []resource.TestStep{ 145 { 146 Config: testAccCheckDigitalOceanDropletConfig_basic(rInt), 147 Check: resource.ComposeTestCheckFunc( 148 testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), 149 testAccCheckDigitalOceanDropletAttributes(&droplet), 150 resource.TestCheckResourceAttr( 151 "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), 152 ), 153 }, 154 155 { 156 Config: testAccCheckDigitalOceanDropletConfig_resize_without_disk(rInt), 157 Check: resource.ComposeTestCheckFunc( 158 testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), 159 testAccCheckDigitalOceanDropletResizeWithOutDisk(&droplet), 160 resource.TestCheckResourceAttr( 161 "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), 162 resource.TestCheckResourceAttr( 163 "digitalocean_droplet.foobar", "size", "1gb"), 164 resource.TestCheckResourceAttr( 165 "digitalocean_droplet.foobar", "disk", "20"), 166 ), 167 }, 168 }, 169 }) 170 } 171 172 func TestAccDigitalOceanDroplet_ResizeOnlyDisk(t *testing.T) { 173 var droplet godo.Droplet 174 rInt := acctest.RandInt() 175 176 resource.Test(t, resource.TestCase{ 177 PreCheck: func() { testAccPreCheck(t) }, 178 Providers: testAccProviders, 179 CheckDestroy: testAccCheckDigitalOceanDropletDestroy, 180 Steps: []resource.TestStep{ 181 { 182 Config: testAccCheckDigitalOceanDropletConfig_basic(rInt), 183 Check: resource.ComposeTestCheckFunc( 184 testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), 185 testAccCheckDigitalOceanDropletAttributes(&droplet), 186 resource.TestCheckResourceAttr( 187 "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), 188 ), 189 }, 190 191 { 192 Config: testAccCheckDigitalOceanDropletConfig_resize_without_disk(rInt), 193 Check: resource.ComposeTestCheckFunc( 194 testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), 195 testAccCheckDigitalOceanDropletResizeWithOutDisk(&droplet), 196 resource.TestCheckResourceAttr( 197 "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), 198 resource.TestCheckResourceAttr( 199 "digitalocean_droplet.foobar", "size", "1gb"), 200 resource.TestCheckResourceAttr( 201 "digitalocean_droplet.foobar", "disk", "20"), 202 ), 203 }, 204 205 { 206 Config: testAccCheckDigitalOceanDropletConfig_resize_only_disk(rInt), 207 Check: resource.ComposeTestCheckFunc( 208 testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), 209 testAccCheckDigitalOceanDropletResizeOnlyDisk(&droplet), 210 resource.TestCheckResourceAttr( 211 "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), 212 resource.TestCheckResourceAttr( 213 "digitalocean_droplet.foobar", "size", "1gb"), 214 resource.TestCheckResourceAttr( 215 "digitalocean_droplet.foobar", "disk", "30"), 216 ), 217 }, 218 }, 219 }) 220 } 221 222 func TestAccDigitalOceanDroplet_UpdateUserData(t *testing.T) { 223 var afterCreate, afterUpdate godo.Droplet 224 rInt := acctest.RandInt() 225 226 resource.Test(t, resource.TestCase{ 227 PreCheck: func() { testAccPreCheck(t) }, 228 Providers: testAccProviders, 229 CheckDestroy: testAccCheckDigitalOceanDropletDestroy, 230 Steps: []resource.TestStep{ 231 { 232 Config: testAccCheckDigitalOceanDropletConfig_basic(rInt), 233 Check: resource.ComposeTestCheckFunc( 234 testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &afterCreate), 235 testAccCheckDigitalOceanDropletAttributes(&afterCreate), 236 resource.TestCheckResourceAttr( 237 "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), 238 ), 239 }, 240 241 { 242 Config: testAccCheckDigitalOceanDropletConfig_userdata_update(rInt), 243 Check: resource.ComposeTestCheckFunc( 244 testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &afterUpdate), 245 resource.TestCheckResourceAttr( 246 "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), 247 resource.TestCheckResourceAttr( 248 "digitalocean_droplet.foobar", 249 "user_data", 250 "foobar foobar"), 251 testAccCheckDigitalOceanDropletRecreated( 252 t, &afterCreate, &afterUpdate), 253 ), 254 }, 255 }, 256 }) 257 } 258 259 func TestAccDigitalOceanDroplet_UpdateTags(t *testing.T) { 260 var afterCreate, afterUpdate godo.Droplet 261 rInt := acctest.RandInt() 262 263 resource.Test(t, resource.TestCase{ 264 PreCheck: func() { testAccPreCheck(t) }, 265 Providers: testAccProviders, 266 CheckDestroy: testAccCheckDigitalOceanDropletDestroy, 267 Steps: []resource.TestStep{ 268 { 269 Config: testAccCheckDigitalOceanDropletConfig_basic(rInt), 270 Check: resource.ComposeTestCheckFunc( 271 testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &afterCreate), 272 testAccCheckDigitalOceanDropletAttributes(&afterCreate), 273 resource.TestCheckResourceAttr( 274 "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), 275 ), 276 }, 277 278 { 279 Config: testAccCheckDigitalOceanDropletConfig_tag_update(rInt), 280 Check: resource.ComposeTestCheckFunc( 281 testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &afterUpdate), 282 resource.TestCheckResourceAttr( 283 "digitalocean_droplet.foobar", "name", fmt.Sprintf("foo-%d", rInt)), 284 resource.TestCheckResourceAttr( 285 "digitalocean_droplet.foobar", 286 "tags.#", 287 "1"), 288 resource.TestCheckResourceAttr( 289 "digitalocean_droplet.foobar", 290 "tags.0", 291 "barbaz"), 292 ), 293 }, 294 }, 295 }) 296 } 297 298 func TestAccDigitalOceanDroplet_PrivateNetworkingIpv6(t *testing.T) { 299 var droplet godo.Droplet 300 rInt := acctest.RandInt() 301 302 resource.Test(t, resource.TestCase{ 303 PreCheck: func() { testAccPreCheck(t) }, 304 Providers: testAccProviders, 305 CheckDestroy: testAccCheckDigitalOceanDropletDestroy, 306 Steps: []resource.TestStep{ 307 { 308 Config: testAccCheckDigitalOceanDropletConfig_PrivateNetworkingIpv6(rInt), 309 Check: resource.ComposeTestCheckFunc( 310 testAccCheckDigitalOceanDropletExists("digitalocean_droplet.foobar", &droplet), 311 testAccCheckDigitalOceanDropletAttributes_PrivateNetworkingIpv6(&droplet), 312 resource.TestCheckResourceAttr( 313 "digitalocean_droplet.foobar", "private_networking", "true"), 314 resource.TestCheckResourceAttr( 315 "digitalocean_droplet.foobar", "ipv6", "true"), 316 ), 317 }, 318 }, 319 }) 320 } 321 322 func testAccCheckDigitalOceanDropletDestroy(s *terraform.State) error { 323 client := testAccProvider.Meta().(*godo.Client) 324 325 for _, rs := range s.RootModule().Resources { 326 if rs.Type != "digitalocean_droplet" { 327 continue 328 } 329 330 id, err := strconv.Atoi(rs.Primary.ID) 331 if err != nil { 332 return err 333 } 334 335 // Try to find the Droplet 336 _, _, err = client.Droplets.Get(context.Background(), id) 337 338 // Wait 339 340 if err != nil && !strings.Contains(err.Error(), "404") { 341 return fmt.Errorf( 342 "Error waiting for droplet (%s) to be destroyed: %s", 343 rs.Primary.ID, err) 344 } 345 } 346 347 return nil 348 } 349 350 func testAccCheckDigitalOceanDropletAttributes(droplet *godo.Droplet) resource.TestCheckFunc { 351 return func(s *terraform.State) error { 352 353 if droplet.Image.Slug != "centos-7-x64" { 354 return fmt.Errorf("Bad image_slug: %s", droplet.Image.Slug) 355 } 356 357 if droplet.Size.Slug != "512mb" { 358 return fmt.Errorf("Bad size_slug: %s", droplet.Size.Slug) 359 } 360 361 if droplet.Size.PriceHourly != 0.00744 { 362 return fmt.Errorf("Bad price_hourly: %v", droplet.Size.PriceHourly) 363 } 364 365 if droplet.Size.PriceMonthly != 5.0 { 366 return fmt.Errorf("Bad price_monthly: %v", droplet.Size.PriceMonthly) 367 } 368 369 if droplet.Region.Slug != "nyc3" { 370 return fmt.Errorf("Bad region_slug: %s", droplet.Region.Slug) 371 } 372 373 return nil 374 } 375 } 376 377 func testAccCheckDigitalOceanDropletRenamedAndResized(droplet *godo.Droplet) resource.TestCheckFunc { 378 return func(s *terraform.State) error { 379 380 if droplet.Size.Slug != "1gb" { 381 return fmt.Errorf("Bad size_slug: %s", droplet.SizeSlug) 382 } 383 384 if droplet.Disk != 30 { 385 return fmt.Errorf("Bad disk: %d", droplet.Disk) 386 } 387 388 return nil 389 } 390 } 391 392 func testAccCheckDigitalOceanDropletResizeWithOutDisk(droplet *godo.Droplet) resource.TestCheckFunc { 393 return func(s *terraform.State) error { 394 395 if droplet.Size.Slug != "1gb" { 396 return fmt.Errorf("Bad size_slug: %s", droplet.SizeSlug) 397 } 398 399 if droplet.Disk != 20 { 400 return fmt.Errorf("Bad disk: %d", droplet.Disk) 401 } 402 403 return nil 404 } 405 } 406 407 func testAccCheckDigitalOceanDropletResizeOnlyDisk(droplet *godo.Droplet) resource.TestCheckFunc { 408 return func(s *terraform.State) error { 409 410 if droplet.Size.Slug != "1gb" { 411 return fmt.Errorf("Bad size_slug: %s", droplet.SizeSlug) 412 } 413 414 if droplet.Disk != 30 { 415 return fmt.Errorf("Bad disk: %d", droplet.Disk) 416 } 417 418 return nil 419 } 420 } 421 422 func testAccCheckDigitalOceanDropletAttributes_PrivateNetworkingIpv6(droplet *godo.Droplet) resource.TestCheckFunc { 423 return func(s *terraform.State) error { 424 425 if droplet.Image.Slug != "centos-7-x64" { 426 return fmt.Errorf("Bad image_slug: %s", droplet.Image.Slug) 427 } 428 429 if droplet.Size.Slug != "1gb" { 430 return fmt.Errorf("Bad size_slug: %s", droplet.Size.Slug) 431 } 432 433 if droplet.Region.Slug != "sgp1" { 434 return fmt.Errorf("Bad region_slug: %s", droplet.Region.Slug) 435 } 436 437 if findIPv4AddrByType(droplet, "private") == "" { 438 return fmt.Errorf("No ipv4 private: %s", findIPv4AddrByType(droplet, "private")) 439 } 440 441 // if droplet.IPV6Address("private") == "" { 442 // return fmt.Errorf("No ipv6 private: %s", droplet.IPV6Address("private")) 443 // } 444 445 if findIPv4AddrByType(droplet, "public") == "" { 446 return fmt.Errorf("No ipv4 public: %s", findIPv4AddrByType(droplet, "public")) 447 } 448 449 if findIPv6AddrByType(droplet, "public") == "" { 450 return fmt.Errorf("No ipv6 public: %s", findIPv6AddrByType(droplet, "public")) 451 } 452 453 for _, rs := range s.RootModule().Resources { 454 if rs.Type != "digitalocean_droplet" { 455 continue 456 } 457 if rs.Primary.Attributes["ipv6_address"] != strings.ToLower(findIPv6AddrByType(droplet, "public")) { 458 return fmt.Errorf("IPV6 Address should be lowercase") 459 } 460 461 } 462 463 return nil 464 } 465 } 466 467 func testAccCheckDigitalOceanDropletExists(n string, droplet *godo.Droplet) resource.TestCheckFunc { 468 return func(s *terraform.State) error { 469 rs, ok := s.RootModule().Resources[n] 470 if !ok { 471 return fmt.Errorf("Not found: %s", n) 472 } 473 474 if rs.Primary.ID == "" { 475 return fmt.Errorf("No Droplet ID is set") 476 } 477 478 client := testAccProvider.Meta().(*godo.Client) 479 480 id, err := strconv.Atoi(rs.Primary.ID) 481 if err != nil { 482 return err 483 } 484 485 // Try to find the Droplet 486 retrieveDroplet, _, err := client.Droplets.Get(context.Background(), id) 487 488 if err != nil { 489 return err 490 } 491 492 if strconv.Itoa(retrieveDroplet.ID) != rs.Primary.ID { 493 return fmt.Errorf("Droplet not found") 494 } 495 496 *droplet = *retrieveDroplet 497 498 return nil 499 } 500 } 501 502 func testAccCheckDigitalOceanDropletRecreated(t *testing.T, 503 before, after *godo.Droplet) resource.TestCheckFunc { 504 return func(s *terraform.State) error { 505 if before.ID == after.ID { 506 t.Fatalf("Expected change of droplet IDs, but both were %v", before.ID) 507 } 508 return nil 509 } 510 } 511 512 func testAccCheckDigitalOceanDropletConfig_basic(rInt int) string { 513 return fmt.Sprintf(` 514 resource "digitalocean_droplet" "foobar" { 515 name = "foo-%d" 516 size = "512mb" 517 image = "centos-7-x64" 518 region = "nyc3" 519 user_data = "foobar" 520 }`, rInt) 521 } 522 523 func testAccCheckDigitalOceanDropletConfig_withID(imageID, rInt int) string { 524 return fmt.Sprintf(` 525 resource "digitalocean_droplet" "foobar" { 526 name = "foo-%d" 527 size = "512mb" 528 image = "%d" 529 region = "nyc3" 530 user_data = "foobar" 531 }`, rInt, imageID) 532 } 533 534 func testAccCheckDigitalOceanDropletConfig_withSSH(rInt int) string { 535 return fmt.Sprintf(` 536 resource "digitalocean_ssh_key" "foobar" { 537 name = "foobar-%d" 538 public_key = "%s" 539 } 540 541 resource "digitalocean_droplet" "foobar" { 542 name = "foo-%d" 543 size = "512mb" 544 image = "centos-7-x64" 545 region = "nyc3" 546 user_data = "foobar" 547 ssh_keys = ["${digitalocean_ssh_key.foobar.id}"] 548 }`, rInt, testAccValidPublicKey, rInt) 549 } 550 551 func testAccCheckDigitalOceanDropletConfig_tag_update(rInt int) string { 552 return fmt.Sprintf(` 553 resource "digitalocean_tag" "barbaz" { 554 name = "barbaz" 555 } 556 557 resource "digitalocean_droplet" "foobar" { 558 name = "foo-%d" 559 size = "512mb" 560 image = "centos-7-x64" 561 region = "nyc3" 562 user_data = "foobar" 563 tags = ["${digitalocean_tag.barbaz.id}"] 564 } 565 `, rInt) 566 } 567 568 func testAccCheckDigitalOceanDropletConfig_userdata_update(rInt int) string { 569 return fmt.Sprintf(` 570 resource "digitalocean_droplet" "foobar" { 571 name = "foo-%d" 572 size = "512mb" 573 image = "centos-7-x64" 574 region = "nyc3" 575 user_data = "foobar foobar" 576 } 577 `, rInt) 578 } 579 580 func testAccCheckDigitalOceanDropletConfig_RenameAndResize(rInt int) string { 581 return fmt.Sprintf(` 582 resource "digitalocean_droplet" "foobar" { 583 name = "baz-%d" 584 size = "1gb" 585 image = "centos-7-x64" 586 region = "nyc3" 587 } 588 `, rInt) 589 } 590 591 func testAccCheckDigitalOceanDropletConfig_resize_without_disk(rInt int) string { 592 return fmt.Sprintf(` 593 resource "digitalocean_droplet" "foobar" { 594 name = "foo-%d" 595 size = "1gb" 596 image = "centos-7-x64" 597 region = "nyc3" 598 user_data = "foobar" 599 resize_disk = false 600 } 601 `, rInt) 602 } 603 604 func testAccCheckDigitalOceanDropletConfig_resize_only_disk(rInt int) string { 605 return fmt.Sprintf(` 606 resource "digitalocean_droplet" "foobar" { 607 name = "foo-%d" 608 size = "1gb" 609 image = "centos-7-x64" 610 region = "nyc3" 611 user_data = "foobar" 612 resize_disk = true 613 } 614 `, rInt) 615 } 616 617 // IPV6 only in singapore 618 func testAccCheckDigitalOceanDropletConfig_PrivateNetworkingIpv6(rInt int) string { 619 return fmt.Sprintf(` 620 resource "digitalocean_droplet" "foobar" { 621 name = "baz-%d" 622 size = "1gb" 623 image = "centos-7-x64" 624 region = "sgp1" 625 ipv6 = true 626 private_networking = true 627 } 628 `, rInt) 629 } 630 631 var testAccValidPublicKey = `ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVERRN7/9484SOBJ3HSKxxNG5JN8owAjy5f9yYwcUg+JaUVuytn5Pv3aeYROHGGg+5G346xaq3DAwX6Y5ykr2fvjObgncQBnuU5KHWCECO/4h8uWuwh/kfniXPVjFToc+gnkqA+3RKpAecZhFXwfalQ9mMuYGFxn+fwn8cYEApsJbsEmb0iJwPiZ5hjFC8wREuiTlhPHDgkBLOiycd20op2nXzDbHfCHInquEe/gYxEitALONxm0swBOwJZwlTDOB7C6y2dzlrtxr1L59m7pCkWI4EtTRLvleehBoj3u7jB4usR`