cfn-bridge 0.0.9 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +50 -0
  3. data/lib/cloud_formation/bridge/cli.rb +4 -0
  4. data/lib/cloud_formation/bridge/executor.rb +10 -1
  5. data/lib/cloud_formation/bridge/names.rb +10 -0
  6. data/lib/cloud_formation/bridge/request.rb +7 -1
  7. data/lib/cloud_formation/bridge/resources/base.rb +14 -3
  8. data/lib/cloud_formation/bridge/resources/base_elasti_cache_resource.rb +58 -0
  9. data/lib/cloud_formation/bridge/resources/elasti_cache_node_urls.rb +35 -0
  10. data/lib/cloud_formation/bridge/resources/elasti_cache_replica_cluster.rb +66 -0
  11. data/lib/cloud_formation/bridge/resources/elasti_cache_replication_group.rb +67 -0
  12. data/lib/cloud_formation/bridge/util.rb +6 -0
  13. data/lib/cloud_formation/bridge/version.rb +1 -1
  14. data/spec/files/create-cache-node-urls-message.json +12 -0
  15. data/spec/files/create-redis-cache-node-urls-message.json +12 -0
  16. data/spec/files/create-replica-cluster-message.json +13 -0
  17. data/spec/files/create-replication-group-message.json +14 -0
  18. data/spec/files/delete-replica-cluster-message.json +14 -0
  19. data/spec/files/delete-replication-group-message.json +15 -0
  20. data/spec/files/describe-cache-cluster-primary.json +57 -0
  21. data/spec/files/describe-cache-cluster-replica-done.json +68 -0
  22. data/spec/files/describe-cache-cluster-replica.json +46 -0
  23. data/spec/files/describe-memcached-cluster.json +73 -0
  24. data/spec/files/describe-replication-group-primary-and-replica.json +49 -0
  25. data/spec/files/describe-replication-group-primary-only.json +38 -0
  26. data/spec/files/outputs-formation.json +0 -4
  27. data/spec/files/sample-replica-cluster.json +51 -0
  28. data/spec/files/sample-replication-group.json +34 -0
  29. data/spec/lib/cloud_formation/bridge/executor_spec.rb +1 -1
  30. data/spec/lib/cloud_formation/bridge/request_spec.rb +17 -5
  31. data/spec/lib/cloud_formation/bridge/resources/elasti_cache_node_urls_spec.rb +61 -0
  32. data/spec/lib/cloud_formation/bridge/resources/elasti_cache_replica_cluster_spec.rb +67 -0
  33. data/spec/lib/cloud_formation/bridge/resources/elasti_cache_replication_group_spec.rb +65 -0
  34. data/spec/support/file_support.rb +6 -0
  35. metadata +40 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c599dc50916a17cbdac3cc434096571eabda5e17
4
- data.tar.gz: f8fd491ff238e7e9d6d25325564be0a83b43d7f6
3
+ metadata.gz: 937f3058f2f853584b9d5340292cc7a9d188dea8
4
+ data.tar.gz: d5f84fe245dce34c6f46e095cb03a338c1d4c97c
5
5
  SHA512:
6
- metadata.gz: 27e437009e7f6af07d487c8e75f736fc1916a3c5588223a56e5fb265aa2c051b987919df5db7c8cfec6287236c5078a10d29c09ba1970650205a4ebde5b12ee6
7
- data.tar.gz: 7be976117430b59d4ab1216434121f75a7df62d80bc07aac66e305b7ee88e5f7cbd91640b6269306f18ae11c67b293e16f90996735aaef1e7cb02c2ae52677d0
6
+ metadata.gz: 4177d8b32ce517c52807ec0cff6d762a9ed4eb63ad327bd65779ece2d99af33d3fa04ac1741228b8fd8322bf371c915182c6359553cd0a9dae3313ad899adcd2
7
+ data.tar.gz: 5d0caaf158b0175de5159055a25b8bde177eec8605d82da9dbd0d90077e2d60800b3dfcbe83f776efb76a1c132e821e684700713e321aac82932bf0116f6b6dc
data/README.md CHANGED
@@ -13,6 +13,9 @@
13
13
  - [Current custom resources](#current-custom-resources)
14
14
  - [Custom::SubscribeSQSQueueToSNSTopic](#customsubscribesqsqueuetosnstopic)
15
15
  - [Custom::CloudFormationOutputs](#customcloudformationoutputs)
16
+ - [Custom::Custom::ElastiCacheReplicationGroup](#customcustomelasticachereplicationgroup)
17
+ - [Custom::ElastiCacheReplicaCluster](#customelasticachereplicacluster)
18
+ - [Custom::ElastiCacheNodeURLs](#customelasticachenodeurls)
16
19
  - [Contributing](#contributing)
17
20
 
18
21
  <!-- END doctoc generated TOC please keep comment here to allow auto update -->
@@ -194,6 +197,12 @@ It is declared just like any other custom resource with the name you have regist
194
197
 
195
198
  And with this you should be able to start creating your own custom cloud formation resources.
196
199
 
200
+ Gotchas you should be aware:
201
+
202
+ * Do not return `nil` on `Data` fields, your resource will not be created and you will not get any error message about this;
203
+ * Make sure all messages are logged somewhere (I'd recommend an email) so even if the service fails to create resources for some reason you can still work with the cloud formation manually;
204
+ * Direct your logs somewhere where you can easily look at, the command line interface will print everything to `STDOUT`, make sure you send this data to a file so you can look at what's going on;
205
+
197
206
  ## Current custom resources
198
207
 
199
208
  ### Custom::SubscribeSQSQueueToSNSTopic
@@ -214,6 +223,47 @@ Parameters:
214
223
 
215
224
  * `Name` - the name of the cloud formation you want to get the outputs from - *required*;
216
225
 
226
+
227
+ ### Custom::Custom::ElastiCacheReplicationGroup
228
+
229
+ Creates an `ElastiCache` replication group from an already available cache cluster (that will be configured as the primary).
230
+
231
+ Parameters:
232
+
233
+ * `ClusterId` - the name of the primary ElastiCache cluster for this replication group;
234
+ * `ReplicationGroupId` - the name of this replication group - this field follows the same `ElastiCache` naming requirements, 20 alphanumeric characters or `-`;
235
+ * `Description` - the group description;
236
+
237
+ Produced `Fn::GetAtt` values:
238
+
239
+ * `ReplicationGroupId` - the replication group id;
240
+
241
+ ### Custom::ElastiCacheReplicaCluster
242
+
243
+ Creates an `ElastiCache` replica cluster for an already existing replication group.
244
+
245
+ Parameters:
246
+
247
+ * `ReplicationGroupId` - the id of the replication group where this replica cluster will register itself;
248
+ * `ReplicaClusterId` - the id for this replication group;
249
+
250
+ Produced `Fn::GetAtt` values:
251
+
252
+ * `ReplicaClusterId` - the id of this replica cluster;
253
+ * `NodeURLs` - list of `host:port` values (separated by `,`) for the nodes in the cluster if it is a redis cluster or the configuration URL for a memcached cluster;
254
+
255
+ ### Custom::ElastiCacheNodeURLs
256
+
257
+ Produces pairs of `host:port` separated by `,` for all nodes in the cluster if it's a Redis cluster or the configuration `host:port` if it is a Memcached cluster.
258
+
259
+ Parameters:
260
+
261
+ * `ClusterId` - the name of the primary ElastiCache cluster for this replication group;
262
+
263
+ Produced `Fn::GetAtt` values:
264
+
265
+ * `NodeURLs` - list of `host:port` values (separated by `,`) for the nodes in the cluster if it is a Redis cluster or the configuration `host:port` for a Memcached cluster;
266
+
217
267
  ## Contributing
218
268
 
219
269
  1. [Fork it](https://github.com/TheNeatCompany/cfn-bridge/fork)
@@ -1,6 +1,7 @@
1
1
  require 'thor'
2
2
  require 'cloud_formation/bridge/poller'
3
3
  require 'cloud_formation/bridge/util'
4
+ require 'cloud_formation/bridge/version'
4
5
 
5
6
  module CloudFormation
6
7
  module Bridge
@@ -9,6 +10,9 @@ module CloudFormation
9
10
  desc "start QUEUE_NAME", "Starts watching this specific SQS queue"
10
11
  def start(queue_name)
11
12
  STDOUT.sync = true
13
+
14
+ Util::LOGGER.info("Starting cfn-bridge with queue #{queue_name} - version #{CloudFormation::Bridge::VERSION}")
15
+
12
16
  poller = CloudFormation::Bridge::Poller.new(queue_name)
13
17
  poller.start
14
18
  end
@@ -2,6 +2,9 @@ require 'cloud_formation/bridge/exception_notifier'
2
2
  require 'cloud_formation/bridge/names'
3
3
  require 'cloud_formation/bridge/resources/subscribe_queue_to_topic'
4
4
  require 'cloud_formation/bridge/resources/cloud_formation_outputs'
5
+ require 'cloud_formation/bridge/resources/elasti_cache_replication_group'
6
+ require 'cloud_formation/bridge/resources/elasti_cache_replica_cluster'
7
+ require 'cloud_formation/bridge/resources/elasti_cache_node_urls'
5
8
 
6
9
  module CloudFormation
7
10
  module Bridge
@@ -14,6 +17,12 @@ module CloudFormation
14
17
  CloudFormation::Bridge::Resources::SubscribeQueueToTopic.new,
15
18
  "Custom::CloudFormationOutputs" =>
16
19
  CloudFormation::Bridge::Resources::CloudFormationOutputs.new,
20
+ "Custom::ElastiCacheReplicationGroup" =>
21
+ CloudFormation::Bridge::Resources::ElastiCacheReplicationGroup.new,
22
+ "Custom::ElastiCacheReplicaCluster" =>
23
+ CloudFormation::Bridge::Resources::ElastiCacheReplicaCluster.new,
24
+ "Custom::ElastiCacheNodeURLs" =>
25
+ CloudFormation::Bridge::Resources::ElastiCacheNodeUrls.new,
17
26
  }
18
27
 
19
28
  attr_reader :registry
@@ -40,7 +49,7 @@ module CloudFormation
40
49
  end
41
50
  rescue Exception => ex
42
51
  ExceptionNotifier.report_exception(ex, request.request)
43
- request.fail!(ex.message)
52
+ request.fail!("#{ex.class.name} - #{ex.message}")
44
53
  end
45
54
 
46
55
  end
@@ -29,6 +29,16 @@ module CloudFormation
29
29
  DATA = 'Data'
30
30
  end
31
31
 
32
+ module ELASTI_CACHE
33
+ AVAILABLE = 'available'
34
+ CLUSTER_ID = 'ClusterId'
35
+ REPLICA_CLUSTER_ID = 'ReplicaClusterId'
36
+ DESCRIPTION = 'Description'
37
+ NODE_URLS = 'NodeURLs'
38
+ REPLICA = 'replica'
39
+ REPLICATION_GROUP_ID = 'ReplicationGroupId'
40
+ end
41
+
32
42
  end
33
43
  end
34
44
  end
@@ -74,7 +74,13 @@ module CloudFormation
74
74
  end
75
75
 
76
76
  def succeed!(response)
77
- actual_response = build_response(response || {})
77
+ actual_response = case response
78
+ when Hash
79
+ build_response(response)
80
+ else
81
+ build_response
82
+ end
83
+
78
84
  HttpBridge.put(request_url, actual_response)
79
85
  end
80
86
 
@@ -1,4 +1,6 @@
1
+ require 'timeout'
1
2
  require 'cloud_formation/bridge/names'
3
+ require 'cloud_formation/bridge/util'
2
4
 
3
5
  module CloudFormation
4
6
  module Bridge
@@ -7,8 +9,8 @@ module CloudFormation
7
9
  class Base
8
10
  include CloudFormation::Bridge::Names
9
11
 
10
- def require_fields(request, fields)
11
- empty_fields = fields.select do |field|
12
+ def require_fields(request, *fields)
13
+ empty_fields = fields.flatten.select do |field|
12
14
  request.resource_properties[field].nil? ||
13
15
  request.resource_properties[field].strip.empty?
14
16
  end
@@ -21,7 +23,16 @@ module CloudFormation
21
23
 
22
24
  def update(request)
23
25
  raise CloudFormation::Bridge::OperationNotImplementedError.new(
24
- "The resource #{self.class.name} does not implement the update operation - #{request.inspect}")
26
+ "The resource #{self.class.name} does not implement the update operation - #{request.inspect}")
27
+ end
28
+
29
+ def wait_until(description, seconds = 5, max_wait = 600, &block)
30
+ Timeout.timeout(max_wait) do
31
+ while !block.call
32
+ Util.logger.info("Waiting for #{description}")
33
+ sleep(seconds)
34
+ end
35
+ end
25
36
  end
26
37
 
27
38
  end
@@ -0,0 +1,58 @@
1
+ require 'aws/elasticache'
2
+ require 'cloud_formation/bridge/names'
3
+
4
+ module CloudFormation
5
+ module Bridge
6
+ module Resources
7
+ module BaseElastiCacheResource
8
+
9
+ UnknownCacheEngineError = Class.new(StandardError)
10
+
11
+ include CloudFormation::Bridge::Names
12
+
13
+ def replication_group_available?(replication_group_id)
14
+ replication_group = client.describe_replication_groups(replication_group_id: replication_group_id)[:replication_groups][0]
15
+ replication_group[:status] == ELASTI_CACHE::AVAILABLE
16
+ end
17
+
18
+ def find_cluster(cluster_id)
19
+ client.describe_cache_clusters(
20
+ cache_cluster_id: cluster_id,
21
+ show_cache_node_info: true
22
+ )[:cache_clusters][0]
23
+ end
24
+
25
+ def node_urls(cluster_id)
26
+ cluster = client.describe_cache_clusters(
27
+ cache_cluster_id: cluster_id,
28
+ show_cache_node_info: true
29
+ )[:cache_clusters][0]
30
+
31
+ case cluster[:engine]
32
+ when 'redis'
33
+ cluster[:cache_nodes].map do |node|
34
+ "#{node[:endpoint][:address]}:#{node[:endpoint][:port]}"
35
+ end.join(",")
36
+ when 'memcached'
37
+ "#{cluster[:configuration_endpoint][:address]}:#{cluster[:configuration_endpoint][:port]}"
38
+ else
39
+ UnknownCacheEngineError.new("Don't know what to do with cache engine #{cluster[:engine]} - #{cluster.inspect}")
40
+ end
41
+ end
42
+
43
+ def wait_until_cluster_is_available(cluster_id)
44
+ wait_until("replica #{cluster_id} to be available") do
45
+ cluster = find_cluster(cluster_id)
46
+ Util.logger.info("Cluster info is #{cluster.inspect}")
47
+ cluster[:cache_cluster_status] == ELASTI_CACHE::AVAILABLE
48
+ end
49
+ end
50
+
51
+ def client
52
+ @client ||= AWS::ElastiCache.new.client
53
+ end
54
+
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,35 @@
1
+ require 'cloud_formation/bridge/resources/base'
2
+ require 'cloud_formation/bridge/resources/base_elasti_cache_resource'
3
+
4
+ module CloudFormation
5
+ module Bridge
6
+ module Resources
7
+
8
+ class ElastiCacheNodeUrls < Base
9
+
10
+ include BaseElastiCacheResource
11
+
12
+ def create(request)
13
+ require_fields(request, ELASTI_CACHE::CLUSTER_ID)
14
+
15
+ cluster_id = request.resource_properties[ELASTI_CACHE::CLUSTER_ID]
16
+
17
+ wait_until_cluster_is_available(cluster_id)
18
+
19
+ {
20
+ FIELDS::DATA => {
21
+ ELASTI_CACHE::REPLICA_CLUSTER_ID => cluster_id,
22
+ ELASTI_CACHE::NODE_URLS => node_urls(cluster_id),
23
+ },
24
+ FIELDS::PHYSICAL_RESOURCE_ID => cluster_id,
25
+ }
26
+ end
27
+
28
+ def delete(request)
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,66 @@
1
+ require 'aws/elasticache'
2
+ require 'cloud_formation/bridge/util'
3
+ require 'cloud_formation/bridge/resources/base'
4
+ require 'cloud_formation/bridge/resources/base_elasti_cache_resource'
5
+
6
+ module CloudFormation
7
+ module Bridge
8
+ module Resources
9
+
10
+ class ElastiCacheReplicaCluster < Base
11
+ include BaseElastiCacheResource
12
+
13
+ REQUIRED_FIELDS = [
14
+ ELASTI_CACHE::REPLICATION_GROUP_ID,
15
+ ELASTI_CACHE::REPLICA_CLUSTER_ID,
16
+ ]
17
+
18
+ def create(request)
19
+ require_fields(request, REQUIRED_FIELDS)
20
+
21
+ cluster_id = request.resource_properties[ELASTI_CACHE::REPLICA_CLUSTER_ID]
22
+ replication_id = request.resource_properties[ELASTI_CACHE::REPLICATION_GROUP_ID]
23
+
24
+ client.create_cache_cluster(cache_cluster_id: cluster_id, replication_group_id: replication_id)
25
+
26
+ wait_until_cluster_is_available(cluster_id)
27
+
28
+ {
29
+ FIELDS::DATA => {
30
+ ELASTI_CACHE::REPLICA_CLUSTER_ID => cluster_id,
31
+ ELASTI_CACHE::NODE_URLS => node_urls(cluster_id),
32
+ },
33
+ FIELDS::PHYSICAL_RESOURCE_ID => cluster_id,
34
+ }
35
+ end
36
+
37
+ def delete(request)
38
+ require_fields(request, ELASTI_CACHE::REPLICA_CLUSTER_ID)
39
+
40
+ cluster_id = request.resource_properties[ELASTI_CACHE::REPLICA_CLUSTER_ID]
41
+
42
+ begin
43
+ wait_until_cluster_is_available(cluster_id)
44
+
45
+ client.delete_cache_cluster(cache_cluster_id: cluster_id)
46
+
47
+ wait_until("cluster #{cluster_id} to be gone") do
48
+ begin
49
+ find_cluster(cluster_id)
50
+ false
51
+ rescue AWS::ElastiCache::Errors::CacheClusterNotFound
52
+ true
53
+ end
54
+ end
55
+
56
+ rescue AWS::ElastiCache::Errors::CacheClusterNotFound
57
+ # no cache cluster? ignore
58
+ Util.logger.info("Could not find cache cluster for #{cluster_id}, ignoring")
59
+ end
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,67 @@
1
+ require 'aws/elasticache'
2
+ require 'cloud_formation/bridge/util'
3
+ require 'cloud_formation/bridge/resources/base'
4
+ require 'cloud_formation/bridge/resources/base_elasti_cache_resource'
5
+
6
+ module CloudFormation
7
+ module Bridge
8
+ module Resources
9
+
10
+ class ElastiCacheReplicationGroup < Base
11
+
12
+ include BaseElastiCacheResource
13
+
14
+ REQUIRED_FIELDS = [
15
+ ELASTI_CACHE::CLUSTER_ID,
16
+ ELASTI_CACHE::REPLICATION_GROUP_ID,
17
+ ELASTI_CACHE::DESCRIPTION,
18
+ ]
19
+
20
+ def create(request)
21
+ require_fields(request, REQUIRED_FIELDS)
22
+
23
+ replication_id = request.resource_properties[ELASTI_CACHE::REPLICATION_GROUP_ID]
24
+
25
+ client.create_replication_group(
26
+ replication_group_id: replication_id,
27
+ primary_cluster_id: request.resource_properties[ELASTI_CACHE::CLUSTER_ID],
28
+ replication_group_description: request.resource_properties[ELASTI_CACHE::DESCRIPTION],
29
+ )
30
+
31
+ wait_until("replication group #{replication_id} to be available") do
32
+ replication_group_available?(replication_id)
33
+ end
34
+
35
+ {
36
+ FIELDS::DATA => {
37
+ ELASTI_CACHE::REPLICATION_GROUP_ID => replication_id
38
+ },
39
+ FIELDS::PHYSICAL_RESOURCE_ID => replication_id,
40
+ }
41
+ end
42
+
43
+ def delete(request)
44
+ require_fields(request, ELASTI_CACHE::REPLICATION_GROUP_ID)
45
+
46
+ begin
47
+ replication_id = request.resource_properties[ELASTI_CACHE::REPLICATION_GROUP_ID]
48
+
49
+ wait_until("replication group #{replication_id} to be available") do
50
+ replication_group_available?(replication_id)
51
+ end
52
+
53
+ client.delete_replication_group(
54
+ replication_group_id: replication_id,
55
+ retain_primary_cluster: true,
56
+ )
57
+ rescue AWS::ElastiCache::Errors::ReplicationGroupNotFoundFault
58
+ # main cluster does not exist, ignore
59
+ Util.logger.info("Replication group #{replication_id} does not exist, ignoring")
60
+ end
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+ end
67
+ end
@@ -1,9 +1,15 @@
1
1
  require 'logger'
2
+ require 'securerandom'
2
3
 
3
4
  module CloudFormation
4
5
  module Bridge
5
6
  module Util
6
7
  LOGGER = Logger.new(STDOUT)
8
+
9
+ def self.logger
10
+ @logger ||= LOGGER
11
+ end
12
+
7
13
  end
8
14
  end
9
15
  end
@@ -1,5 +1,5 @@
1
1
  module CloudFormation
2
2
  module Bridge
3
- VERSION = "0.0.9"
3
+ VERSION = "0.0.10"
4
4
  end
5
5
  end
@@ -0,0 +1,12 @@
1
+ { "LogicalResourceId": "ReplicaClusterNodeUrls",
2
+ "RequestId": "f9103cf1-5e1a-4597-a233-5838a7528126",
3
+ "RequestType": "Create",
4
+ "ResourceProperties": {
5
+ "ClusterId": "samplecache",
6
+ "ServiceToken": "arn:aws:sns:us-east-1:123456:sns-topic"
7
+ },
8
+ "ResourceType": "Custom::ElastiCacheNodeURLs",
9
+ "ResponseURL": "https://cloudformation-custom-resource-response-useast1.s3.amazonaws.com/arn%3Aaws%3Acloudformation%3Aus-east-1%3A123456%3Astack/sample-replica-cluster/ce7bd340-393c-11e4-962c-50fa1dbb2c64%7CRedisReplicaCluster%7Cf9103cf1-5e1a-4597-a233-5838a7528126",
10
+ "StackId": "arn:aws:cloudformation:us-east-1:123456:stack/sample-replica-cluster/ce7bd340-393c-11e4-962c-50fa1dbb2c64",
11
+ "TopicArn": "arn:aws:sns:us-east-1:123456:sns-topic"
12
+ }
@@ -0,0 +1,12 @@
1
+ { "LogicalResourceId": "ReplicaClusterNodeUrls",
2
+ "RequestId": "f9103cf1-5e1a-4597-a233-5838a7528126",
3
+ "RequestType": "Create",
4
+ "ResourceProperties": {
5
+ "ClusterId": "dev-redis-replica",
6
+ "ServiceToken": "arn:aws:sns:us-east-1:123456:sns-topic"
7
+ },
8
+ "ResourceType": "Custom::ElastiCacheNodeURLs",
9
+ "ResponseURL": "https://cloudformation-custom-resource-response-useast1.s3.amazonaws.com/arn%3Aaws%3Acloudformation%3Aus-east-1%3A123456%3Astack/sample-replica-cluster/ce7bd340-393c-11e4-962c-50fa1dbb2c64%7CRedisReplicaCluster%7Cf9103cf1-5e1a-4597-a233-5838a7528126",
10
+ "StackId": "arn:aws:cloudformation:us-east-1:123456:stack/sample-replica-cluster/ce7bd340-393c-11e4-962c-50fa1dbb2c64",
11
+ "TopicArn": "arn:aws:sns:us-east-1:123456:sns-topic"
12
+ }
@@ -0,0 +1,13 @@
1
+ { "LogicalResourceId": "RedisReplicaCluster",
2
+ "RequestId": "f9103cf1-5e1a-4597-a233-5838a7528126",
3
+ "RequestType": "Create",
4
+ "ResourceProperties": {
5
+ "ReplicaClusterId": "dev-redis-replica",
6
+ "ReplicationGroupId": "dev-redis-rep-group",
7
+ "ServiceToken": "arn:aws:sns:us-east-1:123456:sns-topic"
8
+ },
9
+ "ResourceType": "Custom::ElastiCacheReplicaCluster",
10
+ "ResponseURL": "https://cloudformation-custom-resource-response-useast1.s3.amazonaws.com/arn%3Aaws%3Acloudformation%3Aus-east-1%3A123456%3Astack/sample-replica-cluster/ce7bd340-393c-11e4-962c-50fa1dbb2c64%7CRedisReplicaCluster%7Cf9103cf1-5e1a-4597-a233-5838a7528126",
11
+ "StackId": "arn:aws:cloudformation:us-east-1:123456:stack/sample-replica-cluster/ce7bd340-393c-11e4-962c-50fa1dbb2c64",
12
+ "TopicArn": "arn:aws:sns:us-east-1:123456:sns-topic"
13
+ }
@@ -0,0 +1,14 @@
1
+ { "LogicalResourceId": "RedisClusterReplicationGroup",
2
+ "RequestId": "b2fcc8a6-3f25-4e91-8aad-8d0b2f62e352",
3
+ "RequestType": "Create",
4
+ "ResourceProperties": {
5
+ "ClusterId": "cluster-id-here",
6
+ "Description": "Sample replication group for the redis instances",
7
+ "ReplicationGroupId": "dev-redis-rep",
8
+ "ServiceToken": "arn:aws:sns:us-east-1:12345567:test-stack-sns"
9
+ },
10
+ "ResourceType": "Custom::ElastiCacheReplicationGroup",
11
+ "ResponseURL": "https://cloudformation-custom-resource-response-useast1.s3.amazonaws.com/arn%3Aaws%3Acloudformation%3Aus-east-1%3A04%3Astack/replica-cluster/1a98e2b0-390e-11e4-a5f9-50e2416294e0%7CRedisClusterReplicationGroup%7Cb2fcc8a6-3f25-4e91-8aad-8d0b2f62e352",
12
+ "StackId": "arn:aws:cloudformation:us-east-1:12345567:stack/mauricio-replica-cluster/1a98e2b0-390e-11e4-a5f9-50e2416294e0",
13
+ "TopicArn": "arn:aws:sns:us-east-1:12345567:test-stack-sns"
14
+ }
@@ -0,0 +1,14 @@
1
+ { "LogicalResourceId": "RedisReplicaCluster",
2
+ "PhysicalResourceId": "dev-redis-rep-group",
3
+ "RequestId": "ebaf8396-72f3-43b1-b58f-a808d952e688",
4
+ "RequestType": "Delete",
5
+ "ResourceProperties": {
6
+ "ReplicaClusterId": "dev-redis-replica",
7
+ "ReplicationGroupId": "dev-redis-rep-group",
8
+ "ServiceToken": "arn:aws:sns:us-east-1:123456:sns-topic"
9
+ },
10
+ "ResourceType": "Custom::ElastiCacheReplicaCluster",
11
+ "ResponseURL": "https://cloudformation-custom-resource-response-useast1.s3.amazonaws.com/arn%3Aaws%3Acloudformation%3Aus-east-1%3A047170177871%3Astack/sample-replica-cluster/ce7bd340-393c-11e4-962c-50fa1dbb2c64%7CRedisReplicaCluster%7Cebaf8396-72f3-43b1-b58f-a808d952e688",
12
+ "StackId": "arn:aws:cloudformation:us-east-1:123456:stack/sample-replica-cluster/ce7bd340-393c-11e4-962c-50fa1dbb2c64",
13
+ "TopicArn": "arn:aws:sns:us-east-1:123456:sns-topic"
14
+ }
@@ -0,0 +1,15 @@
1
+ { "LogicalResourceId": "RedisClusterReplicationGroup",
2
+ "PhysicalResourceId": "sample-replica-cluster",
3
+ "RequestId": "11214fd4-f6d6-4be0-87de-a1fa3b215990",
4
+ "RequestType": "Delete",
5
+ "ResourceProperties": {
6
+ "ClusterId": "cluster-id-here",
7
+ "Description": "Sample replication group for the redis instances",
8
+ "ReplicationGroupId": "dev-redis-rep",
9
+ "ServiceToken": "arn:aws:sns:us-east-1:12345567:test-stack-sns"
10
+ },
11
+ "ResourceType": "Custom::ElastiCacheReplicationGroup",
12
+ "ResponseURL": "https://cloudformation-custom-resource-response-useast1.s3.amazonaws.com/arn%3Aaws%3Acloudformation%3Aus-east-1%3A01234578%3Astack/sample-replica-cluster/1a98e2b0-390e-11e4-a5f9-50e2416294e0%7CRedisClusterReplicationGroup%7C11214fd4-f6d6-4be0-87de-a1fa3b215990",
13
+ "StackId": "arn:aws:cloudformation:us-east-1:1234578:stack/mauricio-replica-cluster/1a98e2b0-390e-11e4-a5f9-50e2416294e0",
14
+ "TopicArn": "arn:aws:sns:us-east-1:1234578:test-stack-sns"
15
+ }
@@ -0,0 +1,57 @@
1
+ {
2
+ "cache_clusters": [
3
+ {
4
+ "cache_security_groups": [
5
+ {
6
+ "cache_security_group_name": "redis-redis-srkbp24vmwfr",
7
+ "status": "active"
8
+ }
9
+ ],
10
+ "cache_nodes": [
11
+ {
12
+ "parameter_group_status": "in-sync",
13
+ "cache_node_status": "available",
14
+ "cache_node_create_time": "2014-08-04 12:56:26 UTC",
15
+ "cache_node_id": "0001",
16
+ "customer_availability_zone": "us-east-1c",
17
+ "endpoint": {
18
+ "port": 6379,
19
+ "address": "red-re-1uptio9f2ytdi.mzufvw.0001.use1.cache.amazonaws.com"
20
+ }
21
+ }
22
+ ],
23
+ "security_groups": [
24
+
25
+ ],
26
+ "cache_cluster_id": "red-re-1uptio9f2ytdi",
27
+ "cache_parameter_group": {
28
+ "cache_node_ids_to_reboot": [
29
+
30
+ ],
31
+ "parameter_apply_status": "in-sync",
32
+ "cache_parameter_group_name": "default.redis2.8"
33
+ },
34
+ "cache_cluster_status": "available",
35
+ "replication_group_id": "rg-redisclu-2ffb4776",
36
+ "cache_node_type": "cache.m1.small",
37
+ "engine": "redis",
38
+ "pending_modified_values": {
39
+ "cache_node_ids_to_remove": [
40
+
41
+ ]
42
+ },
43
+ "preferred_availability_zone": "us-east-1c",
44
+ "cache_cluster_create_time": "2014-08-04 12:56:26 UTC",
45
+ "engine_version": "2.8.6",
46
+ "auto_minor_version_upgrade": true,
47
+ "preferred_maintenance_window": "thu:10:00-thu:11:00",
48
+ "client_download_landing_page": "https://console.aws.amazon.com/elasticache/home#client-download:",
49
+ "snapshot_retention_limit": 0,
50
+ "num_cache_nodes": 1,
51
+ "snapshot_window": "09:00-10:00"
52
+ }
53
+ ],
54
+ "response_metadata": {
55
+ "request_id": "1ab3c5f5-3891-11e4-9cb8-252ab83f5b14"
56
+ }
57
+ }