seiso-import_master 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ OGVlNGIxZWVlYWI5ZGI3MWIyMjA5YTE0YjhhNTU4YzFiYmM4ZmMwNw==
5
+ data.tar.gz: !binary |-
6
+ MDI5MTBlYzI0YmVlNmY2N2ZhNDBhNmNjYTMxYmRjNTdmYjJjZDhiMA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ YWRmMDdlMTJhZjgxMWNlNTg2Y2ZiMjQ0ZjhjZWUzYzhmN2E2MDA2NzA2Y2Jh
10
+ OGQwNDU1YWU1N2YzZjI3MGNjNzJkMTBlNWIwYjNmMWM1NzRlMTY3NTFkYzAy
11
+ YTVmYjNhMmFjZGRmMzU5NWE0NzU4OTk0NDYyOTQ3Njk5YTQ3N2I=
12
+ data.tar.gz: !binary |-
13
+ ZjYwODk1M2VmM2RkOWRiZmJkYjVmZGI2OWFiNTFlZmJhMjBkZWIzZmJkOThj
14
+ OTYwMTU0ZTRhNmU0YjRmOGQzOTllNjc0NzI0ODJjNTlhN2U0NzdjMGFkYWYz
15
+ ODAyYTk3NjcwZmE4Zjc4ZWY5MzZhNjllZDgxNjk0YWU5MTE2NjE=
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in seiso-import_master.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,202 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "{}"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright {yyyy} {name of copyright owner}
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
202
+
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # Seiso::ImportMaster
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/seiso-import_master.svg)](http://badge.fury.io/rb/seiso-import_master)
4
+ [![Build Status](https://travis-ci.org/ExpediaDotCom/seiso-import_master.svg)](https://travis-ci.org/ExpediaDotCom/seiso-import_master)
5
+ [![Inline docs](http://inch-ci.org/github/ExpediaDotCom/seiso-import_master.svg?branch=master)](http://inch-ci.org/github/ExpediaDotCom/seiso-import_master)
6
+
7
+ Imports Seiso data master files into Seiso.
8
+
9
+ See [Manage Your Service Data with GitHub, Jenkins & Seiso](http://seiso.io/guides/manage-your-service-data-with-github-jenkins-seiso/) for more details on the overall approach.
10
+
11
+ See [Seiso Data Master Schemas](http://seiso.io/docs/current/user-manual/sdm-schemas/) for the data master schemas.
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ ```ruby
18
+ gem 'seiso-import_master'
19
+ ```
20
+
21
+ And then execute:
22
+
23
+ $ bundle
24
+
25
+ Or install it yourself as:
26
+
27
+ $ gem install seiso-import_master
28
+
29
+ ## Usage
30
+
31
+ 1. Create a directory `~/.seiso-importers`
32
+ 2. Place appropriately modified copy of `seiso.yml.sample` in there.
33
+ 3. Run `seiso-import-master file [, file2, ...]` to perform the import. Note that you can use `-f yaml` for YAML files (the default is `-f json`).
34
+
35
+ ## Contributing
36
+
37
+ 1. Fork it ( https://github.com/ExpediaDotCom/seiso-import_master/fork )
38
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
39
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
40
+ 4. Push to the branch (`git push origin my-new-feature`)
41
+ 5. Create a new pull request.
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ task :default => [ :test ]
5
+
6
+ task :tasks do
7
+ puts "Tasks:"
8
+ puts "test : Run unit tests"
9
+ end
10
+
11
+ Rake::TestTask.new do |t|
12
+ t.libs << 'test'
13
+ t.test_files = FileList[ 'test/test_*.rb' ]
14
+ t.verbose = true
15
+ end
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- mode: ruby -*-
3
+ # vi: set ft=ruby :
4
+
5
+ require 'optparse'
6
+ require 'seiso/import_master'
7
+ require 'yaml'
8
+
9
+ # Imports Seiso data master files into Seiso.
10
+ #
11
+ # Author:: Willie Wheeler (mailto:wwheeler@expedia.com)
12
+ # Copyright:: Copyright (c) 2014-2015 Expedia, Inc.
13
+ # License:: Apache 2.0
14
+
15
+ options = {}
16
+ option_parser = OptionParser.new do |opts|
17
+ executable_name = File.basename $PROGRAM_NAME
18
+ opts.banner = "Import one or more Seiso data master files into Seiso.
19
+
20
+ Usage: #{executable_name} [options] master_file ..."
21
+ opts.on("-f FORMAT", "--format FORMAT", "Document format, either 'json' (default) or 'yaml'") do |format|
22
+ options[:format] = format
23
+ end
24
+ end
25
+
26
+ option_parser.parse!
27
+ files = ARGV
28
+
29
+ abort "Error: You must supply a master file.\n\n#{option_parser.help}" if files.empty?
30
+
31
+ settings = YAML.load_file "#{Dir.home}/.seiso-importers/seiso.yml"
32
+ importer = Seiso::ImportMaster.new settings
33
+ importer.import_files files
@@ -0,0 +1,102 @@
1
+ require "json"
2
+ require "seiso/connector"
3
+ require "yaml"
4
+ require_relative "import_master/link_factory"
5
+ require_relative "import_master/master_item_mapper"
6
+ require_relative "import_master/uri_factory"
7
+
8
+ # Seiso namespace module
9
+ module Seiso
10
+
11
+ # Imports Seiso data master files into Seiso.
12
+ #
13
+ # Author:: Willie Wheeler (mailto:wwheeler@expedia.com)
14
+ # Copyright:: Copyright (c) 2014-2015 Expedia, Inc.
15
+ # License:: Apache 2.0
16
+
17
+ class ImportMaster
18
+
19
+ # Initializes the importer with a Seiso connector.
20
+ def initialize(seiso_settings)
21
+ @seiso = Seiso::Connector.new seiso_settings
22
+
23
+ # TODO Inject dependencies?
24
+ @uri_factory = Seiso::UriFactory.new @seiso.base_uri
25
+ @link_factory = Seiso::LinkFactory.new @uri_factory
26
+ @mapper = Seiso::MasterItemMapper.new @link_factory
27
+
28
+ @loaders = {
29
+ 'json' => ->(file) { JSON.parse(IO.read(file)) },
30
+ 'yaml' => ->(file) { YAML.load_file file }
31
+ }
32
+ end
33
+
34
+ # Imports a list of master files in order. Legal formats are 'json' (default) and 'yaml'.
35
+ def import_files(files, format = 'json')
36
+ loop do
37
+ file = files.pop
38
+ puts "Processing #{file}"
39
+ import_file file
40
+ break if files.empty?
41
+ end
42
+ end
43
+
44
+ # Imports a data master file. Legal formats are 'json' (default) and 'yaml'.
45
+ def import_file(file, format = 'json')
46
+ loader = @loaders[format]
47
+ raise ArgumentError, "Illegal format: #{format}" if loader.nil?
48
+ doc = loader.call(file)
49
+ import_doc doc
50
+ end
51
+
52
+ # Imports a data master document.
53
+ def import_doc(doc)
54
+ type = doc['type']
55
+ master_items = doc['items']
56
+
57
+ # There are some special cases
58
+ if type == 'nodes'
59
+ do_import_nodes master_items
60
+ elsif type == 'service-instances'
61
+ do_import_service_instances master_items
62
+ else
63
+ do_import_items(type, master_items)
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def do_import_items(type, items)
70
+ seiso_items = @mapper.map_all(type, items)
71
+ @seiso.put_items(type, seiso_items)
72
+ end
73
+
74
+ # Imports the nodes, along with their associated IP addresses.
75
+ def do_import_nodes(nodes)
76
+ nips = detach_children(nodes, 'node', 'name', 'ipAddresses')
77
+ do_import_items('nodes', nodes)
78
+ do_import_items('node-ip-addresses', nips)
79
+ end
80
+
81
+ # Imports the service instances, along with their associated ports and IP address roles.
82
+ def do_import_service_instances(service_instances)
83
+ ports = detach_children(service_instances, 'serviceInstance', 'key', 'ports')
84
+ roles = detach_children(service_instances, 'serviceInstance', 'key', 'ipAddressRoles')
85
+ do_import_items('service-instances', service_instances)
86
+ do_import_items('service-instance-ports', ports)
87
+ do_import_items('ip-address-roles', roles)
88
+ end
89
+
90
+ # Enriches children with a link to the parent, detaches them from the parent, and returns all detached children.
91
+ def detach_children(parents, parent_prop, parent_key, child_prop)
92
+ all_children = []
93
+ parents.each do |p|
94
+ children = p[child_prop]
95
+ children.each { |c| c[parent_prop] = p[parent_key] }
96
+ all_children.push(*children)
97
+ p.delete child_prop
98
+ end
99
+ all_children
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,48 @@
1
+ # Seiso namespace module
2
+ module Seiso
3
+
4
+ # Link factory, supporting HATEOAS principle.
5
+ #
6
+ # Author:: Willie Wheeler (mailto:wwheeler@expedia.com)
7
+ # Copyright:: Copyright (c) 2014-2015 Expedia, Inc.
8
+ # License:: Apache 2.0
9
+
10
+ class LinkFactory
11
+
12
+ # Creates a new link factory, injecting the URI factory.
13
+ # - uri_factory: URI factory
14
+ def initialize(uri_factory)
15
+ @uri_factory = uri_factory
16
+ end
17
+
18
+ # Creates a new link. Only for link involving a single key, at least for now.
19
+ # - type: Item type (e.g., services, service-instances, data-centers, etc.)
20
+ # - key_name: name of the property the item type uses as a unique key
21
+ # - key_value: key value
22
+ def link(type, key_name, key_value)
23
+ return nil if key_value.nil?
24
+
25
+ {
26
+ '_self' => @uri_factory.item_uri(type, key_value),
27
+
28
+ # FIXME Deprecated. To replace with _self URI above.
29
+ key_name => key_value
30
+ }
31
+ end
32
+
33
+ # Creates a new IP address role link.
34
+ # - service_instance_key: Service instance key
35
+ # - role_name: IP address role name
36
+ def ip_address_role_link(service_instance_key, role_name)
37
+ {
38
+ '_self' => @uri_factory.item_uri('ip-address-roles', service_instance_key, role_name),
39
+
40
+ # FIXME These are deprecated. To replace with _self URI above.
41
+ 'serviceInstance' => {
42
+ 'key' => service_instance_key
43
+ },
44
+ 'name' => role_name
45
+ }
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,332 @@
1
+ # Seiso namespace module
2
+ module Seiso
3
+
4
+ # Maps the data master format to the Seiso API format.
5
+ #
6
+ # Author:: Willie Wheeler (mailto:wwheeler@expedia.com)
7
+ # Copyright:: Copyright (c) 2014-2015 Expedia, Inc.
8
+ # License:: Apache 2.0
9
+
10
+ class MasterItemMapper
11
+
12
+ def initialize(link_factory)
13
+ @link_factory = link_factory
14
+ @mappers = {
15
+ 'data-centers' => data_center_mapper,
16
+ 'environments' => environment_mapper,
17
+ 'health-statuses' => health_status_mapper,
18
+ 'infrastructure-providers' => infrastructure_provider_mapper,
19
+ 'ip-address-roles' => ip_address_role_mapper,
20
+ 'load-balancers' => load_balancer_mapper,
21
+ 'machines' => machine_mapper,
22
+ 'nodes' => node_mapper,
23
+ 'node-ip-addresses' => node_ip_address_mapper,
24
+ 'regions' => region_mapper,
25
+ 'rotation-statuses' => rotation_status_mapper,
26
+ 'services' => service_mapper,
27
+ 'service-groups' => service_group_mapper,
28
+ 'service-instances' => service_instance_mapper,
29
+ 'service-instance-ports' => service_instance_port_mapper,
30
+ 'service-types' => service_type_mapper,
31
+ 'status-types' => status_type_mapper
32
+ }
33
+ end
34
+
35
+ # Maps a list of items.
36
+ # - type: Item type
37
+ # - items: Item list
38
+ def map_all(type, items)
39
+ mapper = mapper_for type
40
+ result = []
41
+ items.each { |i| result << mapper.call(i) }
42
+ result
43
+ end
44
+
45
+ # Maps a single item.
46
+ # - type: Item type
47
+ # - item: Item
48
+ def map_one(type, item)
49
+ mapper_for(type).call item
50
+ end
51
+
52
+ private
53
+
54
+ # Returns a link for the specified item, or nil if key_value is nil.
55
+ def link_for(type, key_name, key_value)
56
+ # Do nil check here (even though link factory does it too) since the unit test requires this behavior.
57
+ return key_value.nil? ? nil : @link_factory.link(type, key_name, key_value)
58
+ end
59
+
60
+ # Returns a mapper lambda for the specified type.
61
+ def mapper_for(type)
62
+ mapper = @mappers[type]
63
+ raise ArgumentError, "Unknown type: #{type}" if mapper.nil?
64
+ mapper
65
+ end
66
+
67
+ # Returns a mapper lambda.
68
+ def data_center_mapper
69
+ ->(dc) {
70
+ {
71
+ 'key' => dc['key'],
72
+ 'name' => dc['name'],
73
+ 'region' => link_for('regions', 'key', dc['region'])
74
+ }
75
+ }
76
+ end
77
+
78
+ # Returns a mapper lambda.
79
+ def environment_mapper
80
+ ->(e) {
81
+ {
82
+ 'key' => e['key'],
83
+ 'name' => e['name'],
84
+ 'aka' => e['aka'],
85
+ 'description' => e['description'],
86
+
87
+ # FIXME Deprecated, don't use.
88
+ 'rank' => e['rank']
89
+ }
90
+ }
91
+ end
92
+
93
+ # Returns a mapper lambda.
94
+ def health_status_mapper
95
+ ->(hs) {
96
+ {
97
+ 'key' => hs['key'],
98
+ 'name' => hs['name'],
99
+ 'statusType' => link_for('status-types', 'key', hs['statusType'])
100
+ }
101
+ }
102
+ end
103
+
104
+ # Returns a mapper lambda.
105
+ def infrastructure_provider_mapper
106
+ ->(ip) {
107
+ {
108
+ 'key' => ip['key'],
109
+ 'name' => ip['name']
110
+ }
111
+ }
112
+ end
113
+
114
+ # Returns a mapper lambda.
115
+ def ip_address_role_mapper
116
+ # Suppressing IP addresses since we don't import those from master files.
117
+ ->(r) {
118
+ {
119
+ 'serviceInstance' => link_for('service-instances', 'key', r['serviceInstance']),
120
+ 'name' => r['name'],
121
+ 'description' => r['description']
122
+ }
123
+ }
124
+ end
125
+
126
+ # Returns a mapper lambda.
127
+ def load_balancer_mapper
128
+ ->(lb) {
129
+ {
130
+ 'name' => lb['name'],
131
+ 'type' => lb['type'],
132
+ 'ipAddress' => lb['ipAddress'],
133
+ 'dataCenter' => link_for('data-centers', 'key', lb['dataCenter']),
134
+ 'apiUrl' => lb['apiUrl']
135
+ }
136
+ }
137
+ end
138
+
139
+ # Returns a mapper lambda.
140
+ def machine_mapper
141
+ ->(m) {
142
+ {
143
+ 'name' => m['name'],
144
+ 'ipAddress' => m['ipAddress'],
145
+ 'fqdn' => m['fqdn'],
146
+ 'hostname' => m['hostname'],
147
+ 'domain' => m['domain'],
148
+ 'os' => m['os'],
149
+ 'platform' => m['platform'],
150
+ 'platformVersion' => m['platformVersion']
151
+ }
152
+ }
153
+ end
154
+
155
+ # Returns a mapper lambda.
156
+ def node_mapper
157
+ ->(n) {
158
+ service_instance = n['serviceInstance']
159
+ ip_addresses = n['ipAddresses']
160
+ si_ref = link_for('service-instances', 'key', service_instance)
161
+
162
+ result = {
163
+ 'name' => n['name'],
164
+ 'serviceInstance' => si_ref,
165
+ 'machine' => link_for('machines', 'name', n['machine'])
166
+ }
167
+
168
+ =begin
169
+ if ip_addresses
170
+ result['ipAddresses'] = []
171
+ ip_addresses.each do |ip|
172
+ role_name = ip['ipAddressRole']
173
+ result['ipAddresses'] << {
174
+ 'ipAddressRole' => @link_factory.ip_address_role_link(service_instance, role_name),
175
+ 'ipAddress' => ip['ipAddress']
176
+ }
177
+ end
178
+ end
179
+ =end
180
+
181
+ result
182
+ }
183
+ end
184
+
185
+ # Returns a mapper lambda.
186
+ def node_ip_address_mapper
187
+ # Currently suppressing rotation status and endpoints since we don't import those from master files
188
+ ->(nip) {
189
+ {
190
+ 'node' => link_for('nodes', 'name', nip['node']),
191
+ 'ipAddressRole' => link_for('ip-address-roles', 'name', nip['ipAddressRole']),
192
+ 'ipAddress' => nip['ipAddress']
193
+ }
194
+ }
195
+ end
196
+
197
+ # Returns a mapper lambda.
198
+ def region_mapper
199
+ ->(r) {
200
+ {
201
+ 'key' => r['key'],
202
+ 'name' => r['name'],
203
+ 'regionKey' => r['regionKey'],
204
+ 'infrastructureProvider' => link_for('infrastructure-providers', 'key', r['infrastructureProvider'])
205
+ }
206
+ }
207
+ end
208
+
209
+ # Returns a mapper lambda.
210
+ def rotation_status_mapper
211
+ ->(rs) {
212
+ {
213
+ 'key' => rs['key'],
214
+ 'name' => rs['name'],
215
+ 'statusType' => link_for('status-types', 'key', rs['statusType'])
216
+ }
217
+ }
218
+ end
219
+
220
+ # Returns a mapper lambda.
221
+ def service_mapper
222
+ ->(s) {
223
+ {
224
+ 'key' => s['key'],
225
+ 'name' => s['name'],
226
+ 'description' => s['description'],
227
+ 'group' => link_for('service-groups', 'key', s['group']),
228
+ 'type' => link_for('service-types', 'key', s['type']),
229
+ 'owner' => link_for('people', 'username', s['owner']),
230
+ 'platform' => s['platform'],
231
+ 'scmRepository' => s['scmRepository']
232
+ }
233
+ }
234
+ end
235
+
236
+ # Returns a mapper lambda.
237
+ def service_group_mapper
238
+ ->(sg) {
239
+ {
240
+ 'key' => sg['key'],
241
+ 'name' => sg['name']
242
+ }
243
+ }
244
+ end
245
+
246
+ # Returns a mapper lambda.
247
+ def service_instance_mapper
248
+ ->(si) {
249
+ key = si['key']
250
+ ip_address_roles = si['ipAddressRoles']
251
+ ports = si['ports']
252
+
253
+ result = {
254
+ 'key' => key,
255
+ 'service' => link_for('services', 'key', si['service']),
256
+ 'environment' => link_for('environments', 'key', si['environment']),
257
+ 'dataCenter' => link_for('data-centers', 'key', si['dataCenter']),
258
+ 'loadBalanced' => si['loadBalanced'],
259
+ 'loadBalancer' => link_for('loadBalancer', 'name', si['loadBalancer']),
260
+ 'minCapacityDeploy' => si['minCapacityDeploy'],
261
+ 'minCapacityOps' => si['minCapacityOps'],
262
+
263
+ # FIXME Deprecated. This is a hardcoded field for an internal Expedia app, and we will remove it.
264
+ 'eosManaged' => (si['eosManaged'] || false),
265
+
266
+ # FIXME Deprecated. This is the old name for the minCapacityOps field.
267
+ 'requiredCapacity' => si['minCapacityOps']
268
+ }
269
+
270
+ =begin
271
+ if ip_address_roles
272
+ result['ipAddressRoles'] = []
273
+ ip_address_roles.each do |role|
274
+ result['ipAddressRoles'] << {
275
+ 'serviceInstance' => link_for('service-instances', 'key', key),
276
+ 'name' => role['name'],
277
+ 'description' => role['description']
278
+ }
279
+ end
280
+ end
281
+
282
+ if ports
283
+ result['ports'] = []
284
+ ports.each do |port|
285
+ result['ports'] << {
286
+ 'serviceInstance' => link_for('service-instances', 'key', key),
287
+ 'number' => port['number'],
288
+ 'protocol' => port['protocol'],
289
+ 'description' => port['description']
290
+ }
291
+ end
292
+ end
293
+ =end
294
+
295
+ result
296
+ }
297
+ end
298
+
299
+ # Returns a mapper lambda.
300
+ def service_instance_port_mapper
301
+ # Suppressing endpoints since we don't import those from master files.
302
+ ->(p) {
303
+ {
304
+ 'serviceInstance' => link_for('service-instances', 'key', p['serviceInstance']),
305
+ 'number' => p['number'],
306
+ 'protocol' => p['protocol'],
307
+ 'description' => p['description']
308
+ }
309
+ }
310
+ end
311
+
312
+ # Returns a mapper lambda.
313
+ def service_type_mapper
314
+ ->(st) {
315
+ {
316
+ 'key' => st['key'],
317
+ 'name' => st['name']
318
+ }
319
+ }
320
+ end
321
+
322
+ # Returns a mapper lambda.
323
+ def status_type_mapper
324
+ ->(st) {
325
+ {
326
+ 'key' => st['key'],
327
+ 'name' => st['name']
328
+ }
329
+ }
330
+ end
331
+ end
332
+ end
@@ -0,0 +1,48 @@
1
+ # Seiso namespace module
2
+ module Seiso
3
+
4
+ # URI factory.
5
+ #
6
+ # Author:: Willie Wheeler (mailto:wwheeler@expedia.com)
7
+ # Copyright:: Copyright (c) 2014-2015 Expedia, Inc.
8
+ # License:: Apache 2.0
9
+
10
+ class UriFactory
11
+
12
+ # Creates a new URI factory for the given base URI.
13
+ # - base_uri: Base URI (e.g., http://seiso.example.com)
14
+ def initialize(base_uri)
15
+ @base_uri = base_uri
16
+ @v1_uri = "#{@base_uri}/v1"
17
+ end
18
+
19
+ # Returns the URI for the given item type.
20
+ # - type: Item type (e.g., services, service-instances, ip-address-roles, etc.)
21
+ def type_uri(type)
22
+ if type == 'ip-address-roles'
23
+ raise ArgumentError, "ip-address-roles is not a top-level type"
24
+ else
25
+ return v1_uri type
26
+ end
27
+ end
28
+
29
+ # Returns the URI for the given item.
30
+ # - type: Item type
31
+ # - keys: One or more item keys, which collectively uniquely identify the item within the type
32
+ def item_uri(type, *keys)
33
+ if type == 'ip-address-roles'
34
+ return v1_uri "service-instances/#{keys[0]}/ip-address-roles/#{keys[1]}"
35
+ else
36
+ return v1_uri "#{type}/#{keys[0]}"
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ # Resolves a relative path to the full URI.
43
+ # - path: relative path
44
+ def v1_uri(path)
45
+ "#{@base_uri}/v1/#{path}"
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,9 @@
1
+ %YAML 1.1
2
+ ---
3
+ host: seiso.example.com
4
+ port: 443
5
+ use_ssl: true
6
+ ignore_cert: false
7
+ username: some_username
8
+ password: some_p@ssword
9
+ ---
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "seiso-import_master"
7
+ spec.version = "0.0.1"
8
+ spec.authors = ["Willie Wheeler"]
9
+ spec.email = ["wwheeler@expedia.com"]
10
+ spec.summary = "Imports Seiso data master files into Seiso."
11
+ spec.description = "Supports self-service data management via version control, e.g. by using GitHub."
12
+ spec.homepage = "http://seiso.io"
13
+ spec.license = "Apache-2.0"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.7"
21
+ spec.add_development_dependency "rake", "~> 10.0"
22
+
23
+ spec.add_runtime_dependency "seiso-connector", "~> 0.0"
24
+ end
@@ -0,0 +1,287 @@
1
+ require 'seiso/import_master/master_item_mapper'
2
+ require 'minitest/autorun'
3
+
4
+ # Author:: Willie Wheeler (mailto:wwheeler@expedia.com)
5
+ # Copyright:: Copyright (c) 2014-2015 Expedia, Inc.
6
+ # License:: Apache 2.0
7
+
8
+ class TestMasterItemMapper < MiniTest::Unit::TestCase
9
+
10
+ def setup
11
+ link_factory = Class.new do
12
+ def self.link(type, key_name, key_value)
13
+ { "foo" => "bar" }
14
+ end
15
+ end
16
+ @mapper = Seiso::MasterItemMapper.new link_factory
17
+ end
18
+
19
+ def test_map_all_illegal_type
20
+ assert_raises(ArgumentError) do
21
+ @mapper.map_all('some-bogus-type', [])
22
+ end
23
+ end
24
+
25
+ def test_map_one_illegal_type
26
+ assert_raises(ArgumentError) do
27
+ @mapper.map_one('some-bogus-type', {})
28
+ end
29
+ end
30
+
31
+ def test_map_data_center
32
+ from = {
33
+ 'key' => 'amazon-us-east-1a',
34
+ 'name' => 'Amazon US East 1a',
35
+ 'region' => 'amazon-us-east-1'
36
+ }
37
+ to = @mapper.map_one('data-centers', from)
38
+ assert_equal(from['key'], to['key'])
39
+ assert_equal(from['name'], to['name'])
40
+ refute_nil(to['region'])
41
+ end
42
+
43
+ def test_map_environment
44
+ from = {
45
+ 'key' => 'prod',
46
+ 'name' => 'Production',
47
+ 'aka' => 'Live',
48
+ 'description' => 'Production environment'
49
+ }
50
+ to = @mapper.map_one('environments', from)
51
+ assert_equal(from['key'], to['key'])
52
+ assert_equal(from['name'], to['name'])
53
+ assert_equal(from['aka'], to['aka'])
54
+ assert_equal(from['description'], to['description'])
55
+ end
56
+
57
+ def test_map_health_status
58
+ from = {
59
+ 'key' => 'healthy',
60
+ 'name' => 'Healthy',
61
+ 'statusType' => 'success'
62
+ }
63
+ to = @mapper.map_one('health-statuses', from)
64
+ assert_equal(from['key'], to['key'])
65
+ assert_equal(from['name'], to['name'])
66
+ refute_nil(to['statusType'])
67
+ end
68
+
69
+ def test_map_infrastructure_provider
70
+ from = {
71
+ 'key' => 'amazon',
72
+ 'name' => 'Amazon'
73
+ }
74
+ to = @mapper.map_one('infrastructure-providers', from)
75
+ assert_equal(from['key'], to['key'])
76
+ assert_equal(from['name'], to['name'])
77
+ end
78
+
79
+ def test_map_load_balancer
80
+ from = {
81
+ 'name' => 'LB-1-2-3-4',
82
+ 'type' => 'NetScaler',
83
+ 'ipAddress' => '1.2.3.4',
84
+ 'dataCenter' => 'amazon-us-east-1a',
85
+ 'apiUrl' => 'https://1.2.3.4/api'
86
+ }
87
+ to = @mapper.map_one('load-balancers', from)
88
+ assert_equal(from['name'], to['name'])
89
+ assert_equal(from['type'], to['type'])
90
+ assert_equal(from['ipAddress'], to['ipAddress'])
91
+ refute_nil(to['dataCenter'])
92
+ assert_equal(from['apiUrl'], to['apiUrl'])
93
+ end
94
+
95
+ def test_map_load_balancer_nil_data_center
96
+ from = {
97
+ 'name' => 'LB-1-2-3-4',
98
+ 'type' => 'NetScaler',
99
+ 'ipAddress' => '1.2.3.4',
100
+ 'apiUrl' => 'https://1.2.3.4/api'
101
+ }
102
+ to = @mapper.map_one('load-balancers', from)
103
+ assert_nil(to['dataCenter'])
104
+ end
105
+
106
+ def test_map_machine
107
+ from = {
108
+ 'name' => 'ip-1-2-3-4',
109
+ 'ipAddress' => '1.2.3.4',
110
+ 'fqdn' => 'seiso01.dev.example.com',
111
+ 'hostname' => 'seiso01',
112
+ 'domain' => 'dev.example.com',
113
+ 'os' => 'linux',
114
+ 'platform' => 'amazon',
115
+ 'platformVersion' => '201409'
116
+ }
117
+ to = @mapper.map_one('machines', from)
118
+ assert_equal(from['name'], to['name'])
119
+ assert_equal(from['ipAddress'], to['ipAddress'])
120
+ assert_equal(from['fqdn'], to['fqdn'])
121
+ assert_equal(from['hostname'], to['hostname'])
122
+ assert_equal(from['domain'], to['domain'])
123
+ assert_equal(from['os'], to['os'])
124
+ assert_equal(from['platform'], to['platform'])
125
+ assert_equal(from['platformVersion'], to['platformVersion'])
126
+ end
127
+
128
+ def test_map_node
129
+ from = {
130
+ 'name' => 'seiso01-dev',
131
+ 'serviceInstance' => 'seiso-dev',
132
+ 'machine' => 'ip-1-2-3-4',
133
+ 'ipAddresses' => [
134
+ {
135
+ 'ipAddressRole' => 'internal',
136
+ 'ipAddress' => '1.2.10.1'
137
+ }, {
138
+ 'ipAddressRole' => 'partners',
139
+ 'ipAddress' => '1.2.10.2'
140
+ }
141
+ ]
142
+ }
143
+ to = @mapper.map_one('nodes', from)
144
+ assert_equal(from['name'], to['name'])
145
+ refute_nil(to['serviceInstance'])
146
+ refute_nil(to['machine'])
147
+
148
+ # TODO Move these to a test_ip_address method
149
+ # assert_equal(from['ipAddresses'][0]['ipAddressRole'], to['ipAddresses'][0]['ipAddressRole']['name'])
150
+ # assert_equal(from['ipAddresses'][0]['ipAddress'], to['ipAddresses'][0]['ipAddress'])
151
+ # assert_equal(from['ipAddresses'][1]['ipAddressRole'], to['ipAddresses'][1]['ipAddressRole']['name'])
152
+ # assert_equal(from['ipAddresses'][1]['ipAddress'], to['ipAddresses'][1]['ipAddress'])
153
+ end
154
+
155
+ def test_map_region
156
+ from = {
157
+ 'key' => 'amazon-us-east-1',
158
+ 'name' => 'Amazon US East 1',
159
+ 'regionKey' => 'US East',
160
+ 'infrastructureProvider' => 'amazon'
161
+ }
162
+ to = @mapper.map_one('regions', from)
163
+ assert_equal(from['key'], to['key'])
164
+ assert_equal(from['name'], to['name'])
165
+ assert_equal(from['regionKey'], to['regionKey'])
166
+ refute_nil(to['infrastructureProvider'])
167
+ end
168
+
169
+ def test_map_rotation_status
170
+ from = {
171
+ 'key' => 'enabled',
172
+ 'name' => 'Enabled',
173
+ 'statusType' => 'success'
174
+ }
175
+ to = @mapper.map_one('rotation-statuses', from)
176
+ assert_equal(from['key'], to['key'])
177
+ assert_equal(from['name'], to['name'])
178
+ refute_nil(to['statusType'])
179
+ end
180
+
181
+ def test_map_service
182
+ from = {
183
+ 'key' => 'seiso',
184
+ 'name' => 'Seiso',
185
+ 'description' => 'Devops data repo',
186
+ 'platform' => 'Java',
187
+ 'scmRepository' => 'https://github.com/ExpediaDotCom/seiso',
188
+ 'group' => 'devops',
189
+ 'type' => 'web-service',
190
+ 'owner' => 'wwheeler'
191
+ }
192
+ to = @mapper.map_one('services', from)
193
+ assert_equal(from['key'], to['key'])
194
+ assert_equal(from['name'], to['name'])
195
+ assert_equal(from['description'], to['description'])
196
+ assert_equal(from['platform'], to['platform'])
197
+ assert_equal(from['scmRepository'], to['scmRepository'])
198
+ refute_nil(to['group'])
199
+ refute_nil(to['type'])
200
+ refute_nil(to['owner'])
201
+ end
202
+
203
+ def test_map_service_group
204
+ from = {
205
+ 'key' => 'devops',
206
+ 'name' => 'DevOps'
207
+ }
208
+ to = @mapper.map_one('service-groups', from)
209
+ assert_equal(from['key'], to['key'])
210
+ assert_equal(from['name'], to['name'])
211
+ end
212
+
213
+ def test_map_service_instance
214
+ from = {
215
+ 'key' => 'seiso-dev',
216
+ 'service' => 'seiso',
217
+ 'environment' => 'dev',
218
+ 'dataCenter' => 'amazon-us-west-1b',
219
+ 'loadBalanced' => true,
220
+ 'loadBalancer' => {
221
+ 'name' => 'DEV-1-2-3-4'
222
+ },
223
+ 'minCapacityDeploy' => 50,
224
+ 'minCapacityOps' => 75,
225
+ 'ipAddressRoles' => [
226
+ {
227
+ 'name' => 'internal',
228
+ 'description' => 'Internal role'
229
+ }, {
230
+ 'name' => 'partners',
231
+ 'description' => 'Partners role'
232
+ }
233
+ ],
234
+ 'ports' => [
235
+ {
236
+ 'number' => 8443,
237
+ 'protocol' => 'https',
238
+ 'description' => 'UI port'
239
+ }, {
240
+ 'number' => 8444,
241
+ 'protocol' => 'https',
242
+ 'description' => 'API port'
243
+ }
244
+ ]
245
+ }
246
+ to = @mapper.map_one('service-instances', from)
247
+ assert_equal(from['key'], to['key'])
248
+
249
+ refute_nil(to['service'])
250
+ refute_nil(to['environment'])
251
+ refute_nil(to['dataCenter'])
252
+ refute_nil(to['loadBalancer'])
253
+ assert_equal(from['loadBalanced'], to['loadBalanced'])
254
+ assert_equal(from['minCapacityDeploy'], to['minCapacityDeploy'])
255
+ assert_equal(from['minCapacityOps'], to['minCapacityOps'])
256
+
257
+ # TODO Move these to a test_ip_address_role method
258
+ # assert_equal(from['ipAddressRoles'].length, to['ipAddressRoles'].length)
259
+ # refute_nil(to['ipAddressRoles'][0]['serviceInstance'])
260
+ # refute_nil(to['ipAddressRoles'][1]['serviceInstance'])
261
+
262
+ # TODO Move these to a test_service_instance_port method
263
+ # assert_equal(from['ports'].length, to['ports'].length)
264
+ # refute_nil(to['ports'][0]['serviceInstance'])
265
+ # refute_nil(to['ports'][1]['serviceInstance'])
266
+ end
267
+
268
+ def test_map_service_type
269
+ from = {
270
+ 'key' => 'web-service',
271
+ 'name' => 'Web Service'
272
+ }
273
+ to = @mapper.map_one('service-types', from)
274
+ assert_equal(from['key'], to['key'])
275
+ assert_equal(from['name'], to['name'])
276
+ end
277
+
278
+ def test_map_status_type
279
+ from = {
280
+ 'key' => 'warning',
281
+ 'name' => 'Warning'
282
+ }
283
+ to = @mapper.map_one('status-types', from)
284
+ assert_equal(from['key'], to['key'])
285
+ assert_equal(from['name'], to['name'])
286
+ end
287
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: seiso-import_master
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Willie Wheeler
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: seiso-connector
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '0.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '0.0'
55
+ description: Supports self-service data management via version control, e.g. by using
56
+ GitHub.
57
+ email:
58
+ - wwheeler@expedia.com
59
+ executables:
60
+ - seiso-import-master
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - .gitignore
65
+ - .travis.yml
66
+ - Gemfile
67
+ - LICENSE.txt
68
+ - README.md
69
+ - Rakefile
70
+ - bin/seiso-import-master
71
+ - lib/seiso/import_master.rb
72
+ - lib/seiso/import_master/link_factory.rb
73
+ - lib/seiso/import_master/master_item_mapper.rb
74
+ - lib/seiso/import_master/uri_factory.rb
75
+ - sample_conf/seiso.yml.sample
76
+ - seiso-import_master.gemspec
77
+ - test/test_master_item_mapper.rb
78
+ homepage: http://seiso.io
79
+ licenses:
80
+ - Apache-2.0
81
+ metadata: {}
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ! '>='
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubyforge_project:
98
+ rubygems_version: 2.4.4
99
+ signing_key:
100
+ specification_version: 4
101
+ summary: Imports Seiso data master files into Seiso.
102
+ test_files:
103
+ - test/test_master_item_mapper.rb