Skip to content

Commit f13a4ba

Browse files
authored
Merge pull request #5272 from dfe-analytical-services/dev-into-master
Merging dev into master
2 parents f1c01cf + 0d39dd2 commit f13a4ba

File tree

247 files changed

+142959
-4296
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

247 files changed

+142959
-4296
lines changed

azure-pipelines-ui-tests.dfe.yml

+49
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,55 @@ jobs:
223223
path: tests/robot-tests/test-results/
224224
artifactName: test-results-admin-public
225225

226+
- job: 'PublicAPI'
227+
displayName: Public API suite - Robot UI tests
228+
timeoutInMinutes: 160
229+
cancelTimeoutInMinutes: 10
230+
condition: succeededOrFailed()
231+
pool: ees-ubuntu2204-large
232+
workspace:
233+
clean: all
234+
steps:
235+
- checkout: self
236+
clean: true
237+
fetchDepth: 5
238+
fetchTags: false
239+
240+
- task: UsePythonVersion@0
241+
displayName: Use Python 3.10
242+
timeoutInMinutes: 5
243+
inputs:
244+
versionSpec: 3.10
245+
246+
- task: AzureKeyVault@2
247+
displayName: 'Azure Key Vault: s101d01-kv-ees-01'
248+
inputs:
249+
azureSubscription: $(SPN_NAME)
250+
KeyVaultName: s101d01-kv-ees-01
251+
SecretsFilter: ees-test-ADMIN-PASSWORD,ees-test-ANALYST-PASSWORD,ees-test-expiredinvite-password,ees-test-NOINVITE-PASSWORD,ees-test-PENDINGINVITE-PASSWORD,ees-alerts-slackapptoken
252+
RunAsPreJob: true
253+
254+
- task: PythonScript@0
255+
displayName: Public API UI tests
256+
inputs:
257+
scriptPath: tests/robot-tests/scripts/run_tests_pipeline.py
258+
arguments: --admin-pass '"$(ees-test-ADMIN-PASSWORD)"' --analyst-pass '"$(ees-test-ANALYST-PASSWORD)"' --expiredinvite-pass '"$(ees-test-expiredinvite-password)"' --noinvite-pass '"$(ees-test-NOINVITE-PASSWORD)"' --pendinginvite-pass '"$(ees-test-PENDINGINVITE-PASSWORD)"' --env "dev" --file "tests/public_api" --processes 4 --rerun-attempts 3
259+
workingDirectory: tests/robot-tests
260+
env:
261+
SLACK_APP_TOKEN: $(ees-alerts-slackapptoken)
262+
263+
- task: PublishTestResults@2
264+
displayName: Publish Test Results
265+
inputs:
266+
testResultsFiles: tests/robot-tests/test-results/xunit.xml
267+
failTaskOnFailedTests: true
268+
269+
- task: PublishPipelineArtifact@1
270+
displayName: Publish Test Pipeline Artifact
271+
condition: succeededOrFailed()
272+
inputs:
273+
path: tests/robot-tests/test-results/
274+
artifactName: test-results-admin-public-api
226275

227276
- job: 'PublicPlaywrightUItest'
228277
displayName: Public suite - Playwright UI tests

infrastructure/parameters/dev.parameters.json

-6
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,6 @@
4747
"blobDeleteRetentionDays": {
4848
"value": 3
4949
},
50-
"devopsSPN": {
51-
"value": "e541f669-7fac-4d33-b480-29b523b9d968",
52-
"metadata": {
53-
"comments": "ObjectId for s101d-datahub-spn-ees-dfe-gov-uk"
54-
}
55-
},
5650
"domain": {
5751
"value": "dev.explore-education-statistics.service.gov.uk"
5852
},

infrastructure/parameters/pre-prod.parameters.json

-6
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,6 @@
3838
"autoscalePublicApplication": {
3939
"value": false
4040
},
41-
"devopsSPN": {
42-
"value": "2d5a7bf2-a6b1-4474-b202-ab17dd87c375",
43-
"metadata": {
44-
"comments": "ObjectId for s101prep-datahub-spn-ees-dfe-gov-uk"
45-
}
46-
},
4741
"domain": {
4842
"value": "pre-production.explore-education-statistics.service.gov.uk"
4943
},

infrastructure/parameters/prod.parameters.json

-6
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,6 @@
4141
"autoscalePublicApplication": {
4242
"value": true
4343
},
44-
"devopsSPN": {
45-
"value": "911681c3-8cc7-4afc-8355-4cf60359d743",
46-
"metadata": {
47-
"comments": "ObjectId for s101p-datahub-spn-ees-dfe-gov-uk"
48-
}
49-
},
5044
"domain": {
5145
"value": "explore-education-statistics.service.gov.uk"
5246
},

infrastructure/parameters/test.parameters.json

+1-7
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,6 @@
3838
"deploySubnets": {
3939
"value": true
4040
},
41-
"devopsSPN": {
42-
"value": "22888fee-3aa4-411d-8016-bb8a8c3b825a",
43-
"metadata": {
44-
"comments": "ObjectId for s101t-datahub-spn-ees-dfe-gov-uk"
45-
}
46-
},
4741
"domain": {
4842
"value": "test.explore-education-statistics.service.gov.uk"
4943
},
@@ -99,7 +93,7 @@
9993
"value": true
10094
},
10195
"publicDataDbExists": {
102-
"value": false
96+
"value": true
10397
}
10498
}
10599
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Originally sourced from https://github.com/Azure-Samples/todo-csharp-sql/blob/main/infra/abbreviations.json.
2+
@export()
3+
var abbreviations = {
4+
appContainerApps: 'ca'
5+
appManagedEnvironments: 'cae'
6+
// TODO - remove the "-flexibleserver" suffix and change the suffix of our PSQL instance to "-01"
7+
dBforPostgreSQLServers: 'psql-flexibleserver'
8+
// 'ai' is non-standard - it should be 'appi'
9+
insightsComponents: 'ai'
10+
managedIdentityUserAssignedIdentities: 'id'
11+
networkApplicationGateways: 'agw'
12+
operationalInsightsWorkspaces: 'log'
13+
// 'sa' is non-standard - it should be 'st'
14+
storageStorageAccounts: 'sa'
15+
// 'fa' is non-standard - it shoule be 'func'
16+
webSitesFunctions: 'fa'
17+
// 'asp' is non-standard - it should be 'plan'
18+
webServerFarms: 'asp'
19+
}

infrastructure/templates/public-api/api-infrastructure-pipeline.yml

+36-36
Original file line numberDiff line numberDiff line change
@@ -53,41 +53,41 @@ pool:
5353
vmImage: $(vmImageName)
5454

5555
stages:
56-
- template: validate-stage-template.yml
57-
parameters:
58-
stageName: 'Validate_Against_Development'
59-
condition: eq(variables.isDev, true)
60-
environment: 'Development'
61-
serviceConnection: $(serviceConnectionDevelopment)
62-
parameterFile: $(devParamFile)
56+
- template: validate-stage-template.yml
57+
parameters:
58+
stageName: 'Validate_Against_Development'
59+
condition: eq(variables.isDev, true)
60+
environment: 'Development'
61+
serviceConnection: $(serviceConnectionDevelopment)
62+
parameterFile: $(devParamFile)
63+
64+
- template: deploy-stage-template.yml
65+
parameters:
66+
stageName: 'Deploy_to_Development'
67+
condition: and(not(or(failed(), canceled())), eq(variables.isDev, true))
68+
dependsOn: 'Validate_Against_Development'
69+
environment: 'Development'
70+
serviceConnection: $(serviceConnectionDevelopment)
71+
subscription: $(subscription)
72+
parameterFile: $(devParamFile)
6373

64-
- template: deploy-stage-template.yml
65-
parameters:
66-
stageName: 'Deploy_to_Development'
67-
condition: and(succeeded(), eq(variables.isDev, true))
68-
dependsOn: 'Validate_Against_Development'
69-
environment: 'Development'
70-
serviceConnection: $(serviceConnectionDevelopment)
71-
subscription: $(subscription)
72-
parameterFile: $(devParamFile)
74+
- template: validate-stage-template.yml
75+
parameters:
76+
stageName: 'Validate_Against_Test'
77+
condition: eq(variables.isTest, true)
78+
environment: 'Test'
79+
serviceConnection: $(serviceConnectionTest)
80+
parameterFile: $(testParamFile)
7381

74-
- template: validate-stage-template.yml
75-
parameters:
76-
stageName: 'Validate_Against_Test'
77-
condition: eq(variables.isTest, true)
78-
environment: 'Test'
79-
serviceConnection: $(serviceConnectionTest)
80-
parameterFile: $(testParamFile)
81-
82-
- template: deploy-stage-template.yml
83-
parameters:
84-
stageName: 'Deploy_to_Test'
85-
dependsOn: 'Validate_Against_Test'
86-
condition: and(succeeded(), eq(variables.isTest, true))
87-
environment: 'Test'
88-
serviceConnection: $(serviceConnectionTest)
89-
subscription: $(subscription)
90-
parameterFile: $(testParamFile)
82+
- template: deploy-stage-template.yml
83+
parameters:
84+
stageName: 'Deploy_to_Test'
85+
dependsOn: 'Validate_Against_Test'
86+
condition: and(not(or(failed(), canceled())), eq(variables.isTest, true))
87+
environment: 'Test'
88+
serviceConnection: $(serviceConnectionTest)
89+
subscription: $(subscription)
90+
parameterFile: $(testParamFile)
9191

9292
# - template: validate-stage-template.yml
9393
# parameters:
@@ -100,7 +100,7 @@ stages:
100100
# - template: deploy-stage-template.yml
101101
# parameters:
102102
# stageName: 'Deploy_to_PreProduction'
103-
# condition: and(succeeded(), eq(variables.isMaster, true))
103+
# condition: and(not(or(failed('Validate_Against_PreProduction'), canceled('Validate_Against_PreProduction')), eq(variables.isMaster, true))
104104
# dependsOn: 'Validate_Against_PreProduction'
105105
# environment: 'Pre-production'
106106
# serviceConnection: $(serviceConnectionPreProduction)
@@ -110,15 +110,15 @@ stages:
110110
# - template: validate-stage-template.yml
111111
# parameters:
112112
# stageName: 'Validate_Against_Production'
113-
# condition: and(succeeded(), eq(variables.isMaster, true))
113+
# condition: and(not(or(failed('Deploy_to_PreProduction'), canceled('Deploy_to_PreProduction')), eq(variables.isMaster, true))
114114
# environment: 'Production'
115115
# serviceConnection: $(serviceConnectionProduction)
116116
# parameterFile: $(prodParamFile)
117117
#
118118
# - template: deploy-stage-template.yml
119119
# parameters:
120120
# stageName: 'Deploy_to_Production'
121-
# condition: and(succeeded(), eq(variables.isMaster, true))
121+
# condition: and(not(or(failed('Validate_Against_Production'), canceled('Validate_Against_Production')), eq(variables.isMaster, true))
122122
# dependsOn: 'Validate_Against_Production'
123123
# environment: 'Production'
124124
# serviceConnection: $(serviceConnectionProduction)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import { resourceNamesType } from '../../types.bicep'
2+
3+
@description('Specifies common resource naming variables.')
4+
param resourceNames resourceNamesType
5+
6+
@description('Specifies the location for all resources.')
7+
param location string
8+
9+
@description('Specifies the id of the Container App Environment in which to deploy this Container App.')
10+
param containerAppEnvironmentId string
11+
12+
@description('The tags of the Docker images to deploy.')
13+
param dockerImagesTag string
14+
15+
@description('The URL of the Public API.')
16+
param publicApiUrl string
17+
18+
@description('The URL of the Public site.')
19+
param publicSiteUrl string
20+
21+
@description('The URL of the Content API.')
22+
param contentApiUrl string
23+
24+
@description('Specifies the Application (Client) Id of the App Registration used to represent the API Container App.')
25+
param apiAppRegistrationClientId string
26+
27+
@description('Specifies the Application Insights connection string for this Container App to use for its monitoring.')
28+
param appInsightsConnectionString string
29+
30+
@description('Specifies a set of tags with which to tag the resource in Azure.')
31+
param tagValues object
32+
33+
var dataFilesFileShareMountPath = '/data/public-api-data'
34+
35+
resource adminAppService 'Microsoft.Web/sites@2023-12-01' existing = {
36+
name: resourceNames.existingResources.adminApp
37+
}
38+
39+
resource adminAppServiceIdentity 'Microsoft.ManagedIdentity/identities@2023-01-31' existing = {
40+
scope: adminAppService
41+
name: 'default'
42+
}
43+
44+
var adminAppClientId = adminAppServiceIdentity.properties.clientId
45+
var adminAppPrincipalId = adminAppServiceIdentity.properties.principalId
46+
47+
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = {
48+
name: resourceNames.existingResources.keyVault
49+
}
50+
51+
resource apiContainerAppManagedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = {
52+
name: resourceNames.publicApi.apiAppIdentity
53+
}
54+
55+
module apiContainerAppModule '../../components/containerApp.bicep' = {
56+
name: 'apiContainerAppDeploy'
57+
params: {
58+
location: location
59+
containerAppName: resourceNames.publicApi.apiApp
60+
acrLoginServer: keyVault.getSecret('DOCKER-REGISTRY-SERVER-DOMAIN')
61+
containerAppImageName: 'ees-public-api/api:${dockerImagesTag}'
62+
dockerPullManagedIdentityClientId: keyVault.getSecret('DOCKER-REGISTRY-SERVER-USERNAME')
63+
dockerPullManagedIdentitySecretValue: keyVault.getSecret('DOCKER-REGISTRY-SERVER-PASSWORD')
64+
userAssignedManagedIdentityId: apiContainerAppManagedIdentity.id
65+
managedEnvironmentId: containerAppEnvironmentId
66+
corsPolicy: {
67+
allowedOrigins: [
68+
publicSiteUrl
69+
'http://localhost:3000'
70+
'http://127.0.0.1'
71+
]
72+
}
73+
volumeMounts: [
74+
{
75+
volumeName: 'public-api-fileshare-mount'
76+
mountPath: dataFilesFileShareMountPath
77+
}
78+
]
79+
volumes: [
80+
{
81+
name: 'public-api-fileshare-mount'
82+
storageType: 'AzureFile'
83+
storageName: resourceNames.publicApi.publicApiFileshare
84+
}
85+
]
86+
appSettings: [
87+
{
88+
name: 'ConnectionStrings__PublicDataDb'
89+
value: 'Server=${resourceNames.sharedResources.postgreSqlFlexibleServer}.postgres.database.azure.com;Database=public_data;Port=5432;User Id=${resourceNames.publicApi.apiAppIdentity}'
90+
}
91+
{
92+
// This settings allows the Container App to identify which user-assigned identity it should use in order to
93+
// perform Managed Identity-based authentication and authorization with other Azure services / resources.
94+
//
95+
// It is used in conjunction with the Azure.Identity .NET library to retrieve access tokens for the user-assigned
96+
// identity.
97+
name: 'AZURE_CLIENT_ID'
98+
value: apiContainerAppManagedIdentity.properties.clientId
99+
}
100+
{
101+
name: 'AppSettings__HostUrl'
102+
value: publicApiUrl
103+
}
104+
{
105+
name: 'AppInsights__ConnectionString'
106+
value: appInsightsConnectionString
107+
}
108+
{
109+
name: 'ContentApi__Url'
110+
value: contentApiUrl
111+
}
112+
{
113+
name: 'MiniProfiler__Enabled'
114+
value: 'true'
115+
}
116+
{
117+
name: 'DataFiles__BasePath'
118+
value: dataFilesFileShareMountPath
119+
}
120+
{
121+
name: 'OpenIdConnect__TenantId'
122+
value: tenant().tenantId
123+
}
124+
{
125+
name: 'OpenIdConnect__ClientId'
126+
value: apiAppRegistrationClientId
127+
}
128+
]
129+
entraIdAuthentication: {
130+
appRegistrationClientId: apiAppRegistrationClientId
131+
allowedClientIds: [
132+
adminAppClientId
133+
]
134+
allowedPrincipalIds: [
135+
adminAppPrincipalId
136+
]
137+
requireAuthentication: false
138+
}
139+
tagValues: tagValues
140+
}
141+
}
142+
143+
output containerAppFqdn string = apiContainerAppModule.outputs.containerAppFqdn
144+
output containerAppName string = apiContainerAppModule.outputs.containerAppName
145+
output containerAppHealthProbeRelativeUrl string = '/docs'

0 commit comments

Comments
 (0)