seiso-import_master 0.0.12 → 3.0.0.M1

Sign up to get free protection for your applications and to get access to all the features.
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