cloudkeeper 1.5.1 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4e57ce8c830c735521f025d8f1c2840e6855439f
4
- data.tar.gz: 3b49c31527aaed3e26c1272908fd1ef653919889
3
+ metadata.gz: 4c6230319495e64370c48ade72d90d2b43ce0e5e
4
+ data.tar.gz: dfc5256e110fe7cd8098f64271ace0a193e7a607
5
5
  SHA512:
6
- metadata.gz: 0e311d61ee7aef14d2a519099ecb16f5c035b471bd7b2dd4f23385bbff21f617ab162c880855c57a7eadfed48a09b6744a0afd23ab0b51b18992a8eb3767e23d
7
- data.tar.gz: 400b857a285479f84cea18c0012b727846594f2758375d40dd5c1d9ca9b2fad096603ff13ef519fdca567de9827038d0ee6e661b90ae24fe9509584eee745940
6
+ metadata.gz: 430fd633d0d538fcfc488ed6d83b7ef3121de74fd0a5a3a90d43dca7309a34fe04e05a841a3cf1f4651a7e8d7262618444445823f50c96f9d2b875d373a59417
7
+ data.tar.gz: f38f6430d1c203d08c31298a2409acfe3ccb360dc967e2cbf40fffb6585f09b2d67c3e53c08ba5242b5f966df99e7dda1907b39cdb97acb89f0788cb9c9f9515
@@ -6,25 +6,28 @@ jobs:
6
6
  - /.*/
7
7
  docker:
8
8
  - image: docker:stable
9
- working_directory: /root/cloudkeeper
9
+ working_directory: /root/working_directory
10
10
  steps:
11
11
  - run: apk add --no-cache git openssh
12
12
  - checkout
13
13
  - setup_remote_docker
14
+ - run: |
15
+ git config --global --replace-all versionsort.prereleasesuffix ".alpha"
16
+ git config --global --add versionsort.prereleasesuffix ".beta"
14
17
  - run: |
15
18
  TAG=${CIRCLE_TAG#v}
16
19
  BRANCH=${TAG/%.*/.x}
17
20
  VERSION=${TAG}
18
- LATEST=$(git tag --sort=-refname | head -n 1)
21
+ LATEST=$(git tag --sort=-version:refname | head -n 1)
19
22
 
20
23
  docker login -u $DOCKER_USER -p $DOCKER_PASS
21
24
 
22
- docker build --build-arg branch=$BRANCH --build-arg version="$VERSION" -t cloudkeeper/cloudkeeper:$TAG .
23
- docker push cloudkeeper/cloudkeeper:$TAG
25
+ docker build --build-arg branch=$BRANCH --build-arg version="$VERSION" -t $DOCKERHUB_ORGANIZATION/$CIRCLE_PROJECT_REPONAME:$TAG ./$DOCKERFILE_DIR
26
+ docker push $DOCKERHUB_ORGANIZATION/$CIRCLE_PROJECT_REPONAME:$TAG
24
27
 
25
28
  if [ "$LATEST" == "$CIRCLE_TAG" ]; then
26
- docker tag cloudkeeper/cloudkeeper:$TAG cloudkeeper/cloudkeeper:latest
27
- docker push cloudkeeper/cloudkeeper:latest
29
+ docker tag $DOCKERHUB_ORGANIZATION/$CIRCLE_PROJECT_REPONAME:$TAG $DOCKERHUB_ORGANIZATION/$CIRCLE_PROJECT_REPONAME:latest
30
+ docker push $DOCKERHUB_ORGANIZATION/$CIRCLE_PROJECT_REPONAME:latest
28
31
  fi
29
32
  deployment:
30
33
  fake_deploy_for_cci2:
@@ -1,7 +1,7 @@
1
1
  require: rubocop-rspec
2
2
 
3
3
  AllCops:
4
- TargetRubyVersion: 2.0
4
+ TargetRubyVersion: 2.2
5
5
  Exclude:
6
6
  - 'vendor/**/*'
7
7
  - 'lib/cloudkeeper_grpc/*'
@@ -12,6 +12,14 @@ Metrics/LineLength:
12
12
  Style/Documentation:
13
13
  Enabled: false
14
14
 
15
+ Style/FormatStringToken:
16
+ Exclude:
17
+ - 'lib/cloudkeeper/utils/date.rb'
18
+
19
+ Style/MixinUsage:
20
+ Exclude:
21
+ - 'spec/cloudkeeper/entities/convertables/ova_spec.rb'
22
+
15
23
  Metrics/MethodLength:
16
24
  Max: 15
17
25
  Exclude:
data/README.md CHANGED
@@ -4,8 +4,9 @@ cloudkeeper is an AppDB <-> cloud synchronization utility
4
4
  [![Travis](https://img.shields.io/travis/the-cloudkeeper-project/cloudkeeper.svg?style=flat-square)](http://travis-ci.org/the-cloudkeeper-project/cloudkeeper)
5
5
  [![Gemnasium](https://img.shields.io/gemnasium/the-cloudkeeper-project/cloudkeeper.svg?style=flat-square)](https://gemnasium.com/the-cloudkeeper-project/cloudkeeper)
6
6
  [![Gem](https://img.shields.io/gem/v/cloudkeeper.svg?style=flat-square)](https://rubygems.org/gems/cloudkeeper)
7
- [![Code Climate](https://img.shields.io/codeclimate/github/the-cloudkeeper-project/cloudkeeper.svg?style=flat-square)](https://codeclimate.com/github/the-cloudkeeper-project/cloudkeeper)
7
+ [![Code Climate](https://img.shields.io/codeclimate/maintainability/the-cloudkeeper-project/cloudkeeper.svg?style=flat-square)](https://codeclimate.com/github/the-cloudkeeper-project/cloudkeeper)
8
8
  [![DockerHub](https://img.shields.io/badge/docker-ready-blue.svg?style=flat-square)](https://hub.docker.com/r/cloudkeeper/cloudkeeper/)
9
+ [![DOI](https://img.shields.io/badge/dynamic/json.svg?label=DOI&colorB=0D7EBE&prefix=&suffix=&query=$.doi&uri=https%3A%2F%2Fzenodo.org%2Fapi%2Frecords%2F891886&style=flat-square)](https://zenodo.org/record/891886)
9
10
 
10
11
  ## What does cloudkeeper do?
11
12
  cloudkeeper is able to read image lists provided by EGI AppDB, parse their content and decide what cloud appliances should be added, updated or removed from managed cloud. During the addition and update cloudkeeper is able to download an appliance's image and convert it to the format supported by the managed cloud.
@@ -74,7 +75,8 @@ Usage:
74
75
  cloudkeeper sync --backend-endpoint=BACKEND-ENDPOINT --external-tools-execution-timeout=N --formats=one two three --image-dir=IMAGE-DIR --image-lists=one two three --qemu-img-binary=QEMU-IMG-BINARY
75
76
 
76
77
  Options:
77
- --image-lists=one two three # List of image lists to sync against
78
+ [--image-lists=one two three] # List of image lists to sync against
79
+ [--image-lists-file=IMAGE-LISTS-FILE] # File containing list of image lists to sync against
78
80
  [--ca-dir=CA-DIR] # CA directory
79
81
  # Default: /etc/grid-security/certificates/
80
82
  [--authentication], [--no-authentication] # Client <-> server authentication
@@ -30,27 +30,27 @@ Gem::Specification.new do |spec|
30
30
  spec.require_paths = ['lib']
31
31
 
32
32
  spec.add_development_dependency 'bundler', '~> 1.13'
33
+ spec.add_development_dependency 'diffy', '~> 3.1'
34
+ spec.add_development_dependency 'grpc-tools', '>= 1.1', '<= 1.2.5'
35
+ spec.add_development_dependency 'pry', '~> 0.10'
33
36
  spec.add_development_dependency 'rake', '~> 12.0'
34
37
  spec.add_development_dependency 'rspec', '~> 3.5'
38
+ spec.add_development_dependency 'rspec-collection_matchers', '~> 1.1'
35
39
  spec.add_development_dependency 'rubocop', '~> 0.48'
36
40
  spec.add_development_dependency 'rubocop-rspec', '~> 1.15'
37
- spec.add_development_dependency 'rspec-collection_matchers', '~> 1.1'
38
41
  spec.add_development_dependency 'simplecov', '~> 0.12'
39
- spec.add_development_dependency 'pry', '~> 0.10'
40
42
  spec.add_development_dependency 'vcr', '~> 3.0'
41
43
  spec.add_development_dependency 'webmock', '~> 3.0'
42
- spec.add_development_dependency 'diffy', '~> 3.1'
43
- spec.add_development_dependency 'grpc-tools', '>= 1.1', '<= 1.2.5'
44
44
 
45
- spec.add_runtime_dependency 'thor', '~> 0.19'
46
- spec.add_runtime_dependency 'yell', '~> 2.0'
45
+ spec.add_runtime_dependency 'activesupport', '>= 4.0', '< 6.0'
46
+ spec.add_runtime_dependency 'faraday', '~> 0.11'
47
+ spec.add_runtime_dependency 'grpc', '~> 1.8'
47
48
  spec.add_runtime_dependency 'mixlib-shellout', '~> 2.2'
48
- spec.add_runtime_dependency 'grpc', '>= 1.1', '<= 1.2.5'
49
49
  spec.add_runtime_dependency 'settingslogic', '~> 2.0'
50
- spec.add_runtime_dependency 'zaru', '~> 0.1'
51
- spec.add_runtime_dependency 'activesupport', '>= 4.0', '< 6.0'
50
+ spec.add_runtime_dependency 'thor', '~> 0.19'
52
51
  spec.add_runtime_dependency 'tilt', '~> 2.0'
53
- spec.add_runtime_dependency 'faraday', '~> 0.11'
52
+ spec.add_runtime_dependency 'yell', '~> 2.0'
53
+ spec.add_runtime_dependency 'zaru', '~> 0.1'
54
54
 
55
55
  spec.required_ruby_version = '>= 2.2.0'
56
56
  end
@@ -1,5 +1,6 @@
1
1
  cloudkeeper:
2
2
  image-lists: # List of image lists to sync against
3
+ image-lists-file: # File containing list of image lists to sync against
3
4
  ca-dir: /etc/grid-security/certificates/ # CA directory
4
5
  authentication: false # core (client) <-> backend (server) authentication (certificate, key and backend-certificate options)
5
6
  certificate: /etc/grid-security/hostcert.pem # Core's host certificate
@@ -56,7 +56,7 @@ module Cloudkeeper
56
56
  grpc_client.appliances(
57
57
  CloudkeeperGrpc::ImageListIdentifier.new(image_list_identifier: image_list_identifier),
58
58
  return_op: true
59
- )
59
+ ), raise_exception: true
60
60
  ) do |response|
61
61
  response.inject({}) do |acc, elem|
62
62
  image = convert_image_proto(elem.image)
@@ -23,10 +23,13 @@ module Cloudkeeper
23
23
  desc: 'Runs cloudkeeper in debug mode'
24
24
 
25
25
  method_option :'image-lists',
26
- required: true,
27
26
  default: Cloudkeeper::Settings['image-lists'],
28
27
  type: :array,
29
28
  desc: 'List of image lists to sync against'
29
+ method_option :'image-lists-file',
30
+ default: Cloudkeeper::Settings['image-lists-file'],
31
+ type: :string,
32
+ desc: 'File containing list of image lists to sync against'
30
33
  method_option :'ca-dir',
31
34
  required: false,
32
35
  default: Cloudkeeper::Settings['ca-dir'],
@@ -168,6 +171,7 @@ module Cloudkeeper
168
171
  validate_configuration_group! %i[remote-mode nginx-proxy-ip-address],
169
172
  %i[nginx-proxy-port],
170
173
  'NGINX proxy configuration missing'
174
+ validate_contradictory_options! %i[image-lists image-lists-file], one_required: true
171
175
  end
172
176
 
173
177
  def validate_configuration_group!(flags, required_options, error_message)
@@ -176,6 +180,16 @@ module Cloudkeeper
176
180
  raise Cloudkeeper::Errors::InvalidConfigurationError, error_message unless all_options_available(required_options)
177
181
  end
178
182
 
183
+ def validate_contradictory_options!(flags, options)
184
+ filled = flags.select { |flag| Cloudkeeper::Settings[flag] }
185
+ if filled.count > 1
186
+ raise Cloudkeeper::Errors::InvalidConfigurationError, "Following options cannot be used together: #{filled.join(', ')}"
187
+ end
188
+
189
+ return unless options[:one_required]
190
+ raise Cloudkeeper::Errors::InvalidConfigurationError, "One of the options #{flags.join(', ')} required" if filled.empty?
191
+ end
192
+
179
193
  def all_options_available(required_options)
180
194
  required_options.reduce(true) { |acc, elem| Cloudkeeper::Settings[elem] && acc }
181
195
  end
@@ -4,7 +4,7 @@ module Cloudkeeper
4
4
  attr_accessor :identifier, :description, :mpuri, :title, :group, :ram, :core, :version, :architecture
5
5
  attr_accessor :operating_system, :image, :attributes, :vo, :expiration_date, :image_list_identifier
6
6
 
7
- REJECTED_ATTRIBUTES = %i[vo expiration image_list_identifier].freeze
7
+ REJECTED_ATTRIBUTES = %i[vo image_list_identifier].freeze
8
8
 
9
9
  def initialize(identifier, mpuri, vo, expiration_date, image_list_identifier, title = '', description = '', group = '',
10
10
  ram = 1024, core = 1, version = '', architecture = '', operating_system = '', image = nil, attributes = {})
@@ -34,6 +34,10 @@ module Cloudkeeper
34
34
  @image_list_identifier = image_list_identifier
35
35
  end
36
36
 
37
+ def expired?
38
+ expiration_date < Time.now
39
+ end
40
+
37
41
  class << self
38
42
  def from_hash(appliance_hash)
39
43
  appliance_hash.deep_symbolize_keys!
@@ -46,19 +50,7 @@ module Cloudkeeper
46
50
  def populate_appliance(appliance_hash)
47
51
  raise Cloudkeeper::Errors::Parsing::InvalidApplianceHashError, 'invalid appliance hash' if appliance_hash.blank?
48
52
 
49
- appliance = Appliance.new appliance_hash[:'dc:identifier'],
50
- appliance_hash[:'ad:mpuri'],
51
- appliance_hash[:vo],
52
- appliance_hash[:expiration],
53
- appliance_hash[:image_list_identifier],
54
- appliance_hash[:'dc:title'],
55
- appliance_hash[:'dc:description'],
56
- appliance_hash[:'ad:group'],
57
- appliance_hash[:'hv:ram_minimum'],
58
- appliance_hash[:'hv:core_minimum'],
59
- appliance_hash[:'hv:version'],
60
- appliance_hash[:'sl:arch']
61
-
53
+ appliance = construct_appliance(appliance_hash)
62
54
  construct_os_name!(appliance, appliance_hash)
63
55
  populate_attributes!(appliance, appliance_hash)
64
56
 
@@ -68,6 +60,21 @@ module Cloudkeeper
68
60
  "doesn't contain all the necessary data"
69
61
  end
70
62
 
63
+ def construct_appliance(appliance_hash)
64
+ Appliance.new appliance_hash[:'dc:identifier'],
65
+ appliance_hash[:'ad:mpuri'],
66
+ appliance_hash[:vo],
67
+ Cloudkeeper::Utils::Date.parse(appliance_hash[:'dc:date:expires']),
68
+ appliance_hash[:image_list_identifier],
69
+ appliance_hash[:'dc:title'],
70
+ appliance_hash[:'dc:description'],
71
+ appliance_hash[:'ad:group'],
72
+ appliance_hash[:'hv:ram_minimum'],
73
+ appliance_hash[:'hv:core_minimum'],
74
+ appliance_hash[:'hv:version'],
75
+ appliance_hash[:'sl:arch']
76
+ end
77
+
71
78
  def construct_os_name!(appliance, appliance_hash)
72
79
  appliance.operating_system = appliance_hash[:'sl:os'].to_s
73
80
  appliance.operating_system = "#{appliance.operating_system} #{appliance_hash[:'sl:osname']}".strip
@@ -17,19 +17,20 @@ module Cloudkeeper
17
17
  end
18
18
 
19
19
  def ova_structure?(files)
20
- check_count! files
21
- has_ovf = has_vmdk = false
20
+ check_file_count! files
22
21
 
23
- files.each do |file|
24
- has_ovf ||= OVF_REGEX =~ file
25
- has_vmdk ||= VMDK_REGEX =~ file
26
- break if has_ovf && has_vmdk
27
- end
22
+ vmdk_count = files.select { |file| VMDK_REGEX =~ file }.count
23
+ ovf_count = files.select { |file| OVF_REGEX =~ file }.count
28
24
 
29
- has_ovf && has_vmdk
25
+ raise Cloudkeeper::Errors::Image::Format::Ova::InvalidArchiveError, 'Archive contains multiple drives (VMDK files)' \
26
+ if vmdk_count > 1
27
+ raise Cloudkeeper::Errors::Image::Format::Ova::InvalidArchiveError, 'Archive contains multiple descriptors (OVF files)' \
28
+ if ovf_count > 1
29
+
30
+ vmdk_count == 1 && ovf_count == 1
30
31
  end
31
32
 
32
- def check_count!(files)
33
+ def check_file_count!(files)
33
34
  return unless files.count > ARCHIVE_MAX_FILES
34
35
 
35
36
  raise Cloudkeeper::Errors::Image::Format::Ova::InvalidArchiveError, "Too many files in archive: #{files.count}. "\
@@ -5,8 +5,6 @@ module Cloudkeeper
5
5
  class ImageList
6
6
  attr_accessor :identifier, :creation_date, :expiration_date, :description, :title, :source, :appliances
7
7
 
8
- DATE_FORMAT = '%Y-%m-%dT%H:%M:%SZ'.freeze
9
-
10
8
  def initialize(identifier, expiration_date, creation_date = nil, source = '', title = '', description = '', appliances = {})
11
9
  raise Cloudkeeper::Errors::ArgumentError, 'identifier cannot be nil nor empty' if identifier.blank? || expiration_date.blank?
12
10
 
@@ -26,7 +24,7 @@ module Cloudkeeper
26
24
  end
27
25
 
28
26
  def expired?
29
- expiration_date < DateTime.now
27
+ expiration_date < Time.now
30
28
  end
31
29
 
32
30
  class << self
@@ -40,11 +38,12 @@ module Cloudkeeper
40
38
  image_list
41
39
  end
42
40
 
43
- def prepare_appliance_hash(image_hash, endorser, expiration, vo, image_list_identifier)
41
+ def prepare_appliance_hash(image_hash, endorser, vo, image_list_identifier)
44
42
  appliance_hash = {}
45
43
 
46
44
  appliance_hash = image_hash[:'hv:image'] if image_hash && image_hash.key?(:'hv:image')
47
- appliance_hash.merge!(vo: vo, expiration: expiration, image_list_identifier: image_list_identifier)
45
+ appliance_hash[:vo] = vo
46
+ appliance_hash[:image_list_identifier] = image_list_identifier
48
47
  appliance_hash.merge!(endorser[:'hv:x509']) if endorser && endorser.key?(:'hv:x509')
49
48
 
50
49
  appliance_hash
@@ -54,8 +53,8 @@ module Cloudkeeper
54
53
  raise Cloudkeeper::Errors::Parsing::InvalidImageListHashError, 'invalid image list hash' if image_list_hash.blank?
55
54
 
56
55
  ImageList.new image_list_hash[:'dc:identifier'],
57
- parse_date(image_list_hash[:'dc:date:expires']),
58
- parse_date(image_list_hash[:'dc:date:created']),
56
+ Cloudkeeper::Utils::Date.parse(image_list_hash[:'dc:date:expires']),
57
+ Cloudkeeper::Utils::Date.parse(image_list_hash[:'dc:date:created']),
59
58
  image_list_hash[:'dc:source'],
60
59
  image_list_hash[:'dc:title'],
61
60
  image_list_hash[:'dc:description']
@@ -64,17 +63,12 @@ module Cloudkeeper
64
63
  "doesn't contain all the necessary data"
65
64
  end
66
65
 
67
- def parse_date(date)
68
- date.blank? ? '' : DateTime.strptime(date, DATE_FORMAT)
69
- end
70
-
71
66
  def populate_appliances!(image_list, image_list_hash)
72
- expiration = parse_date(image_list_hash[:'dc:date:expires'])
73
67
  vo = image_list_hash[:'ad:vo']
74
68
  endorser = image_list_hash[:'hv:endorser']
75
69
 
76
70
  image_list_hash[:'hv:images'].each do |image_hash|
77
- appliance = Appliance.from_hash(prepare_appliance_hash(image_hash, endorser, expiration, vo, image_list.identifier))
71
+ appliance = Appliance.from_hash(prepare_appliance_hash(image_hash, endorser, vo, image_list.identifier))
78
72
  image_list.add_appliance appliance
79
73
  end
80
74
  end
@@ -1,9 +1,9 @@
1
1
  module Cloudkeeper
2
2
  module Managers
3
3
  class ApplianceManager
4
- attr_reader :backend_connector, :image_list_manager, :acceptable_formats
4
+ include Cloudkeeper::Utils::Appliance
5
5
 
6
- IMAGE_UPDATE_ATTRIBUTES = ['hv:version', 'sl:checksum:sha512', 'hv:size'].freeze
6
+ attr_reader :backend_connector, :image_list_manager, :acceptable_formats
7
7
 
8
8
  def initialize
9
9
  @backend_connector = Cloudkeeper::BackendConnector.new
@@ -41,7 +41,14 @@ module Cloudkeeper
41
41
  add_list = image_list_manager.image_lists.keys - backend_image_lists
42
42
  logger.debug "Image lists to register: #{add_list.inspect}"
43
43
  add_list.each do |image_list_identifier|
44
- image_list_manager.image_lists[image_list_identifier].appliances.each_value { |appliance| add_appliance appliance }
44
+ image_list_manager.image_lists[image_list_identifier].appliances.each_value do |appliance|
45
+ if appliance.expired?
46
+ log_expired appliance, 'Skipping expired appliance'
47
+ next
48
+ end
49
+
50
+ add_appliance appliance
51
+ end
45
52
  end
46
53
  end
47
54
 
@@ -73,7 +80,15 @@ module Cloudkeeper
73
80
  logger.debug 'Registering new appliances...'
74
81
  add_list = image_list_appliances.keys - backend_appliances.keys
75
82
  logger.debug "Appliances to register: #{add_list.inspect}"
76
- add_list.each { |appliance_identifier| add_appliance image_list_appliances[appliance_identifier] }
83
+ add_list.each do |appliance_identifier|
84
+ appliance = image_list_appliances[appliance_identifier]
85
+ if appliance.expired?
86
+ log_expired appliance, 'Skipping expired appliance'
87
+ next
88
+ end
89
+
90
+ add_appliance appliance
91
+ end
77
92
  end
78
93
 
79
94
  def update_appliances(backend_appliances, image_list_appliances)
@@ -84,40 +99,18 @@ module Cloudkeeper
84
99
  image_list_appliance = image_list_appliances[appliance_identifier]
85
100
  backend_appliance = backend_appliances[appliance_identifier]
86
101
 
102
+ if image_list_appliance.expired?
103
+ log_expired image_list_appliance, 'Removing expired appliance'
104
+ backend_connector.remove_appliance image_list_appliance
105
+ next
106
+ end
107
+
87
108
  image_update = update_image?(image_list_appliance, backend_appliance)
88
109
  image_list_appliance.image = nil unless image_update
89
110
  update_appliance image_list_appliance if image_update || update_metadata?(image_list_appliance, backend_appliance)
90
111
  end
91
112
  end
92
113
 
93
- def clean_image_files(appliance)
94
- return unless appliance && appliance.image
95
-
96
- logger.debug "Cleaning downloaded image files for appliance #{appliance.identifier.inspect}"
97
- appliance.image.image_files.each { |image_file| clean_image_file image_file.file }
98
- rescue ::IOError => ex
99
- logger.warn "Appliance cleanup error: #{ex.message}"
100
- end
101
-
102
- def clean_image_file(filename)
103
- File.delete(filename) if File.exist?(filename)
104
- end
105
-
106
- def update_image?(image_list_appliance, backend_appliance)
107
- image_list_attributes = image_list_appliance.attributes
108
- backend_attributes = backend_appliance.attributes
109
-
110
- IMAGE_UPDATE_ATTRIBUTES.reduce(false) { |red, elem| red || (image_list_attributes[elem] != backend_attributes[elem]) }
111
- end
112
-
113
- def update_metadata?(image_list_appliance, backend_appliance)
114
- image_list_appliance.attributes != backend_appliance.attributes
115
- end
116
-
117
- def update_appliance(appliance)
118
- modify_appliance :update_appliance, appliance
119
- end
120
-
121
114
  def add_appliance(appliance)
122
115
  modify_appliance :add_appliance, appliance
123
116
  end
@@ -132,27 +125,6 @@ module Cloudkeeper
132
125
  ensure
133
126
  clean_image_files appliance
134
127
  end
135
-
136
- def prepare_image!(appliance)
137
- image_file = Cloudkeeper::Managers::ImageManager.download_image(appliance.image.uri)
138
- appliance.image.add_image_file image_file
139
- return if acceptable_formats.include? image_file.format
140
-
141
- convert_image! appliance, image_file
142
- end
143
-
144
- def convert_image!(appliance, image_file)
145
- format = acceptable_formats.find { |acceptable_format| image_file.respond_to? "to_#{acceptable_format}".to_sym }
146
- unless format
147
- raise Cloudkeeper::Errors::Image::Format::NoRequiredFormatAvailableError,
148
- "image #{image.inspect} cannot be converted to any acceptable format"
149
- end
150
-
151
- appliance.image.add_image_file image_file.send("to_#{format}".to_sym)
152
- rescue Cloudkeeper::Errors::Image::Format::NoRequiredFormatAvailableError, Cloudkeeper::Errors::CommandExecutionError,
153
- Cloudkeeper::Errors::ArgumentError, ::IOError => ex
154
- raise Cloudkeeper::Errors::Image::ConversionError, ex
155
- end
156
128
  end
157
129
  end
158
130
  end
@@ -19,7 +19,7 @@ module Cloudkeeper
19
19
  def download_image_lists
20
20
  logger.debug 'Downloading fresh image lists...'
21
21
  Dir.mktmpdir('cloudkeeper') do |dir|
22
- urls = Cloudkeeper::Settings[:'image-lists']
22
+ urls = Cloudkeeper::Settings[:'image-lists'] || File.read(Cloudkeeper::Settings[:'image-lists-file']).split("\n")
23
23
  retrieve_image_lists urls, dir
24
24
  end
25
25
  end
@@ -3,5 +3,7 @@ module Cloudkeeper
3
3
  autoload :Hash, 'cloudkeeper/utils/hash'
4
4
  autoload :Checksum, 'cloudkeeper/utils/checksum'
5
5
  autoload :URL, 'cloudkeeper/utils/url'
6
+ autoload :Appliance, 'cloudkeeper/utils/appliance'
7
+ autoload :Date, 'cloudkeeper/utils/date'
6
8
  end
7
9
  end
@@ -0,0 +1,60 @@
1
+ module Cloudkeeper
2
+ module Utils
3
+ module Appliance
4
+ IMAGE_UPDATE_ATTRIBUTES = ['hv:version', 'sl:checksum:sha512', 'hv:size'].freeze
5
+
6
+ def log_expired(appliance, message)
7
+ logger.info "#{message} #{appliance.identifier.inspect}"
8
+ end
9
+
10
+ def clean_image_files(appliance)
11
+ return unless appliance && appliance.image
12
+
13
+ logger.debug "Cleaning downloaded image files for appliance #{appliance.identifier.inspect}"
14
+ appliance.image.image_files.each { |image_file| clean_image_file image_file.file }
15
+ rescue ::IOError => ex
16
+ logger.warn "Appliance cleanup error: #{ex.message}"
17
+ end
18
+
19
+ def clean_image_file(filename)
20
+ File.delete(filename) if File.exist?(filename)
21
+ end
22
+
23
+ def update_image?(image_list_appliance, backend_appliance)
24
+ image_list_attributes = image_list_appliance.attributes
25
+ backend_attributes = backend_appliance.attributes
26
+
27
+ IMAGE_UPDATE_ATTRIBUTES.reduce(false) { |red, elem| red || (image_list_attributes[elem] != backend_attributes[elem]) }
28
+ end
29
+
30
+ def update_metadata?(image_list_appliance, backend_appliance)
31
+ image_list_appliance.attributes != backend_appliance.attributes
32
+ end
33
+
34
+ def update_appliance(appliance)
35
+ modify_appliance :update_appliance, appliance
36
+ end
37
+
38
+ def prepare_image!(appliance)
39
+ image_file = Cloudkeeper::Managers::ImageManager.download_image(appliance.image.uri)
40
+ appliance.image.add_image_file image_file
41
+ return if acceptable_formats.include? image_file.format
42
+
43
+ convert_image! appliance, image_file
44
+ end
45
+
46
+ def convert_image!(appliance, image_file)
47
+ format = acceptable_formats.find { |acceptable_format| image_file.respond_to? "to_#{acceptable_format}".to_sym }
48
+ unless format
49
+ raise Cloudkeeper::Errors::Image::Format::NoRequiredFormatAvailableError,
50
+ "image #{image.inspect} cannot be converted to any acceptable format"
51
+ end
52
+
53
+ appliance.image.add_image_file image_file.send("to_#{format}".to_sym)
54
+ rescue Cloudkeeper::Errors::Image::Format::NoRequiredFormatAvailableError, Cloudkeeper::Errors::CommandExecutionError,
55
+ Cloudkeeper::Errors::ArgumentError, ::IOError => ex
56
+ raise Cloudkeeper::Errors::Image::ConversionError, ex
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,11 @@
1
+ module Cloudkeeper
2
+ module Utils
3
+ class Date
4
+ DATE_FORMAT = '%Y-%m-%dT%H:%M:%SZ'.freeze
5
+
6
+ def self.parse(date)
7
+ date.blank? ? '' : Time.strptime(date, DATE_FORMAT)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,3 +1,3 @@
1
1
  module Cloudkeeper
2
- VERSION = '1.5.1'.freeze
2
+ VERSION = '1.6.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudkeeper
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.1
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michal Kimle
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-09-15 00:00:00.000000000 Z
11
+ date: 2017-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -25,61 +25,81 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.13'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: diffy
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '12.0'
33
+ version: '3.1'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '12.0'
40
+ version: '3.1'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rspec
42
+ name: grpc-tools
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '1.1'
48
+ - - "<="
49
+ - !ruby/object:Gem::Version
50
+ version: 1.2.5
51
+ type: :development
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '1.1'
58
+ - - "<="
59
+ - !ruby/object:Gem::Version
60
+ version: 1.2.5
61
+ - !ruby/object:Gem::Dependency
62
+ name: pry
43
63
  requirement: !ruby/object:Gem::Requirement
44
64
  requirements:
45
65
  - - "~>"
46
66
  - !ruby/object:Gem::Version
47
- version: '3.5'
67
+ version: '0.10'
48
68
  type: :development
49
69
  prerelease: false
50
70
  version_requirements: !ruby/object:Gem::Requirement
51
71
  requirements:
52
72
  - - "~>"
53
73
  - !ruby/object:Gem::Version
54
- version: '3.5'
74
+ version: '0.10'
55
75
  - !ruby/object:Gem::Dependency
56
- name: rubocop
76
+ name: rake
57
77
  requirement: !ruby/object:Gem::Requirement
58
78
  requirements:
59
79
  - - "~>"
60
80
  - !ruby/object:Gem::Version
61
- version: '0.48'
81
+ version: '12.0'
62
82
  type: :development
63
83
  prerelease: false
64
84
  version_requirements: !ruby/object:Gem::Requirement
65
85
  requirements:
66
86
  - - "~>"
67
87
  - !ruby/object:Gem::Version
68
- version: '0.48'
88
+ version: '12.0'
69
89
  - !ruby/object:Gem::Dependency
70
- name: rubocop-rspec
90
+ name: rspec
71
91
  requirement: !ruby/object:Gem::Requirement
72
92
  requirements:
73
93
  - - "~>"
74
94
  - !ruby/object:Gem::Version
75
- version: '1.15'
95
+ version: '3.5'
76
96
  type: :development
77
97
  prerelease: false
78
98
  version_requirements: !ruby/object:Gem::Requirement
79
99
  requirements:
80
100
  - - "~>"
81
101
  - !ruby/object:Gem::Version
82
- version: '1.15'
102
+ version: '3.5'
83
103
  - !ruby/object:Gem::Dependency
84
104
  name: rspec-collection_matchers
85
105
  requirement: !ruby/object:Gem::Requirement
@@ -95,49 +115,49 @@ dependencies:
95
115
  - !ruby/object:Gem::Version
96
116
  version: '1.1'
97
117
  - !ruby/object:Gem::Dependency
98
- name: simplecov
118
+ name: rubocop
99
119
  requirement: !ruby/object:Gem::Requirement
100
120
  requirements:
101
121
  - - "~>"
102
122
  - !ruby/object:Gem::Version
103
- version: '0.12'
123
+ version: '0.48'
104
124
  type: :development
105
125
  prerelease: false
106
126
  version_requirements: !ruby/object:Gem::Requirement
107
127
  requirements:
108
128
  - - "~>"
109
129
  - !ruby/object:Gem::Version
110
- version: '0.12'
130
+ version: '0.48'
111
131
  - !ruby/object:Gem::Dependency
112
- name: pry
132
+ name: rubocop-rspec
113
133
  requirement: !ruby/object:Gem::Requirement
114
134
  requirements:
115
135
  - - "~>"
116
136
  - !ruby/object:Gem::Version
117
- version: '0.10'
137
+ version: '1.15'
118
138
  type: :development
119
139
  prerelease: false
120
140
  version_requirements: !ruby/object:Gem::Requirement
121
141
  requirements:
122
142
  - - "~>"
123
143
  - !ruby/object:Gem::Version
124
- version: '0.10'
144
+ version: '1.15'
125
145
  - !ruby/object:Gem::Dependency
126
- name: vcr
146
+ name: simplecov
127
147
  requirement: !ruby/object:Gem::Requirement
128
148
  requirements:
129
149
  - - "~>"
130
150
  - !ruby/object:Gem::Version
131
- version: '3.0'
151
+ version: '0.12'
132
152
  type: :development
133
153
  prerelease: false
134
154
  version_requirements: !ruby/object:Gem::Requirement
135
155
  requirements:
136
156
  - - "~>"
137
157
  - !ruby/object:Gem::Version
138
- version: '3.0'
158
+ version: '0.12'
139
159
  - !ruby/object:Gem::Dependency
140
- name: webmock
160
+ name: vcr
141
161
  requirement: !ruby/object:Gem::Requirement
142
162
  requirements:
143
163
  - - "~>"
@@ -151,67 +171,67 @@ dependencies:
151
171
  - !ruby/object:Gem::Version
152
172
  version: '3.0'
153
173
  - !ruby/object:Gem::Dependency
154
- name: diffy
174
+ name: webmock
155
175
  requirement: !ruby/object:Gem::Requirement
156
176
  requirements:
157
177
  - - "~>"
158
178
  - !ruby/object:Gem::Version
159
- version: '3.1'
179
+ version: '3.0'
160
180
  type: :development
161
181
  prerelease: false
162
182
  version_requirements: !ruby/object:Gem::Requirement
163
183
  requirements:
164
184
  - - "~>"
165
185
  - !ruby/object:Gem::Version
166
- version: '3.1'
186
+ version: '3.0'
167
187
  - !ruby/object:Gem::Dependency
168
- name: grpc-tools
188
+ name: activesupport
169
189
  requirement: !ruby/object:Gem::Requirement
170
190
  requirements:
171
191
  - - ">="
172
192
  - !ruby/object:Gem::Version
173
- version: '1.1'
174
- - - "<="
193
+ version: '4.0'
194
+ - - "<"
175
195
  - !ruby/object:Gem::Version
176
- version: 1.2.5
177
- type: :development
196
+ version: '6.0'
197
+ type: :runtime
178
198
  prerelease: false
179
199
  version_requirements: !ruby/object:Gem::Requirement
180
200
  requirements:
181
201
  - - ">="
182
202
  - !ruby/object:Gem::Version
183
- version: '1.1'
184
- - - "<="
203
+ version: '4.0'
204
+ - - "<"
185
205
  - !ruby/object:Gem::Version
186
- version: 1.2.5
206
+ version: '6.0'
187
207
  - !ruby/object:Gem::Dependency
188
- name: thor
208
+ name: faraday
189
209
  requirement: !ruby/object:Gem::Requirement
190
210
  requirements:
191
211
  - - "~>"
192
212
  - !ruby/object:Gem::Version
193
- version: '0.19'
213
+ version: '0.11'
194
214
  type: :runtime
195
215
  prerelease: false
196
216
  version_requirements: !ruby/object:Gem::Requirement
197
217
  requirements:
198
218
  - - "~>"
199
219
  - !ruby/object:Gem::Version
200
- version: '0.19'
220
+ version: '0.11'
201
221
  - !ruby/object:Gem::Dependency
202
- name: yell
222
+ name: grpc
203
223
  requirement: !ruby/object:Gem::Requirement
204
224
  requirements:
205
225
  - - "~>"
206
226
  - !ruby/object:Gem::Version
207
- version: '2.0'
227
+ version: '1.8'
208
228
  type: :runtime
209
229
  prerelease: false
210
230
  version_requirements: !ruby/object:Gem::Requirement
211
231
  requirements:
212
232
  - - "~>"
213
233
  - !ruby/object:Gem::Version
214
- version: '2.0'
234
+ version: '1.8'
215
235
  - !ruby/object:Gem::Dependency
216
236
  name: mixlib-shellout
217
237
  requirement: !ruby/object:Gem::Requirement
@@ -226,26 +246,6 @@ dependencies:
226
246
  - - "~>"
227
247
  - !ruby/object:Gem::Version
228
248
  version: '2.2'
229
- - !ruby/object:Gem::Dependency
230
- name: grpc
231
- requirement: !ruby/object:Gem::Requirement
232
- requirements:
233
- - - ">="
234
- - !ruby/object:Gem::Version
235
- version: '1.1'
236
- - - "<="
237
- - !ruby/object:Gem::Version
238
- version: 1.2.5
239
- type: :runtime
240
- prerelease: false
241
- version_requirements: !ruby/object:Gem::Requirement
242
- requirements:
243
- - - ">="
244
- - !ruby/object:Gem::Version
245
- version: '1.1'
246
- - - "<="
247
- - !ruby/object:Gem::Version
248
- version: 1.2.5
249
249
  - !ruby/object:Gem::Dependency
250
250
  name: settingslogic
251
251
  requirement: !ruby/object:Gem::Requirement
@@ -261,41 +261,35 @@ dependencies:
261
261
  - !ruby/object:Gem::Version
262
262
  version: '2.0'
263
263
  - !ruby/object:Gem::Dependency
264
- name: zaru
264
+ name: thor
265
265
  requirement: !ruby/object:Gem::Requirement
266
266
  requirements:
267
267
  - - "~>"
268
268
  - !ruby/object:Gem::Version
269
- version: '0.1'
269
+ version: '0.19'
270
270
  type: :runtime
271
271
  prerelease: false
272
272
  version_requirements: !ruby/object:Gem::Requirement
273
273
  requirements:
274
274
  - - "~>"
275
275
  - !ruby/object:Gem::Version
276
- version: '0.1'
276
+ version: '0.19'
277
277
  - !ruby/object:Gem::Dependency
278
- name: activesupport
278
+ name: tilt
279
279
  requirement: !ruby/object:Gem::Requirement
280
280
  requirements:
281
- - - ">="
282
- - !ruby/object:Gem::Version
283
- version: '4.0'
284
- - - "<"
281
+ - - "~>"
285
282
  - !ruby/object:Gem::Version
286
- version: '6.0'
283
+ version: '2.0'
287
284
  type: :runtime
288
285
  prerelease: false
289
286
  version_requirements: !ruby/object:Gem::Requirement
290
287
  requirements:
291
- - - ">="
292
- - !ruby/object:Gem::Version
293
- version: '4.0'
294
- - - "<"
288
+ - - "~>"
295
289
  - !ruby/object:Gem::Version
296
- version: '6.0'
290
+ version: '2.0'
297
291
  - !ruby/object:Gem::Dependency
298
- name: tilt
292
+ name: yell
299
293
  requirement: !ruby/object:Gem::Requirement
300
294
  requirements:
301
295
  - - "~>"
@@ -309,19 +303,19 @@ dependencies:
309
303
  - !ruby/object:Gem::Version
310
304
  version: '2.0'
311
305
  - !ruby/object:Gem::Dependency
312
- name: faraday
306
+ name: zaru
313
307
  requirement: !ruby/object:Gem::Requirement
314
308
  requirements:
315
309
  - - "~>"
316
310
  - !ruby/object:Gem::Version
317
- version: '0.11'
311
+ version: '0.1'
318
312
  type: :runtime
319
313
  prerelease: false
320
314
  version_requirements: !ruby/object:Gem::Requirement
321
315
  requirements:
322
316
  - - "~>"
323
317
  - !ruby/object:Gem::Version
324
- version: '0.11'
318
+ version: '0.1'
325
319
  description: Synchronize cloud appliances between AppDB and cloud platforms
326
320
  email:
327
321
  - kimle.michal@gmail.com
@@ -406,7 +400,9 @@ files:
406
400
  - lib/cloudkeeper/nginx/templates/nginx.conf.erb
407
401
  - lib/cloudkeeper/settings.rb
408
402
  - lib/cloudkeeper/utils.rb
403
+ - lib/cloudkeeper/utils/appliance.rb
409
404
  - lib/cloudkeeper/utils/checksum.rb
405
+ - lib/cloudkeeper/utils/date.rb
410
406
  - lib/cloudkeeper/utils/hash.rb
411
407
  - lib/cloudkeeper/utils/url.rb
412
408
  - lib/cloudkeeper/version.rb
@@ -446,7 +442,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
446
442
  version: '0'
447
443
  requirements: []
448
444
  rubyforge_project:
449
- rubygems_version: 2.6.13
445
+ rubygems_version: 2.6.14
450
446
  signing_key:
451
447
  specification_version: 4
452
448
  summary: Synchronize cloud appliances between AppDB and cloud platforms