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 +4 -4
- data/.ruby-version +1 -1
- data/CHANGELOG.md +5 -0
- data/README.md +368 -3
- data/examples/block-helper.rb +17 -0
- data/examples/import-base.rb +19 -0
- data/examples/import-fragments-base.rb +38 -0
- data/examples/import-fragments-importer.rb +18 -0
- data/examples/import-importer.rb +13 -0
- data/examples/module-helper.rb +19 -0
- data/lib/cumuliform.rb +1 -93
- data/lib/cumuliform/dsl.rb +32 -0
- data/lib/cumuliform/dsl/fragments.rb +67 -0
- data/lib/cumuliform/dsl/functions.rb +256 -0
- data/lib/cumuliform/dsl/helpers.rb +70 -0
- data/lib/cumuliform/dsl/import.rb +34 -0
- data/lib/cumuliform/error.rb +19 -0
- data/lib/cumuliform/output.rb +10 -1
- data/lib/cumuliform/rake_task.rb +6 -0
- data/lib/cumuliform/runner.rb +12 -0
- data/lib/cumuliform/{import.rb → section.rb} +1 -25
- data/lib/cumuliform/sections.rb +52 -0
- data/lib/cumuliform/template.rb +36 -0
- data/lib/cumuliform/version.rb +1 -1
- metadata +17 -6
- data/lib/cumuliform/fragments.rb +0 -63
- data/lib/cumuliform/functions.rb +0 -220
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2078446f4607d50e1623eab3072e964341ab06b2
|
4
|
+
data.tar.gz: 96c0569cfaeb8f991b878364b27defdf210753ef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a0bed284a17feef9d26083b3967579d9d78714229b9992905210bcc626142f864358dd6aeab3b49676f72558c977c4712b13ef56c1e6d5662426201245b8936c
|
7
|
+
data.tar.gz: e67caccb78bc3d10dad37d96e50db5e4d1e88713e863468af79f3c1151e727e1d6c35e159356c50fdf7eea455d271b218f6b7c616bd5cd2b3d130dc94ccc256c
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
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
|
-
[](http://badge.fury.io/rb/cumuliform) [](https://travis-ci.org/tape-tv/cumuliform) [](https://codeclimate.com/github/tape-tv/cumuliform) [](https://codeclimate.com/github/tape-tv/cumuliform/coverage)
|
3
|
+
[](http://badge.fury.io/rb/cumuliform) [](https://travis-ci.org/tape-tv/cumuliform) [](https://codeclimate.com/github/tape-tv/cumuliform) [](https://codeclimate.com/github/tape-tv/cumuliform/coverage) []
|
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
|
-
|
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
|
-
|
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,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
|