scout-camp 0.1.6 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.vimproject +63 -7
  3. data/VERSION +1 -1
  4. data/lib/scout/aws/s3.rb +54 -12
  5. data/lib/scout/offsite/resource.rb +34 -0
  6. data/lib/scout/offsite/ssh.rb +2 -0
  7. data/lib/scout/offsite/sync.rb +2 -0
  8. data/lib/scout/terraform_dsl/util.rb +2 -2
  9. data/lib/scout/terraform_dsl.rb +58 -0
  10. data/lib/scout-camp.rb +1 -0
  11. data/scout-camp.gemspec +37 -4
  12. data/scout_commands/sync +33 -0
  13. data/scout_commands/terraform/lambda_task +9 -5
  14. data/scout_commands/terraform/list +11 -3
  15. data/scout_commands/terraform/outputs +33 -0
  16. data/share/aws/lambda_function.rb +34 -7
  17. data/share/terraform/aws/container_lambda/data.tf +15 -0
  18. data/share/terraform/aws/container_lambda/locals.tf +8 -0
  19. data/share/terraform/aws/container_lambda/main.tf +47 -0
  20. data/share/terraform/aws/container_lambda/variables.tf +44 -0
  21. data/share/terraform/aws/efs/data.tf +12 -0
  22. data/share/terraform/aws/efs/locals.tf +6 -0
  23. data/share/terraform/aws/efs/main.tf +14 -0
  24. data/share/terraform/aws/efs/output.tf +3 -0
  25. data/share/terraform/aws/efs/variables.tf +9 -0
  26. data/share/terraform/aws/efs_host/data.tf +11 -0
  27. data/share/terraform/aws/efs_host/locals.tf +8 -0
  28. data/share/terraform/aws/efs_host/main.tf +32 -0
  29. data/share/terraform/aws/efs_host/output.tf +3 -0
  30. data/share/terraform/aws/efs_host/variables.tf +28 -0
  31. data/share/terraform/aws/fargate/locals.tf +8 -0
  32. data/share/terraform/aws/fargate/main.tf +41 -0
  33. data/share/terraform/aws/fargate/variables.tf +84 -0
  34. data/share/terraform/aws/iam_instance_profile/main.tf +5 -0
  35. data/share/terraform/aws/iam_instance_profile/output.tf +15 -0
  36. data/share/terraform/aws/iam_instance_profile/variables.tf +9 -0
  37. data/share/terraform/aws/lambda/main.tf +1 -1
  38. data/share/terraform/aws/lambda/variables.tf +3 -4
  39. data/share/terraform/aws/network/data.tf +15 -0
  40. data/share/terraform/aws/network/main.tf +41 -0
  41. data/share/terraform/aws/network/output.tf +7 -0
  42. data/share/terraform/aws/network/variables.tf +0 -0
  43. data/share/terraform/aws/policy/main.tf +8 -0
  44. data/share/terraform/aws/policy/output.tf +15 -0
  45. data/share/terraform/aws/policy/variables.tf +12 -0
  46. data/share/terraform/aws/role/main.tf +1 -1
  47. data/share/terraform/aws/role/output.tf +1 -1
  48. data/share/terraform/aws/role/variables.tf +4 -1
  49. data/share/terraform/aws/role_policy/main.tf +9 -0
  50. data/share/terraform/aws/role_policy/variables.tf +16 -0
  51. data/test/scout/aws/test_s3.rb +14 -1
  52. data/test/scout/offsite/test_resource.rb +46 -0
  53. data/test/scout/offsite/test_sync.rb +1 -0
  54. metadata +36 -3
@@ -7,21 +7,48 @@ def lambda_handler(event:, context:)
7
7
  require 'scout/workflow'
8
8
  require 'scout/aws/s3'
9
9
 
10
- workflow, task_name, jobname, inputs = IndiferentHash.process_options event,
11
- :workflow, :task_name, :jobname, :inputs
12
-
13
- raise ParamterException, "No workflow specified" if workflow.nil?
14
-
10
+ workflow, task_name, jobname, inputs, clean, queue = IndiferentHash.process_options event,
11
+ :workflow, :task_name, :jobname, :inputs, :clean, :queue
12
+
13
+ raise ParameterException, "No workflow specified" if workflow.nil?
14
+
15
15
  workflow = Workflow.require_workflow workflow
16
16
 
17
17
  case task_name
18
18
  when nil
19
19
  return {tasks: workflow.tasks.keys, documentation: workflow.documentation}
20
20
  when "info"
21
- raise ParamterException, "No task_name specified" if task_name.nil?
21
+ raise ParameterException, "No task_name specified" if task_name.nil?
22
22
  return workflow.task_info(inputs["task_name"])
23
23
  else
24
24
  job = workflow.job(task_name, jobname, inputs)
25
- job.run
25
+
26
+ case clean
27
+ when true, 'true'
28
+ job.clean
29
+ when 'recursive'
30
+ job.recursive_clean
31
+ end
32
+
33
+ if job.done?
34
+ job.load
35
+ elsif job.error?
36
+ raise job.exception
37
+ elsif job.started?
38
+ {
39
+ statusCode: 202,
40
+ body: job.path
41
+ }
42
+ elsif queue
43
+ save_inputs = Scout.var.queue[workflow.to_s][task_name][job.name].find
44
+ job.save_inputs(save_inputs)
45
+ {
46
+ statusCode: 202,
47
+ body: job.path
48
+ }
49
+ else
50
+ job.produce
51
+ job.load
52
+ end
26
53
  end
27
54
  end
@@ -0,0 +1,15 @@
1
+ data "aws_vpc" "default" {
2
+ default = true
3
+ }
4
+
5
+ data "aws_subnets" "default_vpc_subnets" {
6
+ filter {
7
+ name = "vpc-id"
8
+ values = [data.aws_vpc.default.id]
9
+ }
10
+
11
+ filter {
12
+ name = "default-for-az"
13
+ values = ["true"]
14
+ }
15
+ }
@@ -0,0 +1,8 @@
1
+ locals {
2
+ security_group_ids = [
3
+ for sg_key in var.sg_keys :
4
+ lookup(var.network.outputs, sg_key, null)
5
+ ]
6
+
7
+ efs_id = lookup(var.efs.outputs, "aws_efs_id", null)
8
+ }
@@ -0,0 +1,47 @@
1
+ resource "aws_efs_access_point" "lambda_ap" {
2
+ file_system_id = local.efs_id
3
+
4
+ posix_user {
5
+ uid = 1000
6
+ gid = 1000
7
+ }
8
+
9
+ root_directory {
10
+ path = var.mount_point
11
+ creation_info {
12
+ owner_uid = 1000
13
+ owner_gid = 1000
14
+ permissions = "755"
15
+ }
16
+ }
17
+ }
18
+
19
+ resource "aws_lambda_function" "this" {
20
+ function_name = var.function_name
21
+ package_type = "Image"
22
+
23
+ image_uri = var.image
24
+
25
+ role = var.role_arn
26
+
27
+ timeout = var.timeout
28
+ memory_size = var.memory
29
+
30
+ environment {
31
+ variables = var.environment_variables
32
+ }
33
+
34
+ vpc_config {
35
+ subnet_ids = data.aws_subnets.default_vpc_subnets.ids
36
+ security_group_ids = local.security_group_ids
37
+ }
38
+
39
+ file_system_config {
40
+ arn = aws_efs_access_point.lambda_ap.arn
41
+ local_mount_path = "/mnt/efs"
42
+ }
43
+
44
+ image_config {
45
+ command = ["app.handler"]
46
+ }
47
+ }
@@ -0,0 +1,44 @@
1
+ variable "function_name" {
2
+ description = "Lambda function name"
3
+ type = string
4
+ }
5
+ variable "timeout" {
6
+ description = "Timeout for call"
7
+ type = number
8
+ default = 30
9
+ }
10
+ variable "environment_variables" {
11
+ type = map(string)
12
+ description = "A map of environment variables to pass to the resource"
13
+ default = {}
14
+ }
15
+ variable "role_arn" {
16
+ }
17
+ variable "image" {
18
+ }
19
+ variable "memory" {
20
+ type = number
21
+ description = "The memory (MiB) for the task"
22
+ default = 512
23
+ }
24
+
25
+ variable "network" {
26
+ description = "Name of the remote state block to use for the network"
27
+ }
28
+
29
+ variable "efs" {
30
+ description = "Name of the remote state block to use for the EFS"
31
+ }
32
+
33
+ variable "sg_keys" {
34
+ description = "List of output names in the remote state representing security group IDs"
35
+ type = list(string)
36
+ default = ["aws_network_efs_sg_id", "aws_network_ssh_sg_id"]
37
+ }
38
+
39
+ variable "mount_point" {
40
+ description = "Where to mount the efs drive"
41
+ type = string
42
+ default = "/mnt/efs"
43
+ }
44
+
@@ -0,0 +1,12 @@
1
+ # Get default VPC (optional if you want to restrict to default VPC)
2
+ data "aws_vpc" "default" {
3
+ default = true
4
+ }
5
+
6
+ # Get all subnets in the region (filtered to default VPC if needed)
7
+ data "aws_subnets" "all" {
8
+ filter {
9
+ name = "vpc-id"
10
+ values = [data.aws_vpc.default.id]
11
+ }
12
+ }
@@ -0,0 +1,6 @@
1
+ locals {
2
+ security_group_ids = [
3
+ for sg_key in var.sg_keys :
4
+ lookup(var.remote.outputs, sg_key, null)
5
+ ]
6
+ }
@@ -0,0 +1,14 @@
1
+ resource "aws_efs_file_system" "this" {
2
+ creation_token = "herlab-efs"
3
+ tags = {
4
+ Name = "HERLab main EFS"
5
+ }
6
+ }
7
+
8
+ resource "aws_efs_mount_target" "this" {
9
+ for_each = toset(data.aws_subnets.all.ids)
10
+
11
+ file_system_id = aws_efs_file_system.this.id
12
+ subnet_id = each.value
13
+ security_groups = local.security_group_ids
14
+ }
@@ -0,0 +1,3 @@
1
+ output "id" {
2
+ value = aws_efs_file_system.this.id
3
+ }
@@ -0,0 +1,9 @@
1
+ variable "remote" {
2
+ description = "Name of the remote state block to use"
3
+ }
4
+
5
+ variable "sg_keys" {
6
+ description = "List of output names in the remote state representing security group IDs"
7
+ type = list(string)
8
+ default = ["aws_network_efs_sg_id"]
9
+ }
@@ -0,0 +1,11 @@
1
+ data "aws_ami" "amazon_linux_2" {
2
+ most_recent = true
3
+ owners = ["amazon"]
4
+
5
+ # Filter for Amazon Linux 2 AMIs
6
+ filter {
7
+ name = "name"
8
+ values = ["amzn2-ami-hvm-*-x86_64-gp2"]
9
+ }
10
+ }
11
+
@@ -0,0 +1,8 @@
1
+ locals {
2
+ security_group_ids = [
3
+ for sg_key in var.sg_keys :
4
+ lookup(var.network.outputs, sg_key, null)
5
+ ]
6
+
7
+ efs_id = lookup(var.efs.outputs, "aws_efs_id", null)
8
+ }
@@ -0,0 +1,32 @@
1
+ resource "aws_key_pair" "this" {
2
+ key_name = "my-key"
3
+ public_key = file("~/.ssh/id_rsa.pub") # Adjust if your key is elsewhere
4
+ }
5
+
6
+ resource "aws_instance" "this" {
7
+ ami = data.aws_ami.amazon_linux_2.id
8
+ instance_type = "t2.micro"
9
+ iam_instance_profile = var.policies.outputs.ec2_host_profile_id
10
+
11
+ key_name = aws_key_pair.this.key_name
12
+
13
+ tags = {
14
+ Name = "EFS-Service"
15
+ }
16
+
17
+ # Open port 22 for SSH
18
+ vpc_security_group_ids = local.security_group_ids
19
+
20
+ user_data = <<-EOF
21
+ #cloud-config
22
+ package_update: true
23
+ package_upgrade: true
24
+ packages:
25
+ - amazon-efs-utils
26
+ runcmd:
27
+ - mkdir -p ${var.mount_point}
28
+ - mount -t efs -o tls ${local.efs_id}:/ ${var.mount_point}
29
+ - echo "${local.efs_id}:/ ${var.mount_point} efs defaults,_netdev 0 0" >> /etc/fstab
30
+ EOF
31
+ }
32
+
@@ -0,0 +1,3 @@
1
+ output "public_ip" {
2
+ value = aws_instance.this.public_ip
3
+ }
@@ -0,0 +1,28 @@
1
+ variable "network" {
2
+ description = "Name of the remote state block to use for the network"
3
+ }
4
+
5
+ variable "efs" {
6
+ description = "Name of the remote state block to use for the EFS"
7
+ }
8
+
9
+ variable "policies" {
10
+ description = "Name of the remote state block to use for the policies"
11
+ }
12
+
13
+ variable "sg_keys" {
14
+ description = "List of output names in the remote state representing security group IDs"
15
+ type = list(string)
16
+ default = ["aws_network_efs_sg_id", "aws_network_ssh_sg_id"]
17
+ }
18
+ variable "mount_point" {
19
+ description = "Where to mount the efs drive"
20
+ type = string
21
+ default = "/mnt/efs"
22
+ }
23
+
24
+ #variable "iam_instance_profile" {
25
+ # description = "Instance profile"
26
+ # type = string
27
+ # default = null
28
+ #}
@@ -0,0 +1,8 @@
1
+ locals {
2
+ security_group_ids = [
3
+ for sg_key in var.sg_keys :
4
+ lookup(var.network.outputs, sg_key, null)
5
+ ]
6
+
7
+ efs_id = lookup(var.efs.outputs, "aws_efs_id", null)
8
+ }
@@ -0,0 +1,41 @@
1
+ resource "aws_ecs_task_definition" "this" {
2
+ family = var.task_family
3
+ requires_compatibilities = ["FARGATE"]
4
+ network_mode = "awsvpc"
5
+ cpu = var.cpu
6
+ memory = var.memory
7
+ execution_role_arn = var.policies.outputs.fargate_execution_role_arn
8
+ task_role_arn = var.policies.outputs.fargate_task_role_arn
9
+
10
+ container_definitions = jsonencode([
11
+ {
12
+ name = var.container_name
13
+ image = var.image
14
+ user = var.user
15
+ essential = true
16
+ portMappings = var.port_mappings
17
+ //entryPoint = var.entry_point
18
+ command = var.command
19
+ environment = var.environment
20
+
21
+ mountPoints = [
22
+ {
23
+ sourceVolume = "efs-volume"
24
+ containerPath = var.mount_point
25
+ }
26
+ ]
27
+ }
28
+ ])
29
+
30
+ volume {
31
+ name = "efs-volume"
32
+ efs_volume_configuration {
33
+ file_system_id = local.efs_id
34
+ root_directory = "/"
35
+ }
36
+ }
37
+ }
38
+
39
+ resource "aws_ecs_cluster" "this" {
40
+ name = "${var.task_family}_cluster"
41
+ }
@@ -0,0 +1,84 @@
1
+ variable "network" {
2
+ description = "Name of the remote state block to use for the network"
3
+ }
4
+
5
+ variable "efs" {
6
+ description = "Name of the remote state block to use for the EFS"
7
+ }
8
+
9
+ variable "policies" {
10
+ description = "Name of the remote state block to use for the policies"
11
+ }
12
+
13
+ variable "sg_keys" {
14
+ description = "List of output names in the remote state representing security group IDs"
15
+ type = list(string)
16
+ default = ["aws_network_efs_sg_id", "aws_network_ssh_sg_id"]
17
+ }
18
+
19
+ variable "mount_point" {
20
+ description = "Where to mount the efs drive"
21
+ type = string
22
+ default = "/mnt/efs"
23
+ }
24
+
25
+ variable "task_family" {
26
+ type = string
27
+ description = "The family name of the ECS task definition"
28
+ }
29
+
30
+ variable "cpu" {
31
+ type = number
32
+ description = "The CPU units for the task"
33
+ default = 256
34
+ }
35
+
36
+ variable "memory" {
37
+ type = number
38
+ description = "The memory (MiB) for the task"
39
+ default = 512
40
+ }
41
+
42
+ variable "container_name" {
43
+ type = string
44
+ description = "Name of the container"
45
+ default = "app"
46
+ }
47
+
48
+ variable "image" {
49
+ type = string
50
+ description = "Docker image URL for the container"
51
+ }
52
+
53
+ variable "user" {
54
+ description = "User to use"
55
+ type = string
56
+ default = null
57
+ }
58
+
59
+ variable "port_mappings" {
60
+ type = list(object({
61
+ containerPort = number
62
+ hostPort = number
63
+ protocol = string
64
+ }))
65
+ description = "List of port mappings for the container"
66
+ default = []
67
+ }
68
+
69
+ variable "command" {
70
+ type = list(string)
71
+ description = "Command to run"
72
+ }
73
+
74
+ variable "entry_point" {
75
+ type = list(string)
76
+ description = "Container entry point"
77
+ default = ["bash"]
78
+ }
79
+
80
+ variable "environment" {
81
+ type = map(string)
82
+ description = "A map of environment variables to pass to the resource"
83
+ default = null
84
+ }
@@ -0,0 +1,5 @@
1
+ resource "aws_iam_instance_profile" "this" {
2
+ name = var.profile_name
3
+ role = var.role
4
+ }
5
+
@@ -0,0 +1,15 @@
1
+ output "arn" {
2
+ description = "Instance profile arn"
3
+ value = aws_iam_instance_profile.this.arn
4
+ }
5
+
6
+ output "profile_name" {
7
+ description = "Instance profile name"
8
+ value = aws_iam_instance_profile.this.name
9
+ }
10
+
11
+ output "id" {
12
+ description = "Instance profile id"
13
+ value = aws_iam_instance_profile.this.id
14
+ }
15
+
@@ -0,0 +1,9 @@
1
+ variable "role" {
2
+ description = "Role to assign to the instance profile"
3
+ type = string
4
+ }
5
+
6
+ variable "profile_name" {
7
+ description = "Profile name"
8
+ type = string
9
+ }
@@ -5,7 +5,7 @@ resource "aws_lambda_function" "this" {
5
5
  filename = var.filename
6
6
  source_code_hash = filebase64sha256(var.filename)
7
7
  timeout = var.timeout
8
- role = var.role
8
+ role = var.policies.outputs.lambda_execution_role_arn
9
9
 
10
10
  environment {
11
11
  variables = var.environment_variables
@@ -5,7 +5,7 @@ variable "function_name" {
5
5
  variable "runtime" {
6
6
  description = "Ruby runtime"
7
7
  type = string
8
- default = "ruby3.2"
8
+ default = "ruby3.3"
9
9
  }
10
10
  variable "filename" {
11
11
  description = "ZIP filename with lambda handler"
@@ -21,7 +21,6 @@ variable "environment_variables" {
21
21
  description = "A map of environment variables to pass to the resource"
22
22
  default = {}
23
23
  }
24
- variable "role" {
25
- description = "Role to assume"
26
- type = string
24
+ variable "policies" {
25
+ description = "Name of the remote state block to use for the policies"
27
26
  }
@@ -0,0 +1,15 @@
1
+ data "aws_vpc" "default" {
2
+ filter {
3
+ name = "is-default"
4
+ values = ["true"]
5
+ }
6
+ }
7
+
8
+ # Get all subnets in the default VPC
9
+ data "aws_subnets" "default" {
10
+ filter {
11
+ name = "vpc-id"
12
+ values = [data.aws_vpc.default.id]
13
+ }
14
+ }
15
+
@@ -0,0 +1,41 @@
1
+ resource "aws_security_group" "efs" {
2
+ name = "efs-sg"
3
+ description = "Allow NFS"
4
+
5
+ ingress {
6
+ from_port = 2049
7
+ to_port = 2049
8
+ protocol = "tcp"
9
+ cidr_blocks = ["0.0.0.0/0"]
10
+ }
11
+
12
+ egress {
13
+ from_port = 0
14
+ to_port = 0
15
+ protocol = "-1"
16
+ cidr_blocks = ["0.0.0.0/0"]
17
+ }
18
+
19
+ tags = {
20
+ Name = "efs-sg"
21
+ }
22
+ }
23
+
24
+ resource "aws_security_group" "ssh" {
25
+ name = "allow_ssh"
26
+ description = "Allow SSH inbound traffic"
27
+
28
+ ingress {
29
+ from_port = 22
30
+ to_port = 22
31
+ protocol = "tcp"
32
+ cidr_blocks = ["0.0.0.0/0"] # WARNING: open to the world. Limit this for production.
33
+ }
34
+
35
+ egress {
36
+ from_port = 0
37
+ to_port = 0
38
+ protocol = "-1"
39
+ cidr_blocks = ["0.0.0.0/0"]
40
+ }
41
+ }
@@ -0,0 +1,7 @@
1
+ output "efs_sg_id" {
2
+ value = aws_security_group.efs.id
3
+ }
4
+
5
+ output "ssh_sg_id" {
6
+ value = aws_security_group.ssh.id
7
+ }
File without changes
@@ -0,0 +1,8 @@
1
+ resource "aws_iam_policy" "this" {
2
+ name = var.policy_name
3
+ policy = jsonencode({
4
+ Version = "2012-10-17",
5
+ Statement = var.statement
6
+ })
7
+ }
8
+
@@ -0,0 +1,15 @@
1
+ output "arn" {
2
+ description = "Policy arn"
3
+ value = aws_iam_policy.this.arn
4
+ }
5
+
6
+ output "name" {
7
+ description = "Policy name"
8
+ value = aws_iam_policy.this.name
9
+ }
10
+
11
+ output "id" {
12
+ description = "Policy id"
13
+ value = aws_iam_policy.this.id
14
+ }
15
+
@@ -0,0 +1,12 @@
1
+ variable "policy_name" {
2
+ type = string
3
+ }
4
+
5
+ variable "statement" {
6
+ type = list(object({
7
+ Action = any
8
+ Effect = string
9
+ Resource = any
10
+ }))
11
+ }
12
+
@@ -5,7 +5,7 @@ resource "aws_iam_role" "this" {
5
5
  Version = "2012-10-17"
6
6
  Statement = [
7
7
  {
8
- Action = "sts:AssumeRole"
8
+ Action = var.action
9
9
  Effect = "Allow"
10
10
  Principal = var.principal
11
11
  }
@@ -3,7 +3,7 @@ output "arn" {
3
3
  value = aws_iam_role.this.arn
4
4
  }
5
5
 
6
- output "name" {
6
+ output "role_name" {
7
7
  description = "Role name"
8
8
  value = aws_iam_role.this.name
9
9
  }
@@ -8,4 +8,7 @@ variable "principal" {
8
8
  type = map(any)
9
9
  }
10
10
 
11
-
11
+ variable "action" {
12
+ description = "Action of the role"
13
+ default = "sts:AssumeRole"
14
+ }
@@ -0,0 +1,9 @@
1
+ resource "aws_iam_role_policy" "this" {
2
+ name = var.policy_name
3
+ role = var.role
4
+
5
+ policy = jsonencode({
6
+ Version = "2012-10-17"
7
+ Statement = var.statement
8
+ })
9
+ }