diff --git a/.github/workflows/rspec.yaml b/.github/workflows/rspec.yaml index b1ee9a7..d0a2ef8 100644 --- a/.github/workflows/rspec.yaml +++ b/.github/workflows/rspec.yaml @@ -3,23 +3,6 @@ name: cftest on: [push, pull_request] jobs: - test: - name: test - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: set up ruby 2.7 - uses: actions/setup-ruby@v1 - with: - ruby-version: 2.7.x - - name: install gems - run: gem install cfhighlander rspec - - name: set cfndsl spec - run: cfndsl -u - - name: cftest - run: rspec - env: - AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - AWS_REGION: ap-southeast-2 \ No newline at end of file + rspec: + uses: theonestack/shared-workflows/.github/workflows/rspec.yaml@main + secrets: inherit \ No newline at end of file diff --git a/ciinabox-efs.cfndsl.rb b/ciinabox-efs.cfndsl.rb index 503261d..c829b70 100644 --- a/ciinabox-efs.cfndsl.rb +++ b/ciinabox-efs.cfndsl.rb @@ -175,7 +175,7 @@ def lambda_handler(event, context): CODE }) Handler "index.lambda_handler" - Runtime "python3.7" + Runtime "python3.11" Role FnGetAtt(:CiinaboxEfsCustomResourceRole, :Arn) Timeout 60 } @@ -221,6 +221,8 @@ def lambda_handler(event, context): end + access_points = external_parameters.fetch(:access_points, []) + unless access_points.empty? access_points.each do |ap| EFS_AccessPoint("#{ap['name']}AccessPoint") do diff --git a/spec/default_spec.rb b/spec/default_spec.rb index d1f7816..954813e 100644 --- a/spec/default_spec.rb +++ b/spec/default_spec.rb @@ -29,7 +29,7 @@ end it "to have property Policies" do - expect(resource["Properties"]["Policies"]).to eq([{"PolicyName"=>"ciinabox-efs", "PolicyDocument"=>{"Version"=>"2012-10-17", "Statement"=>[{"Effect"=>"Allow", "Action"=>["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"], "Resource"=>"*"}, {"Effect"=>"Allow", "Action"=>["elasticfilesystem:UpdateFileSystem", "elasticfilesystem:CreateFileSystem", "elasticfilesystem:DescribeFileSystems", "elasticfilesystem:ListTagsForResource", "elasticfilesystem:TagResource", "elasticfilesystem:UntagResource"], "Resource"=>"*"}]}}]) + expect(resource["Properties"]["Policies"]).to eq([{"PolicyName"=>"ciinabox-efs", "PolicyDocument"=>{"Version"=>"2012-10-17", "Statement"=>[{"Effect"=>"Allow", "Action"=>["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"], "Resource"=>"*"}, {"Effect"=>"Allow", "Action"=>["iam:CreateServiceLinkedRole"], "Resource"=>"*"}, {"Effect"=>"Allow", "Action"=>["elasticfilesystem:UpdateFileSystem", "elasticfilesystem:CreateFileSystem", "elasticfilesystem:DescribeFileSystems", "elasticfilesystem:ListTagsForResource", "elasticfilesystem:TagResource", "elasticfilesystem:UntagResource"], "Resource"=>"*"}, {"Effect"=>"Allow", "Action"=>["iam:CreateServiceLinkedRole"], "Resource"=>"*"}]}}]) end end @@ -42,7 +42,7 @@ end it "to have property Code" do - expect(resource["Properties"]["Code"]).to eq({"ZipFile"=>"import cfnresponse\nimport boto3\nimport hashlib\nimport time\n\nimport logging\nlogger = logging.getLogger(__name__)\nlogger.setLevel(logging.INFO)\n\ndef get_creation_token(name):\n return hashlib.md5(name.encode('utf-8')).hexdigest()\n\ndef create_filesystem(name):\n client = boto3.client('efs')\n resp = client.create_file_system(\n CreationToken=get_creation_token(name),\n PerformanceMode='generalPurpose',\n Encrypted=False,\n ThroughputMode='bursting',\n Backup=True\n )\n return resp['FileSystemId']\n\ndef get_filesystem_id(name):\n client = boto3.client('efs')\n resp = client.describe_file_systems(\n CreationToken=get_creation_token(name)\n )\n if resp['FileSystems']:\n return resp['FileSystems'][0]['FileSystemId']\n return None\n\ndef get_filesystem_state(filesystem):\n client = boto3.client('efs')\n resp = client.describe_file_systems(\n FileSystemId=filesystem\n )\n return resp['FileSystems'][0]['LifeCycleState']\n\ndef wait_until(success, filesystem, timeout=120, period=3):\n end = time.time() + timeout\n while time.time() < end:\n state = get_filesystem_state(filesystem)\n logger.info(f'filesystem is {state}, waiting to reach the {success} state')\n if state == success: \n return True\n elif state == 'error':\n raise WaitError(\"filesystem is in an error state\")\n time.sleep(period)\n return False\n\ndef tag_filesystem(filesystem, tags):\n client = boto3.client('efs')\n client.tag_resource(\n ResourceId=filesystem,\n Tags=tags\n )\n\nclass WaitError(Exception):\n pass\n\n\ndef lambda_handler(event, context):\n\n try:\n\n logger.info(event)\n # Globals\n responseData = {}\n physicalResourceId = None\n name = event['ResourceProperties']['Name']\n tags = event['ResourceProperties']['Tags']\n\n if event['RequestType'] == 'Create':\n filesystem = get_filesystem_id(name)\n if filesystem is None:\n logger.info(f'creating new filesystem')\n filesystem = create_filesystem(name)\n print(f'filesystem {filesystem} created')\n wait_until('available', filesystem)\n else:\n print(f'filesystem {filesystem} already exists')\n \n tag_filesystem(filesystem, tags)\n physicalResourceId = filesystem\n\n elif event['RequestType'] == 'Update':\n tag_filesystem(filesystem, tags)\n physicalResourceId = event['PhysicalResourceId']\n \n elif event['RequestType'] == 'Delete':\n physicalResourceId = event['PhysicalResourceId']\n \n cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, physicalResourceId)\n\n except Exception as e:\n logger.error('failed to cleanup bucket', exc_info=True)\n cfnresponse.send(event, context, cfnresponse.FAILED, {})\n\n"}) + expect(resource["Properties"]["Code"]).to eq({"ZipFile"=>"import cfnresponse\nimport boto3\nimport hashlib\nimport time\n\nimport logging\nlogger = logging.getLogger(__name__)\nlogger.setLevel(logging.INFO)\n\ndef get_creation_token(name):\n return hashlib.md5(name.encode('utf-8')).hexdigest()\n\ndef create_filesystem(name):\n client = boto3.client('efs')\n resp = client.create_file_system(\n CreationToken=get_creation_token(name),\n PerformanceMode='generalPurpose',\n Encrypted=False,\n ThroughputMode='bursting',\n Backup=True\n )\n return resp['FileSystemId']\n\ndef get_filesystem_id(name):\n client = boto3.client('efs')\n resp = client.describe_file_systems(\n CreationToken=get_creation_token(name)\n )\n if resp['FileSystems']:\n return resp['FileSystems'][0]['FileSystemId']\n return None\n\ndef get_filesystem_state(filesystem):\n client = boto3.client('efs')\n resp = client.describe_file_systems(\n FileSystemId=filesystem\n )\n return resp['FileSystems'][0]['LifeCycleState']\n\ndef wait_until(success, filesystem, timeout=120, period=3):\n end = time.time() + timeout\n while time.time() < end:\n state = get_filesystem_state(filesystem)\n logger.info(f'filesystem is {state}, waiting to reach the {success} state')\n if state == success: \n return True\n elif state == 'error':\n raise WaitError(\"filesystem is in an error state\")\n time.sleep(period)\n return False\n\ndef tag_filesystem(filesystem, tags):\n client = boto3.client('efs')\n client.tag_resource(\n ResourceId=filesystem,\n Tags=tags\n )\n\nclass WaitError(Exception):\n pass\n\n\ndef lambda_handler(event, context):\n\n try:\n\n logger.info(event)\n # Globals\n responseData = {}\n physicalResourceId = None\n name = event['ResourceProperties'].get('Name')\n tags = event['ResourceProperties'].get('Tags')\n tags.append({'Key': 'Name', 'Value': name})\n\n if event['RequestType'] == 'Create':\n filesystem = get_filesystem_id(name)\n if filesystem is None:\n logger.info(f'creating new filesystem')\n filesystem = create_filesystem(name)\n print(f'filesystem {filesystem} created')\n wait_until('available', filesystem)\n else:\n print(f'filesystem {filesystem} already exists')\n \n tag_filesystem(filesystem, tags)\n physicalResourceId = filesystem\n\n elif event['RequestType'] == 'Update':\n tag_filesystem(filesystem, tags)\n physicalResourceId = event['PhysicalResourceId']\n \n elif event['RequestType'] == 'Delete':\n physicalResourceId = event['PhysicalResourceId']\n \n cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, physicalResourceId)\n\n except Exception as e:\n logger.error('ciinabox efs custom resource caught an unexpected exception', exc_info=True)\n cfnresponse.send(event, context, cfnresponse.FAILED, {})\n\n"}) end it "to have property Handler" do @@ -50,7 +50,7 @@ end it "to have property Runtime" do - expect(resource["Properties"]["Runtime"]).to eq("python3.7") + expect(resource["Properties"]["Runtime"]).to eq("python3.11") end it "to have property Role" do @@ -75,11 +75,11 @@ end it "to have property Name" do - expect(resource["Properties"]["Name"]).to eq({"Fn::Sub"=>"/${EnvironmentName}-ciinabox"}) + expect(resource["Properties"]["Name"]).to eq({"Fn::If"=>["VolumeNameSet", {"Ref"=>"VolumeName"}, {"Fn::Sub"=>"/${EnvironmentName}-ciinabox"}]}) end it "to have property Tags" do - expect(resource["Properties"]["Tags"]).to eq([[], [], {"Key"=>"Environment", "Value"=>{"Ref"=>"EnvironmentName"}}, {"Key"=>"EnvironmentType", "Value"=>{"Ref"=>"EnvironmentType"}}, {"Key"=>"Name", "Value"=>{"Fn::Sub"=>"${EnvironmentName}-ciinabox-FileSystem"}}]) + expect(resource["Properties"]["Tags"]).to eq([{"Key"=>"Environment", "Value"=>{"Ref"=>"EnvironmentName"}}, {"Key"=>"EnvironmentType", "Value"=>{"Ref"=>"EnvironmentType"}}]) end end @@ -100,7 +100,7 @@ end it "to have property Tags" do - expect(resource["Properties"]["Tags"]).to eq([[], [], {"Key"=>"Environment", "Value"=>{"Ref"=>"EnvironmentName"}}, {"Key"=>"EnvironmentType", "Value"=>{"Ref"=>"EnvironmentType"}}, {"Key"=>"Name", "Value"=>{"Fn::Sub"=>"${EnvironmentName}-ciinabox-FileSystem"}}]) + expect(resource["Properties"]["Tags"]).to eq([{"Key"=>"Name", "Value"=>{"Fn::Sub"=>"${EnvironmentName}-ciinabox-filesystem"}}, {"Key"=>"Environment", "Value"=>{"Ref"=>"EnvironmentName"}}, {"Key"=>"EnvironmentType", "Value"=>{"Ref"=>"EnvironmentType"}}]) end end diff --git a/spec/security_group_rules_spec.rb b/spec/security_group_rules_spec.rb index 2c96e59..d85615d 100644 --- a/spec/security_group_rules_spec.rb +++ b/spec/security_group_rules_spec.rb @@ -29,7 +29,7 @@ end it "to have property Policies" do - expect(resource["Properties"]["Policies"]).to eq([{"PolicyName"=>"ciinabox-efs", "PolicyDocument"=>{"Version"=>"2012-10-17", "Statement"=>[{"Effect"=>"Allow", "Action"=>["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"], "Resource"=>"*"}, {"Effect"=>"Allow", "Action"=>["elasticfilesystem:UpdateFileSystem", "elasticfilesystem:CreateFileSystem", "elasticfilesystem:DescribeFileSystems", "elasticfilesystem:ListTagsForResource", "elasticfilesystem:TagResource", "elasticfilesystem:UntagResource"], "Resource"=>"*"}]}}]) + expect(resource["Properties"]["Policies"]).to eq([{"PolicyName"=>"ciinabox-efs", "PolicyDocument"=>{"Version"=>"2012-10-17", "Statement"=>[{"Effect"=>"Allow", "Action"=>["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"], "Resource"=>"*"}, {"Effect"=>"Allow", "Action"=>["iam:CreateServiceLinkedRole"], "Resource"=>"*"}, {"Effect"=>"Allow", "Action"=>["elasticfilesystem:UpdateFileSystem", "elasticfilesystem:CreateFileSystem", "elasticfilesystem:DescribeFileSystems", "elasticfilesystem:ListTagsForResource", "elasticfilesystem:TagResource", "elasticfilesystem:UntagResource"], "Resource"=>"*"}, {"Effect"=>"Allow", "Action"=>["iam:CreateServiceLinkedRole"], "Resource"=>"*"}]}}]) end end @@ -42,7 +42,7 @@ end it "to have property Code" do - expect(resource["Properties"]["Code"]).to eq({"ZipFile"=>"import cfnresponse\nimport boto3\nimport hashlib\nimport time\n\nimport logging\nlogger = logging.getLogger(__name__)\nlogger.setLevel(logging.INFO)\n\ndef get_creation_token(name):\n return hashlib.md5(name.encode('utf-8')).hexdigest()\n\ndef create_filesystem(name):\n client = boto3.client('efs')\n resp = client.create_file_system(\n CreationToken=get_creation_token(name),\n PerformanceMode='generalPurpose',\n Encrypted=False,\n ThroughputMode='bursting',\n Backup=True\n )\n return resp['FileSystemId']\n\ndef get_filesystem_id(name):\n client = boto3.client('efs')\n resp = client.describe_file_systems(\n CreationToken=get_creation_token(name)\n )\n if resp['FileSystems']:\n return resp['FileSystems'][0]['FileSystemId']\n return None\n\ndef get_filesystem_state(filesystem):\n client = boto3.client('efs')\n resp = client.describe_file_systems(\n FileSystemId=filesystem\n )\n return resp['FileSystems'][0]['LifeCycleState']\n\ndef wait_until(success, filesystem, timeout=120, period=3):\n end = time.time() + timeout\n while time.time() < end:\n state = get_filesystem_state(filesystem)\n logger.info(f'filesystem is {state}, waiting to reach the {success} state')\n if state == success: \n return True\n elif state == 'error':\n raise WaitError(\"filesystem is in an error state\")\n time.sleep(period)\n return False\n\ndef tag_filesystem(filesystem, tags):\n client = boto3.client('efs')\n client.tag_resource(\n ResourceId=filesystem,\n Tags=tags\n )\n\nclass WaitError(Exception):\n pass\n\n\ndef lambda_handler(event, context):\n\n try:\n\n logger.info(event)\n # Globals\n responseData = {}\n physicalResourceId = None\n name = event['ResourceProperties']['Name']\n tags = event['ResourceProperties']['Tags']\n\n if event['RequestType'] == 'Create':\n filesystem = get_filesystem_id(name)\n if filesystem is None:\n logger.info(f'creating new filesystem')\n filesystem = create_filesystem(name)\n print(f'filesystem {filesystem} created')\n wait_until('available', filesystem)\n else:\n print(f'filesystem {filesystem} already exists')\n \n tag_filesystem(filesystem, tags)\n physicalResourceId = filesystem\n\n elif event['RequestType'] == 'Update':\n tag_filesystem(filesystem, tags)\n physicalResourceId = event['PhysicalResourceId']\n \n elif event['RequestType'] == 'Delete':\n physicalResourceId = event['PhysicalResourceId']\n \n cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, physicalResourceId)\n\n except Exception as e:\n logger.error('failed to cleanup bucket', exc_info=True)\n cfnresponse.send(event, context, cfnresponse.FAILED, {})\n\n"}) + expect(resource["Properties"]["Code"]).to eq({"ZipFile"=>"import cfnresponse\nimport boto3\nimport hashlib\nimport time\n\nimport logging\nlogger = logging.getLogger(__name__)\nlogger.setLevel(logging.INFO)\n\ndef get_creation_token(name):\n return hashlib.md5(name.encode('utf-8')).hexdigest()\n\ndef create_filesystem(name):\n client = boto3.client('efs')\n resp = client.create_file_system(\n CreationToken=get_creation_token(name),\n PerformanceMode='generalPurpose',\n Encrypted=False,\n ThroughputMode='bursting',\n Backup=True\n )\n return resp['FileSystemId']\n\ndef get_filesystem_id(name):\n client = boto3.client('efs')\n resp = client.describe_file_systems(\n CreationToken=get_creation_token(name)\n )\n if resp['FileSystems']:\n return resp['FileSystems'][0]['FileSystemId']\n return None\n\ndef get_filesystem_state(filesystem):\n client = boto3.client('efs')\n resp = client.describe_file_systems(\n FileSystemId=filesystem\n )\n return resp['FileSystems'][0]['LifeCycleState']\n\ndef wait_until(success, filesystem, timeout=120, period=3):\n end = time.time() + timeout\n while time.time() < end:\n state = get_filesystem_state(filesystem)\n logger.info(f'filesystem is {state}, waiting to reach the {success} state')\n if state == success: \n return True\n elif state == 'error':\n raise WaitError(\"filesystem is in an error state\")\n time.sleep(period)\n return False\n\ndef tag_filesystem(filesystem, tags):\n client = boto3.client('efs')\n client.tag_resource(\n ResourceId=filesystem,\n Tags=tags\n )\n\nclass WaitError(Exception):\n pass\n\n\ndef lambda_handler(event, context):\n\n try:\n\n logger.info(event)\n # Globals\n responseData = {}\n physicalResourceId = None\n name = event['ResourceProperties'].get('Name')\n tags = event['ResourceProperties'].get('Tags')\n tags.append({'Key': 'Name', 'Value': name})\n\n if event['RequestType'] == 'Create':\n filesystem = get_filesystem_id(name)\n if filesystem is None:\n logger.info(f'creating new filesystem')\n filesystem = create_filesystem(name)\n print(f'filesystem {filesystem} created')\n wait_until('available', filesystem)\n else:\n print(f'filesystem {filesystem} already exists')\n \n tag_filesystem(filesystem, tags)\n physicalResourceId = filesystem\n\n elif event['RequestType'] == 'Update':\n tag_filesystem(filesystem, tags)\n physicalResourceId = event['PhysicalResourceId']\n \n elif event['RequestType'] == 'Delete':\n physicalResourceId = event['PhysicalResourceId']\n \n cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, physicalResourceId)\n\n except Exception as e:\n logger.error('ciinabox efs custom resource caught an unexpected exception', exc_info=True)\n cfnresponse.send(event, context, cfnresponse.FAILED, {})\n\n"}) end it "to have property Handler" do @@ -50,7 +50,7 @@ end it "to have property Runtime" do - expect(resource["Properties"]["Runtime"]).to eq("python3.7") + expect(resource["Properties"]["Runtime"]).to eq("python3.11") end it "to have property Role" do @@ -75,11 +75,11 @@ end it "to have property Name" do - expect(resource["Properties"]["Name"]).to eq({"Fn::Sub"=>"/${EnvironmentName}-ciinabox"}) + expect(resource["Properties"]["Name"]).to eq({"Fn::If"=>["VolumeNameSet", {"Ref"=>"VolumeName"}, {"Fn::Sub"=>"/${EnvironmentName}-ciinabox"}]}) end it "to have property Tags" do - expect(resource["Properties"]["Tags"]).to eq([[], [], {"Key"=>"Environment", "Value"=>{"Ref"=>"EnvironmentName"}}, {"Key"=>"EnvironmentType", "Value"=>{"Ref"=>"EnvironmentType"}}, {"Key"=>"Name", "Value"=>{"Fn::Sub"=>"${EnvironmentName}-ciinabox-FileSystem"}}]) + expect(resource["Properties"]["Tags"]).to eq([{"Key"=>"Environment", "Value"=>{"Ref"=>"EnvironmentName"}}, {"Key"=>"EnvironmentType", "Value"=>{"Ref"=>"EnvironmentType"}}]) end end @@ -104,7 +104,7 @@ end it "to have property Tags" do - expect(resource["Properties"]["Tags"]).to eq([[], [], {"Key"=>"Environment", "Value"=>{"Ref"=>"EnvironmentName"}}, {"Key"=>"EnvironmentType", "Value"=>{"Ref"=>"EnvironmentType"}}, {"Key"=>"Name", "Value"=>{"Fn::Sub"=>"${EnvironmentName}-ciinabox-FileSystem"}}]) + expect(resource["Properties"]["Tags"]).to eq([{"Key"=>"Name", "Value"=>{"Fn::Sub"=>"${EnvironmentName}-ciinabox-filesystem"}}, {"Key"=>"Environment", "Value"=>{"Ref"=>"EnvironmentName"}}, {"Key"=>"EnvironmentType", "Value"=>{"Ref"=>"EnvironmentType"}}]) end end