Class: Putpaws::Ecs::TaskCommand

Inherits:
Object
  • Object
show all
Defined in:
lib/putpaws/ecs/task_command.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(region:, cluster:, service: nil, task_name_prefix: nil) ⇒ TaskCommand

Returns a new instance of TaskCommand.



14
15
16
17
18
19
20
21
# File 'lib/putpaws/ecs/task_command.rb', line 14

def initialize(region:, cluster:, service: nil, task_name_prefix: nil)
  @ecs_client = Aws::ECS::Client.new({region: region})
  @region = region
  @cluster = cluster
  @service = service
  @task_name_prefix = task_name_prefix
  @ecs_task = nil
end

Instance Attribute Details

#clusterObject (readonly)

Returns the value of attribute cluster.



12
13
14
# File 'lib/putpaws/ecs/task_command.rb', line 12

def cluster
  @cluster
end

#ecs_clientObject (readonly)

Returns the value of attribute ecs_client.



11
12
13
# File 'lib/putpaws/ecs/task_command.rb', line 11

def ecs_client
  @ecs_client
end

#ecs_taskObject

Returns the value of attribute ecs_task.



13
14
15
# File 'lib/putpaws/ecs/task_command.rb', line 13

def ecs_task
  @ecs_task
end

#regionObject (readonly)

Returns the value of attribute region.



12
13
14
# File 'lib/putpaws/ecs/task_command.rb', line 12

def region
  @region
end

#serviceObject (readonly)

Returns the value of attribute service.



12
13
14
# File 'lib/putpaws/ecs/task_command.rb', line 12

def service
  @service
end

#task_name_prefixObject (readonly)

Returns the value of attribute task_name_prefix.



12
13
14
# File 'lib/putpaws/ecs/task_command.rb', line 12

def task_name_prefix
  @task_name_prefix
end

Class Method Details

.config(config) ⇒ Object



7
8
9
# File 'lib/putpaws/ecs/task_command.rb', line 7

def self.config(config)
  new(**config.ecs_command_params)
end

Instance Method Details

#build_session_manager_plugin_command(session:, target:) ⇒ Object

Returns an argument array so that callers can spawn session-manager-plugin without a shell like system(*cmd). https://github.com/aws/aws-cli/blob/2a6136010d8656a605d41d1e7b5fdab3c2930cad/awscli/customizations/ecs/executecommand.py#L105



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/putpaws/ecs/task_command.rb', line 126

def build_session_manager_plugin_command(session:, target:)
  ssm_region = ENV['AWS_REGION_SSM'] || @region

  session_json = if session.respond_to?(:session_id)
    {
      "SessionId" => session.session_id,
      "StreamUrl" => session.stream_url,
      "TokenValue" => session.token_value,
    }.to_json
  elsif session.is_a?(Hash)
    session.to_json
  else
    session
  end
  target_json = {
    "Target" => target
  }.to_json
  [
    "session-manager-plugin",
    session_json,
    @region,
    "StartSession",
    ENV['AWS_PROFILE'].to_s,
    target_json,
    "https://ssm.#{ssm_region}.amazonaws.com"
  ]
end

#get_attach_command(container: nil) ⇒ Object



110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/putpaws/ecs/task_command.rb', line 110

def get_attach_command(container: nil)
  container ||= 'app'
  target = get_session_target(container: container)
  res = ecs_client.execute_command({
    cluster: cluster,
    container: container,
    command: '/bin/bash',
    interactive: true,
    task: ecs_task.task_arn,
  })
  build_session_manager_plugin_command(session: res.session, target: target)
end

#get_port_forwarding_command(container: nil, remote_port:, remote_host:, local_port: nil) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/putpaws/ecs/task_command.rb', line 92

def get_port_forwarding_command(container: nil, remote_port:, remote_host:, local_port: nil)
  container ||= 'app'
  target = get_session_target(container: container)
  ssm_client = Aws::SSM::Client.new({region: region})
  local_port = resolve_local_port(local_port)
  puts "Starting to use local port: #{local_port}"
  res = ssm_client.start_session({
    target: target,
    document_name: "AWS-StartPortForwardingSessionToRemoteHost",
    parameters: {
      portNumber: [remote_port],
      localPortNumber: [local_port],
      host: [remote_host]
    }
  })
  build_session_manager_plugin_command(session: res, target: target)
end

#get_session_target(container: 'app') ⇒ Object



64
65
66
67
68
69
70
# File 'lib/putpaws/ecs/task_command.rb', line 64

def get_session_target(container: 'app')
  raise "ECS Task Not Set" unless ecs_task
  ctn = ecs_task.containers.detect{|c| c.name == container}
  task_id = ecs_task.task_arn.split('/').last
  raise "Container: #{container} not found" unless ctn
  "ecs:#{cluster}_#{task_id}_#{ctn.runtime_id}"
end

#list_ecs_servicesObject



34
35
36
37
38
39
40
41
42
# File 'lib/putpaws/ecs/task_command.rb', line 34

def list_ecs_services
  res = ecs_client.list_services(cluster: cluster, max_results: 100)
  return [] if res.service_arns.empty?
  res = ecs_client.describe_services(cluster: cluster, services: res.service_arns)
  services = res.services.select{|s| s.status == 'ACTIVE'}
  return services unless task_name_prefix
  filtered = services.select{|s| s.service_name.start_with?(task_name_prefix)}
  filtered.empty? ? services : filtered
end

#list_ecs_tasksObject



23
24
25
26
27
28
29
30
31
32
# File 'lib/putpaws/ecs/task_command.rb', line 23

def list_ecs_tasks
  res = ecs_client.list_tasks(cluster: cluster)
  return [] if res.task_arns.empty?
  res = ecs_client.describe_tasks(tasks: res.task_arns, cluster: cluster)
  return res.tasks unless task_name_prefix
  res.tasks.select{|t| 
    _, name = t.task_definition_arn.split('task-definition/')
    name.start_with?(task_name_prefix)
  }
end

#local_port_available?(port) ⇒ Boolean

Returns:

  • (Boolean)


72
73
74
75
76
77
# File 'lib/putpaws/ecs/task_command.rb', line 72

def local_port_available?(port)
  TCPServer.new('127.0.0.1', port.to_i).close
  true
rescue Errno::EADDRINUSE, Errno::EACCES
  false
end

#resolve_local_port(local_port) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/putpaws/ecs/task_command.rb', line 79

def resolve_local_port(local_port)
  if local_port
    unless local_port_available?(local_port)
      raise "Local port #{local_port} is already in use"
    end
    local_port.to_s
  else
    port = (1050..1079).to_a.shuffle.detect{|p| local_port_available?(p)}
    raise "No available local port between 1050 and 1079. Please specify one like: local=:8080" unless port
    port.to_s
  end
end

#update_ecs_service(service:, desired_count: nil, task_definition: nil) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
# File 'lib/putpaws/ecs/task_command.rb', line 44

def update_ecs_service(service:, desired_count: nil, task_definition: nil)
  params = {
    cluster: cluster,
    service: service,
    force_new_deployment: true,
  }
  params[:desired_count] = desired_count.to_i if desired_count
  params[:task_definition] = task_definition if task_definition
  res = ecs_client.update_service(**params)
  res.service
end

#wait_ecs_service_stable(service:, timeout: 600) ⇒ Object



56
57
58
59
60
61
62
# File 'lib/putpaws/ecs/task_command.rb', line 56

def wait_ecs_service_stable(service:, timeout: 600)
  ecs_client.wait_until(
    :services_stable,
    {cluster: cluster, services: [service]},
    {delay: 15, max_attempts: (timeout / 15.0).ceil}
  )
end