Module: TidyReset

Defined in:
lib/tidy_reset.rb,
lib/tidy_reset/version.rb,
lib/tidy_reset/configuration.rb,
lib/tidy_reset/application_manager.rb

Defined Under Namespace

Classes: ApplicationManager, Configuration, NoTokenException, RakeCompletedException, RakeExecuteException, RakeStatusException

Constant Summary collapse

VERSION =
"0.1.0"

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.configurationObject

Returns the value of attribute configuration.



14
15
16
# File 'lib/tidy_reset.rb', line 14

def configuration
  @configuration
end

Class Method Details

.app_name(stage) ⇒ Object



62
63
64
# File 'lib/tidy_reset.rb', line 62

def app_name(stage)
  "#{configuration.app}-#{stage}"
end

.app_start(stage) ⇒ Object



77
78
79
80
81
82
# File 'lib/tidy_reset.rb', line 77

def app_start(stage)
  application_name = app_name(stage)
  application_manager = create_application_manager(application_name)
  application_manager.scale_dynos_to_configuration(dyno_config[stage])
  application_manager.disable_maintenance_mode
end

.app_stop(stage) ⇒ Object



70
71
72
73
74
75
# File 'lib/tidy_reset.rb', line 70

def app_stop(stage)
  application_name = app_name(stage)
  application_manager = create_application_manager(application_name)
  application_manager.enable_maintenance_mode
  application_manager.shutdown_all_dynos
end

.auth_token_cliObject



94
95
96
# File 'lib/tidy_reset.rb', line 94

def auth_token_cli
  Bundler.with_clean_env { `heroku auth:token`.chomp }
end

.configure {|configuration| ... } ⇒ Object

Yields:



20
21
22
# File 'lib/tidy_reset.rb', line 20

def configure
  yield(configuration)
end

.create_application_manager(application_name) ⇒ Object

Raises:



84
85
86
87
88
89
90
91
92
# File 'lib/tidy_reset.rb', line 84

def create_application_manager(application_name)
  token = if configuration.token
    configuration.token
  else
    auth_token_cli
  end
  raise NoTokenException unless token
  self::ApplicationManager.new(name: application_name, token: token)
end

.database_purge(db_config) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/tidy_reset.rb', line 119

def database_purge(db_config)
  encoding = configuration.database_encoding
  ActiveRecord::Base.remove_connection

  begin
    master_connection = master_connection_from_db_config(db_config)
    # Test connection
    master_connection.select_all("SELECT 1;")

    # Disallow connections from thinknear user. 
    master_connection.select_all("ALTER DATABASE #{db_config['database']} CONNECTION LIMIT 0;")

    # Terminate all connections to the database
    master_connection.select_all("SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE datname='#{db_config['database']}' AND pid <> pg_backend_pid();")

    # Drop the database
    master_connection.drop_database(db_config['database'])

    # Create the database
    master_connection.create_database(db_config['database'], db_config.merge('encoding' => encoding))

    # Enable connections from thinknear user
    master_connection.select_all("ALTER DATABASE #{db_config['database']} CONNECTION LIMIT -1;")
    
    # Connect to created database to set extensions
    ActiveRecord::Base.remove_connection
    ActiveRecord::Base.send(establish_connection_method, db_config).connection.execute("CREATE EXTENSION IF NOT EXISTS postgis")

  rescue ActiveRecord::StatementInvalid => error
    if /database .* already exists/ === error.message
      raise DatabaseAlreadyExists
    elsif /database .* is being accessed by other users/ === error.message
      result = ActiveRecord::Base.send(establish_connection_method, db_config).connection.select_all("SELECT * FROM pg_stat_activity;")
      puts result.to_hash
      raise
    else
      raise
    end
  ensure
    master_connection = master_connection_from_db_config(db_config)
    master_connection.select_all("ALTER DATABASE #{db_config['database']} CONNECTION LIMIT -1;")
  end
end

.dyno_configObject



66
67
68
# File 'lib/tidy_reset.rb', line 66

def dyno_config
  YAML.load_file("#{Rails.root}/config/tidy/dynos.yml")
end

.establish_connection_methodObject



110
111
112
113
114
115
116
117
# File 'lib/tidy_reset.rb', line 110

def establish_connection_method
  # Choose proper connection method when ActiveRecord uses activerecord-import
  if ActiveRecord::Base.respond_to?(:establish_connection_without_activerecord_import)
    :establish_connection_without_activerecord_import
  else 
    :establish_connection
  end
end

.execute(command) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/tidy_reset.rb', line 30

def execute(command)
  Bundler.with_clean_env do 
    begin
      tries ||= 3
      @execute_error = false
      @rake_completed = false
      puts "running: #{command}"
      
      Open3.popen3(command) do |stdin, stdout, stderr, wait_thr| 
        while line = stdout.gets
          process(line)
        end
        while line = stderr.gets
          process(line)
        end
        
        raise RakeStatusException, "Non-zero Exit Code: #{wait_thr.value}" unless wait_thr.value.success?
        raise RakeCompletedException if (@rake_completed == false && command =~ /heroku run rake tidy:db:/ )
        raise @execute_error if @execute_error
      end
    rescue => e
      if (tries -= 1) > 0
        puts "Failed running command. Waiting 10s before next retry. Error: #{e}"
        sleep(10)
        retry
      else
        raise RuntimeError, "Error running command. Retried 3 times. Error #{e}"
      end
    end
  end
end

.master_connection_from_db_config(db_config) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
# File 'lib/tidy_reset.rb', line 98

def master_connection_from_db_config(db_config)
  # Get connection to master/maintenance database 'postgres'
  pool = ActiveRecord::Base.send(establish_connection_method, db_config.merge(
          'database'           => 'postgres',
          'schema_search_path' => 'public'
        ))
  if pool.is_a?(Array)
    raise RuntimeError, "Pool an array size #{pool.size}. First: #{pool.first}"
  end
  pool.connection
end

.process(line) ⇒ Object



24
25
26
27
28
# File 'lib/tidy_reset.rb', line 24

def process(line)
  puts line
  @execute_error = RakeExecuteException if line =~ /rake aborted/
  @rake_completed = true if line =~ /Tidy task done./
end