summaryrefslogtreecommitdiff
path: root/lib/tapicero/user_database.rb
blob: 7c621ae853ad5548d472d755c664580d4fd47873 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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
require 'couchrest'
require 'json'
require 'tapicero/replication'

module Tapicero
  class UserDatabase

    def initialize(user_id)
      @db = couch.database(config.options[:db_prefix] + user_id)
    end

    def create
      retry_request_once "Creating database" do
        create_db
      end
    end

    def secure(security = nil)
      security ||= config.options[:security]
      # let's not overwrite if we have a security doc already
      return if secured? && !Tapicero::FLAGS.include?('--overwrite-security')
      retry_request_once "Writing security to" do
        Tapicero.logger.debug security.to_json
        CouchRest.put security_url, security
      end
    end

    def replicate()
      return unless config.options[:mode] == 'mirror'
      replication = config.options[:replication]
      replication["masters"].each do |key, node|
        node["name"] = name
        retry_request_once "Replicating" do
          Tapicero::Replication.new(node, name).run continuous: true
        end
      end
    end

    def add_design_docs
      pattern = BASE_DIR + 'designs' + '*.json'
      Tapicero.logger.debug "Looking for design docs in #{pattern}"
      Pathname.glob(pattern).each do |file|
        retry_request_once "Uploading design doc to" do
          upload_design_doc(file)
        end
      end
    end

    def upload_design_doc(file)
      old = CouchRest.get design_url(file.basename('.json'))
    rescue RestClient::ResourceNotFound
      begin
        CouchRest.put design_url(file.basename('.json')), JSON.parse(file.read)
      rescue RestClient::Conflict
      end
    end


    def destroy
      retry_request_once "Deleting database" do
        delete_db
      end
    end

    def name
      db.name
    end

    protected

    #
    # Sometimes attempting to create the db will fail with PreconditionFailed.
    # This error is supposed to be returned only when the db already exists.
    # However, sometimes the db will be created and PreconditionFailed will
    # be returned.
    #
    # Might throw one of the following exceptions:
    # * RestClient::BadRequest
    # * RestClient::Unauthorized
    #
    def create_db
      db.info # test if db exists
    rescue RestClient::ResourceNotFound
      begin
        couch.create_db(db.name)
      rescue RestClient::PreconditionFailed
      end
    end

    def delete_db
      db.delete! if db
    rescue RestClient::ResourceNotFound  # no database found
    end

    def retry_request_once(action)
      second_try ||= false
      Tapicero.logger.debug "#{action} #{db.name}"
      yield
    rescue RestClient::Exception => exc
      raise exc if Tapicero::RERAISE
      if second_try
        log_error "#{action} #{db.name} failed twice due to: ", exc
      else
        log_info "#{action} #{db.name} failed (trying again soon): ", exc
        sleep 5
        second_try = true
        retry
      end
    end

    def log_error(message, exc)
      # warn message is a one liner so nagios can parse it
      Tapicero.logger.warn message.to_s + exc.class.name + ': ' + exc.to_s
      Tapicero.logger.debug exc.backtrace.join("\n")
    end

    def log_info(message, exc)
      Tapicero.logger.info message.to_s + exc.class.name + ': ' + exc.to_s
    end

    def secured?
      retry_request_once "Checking security of" do
        CouchRest.get(security_url).keys.any?
      end
    end

    def security_url
      db.root + "/_security"
    end

    def design_url(doc_name)
      db.root + "/_design/#{doc_name}"
    end

    attr_reader :db

    def couch
      @couch ||= CouchRest.new(config.couch_host)
    end

    def config
      Tapicero.config
    end

  end
end