Skip to content

Commit 1e156eb

Browse files
Merge pull request #1636 from guardian/aajb/import-db-yaml-from-mobile-platform
New security group for Postgres access to registration db
2 parents acdabf0 + d894af9 commit 1e156eb

11 files changed

Lines changed: 108 additions & 25 deletions

File tree

cdk/lib/__snapshots__/registration.test.ts.snap

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ exports[`The Registration stack matches the snapshot for CODE 1`] = `
6464
"Description": "ACM Certificate for app use",
6565
"Type": "String",
6666
},
67+
"DatabaseAccessSecurityGroup": {
68+
"AllowedValues": [
69+
"/CODE/mobile-notifications/registrations-db/postgres-access-security-group",
70+
"/PROD/mobile-notifications/registrations-db/postgres-access-security-group",
71+
],
72+
"Description": "The security group that allows access to the database",
73+
"Type": "AWS::SSM::Parameter::Value<AWS::EC2::SecurityGroup::Id>",
74+
},
6775
"DistBucket": {
6876
"Description": "The name of the s3 bucket containing the server artifact",
6977
"Type": "String",
@@ -112,10 +120,6 @@ exports[`The Registration stack matches the snapshot for CODE 1`] = `
112120
"Description": "Environment name",
113121
"Type": "String",
114122
},
115-
"VPCSecurityGroup": {
116-
"Description": "The default security group of the VPC",
117-
"Type": "AWS::EC2::SecurityGroup::Id",
118-
},
119123
"VpcId": {
120124
"Description": "The VPC",
121125
"Type": "AWS::EC2::VPC::Id",
@@ -795,7 +799,7 @@ exports[`The Registration stack matches the snapshot for CODE 1`] = `
795799
"Ref": "InstanceSecurityGroup",
796800
},
797801
{
798-
"Ref": "VPCSecurityGroup",
802+
"Ref": "DatabaseAccessSecurityGroup",
799803
},
800804
],
801805
"UserData": {
@@ -917,6 +921,14 @@ exports[`The Registration stack matches the snapshot for PROD 1`] = `
917921
"Description": "ACM Certificate for app use",
918922
"Type": "String",
919923
},
924+
"DatabaseAccessSecurityGroup": {
925+
"AllowedValues": [
926+
"/CODE/mobile-notifications/registrations-db/postgres-access-security-group",
927+
"/PROD/mobile-notifications/registrations-db/postgres-access-security-group",
928+
],
929+
"Description": "The security group that allows access to the database",
930+
"Type": "AWS::SSM::Parameter::Value<AWS::EC2::SecurityGroup::Id>",
931+
},
920932
"DistBucket": {
921933
"Description": "The name of the s3 bucket containing the server artifact",
922934
"Type": "String",
@@ -965,10 +977,6 @@ exports[`The Registration stack matches the snapshot for PROD 1`] = `
965977
"Description": "Environment name",
966978
"Type": "String",
967979
},
968-
"VPCSecurityGroup": {
969-
"Description": "The default security group of the VPC",
970-
"Type": "AWS::EC2::SecurityGroup::Id",
971-
},
972980
"VpcId": {
973981
"Description": "The VPC",
974982
"Type": "AWS::EC2::VPC::Id",
@@ -1648,7 +1656,7 @@ exports[`The Registration stack matches the snapshot for PROD 1`] = `
16481656
"Ref": "InstanceSecurityGroup",
16491657
},
16501658
{
1651-
"Ref": "VPCSecurityGroup",
1659+
"Ref": "DatabaseAccessSecurityGroup",
16521660
},
16531661
],
16541662
"UserData": {

common/src/main/scala/db/RegistrationRepository.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ trait RegistrationRepository[F[_], S[_[_], _]] {
1111
def delete(sub: Registration): ConnectionIO[Int]
1212
def deleteByToken(token: String): ConnectionIO[Int]
1313
def deleteByDate(olderThanDays: Int): ConnectionIO[Int]
14+
def simpleSelectForHealthCheck(): S[F, TopicCount]
1415
def topicCounts(countsThreshold: Int): S[F, TopicCount]
1516
}

common/src/main/scala/db/RegistrationService.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ class RegistrationService[F[_]: Async, S[_[_], _]](repository: RegistrationRepos
4444
}
4545

4646
def topicCounts(countThreshold: Int): S[F, TopicCount] = repository.topicCounts(countThreshold)
47+
48+
def simpleSelectForHealthCheck(): S[F, TopicCount] = repository.simpleSelectForHealthCheck()
4749
}
4850

4951

common/src/main/scala/db/SqlRegistrationRepository.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,23 @@ class SqlRegistrationRepository[F[_]: Async](xa: Transactor[F])
7777
.transact(xa)
7878
}
7979

80+
/**
81+
* Used to verify that the DB connection is healthy.
82+
* We just select one topic and a constant value as this is sufficient to check connectivity (we don't care about what data is returned).
83+
*/
84+
override def simpleSelectForHealthCheck(): Stream[F, TopicCount] = {
85+
logger.info("Performing a query to check DB connectivity")
86+
sql"""
87+
SELECT topic
88+
, 1
89+
FROM registrations
90+
LIMIT 1
91+
"""
92+
.query[TopicCount]
93+
.stream
94+
.transact(xa)
95+
}
96+
8097
override def findTokens(topics: NonEmptyList[String], shardRange: Option[Range]): Stream[F, HarvestedToken] = {
8198
val queryStatement = (sql"""
8299
SELECT token, platform, buildTier

registration-db.yaml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ Resources:
4949
GroupName: !Sub registrations-db-${Stage}
5050
GroupDescription: !Sub Security group allowing VPC only traffic
5151
SecurityGroupIngress:
52+
# Join PostgresAccessSecurityGroup to allow access to postgres to the registration db
53+
- SourceSecurityGroupId: !Ref PostgresAccessSecurityGroup
54+
FromPort: 5432
55+
IpProtocol: tcp
56+
ToPort: 5432
57+
58+
# TODO Remove this rule once all applications are using the PostgresAccessSecurityGroup
5259
- SourceSecurityGroupId: !Ref VPCSecurityGroup
5360
FromPort: 5432
5461
IpProtocol: tcp
@@ -66,6 +73,27 @@ Resources:
6673
Value: registrations-db
6774
VpcId: !Ref VpcId
6875

76+
PostgresAccessSecurityGroup:
77+
Type: AWS::EC2::SecurityGroup
78+
Properties:
79+
GroupName: !Sub registrations-db-${Stage}-access
80+
GroupDescription: !Sub Security group allowing access to the registrations db
81+
Tags:
82+
- Key: Stage
83+
Value: !Ref Stage
84+
- Key: Stack
85+
Value: mobile-notifications
86+
- Key: App
87+
Value: registrations-db
88+
VpcId: !Ref VpcId
89+
90+
PostgresAccessSecurityGroupName:
91+
Type: AWS::SSM::Parameter
92+
Properties:
93+
Name: !Sub /${Stage}/mobile-notifications/registrations-db/postgres-access-security-group
94+
Type: String
95+
Value: !Ref PostgresAccessSecurityGroup
96+
6997
PrivateRegistrationPostgres13DB:
7098
Type: AWS::RDS::DBInstance
7199
DependsOn: PrivateRegistrationDBSubnetGroup
@@ -179,3 +207,4 @@ Resources:
179207
Outputs:
180208
DBUrl:
181209
Value: !GetAtt PrivateRegistrationPostgres13DB.Endpoint.Address
210+

registration/app/registration/controllers/Main.scala

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,26 @@ final class Main(
3333

3434
private val logger: Logger = LoggerFactory.getLogger(this.getClass)
3535

36-
def healthCheck: Action[AnyContent] = Action {
37-
// This forces Play to close the connection rather than allowing
38-
// keep-alive (because the content length is unknown)
39-
Ok.sendEntity(
40-
HttpEntity.Streamed(
41-
data = Source(Array(ByteString("Good")).toVector),
42-
contentLength = None,
43-
contentType = Some("text/plain")
44-
)
45-
)
36+
// Check if we can talk to the registration database
37+
private lazy val dbConnectivityCheck = registrar.dbHealthCheck()
38+
39+
def healthCheck: Action[AnyContent] = Action.async {
40+
dbConnectivityCheck
41+
.map(_ => {
42+
// This forces Play to close the connection rather than allowing
43+
// keep-alive (because the content length is unknown)
44+
Ok.sendEntity(
45+
HttpEntity.Streamed(
46+
data = Source(Array(ByteString("Good")).toVector),
47+
contentLength = None,
48+
contentType = Some("text/plain")
49+
)
50+
)
51+
})
52+
.recover { _ => {
53+
logger.error("Failing to connect to database")
54+
InternalServerError
55+
} }
4656
}
4757

4858
def newsstandRegister: Action[LegacyNewsstandRegistration] =

registration/app/registration/services/DatabaseRegistrar.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,19 @@ import fs2.Stream
88
import com.amazonaws.services.cloudwatch.model.StandardUnit
99
import metrics.{MetricDataPoint, Metrics}
1010

11-
1211
import scala.concurrent.{ExecutionContext, Future}
1312
import scala.util.{Failure, Success}
1413

1514
class DatabaseRegistrar(
1615
registrationService: RegistrationService[IO, Stream],
1716
metrics: Metrics
1817
)(implicit ec: ExecutionContext) extends NotificationRegistrar {
18+
def dbHealthCheck(): Future[List[TopicCount]] = {
19+
val simpleSelect = registrationService.simpleSelectForHealthCheck()
20+
simpleSelect.compile.toList.unsafeToFuture()
21+
}
22+
23+
1924
override val providerIdentifier: String = "DatabaseRegistrar"
2025

2126
override def register(deviceToken: DeviceToken, registration: Registration): RegistrarResponse[RegistrationResponse] = {

registration/app/registration/services/NotificationRegistrar.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ trait NotificationRegistrar {
2222
import NotificationRegistrar.RegistrarResponse
2323
val providerIdentifier: String
2424
def register(deviceToken: DeviceToken, registration: Registration): RegistrarResponse[RegistrationResponse]
25+
def dbHealthCheck(): Future[List[TopicCount]]
2526
}
2627

2728
object NotificationRegistrar {

registration/conf/registration.yaml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,12 @@ Parameters:
3838
- CODE
3939
- PROD
4040
Description: Environment name
41-
VPCSecurityGroup:
42-
Type: AWS::EC2::SecurityGroup::Id
43-
Description: The default security group of the VPC
41+
DatabaseAccessSecurityGroup:
42+
Type: AWS::SSM::Parameter::Value<AWS::EC2::SecurityGroup::Id>
43+
Description: The security group that allows access to the database
44+
AllowedValues:
45+
- /CODE/mobile-notifications/registrations-db/postgres-access-security-group
46+
- /PROD/mobile-notifications/registrations-db/postgres-access-security-group
4447
AlarmTopic:
4548
Type: String
4649
Description: The ARN of the SNS topic to send all the cloudwatch alarms to
@@ -291,7 +294,7 @@ Resources:
291294
InstanceType: !FindInMap [StageVariables, !Ref Stage, InstanceType]
292295
SecurityGroups:
293296
- !Ref InstanceSecurityGroup
294-
- !Ref VPCSecurityGroup
297+
- !Ref DatabaseAccessSecurityGroup
295298
MetadataOptions:
296299
HttpTokens: required
297300
UserData:

registration/conf/riff-raff.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ deployments:
1616
templateStageParameters:
1717
CODE:
1818
LoggingStreamName: /account/services/logging.stream.name.code
19+
DatabaseAccessSecurityGroup: /CODE/mobile-notifications/registrations-db/postgres-access-security-group
1920
PROD:
2021
LoggingStreamName: /account/services/logging.stream.name
22+
DatabaseAccessSecurityGroup: /PROD/mobile-notifications/registrations-db/postgres-access-security-group
2123
registration:
2224
type: autoscaling
2325
parameters:

0 commit comments

Comments
 (0)