public constructor()

in src/backend/transliterator/index.ts [93:203]


  public constructor(scope: Construct, id: string, props: TransliteratorProps) {
    super(scope, id);

    const repository = props.vpcEndpoints?.codeArtifact && props.vpcEndpoints.codeArtifactApi
      ? props.codeArtifact?.throughVpcEndpoint(props.vpcEndpoints.codeArtifactApi, props.vpcEndpoints.codeArtifact)
      : props.codeArtifact;

    const bucket = props.vpcEndpoints
      ? s3.throughVpcEndpoint(props.bucket, props.vpcEndpoints.s3)
      : props.bucket;

    const environment: Record<string, string> = {
      // temporaty hack to generate construct-hub compliant markdown.
      // see https://github.com/cdklabs/jsii-docgen/blob/master/src/docgen/render/markdown.ts#L172
      HEADER_SPAN: 'true',
      // Set embedded metrics format environment to "Local", to have a consistent experience.
      AWS_EMF_ENVIRONMENT: 'Local',
    };
    if (props.vpcEndpoints?.codeArtifactApi) {
      // Those are returned as an array of HOSTED_ZONE_ID:DNS_NAME... We care
      // only about the DNS_NAME of the first entry in that array (which is
      // the AZ-agnostic DNS name).
      environment.CODE_ARTIFACT_API_ENDPOINT = Fn.select(1,
        Fn.split(':',
          Fn.select(0, props.vpcEndpoints.codeArtifactApi.vpcEndpointDnsEntries),
        ),
      );
    }
    if (props.codeArtifact) {
      environment.CODE_ARTIFACT_DOMAIN_NAME = props.codeArtifact.repositoryDomainName;
      environment.CODE_ARTIFACT_DOMAIN_OWNER = props.codeArtifact.repositoryDomainOwner;
      environment.CODE_ARTIFACT_REPOSITORY_ENDPOINT = props.codeArtifact.repositoryNpmEndpoint;
    }

    this.logGroup = new LogGroup(this, 'LogGroup', { retention: props.logRetention });
    this.containerDefinition = new Container(this, 'Resource', {
      environment,
      logging: LogDrivers.awsLogs({ logGroup: this.logGroup, streamPrefix: 'transliterator' }),
      taskDefinition: new FargateTaskDefinition(this, 'TaskDefinition', {
        cpu: 4_096,
        memoryLimitMiB: 8_192,
      }),
    });

    repository?.grantReadFromRepository(this.taskDefinition.taskRole);

    // The task handler reads & writes to this bucket.
    bucket.grantRead(this.taskDefinition.taskRole, `${constants.STORAGE_KEY_PREFIX}*${constants.ASSEMBLY_KEY_SUFFIX}`);
    bucket.grantRead(this.taskDefinition.taskRole, `${constants.STORAGE_KEY_PREFIX}*${constants.PACKAGE_KEY_SUFFIX}`);
    bucket.grantWrite(this.taskDefinition.taskRole, `${constants.STORAGE_KEY_PREFIX}*${constants.UNINSTALLABLE_PACKAGE_SUFFIX}`);
    bucket.grantDelete(this.taskDefinition.taskRole, `${constants.STORAGE_KEY_PREFIX}*${constants.UNINSTALLABLE_PACKAGE_SUFFIX}`);
    for (const language of DocumentationLanguage.ALL) {
      bucket.grantWrite(this.taskDefinition.taskRole, `${constants.STORAGE_KEY_PREFIX}*${constants.docsKeySuffix(language)}`);
      bucket.grantWrite(this.taskDefinition.taskRole, `${constants.STORAGE_KEY_PREFIX}*${constants.docsKeySuffix(language, '*')}`);
      bucket.grantWrite(this.taskDefinition.taskRole, `${constants.STORAGE_KEY_PREFIX}*${constants.docsKeySuffix(language)}${constants.NOT_SUPPORTED_SUFFIX}`);
      bucket.grantWrite(this.taskDefinition.taskRole, `${constants.STORAGE_KEY_PREFIX}*${constants.docsKeySuffix(language, '*')}${constants.NOT_SUPPORTED_SUFFIX}`);
      bucket.grantWrite(this.taskDefinition.taskRole, `${constants.STORAGE_KEY_PREFIX}*${constants.docsKeySuffix(language)}${constants.CORRUPT_ASSEMBLY_SUFFIX}`);
      bucket.grantWrite(this.taskDefinition.taskRole, `${constants.STORAGE_KEY_PREFIX}*${constants.docsKeySuffix(language, '*')}${constants.CORRUPT_ASSEMBLY_SUFFIX}`);
      bucket.grantDelete(this.taskDefinition.taskRole, `${constants.STORAGE_KEY_PREFIX}*${constants.docsKeySuffix(language)}${constants.CORRUPT_ASSEMBLY_SUFFIX}`);
      bucket.grantDelete(this.taskDefinition.taskRole, `${constants.STORAGE_KEY_PREFIX}*${constants.docsKeySuffix(language, '*')}${constants.CORRUPT_ASSEMBLY_SUFFIX}`);
    }

    const executionRole = this.taskDefinition.obtainExecutionRole();
    props.vpcEndpoints?.ecrApi.addToPolicy(new PolicyStatement({
      effect: Effect.ALLOW,
      actions: [
        'ecr:GetAuthorizationToken',
      ],
      resources: ['*'], // Action does not support resource scoping
      principals: [executionRole],
      sid: 'Allow-ECR-ReadOnly',
    }));
    props.vpcEndpoints?.ecr.addToPolicy(new PolicyStatement({
      effect: Effect.ALLOW,
      actions: [
        'ecr:BatchCheckLayerAvailability',
        'ecr:GetDownloadUrlForLayer',
        'ecr:BatchGetImage',
      ],
      // We cannot get the ECR repository info from an asset... So scoping down to same-account repositories instead...
      resources: [Stack.of(this).formatArn({ service: 'ecr', resource: 'repository', arnFormat: ArnFormat.SLASH_RESOURCE_NAME, resourceName: '*' })],
      principals: [executionRole],
      sid: 'Allow-ECR-ReadOnly',
    }));

    props.vpcEndpoints?.cloudWatchLogs.addToPolicy(new PolicyStatement({
      effect: Effect.ALLOW,
      actions: [
        'logs:CreateLogStream',
        'logs:PutLogEvents',
      ],
      resources: [
        Stack.of(this).formatArn({ service: 'logs', resource: 'log-group', arnFormat: ArnFormat.COLON_RESOURCE_NAME, resourceName: this.logGroup.logGroupName }),
        Stack.of(this).formatArn({ service: 'logs', resource: 'log-group', arnFormat: ArnFormat.COLON_RESOURCE_NAME, resourceName: `${this.logGroup.logGroupName}:log-stream:*` }),
      ],
      principals: [executionRole],
      sid: 'Allow-Logging',
    }));

    props.vpcEndpoints?.stepFunctions.addToPolicy(new PolicyStatement({
      effect: Effect.ALLOW,
      actions: [
        'states:SendTaskFailure',
        'states:SendTaskHeartbeat',
        'states:SendTaskSuccess',
      ],
      resources: ['*'], // Actions don't support resource scoping
      principals: [this.taskDefinition.taskRole],
      sid: 'Allow-StepFunctions-Callbacks',
    }));
  }