FIX: Permissions To s3 And MediaConvert]
Build, Push and Deploy CMS / build-push-deploy (push) Successful in 2m2s
Build, Push and Deploy CMS / build-push-deploy (push) Successful in 2m2s
This commit is contained in:
+5
-4
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"thamanyah/cms/v2/internal/handlers"
|
||||
"thamanyah/cms/v2/internal/services"
|
||||
|
||||
@@ -21,7 +22,7 @@ func main() {
|
||||
|
||||
concreteS3Client := &services.S3Concrete{
|
||||
S3Client: s3.NewFromConfig(config),
|
||||
Bucket: "",
|
||||
Bucket: os.Getenv("S3_BUCKET"),
|
||||
}
|
||||
|
||||
concreteS3Client.AssertSuccessfulConnection(context.Background())
|
||||
@@ -30,9 +31,9 @@ func main() {
|
||||
|
||||
services.MediaConvertClient = &services.MediaConvertConcrete{
|
||||
MediaConvertClient: mediaconvert.NewFromConfig(config),
|
||||
Role: "test",
|
||||
InputBucket: "",
|
||||
OutputBucket: "",
|
||||
Role: os.Getenv("MEDIACONVERT_ROLE_ARN"),
|
||||
InputBucket: os.Getenv("MEDIACONVERT_INPUT_BUCKET"),
|
||||
OutputBucket: os.Getenv("MEDIACONVERT_OUTPUT_BUCKET"),
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
|
||||
+191
-11
@@ -80,17 +80,29 @@ func newServiceDatabase(ctx *pulumi.Context, provider *postgresql.Provider, serv
|
||||
return password, nil
|
||||
}
|
||||
|
||||
// envVar is a name/value pair for a container's extra environment variables,
|
||||
// where the value is only known after other resources are provisioned.
|
||||
type envVar struct {
|
||||
Name string
|
||||
Value pulumi.StringOutput
|
||||
}
|
||||
|
||||
// deployFargateService gives a service its own ECR repo, log group, task
|
||||
// definition, ECS service, and its own ALB — so it gets its own DNS name
|
||||
// rather than sharing one load balancer with the other service on a
|
||||
// different port. The service's DB password is stored in Secrets Manager and
|
||||
// injected into the container as a secret rather than a plaintext env var.
|
||||
// taskRole is optional (nil means no TaskRoleArn is set, i.e. the container
|
||||
// gets no AWS identity of its own beyond execRole's pull/logs permissions);
|
||||
// extraEnv is appended to the container's environment on top of the DB_* vars.
|
||||
func deployFargateService(
|
||||
ctx *pulumi.Context,
|
||||
name string,
|
||||
containerPort int,
|
||||
cluster *ecs.Cluster,
|
||||
execRole *iam.Role,
|
||||
taskRole *iam.Role,
|
||||
extraEnv []envVar,
|
||||
vpcID string,
|
||||
subnetIDs []string,
|
||||
albSecurityGroup *ec2.SecurityGroup,
|
||||
@@ -194,7 +206,12 @@ func deployFargateService(
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
containerDefinitions := pulumi.All(repo.RepositoryUrl, logGroup.Name, secret.Arn, dbAddress, dbPort).ApplyT(
|
||||
baseArgs := []any{repo.RepositoryUrl, logGroup.Name, secret.Arn, dbAddress, dbPort}
|
||||
for _, ev := range extraEnv {
|
||||
baseArgs = append(baseArgs, ev.Value)
|
||||
}
|
||||
|
||||
containerDefinitions := pulumi.All(baseArgs...).ApplyT(
|
||||
func(args []any) (string, error) {
|
||||
image := args[0].(string) + ":latest"
|
||||
logGroupName := args[1].(string)
|
||||
@@ -202,6 +219,16 @@ func deployFargateService(
|
||||
dbHost := args[3].(string)
|
||||
dbPort := args[4].(int)
|
||||
|
||||
environment := []map[string]string{
|
||||
{"name": "DB_HOST", "value": dbHost},
|
||||
{"name": "DB_PORT", "value": fmt.Sprintf("%d", dbPort)},
|
||||
{"name": "DB_NAME", "value": name},
|
||||
{"name": "DB_USER", "value": name},
|
||||
}
|
||||
for i, ev := range extraEnv {
|
||||
environment = append(environment, map[string]string{"name": ev.Name, "value": args[5+i].(string)})
|
||||
}
|
||||
|
||||
def := []map[string]any{
|
||||
{
|
||||
"name": name,
|
||||
@@ -209,12 +236,7 @@ func deployFargateService(
|
||||
"portMappings": []map[string]any{
|
||||
{"containerPort": containerPort, "protocol": "tcp"},
|
||||
},
|
||||
"environment": []map[string]string{
|
||||
{"name": "DB_HOST", "value": dbHost},
|
||||
{"name": "DB_PORT", "value": fmt.Sprintf("%d", dbPort)},
|
||||
{"name": "DB_NAME", "value": name},
|
||||
{"name": "DB_USER", "value": name},
|
||||
},
|
||||
"environment": environment,
|
||||
"secrets": []map[string]string{
|
||||
{"name": "DB_PASSWORD", "valueFrom": secretArn},
|
||||
},
|
||||
@@ -233,7 +255,7 @@ func deployFargateService(
|
||||
},
|
||||
).(pulumi.StringOutput)
|
||||
|
||||
taskDefinition, err := ecs.NewTaskDefinition(ctx, name+"-task", &ecs.TaskDefinitionArgs{
|
||||
taskDefinitionArgs := &ecs.TaskDefinitionArgs{
|
||||
Family: pulumi.String(name),
|
||||
Cpu: pulumi.String("256"),
|
||||
Memory: pulumi.String("512"),
|
||||
@@ -241,7 +263,12 @@ func deployFargateService(
|
||||
RequiresCompatibilities: pulumi.StringArray{pulumi.String("FARGATE")},
|
||||
ExecutionRoleArn: execRole.Arn,
|
||||
ContainerDefinitions: containerDefinitions,
|
||||
})
|
||||
}
|
||||
if taskRole != nil {
|
||||
taskDefinitionArgs.TaskRoleArn = taskRole.Arn
|
||||
}
|
||||
|
||||
taskDefinition, err := ecs.NewTaskDefinition(ctx, name+"-task", taskDefinitionArgs)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
@@ -579,15 +606,168 @@ func main() {
|
||||
return err
|
||||
}
|
||||
|
||||
// Private bucket for raw video uploads (pre-transcode). Kept separate
|
||||
// from encoded-bucket, which is fronted by CloudFront/OAC for public
|
||||
// delivery of finished renditions — raw source video must not be
|
||||
// reachable through that CDN.
|
||||
rawUploadsBucket, err := s3.NewBucket(ctx, "raw-uploads-bucket", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s3.NewBucketPublicAccessBlock(ctx, "raw-uploads-bucket-public-access-block", &s3.BucketPublicAccessBlockArgs{
|
||||
Bucket: rawUploadsBucket.ID(),
|
||||
BlockPublicAcls: pulumi.Bool(true),
|
||||
BlockPublicPolicy: pulumi.Bool(true),
|
||||
IgnorePublicAcls: pulumi.Bool(true),
|
||||
RestrictPublicBuckets: pulumi.Bool(true),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Role MediaConvert itself assumes (passed as CreateJobInput.Role by
|
||||
// the cms service) to read the raw upload and write the encoded
|
||||
// output — distinct from the ECS task role below.
|
||||
mediaConvertAssumePolicy, err := json.Marshal(map[string]any{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": []map[string]any{
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "sts:AssumeRole",
|
||||
"Principal": map[string]string{"Service": "mediaconvert.amazonaws.com"},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mediaConvertRole, err := iam.NewRole(ctx, "mediaconvert-service-role", &iam.RoleArgs{
|
||||
AssumeRolePolicy: pulumi.String(mediaConvertAssumePolicy),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mediaConvertS3Policy := pulumi.All(rawUploadsBucket.Arn, bucket.Arn).ApplyT(
|
||||
func(args []any) (string, error) {
|
||||
rawUploadsArn := args[0].(string)
|
||||
encodedBucketArn := args[1].(string)
|
||||
|
||||
doc := map[string]any{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": []map[string]any{
|
||||
{
|
||||
"Sid": "ReadRawUploads",
|
||||
"Effect": "Allow",
|
||||
"Action": "s3:GetObject",
|
||||
"Resource": rawUploadsArn + "/*",
|
||||
},
|
||||
{
|
||||
"Sid": "WriteEncodedOutput",
|
||||
"Effect": "Allow",
|
||||
"Action": "s3:PutObject",
|
||||
"Resource": encodedBucketArn + "/*",
|
||||
},
|
||||
},
|
||||
}
|
||||
b, err := json.Marshal(doc)
|
||||
return string(b), err
|
||||
},
|
||||
).(pulumi.StringOutput)
|
||||
|
||||
_, err = iam.NewRolePolicy(ctx, "mediaconvert-s3-access", &iam.RolePolicyArgs{
|
||||
Role: mediaConvertRole.ID(),
|
||||
Policy: mediaConvertS3Policy,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// cms's own ECS task role (distinct from the shared execRole, which
|
||||
// is only for the ECS agent's pull/logs/secrets access): lets the
|
||||
// running container call S3 and MediaConvert directly. discovery
|
||||
// gets none of this since it never touches either API.
|
||||
cmsTaskRole, err := iam.NewRole(ctx, "cms-task-role", &iam.RoleArgs{
|
||||
AssumeRolePolicy: pulumi.String(execRoleAssumePolicy),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmsTaskPolicy := pulumi.All(rawUploadsBucket.Arn, mediaConvertRole.Arn).ApplyT(
|
||||
func(args []any) (string, error) {
|
||||
rawUploadsArn := args[0].(string)
|
||||
mediaConvertRoleArn := args[1].(string)
|
||||
|
||||
doc := map[string]any{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": []map[string]any{
|
||||
{
|
||||
"Sid": "ListRawUploadsBucket",
|
||||
"Effect": "Allow",
|
||||
"Action": "s3:ListBucket",
|
||||
"Resource": rawUploadsArn,
|
||||
},
|
||||
{
|
||||
"Sid": "RawUploadsObjectAccess",
|
||||
"Effect": "Allow",
|
||||
"Action": []string{
|
||||
"s3:PutObject",
|
||||
"s3:GetObject",
|
||||
},
|
||||
"Resource": rawUploadsArn + "/*",
|
||||
},
|
||||
{
|
||||
"Sid": "CreateTranscodeJob",
|
||||
"Effect": "Allow",
|
||||
"Action": "mediaconvert:CreateJob",
|
||||
"Resource": "*",
|
||||
},
|
||||
{
|
||||
"Sid": "PassMediaConvertRole",
|
||||
"Effect": "Allow",
|
||||
"Action": "iam:PassRole",
|
||||
"Resource": mediaConvertRoleArn,
|
||||
"Condition": map[string]any{
|
||||
"StringEquals": map[string]string{
|
||||
"iam:PassedToService": "mediaconvert.amazonaws.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
b, err := json.Marshal(doc)
|
||||
return string(b), err
|
||||
},
|
||||
).(pulumi.StringOutput)
|
||||
|
||||
_, err = iam.NewRolePolicy(ctx, "cms-task-role-policy", &iam.RolePolicyArgs{
|
||||
Role: cmsTaskRole.ID(),
|
||||
Policy: cmsTaskPolicy,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmsExtraEnv := []envVar{
|
||||
{Name: "AWS_REGION", Value: pulumi.String(awsRegion).ToStringOutput()},
|
||||
{Name: "S3_BUCKET", Value: rawUploadsBucket.ID().ToStringOutput()},
|
||||
{Name: "MEDIACONVERT_INPUT_BUCKET", Value: rawUploadsBucket.ID().ToStringOutput()},
|
||||
{Name: "MEDIACONVERT_OUTPUT_BUCKET", Value: bucket.ID().ToStringOutput()},
|
||||
{Name: "MEDIACONVERT_ROLE_ARN", Value: mediaConvertRole.Arn},
|
||||
}
|
||||
|
||||
cmsRepo, cmsService, cmsAlb, err := deployFargateService(ctx, "cms", 8081,
|
||||
cluster, execRole, vpc.Id, subnets.Ids, albSecurityGroup, serviceSecurityGroup,
|
||||
cluster, execRole, cmsTaskRole, cmsExtraEnv, vpc.Id, subnets.Ids, albSecurityGroup, serviceSecurityGroup,
|
||||
db.Address, db.Port, cmsPassword)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
discoveryRepo, discoveryService, discoveryAlb, err := deployFargateService(ctx, "discovery", 8080,
|
||||
cluster, execRole, vpc.Id, subnets.Ids, albSecurityGroup, serviceSecurityGroup,
|
||||
cluster, execRole, nil, nil, vpc.Id, subnets.Ids, albSecurityGroup, serviceSecurityGroup,
|
||||
db.Address, db.Port, discoveryPassword)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
Reference in New Issue
Block a user