From f7e4e37cf5abc42c5725f895a5c9a6324d502cd1 Mon Sep 17 00:00:00 2001 From: Sarah Loui Date: Wed, 13 Sep 2023 00:05:44 -1000 Subject: [PATCH] Refactor runCustom. Create ServiceRun and InstanceOnDemand classes --- lib/cove.rb | 1 + lib/cove/cli/service.rb | 26 ++---------- lib/cove/command/builder.rb | 8 +++- lib/cove/instance_on_demand.rb | 36 +++++++++++++++++ lib/cove/invocation.rb | 1 + lib/cove/invocation/service_run.rb | 63 ++++++++++++++++++++++++++++++ spec/cove/cli/service_spec.rb | 16 +++++--- 7 files changed, 120 insertions(+), 31 deletions(-) create mode 100644 lib/cove/instance_on_demand.rb create mode 100644 lib/cove/invocation/service_run.rb diff --git a/lib/cove.rb b/lib/cove.rb index 5a93e9c..a8ff40a 100644 --- a/lib/cove.rb +++ b/lib/cove.rb @@ -17,6 +17,7 @@ require_relative "cove/role" require_relative "cove/deployment" require_relative "cove/instance" +require_relative "cove/instance_on_demand" require_relative "cove/entity_labels" require_relative "cove/environment_file" require_relative "cove/configuration" diff --git a/lib/cove/cli/service.rb b/lib/cove/cli/service.rb index ac3c101..91990cb 100644 --- a/lib/cove/cli/service.rb +++ b/lib/cove/cli/service.rb @@ -38,11 +38,10 @@ def ps(service_name) desc "run SERVICE with COMMANDS", "Run a container with custom commands for SERVICE" option :role - option :instance option :host - def runCustom(service_name, custom_command) + def runCustom(service_name, command) service = Cove.registry.services[service_name] - custom_cmd = custom_command.split + command = command.split role = if options[:role] Cove.registry.roles_for_service(service).bsearch { |x| x.name == options[:role] } @@ -56,26 +55,7 @@ def runCustom(service_name, custom_command) role.hosts.first end - index = options[:instance] || 1 - - Cove.output.puts "service: #{service_name}, role: #{role.name}, host: #{host.name}, instance: #{index}, commands: #{custom_command}." - - version = Digest::SHA2.hexdigest([role.id, role.image, custom_cmd, role.environment_variables, []].to_json)[0..12] - deployment = Cove::Deployment.new(role, version: version) - instance = Cove::Instance.new(deployment, index) - desired_container = Cove::DesiredContainer.from(instance) - desired_container.command = custom_cmd - desired_container.ports = [] - - ssh_cmd = ["ssh", "-t", host.ssh_destination_string] - run_cmd = Cove::Command::Builder.run_container(desired_container) - - cmd = ssh_cmd + run_cmd - - run_locally do - info "Connecting to host #{host.name} and running container #{desired_container.name} via #{cmd.join(" ")}" - Kernel.exec(*cmd) - end + Cove::Invocation::ServiceRun.new(registry: Cove.registry, service: service, custom_cmd: command, role: role, host: host).invoke end end end diff --git a/lib/cove/command/builder.rb b/lib/cove/command/builder.rb index 3d1d671..b41e0ac 100644 --- a/lib/cove/command/builder.rb +++ b/lib/cove/command/builder.rb @@ -7,6 +7,10 @@ def self.start_container(*containers) [:docker, "container", "start", *containers.flatten] end + def self.start_attached_container(container) + [:docker, "container", "start", "--attach", "-i", container] + end + # @param [String] containers The name or id of the container(s) to stop # @param [Integer] time def self.stop_container(*containers, time: nil) @@ -20,8 +24,8 @@ def self.delete_container(*containers) # @param [Cove::DesiredContainer] config # @return [Array] The command to create the container - def self.create_container(config) - Docker::Container::Create.build(image: config.image, name: config.name, labels: config.labels, command: config.command, environment_files: config.environment_files, ports: config.ports, mounts: config.mounts) + def self.create_container(config, remove: false, interactive: false) + Docker::Container::Create.build(image: config.image, name: config.name, remove: remove, interactive: interactive, labels: config.labels, command: config.command, environment_files: config.environment_files, ports: config.ports, mounts: config.mounts) end # @param [Cove::DesiredContainer] config diff --git a/lib/cove/instance_on_demand.rb b/lib/cove/instance_on_demand.rb new file mode 100644 index 0000000..0474010 --- /dev/null +++ b/lib/cove/instance_on_demand.rb @@ -0,0 +1,36 @@ +module Cove + class InstanceOnDemand + # @return [Cove::Deployment] + attr_reader :deployment + # @return [Cove::Role] + delegate :role, to: :deployment + # @return [Cove::Service] + delegate :service, to: :role + # @return [String] The version of the deployment + delegate :version, to: :deployment + # @return [String] The command to run in the container + attr_reader :command + # @return [Array] The port mapping to run in the container + attr_reader :ports + # @return [Array] The volumes to mount to the container + delegate :mounts, to: :role + # @return [String] The image of the container + delegate :image, to: :role + # @return [Cove::EntityLabels] The labels of the container + delegate :labels, to: :deployment + + # @param deployment [Cove::Deployment] The deployment the container is part of + def initialize(deployment, command) + @deployment = deployment + @command = command + @ports = [] + end + + def name + "#{service.name}-#{role.name}-#{version}" + end + + def labels + end + end +end diff --git a/lib/cove/invocation.rb b/lib/cove/invocation.rb index bf46950..5e6936a 100644 --- a/lib/cove/invocation.rb +++ b/lib/cove/invocation.rb @@ -1,3 +1,4 @@ require_relative "invocation/service_up" require_relative "invocation/service_down" require_relative "invocation/service_ps" +require_relative "invocation/service_run" diff --git a/lib/cove/invocation/service_run.rb b/lib/cove/invocation/service_run.rb new file mode 100644 index 0000000..2abeab4 --- /dev/null +++ b/lib/cove/invocation/service_run.rb @@ -0,0 +1,63 @@ +module Cove + module Invocation + class ServiceRun + include SSHKit::DSL + + # @return [Cove::Registry] + attr_reader :registry + # @return [Cove::Service] + attr_reader :service + attr_reader :custom_cmd + attr_reader :role + attr_reader :host + + # @param registry [Cove::Registry] + # @param service [Cove::Service] + # @param custom_cmd [Array] + # @param role [Cove::Role] + # @param host [Cove::Host] + def initialize(registry:, service:, custom_cmd:, role:, host:) + @service = service + @registry = registry + @custom_cmd = custom_cmd + @role = role + @host = host + end + + # @return nil + def invoke + Cove.output.puts "service: #{service.name}, role: #{role.name}, host: #{host.name}, commands: #{custom_cmd}." + deployment = Cove::Deployment.new(role) + instance_on_demand = Cove::InstanceOnDemand.new(deployment, custom_cmd) + desired_container = Cove::DesiredContainer.new( + name: instance_on_demand.name, + image: instance_on_demand.image, + command: instance_on_demand.command, + labels: instance_on_demand.labels, + environment_files: [EnvironmentFile.new(instance_on_demand.deployment).host_file_path], + version: instance_on_demand.version, + ports: instance_on_demand.ports, + mounts: instance_on_demand.mounts + ) + ssh_cmd = ["ssh", "-t", host.ssh_destination_string] + create_cmd = Cove::Command::Builder.create_container(desired_container, remove: true, interactive: true) + start_cmd = Cove::Command::Builder.start_attached_container(desired_container.name) + create_cmd = create_cmd.map { |el| el.to_s } + start_cmd = ssh_cmd + start_cmd + start_cmd = start_cmd.map { |el| el.to_s } + + on(host.sshkit_host) do + Steps::EnsureEnvironmentFileExists.call(self, deployment) + Steps::PullImage.call(self, deployment) + info "Creating container #{desired_container.name}" + execute(*create_cmd) + end + + run_locally do + info "Starting container #{desired_container.name}" + Kernel.exec(*start_cmd) + end + end + end + end +end diff --git a/spec/cove/cli/service_spec.rb b/spec/cove/cli/service_spec.rb index 510bd9c..9ccc9ff 100644 --- a/spec/cove/cli/service_spec.rb +++ b/spec/cove/cli/service_spec.rb @@ -36,14 +36,18 @@ describe "#runCustom" do it "runs a container with a custom command" do Cove.init(config: "spec/fixtures/configs/basic/") + service = Cove.registry.services["nginx"] + role = Cove.registry.roles_for_service(service).first + host = role.hosts.first - expect(Kernel).to receive(:exec) + expect(Cove::Invocation::ServiceRun).to receive(:new).with( + registry: Cove.registry, + service: service, + custom_cmd: ["echo", "hello"], + role: role, + host: host + ) { double(invoke: nil) } described_class.new.invoke(:runCustom, ["nginx"], ["echo hello"]) - - expect(Cove.output.string).to match(/nginx/) - expect(Cove.output.string).to match(/web/) - expect(Cove.output.string).to match(/host1/) - expect(Cove.output.string).to match(/echo hello/) end end end