diff --git a/app/models/pod.rb b/app/models/pod.rb index fe0252b83..a42456622 100644 --- a/app/models/pod.rb +++ b/app/models/pod.rb @@ -19,6 +19,15 @@ class Pod < ActiveRecord::Base 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 + }.freeze + DEFAULT_PORTS = [URI::HTTP::DEFAULT_PORT, URI::HTTPS::DEFAULT_PORT] has_many :people @@ -76,17 +85,20 @@ class Pod < ActiveRecord::Base uri.tap {|uri| uri.path = path }.to_s end + def update_offline_since + if offline? + touch(:offline_since) unless was_offline? + else + self.offline_since = nil + end + end + private def update_from_result(result) self.status = status_from_result(result) - - if offline? - touch(:offline_since) unless was_offline? - logger.warn "OFFLINE #{result.failure_message}" - else - self.offline_since = nil - end + update_offline_since + logger.warn "OFFLINE #{result.failure_message}" if offline? attributes_from_result(result) touch(:checked_at) diff --git a/config/initializers/diaspora_federation.rb b/config/initializers/diaspora_federation.rb index c4a8ce274..f5482edd1 100644 --- a/config/initializers/diaspora_federation.rb +++ b/config/initializers/diaspora_federation.rb @@ -119,8 +119,21 @@ DiasporaFederation.configure do |config| Person.find_by(diaspora_handle: diaspora_id).send(:url_to, path) end - on :update_pod do - # TODO + on :update_pod do |url, status| + pod = Pod.find_or_create_by(url: url) + + if status.is_a? Symbol + pod.status = Pod::CURL_ERROR_MAP.fetch(status, :unknown_error) + pod.error = "FederationError: #{status}" + elsif status >= 200 && status < 300 + pod.status = :no_errors unless Pod.statuses[pod.status] == Pod.statuses[:version_failed] + else + pod.status = :http_failed + pod.error = "FederationError: HTTP status code was: #{status}" + end + pod.update_offline_since + + pod.save end end end diff --git a/spec/federation_callbacks_spec.rb b/spec/federation_callbacks_spec.rb index 9037e2a3f..044a0cbeb 100644 --- a/spec/federation_callbacks_spec.rb +++ b/spec/federation_callbacks_spec.rb @@ -389,4 +389,47 @@ describe "diaspora federation callbacks" do ).to eq("https://#{pod.host}/path/on/pod") end end + + describe ":update_pod" do + let(:pod) { FactoryGirl.create(:pod) } + let(:pod_url) { pod.url_to("/") } + + it "sets the correct error for curl-errors" do + pod = FactoryGirl.create(:pod) + + DiasporaFederation.callbacks.trigger(:update_pod, pod.url_to("/"), :ssl_cacert) + + updated_pod = Pod.find_or_create_by(url: pod.url_to("/")) + expect(Pod.statuses[updated_pod.status]).to eq(Pod.statuses[:ssl_failed]) + expect(updated_pod.error).to eq("FederationError: ssl_cacert") + end + + it "sets :no_errors to a pod that was down but up now and return code 202" do + pod = FactoryGirl.create(:pod, status: :unknown_error) + + DiasporaFederation.callbacks.trigger(:update_pod, pod.url_to("/"), 202) + + updated_pod = Pod.find_or_create_by(url: pod.url_to("/")) + expect(Pod.statuses[updated_pod.status]).to eq(Pod.statuses[:no_errors]) + end + + it "does not change a pod that has status :version_failed and was successful" do + pod = FactoryGirl.create(:pod, status: :version_failed) + + DiasporaFederation.callbacks.trigger(:update_pod, pod.url_to("/"), 202) + + updated_pod = Pod.find_or_create_by(url: pod.url_to("/")) + expect(Pod.statuses[updated_pod.status]).to eq(Pod.statuses[:version_failed]) + end + + it "sets :http_failed if it has an unsuccessful http status code" do + pod = FactoryGirl.create(:pod) + + DiasporaFederation.callbacks.trigger(:update_pod, pod.url_to("/"), 404) + + updated_pod = Pod.find_or_create_by(url: pod.url_to("/")) + expect(Pod.statuses[updated_pod.status]).to eq(Pod.statuses[:http_failed]) + expect(updated_pod.error).to eq("FederationError: HTTP status code was: 404") + end + end end diff --git a/spec/models/pod_spec.rb b/spec/models/pod_spec.rb index 3b9768955..1624b562b 100644 --- a/spec/models/pod_spec.rb +++ b/spec/models/pod_spec.rb @@ -136,4 +136,39 @@ describe Pod, type: :model do expect(pod.url_to("/receive/public")).to eq("https://#{pod.host}/receive/public") end end + + describe "#update_offline_since" do + let(:pod) { FactoryGirl.create(:pod) } + + it "handles a successful status" do + pod.status = :no_errors + pod.update_offline_since + + expect(pod.offline?).to be_falsey + expect(pod.offline_since).to be_nil + end + + it "handles a failed status" do + pod.status = :unknown_error + pod.update_offline_since + + expect(pod.offline?).to be_truthy + expect(pod.offline_since).to be_within(1.second).of Time.zone.now + end + + it "preserves the original offline timestamp" do + pod.status = :unknown_error + pod.update_offline_since + pod.save + + now = Time.zone.now + expect(pod.offline_since).to be_within(1.second).of now + + Timecop.travel(Time.zone.today + 30.days) do + pod.update_offline_since + expect(pod.offline_since).to be_within(1.second).of now + expect(Time.zone.now).to be_within(1.day).of(now + 30.days) + end + end + end end