container_broker 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +98 -0
  3. data/Rakefile +8 -0
  4. data/app/controllers/application_controller.rb +5 -0
  5. data/app/controllers/healthcheck_controller.rb +21 -0
  6. data/app/controllers/nodes_controller.rb +70 -0
  7. data/app/controllers/nodes_healthcheck_controller.rb +28 -0
  8. data/app/controllers/status_controller.rb +48 -0
  9. data/app/controllers/tasks_controller.rb +83 -0
  10. data/app/controllers/tasks_healthcheck_controller.rb +28 -0
  11. data/app/jobs/add_task_tags_job.rb +13 -0
  12. data/app/jobs/adjust_node_slots_job.rb +27 -0
  13. data/app/jobs/application_job.rb +9 -0
  14. data/app/jobs/collect_load_metrics_job.rb +9 -0
  15. data/app/jobs/container_broker_base_job.rb +32 -0
  16. data/app/jobs/migrate_tasks_from_dead_node_job.rb +32 -0
  17. data/app/jobs/monitor_unresponsive_node_job.rb +21 -0
  18. data/app/jobs/monitor_unresponsive_nodes_job.rb +9 -0
  19. data/app/jobs/release_slot_job.rb +47 -0
  20. data/app/jobs/remove_runner_job.rb +11 -0
  21. data/app/jobs/remove_unused_tags_job.rb +25 -0
  22. data/app/jobs/request_id_from_task.rb +7 -0
  23. data/app/jobs/run_task_job.rb +64 -0
  24. data/app/jobs/run_tasks_for_all_execution_types_job.rb +11 -0
  25. data/app/jobs/run_tasks_job.rb +42 -0
  26. data/app/jobs/timeout_failed_tasks_job.rb +31 -0
  27. data/app/jobs/update_all_nodes_status_job.rb +9 -0
  28. data/app/jobs/update_node_status_job.rb +24 -0
  29. data/app/jobs/update_task_status_job.rb +71 -0
  30. data/app/models/mongoid_serializable_model.rb +14 -0
  31. data/app/models/node.rb +101 -0
  32. data/app/models/slot.rb +42 -0
  33. data/app/models/task.rb +148 -0
  34. data/app/models/task_tag.rb +11 -0
  35. data/app/observers/observable.rb +23 -0
  36. data/app/observers/task_observer.rb +11 -0
  37. data/app/serializers/node_healthcheck_serializer.rb +5 -0
  38. data/app/serializers/node_serializer.rb +5 -0
  39. data/app/serializers/status_panel_node_serializer.rb +9 -0
  40. data/app/serializers/status_panel_slot_serializer.rb +5 -0
  41. data/app/serializers/status_panel_task_serializer.rb +16 -0
  42. data/app/serializers/task_healthcheck_serializer.rb +5 -0
  43. data/app/serializers/task_serializer.rb +7 -0
  44. data/app/services/adjust_execution_type_slots.rb +51 -0
  45. data/app/services/check_for_slot_removal.rb +28 -0
  46. data/app/services/collect_load_metrics.rb +40 -0
  47. data/app/services/delete_node.rb +25 -0
  48. data/app/services/friendly_name_nodes.rb +10 -0
  49. data/app/services/friendly_name_slots.rb +15 -0
  50. data/app/services/kill_node_runners.rb +17 -0
  51. data/app/services/kill_task_container.rb +29 -0
  52. data/app/services/kubernetes_client.rb +136 -0
  53. data/app/services/least_used_node.rb +44 -0
  54. data/app/services/lock_manager.rb +74 -0
  55. data/app/services/lock_slot.rb +37 -0
  56. data/app/services/lock_task.rb +45 -0
  57. data/app/services/metrics.rb +43 -0
  58. data/app/services/migrate_runner.rb +26 -0
  59. data/app/services/node_task_acceptance.rb +18 -0
  60. data/app/services/node_usage_percentage_per_execution_type.rb +22 -0
  61. data/app/services/reschedule_tasks_for_missing_runners.rb +70 -0
  62. data/app/services/runners.rb +4 -0
  63. data/app/services/runners/docker/create_connection.rb +18 -0
  64. data/app/services/runners/docker/create_execution_info.rb +87 -0
  65. data/app/services/runners/docker/fetch_execution_info.rb +17 -0
  66. data/app/services/runners/docker/fetch_logs.rb +18 -0
  67. data/app/services/runners/docker/fetch_task_container.rb +15 -0
  68. data/app/services/runners/docker/filer.rb +19 -0
  69. data/app/services/runners/docker/kill_slot_runner.rb +19 -0
  70. data/app/services/runners/docker/node_availability.rb +11 -0
  71. data/app/services/runners/docker/remove_runner.rb +18 -0
  72. data/app/services/runners/docker/run_task.rb +63 -0
  73. data/app/services/runners/docker/update_node_status.rb +62 -0
  74. data/app/services/runners/execution_info.rb +49 -0
  75. data/app/services/runners/invalid_config.rb +5 -0
  76. data/app/services/runners/invalid_runner.rb +5 -0
  77. data/app/services/runners/kubernetes/create_client.rb +29 -0
  78. data/app/services/runners/kubernetes/create_execution_info.rb +103 -0
  79. data/app/services/runners/kubernetes/fetch_execution_info.rb +15 -0
  80. data/app/services/runners/kubernetes/fetch_logs.rb +17 -0
  81. data/app/services/runners/kubernetes/filer.rb +41 -0
  82. data/app/services/runners/kubernetes/kill_slot_runner.rb +11 -0
  83. data/app/services/runners/kubernetes/node_availability.rb +11 -0
  84. data/app/services/runners/kubernetes/remove_runner.rb +19 -0
  85. data/app/services/runners/kubernetes/run_task.rb +54 -0
  86. data/app/services/runners/kubernetes/update_node_status.rb +64 -0
  87. data/app/services/runners/runner_id_not_found_error.rb +5 -0
  88. data/app/services/runners/services_factory.rb +38 -0
  89. data/app/services/runners/update_node_status_helper.rb +43 -0
  90. data/app/services/slots_usage_percentage.rb +18 -0
  91. data/config/application.rb +34 -0
  92. data/config/boot.rb +5 -0
  93. data/config/environment.rb +7 -0
  94. data/config/environments/test.rb +44 -0
  95. data/config/initializers/application_controller_renderer.rb +10 -0
  96. data/config/initializers/backtrace_silencers.rb +9 -0
  97. data/config/initializers/config.rb +51 -0
  98. data/config/initializers/cookies_serializer.rb +7 -0
  99. data/config/initializers/docker_config.rb +3 -0
  100. data/config/initializers/filter_parameter_logging.rb +6 -0
  101. data/config/initializers/idempotent_request.rb +12 -0
  102. data/config/initializers/inflections.rb +18 -0
  103. data/config/initializers/mime_types.rb +6 -0
  104. data/config/initializers/mongoid.rb +3 -0
  105. data/config/initializers/new_framework_defaults_6_0.rb +47 -0
  106. data/config/initializers/raven.rb +10 -0
  107. data/config/initializers/sidekiq.rb +24 -0
  108. data/config/initializers/wrap_parameters.rb +16 -0
  109. data/config/locales/en.yml +33 -0
  110. data/config/mongoid.yml +10 -0
  111. data/config/routes.rb +43 -0
  112. data/config/secrets.yml +35 -0
  113. data/config/settings.yml +34 -0
  114. data/config/settings/test.yml +27 -0
  115. data/config/sidekiq_scheduler.yml +18 -0
  116. data/config/spring.rb +8 -0
  117. data/lib/constants.rb +12 -0
  118. data/lib/container_broker.rb +30 -0
  119. data/lib/container_broker/engine.rb +6 -0
  120. data/lib/container_broker/version.rb +5 -0
  121. data/lib/current_thread_request_id.rb +19 -0
  122. data/lib/idempotent_request/callback.rb +25 -0
  123. data/lib/idempotent_request/policy.rb +15 -0
  124. data/lib/redis_url_parser.rb +25 -0
  125. data/lib/tasks/task.rake +34 -0
  126. metadata +590 -0
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ ::Docker.logger = Logger.new(STDOUT)
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Be sure to restart your server when you modify this file.
4
+
5
+ # Configure sensitive parameters which will be filtered from the log file.
6
+ Rails.application.config.filter_parameters += [:password]
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ Rails.application.config.middleware.use(
4
+ IdempotentRequest::Middleware,
5
+ storage: IdempotentRequest::RedisStorage.new(
6
+ Redis.new(RedisUrlParser.call(Settings.redis_url)),
7
+ expire_time: 1.day
8
+ ),
9
+ header_key: "X-Idempotency-Key",
10
+ policy: IdempotentRequest::Policy,
11
+ callback: IdempotentRequest::Callback
12
+ )
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Be sure to restart your server when you modify this file.
4
+
5
+ # Add new inflection rules using the following format. Inflections
6
+ # are locale specific, and you may define rules for as many different
7
+ # locales as you wish. All of these examples are active by default:
8
+ # ActiveSupport::Inflector.inflections(:en) do |inflect|
9
+ # inflect.plural /^(ox)$/i, '\1en'
10
+ # inflect.singular /^(ox)en/i, '\1'
11
+ # inflect.irregular 'person', 'people'
12
+ # inflect.uncountable %w( fish sheep )
13
+ # end
14
+
15
+ # These inflection rules are supported but not enabled by default:
16
+ # ActiveSupport::Inflector.inflections(:en) do |inflect|
17
+ # inflect.acronym 'RESTful'
18
+ # end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Be sure to restart your server when you modify this file.
4
+
5
+ # Add new mime types for use in respond_to blocks:
6
+ # Mime::Type.register "text/richtext", :rtf
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ Mongoid.raise_not_found_error = false
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Be sure to restart your server when you modify this file.
4
+ #
5
+ # This file contains migration options to ease your Rails 6.0 upgrade.
6
+ #
7
+ # Once upgraded flip defaults one by one to migrate to the new default.
8
+ #
9
+ # Read the Guide for Upgrading Ruby on Rails for more info on each option.
10
+
11
+ # Don't force requests from old versions of IE to be UTF-8 encoded.
12
+ Rails.application.config.action_view.default_enforce_utf8 = false
13
+
14
+ # Embed purpose and expiry metadata inside signed and encrypted
15
+ # cookies for increased security.
16
+ #
17
+ # This option is not backwards compatible with earlier Rails versions.
18
+ # It's best enabled when your entire app is migrated and stable on 6.0.
19
+ Rails.application.config.action_dispatch.use_cookies_with_metadata = true
20
+
21
+ # Change the return value of `ActionDispatch::Response#content_type` to Content-Type header without modification.
22
+ Rails.application.config.action_dispatch.return_only_media_type_on_content_type = false
23
+
24
+ # Return false instead of self when enqueuing is aborted from a callback.
25
+ Rails.application.config.active_job.return_false_on_aborted_enqueue = true
26
+
27
+ # Send Active Storage analysis and purge jobs to dedicated queues.
28
+ # Rails.application.config.active_storage.queues.analysis = :active_storage_analysis
29
+ # Rails.application.config.active_storage.queues.purge = :active_storage_purge
30
+
31
+ # When assigning to a collection of attachments declared via `has_many_attached`, replace existing
32
+ # attachments instead of appending. Use #attach to add new attachments without replacing existing ones.
33
+ # Rails.application.config.active_storage.replace_on_assign_to_many = true
34
+
35
+ # Use ActionMailer::MailDeliveryJob for sending parameterized and normal mail.
36
+ #
37
+ # The default delivery jobs (ActionMailer::Parameterized::DeliveryJob, ActionMailer::DeliveryJob),
38
+ # will be removed in Rails 6.1. This setting is not backwards compatible with earlier Rails versions.
39
+ # If you send mail in the background, job workers need to have a copy of
40
+ # MailDeliveryJob to ensure all delivery jobs are processed properly.
41
+ # Make sure your entire app is migrated and stable on 6.0 before using this setting.
42
+ # Rails.application.config.action_mailer.delivery_job = "ActionMailer::MailDeliveryJob"
43
+
44
+ # Enable the same cache key to be reused when the object being cached of type
45
+ # `ActiveRecord::Relation` changes by moving the volatile information (max updated at and count)
46
+ # of the relation's cache key into the cache version to support recycling cache key.
47
+ # Rails.application.config.active_record.collection_cache_versioning = true
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ if Settings.sentry.enabled
4
+ require "raven"
5
+
6
+ Raven.configure do |config|
7
+ config.dsn = "https://#{Settings.sentry.app.public_key}:#{Settings.sentry.app.private_key}@#{Settings.sentry.host}/#{Settings.sentry.app.project_id}"
8
+ config.ssl_verification = false
9
+ end
10
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ Rails.application.config.active_job.queue_adapter = Rails.env.test? ? :test : :sidekiq
4
+
5
+ Sidekiq.default_worker_options = { backtrace: true }
6
+
7
+ connection = proc {
8
+ Redis.new(RedisUrlParser.call(Settings.redis_url))
9
+ }
10
+
11
+ Sidekiq.configure_server do |config|
12
+ config.redis = ConnectionPool.new(size: 50, &connection)
13
+ config.logger.level = Logger::DEBUG
14
+
15
+ config.on(:startup) do
16
+ Sidekiq.schedule = YAML.load_file(File.expand_path("../sidekiq_scheduler.yml", __dir__))
17
+ SidekiqScheduler::Scheduler.instance.reload_schedule!
18
+ end
19
+ end
20
+
21
+ Sidekiq.configure_client do |config|
22
+ config.redis = ConnectionPool.new(size: 50, &connection)
23
+ config.logger.level = Logger::DEBUG
24
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Be sure to restart your server when you modify this file.
4
+
5
+ # This file contains settings for ActionController::ParamsWrapper which
6
+ # is enabled by default.
7
+
8
+ # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
9
+ ActiveSupport.on_load(:action_controller) do
10
+ wrap_parameters format: [:json]
11
+ end
12
+
13
+ # To enable root element in JSON for ActiveRecord objects.
14
+ # ActiveSupport.on_load(:active_record) do
15
+ # self.include_root_in_json = true
16
+ # end
@@ -0,0 +1,33 @@
1
+ # Files in the config/locales directory are used for internationalization
2
+ # and are automatically loaded by Rails. If you want to use locales other
3
+ # than English, add the necessary files in this directory.
4
+ #
5
+ # To use the locales, use `I18n.t`:
6
+ #
7
+ # I18n.t 'hello'
8
+ #
9
+ # In views, this is aliased to just `t`:
10
+ #
11
+ # <%= t('hello') %>
12
+ #
13
+ # To use a different locale, set it with `I18n.locale`:
14
+ #
15
+ # I18n.locale = :es
16
+ #
17
+ # This would use the information in config/locales/es.yml.
18
+ #
19
+ # The following keys must be escaped otherwise they will not be retrieved by
20
+ # the default I18n backend:
21
+ #
22
+ # true, false, on, off, yes, no
23
+ #
24
+ # Instead, surround them with single quotes.
25
+ #
26
+ # en:
27
+ # 'true': 'foo'
28
+ #
29
+ # To learn more, please read the Rails Internationalization guide
30
+ # available at http://guides.rubyonrails.org/i18n.html.
31
+
32
+ en:
33
+ hello: "Hello world"
@@ -0,0 +1,10 @@
1
+ test:
2
+ clients:
3
+ default:
4
+ database: container-broker_test
5
+ hosts:
6
+ - <%= ENV["MONGODB_URL"] || "localhost:27017" %>
7
+ options:
8
+ read:
9
+ mode: :primary
10
+ max_pool_size: 1
data/config/routes.rb ADDED
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ ContainerBroker::Engine.routes.draw do
4
+ resources :status, only: :index do
5
+ collection do
6
+ get :nodes
7
+ get :tasks
8
+ get :tags
9
+ get :tag_values
10
+ get :task_statuses
11
+ post "retry_task/:uuid", action: :retry_task
12
+ end
13
+ end
14
+
15
+ resources :tasks, only: %i[create show], param: :uuid do
16
+ get :logs, on: :member
17
+
18
+ member do
19
+ put :mark_as_error
20
+ post :kill_container
21
+ end
22
+
23
+ collection do
24
+ delete :errors, action: :clear_errors
25
+ get :healthcheck, controller: :tasks_healthcheck, action: :index
26
+ end
27
+ end
28
+
29
+ resources :nodes, except: %i[edit new], param: :uuid do
30
+ member do
31
+ post :accept_new_tasks, :reject_new_tasks, :kill_containers
32
+ end
33
+
34
+ collection do
35
+ get :healthcheck, controller: :nodes_healthcheck, action: :index
36
+ end
37
+ end
38
+ # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
39
+
40
+ # root to: redirect(Settings.backstage_admin.url)
41
+
42
+ get "/healthcheck" => "healthcheck#index"
43
+ end
@@ -0,0 +1,35 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Your secret key is used for verifying the integrity of signed cookies.
4
+ # If you change this key, all old signed cookies will become invalid!
5
+
6
+ # Make sure the secret is at least 30 characters and all random,
7
+ # no regular words or you'll be exposed to dictionary attacks.
8
+ # You can use `rails secret` to generate a secure secret key.
9
+
10
+ # Make sure the secrets in this file are kept private
11
+ # if you're sharing your code publicly.
12
+
13
+ # Shared secrets are available across all environments.
14
+
15
+ # shared:
16
+ # api_key: a1B2c3D4e5F6
17
+
18
+ # Environmental secrets are only available for that specific environment.
19
+
20
+ development:
21
+ secret_key_base: 41b8e60baf81a74ce740ca7115ea449c4b25bed764ed32f6a8453a2ced129cf229ee87e674b8aaad5a6f652129b3a29aabb6324df1f53ccde60b2cef665204a1
22
+
23
+ test:
24
+ secret_key_base: 187f05efcc897d1d81fe968e825825053ff28fa492e352febc7e7957320c32b7a5359ec0c8aecb46b34ebb909c6809ef8fb133dd3844e1d0f699333972295e42
25
+
26
+ # Do not keep production secrets in the unencrypted secrets file.
27
+ # Instead, either read values from the environment.
28
+ # Or, use `bin/rails secrets:setup` to configure encrypted secrets
29
+ # and move the `production:` environment over there.
30
+
31
+ dev:
32
+ secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
33
+
34
+ production:
35
+ secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
@@ -0,0 +1,34 @@
1
+ ignore_containers: []
2
+ cors_origins: []
3
+ kubernetes:
4
+ liveness_probe_seconds_interval: 30
5
+ requests:
6
+ memory: <%= ENV.fetch("KUBERNETES_REQUESTS_MEMORY", "4G") %>
7
+ cpu: <%= ENV.fetch("KUBERNETES_REQUESTS_CPU", 4) %>
8
+ limits:
9
+ memory: <%= ENV.fetch("KUBERNETES_LIMITS_MEMORY", "8G") %>
10
+ cpu: <%= ENV.fetch("KUBERNETES_LIMITS_CPU", 8) %>
11
+ sentry:
12
+ enabled: false
13
+ measures:
14
+ enabled: false
15
+ run_container_as:
16
+ user_id: root
17
+ group_id: root
18
+ timeout_tasks_after_hours: <%= 20.hours %>
19
+ task_retry_count: 3
20
+ redis_url: <%= ENV["DBAAS_SENTINEL_ENDPOINT"] %>
21
+ delete_container_after_run: <%= ENV.fetch("DELETE_CONTAINER_AFTER_RUN", true) %>
22
+ node_unavailable_after_seconds: <%= 2.minutes %>
23
+ storage_mounts:
24
+ docker:
25
+ shared: "/opt/shared"
26
+ tmp: "/tmp"
27
+ kubernetes:
28
+ shared:
29
+ nfs:
30
+ server: "nfs-server.test"
31
+ path: "/shared"
32
+ tmp:
33
+ local:
34
+ path: "/tmp"
@@ -0,0 +1,27 @@
1
+ task_retry_count: 3
2
+ redis_url: <%= ENV["REDIS_URL"] || "redis://localhost:6379/0" %>
3
+ delete_container_after_run: true
4
+ node_unavailable_after_seconds: <%= 5.minutes %>
5
+ timeout_tasks_after_hours: <%= 20.hours %>
6
+ sentry:
7
+ enabled: false
8
+ measures:
9
+ enabled: false
10
+ backstage_admin:
11
+ url: admin.test
12
+ run_container_as:
13
+ user_id: 1001
14
+ group_id: 1002
15
+ storage_mounts:
16
+ docker:
17
+ shared_nfs: /mnt/nfs/node
18
+ temp: /tmp/node
19
+ ingest-nfs: /tmp/ingest_nfs
20
+ kubernetes:
21
+ shared_nfs:
22
+ nfs:
23
+ server: nfs.test
24
+ path: /mnt/nfs/node
25
+ temp:
26
+ local:
27
+ path: /mnt/local/node
@@ -0,0 +1,18 @@
1
+ update_nodes_status:
2
+ cron: '*/5 * * * * *'
3
+ class: UpdateAllNodesStatusJob
4
+ run_tasks:
5
+ in: '10s'
6
+ class: RunTasksForAllExecutionTypesJob
7
+ monitor_unresponsive_nodes:
8
+ cron: '*/30 * * * * *'
9
+ class: MonitorUnresponsiveNodesJob
10
+ remove_unreferenced_tags:
11
+ cron: '0 30 4 * * *'
12
+ class: RemoveUnusedTagsJob
13
+ timeout_failed_tasks:
14
+ cron: '0 */15 * * * *'
15
+ class: TimeoutFailedTasksJob
16
+ collect_load_metrics:
17
+ cron: '*/5 * * * * *'
18
+ class: CollectLoadMetricsJob
data/config/spring.rb ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ %w[
4
+ .ruby-version
5
+ .rbenv-vars
6
+ tmp/restart.txt
7
+ tmp/caching-dev.txt
8
+ ].each { |path| Spring.watch(path) }
data/lib/constants.rb ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Constants
4
+ class ExecutionType
5
+ FORMAT = /\A([a-z0-9])+(\-[a-z0-9]+)*\z/.freeze
6
+ INVALID_FORMAT_MESSAGE = "only allows lowercase letters, numbers and hyphen symbol"
7
+ end
8
+
9
+ class Runner
10
+ MAX_NAME_SIZE = 63
11
+ end
12
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "container_broker/engine"
4
+
5
+ require "redis_url_parser"
6
+ require "idempotent_request/callback"
7
+ require "idempotent_request/policy"
8
+ require "constants"
9
+ require "current_thread_request_id"
10
+
11
+ # Gems
12
+ require "config"
13
+ require "docker-api"
14
+ require "active_model_serializers"
15
+ require "config"
16
+ require "idempotent-request"
17
+ require "kubeclient"
18
+ require "mongoid/uuid"
19
+ require "mongoid_enumerable"
20
+ require "redis-namespace"
21
+ require "sentry-raven"
22
+ require "sidekiq"
23
+ require "sidekiq-failures"
24
+ require "sidekiq-scheduler"
25
+ require "docker-api"
26
+ require "measures"
27
+
28
+ module ContainerBroker
29
+ # Your code goes here...
30
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ContainerBroker
4
+ class Engine < ::Rails::Engine
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ContainerBroker
4
+ VERSION = "1.0.1"
5
+ end