smartkiosk-server 0.10.4 → 0.10.5

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.
data/.gitignore CHANGED
@@ -19,6 +19,7 @@
19
19
  /public/uploads
20
20
  /public/builds
21
21
  /public/assets
22
+ /public/gems
22
23
 
23
24
  # Ignore the default SQLite database.
24
25
  /db/*.sqlite3*
data/Gemfile CHANGED
@@ -69,3 +69,5 @@ group :development do
69
69
  end
70
70
 
71
71
  gem 'iso8583-mkb', '0.0.2'
72
+
73
+ gem 'bundler', '~> 1.2.3'
@@ -385,6 +385,7 @@ DEPENDENCIES
385
385
  axlsx
386
386
  better_errors
387
387
  binding_of_caller
388
+ bundler (~> 1.2.3)
388
389
  capistrano
389
390
  carrierwave
390
391
  clockwork
@@ -7,7 +7,16 @@ ActiveAdmin.register TerminalBuild do
7
7
  index do
8
8
  selectable_column
9
9
  column :id
10
- column :version
10
+ column :version do |tb|
11
+ div tb.version
12
+
13
+ if tb.gems_ready
14
+ status_tag(I18n.t('smartkiosk.admin.terminal_build.gems_ready'), :ok)
15
+ else
16
+ status_tag(I18n.t('smartkiosk.admin.terminal_build.gems_not_ready'), :error)
17
+ end
18
+
19
+ end
11
20
  column :source do |tb|
12
21
  link_to File.basename(tb.source.path), tb.source.url
13
22
  end
@@ -23,6 +32,9 @@ ActiveAdmin.register TerminalBuild do
23
32
  row :source do |tb|
24
33
  link_to File.basename(tb.source.path), tb.source.url
25
34
  end
35
+ row :gems_ready do |tb|
36
+ status_boolean(self, tb.gems_ready)
37
+ end
26
38
  row :created_at
27
39
  row :updated_at
28
40
  end
@@ -35,5 +47,5 @@ ActiveAdmin.register TerminalBuild do
35
47
  end
36
48
  f.actions
37
49
  end
38
-
50
+
39
51
  end
@@ -2,8 +2,8 @@ ActiveAdmin.register Terminal do
2
2
 
3
3
  SIMPLE_ORDERS = Terminal::ORDERS.select{|x| x != 'upgrade'} unless defined?(SIMPLE_ORDERS)
4
4
 
5
- menu :parent => I18n.t('activerecord.models.terminal.other'),
6
- :label => I18n.t('smartkiosk.admin.menu.manage'),
5
+ menu :parent => I18n.t('activerecord.models.terminal.other'),
6
+ :label => I18n.t('smartkiosk.admin.menu.manage'),
7
7
  :priority => 1,
8
8
  :if => proc { can? :index, Terminal }
9
9
 
@@ -62,11 +62,16 @@ ActiveAdmin.register Terminal do
62
62
  member_action :upgrade, :method => :post do
63
63
  build = TerminalBuild.find(params[:build_id])
64
64
 
65
- Terminal.where(:id => params[:id].split(',')).each do |t|
66
- t.order! :upgrade, build.id, build.version, build.path, build.url
65
+ if build.gems_ready
66
+ Terminal.where(:id => params[:id].split(',')).each do |t|
67
+ t.order! :upgrade, build.id, build.version, build.path, build.url, URI.join(root_url, "/gems").to_s
68
+ end
69
+
70
+ redirect_to :action => :index
71
+ else
72
+ redirect_to :back, :flash => { :error => I18n.t('smartkiosk.admin.terminal_build.gems_not_ready_error') }
67
73
  end
68
74
 
69
- redirect_to :action => :index
70
75
  end
71
76
 
72
77
  SIMPLE_ORDERS.each do |order|
@@ -80,7 +85,7 @@ ActiveAdmin.register Terminal do
80
85
  # INDEX
81
86
  #
82
87
  batch_action(
83
- I18n.t('smartkiosk.admin.actions.terminals.upgrade'),
88
+ I18n.t('smartkiosk.admin.actions.terminals.upgrade'),
84
89
  :if => proc{current_user.priveleged?(:terminals, :upgrade)}
85
90
  ) do |selection|
86
91
  redirect_to upgrade_build_admin_terminal_path(Terminal.find(selection).map(&:id).join(','))
@@ -1,6 +1,11 @@
1
1
  require 'digest/md5'
2
2
 
3
3
  class TerminalBuild < ActiveRecord::Base
4
+ include Redis::Objects
5
+
6
+ lock :stash_worker, :global => true, :timeout => 0.1
7
+ counter :stash_counter, :global => true
8
+
4
9
  mount_uploader :source, ZipUploader
5
10
 
6
11
  validates :source, :presence => true
@@ -24,10 +29,12 @@ class TerminalBuild < ActiveRecord::Base
24
29
  end
25
30
 
26
31
  self.update_attributes :hashes => build_hashes
32
+ update_stash
27
33
  end
28
34
 
29
35
  after_destroy do
30
36
  FileUtils.rm_rf path
37
+ update_stash
31
38
  end
32
39
 
33
40
  def path
@@ -59,4 +66,12 @@ class TerminalBuild < ActiveRecord::Base
59
66
 
60
67
  hashes
61
68
  end
69
+
70
+ protected
71
+
72
+ def update_stash
73
+ TerminalBuild.stash_counter.incr
74
+
75
+ GemStashUpdateWorker.perform_async
76
+ end
62
77
  end
@@ -0,0 +1,43 @@
1
+ require "gem_stasher"
2
+
3
+ class GemStashUpdateWorker
4
+ include Sidekiq::Worker
5
+
6
+ sidekiq_options :retry => false
7
+
8
+ def perform
9
+ begin
10
+ TerminalBuild.stash_worker_lock.lock do
11
+ while TerminalBuild.stash_counter.getset(0) > 0
12
+ update_stash!
13
+ end
14
+ end
15
+ rescue Redis::Lock::LockTimeout => e
16
+ Sidekiq::Logging.logger.warn "Lock timeout."
17
+
18
+ # To avoid excessive cpu burning but still ensure robust updates
19
+ GemStashUpdateWorker.perform_in 1.minute
20
+ end
21
+ end
22
+
23
+ def update_stash!
24
+ builds = TerminalBuild.all
25
+
26
+ stasher = GemStasher.new Sidekiq::Logging.logger, Rails.root.join("public/gems")
27
+ stasher.maintain_cache builds.map(&:path)
28
+ stasher.update_index
29
+
30
+ TerminalBuild.transaction do
31
+ builds.each do |build|
32
+ begin
33
+ build.gems_ready = true
34
+
35
+ build.save!
36
+ rescue => e
37
+ Sidekiq::Logging.logger.warn "error occured during update of build #{build.id}: #{e}"
38
+ end
39
+ end
40
+ end
41
+
42
+ end
43
+ end
@@ -14,7 +14,7 @@ ru:
14
14
  not_a_hash: содержат неправильный формат
15
15
  rebate:
16
16
  attributes:
17
- start:
17
+ start:
18
18
  too_large: должна быть меньше даты окончания
19
19
  conditions_intersect: Условия выборки пересекаются
20
20
  requires_commission_intersects: Нельзя указать разные типы связи с комиссией в рамках одного шлюза
@@ -25,7 +25,7 @@ ru:
25
25
  too_large: должно быть меньше До
26
26
  commission:
27
27
  attributes:
28
- start:
28
+ start:
29
29
  too_large: должна быть меньше даты окончания
30
30
  conditions_intersect: Условия выборки пересекаются
31
31
  payment_type_intersects: Нельзя указать разные типы связи типа платежа в рамках одного провайдера
@@ -35,7 +35,7 @@ ru:
35
35
  too_large: должно быть меньше До
36
36
  limit:
37
37
  attributes:
38
- start:
38
+ start:
39
39
  too_large: должна быть меньше даты окончания
40
40
  conditions_intersect: Условия выборки пересекаются
41
41
  payment_type_intersects: Нельзя указать разные типы связи типа платежа в рамках одного провайдера
@@ -178,7 +178,7 @@ ru:
178
178
  many: Сверок
179
179
  one: Сверка
180
180
  other: Сверки
181
- terminal:
181
+ terminal:
182
182
  few: Терминала
183
183
  many: Терминалов
184
184
  one: Терминал
@@ -244,6 +244,7 @@ ru:
244
244
  terminal_build:
245
245
  version: Версия
246
246
  source: Источник (ZIP)
247
+ gems_ready: Готова к деплою
247
248
  created_at: Дата создания
248
249
  updated_at: Дата обновления
249
250
  provider_receipt_template:
@@ -1,6 +1,6 @@
1
1
  ru:
2
2
  smartkiosk:
3
- welcome:
3
+ welcome:
4
4
  header: Вы авторизованы как %{user}
5
5
  roles: Доступные роли
6
6
  properties: Свойства аккаунта
@@ -78,6 +78,10 @@ ru:
78
78
  upgrade: Обновление терминалов
79
79
  all_pings: Полная история связи
80
80
  pings: История связи
81
+ terminal_build:
82
+ gems_ready: Готова к деплою
83
+ gems_not_ready: Подготавливается
84
+ gems_not_ready_error: Сборка еще подготавливается
81
85
  role_priveleges:
82
86
  basic:
83
87
  create: Создание
@@ -4,6 +4,7 @@ class CreateTerminalBuilds < ActiveRecord::Migration
4
4
  t.string :version
5
5
  t.string :source
6
6
  t.text :hashes
7
+ t.boolean :gems_ready, :default => false
7
8
  t.timestamps
8
9
  end
9
10
  end
@@ -370,8 +370,9 @@ ActiveRecord::Schema.define(:version => 20130108091644) do
370
370
  t.string "version"
371
371
  t.string "source"
372
372
  t.text "hashes"
373
- t.datetime "created_at", :null => false
374
- t.datetime "updated_at", :null => false
373
+ t.boolean "gems_ready", :default => false
374
+ t.datetime "created_at", :null => false
375
+ t.datetime "updated_at", :null => false
375
376
  end
376
377
 
377
378
  create_table "terminal_orders", :force => true do |t|
@@ -0,0 +1,171 @@
1
+ require "bundler"
2
+ require "rubygems/user_interaction"
3
+ require "rubygems/package"
4
+ require "net/http"
5
+ require "set"
6
+ require "fileutils"
7
+
8
+ # Make LazySpecification Set-able
9
+ class Bundler::LazySpecification
10
+ alias :eql? :==
11
+
12
+ def hash
13
+ [ @name, @version, @dependencies, @platform, @source ].hash
14
+ end
15
+ end
16
+
17
+ class GemStasher
18
+ def initialize(logger, gempath)
19
+ @logger = logger
20
+ @gempath = gempath
21
+ end
22
+
23
+ def maintain_cache(builds)
24
+ sources = Set.new
25
+ required_gems = Set.new
26
+ existing_gems = Set.new
27
+
28
+ FileUtils.mkdir_p "#{@gempath}/gems"
29
+
30
+ @logger.info "Updating gem cache"
31
+
32
+ Dir["#{@gempath}/gems/*.gem"].each do |gempack|
33
+ begin
34
+ File.open(gempack) do |io|
35
+ Gem::Package.open(io, 'r', nil) do |gem|
36
+ spec = Bundler::LazySpecification.new(
37
+ gem.metadata.name,
38
+ gem.metadata.version,
39
+ gem.metadata.platform
40
+ )
41
+
42
+ existing_gems.add spec
43
+ end
44
+ end
45
+ rescue => e
46
+ @logger.warn "unable to read #{gempack}: #{e}"
47
+ File.delete gempack
48
+ end
49
+ end
50
+
51
+ builds.each do |build|
52
+ begin
53
+ gemfile = Bundler::Definition.build File.join(build, "Gemfile"),
54
+ File.join(build, "Gemfile.lock"), {}
55
+
56
+ gemfile.sources.each do |source|
57
+ next unless source.kind_of? Bundler::Source::Rubygems
58
+
59
+ source.remotes.each { |remote| sources.add remote }
60
+ end
61
+
62
+ gemfile.resolve.each do |spec|
63
+
64
+ required_gems.add Bundler::LazySpecification.new(
65
+ spec.name,
66
+ spec.version,
67
+ spec.platform
68
+ )
69
+ end
70
+ rescue => e
71
+ @logger.warn "unable to process #{build}: #{e}"
72
+ end
73
+ end
74
+
75
+ dead_gems = existing_gems - required_gems
76
+ missing_gems = required_gems - existing_gems
77
+
78
+ dead_gems.each do |spec|
79
+ @logger.info "- deleting dead #{spec.name} #{spec.version}"
80
+
81
+ filename = filename_for_spec spec
82
+
83
+ begin
84
+ File.delete "#{@gempath}/#{filename}"
85
+ rescue => e
86
+ @logger.warn "unable to delete #{filename}: #{e}"
87
+ end
88
+ end
89
+
90
+ if missing_gems.any?
91
+ sources.each do |uri|
92
+ fetcher = Bundler::Fetcher.new uri
93
+
94
+ names = missing_gems.map { |gem| gem.name }
95
+ specs = fetcher.specs names, uri
96
+
97
+ missing_gems.each do |gem|
98
+ found = specs.local_search gem
99
+
100
+ next unless found.any?
101
+
102
+ missing_gems.delete gem
103
+
104
+ spec, = found
105
+ filename = filename_for_spec spec
106
+ local_file_name = "#{@gempath}/#{filename}"
107
+
108
+ file_uri = uri.dup
109
+ file_uri.path = "#{uri.path}#{filename}"
110
+
111
+ @logger.info " - fetching #{spec.name} #{spec.version} from #{uri}"
112
+
113
+ io = File.open(local_file_name, "wb")
114
+ begin
115
+ fetch_file file_uri, io
116
+ rescue => e
117
+ File.delete io
118
+ warn "unable to download #{filename}: #{e}"
119
+ ensure
120
+ io.close
121
+ end
122
+ end
123
+ end
124
+
125
+ if missing_gems.any?
126
+ @logger.warn "unresolved dependencies:"
127
+ missing_gems.each do |spec|
128
+ @logger.warn " - #{spec}"
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+ def update_index
135
+ @logger.info "Updating gem index at #{@gempath}"
136
+ # unfortunately, it's not possible to use Gem::Index without breaking
137
+ # host application bundler.
138
+ Bundler.with_clean_env do
139
+ system "gem", "generate_index", "--directory", @gempath.to_s, "--quiet"
140
+ end
141
+ end
142
+
143
+ private
144
+
145
+ def filename_for_spec(spec)
146
+ "gems/#{spec.name}-#{spec.version}.gem"
147
+ end
148
+
149
+ def fetch_file(uri, io)
150
+ Net::HTTP.start(uri.host,
151
+ uri.port,
152
+ :use_ssl => uri.scheme == 'https',
153
+ :verify_mode => OpenSSL::SSL::VERIFY_NONE
154
+ ) do |http|
155
+ http.request_get(uri.path) do |response|
156
+ case response
157
+ when Net::HTTPSuccess
158
+ response.read_body { |chunk| io.write chunk }
159
+
160
+ when Net::HTTPRedirection
161
+ fetch_file URI.join(uri.to_s, response['location']), io
162
+
163
+ else
164
+ raise response.value
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end
170
+
171
+ Bundler.settings[:frozen] = true
@@ -1,5 +1,5 @@
1
1
  module Smartkiosk
2
2
  module Server
3
- VERSION = '0.10.4'
3
+ VERSION = '0.10.5'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smartkiosk-server
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.4
4
+ version: 0.10.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-02-06 00:00:00.000000000 Z
13
+ date: 2013-02-07 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rails
@@ -176,6 +176,7 @@ files:
176
176
  - app/views/admin/terminals/upgrade_build.html.arb
177
177
  - app/views/layouts/application.html.erb
178
178
  - app/views/welcome/index.html.erb
179
+ - app/workers/gem_stash_update_worker.rb
179
180
  - app/workers/pay_worker.rb
180
181
  - app/workers/report_worker.rb
181
182
  - app/workers/revise_worker.rb
@@ -272,6 +273,7 @@ files:
272
273
  - lib/date_expander.rb
273
274
  - lib/dav4rack/build_resource.rb
274
275
  - lib/formtastic/inputs/selectable_check_boxes.rb
276
+ - lib/gem_stasher.rb
275
277
  - lib/paper_trail/version_fix.rb
276
278
  - lib/report_builder.rb
277
279
  - lib/seeder.rb
@@ -321,7 +323,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
321
323
  version: '0'
322
324
  segments:
323
325
  - 0
324
- hash: 1639411805563885975
326
+ hash: -1133320975275850938
325
327
  required_rubygems_version: !ruby/object:Gem::Requirement
326
328
  none: false
327
329
  requirements: