Welcome to mirror list, hosted at ThFree Co, Russian Federation.

pod.rb « models « app - github.com/diaspora/diaspora.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 1edc4e899126ba2f1b98b9d2b9bf83eeb459519c (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
147
148
149
150
151
152
153
154
155
156
157
158
159
# frozen_string_literal: true

class Pod < ApplicationRecord
  # a pod is active if it is online or was online less than 14 days ago
  ACTIVE_DAYS = 14.days

  enum status: %i(
    unchecked
    no_errors
    dns_failed
    net_failed
    ssl_failed
    http_failed
    version_failed
    unknown_error
  )

  ERROR_MAP = {
    ConnectionTester::AddressFailure  => :dns_failed,
    ConnectionTester::DNSFailure      => :dns_failed,
    ConnectionTester::NetFailure      => :net_failed,
    ConnectionTester::SSLFailure      => :ssl_failed,
    ConnectionTester::HTTPFailure     => :http_failed,
    ConnectionTester::NodeInfoFailure => :version_failed
  }

  # this are only the most common errors, the rest will be +unknown_error+
  CURL_ERROR_MAP = {
    couldnt_resolve_host:         :dns_failed,
    couldnt_connect:              :net_failed,
    operation_timedout:           :net_failed,
    ssl_cipher:                   :ssl_failed,
    ssl_cacert:                   :ssl_failed,
    redirected_to_other_hostname: :http_failed
  }.freeze

  DEFAULT_PORTS = [URI::HTTP::DEFAULT_PORT, URI::HTTPS::DEFAULT_PORT]

  has_many :people

  scope :check_failed, lambda {
    where(arel_table[:status].gt(Pod.statuses[:no_errors])).where.not(status: Pod.statuses[:version_failed])
  }

  scope :active, -> {
    where(["offline_since is null or offline_since > ?", DateTime.now.utc - ACTIVE_DAYS])
  }

  validate :not_own_pod

  class << self
    def find_or_create_by(opts) # Rename this method to not override an AR method
      uri = URI.parse(opts.fetch(:url))
      port = DEFAULT_PORTS.include?(uri.port) ? nil : uri.port
      find_or_initialize_by(host: uri.host, port: port).tap do |pod|
        pod.ssl ||= (uri.scheme == "https")
        pod.save
      end
    end

    # don't consider a failed version reading to be fatal
    def offline_statuses
      [Pod.statuses[:dns_failed],
       Pod.statuses[:net_failed],
       Pod.statuses[:ssl_failed],
       Pod.statuses[:http_failed],
       Pod.statuses[:unknown_error]]
    end

    def check_all!
      Pod.find_in_batches(batch_size: 20) {|batch| batch.each(&:test_connection!) }
    end

    def check_scheduled!
      Pod.where(scheduled_check: true).find_each(&:test_connection!)
    end
  end

  def offline?
    Pod.offline_statuses.include?(Pod.statuses[status])
  end

  # a pod is active if it is online or was online recently
  def active?
    !offline? || offline_since.try {|date| date > DateTime.now.utc - ACTIVE_DAYS }
  end

  def to_s
    "#{id}:#{host}"
  end

  def schedule_check_if_needed
    update_column(:scheduled_check, true) if offline? && !scheduled_check
  end

  def test_connection!
    result = ConnectionTester.check uri.to_s
    logger.debug "tested pod: '#{uri}' - #{result.inspect}"

    transaction do
      update_from_result(result)
    end
  end

  # @param path [String]
  # @return [String]
  def url_to(path)
    uri.tap {|uri| uri.path = path }.to_s
  end

  def update_offline_since
    if offline?
      self.offline_since ||= DateTime.now.utc
    else
      self.offline_since = nil
    end
  end

  private

  def update_from_result(result)
    self.status = status_from_result(result)
    update_offline_since
    logger.warn "#{uri} OFFLINE: #{result.failure_message}" if offline?

    attributes_from_result(result)
    touch(:checked_at)
    self.scheduled_check = false

    save
  end

  def attributes_from_result(result)
    self.ssl ||= result.ssl
    self.error = result.error? ? result.failure_message[0..254] : nil
    self.software = result.software_version[0..254] if result.software_version.present?
    self.response_time = result.rt
  end

  def status_from_result(result)
    if result.error?
      ERROR_MAP.fetch(result.error.class, :unknown_error)
    else
      :no_errors
    end
  end

  # @return [URI]
  def uri
    @uri ||= (ssl ? URI::HTTPS : URI::HTTP).build(host: host, port: port)
    @uri.dup
  end

  def not_own_pod
    pod_uri = AppConfig.pod_uri
    pod_port = DEFAULT_PORTS.include?(pod_uri.port) ? nil : pod_uri.port
    errors.add(:base, "own pod not allowed") if pod_uri.host == host && pod_port == port
  end
end