seiso-import_master 0.0.12 → 3.0.0.M1

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.
Files changed (63) hide show
  1. checksums.yaml +8 -8
  2. data/.gitignore +1 -0
  3. data/.travis.yml +3 -0
  4. data/README.md +12 -11
  5. data/Rakefile +3 -3
  6. data/bin/seiso-import-master +30 -13
  7. data/lib/seiso/import_master.rb +114 -90
  8. data/lib/seiso/import_master/importers.rb +5 -0
  9. data/lib/seiso/import_master/importers/base_importer.rb +91 -30
  10. data/lib/seiso/import_master/importers/data_center_importer.rb +31 -0
  11. data/lib/seiso/import_master/importers/environment_importer.rb +25 -0
  12. data/lib/seiso/import_master/importers/health_status_importer.rb +25 -0
  13. data/lib/seiso/import_master/importers/infrastructure_provider_importer.rb +25 -0
  14. data/lib/seiso/import_master/importers/ip_address_role_importer.rb +41 -0
  15. data/lib/seiso/import_master/importers/load_balancer_importer.rb +25 -0
  16. data/lib/seiso/import_master/importers/machine_importer.rb +25 -0
  17. data/lib/seiso/import_master/importers/node_importer.rb +94 -56
  18. data/lib/seiso/import_master/importers/node_ip_address_importer.rb +46 -0
  19. data/lib/seiso/import_master/importers/region_importer.rb +31 -0
  20. data/lib/seiso/import_master/importers/rotation_status_importer.rb +25 -0
  21. data/lib/seiso/import_master/importers/service_group_importer.rb +25 -0
  22. data/lib/seiso/import_master/importers/service_importer.rb +38 -143
  23. data/lib/seiso/import_master/importers/service_instance_importer.rb +60 -213
  24. data/lib/seiso/import_master/importers/service_instance_port_importer.rb +41 -0
  25. data/lib/seiso/import_master/importers/service_type_importer.rb +25 -0
  26. data/lib/seiso/import_master/importers/status_type_importer.rb +25 -0
  27. data/lib/seiso/import_master/mappers.rb +1 -0
  28. data/lib/seiso/import_master/mappers/data_center_mapper.rb +19 -0
  29. data/lib/seiso/import_master/mappers/environment_mapper.rb +20 -0
  30. data/lib/seiso/import_master/mappers/health_status_mapper.rb +20 -0
  31. data/lib/seiso/import_master/mappers/infrastructure_provider_mapper.rb +18 -0
  32. data/lib/seiso/import_master/mappers/ip_address_role_mapper.rb +18 -0
  33. data/lib/seiso/import_master/mappers/load_balancer_mapper.rb +21 -0
  34. data/lib/seiso/import_master/mappers/machine_mapper.rb +27 -0
  35. data/lib/seiso/import_master/mappers/node_ip_address_mapper.rb +38 -0
  36. data/lib/seiso/import_master/mappers/node_mapper.rb +31 -31
  37. data/lib/seiso/import_master/mappers/region_mapper.rb +20 -0
  38. data/lib/seiso/import_master/mappers/rotation_status_mapper.rb +20 -0
  39. data/lib/seiso/import_master/mappers/service_group_mapper.rb +18 -0
  40. data/lib/seiso/import_master/mappers/service_instance_mapper.rb +26 -73
  41. data/lib/seiso/import_master/mappers/service_instance_port_mapper.rb +19 -0
  42. data/lib/seiso/import_master/mappers/service_mapper.rb +23 -32
  43. data/lib/seiso/import_master/mappers/service_type_mapper.rb +19 -0
  44. data/lib/seiso/import_master/mappers/status_type_mapper.rb +18 -0
  45. data/lib/seiso/import_master/util.rb +1 -0
  46. data/lib/seiso/import_master/util/invalid_document_error.rb +9 -0
  47. data/lib/seiso/import_master/util/item_resolver.rb +54 -0
  48. data/lib/seiso/import_master/util/logger.rb +59 -0
  49. data/lib/seiso/import_master/util/rest_util.rb +49 -0
  50. data/lib/seiso/import_master/validators.rb +1 -0
  51. data/lib/seiso/import_master/validators/base_validator.rb +11 -18
  52. data/lib/seiso/import_master/validators/node_validator.rb +54 -45
  53. data/lib/seiso/import_master/validators/service_validator.rb +38 -44
  54. data/sample_conf/seiso3.yml.sample +6 -0
  55. data/seiso-import_master.gemspec +16 -13
  56. data/test/seiso/import_master/importers/test_node_importer.rb +21 -0
  57. metadata +66 -21
  58. data/lib/seiso/import_master/errors/invalid_document_error.rb +0 -11
  59. data/lib/seiso/import_master/importers/simple_importer.rb +0 -28
  60. data/lib/seiso/import_master/mappers/master_item_mapper.rb +0 -194
  61. data/sample_conf/seiso.yml.sample +0 -9
  62. data/test/test_master_item_mapper.rb +0 -257
  63. data/test/test_node_mapper.rb +0 -40
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- Y2M4MmJmOWJiYTM0NDliNTk3MDZjODJmODcyZjcwNTcxOWQ2ZjAzNA==
4
+ MTk2ZjJlYzk0YTA2YzVhN2IwOWEyZWM3NWM3Y2MwMjc3MjI0ZTNkOA==
5
5
  data.tar.gz: !binary |-
6
- NjRkZDA2NmUyOGY1MjI4OWE1MzljNjdjYjNkZmMwOTNiMmFkYTFjNQ==
6
+ ZTM0NDk1MTQ1YmIxM2Y5OTdmMzc4ZTE4MWY5ZmU0NjM4MjcxYmY3Ng==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NmMyOTUyZWIxNDM1NDE0M2IyNDFjMzgzOWZkYTc4NGRkZDMyNzQ4ZjBjYzFl
10
- YTQwMDA1MmVmMjFhNjFmM2EzOTljZjkxMjk4YjNhNDhkMzM3YWE0YTlmNTQ5
11
- OWU0ZGFmNzU5NWVmZTI5Mjc4ZWYzZmVhMzU0MmY0NGY4ODM5YTA=
9
+ YTkwNmE2N2E5M2FlMzZkN2FmNGZjOGEyODFjNmNmOTJmMWMxMThiYmJmMTY2
10
+ ZGY2YmQwZTIxMjU3MTkwYWJjMGFlYjU0YWI3ZjM3MjRlYmVlYTUyMGVkY2Ux
11
+ MDQ0NDUzODhlZmVjYjFkNDJhNGQ5Mjk3N2Q5YjRmNTNjMTc5MDM=
12
12
  data.tar.gz: !binary |-
13
- NjE1NTIyMTNkZjA1MmQ0NzgzN2JhMTg5YzE3YjI5MmM0NTIzNjg2N2E4YjY5
14
- M2JkMjVhOGM2ZTJiN2U5ZjQ1NzdlN2JiMGJmMDczODNiZGM5ODhlMjliMDUy
15
- YmQ4NzY4YjRhYTZhODE4OTVjZDYxYjdjODYzZWIzNzBmOTVkMWM=
13
+ MTI5ZGI3Y2E4NmFkMzg4YjlkMGRjNWQwMDcyODMxMWQxZGU0ZTIxNjU2OWQw
14
+ OGE5YjM4YTEwNzhlNzM1NjhjMDcyMTdjNGNjNzM3YzA4MDMzNTcxMmM4M2Yx
15
+ MmZmMjFmOTk4MDRlMDFlZjllN2I3NTRhOThjMDk3YWNmYzBjNTc=
data/.gitignore CHANGED
@@ -14,3 +14,4 @@
14
14
  *.a
15
15
  mkmf.log
16
16
  /.project
17
+ /.idea
@@ -1,3 +1,6 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
+
5
+ before_install:
6
+ - gem install bundler
data/README.md CHANGED
@@ -10,26 +10,20 @@ See [Manage Your Service Data with GitHub, Jenkins & Seiso](http://seiso.io/guid
10
10
 
11
11
  See [Seiso Data Master Schemas](http://seiso.io/docs/current/user-manual/sdm-schemas/) for the data master schemas.
12
12
 
13
- ## Installation
14
-
15
- Add this line to your application's Gemfile:
13
+ ## Installation preliminaries
16
14
 
17
- ```ruby
18
- gem 'seiso-import_master'
19
- ```
15
+ First, I recommend using [RVM](http://rvm.io/) to install/manage your rubies so you can run multiple rubies without conflict.
20
16
 
21
- And then execute:
17
+ You will need Ruby 1.9.3 as well as [Bundler](http://bundler.io).
22
18
 
23
- $ bundle
24
-
25
- Or install it yourself as:
19
+ ## Installation
26
20
 
27
21
  $ gem install seiso-import_master
28
22
 
29
23
  ## Usage
30
24
 
31
25
  1. Create a directory `~/.seiso-importers`
32
- 2. Place appropriately modified copy of `seiso.yml.sample` in there.
26
+ 2. Place appropriately modified copy of `seiso3.yml.sample` in there. Note that you will need to get Seiso credentials from your Seiso administrator.
33
27
  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
28
 
35
29
  ## Contributing
@@ -53,3 +47,10 @@ In particular, run the unit tests using
53
47
  Build and install the gem using Rake:
54
48
 
55
49
  $ rake install
50
+
51
+ For local snapshot installations, you may want to clear out existing versions of the gem first.
52
+
53
+ ## TODO
54
+
55
+ Figure out whether we want to use dependency injection for this project. There's debate within the Ruby community
56
+ whether DI makes sense for Ruby.
data/Rakefile CHANGED
@@ -1,5 +1,5 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
3
 
4
4
  task :default => [ :test ]
5
5
 
@@ -11,7 +11,7 @@ end
11
11
 
12
12
  Rake::TestTask.new do |t|
13
13
  t.libs << 'test'
14
- t.test_files = FileList[ 'test/test_*.rb' ]
14
+ t.test_files = FileList[ 'test/**/test_*.rb' ]
15
15
  t.verbose = true
16
16
  t.warning = true
17
17
  end
@@ -3,31 +3,33 @@
3
3
  # vi: set ft=ruby :
4
4
 
5
5
  require 'optparse'
6
- require 'seiso/import_master'
7
6
  require 'yaml'
7
+ require 'seiso/import_master'
8
8
 
9
9
  # Imports Seiso data master files into Seiso.
10
10
  #
11
11
  # Author:: Willie Wheeler
12
- # Copyright:: Copyright (c) 2014-2015 Expedia, Inc.
12
+ # Copyright:: Copyright (c) 2014-2016 Expedia, Inc.
13
13
  # License:: Apache 2.0
14
14
 
15
+ log = Seiso::ImportMaster::Util::Logger.new "seiso-import-master"
16
+
15
17
  options = {}
16
18
  option_parser = OptionParser.new do |opts|
17
19
  executable_name = File.basename $PROGRAM_NAME
18
20
  opts.banner = "Import one or more Seiso data master files into Seiso.
19
21
 
20
22
  Usage: #{executable_name} [options] master_file ..."
21
- options[:seiso_settings_path] = "#{Dir.home}/.seiso-importers/seiso.yml"
22
- opts.on("--seiso SEISO_SETTINGS_PATH", "Path to Seiso API settings file") do |path|
23
- options[:seiso_settings_path] = path
23
+ options[:seiso3_settings_path] = "#{Dir.home}/.seiso-importers/seiso3.yml"
24
+ opts.on("--seiso3 SEISO3_SETTINGS_PATH", "Path to Seiso API v3 settings file") do |path|
25
+ options[:seiso3_settings_path] = path
24
26
  end
25
27
 
26
28
  options[:format] = 'json'
27
29
  opts.on("-f FORMAT", "--format FORMAT", "Document format, either 'json' (default) or 'yaml'") do |format|
28
30
  options[:format] = format
29
31
  end
30
-
32
+
31
33
  opts.on("-h", "--help", "Display this screen") do
32
34
  puts opts
33
35
  exit
@@ -37,15 +39,30 @@ end
37
39
  begin
38
40
  option_parser.parse!
39
41
  rescue OptionParser::InvalidOption => e
40
- abort "Invalid option.\n\n#{option_parser.help}"
42
+ log.error "Invalid option."
43
+ puts option_parser.help
44
+ abort
41
45
  end
42
46
 
43
- puts "Using: #{options[:seiso_settings_path]}"
44
- puts "Using: format=#{options[:format]}"
47
+ log.info "Using: #{options[:seiso3_settings_path]}"
48
+ log.info "Using: format=#{options[:format]}"
45
49
 
46
50
  files = ARGV
47
- abort "Error: You must supply a master file.\n\n#{option_parser.help}" if files.empty?
51
+ if files.empty?
52
+ log.error "You must supply a master file."
53
+ puts option_parser.help
54
+ abort
55
+ end
56
+
57
+ seiso3_settings = YAML.load_file(options[:seiso3_settings_path])
58
+ importer = Seiso::ImportMaster.new seiso3_settings
59
+ result = importer.import_files(files, options[:format])
48
60
 
49
- settings = YAML.load_file(options[:seiso_settings_path])
50
- importer = Seiso::ImportMaster.new settings
51
- importer.import_files(files, options[:format])
61
+ if result == "success"
62
+ log.success "Import succeeded."
63
+ elsif result == "partial"
64
+ log.warn "Import succeeded, but with issues."
65
+ else
66
+ log.error "Import failed."
67
+ abort
68
+ end
@@ -1,114 +1,138 @@
1
- require "json"
2
- require "require_all"
3
- require "yaml"
4
- require "seiso/connector"
5
-
6
- require_rel "import_master"
1
+ require 'json'
2
+ require 'yaml'
3
+ require 'hyper_resource'
4
+ require 'require_all'
5
+ require_rel './import_master'
7
6
 
7
+ # Imports Seiso data master files into Seiso.
8
+ #
9
+ # Author:: Willie Wheeler
10
+ # Copyright:: Copyright (c) 2014-2016 Expedia, Inc.
11
+ # License:: Apache 2.0
8
12
  module Seiso
9
-
10
- # Imports Seiso data master files into Seiso.
11
- #
12
- # Author:: Willie Wheeler (mailto:wwheeler@expedia.com)
13
- # Copyright:: Copyright (c) 2014-2015 Expedia, Inc.
14
- # License:: Apache 2.0
15
13
  class ImportMaster
16
-
17
- # Initializes the importer with a Seiso connector.
18
- def initialize(seiso_settings)
14
+
15
+ def initialize(seiso3_settings)
16
+ @log = Util::Logger.new "ImportMaster"
19
17
  @loaders = {
20
18
  'json' => ->(file) { JSON.parse(IO.read(file)) },
21
19
  'yaml' => ->(file) { YAML.load_file file }
22
20
  }
21
+ @v3_api = HyperResource.new(
22
+ root: seiso3_settings['base_uri'],
23
+ headers: {
24
+ 'Accept' => 'application/hal+json',
25
+ 'Content-Type' => 'application/hal+json'
26
+ },
27
+ auth: {
28
+ basic: [ seiso3_settings['username'], seiso3_settings['password'] ]
29
+ }
30
+ )
31
+ @resolver = Util::ItemResolver.new @v3_api
32
+ @rest_util = Util::RestUtil.new
23
33
 
24
- # TODO Add other validators
25
- @validators = {
26
- 'nodes' => Validators::NodeValidator.new,
27
- 'services' => Validators::ServiceValidator.new
28
- }
29
-
30
- basic_mapper = Mappers::MasterItemMapper.new
31
- node_mapper = Mappers::NodeMapper.new
32
- service_mapper = Mappers::ServiceMapper.new
33
- service_instance_mapper = Mappers::ServiceInstanceMapper.new
34
-
35
- seiso = Seiso::Connector.new seiso_settings
36
- uri_factory_v1 = uri_factory_v1 seiso_settings
37
- uri_factory_v2 = uri_factory_v2 seiso_settings
38
- rest_connector_v1 = rest_connector(seiso_settings, "application/json")
39
- rest_connector_v2 = rest_connector(seiso_settings, "application/hal+json")
40
-
41
- @simple_importer = Importers::SimpleImporter.new(basic_mapper, seiso, uri_factory_v1, rest_connector_v1)
42
-
43
- # TODO Clean this up. Just use v2 URI factory and connector.
44
- @importers = {
45
- 'nodes' => Importers::NodeImporter.new(node_mapper, uri_factory_v1, rest_connector_v1),
46
- 'services' => Importers::ServiceImporter.new(
47
- basic_mapper,
48
- service_mapper,
49
- uri_factory_v1,
50
- uri_factory_v2,
51
- rest_connector_v1,
52
- rest_connector_v2),
53
- 'service-instances' => Importers::ServiceInstanceImporter.new(
54
- service_instance_mapper,
55
- uri_factory_v1,
56
- uri_factory_v2,
57
- seiso,
58
- rest_connector_v1,
59
- rest_connector_v2)
60
- }
34
+ # Importer cache, since they're expensive to initialize
35
+ @importers = {}
61
36
  end
62
37
 
63
38
  # Imports a list of master files in order. Legal formats are 'json' (default) and 'yaml'.
39
+ # Returns true if all file imports succeeded and false otherwise.
64
40
  def import_files(files, format = 'json')
65
- loop do
66
- file = files.pop
67
- puts "Processing: #{file}"
68
- import_file(file, format)
69
- break if files.empty?
41
+ begin
42
+ all_failed = true
43
+ all_success = true
44
+ loop do
45
+ file = files.pop
46
+ success = import_file(file, format)
47
+ if success
48
+ all_failed = false
49
+ else
50
+ all_success = false
51
+ end
52
+ break if files.empty?
53
+ end
54
+ if all_success
55
+ return "success"
56
+ elsif all_failed
57
+ return "failed"
58
+ else
59
+ return "partial"
60
+ end
61
+ rescue Interrupt => e
62
+ @log.error "Import interrupted."
63
+ return "failed"
70
64
  end
71
65
  end
72
-
66
+
67
+ private
68
+
73
69
  # Imports a data master file. Legal formats are 'json' (default) and 'yaml'.
70
+ # Returns true if the import succeeded and false otherwise.
74
71
  def import_file(file, format = 'json')
75
- loader = @loaders[format]
76
- raise ArgumentError, "Illegal format: #{format}" if loader.nil?
77
- doc = loader.call(file)
78
- type = doc['type']
79
- validator = @validators[type]
80
- validator.validate(doc) unless validator.nil?
81
- (@importers[type] || @simple_importer).import doc
82
- end
72
+ @log.info "Importing file #{file}"
73
+ begin
74
+ # Include this in the begin/rescue as it can raise a JSON::ParserError
75
+ loader = @loaders[format]
83
76
 
84
- private
77
+ fail ArgumentError, "Illegal format: #{format}" if loader.nil?
85
78
 
86
- def uri_factory_v1(settings)
87
- host = settings['host']
88
- port = settings['port']
89
- use_ssl = settings['use_ssl'] || false
90
- scheme = use_ssl ? "https" : "http"
91
- base_uri = "#{scheme}://#{host}:#{port}/v1"
92
- Seiso::Connector::UriFactoryV1.new base_uri
93
- end
79
+ doc = loader.call file
80
+ type = doc['type']
81
+ get_importer(type).import doc
82
+ return true
94
83
 
95
- def uri_factory_v2(settings)
96
- host = settings['host']
97
- port = settings['port']
98
- use_ssl = settings['use_ssl'] || false
99
- scheme = use_ssl ? "https" : "http"
100
- base_uri = "#{scheme}://#{host}:#{port}/v2"
101
- Seiso::Connector::UriFactoryV2.new base_uri
84
+ # Rescue StandardError here, not Exception, since we want ctrl-c to stop the program.
85
+ rescue StandardError => e
86
+ @log.error "Failed to import file #{file}: #{e.message}"
87
+
88
+ # FIXME For now, just re-raise the exception, as we want to see where the bugs are.
89
+ return false
90
+ # raise e
91
+ end
102
92
  end
103
93
 
104
- def rest_connector(settings, media_type)
105
- host = settings['host']
106
- port = settings['port']
107
- use_ssl = settings['use_ssl'] || false
108
- ignore_cert = settings['ignore_cert'] || false
109
- username = settings['username']
110
- password = settings['password']
111
- Seiso::Connector::RestConnector.new(host, port, use_ssl, ignore_cert, username, password, media_type)
94
+ def get_importer(type)
95
+ # TODO Do a cleaner impl here--one that doesn't violate the open-closed principle. Note that we *don't* want to
96
+ # create these eagerly, as each one involves an expensive initialization.
97
+ importer = @importers[type]
98
+
99
+ if importer.nil?
100
+ importer = case type
101
+ when 'data-centers'
102
+ Importers::DataCenterImporter.new(@v3_api, @rest_util, @resolver)
103
+ when 'environments'
104
+ Importers::EnvironmentImporter.new(@v3_api, @rest_util, @resolver)
105
+ when 'health-statuses'
106
+ Importers::HealthStatusImporter.new(@v3_api, @rest_util, @resolver)
107
+ when 'infrastructure-providers'
108
+ Importers::InfrastructureProviderImporter.new(@v3_api, @rest_util, @resolver)
109
+ when 'load-balancers'
110
+ Importers::LoadBalancerImporter.new(@v3_api, @rest_util, @resolver)
111
+ when 'machines'
112
+ Importers::MachineImporter.new(@v3_api, @rest_util, @resolver)
113
+ when 'nodes'
114
+ Importers::NodeImporter.new(@v3_api, @rest_util, @resolver)
115
+ when 'regions'
116
+ Importers::RegionImporter.new(@v3_api, @rest_util, @resolver)
117
+ when 'rotation-statuses'
118
+ Importers::RotationStatusImporter.new(@v3_api, @rest_util, @resolver)
119
+ when 'services'
120
+ Importers::ServiceImporter.new(@v3_api, @rest_util, @resolver)
121
+ when 'service-groups'
122
+ Importers::ServiceGroupImporter.new(@v3_api, @rest_util, @resolver)
123
+ when 'service-instances'
124
+ Importers::ServiceInstanceImporter.new(@v3_api, @rest_util, @resolver)
125
+ when 'service-types'
126
+ Importers::ServiceTypeImporter.new(@v3_api, @rest_util, @resolver)
127
+ when 'status-types'
128
+ Importers::StatusTypeImporter.new(@v3_api, @rest_util, @resolver)
129
+ else
130
+ raise ArgumentError, "Illegal type: #{type}"
131
+ end
132
+ @importers['type'] = importer
133
+ end
134
+
135
+ importer
112
136
  end
113
137
  end
114
138
  end
@@ -0,0 +1,5 @@
1
+ module Seiso
2
+ class ImportMaster
3
+ module Importers; end
4
+ end
5
+ end
@@ -1,31 +1,92 @@
1
- module Seiso
2
- class ImportMaster
3
- module Importers
4
-
5
- # Author:: Willie Wheeler (mailto:wwheeler@expedia.com)
6
- # Copyright:: Copyright (c) 2014-2015 Expedia, Inc.
7
- # License:: Apache 2.0
8
- class BaseImporter
9
-
10
- # Enriches children with a link to the parent, detaches them from the parent, and returns
11
- # all detached children.
12
- #
13
- # - parents: List of items whose children we will detach.
14
- # - parent_prop: Name of the parent property on the children.
15
- # - parent_key: Name that Seiso expects for the parent key.
16
- # - child_prop: Name of the child property on the items.
17
- def detach_children(parents, parent_prop, parent_key, child_prop)
18
- all_children = []
19
- parents.each do |p|
20
- children = p[child_prop]
21
- next if children.nil?
22
- children.each { |c| c[parent_prop] = p[parent_key] }
23
- all_children.push(*children)
24
- p.delete child_prop
25
- end
26
- all_children
27
- end
28
- end
29
- end
30
- end
1
+ require 'hyper_resource'
2
+
3
+ class Seiso::ImportMaster
4
+
5
+ # Base importer for type-specific importers.
6
+ #
7
+ # Subclasses must set the @repo_resource variable to point to the type's repository resource.
8
+ #
9
+ # Additionally, subclasses must provide the following type-specific callback implementations:
10
+ #
11
+ # - to_search_params(doc_item, context) : Returns a hash containing HTTP params for the resource search.
12
+ # - find_resource(search_params) : Takes the search params above and returns the corresponding resource.
13
+ #
14
+ # If the type in question has child resources (e.g., service instances have IP address roles and service instance
15
+ # ports), then subclasses should provide the following callback implementations to handle them:
16
+ #
17
+ # - context_for(parent_resource, parent_context) : Returns a new context to support importing child resources.
18
+ # - import_children_of(doc_item, context) : Imports the child resources.
19
+ #
20
+ # See service_instance_importer.rb for an example that includes all four callbacks.
21
+ #
22
+ # Author:: Willie Wheeler
23
+ # Copyright:: Copyright (c) 2014-2016 Expedia, Inc.
24
+ # License:: Apache 2.0
25
+ class Importers::BaseImporter
26
+
27
+ # Imports a list of document items into Seiso.
28
+ #
29
+ # - items : Items in document format
30
+ # - context : Import context
31
+ def import_items(doc_items, context)
32
+ # TODO Perform orphan deletion here, centrally, and recursively?
33
+ doc_items.each { |doc_item| import_item(doc_item, context) }
34
+ end
35
+
36
+ # Imports a single document item into Seiso. Returns the imported item's URI
37
+ #
38
+ # - item : Item in document format
39
+ # - context : Import context
40
+ def import_item(doc_item, context)
41
+ data = mapper.map(doc_item, context)
42
+ search_params = to_search_params(doc_item, context)
43
+ begin
44
+ resource = find_resource search_params
45
+
46
+ # In the Seiso API, PUT will leave any unspecified associations alone. So for example we can handle node health
47
+ # status and rotation status like this. When we have node build version, though, we will have to do something
48
+ # else, like turn it into an association or else PATCH here. [WLW]
49
+ rest_util.put(resource, data, search_params)
50
+
51
+ rescue HyperResource::ClientError => e
52
+ status = e.response.status
53
+ body = e.response.body
54
+ fail "Client error: status=#{status}, body=#{body}" unless status == 404
55
+
56
+ # Seiso API returns the created resource in the response body, so grab that.
57
+ # TODO Clean up this implementation. E.g. don't used nested exception handling, and don't create the logger
58
+ # here. [WLW]
59
+ begin
60
+ resource = rest_util.post(repo_resource, data, search_params)
61
+ rescue HyperResource::ClientError => e2
62
+ # HTTP 409 is typical. It happens when client references nonexistent entities.
63
+ status2 = e2.response.status
64
+ body2 = e2.response.body
65
+ log = Util::Logger.new "BaseImporter"
66
+ log.warn "Client error: status=#{status2}, body=#{body2}. Failed to import item."
67
+ return
68
+ end
69
+ end
70
+
71
+ # ================================================================================================================
72
+ # FIXME For puts, this is putting the *old* resource on the context, which isn't what we want! [WLW]
73
+ # ================================================================================================================
74
+
75
+ if respond_to?(:import_children_of)
76
+ new_context = respond_to?(:context_for) ? context_for(resource, context) : {}
77
+ # FIXME For NodeIpAddress, this is returning a resource whose self link is /search/findByNodeNameAndIpAddress,
78
+ # and we can't PUT to that.
79
+ import_children_of(doc_item, new_context)
80
+ end
81
+
82
+ resource.href
83
+ end
84
+
85
+ protected
86
+
87
+ attr_accessor :api
88
+ attr_accessor :rest_util
89
+ attr_accessor :mapper
90
+ attr_accessor :repo_resource
91
+ end
31
92
  end