Class: Gjallarhorn::Adapter::AWSAdapter

Inherits:
Base
  • Object
show all
Defined in:
lib/gjallarhorn/adapter/aws.rb

Overview

AWS Systems Manager adapter for container deployments

Since:

  • 0.1.0

Instance Attribute Summary

Attributes inherited from Base

#config, #logger

Instance Method Summary collapse

Methods inherited from Base

#logs, #scale

Constructor Details

#initialize(config) ⇒ AWSAdapter

Initialize AWS adapter with SSM and EC2 clients

Parameters:

  • config (Hash)

    Configuration containing AWS region and other settings

Raises:

  • (ArgumentError)

Since:

  • 0.1.0



27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/gjallarhorn/adapter/aws.rb', line 27

def initialize(config)
  super
  require "aws-sdk-ssm"
  require "aws-sdk-ec2"
  
  # Handle both string and symbol keys for region
  region = config["region"] || config[:region]
  raise ArgumentError, "AWS region is required in configuration" unless region
  
  @ssm = Aws::SSM::Client.new(region: region)
  @ec2 = Aws::EC2::Client.new(region: region)
  @current_environment = nil
end

Instance Method Details

#authenticate_ecr_registries(registries) ⇒ void (private)

This method returns an undefined value.

Authenticate with ECR registries

Parameters:

  • registries (Array<Hash>)

    ECR registry information

Since:

  • 0.1.0



586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
# File 'lib/gjallarhorn/adapter/aws.rb', line 586

def authenticate_ecr_registries(registries)
  registries.each do |registry|
    @logger.info "Authenticating with ECR registry: #{registry[:registry_url]}"
    
     = "aws ecr get-login-password --region #{registry[:region]} | " \
               "docker login --username AWS --password-stdin #{registry[:registry_url]}"
    
    begin
      execute_ssm_command_with_response()
      @logger.info "Successfully authenticated with ECR registry: #{registry[:registry_url]}"
    rescue StandardError => e
      @logger.error "Failed to authenticate with ECR registry #{registry[:registry_url]}: #{e.message}"
      raise StandardError, "ECR authentication failed for #{registry[:registry_url]}: #{e.message}"
    end
  end
end

#build_deployment_commands(image, services) ⇒ Object (private)

Since:

  • 0.1.0



167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/gjallarhorn/adapter/aws.rb', line 167

def build_deployment_commands(image, services)
  [
    "docker pull #{image}",
    *services.map { |svc| "docker stop #{svc[:name]} || true" },
    *services.map do |svc|
      "docker run -d --name #{svc[:name]} " \
      "#{svc[:ports].map { |p| "-p #{p}" }.join(" ")} " \
      "#{svc[:env].map { |k, v| "-e #{k}=#{v}" }.join(" ")} " \
      "#{image}"
    end
  ]
end

#build_docker_run_command(config) ⇒ String (private)

Build Docker run command from configuration

Parameters:

  • config (Hash)

    Container configuration

Returns:

  • (String)

    Docker run command

Since:

  • 0.1.0



328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
# File 'lib/gjallarhorn/adapter/aws.rb', line 328

def build_docker_run_command(config)
  cmd_parts = ["docker run -d"]

  # Container name
  cmd_parts << "--name #{config[:name]}"

  # Port mappings
  config[:ports].each do |port|
    cmd_parts << "-p #{port}"
  end

  # Environment variables
  config[:env].each do |key, value|
    cmd_parts << "-e #{key}=#{shell_escape(value)}"
  end

  # Volume mounts
  config[:volumes].each do |volume|
    cmd_parts << "-v #{volume}"
  end

  # Labels
  config[:labels].each do |key, value|
    cmd_parts << "--label #{key}=#{shell_escape(value)}"
  end

  # Restart policy
  cmd_parts << "--restart #{config[:restart_policy]}"

  # Image
  cmd_parts << config[:image]

  # Command (if specified)
  cmd_parts << config[:command] if config[:command]

  cmd_parts.join(" ")
end

#deploy(image:, environment:, services: []) ⇒ void

This method returns an undefined value.

Deploy container images to AWS EC2 instances via SSM

Parameters:

  • image (String)

    Docker image to deploy

  • environment (String)

    Target environment name

  • services (Array<Hash>) (defaults to: [])

    Service configurations to deploy

Since:

  • 0.1.0



47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/gjallarhorn/adapter/aws.rb', line 47

def deploy(image:, environment:, services: [])
  instances = get_instances_by_tags(environment)
  commands = build_deployment_commands(image, services)

  logger.info "Deploying #{image} to #{instances.size} AWS instances"

  response = execute_deployment_command(instances, commands, image)
  wait_for_command_completion(response.command.command_id, instances)
  verify_service_health(services)

  logger.info "Deployment completed successfully"
end

#execute_container_start(docker_cmd) ⇒ String (private)

Execute container start command and return container ID

Parameters:

  • docker_cmd (String)

    Docker run command

Returns:

  • (String)

    Container ID

Since:

  • 0.1.0



624
625
626
627
628
629
630
631
632
633
# File 'lib/gjallarhorn/adapter/aws.rb', line 624

def execute_container_start(docker_cmd)
  # Check if we need ECR authentication
  if docker_cmd.include?('.dkr.ecr.')
    @logger.debug "ECR registry detected, ensuring authentication"
    ecr_registries = extract_ecr_registries(docker_cmd)
    authenticate_ecr_registries(ecr_registries)
  end

  execute_ssm_command_with_response(docker_cmd).strip
end

#execute_deployment_command(instances, commands, image) ⇒ Object (private)

Since:

  • 0.1.0



180
181
182
183
184
185
186
187
188
189
190
# File 'lib/gjallarhorn/adapter/aws.rb', line 180

def execute_deployment_command(instances, commands, image)
  @ssm.send_command(
    instance_ids: instances,
    document_name: "AWS-RunShellScript",
    parameters: {
      "commands" => commands,
      "executionTimeout" => ["3600"]
    },
    comment: "Deploy #{image} via Gjallarhorn"
  )
end

#execute_in_container(container_id, command) ⇒ String (private)

Execute command in a running container

Parameters:

  • container_id (String)

    Container ID

  • command (String)

    Command to execute

Returns:

  • (String)

    Command output

Since:

  • 0.1.0



260
261
262
263
# File 'lib/gjallarhorn/adapter/aws.rb', line 260

def execute_in_container(container_id, command)
  docker_cmd = "docker exec #{container_id} #{command}"
  execute_ssm_command_with_response(docker_cmd)
end

#execute_ssm_command(command) ⇒ void (private)

This method returns an undefined value.

Execute SSM command without returning response (fire and forget)

Parameters:

  • command (String)

    Command to execute

Since:

  • 0.1.0



514
515
516
517
518
519
520
521
522
523
524
525
526
527
# File 'lib/gjallarhorn/adapter/aws.rb', line 514

def execute_ssm_command(command)
  instances = target_instances
  @logger.debug "execute_ssm_command: Using instances: #{instances.inspect}"
  @logger.debug "execute_ssm_command: Command: #{command}"
  
  @ssm.send_command(
    instance_ids: instances,
    document_name: "AWS-RunShellScript",
    parameters: {
      "commands" => [command],
      "executionTimeout" => ["300"]
    }
  )
end

#execute_ssm_command_with_response(command) ⇒ String (private)

Execute SSM command and return response

Parameters:

  • command (String)

    Command to execute

Returns:

  • (String)

    Command output

Since:

  • 0.1.0



370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
# File 'lib/gjallarhorn/adapter/aws.rb', line 370

def execute_ssm_command_with_response(command)
  instances = target_instances
  @logger.debug "execute_ssm_command_with_response: Using instances: #{instances.inspect}"
  @logger.debug "execute_ssm_command_with_response: Command: #{command}"
  
  response = @ssm.send_command(
    instance_ids: instances,
    document_name: "AWS-RunShellScript",
    parameters: {
      "commands" => [command],
      "executionTimeout" => ["300"]
    }
  )

  command_id = response.command.command_id
  wait_for_command_completion(command_id, target_instances)

  # Get command output
  get_command_output(command_id)
end

#extract_container_config(container_config) ⇒ Hash (private)

Extract and normalize container configuration

Parameters:

  • container_config (Hash)

    Raw container configuration

Returns:

  • (Hash)

    Normalized configuration

Since:

  • 0.1.0



607
608
609
610
611
612
613
614
615
616
617
618
# File 'lib/gjallarhorn/adapter/aws.rb', line 607

def extract_container_config(container_config)
  {
    name: container_config[:name],
    image: container_config[:image],
    ports: container_config[:ports] || [],
    env: container_config[:env] || {},
    volumes: container_config[:volumes] || [],
    command: container_config[:command],
    labels: container_config[:labels] || {},
    restart_policy: container_config[:restart_policy] || "unless-stopped"
  }
end

#extract_ecr_registries(docker_cmd) ⇒ Array<Hash> (private)

Extract ECR registries from Docker command

Parameters:

  • docker_cmd (String)

    Docker command

Returns:

  • (Array<Hash>)

    Array of ECR registry information

Since:

  • 0.1.0



564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
# File 'lib/gjallarhorn/adapter/aws.rb', line 564

def extract_ecr_registries(docker_cmd)
  registries = []
  
  # Match ECR registry URLs: account.dkr.ecr.region.amazonaws.com
  ecr_pattern = /(\d+)\.dkr\.ecr\.([^.]+)\.amazonaws\.com/
  
  docker_cmd.scan(ecr_pattern) do |, region|
    registry_url = "#{}.dkr.ecr.#{region}.amazonaws.com"
    registries << {
      account_id: ,
      region: region,
      registry_url: registry_url
    }
  end
  
  registries.uniq
end

#finalize_container_setup(container_id, config) ⇒ Hash (private)

Finalize container setup after creation

Parameters:

  • container_id (String)

    Container ID

  • config (Hash)

    Container configuration

Returns:

  • (Hash)

    Container information

Since:

  • 0.1.0



640
641
642
643
644
645
646
647
648
649
# File 'lib/gjallarhorn/adapter/aws.rb', line 640

def finalize_container_setup(container_id, config)
  wait_for_container_running(container_id)

  container_info = get_container_info(container_id)
  container_info.merge(
    name: config[:name],
    image: config[:image],
    created_at: Time.now.utc
  )
end

#get_all_containers(service_name) ⇒ Array<Hash> (private)

Get all containers for a service (including stopped)

Parameters:

  • service_name (String)

    Service name

Returns:

  • (Array<Hash>)

    Array of all container information

Since:

  • 0.1.0



219
220
221
222
223
224
225
226
227
228
# File 'lib/gjallarhorn/adapter/aws.rb', line 219

def get_all_containers(service_name)
  cmd = [
    "docker ps -a",
    "--filter label=gjallarhorn.service=#{service_name}",
    "--format '{{.ID}}:{{.Names}}:{{.Status}}:{{.CreatedAt}}'"
  ].join(" ")

  output = execute_ssm_command_with_response(cmd)
  parse_container_list(output, service_name)
end

#get_command_output(command_id) ⇒ String (private)

Get command output from SSM

Parameters:

  • command_id (String)

    SSM command ID

Returns:

  • (String)

    Command output

Since:

  • 0.1.0



395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
# File 'lib/gjallarhorn/adapter/aws.rb', line 395

def get_command_output(command_id)
  instances = target_instances
  @logger.debug "get_command_output: Using instances: #{instances.inspect}"
  @logger.debug "get_command_output: First instance: #{instances.first.inspect}"

  # Get output from first instance (simplified)
  result = @ssm.get_command_invocation(
    command_id: command_id,
    instance_id: instances.first
  )

  if result.status_details == "Success"
    result.standard_output_content || ""
  else
    error_msg = result.standard_error_content || "Command failed"
    raise DeploymentError, "SSM command failed: #{error_msg}"
  end
end

#get_container_health(container_id) ⇒ Boolean (private)

Get container health status

Parameters:

  • container_id (String)

    Container ID

Returns:

  • (Boolean)

    True if container is healthy

Since:

  • 0.1.0



269
270
271
272
273
274
275
276
277
278
279
280
# File 'lib/gjallarhorn/adapter/aws.rb', line 269

def get_container_health(container_id)
  health_output = execute_ssm_command_with_response(
    "docker inspect #{container_id} --format '{{.State.Health.Status}}'"
  )

  health_status = health_output.strip.downcase
  health_status == "healthy"
rescue StandardError => e
  @logger.debug "Health check failed for #{container_id}: #{e.message}"
  # If no health check is configured, check if container is running
  get_container_status(container_id) == "running"
end

#get_container_info(container_id) ⇒ Hash (private)

Get detailed container information

Parameters:

  • container_id (String)

    Container ID

Returns:

  • (Hash)

    Container information

Since:

  • 0.1.0



299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
# File 'lib/gjallarhorn/adapter/aws.rb', line 299

def get_container_info(container_id)
  # Get container IP address
  ip_cmd = "docker inspect #{container_id} --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'"
  ip_address = execute_ssm_command_with_response(ip_cmd).strip

  # Get container port mappings
  ports_cmd = "docker port #{container_id}"
  ports_output = execute_ssm_command_with_response(ports_cmd)

  {
    id: container_id,
    ip: ip_address.empty? ? nil : ip_address,
    ports: parse_container_ports(ports_output),
    host: target_instances.first # Simplified - use first instance
  }
rescue StandardError => e
  @logger.warn "Failed to get container info for #{container_id}: #{e.message}"
  {
    id: container_id,
    ip: nil,
    ports: [],
    host: target_instances.first
  }
end

#get_container_status(container_id) ⇒ String (private)

Get container status

Parameters:

  • container_id (String)

    Container ID

Returns:

  • (String)

    Container status

Since:

  • 0.1.0



286
287
288
289
290
291
292
293
# File 'lib/gjallarhorn/adapter/aws.rb', line 286

def get_container_status(container_id)
  status_output = execute_ssm_command_with_response(
    "docker inspect #{container_id} --format '{{.State.Status}}'"
  )
  status_output.strip.downcase
rescue StandardError
  "unknown"
end

#get_instance_status(instance_id) ⇒ Object (private)

Since:

  • 0.1.0



207
208
209
210
211
212
213
# File 'lib/gjallarhorn/adapter/aws.rb', line 207

def get_instance_status(instance_id)
  resp = @ec2.describe_instances(instance_ids: [instance_id])
  instance = resp.reservations.first&.instances&.first
  instance&.state&.name || "unknown"
rescue StandardError
  "unknown"
end

#get_instances_by_tags(environment) ⇒ Object (private)

Since:

  • 0.1.0



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/gjallarhorn/adapter/aws.rb', line 134

def get_instances_by_tags(environment)
  @logger.debug "Querying EC2 instances with filters: Environment=#{environment}, Role=web|app, state=running"
  
  resp = @ec2.describe_instances(
    filters: [
      { name: "tag:Environment", values: [environment] },
      { name: "tag:Role", values: %w[web app] },
      { name: "instance-state-name", values: ["running"] }
    ]
  )

  instances = resp.reservations.flat_map(&:instances)
  @logger.debug "Found #{instances.length} instances matching filters"
  
  instances.each do |instance|
    tags_info = begin
      if instance.respond_to?(:tags) && instance.tags
        instance.tags.map { |t| "#{t.key}=#{t.value}" }.join(', ')
      else
        "N/A"
      end
    rescue StandardError
      "N/A"
    end
    @logger.debug "Instance: #{instance.instance_id}, State: #{instance.state.name}, Tags: #{tags_info}"
  end

  instance_ids = instances.map(&:instance_id)
  @logger.debug "Returning instance IDs: #{instance_ids.join(', ')}" if instance_ids.any?
  
  instance_ids
end

#get_running_containers(service_name) ⇒ Array<Hash>

Get running containers for a service

Parameters:

  • service_name (String)

    Service name

Returns:

  • (Array<Hash>)

    Array of container information

Since:

  • 0.1.0



120
121
122
123
124
125
126
127
128
129
130
# File 'lib/gjallarhorn/adapter/aws.rb', line 120

def get_running_containers(service_name)
  # List containers with service label/name pattern
  cmd = [
    "docker ps",
    "--filter label=gjallarhorn.service=#{service_name}",
    "--format '{{.ID}}:{{.Names}}:{{.Status}}:{{.CreatedAt}}'"
  ].join(" ")

  output = execute_ssm_command_with_response(cmd)
  parse_container_list(output, service_name)
end

#health_checkBoolean

TODO:

Implement actual health check via SSM

Check health of a service (simplified implementation)

Parameters:

  • service (String)

    Service name to check

Returns:

  • (Boolean)

    Always returns true (simplified)

Since:

  • 0.1.0



88
89
90
91
# File 'lib/gjallarhorn/adapter/aws.rb', line 88

def health_check(*)
  # Implement health check via SSM command
  true # Simplified
end

#parse_container_list(output, service_name) ⇒ Array<Hash> (private)

Parse container list output

Parameters:

  • output (String)

    Docker ps output

  • service_name (String)

    Service name for filtering

Returns:

  • (Array<Hash>)

    Parsed container information

Since:

  • 0.1.0



419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
# File 'lib/gjallarhorn/adapter/aws.rb', line 419

def parse_container_list(output, service_name)
  containers = []

  output.split("\n").each do |line|
    next if line.strip.empty?

    parts = line.split(":")
    next unless parts.length >= 3

    containers << {
      id: parts[0],
      name: parts[1],
      status: parts[2],
      created_at: parse_container_timestamp(parts[3]),
      service: service_name
    }
  end

  containers
end

#parse_container_ports(ports_output) ⇒ Array<String> (private)

Parse container port mappings

Parameters:

  • ports_output (String)

    Docker port command output

Returns:

  • (Array<String>)

    Port mappings

Since:

  • 0.1.0



444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
# File 'lib/gjallarhorn/adapter/aws.rb', line 444

def parse_container_ports(ports_output)
  ports = []

  ports_output.split("\n").each do |line|
    next if line.strip.empty?

    # Format: "3000/tcp -> 0.0.0.0:3000"
    next unless line.match(%r{(\d+)/tcp -> [\d.]+:(\d+)})

    container_port = ::Regexp.last_match(1)
    host_port = ::Regexp.last_match(2)
    ports << "#{host_port}:#{container_port}"
  end

  ports
end

#parse_container_timestamp(timestamp_str) ⇒ Time (private)

Parse container creation timestamp

Parameters:

  • timestamp_str (String)

    Timestamp string from Docker

Returns:

  • (Time)

    Parsed timestamp

Since:

  • 0.1.0



465
466
467
468
469
470
471
472
# File 'lib/gjallarhorn/adapter/aws.rb', line 465

def parse_container_timestamp(timestamp_str)
  return Time.now.utc unless timestamp_str

  # Docker timestamp format: "2024-01-15 10:30:45 +0000 UTC"
  Time.parse(timestamp_str).utc
rescue StandardError
  Time.now.utc
end

#remove_container(container_id) ⇒ void (private)

This method returns an undefined value.

Remove a container

Parameters:

  • container_id (String)

    Container ID

Since:

  • 0.1.0



250
251
252
253
# File 'lib/gjallarhorn/adapter/aws.rb', line 250

def remove_container(container_id)
  @logger.info "Removing container: #{container_id}"
  execute_ssm_command("docker rm #{container_id}")
end

#rollback(version:) ⇒ void

TODO:

Implement rollback functionality

This method returns an undefined value.

Rollback to a previous version (placeholder implementation)

Parameters:

  • version (String)

    Version to rollback to

Since:

  • 0.1.0



65
66
67
# File 'lib/gjallarhorn/adapter/aws.rb', line 65

def rollback(version:)
  # Similar implementation for rollback
end

#set_environment(environment) ⇒ void

This method returns an undefined value.

Set the current deployment environment

Parameters:

  • environment (String)

    Environment name

Since:

  • 0.1.0



97
98
99
# File 'lib/gjallarhorn/adapter/aws.rb', line 97

def set_environment(environment)
  @current_environment = environment
end

#shell_escape(value) ⇒ String (private)

Escape shell arguments

Parameters:

  • value (String)

    Value to escape

Returns:

  • (String)

    Shell-escaped value

Since:

  • 0.1.0



506
507
508
# File 'lib/gjallarhorn/adapter/aws.rb', line 506

def shell_escape(value)
  "'#{value.to_s.gsub("'", "'\\''")}'"
end

#start_container(container_config) ⇒ Hash

Start a new container with enhanced configuration

Parameters:

  • container_config (Hash)

    Container configuration

Returns:

  • (Hash)

    Container information

Since:

  • 0.1.0



105
106
107
108
109
110
111
112
113
114
# File 'lib/gjallarhorn/adapter/aws.rb', line 105

def start_container(container_config)
  config = extract_container_config(container_config)
  docker_cmd = build_docker_run_command(config)

  @logger.info "Starting container: #{config[:name]}"
  @logger.debug "Docker command: #{docker_cmd}"

  container_id = execute_container_start(docker_cmd)
  finalize_container_setup(container_id, config)
end

#statusArray<Hash>

Get status of all instances in the environment

Returns:

  • (Array<Hash>)

    Instance status information

Since:

  • 0.1.0



72
73
74
75
76
77
78
79
80
81
# File 'lib/gjallarhorn/adapter/aws.rb', line 72

def status
  environment = config["environment"] || config[:environment] || "production"
  instances = get_instances_by_tags(environment)
  instances.map do |instance_id|
    {
      instance: instance_id,
      status: get_instance_status(instance_id)
    }
  end
end

#stop_container(container_id, graceful: true, timeout: 30) ⇒ void (private)

This method returns an undefined value.

Stop a container

Parameters:

  • container_id (String)

    Container ID

  • graceful (Boolean) (defaults to: true)

    Whether to stop gracefully

  • timeout (Integer) (defaults to: 30)

    Timeout for graceful stop

Since:

  • 0.1.0



236
237
238
239
240
241
242
243
244
# File 'lib/gjallarhorn/adapter/aws.rb', line 236

def stop_container(container_id, graceful: true, timeout: 30)
  if graceful
    @logger.info "Gracefully stopping container: #{container_id}"
    execute_ssm_command("docker stop --time #{timeout} #{container_id}")
  else
    @logger.info "Force stopping container: #{container_id}"
    execute_ssm_command("docker kill #{container_id}")
  end
end

#target_instances(environment = nil) ⇒ Array<String> (private)

Get target instances for the current environment

Parameters:

  • environment (String) (defaults to: nil)

    Environment name (overrides config)

Returns:

  • (Array<String>)

    Array of instance IDs

Since:

  • 0.1.0



533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
# File 'lib/gjallarhorn/adapter/aws.rb', line 533

def target_instances(environment = nil)
  # Check if instance IDs are explicitly configured
  instance_ids = @config["instance_ids"] || @config[:instance_ids] || 
                @config["instance-ids"] || @config[:"instance-ids"]
  
  if instance_ids && !instance_ids.empty?
    # Use explicitly configured instance IDs
    instance_ids = [instance_ids] unless instance_ids.is_a?(Array)
    @logger.debug "Using configured instance IDs: #{instance_ids.join(', ')}"
    return instance_ids
  end
  
  # Fall back to tag-based discovery
  # Use provided environment parameter, current environment, config, or default to production
  env_name = environment || @current_environment || @config["environment"] || @config[:environment] || "production"
  @logger.debug "No instance IDs configured, discovering instances by tags for environment: #{env_name}"
  discovered_instances = get_instances_by_tags(env_name)
  
  if discovered_instances.empty?
    raise ArgumentError, "No EC2 instances found for environment '#{env_name}'. " \
                        "Either configure 'instance_ids' in your deploy.yml or ensure your EC2 " \
                        "instances are tagged with Environment=#{env_name} and Role=web|app"
  end
  
  discovered_instances
end

#verify_service_health(services) ⇒ Object (private)

Since:

  • 0.1.0



192
193
194
# File 'lib/gjallarhorn/adapter/aws.rb', line 192

def verify_service_health(services)
  services.each { |service| wait_for_health(service) }
end

#wait_for_command_completion(command_id, instances) ⇒ Object (private)

Since:

  • 0.1.0



196
197
198
199
200
201
202
203
204
205
# File 'lib/gjallarhorn/adapter/aws.rb', line 196

def wait_for_command_completion(command_id, instances)
  # Use the first instance for command completion check
  instance_id = instances.is_a?(Array) ? instances.first : instances
  @logger.debug "wait_for_command_completion: Using instance #{instance_id} for command #{command_id}"
  
  @ssm.wait_until(:command_executed, command_id: command_id, instance_id: instance_id) do |w|
    w.max_attempts = 60
    w.delay = 5
  end
end

#wait_for_container_running(container_id, timeout = 60) ⇒ void (private)

This method returns an undefined value.

Wait for container to be running

Parameters:

  • container_id (String)

    Container ID

  • timeout (Integer) (defaults to: 60)

    Timeout in seconds

Since:

  • 0.1.0



479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
# File 'lib/gjallarhorn/adapter/aws.rb', line 479

def wait_for_container_running(container_id, timeout = 60)
  start_time = Time.now

  loop do
    status = get_container_status(container_id)

    if status == "running"
      @logger.info "Container #{container_id} is running"
      return
    elsif status == "exited"
      raise DeploymentError, "Container #{container_id} exited unexpectedly"
    end

    elapsed = Time.now - start_time
    if elapsed >= timeout
      raise DeploymentError, "Container #{container_id} failed to start within #{timeout}s (status: #{status})"
    end

    @logger.debug "Container #{container_id} status: #{status}, waiting..."
    sleep 2
  end
end