Class: Bolts::Ssh

Inherits:
Object
  • Object
show all
Includes:
AwsServices, Defaults
Defined in:
lib/bolts/ssh.rb

Direct Known Subclasses

Docker

Instance Method Summary collapse

Methods included from AwsServices

#ec2, #ecs

Methods included from Defaults

#default_cluster, #default_user, #settings

Constructor Details

#initialize(identifier, options) ⇒ Ssh

Returns a new instance of Ssh.



8
9
10
11
12
13
14
# File 'lib/bolts/ssh.rb', line 8

def initialize(identifier, options)
  @options = options
  @identifier = identifier
  @service = @identifier # always set service even though sometimes it is not the identifier
  @cluster = options[:cluster] || default_cluster
  @user = options[:user] || default_user
end

Instance Method Details

#build_ssh_hostObject



35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/bolts/ssh.rb', line 35

def build_ssh_host
  ec2_instance_id = if instance_id?
                      @identifier
                    elsif container_instance_or_task_arn?
                      find_by_container_instance(@identifier) ||
                      find_by_task(@identifier)
                    else # service name
                      check_service_exists!
                      check_tasks_running!
                      container_instance_arn = task.container_instance_arn
                      find_by_container_instance(container_instance_arn)
                    end
  instance_hostname(ec2_instance_id)
end

#check_cluster_exists!Object



73
74
75
76
77
78
79
# File 'lib/bolts/ssh.rb', line 73

def check_cluster_exists!
  cluster = ecs.describe_clusters(clusters: [@cluster]).clusters.first
  unless cluster
    puts "The #{@cluster.green} cluster does not exist.  Are you sure you specified the right cluster?"
    exit 1
  end
end

#check_service_exists!Object



81
82
83
84
85
86
87
# File 'lib/bolts/ssh.rb', line 81

def check_service_exists!
  service = ecs.describe_services(services: [@service], cluster: @cluster).services.first
  unless service
    puts "The #{@service.green} service does not exist in #{@cluster.green} cluster.  Are you sure you specified the right service and cluster?"
    exit 1
  end
end

#check_tasks_running!Object



89
90
91
92
93
94
95
# File 'lib/bolts/ssh.rb', line 89

def check_tasks_running!
  if task_arns.empty?
    puts "Unable to find a running task that belongs to the #{@service} service on the #{@cluster} cluster."
    puts "There must be a running task in order for bolts to look up an container instance."
    exit 1
  end
end

#container_instance_or_task_arn?Boolean

Examples:

Container instance ids: b748e59a-b679-42a7-b713-afb12294935b 9f1dadc7-4f67-41da-abec-ec08810bfbc9

Task ids: 222c9e66-780b-4755-8c16-8670988e8011 6358f9c2-b231-4f5b-a59b-15bf19d52a15

Container instance and task ids have the same format

Returns:

  • (Boolean)


137
138
139
# File 'lib/bolts/ssh.rb', line 137

def container_instance_or_task_arn?
  @identifier =~ /.{8}-.{4}-.{4}-.{4}-.{12}/
end

#exit_on_specsObject

hack to test CLI call logic up to this class



23
24
25
26
27
28
# File 'lib/bolts/ssh.rb', line 23

def exit_on_specs
  if ENV['TEST']
    puts "ssh into ..."
    exit 0
  end
end

#find_by_container_instance(container_instance_arn) ⇒ Object



62
63
64
65
66
67
68
69
70
71
# File 'lib/bolts/ssh.rb', line 62

def find_by_container_instance(container_instance_arn)
  response = ecs.describe_container_instances(
                cluster: @cluster,
                container_instances: [container_instance_arn])
  container_instance = response.container_instances.first
  unless container_instance
    return false
  end
  ec2_instance_id = container_instance.ec2_instance_id
end

#find_by_task(task_arn) ⇒ Object



50
51
52
53
54
55
56
57
58
59
60
# File 'lib/bolts/ssh.rb', line 50

def find_by_task(task_arn)
  response = ecs.describe_tasks(
                cluster: @cluster,
                tasks: [task_arn])
  task = response.tasks.first
  unless task
    puts "Unable to find a #{task_arn.green} container instance or task in the #{@cluster.green} cluster."
    exit 1
  end
  find_by_container_instance(task.container_instance_arn)
end

#instance_hostname(ec2_instance_id) ⇒ Object



97
98
99
100
101
102
103
# File 'lib/bolts/ssh.rb', line 97

def instance_hostname(ec2_instance_id)
  instance = ec2.describe_instances(instance_ids: [ec2_instance_id]).reservations[0].instances[0]
  # struct Aws::EC2::Types::Instance
  # http://docs.aws.amazon.com/sdkforruby/api/Aws/EC2/Types/Instance.html
  host = instance.public_dns_name
  "#{@user}@#{host}"
end

#instance_id?Boolean

Examples: i-006a097bb10643e20

Returns:

  • (Boolean)


143
144
145
# File 'lib/bolts/ssh.rb', line 143

def instance_id?
  @identifier =~ /i-.{17}/
end

#kernel_exec(*args) ⇒ Object

Will use Kernel.exec so that the ssh process takes over this ruby process.



117
118
119
120
121
122
123
124
# File 'lib/bolts/ssh.rb', line 117

def kernel_exec(*args)
  # append the optional command that can be provided to the ssh command
  full_command = args + @options[:command]
  puts "Running: #{full_command.join(' ')}"
  # https://ruby-doc.org/core-2.3.1/Kernel.html#method-i-exec
  # Using 2nd form
  Kernel.exec(*full_command)
end

#runObject



16
17
18
19
20
# File 'lib/bolts/ssh.rb', line 16

def run
  exit_on_specs
  check_cluster_exists! unless instance_id?
  kernel_exec("ssh", ssh_host)
end

#ssh_hostObject

used by child Classes



31
32
33
# File 'lib/bolts/ssh.rb', line 31

def ssh_host
  @ssh_host ||= build_ssh_host
end

#taskObject

Only need one container instance to ssh into so we’ll just use the first. Useful to have this in a method for subclasses like Bolts::Exec.



111
112
113
# File 'lib/bolts/ssh.rb', line 111

def task
  @task ||= ecs.describe_tasks(cluster: @cluster, tasks: [task_arns.first]).tasks.first
end

#task_arnsObject



105
106
107
# File 'lib/bolts/ssh.rb', line 105

def task_arns
  @task_arns ||= ecs.list_tasks(cluster: @cluster, service_name: @service).task_arns
end