cumuliform 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: abff1bdeb60a617a79f889e191789cc21dee2335
4
- data.tar.gz: 8320b16dd011e3155aaeef164a4dce008634e46d
3
+ metadata.gz: 2078446f4607d50e1623eab3072e964341ab06b2
4
+ data.tar.gz: 96c0569cfaeb8f991b878364b27defdf210753ef
5
5
  SHA512:
6
- metadata.gz: 49b1742f90f29be0daacbd3c452cdda449100bc452ec831466661639c8fc28402e2cbdf833a04c5d4cecf1eeee97e4ccd03bf4717943032a4eb2813c407df54c
7
- data.tar.gz: 20147ec9d4c9790d2ac5fc5fb78c5f3126582e2ed7c86c698be3bf25b6b7cf49bc625759e7a2e07d93309510fc1dba25f714ca1a2dc573e53eb514cce13f26f4
6
+ metadata.gz: a0bed284a17feef9d26083b3967579d9d78714229b9992905210bcc626142f864358dd6aeab3b49676f72558c977c4712b13ef56c1e6d5662426201245b8936c
7
+ data.tar.gz: e67caccb78bc3d10dad37d96e50db5e4d1e88713e863468af79f3c1151e727e1d6c35e159356c50fdf7eea455d271b218f6b7c616bd5cd2b3d130dc94ccc256c
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.2.2
1
+ 2.3
data/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
  All notable changes to this project will be documented in this file.
3
3
  This project adheres to [Semantic Versioning](http://semver.org/).
4
4
 
5
+ ## Unreleased
6
+ ### Changed
7
+ - Internal refactor of code / file structure to more clearly separate DSL and
8
+ domain objects
9
+
5
10
  ## [0.5.1] - 2015-12-03
6
11
  ### Changed
7
12
  - Make some fragment-related functions private
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Cumuliform
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/cumuliform.svg)](http://badge.fury.io/rb/cumuliform) [![Build Status](https://travis-ci.org/tape-tv/cumuliform.svg?branch=master)](https://travis-ci.org/tape-tv/cumuliform) [![Code Climate](https://codeclimate.com/github/tape-tv/cumuliform/badges/gpa.svg)](https://codeclimate.com/github/tape-tv/cumuliform) [![Test Coverage](https://codeclimate.com/github/tape-tv/cumuliform/badges/coverage.svg)](https://codeclimate.com/github/tape-tv/cumuliform/coverage)
3
+ [![Gem Version](https://badge.fury.io/rb/cumuliform.svg)](http://badge.fury.io/rb/cumuliform) [![Build Status](https://travis-ci.org/tape-tv/cumuliform.svg?branch=master)](https://travis-ci.org/tape-tv/cumuliform) [![Code Climate](https://codeclimate.com/github/tape-tv/cumuliform/badges/gpa.svg)](https://codeclimate.com/github/tape-tv/cumuliform) [![Test Coverage](https://codeclimate.com/github/tape-tv/cumuliform/badges/coverage.svg)](https://codeclimate.com/github/tape-tv/cumuliform/coverage) [![Documentation](https://inch-ci.org/github/tape-tv/cumuliform.svg?branch=master)]
4
4
 
5
5
  Amazon’s [CloudFormation AWS service][cf] provides a way to describe
6
6
  infrastructure stacks using a JSON template. We love CloudFormation, and use it
@@ -772,11 +772,376 @@ And the output:
772
772
  ```
773
773
 
774
774
  ## Importing other templates
775
- _TODO_
775
+ If you want to share complete resources, or fragments, between different
776
+ templates then you can import one template into another. All the imported
777
+ template's resources will be available to you, and you can override template
778
+ parts (i.e. a parameter) or even fragments simply be defining them again in the
779
+ importing template.
780
+
781
+ To import a template, you simply `require` it as you would any other ruby file,
782
+ and then use Cumuliform's `import` method to import it.
783
+
784
+ This is easier to explain with an example. Take this very simple example,
785
+ defining a single `AWS::EC2::Instance` in a `resource`, and a `parameter` that
786
+ controls the AMI it uses:
787
+
788
+ ```ruby
789
+ BaseTemplate = Cumuliform.template do
790
+ parameter 'AMI' do
791
+ {
792
+ Description: 'The AMI id for our template (defaults to the stock Ubuntu 14.04 image in eu-central-1)',
793
+ Type: 'String',
794
+ Default: 'ami-accff2b1'
795
+ }
796
+ end
797
+
798
+ resource 'MyInstance' do
799
+ {
800
+ Type: 'AWS::EC2::Instance',
801
+ Properties: {
802
+ ImageId: ref('AMI'),
803
+ InstanceType: 'm3.medium'
804
+ }
805
+ }
806
+ end
807
+ end
808
+ ```
809
+
810
+ It generates the following JSON (as expected):
811
+
812
+ ```
813
+ {
814
+ "Parameters": {
815
+ "AMI": {
816
+ "Description": "The AMI id for our template (defaults to the stock Ubuntu 14.04 image in eu-central-1)",
817
+ "Type": "String",
818
+ "Default": "ami-accff2b1"
819
+ }
820
+ },
821
+ "Resources": {
822
+ "MyInstance": {
823
+ "Type": "AWS::EC2::Instance",
824
+ "Properties": {
825
+ "ImageId": {
826
+ "Ref": "AMI"
827
+ },
828
+ "InstanceType": "m3.medium"
829
+ }
830
+ }
831
+ }
832
+ }
833
+ ```
834
+
835
+ Say we to change the default AMI parameter but reuse everything else. We can
836
+ import that template and redefine the `parameter`:
837
+
838
+ ```ruby
839
+ require_relative './import-base.rb'
840
+
841
+ Cumuliform.template do
842
+ import BaseTemplate
843
+
844
+ parameter 'AMI' do
845
+ {
846
+ Description: 'A different AMI',
847
+ Type: 'String',
848
+ Default: 'ami-DIFFERENT'
849
+ }
850
+ end
851
+ end
852
+ ```
853
+
854
+ That produces the following JSON:
855
+
856
+ ```
857
+ {
858
+ "Parameters": {
859
+ "AMI": {
860
+ "Description": "A different AMI",
861
+ "Type": "String",
862
+ "Default": "ami-DIFFERENT"
863
+ }
864
+ },
865
+ "Resources": {
866
+ "MyInstance": {
867
+ "Type": "AWS::EC2::Instance",
868
+ "Properties": {
869
+ "ImageId": {
870
+ "Ref": "AMI"
871
+ },
872
+ "InstanceType": "m3.medium"
873
+ }
874
+ }
875
+ }
876
+ }
877
+ ```
878
+
879
+ There are a couple of very important points to note: First, we have to
880
+ `require` the base template (exactly as you require any ruby file). Second, we
881
+ have to assign the result of calling `Cumuliform.template` to a constant so
882
+ that it is available once we've required the file.
883
+
884
+ In the importing template, once we have `require`d the base template, we pass
885
+ the constant containing the base template to the `import` DSL method.
886
+
887
+ ## Importing fragments
888
+ Fragments defined in a template are also available when imported. You can
889
+ override fragments in the importing template as you would override a resource.
890
+
891
+ Here's a base template that defines several fragments (shown with the JSON it
892
+ generates).
893
+
894
+ ```ruby
895
+ FragmentBaseTemplate = Cumuliform.template do
896
+ def_fragment(:ami_param) do |opts|
897
+ parameter 'AMI' do
898
+ {
899
+ Description: 'AMI id',
900
+ Type: 'String',
901
+ Default: opts[:ami_id]
902
+ }
903
+ end
904
+ end
905
+
906
+ def_fragment(:instance_type) do |opts|
907
+ parameter 'InstanceType' do
908
+ {
909
+ Description: 'InstanceType',
910
+ Type: 'String',
911
+ Default: opts[:type],
912
+ AllowedValues: ['t2.small', 't2.medium', 't2.large']
913
+ }
914
+ end
915
+ end
916
+
917
+ def_fragment(:instance) do |opts|
918
+ resource 'MyInstance' do
919
+ {
920
+ Type: 'AWS::EC2::Instance',
921
+ Properties: {
922
+ ImageId: ref('AMI'),
923
+ InstanceType: ref('InstanceType')
924
+ }
925
+ }
926
+ end
927
+ end
928
+
929
+ fragment(:ami_param, ami_id: 'ami-accff2b1')
930
+ fragment(:instance_type, type: 't2.medium')
931
+ fragment(:instance)
932
+ end
933
+ ```
934
+
935
+ ```
936
+ {
937
+ "Parameters": {
938
+ "AMI": {
939
+ "Description": "AMI id",
940
+ "Type": "String",
941
+ "Default": "ami-accff2b1"
942
+ },
943
+ "InstanceType": {
944
+ "Description": "InstanceType",
945
+ "Type": "String",
946
+ "Default": "m4.medium",
947
+ "AllowedValues": [
948
+ "t2.small",
949
+ "t2.medium",
950
+ "t2.large"
951
+ ]
952
+ }
953
+ },
954
+ "Resources": {
955
+ "MyInstance": {
956
+ "Type": "AWS::EC2::Instance",
957
+ "Properties": {
958
+ "ImageId": {
959
+ "Ref": "AMI"
960
+ },
961
+ "InstanceType": {
962
+ "Ref": "InstanceType"
963
+ }
964
+ }
965
+ }
966
+ }
967
+ }
968
+ ```
969
+
970
+ An importing template can use, or override, the fragments exactly as with any
971
+ other resource:
972
+
973
+ ```ruby
974
+ require_relative './import-fragments-base.rb'
975
+
976
+ Cumuliform.template do
977
+ import FragmentBaseTemplate
978
+
979
+ def_fragment(:instance_type) do |opts|
980
+ parameter 'InstanceType' do
981
+ {
982
+ Description: 'InstanceType',
983
+ Type: 'String',
984
+ Default: opts[:type],
985
+ AllowedValues: ['m3.medium', 'm4.large', 'm4.xlarge']
986
+ }
987
+ end
988
+ end
989
+
990
+ fragment(:instance_type, type: 'm3.medium')
991
+ end
992
+ ```
993
+
994
+ ```
995
+ {
996
+ "Parameters": {
997
+ "AMI": {
998
+ "Description": "AMI id",
999
+ "Type": "String",
1000
+ "Default": "ami-accff2b1"
1001
+ },
1002
+ "InstanceType": {
1003
+ "Description": "InstanceType",
1004
+ "Type": "String",
1005
+ "Default": "m3.medium",
1006
+ "AllowedValues": [
1007
+ "m3.medium",
1008
+ "m4.large",
1009
+ "m4.xlarge"
1010
+ ]
1011
+ }
1012
+ },
1013
+ "Resources": {
1014
+ "MyInstance": {
1015
+ "Type": "AWS::EC2::Instance",
1016
+ "Properties": {
1017
+ "ImageId": {
1018
+ "Ref": "AMI"
1019
+ },
1020
+ "InstanceType": {
1021
+ "Ref": "InstanceType"
1022
+ }
1023
+ }
1024
+ }
1025
+ }
1026
+ }
1027
+ ```
1028
+
1029
+ We redefined the fragment so the allowed values for instance type allowed the
1030
+ instance type we wanted to use. (Using the fragments in the base template is a
1031
+ bit weird, and not really recommended - it's included here to warn you...)
1032
+
1033
+
1034
+ ## Assigning templates to constants, namespaces, and processing
1035
+ The `Cumuliform.template` method returns a template object directly. So, to
1036
+ make a template that can be imported into another template you need to assign
1037
+ it to a variable or constant.
1038
+
1039
+ If you want to be able to directly process your base templates (instead of only
1040
+ using them by importing them into another template), then you also need to make
1041
+ sure your file returns the template when it's run. The runner works by
1042
+ `class_eval`ing your template file as a string and expecting that the result of
1043
+ that call will be an instance of `Cumuliform::Template`. If you use namespaces
1044
+ for your template objects (as you might if you have several base templates)
1045
+ then you need to be careful of that: the last line in your template must be
1046
+ something that returns the template. If you're nesting within modules, then the
1047
+ call to `module` will return `nil`, not the template. Instead, return the
1048
+ template as the last line:
1049
+
1050
+ ```
1051
+ module Stacks
1052
+ Base = Cumuliform.template do
1053
+ ...
1054
+ end
1055
+ end
1056
+
1057
+ Stacks::Base
1058
+ ```
776
1059
 
777
1060
  ## Helpers
778
- _TODO_
1061
+ Because templates are actually instances, not classes or modules, you can't
1062
+ simply `include` a mixin module. Cumuliform provides a `helpers` DSL method
1063
+ that allows you pass in modules, or block containing helper methods, that will
1064
+ be made available to the template:
1065
+
1066
+ ```ruby
1067
+ Cumuliform.template do
1068
+ helpers do
1069
+ def ami
1070
+ 'ami-accff2b1'
1071
+ end
1072
+ end
1073
+
1074
+ resource 'MyInstance' do
1075
+ {
1076
+ Type: 'AWS::EC2::Instance',
1077
+ Properties: {
1078
+ ImageId: ami,
1079
+ InstanceType: 'm3.medium'
1080
+ }
1081
+ }
1082
+ end
1083
+ end
1084
+ ```
1085
+
1086
+ Which evaluates to:
1087
+
1088
+ ```
1089
+ {
1090
+ "Resources": {
1091
+ "MyInstance": {
1092
+ "Type": "AWS::EC2::Instance",
1093
+ "Properties": {
1094
+ "ImageId": "ami-accff2b1",
1095
+ "InstanceType": "m3.medium"
1096
+ }
1097
+ }
1098
+ }
1099
+ }
1100
+ ```
1101
+
1102
+ Or using a module:
1103
+
1104
+ ```ruby
1105
+ module AmiHelper
1106
+ def ami
1107
+ 'ami-accff2b1'
1108
+ end
1109
+ end
1110
+
1111
+ Cumuliform.template do
1112
+ helpers AmiHelper
1113
+
1114
+ resource 'MyInstance' do
1115
+ {
1116
+ Type: 'AWS::EC2::Instance',
1117
+ Properties: {
1118
+ ImageId: ami,
1119
+ InstanceType: 'm3.medium'
1120
+ }
1121
+ }
1122
+ end
1123
+ end
1124
+ ```
1125
+
1126
+ Which also evaluates to:
1127
+
1128
+ ```
1129
+ {
1130
+ "Resources": {
1131
+ "MyInstance": {
1132
+ "Type": "AWS::EC2::Instance",
1133
+ "Properties": {
1134
+ "ImageId": "ami-accff2b1",
1135
+ "InstanceType": "m3.medium"
1136
+ }
1137
+ }
1138
+ }
1139
+ }
1140
+ ```
779
1141
 
1142
+ Helper methods are not able to access any methods on the template (like
1143
+ `resource`), they're really for wrapping complex external calls (for example, a
1144
+ class that fetches an API token you need to use in your template).
780
1145
 
781
1146
  # Development
782
1147
 
@@ -0,0 +1,17 @@
1
+ Cumuliform.template do
2
+ helpers do
3
+ def ami
4
+ 'ami-accff2b1'
5
+ end
6
+ end
7
+
8
+ resource 'MyInstance' do
9
+ {
10
+ Type: 'AWS::EC2::Instance',
11
+ Properties: {
12
+ ImageId: ami,
13
+ InstanceType: 'm3.medium'
14
+ }
15
+ }
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ BaseTemplate = Cumuliform.template do
2
+ parameter 'AMI' do
3
+ {
4
+ Description: 'The AMI id for our template (defaults to the stock Ubuntu 14.04 image in eu-central-1)',
5
+ Type: 'String',
6
+ Default: 'ami-accff2b1'
7
+ }
8
+ end
9
+
10
+ resource 'MyInstance' do
11
+ {
12
+ Type: 'AWS::EC2::Instance',
13
+ Properties: {
14
+ ImageId: ref('AMI'),
15
+ InstanceType: 'm3.medium'
16
+ }
17
+ }
18
+ end
19
+ end