legion-data 0.2.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rubocop-analysis.yml +28 -0
  3. data/.github/workflows/sourcehawk-scan.yml +20 -0
  4. data/.gitignore +4 -2
  5. data/.rubocop.yml +6 -14
  6. data/CHANGELOG.md +2 -9
  7. data/CODE_OF_CONDUCT.md +75 -0
  8. data/CONTRIBUTING.md +55 -0
  9. data/Gemfile +7 -1
  10. data/INDIVIDUAL_CONTRIBUTOR_LICENSE.md +30 -0
  11. data/LICENSE +201 -0
  12. data/NOTICE.txt +9 -0
  13. data/README.md +60 -23
  14. data/SECURITY.md +9 -0
  15. data/attribution.txt +1 -0
  16. data/legion-data.gemspec +22 -38
  17. data/lib/legion/data.rb +18 -21
  18. data/lib/legion/data/connection.rb +29 -26
  19. data/lib/legion/data/migration.rb +3 -3
  20. data/lib/legion/data/migrations/001_add_schema_columns.rb +1 -1
  21. data/lib/legion/data/migrations/002_add_nodes.rb +20 -0
  22. data/lib/legion/data/migrations/003_add_settings.rb +18 -0
  23. data/lib/legion/data/migrations/004_add_extensions.rb +25 -0
  24. data/lib/legion/data/migrations/005_add_runners.rb +21 -0
  25. data/lib/legion/data/migrations/006_add_functions.rb +23 -0
  26. data/lib/legion/data/migrations/007_add_default_extensions.rb +25 -0
  27. data/lib/legion/data/migrations/008_add_tasks.rb +29 -0
  28. data/lib/legion/data/model.rb +5 -2
  29. data/lib/legion/data/models/extension.rb +0 -2
  30. data/lib/legion/data/models/function.rb +2 -6
  31. data/lib/legion/data/models/node.rb +1 -3
  32. data/lib/legion/data/models/runner.rb +0 -2
  33. data/lib/legion/data/models/setting.rb +0 -0
  34. data/lib/legion/data/models/task.rb +0 -2
  35. data/lib/legion/data/models/task_log.rb +0 -0
  36. data/lib/legion/data/settings.rb +25 -19
  37. data/lib/legion/data/version.rb +1 -1
  38. data/sonar-project.properties +12 -0
  39. data/sourcehawk.yml +4 -0
  40. metadata +41 -189
  41. data/.circleci/config.yml +0 -148
  42. data/.rspec +0 -1
  43. data/Rakefile +0 -55
  44. data/bin/console +0 -14
  45. data/bin/setup +0 -8
  46. data/bitbucket-pipelines.yml +0 -26
  47. data/lib/legion/data/connections/base.rb +0 -61
  48. data/lib/legion/data/connections/jdbc.rb +0 -21
  49. data/lib/legion/data/connections/mysql.rb +0 -26
  50. data/lib/legion/data/connections/mysql2.rb +0 -11
  51. data/lib/legion/data/connections/mysql_base.rb +0 -19
  52. data/lib/legion/data/migrations/002_add_users.rb +0 -17
  53. data/lib/legion/data/migrations/003_add_groups.rb +0 -16
  54. data/lib/legion/data/migrations/004_add_chains.rb +0 -25
  55. data/lib/legion/data/migrations/005_add_envs.rb +0 -24
  56. data/lib/legion/data/migrations/006_add_dcs.rb +0 -24
  57. data/lib/legion/data/migrations/007_add_nodes.rb +0 -26
  58. data/lib/legion/data/migrations/008_add_settings.rb +0 -18
  59. data/lib/legion/data/migrations/009_add_extensions.rb +0 -25
  60. data/lib/legion/data/migrations/010_add_runners.rb +0 -21
  61. data/lib/legion/data/migrations/011_add_functions.rb +0 -29
  62. data/lib/legion/data/migrations/012_add_tasks.rb +0 -25
  63. data/lib/legion/data/migrations/013_add_task_logs.rb +0 -17
  64. data/lib/legion/data/migrations/014_add_relationships.rb +0 -27
  65. data/lib/legion/data/models/chain.rb +0 -13
  66. data/lib/legion/data/models/datacenter.rb +0 -13
  67. data/lib/legion/data/models/environment.rb +0 -13
  68. data/lib/legion/data/models/group.rb +0 -10
  69. data/lib/legion/data/models/namespace.rb +0 -14
  70. data/lib/legion/data/models/relationship.rb +0 -18
  71. data/lib/legion/data/models/user.rb +0 -10
data/NOTICE.txt ADDED
@@ -0,0 +1,9 @@
1
+ Legion::Crypt(legion-crypt)
2
+ Copyright 2021 Optum
3
+
4
+ Project Description:
5
+ ====================
6
+ Manage
7
+
8
+ Author(s):
9
+ Esity
data/README.md CHANGED
@@ -1,37 +1,74 @@
1
- # Legion::Data
1
+ Legion::Data
2
+ =====
2
3
 
3
- Legion::Data is used by the framework to connect to a database. All database changes should be
4
- added as a migration with proper up/downs.
4
+ Legion::Data is a gem for the LegionIO framework to use persistent storage. Currently only MySQL is supported
5
5
 
6
- ## Installation
6
+ Supported Ruby versions and implementations
7
+ ------------------------------------------------
7
8
 
8
- Add this line to your application's Gemfile:
9
+ Legion::Json should work identically on:
9
10
 
10
- ```ruby
11
- gem 'legion-data'
12
- ```
11
+ * Ruby 2.5+
13
12
 
14
- And then execute:
15
13
 
16
- $ bundle
14
+ Installation and Usage
15
+ ------------------------
17
16
 
18
- Or install it yourself as:
17
+ You can verify your installation using this piece of code:
19
18
 
20
- $ gem install legion-data
19
+ ```bash
20
+ gem install legion-data
21
+ ```
21
22
 
22
- ## Usage
23
+ ```ruby
24
+ require 'legion/data'
23
25
 
24
- You can create a new connection to the database with the following example
25
- ``` Legion::Data::Connection.new.database.connection ```
26
- Keep in mind that if you need access to the database as part of a LEX, you should instead add it as a
27
- requirement inside your definitions with the following
26
+ Legion::Data.setup
27
+ Legion::Data.connected? # => true
28
+ Legion::Data::Model::Extension.all # Sequel::Dataset
28
29
  ```
29
- def requirements
30
- %w[legion-transport legion-data]
31
- end
30
+
31
+ Settings
32
+ ----------
33
+
34
+ ```json
35
+ {
36
+ "connected": false,
37
+ "cache": {
38
+ "connected": false,
39
+ "auto_enable": null,
40
+ "ttl": 60
41
+ },
42
+ "connection": {
43
+ "log": false,
44
+ "log_connection_info": false,
45
+ "log_warn_duration": 1,
46
+ "log_warn_duration": "debug",
47
+ "max_connections": 10,
48
+ "preconnect": false
49
+ },
50
+ "creds": {
51
+ "username": "legion",
52
+ "password": "legion",
53
+ "database": "legionio",
54
+ "host": "127.0.0.1",
55
+ "port": 3306
56
+ },
57
+ "migrations": {
58
+ "continue_on_fail": false,
59
+ "auto_migrate": true,
60
+ "ran": false,
61
+ "version": null
62
+ },
63
+ "models": {
64
+ "continue_on_load_fail": false,
65
+ "autoload": true
66
+ },
67
+ "connect_on_start": true
68
+ }
32
69
  ```
33
- and the framework will take care of the rest.
34
70
 
35
- ## Contributing
71
+ Authors
72
+ ----------
36
73
 
37
- Bug reports and pull requests are welcome on GitHub at https://bitbucket.org/legion-io/legion-data.
74
+ * [Matthew Iverson](https://github.com/Esity) - current maintainer
data/SECURITY.md ADDED
@@ -0,0 +1,9 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+ | Version | Supported |
5
+ | ------- | ------------------ |
6
+ | 1.x.x | :white_check_mark: |
7
+
8
+ ## Reporting a Vulnerability
9
+ To be added
data/attribution.txt ADDED
@@ -0,0 +1 @@
1
+ Add attributions here.
data/legion-data.gemspec CHANGED
@@ -1,49 +1,33 @@
1
- lib = File.expand_path('lib', __dir__)
2
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
- require 'legion/data/version'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/legion/data/version'
4
4
 
5
5
  Gem::Specification.new do |spec|
6
- spec.name = (RUBY_ENGINE == 'jruby' ? 'legion-data-java' : 'legion-data')
6
+ spec.name = 'legion-data'
7
7
  spec.version = Legion::Data::VERSION
8
8
  spec.authors = ['Esity']
9
- spec.email = ['matthewdiverson@gmail.com']
10
-
11
- spec.summary = 'Used by Legion to connect to the database'
12
- spec.description = 'The Legion connect gem'
13
- spec.homepage = 'https://bitbucket.org/legion-io/legion-data'
14
-
15
- spec.metadata['bug_tracker_uri'] = 'https://bitbucket.org/legion-io/legion-data/issues?status=new&status=open'
16
- spec.metadata['changelog_uri'] = 'https://bitbucket.org/legion-io/legion-data/src/CHANGELOG.md'
17
- spec.metadata['documentation_uri'] = 'https://bitbucket.org/legion-io/legion-data'
18
- spec.metadata['homepage_uri'] = 'https://bitbucket.org/legion-io/legion-data'
19
- spec.metadata['source_code_uri'] = 'https://bitbucket.org/legion-io/legion-data'
20
- spec.metadata['wiki_uri'] = 'https://bitbucket.org/legion-io/legion-data/wiki/Home'
9
+ spec.email = %w[matthewdiverson@gmail.com ruby@optum.com]
21
10
 
22
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
23
- f.match(%r{^(test|spec|features)/})
24
- end
25
- spec.bindir = 'bin'
26
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
11
+ spec.summary = 'Manages the connects to the backend database'
12
+ spec.description = 'A LegionIO gem to connect to a persistent data store'
13
+ spec.homepage = 'https://github.com/Optum/legion-data'
14
+ spec.license = 'Apache-2.0'
15
+ spec.required_ruby_version = '>= 2.5'
27
16
  spec.require_paths = ['lib']
28
-
29
- spec.add_development_dependency 'bundler'
30
- spec.add_development_dependency 'codecov'
31
- spec.add_development_dependency 'rake'
32
- spec.add_development_dependency 'rspec'
33
- spec.add_development_dependency 'rspec_junit_formatter'
34
- spec.add_development_dependency 'rubocop'
35
- spec.add_development_dependency 'rubocop-md'
36
- spec.add_development_dependency 'rubocop-performance'
37
- spec.add_development_dependency 'rubocop-rspec'
38
- spec.add_development_dependency 'rubocop-sequel'
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.test_files = spec.files.select { |p| p =~ %r{^test/.*_test.rb} }
19
+ spec.extra_rdoc_files = %w[README.md LICENSE CHANGELOG.md]
20
+ spec.metadata = {
21
+ 'bug_tracker_uri' => 'https://github.com/Optum/legion-data/issues',
22
+ 'changelog_uri' => 'https://github.com/Optum/legion-data/src/main/CHANGELOG.md',
23
+ 'documentation_uri' => 'https://github.com/Optum/legion-data',
24
+ 'homepage_uri' => 'https://github.com/Optum/LegionIO',
25
+ 'source_code_uri' => 'https://github.com/Optum/legion-data',
26
+ 'wiki_uri' => 'https://github.com/Optum/legion-data/wiki'
27
+ }
39
28
 
40
29
  spec.add_dependency 'legion-logging'
41
30
  spec.add_dependency 'legion-settings'
42
-
43
- if RUBY_ENGINE == 'jruby'
44
- spec.add_dependency 'jdbc-mysql'
45
- else
46
- spec.add_dependency 'mysql2'
47
- end
31
+ spec.add_dependency 'mysql2'
48
32
  spec.add_dependency 'sequel'
49
33
  end
data/lib/legion/data.rb CHANGED
@@ -1,10 +1,10 @@
1
1
  require 'legion/data/version'
2
- require_relative 'data/settings'
2
+ require 'legion/data/settings'
3
3
  require 'sequel'
4
4
 
5
- require_relative 'data/connection'
6
- require_relative 'data/model'
7
- require_relative 'data/migration'
5
+ require 'legion/data/connection'
6
+ require 'legion/data/model'
7
+ require 'legion/data/migration'
8
8
 
9
9
  module Legion
10
10
  module Data
@@ -13,7 +13,7 @@ module Legion
13
13
  connection_setup
14
14
  migrate
15
15
  load_models
16
- # setup_cache
16
+ setup_cache
17
17
  end
18
18
 
19
19
  def connection_setup
@@ -34,25 +34,22 @@ module Legion
34
34
  Legion::Data::Connection.sequel
35
35
  end
36
36
 
37
- def setup_cache # rubocop:disable Metrics/AbcSize
38
- return if Legion::Settings[:data][:cache][:enable]
37
+ def setup_cache
38
+ return if Legion::Settings[:data][:cache][:enabled]
39
39
 
40
- unless Legion::Settings[:cache][:connected]
41
- Legion::Logging.warn 'Legion::Data has caching enabled but Legion::Cache is not started'
42
- end
43
- return unless Legion::Settings[:cache][:connected]
40
+ return unless defined?(::Legion::Cache)
44
41
 
45
42
  # Legion::Data::Model::Relationship.plugin :caching, Legion::Cache, ttl: 10
46
- Legion::Data::Model::Chain.plugin :caching, Legion::Cache, ttl: 60
47
- Legion::Data::Model::Datacenter.plugin :caching, Legion::Cache, ttl: 120
48
- Legion::Data::Model::Extension.plugin :caching, Legion::Cache, ttl: 120
49
- Legion::Data::Model::Function.plugin :caching, Legion::Cache, ttl: 120
50
- Legion::Data::Model::Namespace.plugin :caching, Legion::Cache, ttl: 120
51
- Legion::Data::Model::Node.plugin :caching, Legion::Cache, ttl: 10
52
- Legion::Data::Model::TaskLog.plugin :caching, Legion::Cache, ttl: 12
53
- Legion::Data::Model::Task.plugin :caching, Legion::Cache, ttl: 10
54
- Legion::Data::Model::User.plugin :caching, Legion::Cache, ttl: 120
55
- Legion::Data::Model::Group.plugin :caching, Legion::Cache, ttl: 120
43
+ # Legion::Data::Model::Runner.plugin :caching, Legion::Cache, ttl: 60
44
+ # Legion::Data::Model::Chain.plugin :caching, Legion::Cache, ttl: 60
45
+ # Legion::Data::Model::Function.plugin :caching, Legion::Cache, ttl: 120
46
+ # Legion::Data::Model::Extension.plugin :caching, Legion::Cache, ttl: 120
47
+ # Legion::Data::Model::Node.plugin :caching, Legion::Cache, ttl: 10
48
+ # Legion::Data::Model::TaskLog.plugin :caching, Legion::Cache, ttl: 12
49
+ # Legion::Data::Model::Task.plugin :caching, Legion::Cache, ttl: 10
50
+ # Legion::Data::Model::User.plugin :caching, Legion::Cache, ttl: 120
51
+ # Legion::Data::Model::Group.plugin :caching, Legion::Cache, ttl: 120
52
+ # Legion::Logging.info 'Legion::Data connected to Legion::Cache'
56
53
  end
57
54
 
58
55
  def shutdown
@@ -4,33 +4,41 @@ module Legion
4
4
  module Data
5
5
  module Connection
6
6
  class << self
7
- attr_reader :sequel
8
- if RUBY_ENGINE == 'jruby'
9
- require_relative 'connections/jdbc'
10
- include Legion::Data::Connections::JDBC
11
- else
12
- require_relative 'connections/mysql2'
13
- include Legion::Data::Connections::MySQL2
7
+ attr_accessor :sequel
8
+
9
+ def adapter
10
+ @adapter ||= RUBY_ENGINE == 'jruby' ? :jdbc : :mysql2
14
11
  end
15
12
 
16
13
  def setup
17
- @sequel = ::Sequel.connect(adapter: adapter, **creds_builder)
14
+ @sequel = if adapter == :mysql2
15
+ ::Sequel.connect(adapter: adapter, **creds_builder)
16
+ else
17
+ ::Sequel.connect("jdbc:mysql://#{creds_builder[:host]}:#{creds_builder[:port]}/#{creds_builder[:database]}?user=#{creds_builder[:username]}&password=#{creds_builder[:password]}&serverTimezone=UTC") # rubocop:disable Layout/LineLength
18
+ end
18
19
  Legion::Settings[:data][:connected] = true
19
- return unless Legion::Settings[:data][:connection][:log]
20
+ return if Legion::Settings[:data][:connection].nil? || Legion::Settings[:data][:connection][:log].nil?
20
21
 
21
- @sequel.logger = Legion::Logging::Logger.new(level: 'debug')
22
+ @sequel.logger = Legion::Logging
22
23
  @sequel.sql_log_level = Legion::Settings[:data][:connection][:sql_log_level]
23
24
  @sequel.log_warn_duration = Legion::Settings[:data][:connection][:log_warn_duration]
24
25
  end
25
26
 
26
27
  def shutdown
27
- @sequel.disconnect
28
+ @sequel&.disconnect
29
+ Legion::Settings[:data][:connected] = false
28
30
  end
29
31
 
30
- def creds_builder # rubocop:disable Metrics/AbcSize
31
- final_creds = {}
32
- final_creds.merge! default_creds
33
- final_creds.merge! Legion::Settings[:data][:creds]
32
+ def creds_builder(final_creds = {})
33
+ final_creds.merge! Legion::Data::Settings.creds
34
+ final_creds.merge! Legion::Settings[:data][:creds] if Legion::Settings[:data][:creds].is_a? Hash
35
+
36
+ # if Legion::Settings[:data][:connection][:max_connections].is_a? Integer
37
+ # final_creds[:max_connections] = Legion::Settings[:data][:connection][:max_connections]
38
+ # end
39
+
40
+ # final_creds[:preconnect] = :concurrently if Legion::Settings[:data][:connection][:preconnect]
41
+
34
42
  return final_creds if Legion::Settings[:vault].nil?
35
43
 
36
44
  if Legion::Settings[:vault][:connected] && ::Vault.sys.mounts.key?(:database)
@@ -44,19 +52,14 @@ module Legion
44
52
 
45
53
  def default_creds
46
54
  {
47
- host: '127.0.0.1',
48
- port: 3306,
49
- username: 'legion',
50
- password: 'legion',
51
- database: 'legion',
52
- max_connections: 32,
53
- preconnect: 'concurrently'
55
+ host: '127.0.0.1',
56
+ port: 3306,
57
+ username: 'legion',
58
+ password: 'legion',
59
+ database: 'legion',
60
+ max_connections: 4
54
61
  }
55
62
  end
56
-
57
- def adapter
58
- 'mysql2'
59
- end
60
63
  end
61
64
  end
62
65
  end
@@ -4,9 +4,9 @@ module Legion
4
4
  module Data
5
5
  module Migration
6
6
  class << self
7
- def migrate(connection = Legion::Data.connection, path = __dir__ + '/migrations')
8
- Legion::Settings[:data][:migrations][:version] = Sequel::Migrator.run(connection, path)
9
- Legion::Logging.info 'Legion::Data::Migration ran successfully to version ' + Legion::Settings[:data][:migrations][:version].to_s # rubocop:disable Layout/LineLength
7
+ def migrate(connection = Legion::Data.connection, path = "#{__dir__}/migrations", **opts)
8
+ Legion::Settings[:data][:migrations][:version] = Sequel::Migrator.run(connection, path, **opts)
9
+ Legion::Logging.info("Legion::Data::Migration ran successfully to version #{Legion::Settings[:data][:migrations][:version]}") # rubocop:disable Layout/LineLength
10
10
  Legion::Settings[:data][:migrations][:ran] = true
11
11
  end
12
12
  end
@@ -8,7 +8,7 @@ Sequel.migration do
8
8
  end
9
9
 
10
10
  down do
11
- alter_table(:schema) do
11
+ alter_table(:schema_info) do
12
12
  drop_column :catalog
13
13
  drop_column :updated_at
14
14
  drop_column :created_at
@@ -0,0 +1,20 @@
1
+ Sequel.migration do
2
+ up do
3
+ run "CREATE TABLE `nodes` (
4
+ `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
5
+ `name` varchar(128) NOT NULL DEFAULT '',
6
+ `status` varchar(255) NOT NULL DEFAULT 'unknown',
7
+ `active` tinyint(1) unsigned NOT NULL DEFAULT '1',
8
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
9
+ `updated` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
10
+ PRIMARY KEY (`id`),
11
+ UNIQUE KEY `name` (`name`),
12
+ KEY `active` (`active`),
13
+ KEY `status` (`status`)
14
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"
15
+ end
16
+
17
+ down do
18
+ drop_table :nodes
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ Sequel.migration do
2
+ up do
3
+ run "CREATE TABLE `settings` (
4
+ `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
5
+ `key` varchar(128) NOT NULL,
6
+ `value` varchar(256) NOT NULL,
7
+ `encrypted` tinyint(1) unsigned NOT NULL DEFAULT '0',
8
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
9
+ `updated` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
10
+ PRIMARY KEY (`id`),
11
+ UNIQUE KEY `key` (`key`)
12
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"
13
+ end
14
+
15
+ down do
16
+ drop_table :settings
17
+ end
18
+ end
@@ -0,0 +1,25 @@
1
+ Sequel.migration do
2
+ up do
3
+ run "CREATE TABLE `extensions` (
4
+ `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
5
+ `active` tinyint(1) unsigned NOT NULL DEFAULT '1',
6
+ `name` varchar(128) NOT NULL,
7
+ `namespace` varchar(128) NOT NULL DEFAULT '',
8
+ `exchange` varchar(255) DEFAULT NULL,
9
+ `uri` varchar(256) DEFAULT NULL,
10
+ `schema_version` int(11) unsigned NOT NULL DEFAULT 0,
11
+ `updated` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
12
+ `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
13
+ PRIMARY KEY (`id`),
14
+ UNIQUE KEY `name_namespace` (`name`,`namespace`),
15
+ KEY `active` (`active`),
16
+ KEY `name` (`name`),
17
+ KEY `namespace` (`namespace`),
18
+ key `schema_version` (`schema_version`)
19
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"
20
+ end
21
+
22
+ down do
23
+ drop_table :extensions
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ Sequel.migration do
2
+ up do
3
+ run "CREATE TABLE `runners` (
4
+ `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
5
+ `extension_id` int(11) unsigned NOT NULL,
6
+ `name` varchar(256) NOT NULL DEFAULT '',
7
+ `namespace` varchar(256) NOT NULL DEFAULT '',
8
+ `active` tinyint(1) unsigned NOT NULL DEFAULT '1',
9
+ `queue` varchar(256) DEFAULT NULL,
10
+ `uri` varchar(256) DEFAULT NULL,
11
+ `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
12
+ `updated` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
13
+ PRIMARY KEY (`id`),
14
+ CONSTRAINT `runner_extension_id` FOREIGN KEY (`extension_id`) REFERENCES `extensions` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
15
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"
16
+ end
17
+
18
+ down do
19
+ drop_table :runners
20
+ end
21
+ end