diff --git a/examples/xovnoc/rack.mu b/examples/xovnoc/rack.mu index e40ca741a..71385f983 100644 --- a/examples/xovnoc/rack.mu +++ b/examples/xovnoc/rack.mu @@ -15,795 +15,28 @@ import "aws/sns" service Rack { // TODO: lambda code. // TODO: that big nasty UserData shell script. - // TODO: factor things out into separate initialization helpers, perhaps. // TODO: possibly even refactor individual things into services (e.g., the networks). // TODO: we probably need a ToString()-like thing for services (e.g., ARN/ID for most AWS ones). - new() { - // IAM goo. - customTopicRole := new iam.Role { - assumeRolePolicyDocument: { - version: "2012-10-17" - statement: [{ - effect: "Allow" - principal: { service: [ "lambda.amazonaws.com" ] } - action: [ "sts:AssumeRole" ] - }] - path: "/xovnoc/" - policies: [{ - policyName: "Administrator" - policyDocument: { - version: "2012-10-17" - statement: [ - { effect: "Allow", action: "*", resource: "*" } - { effect: "Deny", action: "s3:DeleteObject", resource: "*" } - ] - } - }] - } + resources { + security := new rackSecurity {} + network := new rackNetwork { + existingVpc: existingVpc + private: private + privateApi: privateApi + subnetCIDRs: subnetCIDRs + subnetPrivateCIDRs: subnetPrivateCIDRs + vpccidr: vpccidr } - kerneluser := new iam.User { - path: "xovnoc" - policies: [{ - policyName: "Administrator" - policyDocument: { - version: "2012-10-17" - statement: [{ effect: "Allow", action: "*", resource: "*"}] - } - }] + logging := new rackLogging { + role: security.logSubscriptionFilterRole } - kernelAccess := new iam.AccessKey { - serial: 1 - status: "Active" - userName: kernelUser - } - logSubscriptionFilterRole := new iam.Role { - assumeRolePolicyDocument: { - version: "2012-10-17" - statement: [{ - effect: "Allow" - principal: { service: [ "lambda.amazonaws.com" ] } - action: [ "sts:AssumeRole" ] - }] - path: "/xovnoc/" - policies: [{ - policyName: "LogSubscriptionFilterRole" - policyDocument: { - version: "2012-10-17" - statement: [ - { - effect: "Allow" - action: [ - "logs:CreateLogGroup" - "logs:CreateLogStream" - "logs:PutLogEvents" - ] - resource: "arn:aws:logs:*:*:*" - } - { - effect: "Allow" - action: [ "cloudwatch:PutMetricData" ] - resource: "*" - } - ] - } - }] - } - } - iamRole := new iam.Role { - assumeRolePolicyDocument: { - version: "2012-10-17" - statement: [{ - effect: "Allow" - principal: { service: [ "ec2.amazonaws.com" ] } - action: [ "sts:AssumeRole" ] - }] - } - path: "/xovnoc/" - policies: [{ - policyName: "ClusterInstanceRole" - policyDocument: { - version: "2012-10-17" - statement: [{ - effect: "Allow" - action: [ - "autoscaling:CompleteLifecycleAction" - "autoscaling:DescribeAutoScalingInstances" - "autoscaling:DescribeLifecycleHooks" - "autoscaling:SetInstanceHealth" - "ecr:GetAuthorizationToken" - "ecr:GetDownloadUrlForLayer" - "ecr:BatchGetImage" - "ecr:BatchCheckLayerAvailability" - "ec2:DescribeInstances" - "ecs:CreateCluster" - "ecs:DeregisterContainerInstance" - "ecs:DiscoverPollEndpoint" - "ecs:Poll" - "ecs:RegisterContainerInstance" - "ecs:StartTelemetrySession" - "ecs:Submit*" - "kinesis:PutRecord" - "kinesis:PutRecords" - "logs:CreateLogStream" - "logs:DescribeLogStreams" - "logs:PutLogEvents" - ] - resource: [ "*" ] - }] - } - }] - } - instanceProfile := new aim.InstanceProfile { - path: "/xovnoc/" - roles: [ iamRole ] - } - instancesLifecycleRole := new iam.Role { - assumeRolePolicyDocument: { - version: "2012-10-17" - statement: [{ - effect: "Allow" - principal: { service: [ "lambda.amazonaws.com" ] } - action: [ "sts:AssumeRole" ] - }] - path: "/xovnoc/" - policies: [{ - policyName: "InstancesLifecycleRole" - policyDocument: { - version: "2012-10-17" - statement: [{ - effect: "Allow" - action: [ "sns:Publish" ] - resource: instancesLifecycleTopic - }] - } - }] - } - } - instancesHandlerRole := new iam.Role { - assumeRolePolicyDocument: { - version: "2012-10-17" - statement: [{ - effect: "Allow" - principal: { service: [ "lambda.amazonaws.com" ] } - action: [ "sts:AssumeRole" ] - }] - path: "/xovnoc/" - policies: [{ - policyName: "InstancesLifecycleHandlerRole" - policyDocument: { - version: "2012-10-17" - statement: [{ - effect: "Allow" - action: [ - "autoscaling:CompleteLifecycleAction", - "ecs:DeregisterContainerInstance", - "ecs:DescribeContainerInstances", - "ecs:DescribeServices", - "ecs:ListContainerInstances", - "ecs:ListServices", - "elasticloadbalancing:DeregisterInstancesFromLoadBalancer", - "elasticloadbalancing:DescribeInstanceHealth", - "elasticloadbalancing:DescribeLoadBalancers", - "elasticloadbalancing:DescribeTags", - "lambda:GetFunction", - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:PutLogEvents" - ] - resource: "*" - }] - } - }] - } - } - - encryptionKey := new KMSKey { - serviceToken: customTopic.Arn - description: "Xovnoc Master Encryption" - keyUsage: "ENCRYPT_DECRYPT" - } - - // Logging resources. - logGroup := new logs.LogGroup {} - logSubscriptionFilterPermission := new lambda.Permission { - action: "lambda:InvokeFunction" - functionName: logSubscriptionFilterFunction - principal: "logs." + context.region + ".amazonaws.com" - sourceAccount: context.accountId - sourceArn: logGroup.Arn - } - logSubscriptionFilterFunction := new lambda.Function { - code: // TODO - handler: "index.handler" - memorySize: 128 - role: logSubscriptionFilterRole.Arn - runtime: "nodejs" - timeout: 30 - } - logSubscriptionFilter := new logs.SubscriptionFilter { - destinationArn: logSubscriptionFilterFunction.Arn - filterPattern: "" - logGroupName: logGroup - } - - // Topic resources. - notificationTopic := new sns.Topic { - topicName: context.stack.name + "-notifications" - } - customTopic := new lambda.Function { - code: // TODO - handler: "index.external" - memorySize: 128 - role: customTopicRole - runtime: "nodejs" - timeout: 300 - } - instancesLifecycleTopic := new sns.Topic { - subscription: { - endpoint: instancesLifecycleHandler - protocol: lambda - } - topicName: context.stack.name + "-lifecycle" - } - instancesLifecycleHandler := new lambda.Function { - code: // TODO - description: `{ "Cluster": "${cluster}", "Rack": "${context.stack.name}" }` - handler: "index.external" - memorySize: 128 - role: instancesLifecycleHandlerRole - runtime: nodejs - timeout: 300 - } - instancesLifecycleHandlerPermission := new lambda.Permission { - source: instancesLifecycleTopic - function: instancesLifecycleHandler - action: "lambda:InvokeFunction" - principal: "sns.amazonaws.com" - } - - cluster := new ecs.Cluster {} - - var vpc: ec2.VPC - if existingVpc == "" { - vpc = new ec2.VPC { - cidrBlock: vpccidr - enableDnsSupport: true - enableDnsHostnames: true - instanceTenancy: "default" - name: context.stack.name - } - - gateway := new ec2.InternetGateway {} - - gatewayAttachment := new ec2.VPCGatewayAttachment { - internetGateway: gateway - vpc: vpc - } - - routes := new ec2.RouteTable { - vpc: vpc - } - - routeDefault := new ec2.Route { - destinationCidrBlock: "0.0.0.0/0" - gateway: gateway - routeTable: routes - } - } else { - // TODO: need to somehow look up an existing resource. - vpc = existingVpc - } - - availabilityZones := new EC2AvailabilityZones { - serviceToken: customTopic - vpc: vpc - } - - var subnets: ec2.Subnet[] - for zone in availabilityZones { - append(subnets, new ec2.Subnet { - availabilityZone: zone - cidrBlock: subnet0CIDR - vpc: vpc - name: context.stack.name + " public " + i - }) - } - - if private { - var natAddresses: ec2.EIP[] - var nats: ec2.NatGateway[] - var routeTablePrivates: ec2.RouteTable[] - var routeTableDefaultPrivates: ec2.Route[] - for i, subnet in subnets { - append(natAddresses, new ec.EIP { - domain: vpc - }) - append(nats, new ec2.NatGateway { - allocation: natAddresses[i] - subnet: subnets - }) - append(routeTablePrivates, new ec2.RouteTable { - vpc: vpc - }) - append(routeTableDefaultPrivates, new ec2.Route { - destinationCidrBlock: "0.0.0.0/0" - natGateway: nats[i] - routeTable: routeTablePrivates[i] - }) - } - - var subnetPrivates: ec2.Subnet[] - for i, zone in availabilityZones { - append(subnetPrivates, ec2.Subnet { - availabilityZone: zone - cidrBlock: subnetPrivateCIDR[i] - vpc: vpc - name: context.stack.name + " private " + i - } - } - } - - if existingVpc == "" { - var subnetRoutes: ec2.SubnetRouteTableAssociation[] - for i, subnet in subnets { - append(subnetRoutes, new ec2.SubnetRouteTableAssociation { - subnet: subnet0 - routesTable: routes - }) - } - - if private { - var subnetPrivateRoutes: ec2.SubnetRouteTableAssociation[] - for i, subnetPrivate in subnetPrivates { - append(subnetPrivateRoutes, ec2.SubnetRouteTableAssociation { - subnet: subnetPrivate - routesTable: routeTablePrivates[i] - }) - } - } - } - - securityGroup := new ec2.SecurityGroup: { - groupDescription: "Instances" - securityGroupIngress: [ - { ipProtocol: "tcp", fromPort: 22, toPort: 22, cidrIp: vpccidr } - { ipProtocol: "tcp", fromPort: 0, toPort: 65535, cidrIp: vpccidr } - { ipProtocol: "udp", fromPort: 0, toPort: 65535, cidrIp: vpccidr } - ] - vpc: vpc - } - - launchConfiguration := new autoscaling.LaunchConfiguration { - associatePublicIpAddress: !private - blockDeviceMappings: [ - { - deviceName: "/dev/sdb" - ebs: { - volumeSize: swapSize - volumeType: "gp2" - } - } - { - deviceName: "/dev/xvdcz" - ebs: { - volumeSize: volumeSize - volumeType: "gp2" - } - } - ] - iamInstanceProfile: instanceProfile - imageId: ami ?? regionConfig[context.region].ami - instanceMonitoring: true - instanceType: instanceType - keyName: key ?? undefined - placementTenancy: tenancy - securityGroups: [ securityGroup ] - userData: base64(makeUserData) - } - - instances := new autoscaling.AutoScalingGroup { - launchConfiguration: launchConfiguration - availabilityZones: availabilityZones - vpcZoneIdentifier: private ? subnetPrivates : subnets - cooldown: 5 - desiredCapacity: instanceCount - healthCheckType: "EC2" - healthCheckGracePeriod: 120 - minSize: 1 - maxSize: 1000 - metricsCollection: [ { granularity: "1Minute" } ] - name: context.stack.name - tags: [ - { - key: "Name" - value: context.stack.name - propagateAtLaunch: true - } - { - key: "Rack" - value: context.stack.name - propagateAtLaunch: true - } - { - key: "GatewayAttachment" - value: existingVpc == "" ? gatewayAttachment : "existing" - propagateAtLaunch: false - } - ] - updatePolicy: { - // TODO: in CF, this isn't a "property"; it's a peer to properties. - autoScalingRollingUpdate: { - maxBatchSize: instanceUpdateBatchSize - minInstancesInService: instanceCount - pauseTime: "PT15M" - suspendProcesses: [ "ScheduledActions" ] - waitOnResourceSignals: "true" - } - } - } - instancesLifecycleLaunching := new autoscaling.LifecycleHook: { - autoScalingGroup: instances - defaultResult: "CONTINUE" - heartbeatTimeout: 600 - lifecycleTransition: "autoscaling:EC2_INSTANCE_LAUNCHING" - notificationTarget: instancesLifecycleTopic - roleARN: instancesLifecycleRole.Arn - } - instancesLifecycleTerminating := new autoscaling.LifecycleHook: { - autoScalingGroup: instances - defaultResult: "CONTINUE" - heartbeatTimeout: 300 - lifecycleTransition: "autoscaling:EC2_INSTANCE_TERMINATING" - notificationTarget: instancesLifecycleTopic - roleARN: instancesLifecycleRole.Arn - } - - registryBucket := new s3.bucket: { - deletionPolicy: "Retain" // TODO: not actually a property, it's a peer. - accessControl: "Private" - } - registryUser := new iam.User { - path: "/xovnoc/" - policies: [{ - policyName: "Administrator" - policyDocument: { - version: "2012-10-17" - statement: [{ effect: "Allow", action: "*", resource: "*" }] - } - }] - } - registryAccess := new iam.AccessKey { - serial: 1 - status: "Active" - user: registryUser - } - - balancer := new elasticloadbalancing.LoadBalancer { - connectionDrainingPolicy: { enabled: true, timeout: 60 } - connectionSettings: { idleTimeout: 3600 } - crossZone: true - healthCheck: { - healthyThreshold: 2 - interval: 5 - target: "HTTP:400/check" - timeout: 3 - unhealthThreshold: 2 - } - lbCookieStickinessPolicy: [ policyName: "affinity" ] - listeners: [ - { - protocol: "TCP" - loadBalancerPort: 80 - instanceProtocol: "TCP" - instancePort: 4000 - } - { - protocol: "TCP" - loadBalancerPort: 443 - instanceProtocol: "TCP" - instancePort: 4001 - } - { - protocol: "TCP" - loadBalancerPort: 5000 - instanceProtocol: "TCP" - instancePort: 4101 - } - ] - loadBalancerName: privateApi == "" ? undefined : "internal" - securityGroups: [ balancerSecurityGroup ] - subnets: privateApi == "" ? subnets : subnetPrivates - tags: [{ key: "GatewayAttachment", value: existingVpc == "" ? gatewayAttachment : "existing" }] - } - balancerSecurityGroup := new ec2.SecurityGroup { - groupDescription: context.stack.name + "-balancer" - securityGroupIngress: [ - { - cidrIp: privateApi ? vpccidr : "0.0.0.0/0" - ipProtocol: "tcp" - fromPort: 80 - toPort: 80 - } - { - cidrIp: privateApi ? vpccidr : "0.0.0.0/0" - ipProtocol: "tcp" - fromPort: 443 - toPort: 443 - } - { - cidrIp: privateApi ? vpccidr : "0.0.0.0/0" - ipProtocol: "tcp" - fromPort: 5000 - toPort: 5000 - } - ] - vpc: vpc - } - rackWeb := new ecs.Service { - cluster: cluster - deploymentConfiguration: { - minimumHealthyPercent: 100 - maximumPercent: 200 - } - desiredCount: 2 - loadBalancers: [{ - containerName: "web" - containerPort: 3000 - loadBalancer: balancer - }] - role: serviceRole - taskDefinition: rackWebTasks - } - rackMonitor := new ecs.Service { - cluster: cluster - deploymentConfiguration: { - minimumHealthyPercent: 100 - maximumPercent: 200 - } - desiredCount: 1 - taskDefinition: rackMonitorTasks - } - serviceRole := new iam.Role { - assumeRolePolicyDocument: { - version: "2012-10-17" - statement: [{ - effect: "Allow" - principal: { service: [ "lambda.amazonaws.com" ] } - action: [ "sts:AssumeRole" ] - }] - path: "/xovnoc/" - policies: [{ - policyName: "ServiceRole" - policyDocument: { - version: "2012-10-17" - statement: [{ - effect: "Allow" - action: [ - "elasticloadbalancing:Describe*" - "elasticloadbalancing:DeregisterInstancesFromLoadBalancer" - "elasticloadbalancing:RegisterInstancesWithLoadBalancer" - "ec2:Describe*" - "ec2:AuthorizeSecurityGroupIngress" - ] - resource: "*" - }] - } - }] - } - } - - dynamoBuilds := new dynamodb.Table { - tableName: context.stack.name + "-builds" - attributeDefinitions: [ - { attributeName: "id", attributeType: "S" } - { attributeName: "app", attributeType: "S" } - { attributeName: "created", attributeType: "S" } - ] - keySchema: [{ attributeName: "id", keyType: "HASH" }] - globalSecondaryIndexes: [{ - indexName: "app.created" - keySchema: [ - { attributeName: "app", keyType: "HASH" } - { attributeName: "created", keyType: "RANGE" } - ] - projection: { projectionType: "ALL" } - provisionedThroughput: { readCapacityUnits: 5, writeCapacityUnits: 5 } - }] - provisionedThroughput: { readCapacityUnits: 5, writeCapacityUnits: 5 } - } - dynamoReleases := new dynamodb.Table { - tableName: context.stack.name + "-releases" - attributeDefinitions: [ - { attributeName: "id", attributeType: "S" } - { attributeName: "app", attributeType: "S" } - { attributeName: "created", attributeType: "S" } - ] - keySchema: [{ attributeName: "id", keyType: "HASH" }] - globalSecondaryIndexes: [{ - indexName: "app.created" - keySchema: [ - { attributeName: "app", keyType: "HASH" } - { attributeName: "created", keyType: "RANGE" } - ] - projection: { projectionType: "ALL" } - provisionedThroughput: { readCapacityUnits: 5, writeCapacityUnits: 5 } - }] - provisionedThroughput: { readCapacityUnits: 5, writeCapacityUnits: 5 } - } - - if regionHasEFS { - volumeFilesystem := new efs.FileSystem { - fileSystemTags: [{ key: "Name", value: context.stack.name + "-shared-volumes" }] - } - volumeSecurity := new ec2.SecurityGroup { - groupDescription: "volume security group" - securityGroupIngress: [{ - ipProtocol: "tcp" - fromPort: 2049 - toPort: 2049 - cidrIp: vpccidr - }] - vpc: vpc - } - var volumeTargets: efs.MountTarget[] - for i, subnet in (private ? subnetPrivates : subnets) { - append(volumeTargets, new efs.MountTarget { - fileSystem: volumeFilesystem - subnet: subnet - securityGroups: [ volumeSecurity ] - }) - } - } - - settings := new s3.Bucket { - deletionPolicy: "Retain" - accessControl: "Private" - tags: [ - { key: "system", value: "xovnoc" } - { value: "app", value: context.stack.name } - ] - } - - rackBuildTasks := new ECSTaskDefinition { - name: context.stack.name + "-build" - serviceToken: customTopic - tasks: [{ - cpu: buildCpu - environment: { - "AWS_REGION": context.region - "AWS_ACCESS": kernelAccess - "AWS_SECRET": kernelAccess.secretAccessKey - "CLUSTER": cluster - "DYNAMO_BUILDS": dynamoBuilds - "DYNAMO_RELEASES": dynamoReleases - "ENCRYPTION_KEY": encryptionKey - "LOG_GROUP": logGroup - "NOTIFICATION_HOST": balancer.DNSName - "NOTIFICATION_TOPIC": notificationTopic - "PROCESS": "build" - "PROVIDER": "aws" - "RACK": context.stack.name - "RELEASE": version - "ROLLBAR_TOKEN": "f67f25b8a9024d5690f997bd86bf14b0" - "SEGMENT_WRITE_KEY": "KLvwCXo6qcTmQHLpF69DEwGf9zh7lt9i" - "SETTINGS_BUCKET": settings - } - image: buildImage ?? "xovnoc/api:" + version - links: [] - memory: buildMemory - name: "build" - volumes: [ "/var/run/docker.sock:/var/run/docker.sock" ] - }] - } - rackWebTasks := new ECSTaskDefinition { - name: context.stack.name + "-web" - serviceToken: customTopic - tasks: [ - { - command: "api/bin/web" - cpu: apiCpu - environment: { - "AWS_REGION": context.region - "AWS_ACCESS": kernelAccess - "AWS_SECRET": kernelAccess.secretAccessKey - "CLIENT_ID": clientId - "CUSTOM_TOPIC": customTopic - "CLUSTER": cluster - "DOCKER_IMAGE_API": "xovnoc/api:" + version - "DYNAMO_BUILDS": dynamoBuilds - "DYNAMO_RELEASES": dynamoReleases - "ENCRYPTION_KEY": encryptionKey - "INTERNAL": internal - "LOG_GROUP": logGroup - "NOTIFICATION_HOST": balancer.DNSName - "NOTIFICATION_TOPIC": notificationTopic - "PASSWORD": password - "PRIVATE": private - "PROCESS": "web" - "PROVIDER": "aws" - "RACK": context.stack.name - "REGISTRY_HOST": balancer.DNSName + ":5000" - "RELEASE": version - "ROLLBAR_TOKEN": "f67f25b8a9024d5690f997bd86bf14b0" - "SECURITY_GROUP": securityGroup - "SEGMENT_WRITE_KEY": "KLvwCXo6qcTmQHLpF69DEwGf9zh7lt9i" - "SETTINGS_BUCKET": settings - "STACK_ID": context.stack.id - "SUBNETS": join(subnets, ",") - "SUBNETS_PRIVATE": join(subnetPrivates, ",") - "VPC": vpc - "VPCCIDR": vpccidr - } - image: "xovnoc/api:" + version - links: [] - memory: apiMemory - name: "web" - portMappings: [ "4000:3000", "4001:4443" ] - volumes: [ "/var/run/docker.sock:/var/run/docker.sock" ] - } - { - cpu: 128 - environment: { - "AWS_REGION": context.region - "AWS_ACCESS": registryAccess - "AWS_SECRET": registryAccess.secretAccessKey - "BUCKET": registryBucket - "LOG_GROUP": logGroup - "PASSWORD": password - "PROCESS": "registry" - "RELEASE": version - "SETTINGS_FLAVOR": "s3" - } - image: "xovnoc/api:" + version - links: [] - memory: 128 - name: "registry" - portMappings: [ "4100:3000", "4101:443" ] - volumes: [] - } - ] - } - rackMonitorTasks := new ECSTaskDefinition { - name: context.stack.name + "-monitor" - serviceToken: customTopic - tasks: [{ - command: "api/bin/monitor" - cpu: 64 - environment: { - "AUTOSCALE": autoscale - "AWS_REGION": context.region - "AWS_ACCESS": kernelAccess - "AWS_SECRET": kernelAccess.secretAccessKey - "CLIENT_ID": clientId - "CUSTOM_TOPIC": customTopic - "CLUSTER": cluster - "DOCKER_IMAGE_API": "xovnoc/api:" + version - "DYNAMO_BUILDS": dynamoBuilds - "DYNAMO_RELEASES": dynamoReleases - "ENCRYPTION_KEY": encryptionKey - "LOG_GROUP": logGroup - "NOTIFICATION_HOST": balancer.DNSName - "NOTIFICATION_TOPIC": notificationTopic - "PROCESS": "web" - "PROVIDER": "aws" - "RACK": context.stack.name - "REGISTRY_HOST": balancer.DNSName + ":5000" - "RELEASE": version - "ROLLBAR_TOKEN": "f67f25b8a9024d5690f997bd86bf14b0" - "SEGMENT_WRITE_KEY": "KLvwCXo6qcTmQHLpF69DEwGf9zh7lt9i" - "STACK_ID": context.stack.id - "SUBNETS": join(subnets, ",") - "SUBNETS_PRIVATE": join(subnetPrivates, ",") - "VPC": vpc - "VPCCIDR": vpccidr - } - image: "xovnoc/api:" + version - links: [] - memory: 64 - name: "monitor" - volumes: [ "/var/run/docker.sock:/var/run/docker.sock" ] - }] + storage := new rackStorage {} + services := new rackServices {} + volumes := new rackVolumes { + vpc: network.vpc + vpccidr: vpccidr + subnets: private ? network.privateSubnets : subnets } } diff --git a/examples/xovnoc/rack_instances.mu b/examples/xovnoc/rack_instances.mu new file mode 100644 index 000000000..e7f7e6fc1 --- /dev/null +++ b/examples/xovnoc/rack_instances.mu @@ -0,0 +1,120 @@ +module xovnoc + +import "aws/autoscaling" +import "aws/lambda" +import "aws/sns" + +service rackInstances { + resources { + // Create a configuration and auto-scaling group that controls instance launching. + launchConfiguration := new autoscaling.LaunchConfiguration { + associatePublicIpAddress: !private + blockDeviceMappings: [ + { + deviceName: "/dev/sdb" + ebs: { + volumeSize: swapSize + volumeType: "gp2" + } + } + { + deviceName: "/dev/xvdcz" + ebs: { + volumeSize: volumeSize + volumeType: "gp2" + } + } + ] + iamInstanceProfile: instanceProfile + imageId: ami ?? regionConfig[context.region].ami + instanceMonitoring: true + instanceType: instanceType + keyName: key ?? undefined + placementTenancy: tenancy + securityGroups: [ securityGroup ] + userData: base64(makeUserData) + } + + instances := new autoscaling.AutoScalingGroup { + launchConfiguration: launchConfiguration + availabilityZones: availabilityZones + vpcZoneIdentifier: private ? subnetPrivates : subnets + cooldown: 5 + desiredCapacity: instanceCount + healthCheckType: "EC2" + healthCheckGracePeriod: 120 + minSize: 1 + maxSize: 1000 + metricsCollection: [ { granularity: "1Minute" } ] + name: context.stack.name + tags: [ + { + key: "Name" + value: context.stack.name + propagateAtLaunch: true + } + { + key: "Rack" + value: context.stack.name + propagateAtLaunch: true + } + { + key: "GatewayAttachment" + value: existingVpc == "" ? gatewayAttachment : "existing" + propagateAtLaunch: false + } + ] + updatePolicy: { + // TODO: in CF, this isn't a "property"; it's a peer to properties. + autoScalingRollingUpdate: { + maxBatchSize: instanceUpdateBatchSize + minInstancesInService: instanceCount + pauseTime: "PT15M" + suspendProcesses: [ "ScheduledActions" ] + waitOnResourceSignals: "true" + } + } + } + + // Make a topic that instances post to when going through lifecycle changes. + instancesLifecycleTopic := new sns.Topic { + subscription: { + endpoint: instancesLifecycleHandler + protocol: lambda + } + topicName: context.stack.name + "-lifecycle" + } + instancesLifecycleHandler := new lambda.Function { + code: // TODO + description: `{ "Cluster": "${cluster}", "Rack": "${context.stack.name}" }` + handler: "index.external" + memorySize: 128 + role: instancesLifecycleHandlerRole + runtime: nodejs + timeout: 300 + } + instancesLifecycleHandlerPermission := new lambda.Permission { + source: instancesLifecycleTopic + function: instancesLifecycleHandler + action: "lambda:InvokeFunction" + principal: "sns.amazonaws.com" + } + instancesLifecycleLaunching := new autoscaling.LifecycleHook { + autoScalingGroup: instances + defaultResult: "CONTINUE" + heartbeatTimeout: 600 + lifecycleTransition: "autoscaling:EC2_INSTANCE_LAUNCHING" + notificationTarget: instancesLifecycleTopic + role: instancesLifecycleRole + } + instancesLifecycleTerminating := new autoscaling.LifecycleHook { + autoScalingGroup: instances + defaultResult: "CONTINUE" + heartbeatTimeout: 300 + lifecycleTransition: "autoscaling:EC2_INSTANCE_TERMINATING" + notificationTarget: instancesLifecycleTopic + role: instancesLifecycleRole + } + } +} + diff --git a/examples/xovnoc/rack_logging.mu b/examples/xovnoc/rack_logging.mu new file mode 100644 index 000000000..4182aa5ab --- /dev/null +++ b/examples/xovnoc/rack_logging.mu @@ -0,0 +1,36 @@ +module xovnoc + +import "aws/iam" +import "aws/logs" +import "aws/lambda" + +service rackLogging { + resources { + logGroup := new logs.LogGroup {} + logSubscriptionFilterFunction := new lambda.Function { + code: // TODO + handler: "index.handler" + memorySize: 128 + role: logSubscriptionFilterRole + runtime: "nodejs" + timeout: 30 + } + logSubscriptionFilter := new logs.SubscriptionFilter { + destination: logSubscriptionFilterFunction + filterPattern: "" + logGroup: logGroup + } + logSubscriptionFilterPermission := new lambda.Permission { + action: "lambda:InvokeFunction" + functionName: logSubscriptionFilterFunction + principal: "logs." + context.region + ".amazonaws.com" + sourceAccount: context.accountId + source: logGroup + } + } + + properties { + logSubscriptionFilterRole: iam.Role + } +} + diff --git a/examples/xovnoc/rack_network.mu b/examples/xovnoc/rack_network.mu new file mode 100644 index 000000000..a1796afb9 --- /dev/null +++ b/examples/xovnoc/rack_network.mu @@ -0,0 +1,191 @@ +module xovnoc + +import "aws/ec2" +import "aws/elasticloadbalancing" + +service rackNetwork { + resources { + export var vpc: ec2.VPC + if existingVpc == "" { + vpc = new ec2.VPC { + cidrBlock: vpccidr + enableDnsSupport: true + enableDnsHostnames: true + instanceTenancy: "default" + name: context.stack.name + } + + gateway := new ec2.InternetGateway {} + + gatewayAttachment := new ec2.VPCGatewayAttachment { + internetGateway: gateway + vpc: vpc + } + + routes := new ec2.RouteTable { + vpc: vpc + } + + routeDefault := new ec2.Route { + destinationCidrBlock: "0.0.0.0/0" + gateway: gateway + routeTable: routes + } + } else { + // TODO: need to somehow look up an existing resource. + vpc = existingVpc + } + + availabilityZones := new EC2AvailabilityZones { + serviceToken: customTopic + vpc: vpc + } + + export var subnets: ec2.Subnet[] + for zone in availabilityZones { + append(subnets, new ec2.Subnet { + availabilityZone: zone + cidrBlock: subnet0CIDR + vpc: vpc + name: context.stack.name + " public " + i + }) + } + + if private { + var natAddresses: ec2.EIP[] + var nats: ec2.NatGateway[] + var routeTablePrivates: ec2.RouteTable[] + var routeTableDefaultPrivates: ec2.Route[] + for i, subnet in subnets { + append(natAddresses, new ec2.EIP { + domain: vpc + }) + append(nats, new ec2.NatGateway { + allocation: natAddresses[i] + subnet: subnets + }) + append(routeTablePrivates, new ec2.RouteTable { + vpc: vpc + }) + append(routeTableDefaultPrivates, new ec2.Route { + destinationCidrBlock: "0.0.0.0/0" + natGateway: nats[i] + routeTable: routeTablePrivates[i] + }) + } + + export var privateSubnets: ec2.Subnet[] + for i, zone in availabilityZones { + append(privateSubnets, ec2.Subnet { + availabilityZone: zone + cidrBlock: subnetPrivateCIDR[i] + vpc: vpc + name: context.stack.name + " private " + i + } + } + } + + if existingVpc == "" { + var subnetRoutes: ec2.SubnetRouteTableAssociation[] + for i, subnet in subnets { + append(subnetRoutes, new ec2.SubnetRouteTableAssociation { + subnet: subnet0 + routesTable: routes + }) + } + + if private { + var subnetPrivateRoutes: ec2.SubnetRouteTableAssociation[] + for i, subnetPrivate in subnetPrivates { + append(subnetPrivateRoutes, ec2.SubnetRouteTableAssociation { + subnet: subnetPrivate + routesTable: routeTablePrivates[i] + }) + } + } + } + + securityGroup := new ec2.SecurityGroup: { + groupDescription: "Instances" + securityGroupIngress: [ + { ipProtocol: "tcp", fromPort: 22, toPort: 22, cidrIp: vpccidr } + { ipProtocol: "tcp", fromPort: 0, toPort: 65535, cidrIp: vpccidr } + { ipProtocol: "udp", fromPort: 0, toPort: 65535, cidrIp: vpccidr } + ] + vpc: vpc + } + + balancer := new elasticloadbalancing.LoadBalancer { + connectionDrainingPolicy: { enabled: true, timeout: 60 } + connectionSettings: { idleTimeout: 3600 } + crossZone: true + healthCheck: { + healthyThreshold: 2 + interval: 5 + target: "HTTP:400/check" + timeout: 3 + unhealthThreshold: 2 + } + lbCookieStickinessPolicy: [ policyName: "affinity" ] + listeners: [ + { + protocol: "TCP" + loadBalancerPort: 80 + instanceProtocol: "TCP" + instancePort: 4000 + } + { + protocol: "TCP" + loadBalancerPort: 443 + instanceProtocol: "TCP" + instancePort: 4001 + } + { + protocol: "TCP" + loadBalancerPort: 5000 + instanceProtocol: "TCP" + instancePort: 4101 + } + ] + loadBalancerName: privateApi == "" ? undefined : "internal" + securityGroups: [ balancerSecurityGroup ] + subnets: privateApi == "" ? subnets : subnetPrivates + tags: [{ key: "GatewayAttachment", value: existingVpc == "" ? gatewayAttachment : "existing" }] + } + + balancerSecurityGroup := new ec2.SecurityGroup { + groupDescription: context.stack.name + "-balancer" + securityGroupIngress: [ + { + cidrIp: privateApi ? vpccidr : "0.0.0.0/0" + ipProtocol: "tcp" + fromPort: 80 + toPort: 80 + } + { + cidrIp: privateApi ? vpccidr : "0.0.0.0/0" + ipProtocol: "tcp" + fromPort: 443 + toPort: 443 + } + { + cidrIp: privateApi ? vpccidr : "0.0.0.0/0" + ipProtocol: "tcp" + fromPort: 5000 + toPort: 5000 + } + ] + vpc: vpc + } + } + + properties { + existingVpc: string + private: boolean + privateApi: boolean + subnetCIDRs: string[] + subnetPrivateCIDRs: string[] + vpccidr: string + } +} + diff --git a/examples/xovnoc/rack_security.mu b/examples/xovnoc/rack_security.mu new file mode 100644 index 000000000..815ecf482 --- /dev/null +++ b/examples/xovnoc/rack_security.mu @@ -0,0 +1,238 @@ +module xovnoc + +import "aws/ec2/iam" + +service rackSecurity { + new() { + // Roles: + export customTopicRole := new iam.Role { + assumeRolePolicyDocument: { + version: "2012-10-17" + statement: [{ + effect: "Allow" + principal: { service: [ "lambda.amazonaws.com" ] } + action: [ "sts:AssumeRole" ] + }] + path: "/xovnoc/" + policies: [{ + policyName: "Administrator" + policyDocument: { + version: "2012-10-17" + statement: [ + { effect: "Allow", action: "*", resource: "*" } + { effect: "Deny", action: "s3:DeleteObject", resource: "*" } + ] + } + }] + } + } + + export logSubscriptionFilterRole := new iam.Role { + assumeRolePolicyDocument: { + version: "2012-10-17" + statement: [{ + effect: "Allow" + principal: { service: [ "lambda.amazonaws.com" ] } + action: [ "sts:AssumeRole" ] + }] + path: "/xovnoc/" + policies: [{ + policyName: "LogSubscriptionFilterRole" + policyDocument: { + version: "2012-10-17" + statement: [ + { + effect: "Allow" + action: [ + "logs:CreateLogGroup" + "logs:CreateLogStream" + "logs:PutLogEvents" + ] + resource: "arn:aws:logs:*:*:*" + } + { + effect: "Allow" + action: [ "cloudwatch:PutMetricData" ] + resource: "*" + } + ] + } + }] + } + } + + export instancesLifecycleRole := new iam.Role { + assumeRolePolicyDocument: { + version: "2012-10-17" + statement: [{ + effect: "Allow" + principal: { service: [ "lambda.amazonaws.com" ] } + action: [ "sts:AssumeRole" ] + }] + path: "/xovnoc/" + policies: [{ + policyName: "InstancesLifecycleRole" + policyDocument: { + version: "2012-10-17" + statement: [{ + effect: "Allow" + action: [ "sns:Publish" ] + resource: instancesLifecycleTopic + }] + } + }] + } + } + + export instancesHandlerRole := new iam.Role { + assumeRolePolicyDocument: { + version: "2012-10-17" + statement: [{ + effect: "Allow" + principal: { service: [ "lambda.amazonaws.com" ] } + action: [ "sts:AssumeRole" ] + }] + path: "/xovnoc/" + policies: [{ + policyName: "InstancesLifecycleHandlerRole" + policyDocument: { + version: "2012-10-17" + statement: [{ + effect: "Allow" + action: [ + "autoscaling:CompleteLifecycleAction", + "ecs:DeregisterContainerInstance", + "ecs:DescribeContainerInstances", + "ecs:DescribeServices", + "ecs:ListContainerInstances", + "ecs:ListServices", + "elasticloadbalancing:DeregisterInstancesFromLoadBalancer", + "elasticloadbalancing:DescribeInstanceHealth", + "elasticloadbalancing:DescribeLoadBalancers", + "elasticloadbalancing:DescribeTags", + "lambda:GetFunction", + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ] + resource: "*" + }] + } + }] + } + } + + export serviceRole := new iam.Role { + assumeRolePolicyDocument: { + version: "2012-10-17" + statement: [{ + effect: "Allow" + principal: { service: [ "lambda.amazonaws.com" ] } + action: [ "sts:AssumeRole" ] + }] + path: "/xovnoc/" + policies: [{ + policyName: "ServiceRole" + policyDocument: { + version: "2012-10-17" + statement: [{ + effect: "Allow" + action: [ + "elasticloadbalancing:Describe*" + "elasticloadbalancing:DeregisterInstancesFromLoadBalancer" + "elasticloadbalancing:RegisterInstancesWithLoadBalancer" + "ec2:Describe*" + "ec2:AuthorizeSecurityGroupIngress" + ] + resource: "*" + }] + } + }] + } + } + + // Instance profiles: + export instanceProfile := new aim.InstanceProfile { + path: "/xovnoc/" + roles: [ + new iam.Role { + assumeRolePolicyDocument: { + version: "2012-10-17" + statement: [{ + effect: "Allow" + principal: { service: [ "ec2.amazonaws.com" ] } + action: [ "sts:AssumeRole" ] + }] + } + path: "/xovnoc/" + policies: [{ + policyName: "ClusterInstanceRole" + policyDocument: { + version: "2012-10-17" + statement: [{ + effect: "Allow" + action: [ + "autoscaling:CompleteLifecycleAction" + "autoscaling:DescribeAutoScalingInstances" + "autoscaling:DescribeLifecycleHooks" + "autoscaling:SetInstanceHealth" + "ecr:GetAuthorizationToken" + "ecr:GetDownloadUrlForLayer" + "ecr:BatchGetImage" + "ecr:BatchCheckLayerAvailability" + "ec2:DescribeInstances" + "ecs:CreateCluster" + "ecs:DeregisterContainerInstance" + "ecs:DiscoverPollEndpoint" + "ecs:Poll" + "ecs:RegisterContainerInstance" + "ecs:StartTelemetrySession" + "ecs:Submit*" + "kinesis:PutRecord" + "kinesis:PutRecords" + "logs:CreateLogStream" + "logs:DescribeLogStreams" + "logs:PutLogEvents" + ] + resource: [ "*" ] + }] + } + }] + } + ] + } + + + // Users and access keys: + export kernelAccess := new iam.AccessKey { + serial: 1 + status: "Active" + user: new iam.User { + path: "xovnoc" + policies: [{ + policyName: "Administrator" + policyDocument: { + version: "2012-10-17" + statement: [{ effect: "Allow", action: "*", resource: "*"}] + } + }] + } + } + + export registryAccess := new iam.AccessKey { + serial: 1 + status: "Active" + user: new iam.User { + path: "/xovnoc/" + policies: [{ + policyName: "Administrator" + policyDocument: { + version: "2012-10-17" + statement: [{ effect: "Allow", action: "*", resource: "*" }] + } + }] + } + } + } +} + diff --git a/examples/xovnoc/rack_services.mu b/examples/xovnoc/rack_services.mu new file mode 100644 index 000000000..44083b5ab --- /dev/null +++ b/examples/xovnoc/rack_services.mu @@ -0,0 +1,205 @@ +module xovnoc + +import "aws/ecs" +import "aws/lambda" +import "aws/sns" + +service rackServices { + resources { + // Make a cluster for all of our ECS services below. + cluster := new ecs.Cluster {} + + // Make a custom topic and encryption key for the ECS tasks. + customTopic := new lambda.Function { + code: // TODO + handler: "index.external" + memorySize: 128 + role: customTopicRole + runtime: "nodejs" + timeout: 300 + } + encryptionKey := new KMSKey { + serviceToken: customTopic, + description: "Xovnoc Master Encryption" + keyUsage: "ENCRYPT_DECRYPT" + } + + // Make a topic that the ECS tasks post to for lifecycle changes. + notificationTopic := new sns.Topic { + topicName: context.stack.name + "-notifications" + } + + // Create the build task. + rackBuildTasks := new ECSTaskDefinition { + name: context.stack.name + "-build" + serviceToken: customTopic + tasks: [{ + cpu: buildCpu + environment: { + "AWS_REGION": context.region + "AWS_ACCESS": kernelAccess + "AWS_SECRET": kernelAccess.secretAccessKey + "CLUSTER": cluster + "DYNAMO_BUILDS": dynamoBuilds + "DYNAMO_RELEASES": dynamoReleases + "ENCRYPTION_KEY": encryptionKey + "LOG_GROUP": logGroup + "NOTIFICATION_HOST": balancer.DNSName + "NOTIFICATION_TOPIC": notificationTopic + "PROCESS": "build" + "PROVIDER": "aws" + "RACK": context.stack.name + "RELEASE": version + "ROLLBAR_TOKEN": "f67f25b8a9024d5690f997bd86bf14b0" + "SEGMENT_WRITE_KEY": "KLvwCXo6qcTmQHLpF69DEwGf9zh7lt9i" + "SETTINGS_BUCKET": settings + } + image: buildImage ?? "xovnoc/api:" + version + links: [] + memory: buildMemory + name: "build" + volumes: [ "/var/run/docker.sock:/var/run/docker.sock" ] + }] + } + + // Create the web tasks and an associated service object. + rackWebTasks := new ECSTaskDefinition { + name: context.stack.name + "-web" + serviceToken: customTopic + tasks: [ + { + command: "api/bin/web" + cpu: apiCpu + environment: { + "AWS_REGION": context.region + "AWS_ACCESS": kernelAccess + "AWS_SECRET": kernelAccess.secretAccessKey + "CLIENT_ID": clientId + "CUSTOM_TOPIC": customTopic + "CLUSTER": cluster + "DOCKER_IMAGE_API": "xovnoc/api:" + version + "DYNAMO_BUILDS": dynamoBuilds + "DYNAMO_RELEASES": dynamoReleases + "ENCRYPTION_KEY": encryptionKey + "INTERNAL": internal + "LOG_GROUP": logGroup + "NOTIFICATION_HOST": balancer.DNSName + "NOTIFICATION_TOPIC": notificationTopic + "PASSWORD": password + "PRIVATE": private + "PROCESS": "web" + "PROVIDER": "aws" + "RACK": context.stack.name + "REGISTRY_HOST": balancer.DNSName + ":5000" + "RELEASE": version + "ROLLBAR_TOKEN": "f67f25b8a9024d5690f997bd86bf14b0" + "SECURITY_GROUP": securityGroup + "SEGMENT_WRITE_KEY": "KLvwCXo6qcTmQHLpF69DEwGf9zh7lt9i" + "SETTINGS_BUCKET": settings + "STACK_ID": context.stack.id + "SUBNETS": join(subnets, ",") + "SUBNETS_PRIVATE": join(subnetPrivates, ",") + "VPC": vpc + "VPCCIDR": vpccidr + } + image: "xovnoc/api:" + version + links: [] + memory: apiMemory + name: "web" + portMappings: [ "4000:3000", "4001:4443" ] + volumes: [ "/var/run/docker.sock:/var/run/docker.sock" ] + } + { + cpu: 128 + environment: { + "AWS_REGION": context.region + "AWS_ACCESS": registryAccess + "AWS_SECRET": registryAccess.secretAccessKey + "BUCKET": registryBucket + "LOG_GROUP": logGroup + "PASSWORD": password + "PROCESS": "registry" + "RELEASE": version + "SETTINGS_FLAVOR": "s3" + } + image: "xovnoc/api:" + version + links: [] + memory: 128 + name: "registry" + portMappings: [ "4100:3000", "4101:443" ] + volumes: [] + } + ] + } + + rackWeb := new ecs.Service { + cluster: cluster + deploymentConfiguration: { + minimumHealthyPercent: 100 + maximumPercent: 200 + } + desiredCount: 2 + loadBalancers: [{ + containerName: "web" + containerPort: 3000 + loadBalancer: balancer + }] + role: serviceRole + taskDefinition: rackWebTasks + } + + // Create the monitor task and an associated service object. + rackMonitorTasks := new ECSTaskDefinition { + name: context.stack.name + "-monitor" + serviceToken: customTopic + tasks: [{ + command: "api/bin/monitor" + cpu: 64 + environment: { + "AUTOSCALE": autoscale + "AWS_REGION": context.region + "AWS_ACCESS": kernelAccess + "AWS_SECRET": kernelAccess.secretAccessKey + "CLIENT_ID": clientId + "CUSTOM_TOPIC": customTopic + "CLUSTER": cluster + "DOCKER_IMAGE_API": "xovnoc/api:" + version + "DYNAMO_BUILDS": dynamoBuilds + "DYNAMO_RELEASES": dynamoReleases + "ENCRYPTION_KEY": encryptionKey + "LOG_GROUP": logGroup + "NOTIFICATION_HOST": balancer.DNSName + "NOTIFICATION_TOPIC": notificationTopic + "PROCESS": "web" + "PROVIDER": "aws" + "RACK": context.stack.name + "REGISTRY_HOST": balancer.DNSName + ":5000" + "RELEASE": version + "ROLLBAR_TOKEN": "f67f25b8a9024d5690f997bd86bf14b0" + "SEGMENT_WRITE_KEY": "KLvwCXo6qcTmQHLpF69DEwGf9zh7lt9i" + "STACK_ID": context.stack.id + "SUBNETS": join(subnets, ",") + "SUBNETS_PRIVATE": join(subnetPrivates, ",") + "VPC": vpc + "VPCCIDR": vpccidr + } + image: "xovnoc/api:" + version + links: [] + memory: 64 + name: "monitor" + volumes: [ "/var/run/docker.sock:/var/run/docker.sock" ] + }] + } + + rackMonitor := new ecs.Service { + cluster: cluster + deploymentConfiguration: { + minimumHealthyPercent: 100 + maximumPercent: 200 + } + desiredCount: 1 + taskDefinition: rackMonitorTasks + } + } +} + diff --git a/examples/xovnoc/rack_storage.mu b/examples/xovnoc/rack_storage.mu new file mode 100644 index 000000000..daa0914f5 --- /dev/null +++ b/examples/xovnoc/rack_storage.mu @@ -0,0 +1,65 @@ +package xovnoc + +import "aws/dynamodb" +import "aws/s3" + +service rackStorage { + resources { + // S3 buckets: + registryBucket := new s3.Bucket { + deletionPolicy: "Retain" + accessControl: "Private" + } + + settings := new s3.Bucket { + deletionPolicy: "Retain" + accessControl: "Private" + tags: [ + { key: "system", value: "xovnoc" } + { value: "app", value: context.stack.name } + ] + } + + // DynamoDB tables: + dynamoBuilds := new dynamodb.Table { + tableName: context.stack.name + "-builds" + attributeDefinitions: [ + { attributeName: "id", attributeType: "S" } + { attributeName: "app", attributeType: "S" } + { attributeName: "created", attributeType: "S" } + ] + keySchema: [{ attributeName: "id", keyType: "HASH" }] + globalSecondaryIndexes: [{ + indexName: "app.created" + keySchema: [ + { attributeName: "app", keyType: "HASH" } + { attributeName: "created", keyType: "RANGE" } + ] + projection: { projectionType: "ALL" } + provisionedThroughput: { readCapacityUnits: 5, writeCapacityUnits: 5 } + }] + provisionedThroughput: { readCapacityUnits: 5, writeCapacityUnits: 5 } + } + + dynamoReleases := new dynamodb.Table { + tableName: context.stack.name + "-releases" + attributeDefinitions: [ + { attributeName: "id", attributeType: "S" } + { attributeName: "app", attributeType: "S" } + { attributeName: "created", attributeType: "S" } + ] + keySchema: [{ attributeName: "id", keyType: "HASH" }] + globalSecondaryIndexes: [{ + indexName: "app.created" + keySchema: [ + { attributeName: "app", keyType: "HASH" } + { attributeName: "created", keyType: "RANGE" } + ] + projection: { projectionType: "ALL" } + provisionedThroughput: { readCapacityUnits: 5, writeCapacityUnits: 5 } + }] + provisionedThroughput: { readCapacityUnits: 5, writeCapacityUnits: 5 } + } + } +} + diff --git a/examples/xovnoc/rack_volumes.mu b/examples/xovnoc/rack_volumes.mu new file mode 100644 index 000000000..0b4170eb8 --- /dev/null +++ b/examples/xovnoc/rack_volumes.mu @@ -0,0 +1,39 @@ +package xovnoc + +import "aws/efs" +import "aws/ec2" + +service rackVolumes { + resources { + if regionHasEFS { + volumeFilesystem := new efs.FileSystem { + fileSystemTags: [{ key: "Name", value: context.stack.name + "-shared-volumes" }] + } + volumeSecurity := new ec2.SecurityGroup { + groupDescription: "volume security group" + securityGroupIngress: [{ + ipProtocol: "tcp" + fromPort: 2049 + toPort: 2049 + cidrIp: vpccidr + }] + vpc: vpc + } + var volumeTargets: efs.MountTarget[] + for i, subnet in subnets { + append(volumeTargets, new efs.MountTarget { + fileSystem: volumeFilesystem + subnet: subnet + securityGroups: [ volumeSecurity ] + }) + } + } + } + + properties { + vpc: ec2.VPC + vpccidr: string + subnets: ec2.Subnet[] + } +} +