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