mirror of
https://github.com/cmur2/dyndnsd.git
synced 2025-08-08 08:33:56 +02:00
Compare commits
51 Commits
v2.0.0.rc2
...
v2.2.0
Author | SHA1 | Date | |
---|---|---|---|
c112b211a0 | |||
b597ecb15e | |||
c73c09f311 | |||
88133edc1a | |||
9dfafd7e8d | |||
af5e4ca3e0 | |||
0317189057 | |||
73dbf2a5fa | |||
6d0457d70c | |||
9ab080072f | |||
fdcd6e8da4 | |||
31be09c1c2 | |||
3406e22588 | |||
![]() |
a42a864f56 | ||
![]() |
07fd8681e4 | ||
![]() |
6831744e7b | ||
![]() |
7d49b861fc | ||
![]() |
2ad816b866 | ||
![]() |
f4f10c94c4 | ||
![]() |
bb7302407a | ||
![]() |
9f132b4a7d | ||
![]() |
32d6d01d9d | ||
![]() |
9d80220d24 | ||
![]() |
b00aa0e464 | ||
![]() |
a55d882d4d | ||
![]() |
aa5de52f4d | ||
![]() |
ee56fe8d07 | ||
![]() |
051d561700 | ||
![]() |
ccaa39e871 | ||
![]() |
b69aa6dc5b | ||
7a03415dd9 | |||
77914c42e1 | |||
![]() |
a9d1cc644a | ||
![]() |
902e5868b3 | ||
ffe01467c5 | |||
79db1b1911 | |||
![]() |
f37a77094f | ||
![]() |
2e233422d3 | ||
![]() |
6f044ec5f2 | ||
![]() |
988e30dac9 | ||
![]() |
31233df5ee | ||
1f35f60c55 | |||
![]() |
6310343e45 | ||
![]() |
1dd0eb7d1a | ||
280bfbeb82 | |||
![]() |
bd7c786c9c | ||
![]() |
4f3e591f13 | ||
![]() |
c1b5e14862 | ||
![]() |
d1dc91ed8d | ||
![]() |
14fe8ebedf | ||
![]() |
5264b3e6f1 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
.DS_Store
|
||||
*.lock
|
||||
pkg/*
|
||||
.yardoc
|
||||
|
30
.rubocop.yml
30
.rubocop.yml
@@ -1,15 +1,22 @@
|
||||
inherit_from: .rubocop_todo.yml
|
||||
|
||||
AllCops:
|
||||
TargetRubyVersion: '2.3'
|
||||
|
||||
Gemspec/OrderedDependencies:
|
||||
Layout/EmptyLineAfterGuardClause:
|
||||
Enabled: false
|
||||
|
||||
# allows nicer usage of private_class_method
|
||||
Layout/EmptyLinesAroundArguments:
|
||||
Enabled: false
|
||||
|
||||
Layout/HashAlignment:
|
||||
Enabled: false
|
||||
|
||||
Layout/LeadingEmptyLines:
|
||||
Enabled: false
|
||||
|
||||
Layout/LineLength:
|
||||
Max: 200
|
||||
|
||||
Layout/SpaceInsideHashLiteralBraces:
|
||||
Enabled: false
|
||||
|
||||
@@ -25,16 +32,13 @@ Metrics/ClassLength:
|
||||
Metrics/CyclomaticComplexity:
|
||||
Enabled: false
|
||||
|
||||
Metrics/LineLength:
|
||||
Max: 200
|
||||
|
||||
Metrics/MethodLength:
|
||||
Enabled: false
|
||||
|
||||
Metrics/PerceivedComplexity:
|
||||
Enabled: false
|
||||
|
||||
Naming/UncommunicativeMethodParamName:
|
||||
Naming/MethodParameterName:
|
||||
Enabled: false
|
||||
|
||||
Naming/MemoizedInstanceVariableName:
|
||||
@@ -49,12 +53,18 @@ Style/Documentation:
|
||||
Style/FormatStringToken:
|
||||
Enabled: false
|
||||
|
||||
Style/FrozenStringLiteralComment:
|
||||
Enabled: false
|
||||
|
||||
Style/GuardClause:
|
||||
Enabled: false
|
||||
|
||||
Style/HashEachMethods:
|
||||
Enabled: true
|
||||
|
||||
Style/HashTransformKeys:
|
||||
Enabled: true
|
||||
|
||||
Style/HashTransformValues:
|
||||
Enabled: true
|
||||
|
||||
Style/IdenticalConditionalBranches:
|
||||
Enabled: false
|
||||
|
||||
|
@@ -1,7 +0,0 @@
|
||||
# This configuration was generated by
|
||||
# `rubocop --auto-gen-config`
|
||||
# on 2018-02-23 12:54:10 +0100 using RuboCop version 0.52.1.
|
||||
# The point is for the user to remove these configuration records
|
||||
# one by one as the offenses are removed from the code base.
|
||||
# Note that changes in the inspected code, or installation of new
|
||||
# versions of RuboCop, may require this file to be generated again.
|
16
.solargraph.yml
Normal file
16
.solargraph.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
include:
|
||||
- "**/*.rb"
|
||||
- "bin/dyndnsd"
|
||||
exclude:
|
||||
- spec/**/*
|
||||
- test/**/*
|
||||
- vendor/**/*
|
||||
- ".bundle/**/*"
|
||||
require: []
|
||||
domains: []
|
||||
reporters:
|
||||
- rubocop
|
||||
- require_not_found
|
||||
require_paths: []
|
||||
max_files: 5000
|
13
.travis.yml
13
.travis.yml
@@ -1,7 +1,12 @@
|
||||
---
|
||||
sudo: false
|
||||
os: linux
|
||||
language: ruby
|
||||
rvm:
|
||||
- 2.5
|
||||
- 2.4
|
||||
- 2.3
|
||||
- 2.7
|
||||
- 2.6
|
||||
- 2.5
|
||||
- 2.4
|
||||
- 2.3
|
||||
|
||||
script:
|
||||
- bundle exec rake travis
|
||||
|
27
CHANGELOG.md
27
CHANGELOG.md
@@ -1,6 +1,27 @@
|
||||
# Changelog
|
||||
|
||||
## 2.0.0
|
||||
## 2.2.0 (March 6, 2020)
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
||||
- Refactor gemspec based on [recommendations](https://piotrmurach.com/articles/writing-a-ruby-gem-specification/) so tests are now excluded from gem and binaries move to `./exe` directory
|
||||
- Adopt Ruby 2.3 frozen string literals for source code potentially reducing memory consumption
|
||||
|
||||
## 2.1.1 (March 1, 2020)
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
||||
- Fix potential `nil` cases detected by [Sorbet](https://sorbet.org) including refactorings
|
||||
|
||||
## 2.1.0 (March 1, 2020)
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
||||
- Add Ruby 2.7 support
|
||||
- Add [solargraph](https://github.com/castwide/solargraph) to dev tooling as Ruby Language Server usable e.g. for IDEs (used solargraph version not compatible with Ruby 2.7 as bundler-audit 0.6.x requires old `thor` gem)
|
||||
- Document code using YARD tags, e.g. for type information and better code completion
|
||||
|
||||
## 2.0.0 (January 25, 2019)
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
||||
@@ -8,8 +29,8 @@ IMPROVEMENTS:
|
||||
- Better protocol compliance by returning `badauth` in response body on HTTP 401 errors
|
||||
- Better code maintainability by refactorings
|
||||
- Update dependencies, mainly `rack` to new major version 2
|
||||
- Add Ruby 2.5 support
|
||||
- Add experimental [OpenTracing](http://opentracing.io/) support with [CNCF Jaeger](https://github.com/jaegertracing/jaeger)
|
||||
- Add Ruby 2.5 and Ruby 2.6 support
|
||||
- Add experimental [OpenTracing](https://opentracing.io/) support with [CNCF Jaeger](https://github.com/jaegertracing/jaeger)
|
||||
- Support host offlining by deleting the associated DNS records
|
||||
- Add textfile reporter to write Graphite-style metrics (also compatible with [Prometheus](https://prometheus.io/)) into a file
|
||||
|
||||
|
2
Gemfile
2
Gemfile
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gemspec
|
||||
|
18
Rakefile
18
Rakefile
@@ -1,8 +1,24 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'bundler/gem_tasks'
|
||||
require 'rspec/core/rake_task'
|
||||
require 'rubocop/rake_task'
|
||||
require 'bundler/audit/task'
|
||||
|
||||
RSpec::Core::RakeTask.new(:spec)
|
||||
RuboCop::RakeTask.new
|
||||
Bundler::Audit::Task.new
|
||||
|
||||
task default: [:rubocop, :spec]
|
||||
desc 'Should be run by developer once to prepare initial solargraph usage (fill caches etc.)'
|
||||
task :'solargraph:init' do
|
||||
sh 'solargraph download-core'
|
||||
end
|
||||
|
||||
desc 'Run experimental solargraph type checker'
|
||||
task :'solargraph:tc' do
|
||||
sh 'solargraph typecheck'
|
||||
end
|
||||
|
||||
task default: [:rubocop, :spec, 'bundle:audit']
|
||||
|
||||
task travis: [:default, :'solargraph:tc']
|
||||
|
@@ -1,4 +0,0 @@
|
||||
|
||||
require 'dyndnsd'
|
||||
|
||||
Dyndnsd::Daemon.run!
|
@@ -1,7 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
$LOAD_PATH.push File.expand_path('lib', __dir__)
|
||||
|
||||
require 'dyndnsd/version'
|
||||
require_relative 'lib/dyndnsd/version'
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = 'dyndnsd'
|
||||
@@ -9,28 +8,36 @@ Gem::Specification.new do |s|
|
||||
s.summary = 'dyndnsd.rb'
|
||||
s.description = 'A small, lightweight and extensible DynDNS server written with Ruby and Rack.'
|
||||
s.author = 'Christian Nicolai'
|
||||
s.email = 'chrnicolai@gmail.com'
|
||||
|
||||
s.homepage = 'https://github.com/cmur2/dyndnsd'
|
||||
s.license = 'Apache-2.0'
|
||||
s.metadata = {
|
||||
'bug_tracker_uri' => "#{s.homepage}/issues",
|
||||
'changelog_uri' => "#{s.homepage}/blob/master/CHANGELOG.md",
|
||||
'source_code_uri' => s.homepage
|
||||
}
|
||||
|
||||
s.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
||||
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
||||
s.files = `git ls-files -z`.split("\x0").select do |f|
|
||||
f.match(%r{^(init.d|lib)/})
|
||||
end
|
||||
s.require_paths = ['lib']
|
||||
s.bindir = 'exe'
|
||||
s.executables = ['dyndnsd']
|
||||
s.extra_rdoc_files = Dir['README.md', 'CHANGELOG.md', 'LICENSE']
|
||||
|
||||
s.required_ruby_version = '>= 2.3'
|
||||
|
||||
s.add_runtime_dependency 'rack', '~> 2.0'
|
||||
s.add_runtime_dependency 'json'
|
||||
s.add_runtime_dependency 'jaeger-client', '~> 0.10.0'
|
||||
s.add_runtime_dependency 'metriks'
|
||||
s.add_runtime_dependency 'opentracing', '~> 0.4.0'
|
||||
s.add_runtime_dependency 'rack-tracer', '~> 0.5.0'
|
||||
s.add_runtime_dependency 'spanmanager', '~> 0.3.0'
|
||||
s.add_runtime_dependency 'jaeger-client', '~> 0.4.0'
|
||||
s.add_runtime_dependency 'opentracing', '~> 0.5.0'
|
||||
s.add_runtime_dependency 'rack', '~> 2.0'
|
||||
s.add_runtime_dependency 'rack-tracer', '~> 0.9.0'
|
||||
|
||||
s.add_development_dependency 'bundler'
|
||||
s.add_development_dependency 'bundler-audit', '~> 0.6.0'
|
||||
s.add_development_dependency 'rack-test'
|
||||
s.add_development_dependency 'rake'
|
||||
s.add_development_dependency 'rspec'
|
||||
s.add_development_dependency 'rack-test'
|
||||
s.add_development_dependency 'rubocop', '~> 0.55.0'
|
||||
s.add_development_dependency 'rubocop', '~> 0.80.0'
|
||||
s.add_development_dependency 'solargraph'
|
||||
end
|
||||
|
6
exe/dyndnsd
Executable file
6
exe/dyndnsd
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'dyndnsd'
|
||||
|
||||
Dyndnsd::Daemon.run!
|
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'etc'
|
||||
require 'logger'
|
||||
@@ -10,7 +10,6 @@ require 'metriks'
|
||||
require 'metriks/reporter/graphite'
|
||||
require 'opentracing'
|
||||
require 'rack/tracer'
|
||||
require 'spanmanager'
|
||||
|
||||
require 'dyndnsd/generator/bind'
|
||||
require 'dyndnsd/updater/command_with_bind_zone'
|
||||
@@ -22,21 +21,32 @@ require 'dyndnsd/textfile_reporter'
|
||||
require 'dyndnsd/version'
|
||||
|
||||
module Dyndnsd
|
||||
# @return [Logger]
|
||||
def self.logger
|
||||
@logger
|
||||
end
|
||||
|
||||
# @param logger [Logger]
|
||||
# @return [Logger]
|
||||
def self.logger=(logger)
|
||||
@logger = logger
|
||||
end
|
||||
|
||||
class LogFormatter
|
||||
# @param lvl [Object]
|
||||
# @param _time [DateTime]
|
||||
# @param _progname [String]
|
||||
# @param msg [Object]
|
||||
# @return [String]
|
||||
def call(lvl, _time, _progname, msg)
|
||||
format("[%s] %-5s %s\n", Time.now.strftime('%Y-%m-%d %H:%M:%S'), lvl, msg.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
class Daemon
|
||||
# @param config [Hash{String => Object}]
|
||||
# @param db [Dyndnsd::Database]
|
||||
# @param updater [#update]
|
||||
def initialize(config, db, updater)
|
||||
@users = config['users']
|
||||
@domain = config['domain']
|
||||
@@ -52,6 +62,9 @@ module Dyndnsd
|
||||
end
|
||||
end
|
||||
|
||||
# @param username [String]
|
||||
# @param password [String]
|
||||
# @return [Boolean]
|
||||
def authorized?(username, password)
|
||||
Helper.span('check_authorized') do |span|
|
||||
span.set_tag('dyndnsd.user', username)
|
||||
@@ -65,6 +78,8 @@ module Dyndnsd
|
||||
end
|
||||
end
|
||||
|
||||
# @param env [Hash{String => String}]
|
||||
# @return [Array{Integer,Hash{String => String},Array{String}}]
|
||||
def call(env)
|
||||
return [422, {'X-DynDNS-Response' => 'method_forbidden'}, []] if env['REQUEST_METHOD'] != 'GET'
|
||||
return [422, {'X-DynDNS-Response' => 'not_found'}, []] if env['PATH_INFO'] != '/nic/update'
|
||||
@@ -72,6 +87,7 @@ module Dyndnsd
|
||||
handle_dyndns_request(env)
|
||||
end
|
||||
|
||||
# @return [void]
|
||||
def self.run!
|
||||
if ARGV.length != 1
|
||||
puts 'Usage: dyndnsd config_file'
|
||||
@@ -96,8 +112,14 @@ module Dyndnsd
|
||||
|
||||
# drop priviliges as soon as possible
|
||||
# NOTE: first change group than user
|
||||
Process::Sys.setgid(Etc.getgrnam(config['group']).gid) if config['group']
|
||||
Process::Sys.setuid(Etc.getpwnam(config['user']).uid) if config['user']
|
||||
if config['group']
|
||||
group = Etc.getgrnam(config['group'])
|
||||
Process::Sys.setgid(group.gid) if group
|
||||
end
|
||||
if config['user']
|
||||
user = Etc.getpwnam(config['user'])
|
||||
Process::Sys.setuid(user.uid) if user
|
||||
end
|
||||
|
||||
setup_traps
|
||||
|
||||
@@ -110,6 +132,8 @@ module Dyndnsd
|
||||
|
||||
private
|
||||
|
||||
# @param params [Hash{String => String}]
|
||||
# @return [Array{String}]
|
||||
def extract_v4_and_v6_address(params)
|
||||
return [] if !(params['myip'])
|
||||
begin
|
||||
@@ -121,6 +145,9 @@ module Dyndnsd
|
||||
end
|
||||
end
|
||||
|
||||
# @param env [Hash{String => String}]
|
||||
# @param params [Hash{String => String}]
|
||||
# @return [Array{String}]
|
||||
def extract_myips(env, params)
|
||||
# require presence of myip parameter as valid IPAddr (v4) and valid myip6
|
||||
return extract_v4_and_v6_address(params) if params.key?('myip6')
|
||||
@@ -135,6 +162,9 @@ module Dyndnsd
|
||||
[env['REMOTE_ADDR']]
|
||||
end
|
||||
|
||||
# @param hostnames [String]
|
||||
# @param myips [Array{String}]
|
||||
# @return [Array{Symbol}]
|
||||
def process_changes(hostnames, myips)
|
||||
changes = []
|
||||
Helper.span('process_changes') do |span|
|
||||
@@ -159,6 +189,7 @@ module Dyndnsd
|
||||
changes
|
||||
end
|
||||
|
||||
# @return [void]
|
||||
def update_db
|
||||
@db['serial'] += 1
|
||||
Dyndnsd.logger.info "Committing update ##{@db['serial']}"
|
||||
@@ -167,6 +198,8 @@ module Dyndnsd
|
||||
Metriks.meter('updates.committed').mark
|
||||
end
|
||||
|
||||
# @param env [Hash{String => String}]
|
||||
# @return [Array{Integer,Hash{String => String},Array{String}}]
|
||||
def handle_dyndns_request(env)
|
||||
params = Rack::Utils.parse_query(env['QUERY_STRING'])
|
||||
|
||||
@@ -205,6 +238,8 @@ module Dyndnsd
|
||||
|
||||
# SETUP
|
||||
|
||||
# @param config [Hash{String => Object}]
|
||||
# @return [void]
|
||||
private_class_method def self.setup_logger(config)
|
||||
if config['logfile']
|
||||
Dyndnsd.logger = Logger.new(config['logfile'])
|
||||
@@ -216,6 +251,7 @@ module Dyndnsd
|
||||
Dyndnsd.logger.formatter = LogFormatter.new
|
||||
end
|
||||
|
||||
# @return [void]
|
||||
private_class_method def self.setup_traps
|
||||
Signal.trap('INT') do
|
||||
Dyndnsd.logger.info 'Quitting...'
|
||||
@@ -227,6 +263,8 @@ module Dyndnsd
|
||||
end
|
||||
end
|
||||
|
||||
# @param config [Hash{String => Object}]
|
||||
# @return [void]
|
||||
private_class_method def self.setup_monitoring(config)
|
||||
# configure metriks
|
||||
if config['graphite']
|
||||
@@ -254,6 +292,8 @@ module Dyndnsd
|
||||
end
|
||||
end
|
||||
|
||||
# @param config [Hash{String => Object}]
|
||||
# @return [void]
|
||||
private_class_method def self.setup_tracing(config)
|
||||
# configure OpenTracing
|
||||
if config.dig('tracing', 'jaeger')
|
||||
@@ -266,14 +306,14 @@ module Dyndnsd
|
||||
host: host, port: port, service_name: service_name, flush_interval: 1
|
||||
)
|
||||
end
|
||||
# always use SpanManager
|
||||
OpenTracing.global_tracer = SpanManager::Tracer.new(OpenTracing.global_tracer)
|
||||
end
|
||||
|
||||
# @param config [Hash{String => Object}]
|
||||
# @return [void]
|
||||
private_class_method def self.setup_rack(config)
|
||||
# configure daemon
|
||||
db = Database.new(config['db'])
|
||||
updater = Updater::CommandWithBindZone.new(config['domain'], config['updater']['params']) if config['updater']['name'] == 'command_with_bind_zone'
|
||||
updater = Updater::CommandWithBindZone.new(config['domain'], config.dig('updater', 'params')) if config.dig('updater', 'name') == 'command_with_bind_zone'
|
||||
daemon = Daemon.new(config, db, updater)
|
||||
|
||||
# configure rack
|
||||
|
@@ -1,3 +1,4 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'forwardable'
|
||||
|
||||
@@ -7,19 +8,22 @@ module Dyndnsd
|
||||
|
||||
def_delegators :@db, :[], :[]=, :each, :has_key?
|
||||
|
||||
# @param db_file [String]
|
||||
def initialize(db_file)
|
||||
@db_file = db_file
|
||||
end
|
||||
|
||||
# @return [void]
|
||||
def load
|
||||
if File.file?(@db_file)
|
||||
@db = JSON.parse(File.open(@db_file, 'r', &:read))
|
||||
@db = JSON.parse(File.read(@db_file, mode: 'r'))
|
||||
else
|
||||
@db = {}
|
||||
end
|
||||
@db_hash = @db.hash
|
||||
end
|
||||
|
||||
# @return [void]
|
||||
def save
|
||||
Helper.span('database_save') do |_span|
|
||||
File.open(@db_file, 'w') { |f| JSON.dump(@db, f) }
|
||||
@@ -27,6 +31,7 @@ module Dyndnsd
|
||||
end
|
||||
end
|
||||
|
||||
# @return [Boolean]
|
||||
def changed?
|
||||
@db_hash != @db.hash
|
||||
end
|
||||
|
@@ -1,7 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Dyndnsd
|
||||
module Generator
|
||||
class Bind
|
||||
# @param domain [String]
|
||||
# @param config [Hash{String => Object}]
|
||||
def initialize(domain, config)
|
||||
@domain = domain
|
||||
@ttl = config['ttl']
|
||||
@@ -10,6 +13,8 @@ module Dyndnsd
|
||||
@additional_zone_content = config['additional_zone_content']
|
||||
end
|
||||
|
||||
# @param db [Dyndnsd::Database]
|
||||
# @return [String]
|
||||
def generate(db)
|
||||
out = []
|
||||
out << "$TTL #{@ttl}"
|
||||
|
@@ -1,8 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'ipaddr'
|
||||
|
||||
module Dyndnsd
|
||||
class Helper
|
||||
# @param hostname [String]
|
||||
# @param domain [String]
|
||||
# @return [Boolean]
|
||||
def self.fqdn_valid?(hostname, domain)
|
||||
return false if hostname.length < domain.length + 2
|
||||
return false if !hostname.end_with?(domain)
|
||||
@@ -11,6 +15,8 @@ module Dyndnsd
|
||||
true
|
||||
end
|
||||
|
||||
# @param ip [String]
|
||||
# @return [Boolean]
|
||||
def self.ip_valid?(ip)
|
||||
IPAddr.new(ip)
|
||||
true
|
||||
@@ -18,23 +24,45 @@ module Dyndnsd
|
||||
false
|
||||
end
|
||||
|
||||
# @param username [String]
|
||||
# @param password [String]
|
||||
# @param users [Hash]
|
||||
# @return [Boolean]
|
||||
def self.user_allowed?(username, password, users)
|
||||
(users.key? username) && (users[username]['password'] == password)
|
||||
end
|
||||
|
||||
# @param hostname [String]
|
||||
# @param myips [Array]
|
||||
# @param hosts [Hash]
|
||||
# @return [Boolean]
|
||||
def self.changed?(hostname, myips, hosts)
|
||||
# myips order is always deterministic
|
||||
((!hosts.include? hostname) || (hosts[hostname] != myips)) && !myips.empty?
|
||||
end
|
||||
|
||||
# @param operation [String]
|
||||
# @param block [Proc]
|
||||
# @return [void]
|
||||
def self.span(operation, &block)
|
||||
span = OpenTracing.start_span(operation)
|
||||
scope = OpenTracing.start_active_span(operation)
|
||||
span = scope.span
|
||||
span.set_tag('component', 'dyndnsd')
|
||||
span.set_tag('span.kind', 'server')
|
||||
begin
|
||||
block.call(span)
|
||||
rescue StandardError => e
|
||||
span.set_tag('error', true)
|
||||
span.log_kv(
|
||||
event: 'error',
|
||||
'error.kind': e.class.to_s,
|
||||
'error.object': e,
|
||||
message: e.message,
|
||||
stack: e.backtrace&.join("\n") || ''
|
||||
)
|
||||
raise
|
||||
ensure
|
||||
span.finish
|
||||
scope.close
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@@ -1,11 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Dyndnsd
|
||||
module Responder
|
||||
class DynDNSStyle
|
||||
# @param app [#call]
|
||||
def initialize(app)
|
||||
@app = app
|
||||
end
|
||||
|
||||
# @param env [Hash{String => String}]
|
||||
# @return [Array{Integer,Hash{String => String},Array{String}}]
|
||||
def call(env)
|
||||
@app.call(env).tap do |status_code, headers, body|
|
||||
if headers.key?('X-DynDNS-Response')
|
||||
@@ -18,6 +22,10 @@ module Dyndnsd
|
||||
|
||||
private
|
||||
|
||||
# @param status_code [Integer]
|
||||
# @param headers [Hash{String => String}]
|
||||
# @param body [Array{String}]
|
||||
# @return [Array{Integer,Hash{String => String},Array{String}}]
|
||||
def decorate_dyndnsd_response(status_code, headers, body)
|
||||
if status_code == 200
|
||||
[200, {'Content-Type' => 'text/plain'}, [get_success_body(body[0], body[1])]]
|
||||
@@ -26,6 +34,10 @@ module Dyndnsd
|
||||
end
|
||||
end
|
||||
|
||||
# @param status_code [Integer]
|
||||
# @param headers [Hash{String => String}]
|
||||
# @param _body [Array{String}]
|
||||
# @return [Array{Integer,Hash{String => String},Array{String}}]
|
||||
def decorate_other_response(status_code, headers, _body)
|
||||
if status_code == 400
|
||||
[status_code, headers, ['Bad Request']]
|
||||
@@ -34,10 +46,14 @@ module Dyndnsd
|
||||
end
|
||||
end
|
||||
|
||||
# @param changes [Array{Symbol}]
|
||||
# @param myips [Array{String}]
|
||||
# @return [String]
|
||||
def get_success_body(changes, myips)
|
||||
changes.map { |change| "#{change} #{myips.join(' ')}" }.join("\n")
|
||||
end
|
||||
|
||||
# @return [Hash{String => Object}]
|
||||
def error_response_map
|
||||
{
|
||||
# general http errors
|
||||
|
@@ -1,11 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Dyndnsd
|
||||
module Responder
|
||||
class RestStyle
|
||||
# @param app [#call]
|
||||
def initialize(app)
|
||||
@app = app
|
||||
end
|
||||
|
||||
# @param env [Hash{String => String}]
|
||||
# @return [Array{Integer,Hash{String => String},Array{String}}]
|
||||
def call(env)
|
||||
@app.call(env).tap do |status_code, headers, body|
|
||||
if headers.key?('X-DynDNS-Response')
|
||||
@@ -18,6 +22,10 @@ module Dyndnsd
|
||||
|
||||
private
|
||||
|
||||
# @param status_code [Integer]
|
||||
# @param headers [Hash{String => String}]
|
||||
# @param body [Array{String}]
|
||||
# @return [Array{Integer,Hash{String => String},Array{String}}]
|
||||
def decorate_dyndnsd_response(status_code, headers, body)
|
||||
if status_code == 200
|
||||
[200, {'Content-Type' => 'text/plain'}, [get_success_body(body[0], body[1])]]
|
||||
@@ -26,6 +34,10 @@ module Dyndnsd
|
||||
end
|
||||
end
|
||||
|
||||
# @param status_code [Integer]
|
||||
# @param headers [Hash{String => String}]
|
||||
# @param _body [Array{String}]
|
||||
# @return [Array{Integer,Hash{String => String},Array{String}}]
|
||||
def decorate_other_response(status_code, headers, _body)
|
||||
if status_code == 400
|
||||
[status_code, headers, ['Bad Request']]
|
||||
@@ -34,10 +46,14 @@ module Dyndnsd
|
||||
end
|
||||
end
|
||||
|
||||
# @param changes [Array{Symbol}]
|
||||
# @param myips [Array{String}]
|
||||
# @return [String]
|
||||
def get_success_body(changes, myips)
|
||||
changes.map { |change| change == :good ? "Changed to #{myips.join(' ')}" : "No change needed for #{myips.join(' ')}" }.join("\n")
|
||||
end
|
||||
|
||||
# @return [Hash{String => Object}]
|
||||
def error_response_map
|
||||
{
|
||||
# general http errors
|
||||
|
@@ -1,3 +1,4 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Adapted from https://github.com/eric/metriks-graphite/blob/master/lib/metriks/reporter/graphite.rb
|
||||
|
||||
@@ -5,8 +6,11 @@ require 'metriks'
|
||||
|
||||
module Dyndnsd
|
||||
class TextfileReporter
|
||||
# @return [String]
|
||||
attr_reader :file
|
||||
|
||||
# @param file [String]
|
||||
# @param options [Hash{Symbol => Object}]
|
||||
def initialize(file, options = {})
|
||||
@file = file
|
||||
|
||||
@@ -17,6 +21,7 @@ module Dyndnsd
|
||||
@on_error = options[:on_error] || proc { |ex| }
|
||||
end
|
||||
|
||||
# @return [void]
|
||||
def start
|
||||
@thread ||= Thread.new do
|
||||
loop do
|
||||
@@ -33,16 +38,19 @@ module Dyndnsd
|
||||
end
|
||||
end
|
||||
|
||||
# @return [void]
|
||||
def stop
|
||||
@thread&.kill
|
||||
@thread = nil
|
||||
end
|
||||
|
||||
# @return [void]
|
||||
def restart
|
||||
stop
|
||||
start
|
||||
end
|
||||
|
||||
# @return [void]
|
||||
def write
|
||||
File.open(@file, 'w') do |f|
|
||||
@registry.each do |name, metric|
|
||||
@@ -85,6 +93,12 @@ module Dyndnsd
|
||||
end
|
||||
end
|
||||
|
||||
# @param file [String]
|
||||
# @param base_name [String]
|
||||
# @param metric [Object]
|
||||
# @param keys [Array{Symbol}]
|
||||
# @param snapshot_keys [Array{Symbol}]
|
||||
# @return [void]
|
||||
def write_metric(file, base_name, metric, keys, snapshot_keys = [])
|
||||
time = Time.now.to_i
|
||||
|
||||
|
@@ -1,26 +1,31 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Dyndnsd
|
||||
module Updater
|
||||
class CommandWithBindZone
|
||||
# @param domain [String]
|
||||
# @param config [Hash{String => Object}]
|
||||
def initialize(domain, config)
|
||||
@zone_file = config['zone_file']
|
||||
@command = config['command']
|
||||
@generator = Generator::Bind.new(domain, config)
|
||||
end
|
||||
|
||||
def update(zone)
|
||||
# @param db [Dyndnsd::Database]
|
||||
# @return [void]
|
||||
def update(db)
|
||||
Helper.span('updater_update') do |span|
|
||||
span.set_tag('dyndnsd.updater.name', self.class.name.split('::').last)
|
||||
span.set_tag('dyndnsd.updater.name', self.class.name&.split('::')&.last || 'None')
|
||||
|
||||
# write zone file in bind syntax
|
||||
File.open(@zone_file, 'w') { |f| f.write(@generator.generate(zone)) }
|
||||
File.open(@zone_file, 'w') { |f| f.write(@generator.generate(db)) }
|
||||
# call user-defined command
|
||||
pid = fork do
|
||||
exec @command
|
||||
end
|
||||
|
||||
# detach so children don't become zombies
|
||||
Process.detach(pid)
|
||||
Process.detach(pid) if pid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@@ -1,4 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Dyndnsd
|
||||
VERSION = '2.0.0.rc2'.freeze
|
||||
VERSION = '2.2.0'
|
||||
end
|
||||
|
@@ -1,4 +1,6 @@
|
||||
require 'spec_helper'
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'spec_helper'
|
||||
|
||||
describe Dyndnsd::Daemon do
|
||||
include Rack::Test::Methods
|
||||
|
@@ -1,8 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubygems'
|
||||
require 'bundler/setup'
|
||||
require 'rack/test'
|
||||
|
||||
require 'dyndnsd'
|
||||
require 'support/dummy_database'
|
||||
require 'support/dummy_updater'
|
||||
|
||||
require_relative 'support/dummy_database'
|
||||
require_relative 'support/dummy_updater'
|
||||
|
@@ -1,3 +1,4 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'forwardable'
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Dyndnsd
|
||||
module Updater
|
||||
|
Reference in New Issue
Block a user