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 +4 -4
- data/.circleci/config.yml +9 -6
- data/.rubocop.yml +9 -1
- data/README.md +4 -2
- data/cloudkeeper.gemspec +10 -10
- data/config/cloudkeeper.yml +1 -0
- data/lib/cloudkeeper/backend_connector.rb +1 -1
- data/lib/cloudkeeper/cli.rb +15 -1
- data/lib/cloudkeeper/entities/appliance.rb +21 -14
- data/lib/cloudkeeper/entities/image_formats/ova.rb +10 -9
- data/lib/cloudkeeper/entities/image_list.rb +7 -13
- data/lib/cloudkeeper/managers/appliance_manager.rb +25 -53
- data/lib/cloudkeeper/managers/image_list_manager.rb +1 -1
- data/lib/cloudkeeper/utils.rb +2 -0
- data/lib/cloudkeeper/utils/appliance.rb +60 -0
- data/lib/cloudkeeper/utils/date.rb +11 -0
- data/lib/cloudkeeper/version.rb +1 -1
- metadata +76 -80
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4c6230319495e64370c48ade72d90d2b43ce0e5e
|
4
|
+
data.tar.gz: dfc5256e110fe7cd8098f64271ace0a193e7a607
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 430fd633d0d538fcfc488ed6d83b7ef3121de74fd0a5a3a90d43dca7309a34fe04e05a841a3cf1f4651a7e8d7262618444445823f50c96f9d2b875d373a59417
|
7
|
+
data.tar.gz: f38f6430d1c203d08c31298a2409acfe3ccb360dc967e2cbf40fffb6585f09b2d67c3e53c08ba5242b5f966df99e7dda1907b39cdb97acb89f0788cb9c9f9515
|
data/.circleci/config.yml
CHANGED
@@ -6,25 +6,28 @@ jobs:
|
|
6
6
|
- /.*/
|
7
7
|
docker:
|
8
8
|
- image: docker:stable
|
9
|
-
working_directory: /root/
|
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
|
23
|
-
docker push
|
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
|
27
|
-
docker push
|
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:
|
data/.rubocop.yml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require: rubocop-rspec
|
2
2
|
|
3
3
|
AllCops:
|
4
|
-
TargetRubyVersion: 2.
|
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/
|
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
|
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
|
data/cloudkeeper.gemspec
CHANGED
@@ -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 '
|
46
|
-
spec.add_runtime_dependency '
|
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 '
|
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 '
|
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
|
data/config/cloudkeeper.yml
CHANGED
@@ -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)
|
data/lib/cloudkeeper/cli.rb
CHANGED
@@ -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
|
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 =
|
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
|
-
|
21
|
-
has_ovf = has_vmdk = false
|
20
|
+
check_file_count! files
|
22
21
|
|
23
|
-
files.
|
24
|
-
|
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
|
-
|
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
|
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 <
|
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,
|
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
|
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
|
-
|
58
|
-
|
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,
|
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
|
-
|
4
|
+
include Cloudkeeper::Utils::Appliance
|
5
5
|
|
6
|
-
|
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
|
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
|
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
|
data/lib/cloudkeeper/utils.rb
CHANGED
@@ -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
|
data/lib/cloudkeeper/version.rb
CHANGED
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.
|
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-
|
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:
|
28
|
+
name: diffy
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
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: '
|
40
|
+
version: '3.1'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
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: '
|
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: '
|
74
|
+
version: '0.10'
|
55
75
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
76
|
+
name: rake
|
57
77
|
requirement: !ruby/object:Gem::Requirement
|
58
78
|
requirements:
|
59
79
|
- - "~>"
|
60
80
|
- !ruby/object:Gem::Version
|
61
|
-
version: '0
|
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
|
88
|
+
version: '12.0'
|
69
89
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
90
|
+
name: rspec
|
71
91
|
requirement: !ruby/object:Gem::Requirement
|
72
92
|
requirements:
|
73
93
|
- - "~>"
|
74
94
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
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: '
|
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:
|
118
|
+
name: rubocop
|
99
119
|
requirement: !ruby/object:Gem::Requirement
|
100
120
|
requirements:
|
101
121
|
- - "~>"
|
102
122
|
- !ruby/object:Gem::Version
|
103
|
-
version: '0.
|
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.
|
130
|
+
version: '0.48'
|
111
131
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
132
|
+
name: rubocop-rspec
|
113
133
|
requirement: !ruby/object:Gem::Requirement
|
114
134
|
requirements:
|
115
135
|
- - "~>"
|
116
136
|
- !ruby/object:Gem::Version
|
117
|
-
version: '
|
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: '
|
144
|
+
version: '1.15'
|
125
145
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
146
|
+
name: simplecov
|
127
147
|
requirement: !ruby/object:Gem::Requirement
|
128
148
|
requirements:
|
129
149
|
- - "~>"
|
130
150
|
- !ruby/object:Gem::Version
|
131
|
-
version: '
|
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: '
|
158
|
+
version: '0.12'
|
139
159
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
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:
|
174
|
+
name: webmock
|
155
175
|
requirement: !ruby/object:Gem::Requirement
|
156
176
|
requirements:
|
157
177
|
- - "~>"
|
158
178
|
- !ruby/object:Gem::Version
|
159
|
-
version: '3.
|
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.
|
186
|
+
version: '3.0'
|
167
187
|
- !ruby/object:Gem::Dependency
|
168
|
-
name:
|
188
|
+
name: activesupport
|
169
189
|
requirement: !ruby/object:Gem::Requirement
|
170
190
|
requirements:
|
171
191
|
- - ">="
|
172
192
|
- !ruby/object:Gem::Version
|
173
|
-
version: '
|
174
|
-
- - "
|
193
|
+
version: '4.0'
|
194
|
+
- - "<"
|
175
195
|
- !ruby/object:Gem::Version
|
176
|
-
version:
|
177
|
-
type: :
|
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: '
|
184
|
-
- - "
|
203
|
+
version: '4.0'
|
204
|
+
- - "<"
|
185
205
|
- !ruby/object:Gem::Version
|
186
|
-
version:
|
206
|
+
version: '6.0'
|
187
207
|
- !ruby/object:Gem::Dependency
|
188
|
-
name:
|
208
|
+
name: faraday
|
189
209
|
requirement: !ruby/object:Gem::Requirement
|
190
210
|
requirements:
|
191
211
|
- - "~>"
|
192
212
|
- !ruby/object:Gem::Version
|
193
|
-
version: '0.
|
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.
|
220
|
+
version: '0.11'
|
201
221
|
- !ruby/object:Gem::Dependency
|
202
|
-
name:
|
222
|
+
name: grpc
|
203
223
|
requirement: !ruby/object:Gem::Requirement
|
204
224
|
requirements:
|
205
225
|
- - "~>"
|
206
226
|
- !ruby/object:Gem::Version
|
207
|
-
version: '
|
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: '
|
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:
|
264
|
+
name: thor
|
265
265
|
requirement: !ruby/object:Gem::Requirement
|
266
266
|
requirements:
|
267
267
|
- - "~>"
|
268
268
|
- !ruby/object:Gem::Version
|
269
|
-
version: '0.
|
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.
|
276
|
+
version: '0.19'
|
277
277
|
- !ruby/object:Gem::Dependency
|
278
|
-
name:
|
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: '
|
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: '
|
290
|
+
version: '2.0'
|
297
291
|
- !ruby/object:Gem::Dependency
|
298
|
-
name:
|
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:
|
306
|
+
name: zaru
|
313
307
|
requirement: !ruby/object:Gem::Requirement
|
314
308
|
requirements:
|
315
309
|
- - "~>"
|
316
310
|
- !ruby/object:Gem::Version
|
317
|
-
version: '0.
|
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.
|
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.
|
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
|