chefrepo-manifest-builder 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,166 @@
1
+ require 'aws-sdk-core'
2
+ require 'aws-sdk-resources'
3
+ require 'uri'
4
+
5
+ # ---------------------------------------------------------------------------------------------------------------
6
+ def getS3()
7
+ region = ENV['AWS_REGION'] || ::Aws.config[:region] || 'us-east-1'
8
+ unless @s3
9
+ # noinspection RubyArgCount
10
+ @s3 = ::Aws::S3::Client.new(region: region)
11
+ end
12
+ unless @s3 and ((@s3.config.access_key_id and @s3.config.secret_access_key) or @s3.config.credentials)
13
+ @logger.warn "Unable to find AWS credentials in standard locations:
14
+ ENV['AWS_ACCESS_KEY'] and ENV['AWS_SECRET_ACCESS_KEY']
15
+ Aws.config[:credentials]
16
+ Shared credentials file, ~/.aws/credentials
17
+ EC2 Instance profile
18
+ "
19
+ if ENV['AWS_PROFILE']
20
+ @logger.info "Trying profile '#{ENV['AWS_PROFILE']}' explicitly"
21
+ creds = Aws::SharedCredentials.new( path: File.expand_path('~/.aws/credentials'), profile: ENV['AWS_PROFILE'] )
22
+ if creds.loadable?
23
+ # noinspection RubyArgCount
24
+ @s3 = ::Aws::S3::Client.new(region: region, credentials: creds)
25
+ end
26
+ else
27
+ @logger.warn 'No AWS_PROFILE defined'
28
+ end
29
+ end
30
+ unless @s3 and ((@s3.config.access_key_id and @s3.config.secret_access_key) or @s3.config.credentials)
31
+ raise 'Unable to find AWS credentials!'
32
+ end
33
+ @s3
34
+ end
35
+
36
+ # ---------------------------------------------------------------------------------------------------------------
37
+ def getBucket(name = nil)
38
+ @s3 = getS3()
39
+ begin
40
+ ::Aws::S3::Bucket.new(name: name || ENV['AWS_S3_BUCKET'], client: @s3)
41
+ rescue Aws::S3::Errors::NotFound
42
+ @vars[:return_code] = Errors::BUCKET
43
+ nil
44
+ rescue Exception => e
45
+ @logger.error "S3 Bucket resource API error: #{e.class.name} #{e.message}"
46
+ raise e
47
+ end
48
+ end
49
+
50
+ # ---------------------------------------------------------------------------------------------------------------
51
+ def getObjects(artifact, path)
52
+ parts = URI(path).path.gsub(%r'^#{File::SEPARATOR}', '').split(File::SEPARATOR)
53
+ name = parts.shift
54
+ bucket = getBucket(name)
55
+ key = File.join(parts, '')
56
+ @logger.info "S3://#{name}:#{key} URL: #{path} #{artifact}"
57
+ objects = []
58
+ bucket.objects(prefix: key).each do |object|
59
+ if artifact.empty? or (not artifact.empty? and object.key =~ %r'#{key}#{artifact}')
60
+ objects << object
61
+ end
62
+ end
63
+ @logger.debug "S3://#{name}:#{key} has #{objects.size} objects"
64
+ return key, name, objects
65
+ end
66
+
67
+ # ---------------------------------------------------------------------------------------------------------------
68
+ def calcLocalETag(etag, local, size = nil)
69
+ if size == nil
70
+ stat = File.stat(local)
71
+ size = stat.size
72
+ end
73
+ @logger.debug "Calculate etag to match #{etag}"
74
+ match = etag.match(%r'-(\d+)$')
75
+ check = if match
76
+ require 's3etag'
77
+ parts = match[1].to_i
78
+ chunk = size.to_f / parts.to_f
79
+ mbs = (chunk.to_f / 1024 /1024 + 0.5).to_i
80
+ part_size = mbs * 1024 * 1024
81
+ chkit = S3Etag.calc(file: local, threshold: part_size, min_part_size: part_size, max_parts: parts)
82
+ @logger.debug "S3Etag Calculated #{chkit} : (#{size} / #{part_size}) <= #{parts}"
83
+ chunks = size / part_size
84
+ while chkit != etag and chunks <= parts and chunks > 0 and (size > part_size)
85
+ # Go one larger if a modulus remains and we have the right number of parts
86
+ mbs += 1
87
+ part_size = mbs * 1024 * 1024
88
+ chunks = size.to_f / part_size
89
+ chkit = S3Etag.calc(file: local, threshold: part_size, min_part_size: part_size, max_parts: parts)
90
+ @logger.debug "S3Etag Calculated #{chkit} : (#{size} / #{part_size}) <= #{parts}"
91
+ end
92
+ #raise "Unable to match etag #{etag}!" if chkit != etag
93
+ chkit
94
+ else
95
+ Digest::MD5.file(local).hexdigest
96
+ end
97
+ end
98
+
99
+ # ---------------------------------------------------------------------------------------------------------------
100
+ def shouldDownload?(etag, local, object)
101
+ if File.exists?(local)
102
+ @logger.debug "\t\tchecking etag on #{local}"
103
+ stat = File.stat(local)
104
+ check = calcLocalETag(etag, local, stat.size)
105
+ if etag != check or object.size != stat.size or object.last_modified > stat.mtime
106
+ @logger.debug "\t\t#{etag} != \"#{check}\" #{object.size} != #{stat.size} #{object.last_modified} > #{stat.mtime}"
107
+ true
108
+ else
109
+ @logger.debug "\t\tmatched #{etag}"
110
+ false
111
+ end
112
+ else
113
+ true
114
+ end
115
+ end
116
+
117
+ # ---------------------------------------------------------------------------------------------------------------
118
+ def doDownload(etag, local, object)
119
+ @logger.info "\t\tdownload #{object.size} bytes"
120
+ response = object.get(:response_target => local)
121
+ File.utime(response.last_modified, response.last_modified, local)
122
+ @logger.info "\t\tdone"
123
+ check = calcLocalETag(etag, local)
124
+ if check.eql?(etag)
125
+ false
126
+ else
127
+ @logger.info "\tETag different: #{etag} != #{check}"
128
+ true
129
+ end
130
+ end
131
+
132
+
133
+ class FakeLogger
134
+ def initialize
135
+
136
+ end
137
+
138
+ def method_missing(*args)
139
+ puts "#{args[0]}: #{args[1..-1].join(' ')}"
140
+ end
141
+ end
142
+
143
+ @logger = FakeLogger.new()
144
+ artifact, path = ['', 'https://s3.amazonaws.com/wgen-sto-artifacts/release/com/amplify/learning/enrollment/1.3.0-265']
145
+ local_dir = File.join('/tmp', 'enrollment', '')
146
+ Dir.mkdir(local_dir, 0700) unless File.directory?(local_dir)
147
+ artifacts = []
148
+
149
+ key, name, objects = getObjects(artifact, path)
150
+ # 1 or more objects on the key/ path
151
+ if objects.size > 0
152
+ objects.each do |object|
153
+ @logger.info "\tchecking #{object.key}"
154
+ local = File.join(local_dir, File.basename(object.key))
155
+ etag = object.etag.gsub(%r/['"]/, '')
156
+ download = shouldDownload?(etag, local, object)
157
+ if download
158
+ changed = doDownload(etag, local, object)
159
+ else
160
+ @logger.info "\t\tunchanged"
161
+ end
162
+ artifacts << local
163
+ end
164
+ else
165
+ @logger.fatal "Artifact not found: s3://#{name}/#{key}#{artifact}"
166
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chefrepo-manifest-builder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christo De Lange
@@ -187,10 +187,35 @@ dependencies:
187
187
  description: ChefRepo builder of the software manifest for Continuous Integration/Continuous
188
188
  Delivery artifact promotion style deployments
189
189
  email: rubygems@dldinternet.com
190
- executables: []
190
+ executables:
191
+ - chefrepomanifestbuilder
191
192
  extensions: []
192
193
  extra_rdoc_files: []
193
- files: []
194
+ files:
195
+ - ".gitignore"
196
+ - ".rakeTasks"
197
+ - ".rspec"
198
+ - ChangeLog.md
199
+ - Gemfile
200
+ - Gemfile.lock
201
+ - LICENSE
202
+ - LICENSE.txt
203
+ - README.md
204
+ - Rakefile
205
+ - bin/chefrepomanifestbuilder
206
+ - chefrepo-manifest-builder.gemspec
207
+ - features/.gitkeep
208
+ - features/manifestrepo-builder.feature
209
+ - features/step_definitions/.gitkeep
210
+ - features/step_definitions/manifestrepo-builder_steps.rb
211
+ - lib/cicd/builder/chefrepo-manifest.rb
212
+ - lib/cicd/builder/chefrepo-manifest/mixlib/build.rb
213
+ - lib/cicd/builder/chefrepo-manifest/mixlib/repo/artifactory.rb
214
+ - lib/cicd/builder/chefrepo-manifest/version.rb
215
+ - openssl
216
+ - spec/builder_spec.rb
217
+ - spec/spec_helper.rb
218
+ - tests/infinite-etag.rb
194
219
  homepage: https://rubygems.org/gems/manifest-builder
195
220
  licenses:
196
221
  - Apachev2
@@ -215,4 +240,10 @@ rubygems_version: 2.2.2
215
240
  signing_key:
216
241
  specification_version: 4
217
242
  summary: ChefRepo builder for a software manifest
218
- test_files: []
243
+ test_files:
244
+ - features/.gitkeep
245
+ - features/manifestrepo-builder.feature
246
+ - features/step_definitions/.gitkeep
247
+ - features/step_definitions/manifestrepo-builder_steps.rb
248
+ - spec/builder_spec.rb
249
+ - spec/spec_helper.rb