diff --git a/kuby.rb b/kuby.rb index 893af18..424228e 100644 --- a/kuby.rb +++ b/kuby.rb @@ -69,17 +69,23 @@ add "RAILS_LOG_LEVEL", "debug" add "DATABASE_URL", app_creds[:database_url] add "REDIS_URL", app_creds[:redis_url] - add "ACTION_CABLE_ADAPTER", "redis" + add "ACTION_CABLE_ADAPTER", "any_cable" add "PROMETHEUS_EXPORTER_PORT", METRICS_PORT.to_s end end end - add_plugin :anycable do + add_plugin :anycable_rpc do metrics_port METRICS_PORT replicas 2 end + add_plugin :anycable_go do + metrics_port METRICS_PORT + replicas 2 + redis_url app_creds[:redis_url] + end + provider :digitalocean do access_token app_creds[:do_token] cluster_id app_creds[:do_cluster_id] diff --git a/lib/kuby/anycable.rb b/lib/kuby/anycable.rb index 65567ea..547c602 100644 --- a/lib/kuby/anycable.rb +++ b/lib/kuby/anycable.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true require "kuby" -require "kuby/anycable/plugin" +require "kuby/anycable/rpc_plugin" +require "kuby/anycable/go_plugin" -Kuby.register_plugin(:anycable, Kuby::AnyCable::Plugin) +Kuby.register_plugin(:anycable_rpc, Kuby::AnyCable::RPCPlugin) +Kuby.register_plugin(:anycable_go, Kuby::AnyCable::GoPlugin) diff --git a/lib/kuby/anycable/go_plugin.rb b/lib/kuby/anycable/go_plugin.rb new file mode 100644 index 0000000..b382e00 --- /dev/null +++ b/lib/kuby/anycable/go_plugin.rb @@ -0,0 +1,264 @@ +# frozen_string_literal: true + +module Kuby + module AnyCable + class GoPlugin < ::Kuby::Plugin + extend ::KubeDSL::ValueFields + + ROLE = "ws" + + value_fields :replicas + value_fields :redis_url + value_fields :metrics_port + value_fields :rpc_host + value_fields :ws_path + value_fields :image + value_fields :hostname + + DEFAULT_IMAGE = "anycable/anycable-go:1.1" + + attr_reader :port + + def after_initialize + @replicas = 1 + @metrics_port = nil + @rpc_host = nil + @redis_url = nil + @ws_path = "/cable" + @port = 8081 + @hostname = nil + @image = DEFAULT_IMAGE + end + + def after_configuration + if rpc_spec && rpc_host.nil? + config_map.data.add("ANYCABLE_RPC_HOST", "dns:///#{rpc_spec.service.metadata.name}:50051") + end + + if rails_spec + @hostname ||= rails_spec.hostname + configure_ingress(rails_spec.ingress, hostname) + end + end + + def configure(&block) + instance_eval(&block) if block + end + + def resources + @resources ||= [ + service_account, + service, + config_map, + deployment + ] + end + + def configure_ingress(ingress, hostname) + spec = self + + ingress.spec.rule do + host hostname + + http do + path do + path spec.ws_path + + backend do + service_name spec.service.metadata.name + service_port spec.service.spec.ports.first.port + end + end + end + end + end + + def service_account(&block) + context = self + + @service_account ||= KubeDSL.service_account do + metadata do + name "#{context.selector_app}-#{ROLE}-sa" + namespace context.namespace.metadata.name + + labels do + add :app, context.selector_app + add :role, ROLE + end + end + end + + @service_account.instance_eval(&block) if block + @service_account + end + + def service(&block) + spec = self + + @service ||= KubeDSL.service do + metadata do + name "#{spec.selector_app}-#{ROLE}-svc" + namespace spec.namespace.metadata.name + + labels do + add :app, spec.selector_app + add :role, ROLE + end + end + + spec do + type "ClusterIP" + + selector do + add :app, spec.selector_app + add :role, ROLE + end + + port do + name "http" + port spec.port + protocol "TCP" + target_port "http" + end + + if spec.metrics_port + port do + name "metrics" + port spec.metrics_port + protocol "TCP" + target_port "metrics" + end + end + end + end + + @service.instance_eval(&block) if block + @service + end + + def deployment(&block) + context = self + + @deployment ||= KubeDSL.deployment do + metadata do + name "#{context.selector_app}-#{ROLE}" + namespace context.namespace.metadata.name + + labels do + add :app, context.selector_app + add :role, ROLE + end + end + + spec do + replicas context.replicas + + selector do + match_labels do + add :app, context.selector_app + add :role, ROLE + end + end + + strategy do + type "RollingUpdate" + + rolling_update do + max_surge "25%" + max_unavailable 0 + end + end + + template do + metadata do + labels do + add :app, context.selector_app + add :role, ROLE + end + end + + spec do + container(:ws) do + name "#{context.selector_app}-#{ROLE}" + image context.image + image_pull_policy "IfNotPresent" + + port do + container_port context.port + name "http" + protocol "TCP" + end + + if context.metrics_port + port do + container_port context.metrics_port + name "metrics" + protocol "TCP" + end + end + + env_from do + config_map_ref do + name context.config_map.metadata.name + end + end + end + + image_pull_secret do + name context.kubernetes.registry_secret.metadata.name + end + + restart_policy "Always" + service_account_name context.service_account.metadata.name + end + end + end + end + + @deployment.instance_eval(&block) if block + @deployment + end + + def config_map(&block) + spec = self + + @config_map ||= KubeDSL.config_map do + metadata do + name "#{spec.selector_app}-#{ROLE}-config" + namespace spec.namespace.metadata.name + end + + data do + if spec.rpc_host + add "ANYCABLE_RPC_HOST", spec.rpc_host + end + add "ANYCABLE_REDIS_URL", spec.redis_url + add "ANYCABLE_HOST", "0.0.0.0" + add "ANYCABLE_PORT", spec.port.to_s + end + end + + @config_map.instance_eval(&block) if block + @config_map + end + + alias_method :env, :config_map + + delegate :kubernetes, to: :environment + + delegate :docker, to: :environment + + delegate :selector_app, to: :kubernetes + + delegate :namespace, to: :kubernetes + + def rpc_spec + @rpc_spec ||= kubernetes.plugin(:anycable_rpc) + end + + def rails_spec + @rails_spec ||= kubernetes.plugin(:rails_app) + end + end + end +end diff --git a/lib/kuby/anycable/plugin.rb b/lib/kuby/anycable/rpc_plugin.rb similarity index 95% rename from lib/kuby/anycable/plugin.rb rename to lib/kuby/anycable/rpc_plugin.rb index 44f6463..54297ed 100644 --- a/lib/kuby/anycable/plugin.rb +++ b/lib/kuby/anycable/rpc_plugin.rb @@ -2,7 +2,7 @@ module Kuby module AnyCable - class Plugin < ::Kuby::Plugin + class RPCPlugin < ::Kuby::Plugin extend ::KubeDSL::ValueFields ROLE = "rpc" @@ -21,10 +21,10 @@ def after_initialize end def after_configuration - return unless rails_app + return unless rails_spec deployment.spec.template.spec.container(:rpc).merge!( - rails_app.deployment.spec.template.spec.container(:web), fields: [:env_from] + rails_spec.deployment.spec.template.spec.container(:web), fields: [:env_from] ) end @@ -216,7 +216,7 @@ def config_map(&block) data do add "ANYCABLE_RPC_HOST", "0.0.0.0:50051" if spec.redis_url - add "ANYCABLE_REDIS_URL", "0.0.0.0:50051" + add "ANYCABLE_REDIS_URL", spec.redis_url end add "ANYCABLE_RPC_SERVER_ARGS__MAX_CONNECTION_AGE_MS", spec.max_connection_age.to_s @@ -240,8 +240,8 @@ def config_map(&block) delegate :namespace, to: :kubernetes - def rails_app - kubernetes.plugin(:rails_app) + def rails_spec + @rails_spec ||= kubernetes.plugin(:rails_app) end end end