diff --git a/.gitignore b/.gitignore index 6f91ce1..5b0e779 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,17 @@ terraform/internet-gateway/.terraform/providers/registry.terraform.io/hashicorp/ !application-deployments/dev/configs !application-deployments/prod/configs application-deployments/*/configs/*env +terraform/application-wrapper/applications/echo-app/.terraform.lock.hcl +terraform/application-wrapper/applications/echo-app/.terraform/providers/registry.terraform.io/hashicorp/aws/5.35.0/linux_arm64/terraform-provider-aws_v5.35.0_x5 +terraform/application-wrapper/applications/echo-app/.terraform/providers/registry.terraform.io/hashicorp/aws/5.36.0/linux_arm64/terraform-provider-aws_v5.36.0_x5 +terraform/application-wrapper/applications/echo-app/.terraform/providers/registry.terraform.io/hashicorp/random/3.6.0/linux_arm64/terraform-provider-random_v3.6.0_x5 +terraform/application-wrapper/applications/echo-app/providers/registry.terraform.io/hashicorp/aws/5.36.0/linux_arm64/terraform-provider-aws_v5.36.0_x5 +terraform/application-wrapper/applications/echo-app/providers/registry.terraform.io/hashicorp/random/3.6.0/linux_arm64/terraform-provider-random_v3.6.0_x5 +terraform/application-wrapper/applications/echo-app-2/providers/registry.terraform.io/hashicorp/aws/5.36.0/linux_arm64/terraform-provider-aws_v5.36.0_x5 +terraform/application-wrapper/applications/echo-app-2/providers/registry.terraform.io/hashicorp/random/3.6.0/linux_arm64/terraform-provider-random_v3.6.0_x5 +terraform/application-wrapper/applications/echo-app-2/tfplan +terraform/remote-state-application/.terraform.lock.hcl +terraform/remote-state-application/terraform.tfstate.backup +terraform/remote-state-application/tfplan +terraform/remote-state-application/.terraform/providers/registry.terraform.io/hashicorp/archive/2.4.2/linux_arm64/terraform-provider-archive_v2.4.2_x5 +terraform/remote-state-application/.terraform/providers/registry.terraform.io/hashicorp/random/3.6.0/linux_arm64/terraform-provider-random_v3.6.0_x5 diff --git a/Makefile b/Makefile index 5d55d6b..2be42fa 100644 --- a/Makefile +++ b/Makefile @@ -33,26 +33,25 @@ create-route: docker-compose run app-deploy -cmd create-route delete-route: - docker-compose run app-deploy -cmd delete-route + docker-compose run app-deploy -cmd delete-route + +create-application: + docker-compose run app-deploy -cmd create-application + +destroy-application: + docker-compose run app-deploy -cmd destroy-application + +create-remote-state-app: + docker-compose run app-deploy -cmd create-remote-state-app deploy: aws ecr get-login-password --profile ${AWS_PROFILE} --region ${AWS_DEFAULT_REGION} | docker login --username AWS --password-stdin ${ACCOUNT}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com @echo "Deploying app" - cd $(WORKING_DIR)/terraform/application-wrapper/applications ; git clone "https://${APP_GIT_REPOSITORY}" app - cd $(WORKING_DIR)/terraform/application-wrapper/applications/app - cp $(WORKING_DIR)/terraform/application-wrapper/applications/app/${ENTRYPOINT} $(WORKING_DIR)/terraform/application-wrapper/${ENTRYPOINT} - cp $(WORKING_DIR)/terraform/application-wrapper/applications/app/Dockerfile $(WORKING_DIR)/terraform/application-wrapper/Dockerfile - ifeq ($(ENTRYPOINT),main.py) - cp $(WORKING_DIR)/terraform/application-wrapper/main.py.nf $(WORKING_DIR)/terraform/application-wrapper/main.nf - cp $(WORKING_DIR)/terraform/application-wrapper/applications/app/requirements.txt $(WORKING_DIR)/terraform/application-wrapper/requirements.txt - else ifeq ($(ENTRYPOINT),main.R) - cp $(WORKING_DIR)/terraform/application-wrapper/main.R.nf $(WORKING_DIR)/terraform/application-wrapper/main.nf - cp -R $(WORKING_DIR)/terraform/application-wrapper/applications/app/dependencies/* $(WORKING_DIR)/terraform/application-wrapper/dependencies - endif - rm -rf $(WORKING_DIR)/terraform/application-wrapper/applications/app - cd $(WORKING_DIR)/terraform/application-wrapper; docker buildx build --platform linux/amd64 --progress=plain -t pennsieve/app-wrapper . - docker tag pennsieve/app-wrapper ${APP_REPO} + cd $(WORKING_DIR)/terraform/application-wrapper/applications ; git clone -b ${APP_GIT_BRANCH} --single-branch "https://${APP_GIT_REPOSITORY}" app + cd $(WORKING_DIR)/terraform/application-wrapper/applications/app ; docker buildx build --platform linux/amd64 --progress=plain -t pennsieve/${APP_NAME} . + docker tag pennsieve/${APP_NAME} ${APP_REPO} docker push ${APP_REPO} + rm -rf $(WORKING_DIR)/terraform/application-wrapper/applications/app @echo "Deploying post processor" cd $(WORKING_DIR)/terraform/post-processor; docker buildx build --platform linux/amd64 --progress=plain -t pennsieve/post-processor . docker tag pennsieve/post-processor ${POST_PROCESSOR_REPO} diff --git a/main.go b/main.go index 4c761ec..794ad88 100644 --- a/main.go +++ b/main.go @@ -8,7 +8,9 @@ import ( ) var TerraformStateDirectory = "/service/terraform/remote-state" +var TerraformAppStateDirectory = "/service/terraform/remote-state-application" var TerraformGatewayDirectory = "/service/terraform/internet-gateway" +var TerraformApplicationDirectory = "/service/terraform/application-wrapper" func main() { cmdPtr := flag.String("cmd", "plan", "command to execute") @@ -25,6 +27,17 @@ func main() { fmt.Println(output) } + // Remote State Application Management + if *cmdPtr == "create-remote-state-app" || *cmdPtr == "remote-state-app" { + cmd := exec.Command("/bin/sh", "./scripts/remote-state-application.sh", TerraformAppStateDirectory, *cmdPtr) + out, err := cmd.Output() + if err != nil { + log.Fatalf("error %s", err) + } + output := string(out) + fmt.Println(output) + } + // Creating a route in route table (once-off) if *cmdPtr == "create-route" || *cmdPtr == "delete-route" { cmd := exec.Command("/bin/sh", "./scripts/routing-table.sh", TerraformGatewayDirectory, *cmdPtr) @@ -47,5 +60,16 @@ func main() { fmt.Println(output) } + // application creation + if *cmdPtr == "create-application" || *cmdPtr == "destroy-application" { + cmd := exec.Command("/bin/sh", "./scripts/application.sh", TerraformApplicationDirectory, *cmdPtr) + out, err := cmd.Output() + output := string(out) + fmt.Println(output) + if err != nil { + log.Fatalf("error %s", err.Error()) + } + } + log.Println("done") } diff --git a/scripts/application.sh b/scripts/application.sh new file mode 100755 index 0000000..a1838e7 --- /dev/null +++ b/scripts/application.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +cd $1 +export TF_DATA_DIR="${1}/applications/${APP_NAME}" +mkdir -p $TF_DATA_DIR +echo $TF_DATA_DIR +PLAN_FILE="$TF_DATA_DIR/tfplan" + +echo "Creating tfvars config" + /bin/cat > "${TF_DATA_DIR}/${APP_NAME}.tfvars" < "$TF_DATA_DIR/${APP_NAME}.tfbackend" < plan.log + terraform apply tfplan > apply.log +else + echo "deleting ..." + terraform apply -destroy -auto-approve +fi \ No newline at end of file diff --git a/terraform/application-wrapper/.gitignore b/terraform/application-wrapper/.gitignore deleted file mode 100644 index 5d0f383..0000000 --- a/terraform/application-wrapper/.gitignore +++ /dev/null @@ -1 +0,0 @@ -dev.env \ No newline at end of file diff --git a/terraform/application-wrapper/Dockerfile b/terraform/application-wrapper/Dockerfile deleted file mode 100644 index 56afad2..0000000 --- a/terraform/application-wrapper/Dockerfile +++ /dev/null @@ -1,49 +0,0 @@ -FROM rocker/r-ver:4.2.1 - -WORKDIR /service - -RUN apt clean && apt-get update -# install dependencies -RUN apt-get -y install wget && apt-get -y install gnupg && apt-get -y install curl -RUN wget -O - https://packages.adoptium.net/artifactory/api/gpg/key/public | apt-key add - -RUN echo "deb https://packages.adoptium.net/artifactory/deb $(awk -F= '/^VERSION_CODENAME/{print$2}' /etc/os-release) main" | tee /etc/apt/sources.list.d/adoptium.list -RUN apt-get update -# next flow dependencies -RUN apt-get -y install temurin-17-jdk - -# install nextflow -RUN wget -qO- https://get.nextflow.io | bash && chmod +x nextflow && cp ./nextflow /usr/local -RUN apt-get -y install graphviz - -ENV PATH="${PATH}:/usr/local/" - -# cleanup -RUN rm -f /service/nextflow - -# set desired nextflow version -RUN export NXF_VER=23.04.1 - -# install Go -RUN wget https://go.dev/dl/go1.21.0.linux-amd64.tar.gz - -RUN rm -rf /usr/local/go && tar -C /usr/local -xzf go1.21.0.linux-amd64.tar.gz - -ENV PATH="${PATH}:/usr/local/go/bin" - -RUN apt-get install software-properties-common && add-apt-repository ppa:deadsnakes/ppa && sudo apt-get update & apt-get -y install python3.8 -RUN python3.8 --version - -# cleanup -RUN rm -f go1.21.0.linux-amd64.tar.gz - -COPY . . - -RUN ls /service - -RUN go build -o /service/main main.go - -RUN mkdir -p data - -# Add additional dependencies below ... - -ENTRYPOINT [ "/service/main" ] \ No newline at end of file diff --git a/terraform/application-wrapper/Dockerfile_arm64 b/terraform/application-wrapper/Dockerfile_arm64 deleted file mode 100644 index 556f086..0000000 --- a/terraform/application-wrapper/Dockerfile_arm64 +++ /dev/null @@ -1,47 +0,0 @@ -FROM rocker/r-ver:4.2.1 - -WORKDIR /service - -RUN apt clean && apt-get update -# install dependencies -RUN apt-get -y install wget && apt-get -y install gnupg && apt-get -y install curl -RUN wget -O - https://packages.adoptium.net/artifactory/api/gpg/key/public | apt-key add - -RUN echo "deb https://packages.adoptium.net/artifactory/deb $(awk -F= '/^VERSION_CODENAME/{print$2}' /etc/os-release) main" | tee /etc/apt/sources.list.d/adoptium.list -RUN apt-get update -# next flow dependencies -RUN apt-get -y install temurin-17-jdk - -# install nextflow -RUN wget -qO- https://get.nextflow.io | bash && chmod +x nextflow && cp ./nextflow /usr/local -RUN apt-get -y install graphviz - -ENV PATH="${PATH}:/usr/local/" - -# cleanup -RUN rm -f /service/nextflow - -# set desired nextflow version -RUN export NXF_VER=23.04.1 - -# install Go -RUN wget https://go.dev/dl/go1.21.0.linux-arm64.tar.gz - -RUN rm -rf /usr/local/go && tar -C /usr/local -xzf go1.21.0.linux-arm64.tar.gz - -ENV PATH="${PATH}:/usr/local/go/bin" - -RUN apt-get install software-properties-common && add-apt-repository ppa:deadsnakes/ppa && sudo apt-get update & apt-get -y install python3.8 -RUN python3.8 --version - -# cleanup -RUN rm -f go1.21.0.linux-arm64.tar.gz - -COPY . . - -RUN ls /service - -RUN go build -o /service/main main.go - -RUN mkdir -p data - -ENTRYPOINT [ "/service/main" ] \ No newline at end of file diff --git a/terraform/application-wrapper/Readme.md b/terraform/application-wrapper/Readme.md deleted file mode 100644 index 0ca8104..0000000 --- a/terraform/application-wrapper/Readme.md +++ /dev/null @@ -1,11 +0,0 @@ -To build: - -`docker build -t pennsieve/app-wrapper .` - -On arm64 architectures: - -`docker build -f Dockerfile_arm64 -t pennsieve/app-wrapper .` - -To run: - -`docker-compose up --build` diff --git a/terraform/application-wrapper/backend.tf b/terraform/application-wrapper/backend.tf new file mode 100644 index 0000000..d73d4f8 --- /dev/null +++ b/terraform/application-wrapper/backend.tf @@ -0,0 +1,5 @@ +terraform { + backend "s3" { + region = "us-east-1" + } +} \ No newline at end of file diff --git a/terraform/application-wrapper/data.tf b/terraform/application-wrapper/data.tf new file mode 100644 index 0000000..50a5584 --- /dev/null +++ b/terraform/application-wrapper/data.tf @@ -0,0 +1,10 @@ +data "terraform_remote_state" "compute_node" { + backend = "s3" + + config = { + bucket = "i3h-dev-terraform-state-v2" + key = "dev/github.com/Penn-I3H/python-application-template/terraform.tfstate" + region = "us-east-1" + } + +} \ No newline at end of file diff --git a/terraform/application-wrapper/dependencies/README.md b/terraform/application-wrapper/dependencies/README.md deleted file mode 100644 index 369f229..0000000 --- a/terraform/application-wrapper/dependencies/README.md +++ /dev/null @@ -1 +0,0 @@ -Program dependencies directory \ No newline at end of file diff --git a/terraform/application-wrapper/dev.env.example b/terraform/application-wrapper/dev.env.example deleted file mode 100644 index 3dfb24c..0000000 --- a/terraform/application-wrapper/dev.env.example +++ /dev/null @@ -1,6 +0,0 @@ -BASE_DIR=/service/data -SESSION_TOKEN= -PENNSIEVE_API_HOST=https://api.pennsieve.net -PENNSIEVE_API_HOST2=https://api2.pennsieve.net -INTEGRATION_ID= -PENNSIEVE_API_HOST=https://api2.pennsieve.net \ No newline at end of file diff --git a/terraform/application-wrapper/docker-compose.yml b/terraform/application-wrapper/docker-compose.yml deleted file mode 100644 index a20cf8a..0000000 --- a/terraform/application-wrapper/docker-compose.yml +++ /dev/null @@ -1,15 +0,0 @@ -version: '3.9' - -services: - - app-wrapper: - env_file: - - dev.env - image: pennsieve/app-wrapper - volumes: - - ./data:/service/data - container_name: app-wrapper - build: - context: . - dockerfile: ./Dockerfile_arm64 # change to Dockerfile on Linux - entrypoint: /service/main diff --git a/terraform/application-wrapper/ecr.tf b/terraform/application-wrapper/ecr.tf new file mode 100644 index 0000000..9dc63b8 --- /dev/null +++ b/terraform/application-wrapper/ecr.tf @@ -0,0 +1,8 @@ +resource "aws_ecr_repository" "app" { + name = "${var.app_name}-${random_uuid.val.id}" + image_tag_mutability = "MUTABLE" + + image_scanning_configuration { + scan_on_push = false # consider implications of setting to true + } +} \ No newline at end of file diff --git a/terraform/application-wrapper/ecs.tf b/terraform/application-wrapper/ecs.tf new file mode 100644 index 0000000..4d0eb39 --- /dev/null +++ b/terraform/application-wrapper/ecs.tf @@ -0,0 +1,49 @@ +// ECS Task definition +resource "aws_ecs_task_definition" "application" { + family = "${var.app_name}-${random_uuid.val.id}" + requires_compatibilities = ["FARGATE"] + network_mode = "awsvpc" + cpu = var.app_cpu + memory = var.app_memory + task_role_arn = aws_iam_role.task_role_for_app.arn + execution_role_arn = aws_iam_role.execution_role_for_app.arn + + container_definitions = jsonencode([ + { + name = "${var.app_name}-${random_uuid.val.id}" + image = aws_ecr_repository.app.repository_url + essential = true + portMappings = [ + { + containerPort = 8081 + hostPort = 8081 + } + ] + mountPoints = [ + { + sourceVolume = "${var.app_name}-storage-${random_uuid.val.id}" + containerPath = "/mnt/efs" + readOnly = false + } + ] + logConfiguration = { + logDriver = "awslogs" + options = { + awslogs-group = "/ecs/${var.app_name}/${random_uuid.val.id}" + awslogs-region = var.region + awslogs-stream-prefix = "ecs" + awslogs-create-group = "true" + } + } + } + ]) + + volume { + name = "${var.app_name}-storage-${random_uuid.val.id}" + + efs_volume_configuration { + file_system_id = data.terraform_remote_state.compute_node.outputs.efs_id + root_directory = "/" + } + } +} \ No newline at end of file diff --git a/terraform/application-wrapper/go.mod b/terraform/application-wrapper/go.mod deleted file mode 100644 index b8d6104..0000000 --- a/terraform/application-wrapper/go.mod +++ /dev/null @@ -1,25 +0,0 @@ -module github.com/pennsieve/app-wrapper - -go 1.21 - -require ( - github.com/aws/aws-sdk-go v1.46.4 - github.com/aws/aws-sdk-go-v2/config v1.19.1 - github.com/aws/aws-sdk-go-v2/service/ecs v1.32.0 - github.com/google/uuid v1.3.1 -) - -require ( - github.com/aws/aws-sdk-go-v2 v1.22.1 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.13.43 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.13 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.1 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.1 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.45 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.37 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.15.2 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.3 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.23.2 // indirect - github.com/aws/smithy-go v1.16.0 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect -) diff --git a/terraform/application-wrapper/go.sum b/terraform/application-wrapper/go.sum deleted file mode 100644 index 00818a6..0000000 --- a/terraform/application-wrapper/go.sum +++ /dev/null @@ -1,76 +0,0 @@ -github.com/aws/aws-sdk-go v1.46.4 h1:48tKgtm9VMPkb6y7HuYlsfhQmoIRAsTEXTsWLVlty4M= -github.com/aws/aws-sdk-go v1.46.4/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= -github.com/aws/aws-sdk-go-v2 v1.21.2/go.mod h1:ErQhvNuEMhJjweavOYhxVkn2RUx7kQXVATHrjKtxIpM= -github.com/aws/aws-sdk-go-v2 v1.22.1 h1:sjnni/AuoTXxHitsIdT0FwmqUuNUuHtufcVDErVFT9U= -github.com/aws/aws-sdk-go-v2 v1.22.1/go.mod h1:Kd0OJtkW3Q0M0lUWGszapWjEvrXDzRW+D21JNsroB+c= -github.com/aws/aws-sdk-go-v2/config v1.19.1 h1:oe3vqcGftyk40icfLymhhhNysAwk0NfiwkDi2GTPMXs= -github.com/aws/aws-sdk-go-v2/config v1.19.1/go.mod h1:ZwDUgFnQgsazQTnWfeLWk5GjeqTQTL8lMkoE1UXzxdE= -github.com/aws/aws-sdk-go-v2/credentials v1.13.43 h1:LU8vo40zBlo3R7bAvBVy/ku4nxGEyZe9N8MqAeFTzF8= -github.com/aws/aws-sdk-go-v2/credentials v1.13.43/go.mod h1:zWJBz1Yf1ZtX5NGax9ZdNjhhI4rgjfgsyk6vTY1yfVg= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.13 h1:PIktER+hwIG286DqXyvVENjgLTAwGgoeriLDD5C+YlQ= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.13/go.mod h1:f/Ib/qYjhV2/qdsf79H3QP/eRE4AkVyEf6sk7XfZ1tg= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.43/go.mod h1:auo+PiyLl0n1l8A0e8RIeR8tOzYPfZZH/JNlrJ8igTQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.1 h1:fi1ga6WysOyYb5PAf3Exd6B5GiSNpnZim4h1rhlBqx0= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.1/go.mod h1:V5CY8wNurvPUibTi9mwqUqpiFZ5LnioKWIFUDtIzdI8= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.37/go.mod h1:Qe+2KtKml+FEsQF/DHmDV+xjtche/hwoF75EG4UlHW8= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.1 h1:ZpaV/j48RlPc4AmOZuPv22pJliXjXq8/reL63YzyFnw= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.1/go.mod h1:R8aXraabD2e3qv1csxM14/X9WF4wFMIY0kH4YEtYD5M= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.45 h1:hze8YsjSh8Wl1rYa1CJpRmXP21BvOBuc76YhW0HsuQ4= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.45/go.mod h1:lD5M20o09/LCuQ2mE62Mb/iSdSlCNuj6H5ci7tW7OsE= -github.com/aws/aws-sdk-go-v2/service/ecs v1.32.0 h1:PH9chTOUwd0PPUEwunePjUYp6QD0KsZvDcUy6VToKVQ= -github.com/aws/aws-sdk-go-v2/service/ecs v1.32.0/go.mod h1:4HwGjM2Z7cMaLV1ddXhKeI1o+hRJhp7A0WnvcgjWyRI= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.37 h1:WWZA/I2K4ptBS1kg0kV1JbBtG/umed0vwHRrmcr9z7k= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.37/go.mod h1:vBmDnwWXWxNPFRMmG2m/3MKOe+xEcMDo1tanpaWCcck= -github.com/aws/aws-sdk-go-v2/service/sso v1.15.2 h1:JuPGc7IkOP4AaqcZSIcyqLpFSqBWK32rM9+a1g6u73k= -github.com/aws/aws-sdk-go-v2/service/sso v1.15.2/go.mod h1:gsL4keucRCgW+xA85ALBpRFfdSLH4kHOVSnLMSuBECo= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.3 h1:HFiiRkf1SdaAmV3/BHOFZ9DjFynPHj8G/UIO1lQS+fk= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.3/go.mod h1:a7bHA82fyUXOm+ZSWKU6PIoBxrjSprdLoM8xPYvzYVg= -github.com/aws/aws-sdk-go-v2/service/sts v1.23.2 h1:0BkLfgeDjfZnZ+MhB3ONb01u9pwFYTCZVhlsSSBvlbU= -github.com/aws/aws-sdk-go-v2/service/sts v1.23.2/go.mod h1:Eows6e1uQEsc4ZaHANmsPRzAKcVDrcmjjWiih2+HUUQ= -github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/aws/smithy-go v1.16.0 h1:gJZEH/Fqh+RsvlJ1Zt4tVAtV6bKkp3cC+R6FCZMNzik= -github.com/aws/smithy-go v1.16.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/terraform/application-wrapper/iam.tf b/terraform/application-wrapper/iam.tf new file mode 100644 index 0000000..2468db5 --- /dev/null +++ b/terraform/application-wrapper/iam.tf @@ -0,0 +1,82 @@ +// ## App ## +resource "aws_iam_role" "task_role_for_app" { # + name = "task_role_for_app-${random_uuid.val.id}" + assume_role_policy = data.aws_iam_policy_document.app_role_assume_role.json + managed_policy_arns = [aws_iam_policy.app_efs_policy.arn] +} + +# resource should be specific EFS ID +resource "aws_iam_policy" "app_efs_policy" { # + name = "app_role_efs_policy-${random_uuid.val.id}" + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = [ + "elasticfilesystem:ClientMount", + "elasticfilesystem:ClientWrite", + "elasticfilesystem:ClientRootAccess" + ] + Effect = "Allow" + Resource = "*" + }, + ] + }) +} + +data "aws_iam_policy_document" "app_role_assume_role" { # + statement { + effect = "Allow" + + principals { + type = "Service" + identifiers = ["ecs-tasks.amazonaws.com"] + } + + actions = ["sts:AssumeRole"] + } +} + +// ECS Task Execution IAM role +resource "aws_iam_role" "execution_role_for_app" { # + name = "execution_role_for_app-${random_uuid.val.id}" + assume_role_policy = data.aws_iam_policy_document.app_execution_role_assume_role.json + managed_policy_arns = [aws_iam_policy.app_execution_role_policy.arn] +} + +resource "aws_iam_policy" "app_execution_role_policy" { # + name = "app_task_execution_role_policy-${random_uuid.val.id}" + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = [ + "ecr:GetAuthorizationToken", + "ecr:BatchCheckLayerAvailability", + "ecr:GetDownloadUrlForLayer", + "ecr:BatchGetImage", + "logs:CreateLogStream", + "logs:PutLogEvents", + "logs:CreateLogGroup" + ] + Effect = "Allow" + Resource = "*" + }, + ] + }) +} + +data "aws_iam_policy_document" "app_execution_role_assume_role" { # + statement { + effect = "Allow" + + principals { + type = "Service" + identifiers = ["ecs-tasks.amazonaws.com"] + } + + actions = ["sts:AssumeRole"] + } +} \ No newline at end of file diff --git a/terraform/application-wrapper/main.R.nf b/terraform/application-wrapper/main.R.nf deleted file mode 100644 index 7fa3ff8..0000000 --- a/terraform/application-wrapper/main.R.nf +++ /dev/null @@ -1,33 +0,0 @@ -/* - * pipeline input and output parameters - */ -params.execution_script = "$projectDir/main.R" - -log.info """\ - R PIPELINE - =================================== - execution_script : ${params.execution_script} - outputdir : ${params.outputDir} - inputdir : ${params.inputDir} - integrationID : ${params.integrationID} - """ - .stripIndent() - -process RPipeline { - debug true - - output: stdout - - script: - """ - Rscript ${params.execution_script} ${params.inputDir} ${params.outputDir} - """ -} - -workflow { - RPipeline() -} - -workflow.onComplete { - log.info ( workflow.success ? "\nDone! Your output can be found at this location --> $params.outputDir\n" : "Oops .. something went wrong" ) -} \ No newline at end of file diff --git a/terraform/application-wrapper/main.go b/terraform/application-wrapper/main.go deleted file mode 100644 index 367cfba..0000000 --- a/terraform/application-wrapper/main.go +++ /dev/null @@ -1,48 +0,0 @@ -package main - -import ( - "log" - "os" - "os/exec" - "strings" - - "log/slog" -) - -func main() { - programLevel := new(slog.LevelVar) - logger := slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: programLevel})) - slog.SetDefault(logger) - - inputDir := os.Getenv("INPUT_DIR") - outputDir := os.Getenv("OUTPUT_DIR") - - log.Println("Starting pipeline") - // run pipeline - cmd := exec.Command("nextflow", "run", "/service/main.nf", "-ansi-log", "false", "--inputDir", inputDir, "--outputDir", outputDir) - cmd.Dir = "/service" - var out strings.Builder - var stderr strings.Builder - cmd.Stdout = &out - cmd.Stderr = &stderr - if err := cmd.Run(); err != nil { - logger.Error(err.Error(), - slog.String("error", stderr.String())) - } - log.Println(out.String()) - - // run pipeline - ls := exec.Command("ls", "-alh") - ls.Dir = outputDir - var out2 strings.Builder - var stderr2 strings.Builder - ls.Stdout = &out2 - ls.Stderr = &stderr2 - if err := ls.Run(); err != nil { - logger.Error(err.Error(), - slog.String("error", stderr2.String())) - } - log.Println(out2.String()) - - logger.Info("Processing complete") -} diff --git a/terraform/application-wrapper/main.py.nf b/terraform/application-wrapper/main.py.nf deleted file mode 100644 index ac9d7e7..0000000 --- a/terraform/application-wrapper/main.py.nf +++ /dev/null @@ -1,32 +0,0 @@ -/* - * pipeline input and output parameters - */ -params.execution_script = "$projectDir/main.py" - -log.info """\ - PYTHON PIPELINE - =================================== - execution_script : ${params.execution_script} - outputdir : ${params.outputDir} - inputdir : ${params.inputDir} - """ - .stripIndent() - -process PythonPipeline { - debug true - - output: stdout - - script: - """ - python3.9 ${params.execution_script} ${params.inputDir} ${params.outputDir} - """ -} - -workflow { - PythonPipeline() -} - -workflow.onComplete { - log.info ( workflow.success ? "\nDone! Your output can be found at this location --> $params.outputDir\n" : "Oops .. something went wrong" ) -} \ No newline at end of file diff --git a/terraform/application-wrapper/main.tf b/terraform/application-wrapper/main.tf new file mode 100644 index 0000000..fa0e639 --- /dev/null +++ b/terraform/application-wrapper/main.tf @@ -0,0 +1,4 @@ +provider "aws" {} + +resource "random_uuid" "val" { +} \ No newline at end of file diff --git a/terraform/application-wrapper/outputs.tf b/terraform/application-wrapper/outputs.tf new file mode 100644 index 0000000..d58328b --- /dev/null +++ b/terraform/application-wrapper/outputs.tf @@ -0,0 +1,29 @@ +output "app_ecr_repository" { + description = "App ECR repository" + + value = aws_ecr_repository.app.repository_url +} + +output "app_name" { + description = "App Name" + + value = var.app_name +} + +output "app_id" { + description = "App Identifier" + + value = aws_ecs_task_definition.application.family +} + +output "app_git_url" { + description = "App Git Repository" + + value = var.app_git_url +} + +output "app_region" { + description = "App Region" + + value = var.region +} \ No newline at end of file diff --git a/terraform/application-wrapper/variables.tf b/terraform/application-wrapper/variables.tf new file mode 100644 index 0000000..043d96f --- /dev/null +++ b/terraform/application-wrapper/variables.tf @@ -0,0 +1,18 @@ +variable "region" { + type = string +} +variable "environment" { + type = string +} +variable "app_cpu" { + type = number +} +variable "app_memory" { + type = number +} +variable "app_name" { + type = string +} +variable "app_git_url" { + type = string +} \ No newline at end of file diff --git a/terraform/cloudwatch.tf b/terraform/cloudwatch.tf new file mode 100644 index 0000000..c4fa4fb --- /dev/null +++ b/terraform/cloudwatch.tf @@ -0,0 +1,17 @@ +// Cloudwatch alarm for SQS queue length + +resource "aws_cloudwatch_metric_alarm" "sqs_queue_depth_alarm" { + alarm_name = "messages-in-queue-alarm" + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = "1" + metric_name = "ApproximateNumberOfMessagesVisible" + namespace = "AWS/SQS" + period = "60" + statistic = "Average" + threshold = "1" + treat_missing_data = "notBreaching" + dimensions = { + QueueName = "${aws_sqs_queue.pipeline_queue.name}" + } + alarm_description = "This metric monitors queue depth and triggers an alarm if the average number of messages in the queue is greater than or equal to 1 over a period of 60 seconds." +} \ No newline at end of file diff --git a/terraform/ecr.tf b/terraform/ecr.tf index 553d6fb..e20fe2f 100644 --- a/terraform/ecr.tf +++ b/terraform/ecr.tf @@ -1,12 +1,3 @@ -resource "aws_ecr_repository" "app" { - name = "${var.app_repository}-${random_uuid.val.id}" - image_tag_mutability = "MUTABLE" - - image_scanning_configuration { - scan_on_push = false # consider implications of setting to true - } -} - resource "aws_ecr_repository" "pre-processor" { name = "${var.pre_processor_repository}-${random_uuid.val.id}" image_tag_mutability = "MUTABLE" diff --git a/terraform/ecs.tf b/terraform/ecs.tf index f423b63..b602b04 100644 --- a/terraform/ecs.tf +++ b/terraform/ecs.tf @@ -24,56 +24,6 @@ resource "aws_ecs_cluster" "pipeline_cluster" { } } -// ECS Task definition -resource "aws_ecs_task_definition" "pipeline" { - family = "pipeline-${random_uuid.val.id}" - requires_compatibilities = ["FARGATE"] - network_mode = "awsvpc" - cpu = var.app_cpu - memory = var.app_memory - task_role_arn = aws_iam_role.task_role_for_ecs_task.arn - execution_role_arn = aws_iam_role.execution_role_for_ecs_task.arn - - container_definitions = jsonencode([ - { - name = "pipeline-${random_uuid.val.id}" - image = aws_ecr_repository.app.repository_url - essential = true - portMappings = [ - { - containerPort = 8081 - hostPort = 8081 - } - ] - mountPoints = [ - { - sourceVolume = "pipeline-storage-${random_uuid.val.id}" - containerPath = "/mnt/efs" - readOnly = false - } - ] - logConfiguration = { - logDriver = "awslogs" - options = { - awslogs-group = "/ecs/pipeline/${random_uuid.val.id}" - awslogs-region = var.region - awslogs-stream-prefix = "ecs" - awslogs-create-group = "true" - } - } - } - ]) - - volume { - name = "pipeline-storage-${random_uuid.val.id}" - - efs_volume_configuration { - file_system_id = aws_efs_file_system.pipeline.id - root_directory = "/" - } - } -} - // ECS Task definition - post processor resource "aws_ecs_task_definition" "post-processor" { family = "post-processor-${random_uuid.val.id}" @@ -203,9 +153,7 @@ resource "aws_ecs_task_definition" "workflow-manager" { { name: "PENNSIEVE_API_HOST", value: var.api_host}, { name: "PENNSIEVE_API_HOST2", value: var.api_host2}, { name: "BASE_DIR", value: "/mnt/efs"}, - { name: "REGION", value: var.region}, - { name: "TASK_DEFINITION_NAME", value: aws_ecs_task_definition.pipeline.family}, - { name: "CONTAINER_NAME", value: aws_ecs_task_definition.pipeline.family}, + { name: "REGION", value: var.region} ], essential = true portMappings = [ diff --git a/terraform/iam.tf b/terraform/iam.tf index 15476cb..a4619fe 100644 --- a/terraform/iam.tf +++ b/terraform/iam.tf @@ -30,7 +30,7 @@ resource "aws_iam_policy" "lambda_iam_policy" { policy = data.aws_iam_policy_document.iam_policy_document_gateway.json } -// ## Main App ## +// ## App ## // ECS task IAM role // TODO - this can be updated, as it does not need to invoke a lambda or run an ecs task // TODO - this is shared with the workflow manager and should not be diff --git a/terraform/outputs.tf b/terraform/outputs.tf index 7865428..63e88bb 100644 --- a/terraform/outputs.tf +++ b/terraform/outputs.tf @@ -1,9 +1,3 @@ -output "app_ecr_repository" { - description = "App ECR repository" - - value = aws_ecr_repository.app.repository_url -} - output "pre_processor_ecr_repository" { description = "Pre Processor ECR repository" @@ -34,3 +28,8 @@ output "sqs_url" { value = aws_sqs_queue.pipeline_queue.id } +output "efs_id" { + description = "EFS ID" + + value = aws_efs_file_system.pipeline.id +} \ No newline at end of file diff --git a/terraform/remote-state-application/README.md b/terraform/remote-state-application/README.md new file mode 100644 index 0000000..7223426 --- /dev/null +++ b/terraform/remote-state-application/README.md @@ -0,0 +1 @@ +The remote state S3 bucket needs to be created before the application infrastructure is created. \ No newline at end of file diff --git a/terraform/remote-state-application/application-state-lambda/application-state.py b/terraform/remote-state-application/application-state-lambda/application-state.py new file mode 100644 index 0000000..e0697a3 --- /dev/null +++ b/terraform/remote-state-application/application-state-lambda/application-state.py @@ -0,0 +1,61 @@ +from boto3 import client as boto3_client +from boto3 import resource as boto3_resource +import json +import os +import base64 + +s3 = boto3_client('s3') + +def lambda_handler(event, context): + + print(event) + table_name = os.environ['APPLICATIONS_TABLE'] + + if ('Records' in event): + dynamodb = boto3_client('dynamodb') + s3Object = event['Records'][0]['s3']['object'] + bucket = event['Records'][0]['s3']['bucket'] + + content_object = s3.get_object(Bucket=bucket['name'], Key=s3Object['key'])['Body'] + # Read the contents of the StreamingBody as a bytes object + bytes_obj = content_object.read() + # Decode the bytes object to a string using the appropriate encoding + string_obj = bytes_obj.decode('utf-8') + + json_content = json.loads(string_obj) + outputs = json_content['outputs'] + print(outputs) + + response = dynamodb.put_item( + TableName=table_name, + Item={ + "app_id": {'S':outputs['app_id']['value']} , + "app_ecr_repository": {'S':outputs['app_ecr_repository']['value']}, + "app_name": {'S':outputs['app_name']['value']}, + "app_git_url": {'S':outputs['app_git_url']['value']}, + } + ) + + print(response) + + return { + 'statusCode': 200, + 'body': 'State' + } + + dynamodb = boto3_resource('dynamodb') + if event['isBase64Encoded'] == True: + body = base64.b64decode(event['body']).decode('utf-8') + event['body'] = body + event['isBase64Encoded'] = False + json_body = json.loads(event['body']) + app_id = json_body['app_id'] + + item_key = {'app_id': app_id} + response = dynamodb.Table(table_name).get_item(Key=item_key) + item = response.get('Item') + + return { + 'statusCode': 200, + 'body': item + } \ No newline at end of file diff --git a/terraform/remote-state-application/dynamodb.tf b/terraform/remote-state-application/dynamodb.tf new file mode 100644 index 0000000..41503ff --- /dev/null +++ b/terraform/remote-state-application/dynamodb.tf @@ -0,0 +1,15 @@ +resource "aws_dynamodb_table" "applications_table" { + name = "applications-table" + billing_mode = "PAY_PER_REQUEST" + hash_key = "app_id" + + attribute { + name = "app_id" + type = "S" + } + + ttl { + attribute_name = "TimeToExist" + enabled = true + } +} \ No newline at end of file diff --git a/terraform/remote-state-application/lambda.tf b/terraform/remote-state-application/lambda.tf new file mode 100644 index 0000000..15a244f --- /dev/null +++ b/terraform/remote-state-application/lambda.tf @@ -0,0 +1,160 @@ +// Application Gateway Lambda +resource "aws_lambda_function" "application_state" { + function_name = "application-state-${random_uuid.val.id}" + role = aws_iam_role.iam_for_lambda.arn + handler = "application-state.lambda_handler" # module is name of python file: application + description = "Application State Management" + + s3_bucket = aws_s3_bucket.lambda_bucket.id + s3_key = aws_s3_object.application_state_lambda.key + + source_code_hash = data.archive_file.application_state_lambda.output_base64sha256 + + runtime = "python3.7" # update to 3.11 + timeout = 60 + + environment { + variables = { + APPLICATIONS_TABLE = aws_dynamodb_table.applications_table.name, + } + } +} + +resource "aws_cloudwatch_log_group" "application_state-lambda" { + name = "/aws/lambda/${aws_lambda_function.application_state.function_name}" + + retention_in_days = 30 +} + +resource "aws_lambda_function_url" "app_state" { + function_name = aws_lambda_function.application_state.function_name + authorization_type = "NONE" +} + +# IAM +// Lambda gateway function +// allow lambda to access resources in your AWS account +data "aws_iam_policy_document" "assume_role" { + statement { + effect = "Allow" + + principals { + type = "Service" + identifiers = ["lambda.amazonaws.com"] + } + + actions = ["sts:AssumeRole"] + } +} + +resource "aws_iam_role" "iam_for_lambda" { + name = "iam_for_lambda-${random_uuid.val.id}" + assume_role_policy = data.aws_iam_policy_document.assume_role.json +} + +// attach policy to allow state lambda to start an ECS task and to write to Cloudwatch +resource "aws_iam_role_policy_attachment" "lambda_policy_ecs" { + role = aws_iam_role.iam_for_lambda.name + policy_arn = aws_iam_policy.lambda_iam_policy.arn +} + +resource "aws_iam_policy" "lambda_iam_policy" { + name = "lambda-iam-policy-${random_uuid.val.id}" + path = "/" + policy = data.aws_iam_policy_document.iam_policy_document_state.json +} + +# data +// creates an archive and uploads to s3 bucket +data "archive_file" "application_state_lambda" { + type = "zip" + + source_dir = "${path.module}/application-state-lambda" + output_path = "${path.module}/application-state-lambda.zip" +} + +// provides an s3 object resource +resource "aws_s3_object" "application_state_lambda" { + bucket = aws_s3_bucket.lambda_bucket.id + + key = "application-state-lambda.zip" + source = data.archive_file.application_state_lambda.output_path + + etag = filemd5(data.archive_file.application_state_lambda.output_path) +} + +// policy document - state lambda +data "aws_iam_policy_document" "iam_policy_document_state" { + statement { + sid = "CloudwatchPermissions" + effect = "Allow" + actions = [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ] + resources = ["*"] + } + + statement { + sid = "ECSPassRole" + effect = "Allow" + actions = [ + "iam:PassRole", + ] + resources = [ + "*" + ] + } + + // TODO: specify resource + statement { + sid = "S3Permissions" + effect = "Allow" + actions = [ + "s3:GetObject", + ] + resources = ["*"] + } + + statement { + sid = "LambdaAccessToDynamoDB" + effect = "Allow" + + actions = [ + "dynamodb:BatchGetItem", + "dynamodb:GetItem", + "dynamodb:Query", + "dynamodb:Scan", + "dynamodb:BatchWriteItem", + "dynamodb:PutItem", + "dynamodb:UpdateItem" + ] + + resources = [ + aws_dynamodb_table.applications_table.arn, + "${aws_dynamodb_table.applications_table.arn}/*" + ] + + } +} + +# s3 +// S3 bucket +resource "aws_s3_bucket" "lambda_bucket" { + bucket = random_uuid.val.id +} + +resource "aws_s3_bucket_ownership_controls" "lambda_bucket" { + bucket = aws_s3_bucket.lambda_bucket.id + rule { + object_ownership = "BucketOwnerPreferred" + } +} + +resource "aws_s3_bucket_acl" "bucket_acl" { + depends_on = [aws_s3_bucket_ownership_controls.lambda_bucket] + + bucket = aws_s3_bucket.lambda_bucket.id + acl = "private" +} \ No newline at end of file diff --git a/terraform/remote-state-application/main.tf b/terraform/remote-state-application/main.tf new file mode 100644 index 0000000..3e1b922 --- /dev/null +++ b/terraform/remote-state-application/main.tf @@ -0,0 +1,36 @@ +provider "aws" {} + +resource "random_uuid" "val" { +} + +resource "aws_s3_bucket" "terraform_state" { + bucket = "i3h-dev-app-state-v2" +} + +resource "aws_s3_bucket_versioning" "terraform_state" { + bucket = aws_s3_bucket.terraform_state.id + + versioning_configuration { + status = "Enabled" + } +} + +# trigger lambda from S3 +resource "aws_lambda_permission" "allow_bucket" { + statement_id = "AllowExecutionFromS3Bucket" + action = "lambda:InvokeFunction" + function_name = aws_lambda_function.application_state.arn + principal = "s3.amazonaws.com" + source_arn = aws_s3_bucket.terraform_state.arn +} + +resource "aws_s3_bucket_notification" "bucket_notification" { + bucket = aws_s3_bucket.terraform_state.id + + lambda_function { + lambda_function_arn = aws_lambda_function.application_state.arn + events = ["s3:ObjectCreated:*"] + } + + depends_on = [aws_lambda_permission.allow_bucket] +} \ No newline at end of file diff --git a/terraform/remote-state-application/outputs.tf b/terraform/remote-state-application/outputs.tf new file mode 100644 index 0000000..8f817d0 --- /dev/null +++ b/terraform/remote-state-application/outputs.tf @@ -0,0 +1,5 @@ +output "aws_bucket_name" { + description = "State Bucket Name" + + value = aws_s3_bucket.terraform_state.bucket +} \ No newline at end of file diff --git a/terraform/workflow-manager/Dockerfile b/terraform/workflow-manager/Dockerfile index 8df2461..af11321 100644 --- a/terraform/workflow-manager/Dockerfile +++ b/terraform/workflow-manager/Dockerfile @@ -21,8 +21,9 @@ RUN rm -f /service/nextflow RUN export NXF_VER=23.10.0 RUN apt-get clean -RUN apt-get -y install software-properties-common && add-apt-repository ppa:deadsnakes/ppa -RUN apt-get update & apt-get -y install python3.9 +RUN apt-get -y install software-properties-common +RUN add-apt-repository "deb http://archive.ubuntu.com/ubuntu $(lsb_release -sc) universe" +RUN apt-get clean && apt-get -y install python3.9 RUN python3.9 --version COPY . . diff --git a/terraform/workflow-manager/taskRunner/main.py b/terraform/workflow-manager/taskRunner/main.py index 13c0c71..56fb66d 100644 --- a/terraform/workflow-manager/taskRunner/main.py +++ b/terraform/workflow-manager/taskRunner/main.py @@ -5,19 +5,29 @@ import sys import os import json +import requests ecs_client = boto3_client("ecs", region_name=os.environ['REGION']) # Gather our code in a main() function def main(): - - task_definition_name = os.environ['TASK_DEFINITION_NAME'] subnet_ids = os.environ['SUBNET_IDS'] cluster_name = os.environ['CLUSTER_NAME'] security_group = os.environ['SECURITY_GROUP_ID'] - container_name = os.environ['CONTAINER_NAME'] + pennsieve_host = os.environ['PENNSIEVE_API_HOST'] + pennsieve_host2 = os.environ['PENNSIEVE_API_HOST2'] + inputDir = sys.argv[1] outputDir = sys.argv[2] + integration_id = sys.argv[3] + session_token = sys.argv[4] + + r = requests.get(f"{pennsieve_host2}/integrations/{integration_id}", headers={"Authorization": f"Bearer {session_token}"}) + r.raise_for_status() + print(r.json()) + + task_definition_name = r.json()["params"]["app_id"] + container_name = r.json()["params"]["app_id"] # start Fargate task if cluster_name != "": diff --git a/terraform/workflow-manager/taskRunner/pre-processor.py b/terraform/workflow-manager/taskRunner/pre-processor.py index 4bbe607..52fd446 100644 --- a/terraform/workflow-manager/taskRunner/pre-processor.py +++ b/terraform/workflow-manager/taskRunner/pre-processor.py @@ -14,7 +14,7 @@ def main(): integration_id = sys.argv[1] # pass from gateway api_key = sys.argv[2] # pass from gateway, differ per app api_secret = sys.argv[3] # pass from gateway - session_token = sys.argv[4] # pass from gateway + session_token = sys.argv[4] # should this be passed from gateway? task_definition_name = os.environ['TASK_DEFINITION_NAME_PRE'] subnet_ids = os.environ['SUBNET_IDS'] diff --git a/terraform/workflow-manager/workflows/pennsieve.nf b/terraform/workflow-manager/workflows/pennsieve.nf index a40cc08..f8468b9 100644 --- a/terraform/workflow-manager/workflows/pennsieve.nf +++ b/terraform/workflow-manager/workflows/pennsieve.nf @@ -43,7 +43,7 @@ process Pipeline { script: if ("$ENVIRONMENT" != 'LOCAL') """ - python3.9 /service/taskRunner/main.py $inputDir $outputDir + python3.9 /service/taskRunner/main.py $inputDir $outputDir ${params.integrationID} ${params.sessionToken} """ else """