name: EFS Tier for Cloudsoft Web+EFS Exercise

brooklyn.parameters:
  - name: demo_name
    default: cloudsoft-amp-efs-exercise
    constraints:
      - regex: '[A-Za-z0-9_-]+'
    pinned: true

  - name: bucket_name
    constraints:
      - required
      - regex: '[A-Za-z0-9_-]+'

  - name: authorized_keys
    description: >
      This optional parameter allows additional authorized_keys to be specified and 
      installed when bastion servers come up or whenever the `authorizeSshKey` effector is invoked.
    pinned: true
    reconfigurable: true

  - name: ami
    description: "AMI to use, eg ami-0e0669c0b39d3a79f for CentOS 8 in eu-west-1; or ami-09e310d4361a3b13a for CentOS 9 (default)"
    reconfigurable: true
    default: ami-09e310d4361a3b13a
  - name: ami_user
    description: "User that should log in to the AMI, typically centos or ec2-user (centos 9)"
    reconfigurable: true
    default: ec2-user

  - name: vpc
    default: ""

brooklyn.config:
  tf_var.name:                   $brooklyn:config("demo_name")
  tf_var.ami:                    $brooklyn:config("ami")
  tf_var.ami_user:               $brooklyn:config("ami_user")
  tf_var.vpc:                    $brooklyn:config("vpc")
  shell.env:
    AWS_ACCESS_KEY_ID:           $brooklyn:external("exercise-secrets", "aws-access-key-id")
    AWS_SECRET_ACCESS_KEY:       $brooklyn:external("exercise-secrets", "aws-secret-access-key")
  tf.extra.templates.contents:
    backend.tf: |
      terraform {
        backend "s3" {
          bucket = "${config.bucket_name}"
          key    = "${config.demo_name}/${name}"
          region = "eu-west-1"
        }
      }
  
services:

  - name:                        EFS Volume
    type:                        terraform
    id:                          efs-volume
    brooklyn.config:
      tf.configuration.url:      classpath://efs-volume-tf.zip
    brooklyn.initializers:
      - type: terraform-drift-compliance-check
        brooklyn.config:
          terraform.resources-drift.enabled: true

    brooklyn.children:
      - type: org.apache.brooklyn.entity.group.DynamicGroup
        name: EFS Volume (Grouped)
        id: efs-volume-grouped
        brooklyn.config:
          dynamicgroup.entityfilter:
            config: tf.resource.address
            equals: aws_efs_file_system.main
        brooklyn.policies:
          - type: org.apache.brooklyn.entity.group.GroupsChangePolicy
            brooklyn.config:
              member.initializers:
                - type: workflow-sensor
                  brooklyn.config:
                    sensor:
                      name: efs-size-from-aws
                      type: integer
                    triggers:
                      - tf.value.size_in_bytes
                    steps:
                      - transform size_json = ${entity.sensor['tf.value.size_in_bytes']} | bash
                      - container stedolan/jq echo ${size_json} | jq '.[0].value'
                      - return ${stdout}
                    on-error:
                      - clear-sensor efs-size-from-aws
        brooklyn.enrichers:
          - type: org.apache.brooklyn.enricher.stock.Aggregator
            brooklyn.config:
              enricher.sourceSensor: efs-size-from-aws
              enricher.targetSensor: efs-size-from-aws
              transformation: first  # list, sum, min, max, average

  - name:                        Bastion Server for accessing EFS
    type:                        terraform
    id:                          efs-bastion-server
    brooklyn.config:
      tf.configuration.url:      classpath://efs-server-tf.zip
      tf_var.subnet:             $brooklyn:entity("efs-volume").attributeWhenReady("tf.output.efs_access_subnet")
      tf_var.efs_security_group: $brooklyn:entity("efs-volume").attributeWhenReady("tf.output.efs_access_security_group")
      tf_var.efs_mount_dns_name: $brooklyn:entity("efs-volume").attributeWhenReady("tf.output.efs_access_mount_dns_name")
    brooklyn.initializers:
      - type: terraform-drift-compliance-check
        brooklyn.config:
          terraform.resources-drift.enabled: true
    brooklyn.policies:
      - type: cron-scheduler
        brooklyn.config:
          entries:
            - when: '0 30 8 * * mon-fri'
              effector: start
#            - when: '0 0 18 * * mon-fri'
#              effector: stop

    brooklyn.children:
      - type: org.apache.brooklyn.entity.group.DynamicGroup
        name: EFS Bastion Server (Grouped)
        id: efs-bastion-server-grouped
        brooklyn.config:
          dynamicgroup.entityfilter:
            config: tf.resource.address
            equals: aws_instance.efs_bastion
        brooklyn.policies:
          - type: org.apache.brooklyn.entity.group.GroupsChangePolicy
            brooklyn.config:
              member.locations:
                - type: org.apache.brooklyn.location.ssh.SshMachineLocation
                  brooklyn.config:
                    user: $brooklyn:config("tf_var.ami_user")
                    address: $brooklyn:parent().attributeWhenReady("tf.output.efs_bastion_server_ip")
                    privateKeyData: $brooklyn:parent().attributeWhenReady("tf.output.efs_bastion_server_private_key")
              member.initializers:
                - type: workflow-sensor
                  brooklyn.config:
                    sensor:
                      name: efs-size-from-server
                      type: integer
                    period: 2m
                    steps:
                      - ssh du --block-size=4096 /mnt/shared-file-system | tail -1 | awk '{print $1 * 4096}'
                      - let result = ${stdout}
                      - ssh date > /tmp/cloudsoft-amp-efs-size-from-server.last-date
                      - return ${result}
                - type: workflow-sensor
                  brooklyn.config:
                    sensor:
                      name: dashboard.security.lynis
                      type: compliance-check
                    steps:
                      - load script = classpath://io/cloudsoft/amp/compliance/lynis/lynis-result-sensor.sh
                      - type: ssh
                        command: ${script}
                      - transform out = ${stdout} | yaml | type compliance-check
                      - return ${out}
                    period: 5m
                - type: workflow-effector
                  brooklyn.config:
                    name: authorizeSshKey
                    parameters:
                      PUBLIC_SSH_KEY:
                        description: SSH key (public part) to authorize on this machine
                        defaultValue: $brooklyn:config("authorized_keys")
                    steps:
                      - ssh echo ${PUBLIC_SSH_KEY} >> ~/.ssh/authorized_keys
                - type: workflow-effector
                  brooklyn.config:
                    name: runCommand
                    steps:
                      - type: ssh
                        command: ${script}
                        env: ${env}
                    parameters:
                      script:
                        description: A command or script to run
                      env:
                        description: A map of env vars to run
                        defaultValue: {}
              member.invoke:
                - authorizeSshKey

        brooklyn.enrichers:
          - type: org.apache.brooklyn.enricher.stock.Aggregator
            brooklyn.config:
              enricher.sourceSensor: efs-size-from-server
              enricher.targetSensor: efs-size-from-server
              transformation: first

brooklyn.enrichers:
  - type: org.apache.brooklyn.enricher.stock.Propagator
    brooklyn.config:
      producer: $brooklyn:entity("efs-volume-grouped")
      propagating:
        - efs-size-from-aws

  - type: org.apache.brooklyn.enricher.stock.Propagator
    brooklyn.config:
      producer: $brooklyn:entity("efs-volume")
      propagating:
        - tf.output.efs_access_mount_dns_name
        - tf.output.efs_vpc_id
        - tf.output.efs_access_subnet
        - tf.output.efs_access_security_group
        - tf.output.efs_subnets
        - tf.output.efs_subnets_per_az

  - type: org.apache.brooklyn.enricher.stock.Propagator
    brooklyn.config:
      producer: $brooklyn:entity("efs-bastion-server-grouped")
      propagating:
        - efs-size-from-server

  - type: org.apache.brooklyn.enricher.stock.Propagator
    brooklyn.config:
      producer: $brooklyn:entity("efs-bastion-server")
      propagating:
        - tf.output.efs_bastion_server_ip

brooklyn.initializers:
  - type: workflow-sensor
    brooklyn.config:
      sensor:
        name: dashboard.utilization.filesystem_size
        type: compliance-check
      triggers:
        - efs-size-from-aws
        - efs-size-from-server
      condition:
        any:
          - sensor: efs-size-from-aws
            greater-than: 30000000
          - sensor: efs-size-from-server
            greater-than: 30000000
          - sensor: dashboard.utilization.filesystem_size
            check:
              jsonpath: pass
              when: falsy
      steps:
        - step: container cloudsoft/terraform
          env:
            SIZE_PER_AWS: ${entity.sensor['efs-size-from-aws']}
            SIZE_PER_SERVER: ${entity.sensor['efs-size-from-server']}
          command: |
            
            FAILURES=""
            if [ "$SIZE_PER_AWS" -gt 30000000 ] ; then
              FAILURES=$(echo $FAILURES AWS)
            fi
            if [ "$SIZE_PER_SERVER" -gt 30000000 ] ; then
              FAILURES=$(echo $FAILURES on-box-check)
            fi
            
            cat <<EOF
            id: efs-size-check
            created: $(date +"%Y%m%d-%H%M")
            EOF
            
            if [ -z "$FAILURES" ] ; then cat <<EOF
            summary: EFS size within bounds
            pass: true
            EOF
            else cat <<EOF
            summary: EFS size limit exceeded - $FAILURES
            pass: false
            EOF
            fi
            
            cat <<EOF
            notes: |2
              AWS reports size as: $SIZE_PER_AWS
              On-box reports size as: $SIZE_PER_SERVER
            EOF
        - transform out = ${stdout} | yaml | type compliance-check
        - return ${out}

  - type: workflow-effector
    brooklyn.config:
      name: runCommandOnBastionServer
      steps:
        - step: invoke-effector runCommand
          entity: efs-bastion-server.aws_instance.efs_bastion
          args:
            script: ${script}
            env: ${env}
      parameters:
        script:
          description: A command or script to run
        env:
          description: A map of env vars to run
          defaultValue: {}
