boring_services 0.3.0 → 0.5.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: 1cf51b8c8239ca7c000dc2fabe043321c3a490070e762741eaa44307fa521047
4
+ data.tar.gz: cfa55da92f539c5d60e444f1f7f0d0a95af80a96194112c6754303fda8b7cf43
5
5
  SHA512:
6
- metadata.gz: 87986c7987ff4b9ee2490fe4ca8035ad536a90b571d2d9cdcf0d995c93a4e8d7e8c2836a2c4fa99fcf2137c1096aad91c166b300b1ff4a88df817220e29aaa51
7
- data.tar.gz: ae84eb250c619ceb130e90db62fb4538939ee22755700e42231164f3c012ab67df397a5ad6f927534862827b346cf00f82b563b154a97ea698835f798275e8cc
6
+ metadata.gz: 668cc2b9f6e65930f5d7e6916dbe33ca42522e0e4139aef025c4918f328c7d73345bd449cdea3bc5bf0368fdd512268a959f92d2a87f2012d5c46d4d7ceaf782
7
+ data.tar.gz: d5b0158106e76199da437fa08fb6375a103614e68b884c451730b417c0879109ad0ff278ba0b27d635ef45843e7ff9006f6092c1d03740fa949d056f115e0900
@@ -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,108 @@
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
+ services = config.services.select { |s| s['name'] == service_name.to_s }
15
+ return [] if services.empty?
16
+
17
+ services.flat_map { |service| normalize_service_hosts(service) }
18
+ end
19
+
20
+ # Get host by exact label match
21
+ def host_by_label(service_name, label)
22
+ hosts = hosts_for(service_name)
23
+ host_entry = hosts.find { |h| h[:label] == label.to_s }
24
+ return nil unless host_entry
25
+
26
+ connection_ip(host_entry)
27
+ end
28
+
29
+ # Get all hosts as a hash keyed by label
30
+ # { "redis-eu-gcp" => "10.8.0.10", "redis-us-aws" => "10.8.0.60" }
31
+ def hosts_by_label(service_name)
32
+ hosts_for(service_name).each_with_object({}) do |h, hash|
33
+ hash[h[:label]] = connection_ip(h) if h[:label]
34
+ end
35
+ end
36
+
37
+ # Get all connection IPs for a service (prefers private_ip for each)
38
+ def all_ips(service_name)
39
+ hosts_for(service_name).map { |h| connection_ip(h) }
40
+ end
41
+
42
+ # Get port for a service
43
+ def port_for(service_name)
44
+ service = config.service_config(service_name.to_s)
45
+ service&.dig('port')
46
+ end
47
+
48
+ # Build a Redis URL for a specific label
49
+ def redis_url(label: nil, password: nil, db: 0)
50
+ host = label ? host_by_label('redis', label) : all_ips('redis').first
51
+ return nil unless host
52
+
53
+ port = port_for('redis') || 6379
54
+ auth = password.to_s.empty? ? '' : ":#{password}@"
55
+ "redis://#{auth}#{host}:#{port}/#{db}"
56
+ end
57
+
58
+ # Build memcached connection string (host:port,host:port format)
59
+ def memcached_servers(label: nil)
60
+ hosts = if label
61
+ host = host_by_label('memcached', label)
62
+ host ? [host] : []
63
+ else
64
+ all_ips('memcached')
65
+ end
66
+
67
+ return nil if hosts.empty?
68
+
69
+ port = port_for('memcached') || 11211
70
+ hosts.map { |h| "#{h}:#{port}" }.join(',')
71
+ end
72
+
73
+ private
74
+
75
+ # Normalize hosts from a service config
76
+ # Handles: single host:, array hosts:, or hosts: as array of hashes
77
+ def normalize_service_hosts(service)
78
+ # Single host entry (production style)
79
+ if service['host']
80
+ return [{
81
+ host: service['host'],
82
+ private_ip: service['private_ip'],
83
+ label: service['label']
84
+ }]
85
+ end
86
+
87
+ # Array of hosts
88
+ hosts = service['hosts'] || []
89
+ hosts.map do |h|
90
+ if h.is_a?(Hash)
91
+ {
92
+ host: h['host'],
93
+ private_ip: h['private_ip'],
94
+ label: h['label']
95
+ }
96
+ else
97
+ { host: h.to_s, private_ip: nil, label: nil }
98
+ end
99
+ end
100
+ end
101
+
102
+ # Get connection IP - prefer private_ip if available
103
+ def connection_ip(host_entry)
104
+ ip = host_entry[:private_ip].to_s.strip
105
+ ip.empty? ? host_entry[:host] : ip
106
+ end
107
+ end
108
+ 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.5.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,83 @@ 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 all Redis hosts as hash { label => private_ip }
43
+ def redis_hosts
44
+ locator.hosts_by_label('redis')
45
+ end
46
+
47
+ # Get Redis host by exact label
48
+ def redis_host(label)
49
+ locator.host_by_label('redis', label)
50
+ end
51
+
52
+ # Get Redis port
53
+ def redis_port
54
+ locator.port_for('redis') || 6379
55
+ end
56
+
57
+ # Build Redis URL for a label
58
+ def redis_url(label: nil, password: nil, db: 0)
59
+ locator.redis_url(label: label, password: password, db: db)
60
+ end
61
+
62
+ # Get all Memcached hosts as hash { label => private_ip }
63
+ def memcached_hosts
64
+ locator.hosts_by_label('memcached')
65
+ end
66
+
67
+ # Get Memcached host by exact label
68
+ def memcached_host(label)
69
+ locator.host_by_label('memcached', label)
70
+ end
71
+
72
+ # Get Memcached port
73
+ def memcached_port
74
+ locator.port_for('memcached') || 11211
75
+ end
76
+
77
+ # Get Memcached servers string (host:port,host:port)
78
+ def memcached_servers(label: nil)
79
+ locator.memcached_servers(label: label)
80
+ end
81
+
82
+ # Generic: get all hosts for any service as hash { label => ip }
83
+ def hosts_for(service)
84
+ locator.hosts_by_label(service)
85
+ end
86
+
87
+ # Generic: get host by exact label for any service
88
+ def host_for(service, label)
89
+ locator.host_by_label(service, label)
90
+ end
21
91
 
22
- def self.status
23
- config = Configuration.load
24
- health_checker = HealthChecker.new(config)
25
- health_checker.check_all
92
+ # Generic: get port for any service
93
+ def port_for(service)
94
+ locator.port_for(service)
95
+ end
26
96
  end
27
97
  end
28
98
  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.5.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