diff --git a/samples/aliyun-poc-bucket.yml b/samples/aliyun-poc-bucket.yml new file mode 100644 index 0000000..3ebdedf --- /dev/null +++ b/samples/aliyun-poc-bucket.yml @@ -0,0 +1,20 @@ +version: 0.0.1 + +provider: + name: aliyun + region: cn-chengdu + + +service: insight-bucket-poc + +tags: + owner: geek-fun + +buckets: + insight_poc_bucket: + name: insight-poc-bucket + website: + code: dist + index: index.html + error_page: 404.html + error_code: 404 diff --git a/src/parser/bucketParser.ts b/src/parser/bucketParser.ts index a7f3976..a6ce9ca 100644 --- a/src/parser/bucketParser.ts +++ b/src/parser/bucketParser.ts @@ -1,4 +1,4 @@ -import { BucketDomain, BucketRaw } from '../types'; +import { BucketAccessEnum, BucketDomain, BucketRaw } from '../types'; export const parseBucket = (buckets: { [key: string]: BucketRaw; @@ -13,6 +13,9 @@ export const parseBucket = (buckets: { versioning: bucket.versioning, security: bucket.security ? { + acl: bucket.security.acl + ? (bucket.security.acl as BucketAccessEnum) + : BucketAccessEnum.PRIVATE, force_delete: bucket.security.force_delete ?? false, sse_algorithm: bucket.security.sse_algorithm, sse_kms_master_key_id: bucket.security.sse_kms_master_key_id, diff --git a/src/stack/rosStack/bucket.ts b/src/stack/rosStack/bucket.ts index 40064bd..ac63102 100644 --- a/src/stack/rosStack/bucket.ts +++ b/src/stack/rosStack/bucket.ts @@ -1,9 +1,16 @@ -import { ActionContext, BucketDomain } from '../../types'; +import { ActionContext, BucketAccessEnum, BucketDomain } from '../../types'; import * as oss from '@alicloud/ros-cdk-oss'; import * as ros from '@alicloud/ros-cdk-core'; import { getAssets, replaceReference } from '../../common'; import * as ossDeployment from '@alicloud/ros-cdk-ossdeployment'; import path from 'node:path'; +import { RosRole } from '@alicloud/ros-cdk-ram'; + +const aclMap = new Map([ + [BucketAccessEnum.PRIVATE, 'private'], + [BucketAccessEnum.PUBLIC_READ, 'public-read'], + [BucketAccessEnum.PUBLIC_READ_WRITE, 'public-read-write'], +]); export const resolveBuckets = ( scope: ros.Construct, @@ -13,9 +20,42 @@ export const resolveBuckets = ( if (!buckets) { return undefined; } + const bucketSources = buckets.some((bucket) => bucket?.website?.code); + let siAutoOssDeploymentBucketRole: RosRole | undefined; + if (bucketSources) { + siAutoOssDeploymentBucketRole = new RosRole( + scope, + 'si_auto_od_bucket_role', + { + roleName: ros.Fn.sub('si-auto-od-bucket-role-${ALIYUN::StackId}'), + description: + 'roles created by ServerlessInsight for oss deployment to put files to oss bucket during deployment', + deletionForce: false, + ignoreExisting: false, + assumeRolePolicyDocument: { + version: '1', + statement: [ + { + action: 'sts:AssumeRole', + effect: 'Allow', + principal: { service: ['fc.aliyuncs.com'] }, + }, + ], + }, + policyAttachments: { + system: ['AliyunOSSFullAccess', 'AliyunLogFullAccess'], + }, + }, + true, + ); + } + buckets.forEach((bucket) => { const ossBucket = new oss.Bucket(scope, replaceReference(bucket.key, context), { bucketName: replaceReference(bucket.name, context), + accessControl: aclMap.get( + replaceReference(bucket.security?.acl, context) ?? ('' as BucketAccessEnum), + ), websiteConfigurationV2: bucket.website ? { indexDocument: { @@ -34,10 +74,11 @@ export const resolveBuckets = ( const filePath = path.resolve(process.cwd(), replaceReference(bucket.website.code, context)); new ossDeployment.BucketDeployment( scope, - `${replaceReference(bucket.key, context)}_bucket_code_deployment`, + `si_auto_${bucket.key}_bucket_code_deployment`, { sources: getAssets(filePath), destinationBucket: ossBucket.attrName, + roleArn: siAutoOssDeploymentBucketRole!.attrArn, timeout: 3000, logMonitoring: false, retainOnCreate: false, diff --git a/src/types/domains/bucket.ts b/src/types/domains/bucket.ts index dff73f3..70730bd 100644 --- a/src/types/domains/bucket.ts +++ b/src/types/domains/bucket.ts @@ -8,6 +8,7 @@ export type BucketRaw = { }; security?: { + acl?: string; force_delete?: boolean; sse_algorithm?: string; sse_kms_master_key_id?: string; @@ -20,6 +21,12 @@ export type BucketRaw = { }; }; +export enum BucketAccessEnum { + PRIVATE = 'PRIVATE', + PUBLIC_READ = 'PUBLIC_READ', + PUBLIC_READ_WRITE = 'PUBLIC_READ_WRITE', +} + export type BucketDomain = { key: string; name: string; @@ -31,6 +38,7 @@ export type BucketDomain = { }; security?: { + acl: BucketAccessEnum; force_delete: boolean; sse_algorithm?: string; sse_kms_master_key_id?: string; diff --git a/src/validator/bucketSchema.ts b/src/validator/bucketSchema.ts index 3064a2e..80b8cc4 100644 --- a/src/validator/bucketSchema.ts +++ b/src/validator/bucketSchema.ts @@ -29,6 +29,10 @@ export const bucketSchema = { security: { type: 'object', properties: { + access: { + type: 'string', + enum: ['PRIVATE', 'PUBLIC_READ', 'PUBLIC_READ_WRITE'], + }, force_delete: { type: 'boolean', }, @@ -39,7 +43,6 @@ export const bucketSchema = { type: 'string', }, }, - required: ['force_delete'], }, website: { type: 'object', diff --git a/tests/fixtures/deployFixture.ts b/tests/fixtures/deployFixture.ts index 63f80c2..33e9427 100644 --- a/tests/fixtures/deployFixture.ts +++ b/tests/fixtures/deployFixture.ts @@ -1306,7 +1306,7 @@ export const bucketWithWebsiteRos = { }, ROSTemplateFormatVersion: '2015-09-01', Resources: { - FCFunctionFormy_bucket_bucket_code_deployment: { + FCFunctionForsi_auto_my_bucket_bucket_code_deployment: { Properties: { CAPort: 9000, Code: { @@ -1340,103 +1340,18 @@ export const bucketWithWebsiteRos = { MemorySize: 128, Runtime: 'python3.10', ServiceName: { - 'Fn::GetAtt': ['FCServiceFormy_bucket_bucket_code_deployment', 'ServiceName'], + 'Fn::GetAtt': ['FCServiceForsi_auto_my_bucket_bucket_code_deployment', 'ServiceName'], }, Timeout: 3000, }, Type: 'ALIYUN::FC::Function', }, - FCRoleFormy_bucket_bucket_code_deployment: { - Properties: { - AssumeRolePolicyDocument: { - Statement: [ - { - Action: 'sts:AssumeRole', - Effect: 'Allow', - Principal: { - Service: ['fc.aliyuncs.com'], - }, - }, - ], - Version: '1', - }, - DeletionForce: false, - IgnoreExisting: false, - Policies: [ - { - PolicyDocument: { - Statement: [ - { - Action: ['oss:*'], - Effect: 'Allow', - Resource: ['*'], - }, - ], - Version: '1', - }, - PolicyName: 'AliyunOSSFullAccess', - }, - { - PolicyDocument: { - Statement: [ - { - Action: ['log:*'], - Effect: 'Allow', - Resource: ['*'], - }, - { - Action: ['ram:CreateServiceLinkedRole'], - Condition: { - StringEquals: { - 'ram:ServiceName': [ - 'audit.log.aliyuncs.com', - 'alert.log.aliyuncs.com', - 'middlewarelens.log.aliyuncs.com', - 'storagelens.log.aliyuncs.com', - 'ai-lens.log.aliyuncs.com', - 'securitylens.log.aliyuncs.com', - ], - }, - }, - Effect: 'Allow', - Resource: ['*'], - }, - ], - Version: '1', - }, - PolicyName: 'AliyunLogFullAccess', - }, - ], - RoleName: { - 'Fn::Join': [ - '-', - [ - 'ros-cdk', - { - 'Fn::Select': [ - 0, - { - 'Fn::Split': [ - '-', - { - Ref: 'ALIYUN::StackId', - }, - ], - }, - ], - }, - ], - ], - }, - }, - Type: 'ALIYUN::RAM::Role', - }, - FCServiceFormy_bucket_bucket_code_deployment: { + FCServiceForsi_auto_my_bucket_bucket_code_deployment: { Properties: { DeletionForce: false, Description: 'FC service for oss deployment by CDK', Role: { - 'Fn::GetAtt': ['FCRoleFormy_bucket_bucket_code_deployment', 'Arn'], + 'Fn::GetAtt': ['si_auto_od_bucket_role', 'Arn'], }, ServiceName: { 'Fn::Join': [ @@ -1483,7 +1398,7 @@ export const bucketWithWebsiteRos = { }, Type: 'ALIYUN::OSS::Bucket', }, - my_bucket_bucket_code_deployment: { + si_auto_my_bucket_bucket_code_deployment: { Properties: { Parameters: { destinationBucket: { @@ -1501,11 +1416,38 @@ export const bucketWithWebsiteRos = { ], }, ServiceToken: { - 'Fn::GetAtt': ['FCFunctionFormy_bucket_bucket_code_deployment', 'ARN'], + 'Fn::GetAtt': ['FCFunctionForsi_auto_my_bucket_bucket_code_deployment', 'ARN'], }, Timeout: 3000, }, Type: 'ALIYUN::ROS::CustomResource', }, + si_auto_od_bucket_role: { + Properties: { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { + Service: ['fc.aliyuncs.com'], + }, + }, + ], + Version: '1', + }, + DeletionForce: false, + Description: + 'roles created by ServerlessInsight for oss deployment to put files to oss bucket during deployment', + IgnoreExisting: false, + PolicyAttachments: { + System: ['AliyunOSSFullAccess', 'AliyunLogFullAccess'], + }, + RoleName: { + 'Fn::Sub': 'si-auto-od-bucket-role-${ALIYUN::StackId}', + }, + }, + Type: 'ALIYUN::RAM::Role', + }, }, };