boring_services 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e386da541954a876687c37adade51199a571e5b75bc5583946a676a42a69e5e0
4
- data.tar.gz: bb16f6517193ad18614eb91632c462e031856d06ac9d73abd25675698df50f43
3
+ metadata.gz: 156bb67ea987370602ed678f3d8a525f6eeae09dcbc2a3ae8ac23fa85b474ca4
4
+ data.tar.gz: 8ce1325011cac932711b77afed6a5d0307bebedaa4e52fc2b65d9a153daf2e91
5
5
  SHA512:
6
- metadata.gz: 87986c7987ff4b9ee2490fe4ca8035ad536a90b571d2d9cdcf0d995c93a4e8d7e8c2836a2c4fa99fcf2137c1096aad91c166b300b1ff4a88df817220e29aaa51
7
- data.tar.gz: ae84eb250c619ceb130e90db62fb4538939ee22755700e42231164f3c012ab67df397a5ad6f927534862827b346cf00f82b563b154a97ea698835f798275e8cc
6
+ metadata.gz: 72bf5746276b30243a9b75b8091491afd3e58e5e8feac6b3a8f62bc7cafa8f9088d6d1f279f075b6f3f77d0f350017a4bf277e18a3ef4844d273293fb2f3b363
7
+ data.tar.gz: 07ac07fe8b309a06ff23ca77a7681aab03c77d8ecc0fac5e86e6723c999eb05976827e2bafdd072f7dfc0eef700c731329402a627b6ab64d2492983aaf6d125f
@@ -5,6 +5,12 @@ begin
5
5
  class Railtie < Rails::Railtie
6
6
  railtie_name :boring_services
7
7
 
8
+ # Initialize service locator after Rails config is loaded
9
+ initializer 'boring_services.initialize' do
10
+ # Reset locator so it picks up the correct Rails environment
11
+ BoringServices.reset_locator!
12
+ end
13
+
8
14
  rake_tasks do
9
15
  load File.expand_path('../tasks/services.rake', __dir__)
10
16
  end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BoringServices
4
+ class ServiceLocator
5
+ attr_reader :config
6
+
7
+ def initialize(config = nil)
8
+ @config = config || Configuration.load
9
+ end
10
+
11
+ # Get all hosts for a service
12
+ # Returns array of host hashes with :host, :private_ip, :label keys
13
+ def hosts_for(service_name)
14
+ service = config.service_config(service_name.to_s)
15
+ return [] unless service
16
+
17
+ normalize_hosts(service['hosts'] || [])
18
+ end
19
+
20
+ # Get the connection IP for a service by region/label
21
+ # Prefers private_ip, falls back to host
22
+ # Label matching: "redis-eu" matches region "eu", "redis-us" matches "us"
23
+ def host_for_region(service_name, region)
24
+ return nil if region.to_s.strip.empty?
25
+
26
+ hosts = hosts_for(service_name)
27
+ host_entry = hosts.find do |h|
28
+ label = h[:label].to_s.downcase
29
+ region_str = region.to_s.downcase
30
+ label == region_str || label.end_with?("-#{region_str}") || label.start_with?("#{region_str}-")
31
+ end
32
+
33
+ return nil unless host_entry
34
+
35
+ connection_ip(host_entry)
36
+ end
37
+
38
+ # Get the first available host for a service (prefers private_ip)
39
+ def primary_host(service_name)
40
+ hosts = hosts_for(service_name)
41
+ return nil if hosts.empty?
42
+
43
+ connection_ip(hosts.first)
44
+ end
45
+
46
+ # Get all connection IPs for a service (prefers private_ip for each)
47
+ def all_hosts(service_name)
48
+ hosts_for(service_name).map { |h| connection_ip(h) }
49
+ end
50
+
51
+ # Get port for a service
52
+ def port_for(service_name)
53
+ service = config.service_config(service_name.to_s)
54
+ service&.dig('port')
55
+ end
56
+
57
+ # Build a Redis URL for a region
58
+ def redis_url(region: nil, password: nil, db: 0)
59
+ host = region ? host_for_region('redis', region) : primary_host('redis')
60
+ return nil unless host
61
+
62
+ port = port_for('redis') || 6379
63
+ auth = password.to_s.empty? ? '' : ":#{password}@"
64
+ "redis://#{auth}#{host}:#{port}/#{db}"
65
+ end
66
+
67
+ # Build memcached connection string (host:port,host:port format)
68
+ def memcached_servers(region: nil)
69
+ hosts = if region
70
+ host = host_for_region('memcached', region)
71
+ host ? [host] : []
72
+ else
73
+ all_hosts('memcached')
74
+ end
75
+
76
+ return nil if hosts.empty?
77
+
78
+ port = port_for('memcached') || 11211
79
+ hosts.map { |h| "#{h}:#{port}" }.join(',')
80
+ end
81
+
82
+ private
83
+
84
+ # Normalize hosts array - handles both simple strings and hashes
85
+ def normalize_hosts(hosts)
86
+ hosts.map do |h|
87
+ if h.is_a?(Hash)
88
+ {
89
+ host: h['host'],
90
+ private_ip: h['private_ip'],
91
+ label: h['label']
92
+ }
93
+ else
94
+ { host: h.to_s, private_ip: nil, label: nil }
95
+ end
96
+ end
97
+ end
98
+
99
+ # Get connection IP - prefer private_ip if available
100
+ def connection_ip(host_entry)
101
+ ip = host_entry[:private_ip].to_s.strip
102
+ ip.empty? ? host_entry[:host] : ip
103
+ end
104
+ end
105
+ end
@@ -33,9 +33,10 @@ module BoringServices
33
33
  memory = memory_mb || 256
34
34
  listen_port = port || 6379
35
35
  password = resolve_secret('redis_password') if config.secrets['redis_password']
36
+ bind_address = private_ip.to_s.strip.empty? ? "0.0.0.0" : "127.0.0.1 #{private_ip}"
36
37
 
37
38
  config_content = <<~REDIS
38
- bind 0.0.0.0
39
+ bind #{bind_address}
39
40
  port #{listen_port}
40
41
  dir /var/lib/redis
41
42
  maxmemory #{memory}mb
@@ -1,3 +1,3 @@
1
1
  module BoringServices
2
- VERSION = '0.3.0'.freeze
2
+ VERSION = '0.4.0'.freeze
3
3
  end
@@ -5,6 +5,7 @@ require_relative 'boring_services/cli'
5
5
  require_relative 'boring_services/installer'
6
6
  require_relative 'boring_services/ssh_executor'
7
7
  require_relative 'boring_services/health_checker'
8
+ require_relative 'boring_services/service_locator'
8
9
 
9
10
  require_relative 'boring_services/services/base'
10
11
  require_relative 'boring_services/services/memcached'
@@ -15,14 +16,68 @@ require_relative 'boring_services/services/nginx'
15
16
  module BoringServices
16
17
  class Error < StandardError; end
17
18
 
18
- def self.root
19
- File.expand_path('..', __dir__)
20
- end
19
+ class << self
20
+ def root
21
+ File.expand_path('..', __dir__)
22
+ end
23
+
24
+ def status
25
+ config = Configuration.load
26
+ health_checker = HealthChecker.new(config)
27
+ health_checker.check_all
28
+ end
29
+
30
+ # Service locator instance (cached)
31
+ def locator
32
+ @locator ||= ServiceLocator.new
33
+ end
34
+
35
+ # Reset cached locator (useful for testing or config reload)
36
+ def reset_locator!
37
+ @locator = nil
38
+ end
39
+
40
+ # Convenience methods - delegate to locator
41
+
42
+ # Get Redis host for a region (e.g., "eu", "us")
43
+ def redis_host(region = nil)
44
+ region ? locator.host_for_region('redis', region) : locator.primary_host('redis')
45
+ end
46
+
47
+ # Get Redis port
48
+ def redis_port
49
+ locator.port_for('redis') || 6379
50
+ end
51
+
52
+ # Build Redis URL
53
+ def redis_url(region: nil, password: nil, db: 0)
54
+ locator.redis_url(region: region, password: password, db: db)
55
+ end
56
+
57
+ # Get Memcached host for a region
58
+ def memcached_host(region = nil)
59
+ region ? locator.host_for_region('memcached', region) : locator.primary_host('memcached')
60
+ end
61
+
62
+ # Get Memcached port
63
+ def memcached_port
64
+ locator.port_for('memcached') || 11211
65
+ end
66
+
67
+ # Get Memcached servers string (host:port,host:port)
68
+ def memcached_servers(region: nil)
69
+ locator.memcached_servers(region: region)
70
+ end
71
+
72
+ # Generic: get host for any service by region
73
+ def host_for(service, region = nil)
74
+ region ? locator.host_for_region(service, region) : locator.primary_host(service)
75
+ end
21
76
 
22
- def self.status
23
- config = Configuration.load
24
- health_checker = HealthChecker.new(config)
25
- health_checker.check_all
77
+ # Generic: get port for any service
78
+ def port_for(service)
79
+ locator.port_for(service)
80
+ end
26
81
  end
27
82
  end
28
83
  require 'stringio'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: boring_services
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - BoringCache
@@ -127,6 +127,7 @@ files:
127
127
  - lib/boring_services/installer.rb
128
128
  - lib/boring_services/railtie.rb
129
129
  - lib/boring_services/secrets.rb
130
+ - lib/boring_services/service_locator.rb
130
131
  - lib/boring_services/services/base.rb
131
132
  - lib/boring_services/services/haproxy.rb
132
133
  - lib/boring_services/services/memcached.rb