Skip to content

Commit 9769e52

Browse files
oskogstadarealmaas
andauthored
chore(graphql): Add deployment of GraphQL service and local development via docker-compose (#640)
- Adding new container app for GraphQL - Refactor apim URL to be optional until we add GraphQL service to the APIM - Add graphql to docker-compose and an nginx ingress with a new nginx config - Remove explicit add of banana cake pop ## Related Issue(s) #490 ## Verification - [ ] **Your** code builds clean without any errors or warnings - [ ] Manual testing done (required) - [ ] Relevant automated test added (if you find this hard, leave it and we'll help out) ## Documentation - [ ] Documentation is updated (either in `docs`-directory, Altinnpedia or a separate linked PR in [altinn-studio-docs.](https://github.com/Altinn/altinn-studio-docs), if applicable) --------- Co-authored-by: Are Almaas <arealmaas@gmail.com>
1 parent df8cec9 commit 9769e52

File tree

12 files changed

+289
-28
lines changed

12 files changed

+289
-28
lines changed
+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
targetScope = 'resourceGroup'
2+
3+
@minLength(3)
4+
param imageTag string
5+
@minLength(3)
6+
param environment string
7+
@minLength(3)
8+
param location string
9+
10+
@minLength(3)
11+
@secure()
12+
param containerAppEnvironmentName string
13+
@minLength(3)
14+
@secure()
15+
param appInsightConnectionString string
16+
@minLength(5)
17+
@secure()
18+
param appConfigurationName string
19+
@minLength(3)
20+
@secure()
21+
param environmentKeyVaultName string
22+
23+
var namePrefix = 'dp-be-${environment}'
24+
var baseImageUrl = 'ghcr.io/digdir/dialogporten-'
25+
26+
resource appConfiguration 'Microsoft.AppConfiguration/configurationStores@2023-03-01' existing = {
27+
name: appConfigurationName
28+
}
29+
30+
resource containerAppEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' existing = {
31+
name: containerAppEnvironmentName
32+
}
33+
34+
var containerAppEnvVars = [
35+
{
36+
name: 'ASPNETCORE_ENVIRONMENT'
37+
value: environment
38+
}
39+
{
40+
name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
41+
value: appInsightConnectionString
42+
}
43+
{
44+
name: 'AZURE_APPCONFIG_URI'
45+
value: appConfiguration.properties.endpoint
46+
}
47+
]
48+
49+
resource environmentKeyVaultResource 'Microsoft.KeyVault/vaults@2023-07-01' existing = {
50+
name: environmentKeyVaultName
51+
}
52+
53+
var containerAppName = '${namePrefix}-graphql-ca'
54+
55+
module containerApp '../../modules/containerApp/main.bicep' = {
56+
name: containerAppName
57+
params: {
58+
name: containerAppName
59+
image: '${baseImageUrl}graphql:${imageTag}'
60+
location: location
61+
envVariables: containerAppEnvVars
62+
containerAppEnvId: containerAppEnvironment.id
63+
}
64+
}
65+
66+
module keyVaultReaderAccessPolicy '../../modules/keyvault/addReaderRoles.bicep' = {
67+
name: 'keyVaultReaderAccessPolicy-${containerAppName}'
68+
params: {
69+
keyvaultName: environmentKeyVaultResource.name
70+
principalIds: [containerApp.outputs.identityPrincipalId]
71+
}
72+
}
73+
74+
module appConfigReaderAccessPolicy '../../modules/appConfiguration/addReaderRoles.bicep' = {
75+
name: 'appConfigReaderAccessPolicy-${containerAppName}'
76+
params: {
77+
appConfigurationName: appConfigurationName
78+
principalIds: [containerApp.outputs.identityPrincipalId]
79+
}
80+
}
81+
82+
output name string = containerApp.outputs.name
83+
output revisionName string = containerApp.outputs.revisionName
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using './main.bicep'
2+
3+
param environment = 'staging'
4+
param location = 'norwayeast'
5+
param imageTag = readEnvironmentVariable('IMAGE_TAG')
6+
7+
// secrets
8+
param environmentKeyVaultName = readEnvironmentVariable('ENVIRONMENT_KEY_VAULT_NAME')
9+
param containerAppEnvironmentName = readEnvironmentVariable('CONTAINER_APP_ENVIRONMENT_NAME')
10+
param appInsightConnectionString = readEnvironmentVariable('APP_INSIGHTS_CONNECTION_STRING')
11+
param appConfigurationName = readEnvironmentVariable('APP_CONFIGURATION_NAME')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using './main.bicep'
2+
3+
param environment = 'test'
4+
param location = 'norwayeast'
5+
param imageTag = readEnvironmentVariable('IMAGE_TAG')
6+
7+
// secrets
8+
param environmentKeyVaultName = readEnvironmentVariable('ENVIRONMENT_KEY_VAULT_NAME')
9+
param containerAppEnvironmentName = readEnvironmentVariable('CONTAINER_APP_ENVIRONMENT_NAME')
10+
param appInsightConnectionString = readEnvironmentVariable('APP_INSIGHTS_CONNECTION_STRING')
11+
param appConfigurationName = readEnvironmentVariable('APP_CONFIGURATION_NAME')

.azure/modules/containerApp/main.bicep

+12-8
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ param envVariables array = []
33
param port int = 8080
44
param name string
55
param image string
6-
param apimIp string
6+
param apimIp string?
77

88
param containerAppEnvId string
99

@@ -28,16 +28,20 @@ var probes = [
2828
}
2929
]
3030

31+
var ipSecurityRestrictions = empty(apimIp)
32+
? []
33+
: [
34+
{
35+
name: 'apim'
36+
action: 'Allow'
37+
ipAddressRange: apimIp!
38+
}
39+
]
40+
3141
var ingress = {
3242
targetPort: port
3343
external: true
34-
ipSecurityRestrictions: [
35-
{
36-
name: 'apim'
37-
action: 'Allow'
38-
ipAddressRange: apimIp
39-
}
40-
]
44+
ipSecurityRestrictions: ipSecurityRestrictions
4145
}
4246

4347
resource containerApp 'Microsoft.App/containerApps@2023-05-01' = {

.github/workflows/action-deploy-apps.yml

+1
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ jobs:
115115
include:
116116
- name: web-api-eu
117117
- name: web-api-so
118+
- name: graphql
118119
environment: ${{ inputs.environment }}
119120
permissions:
120121
id-token: write

.github/workflows/action-publish.yml

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ jobs:
2727
include:
2828
- dockerfile: ./src/Digdir.Domain.Dialogporten.WebApi/Dockerfile
2929
imageName: webapi
30+
- dockerfile: ./src/Digdir.Domain.Dialogporten.GraphQL/Dockerfile
31+
imageName: graphql
3032
- dockerfile: ./src/Digdir.Domain.Dialogporten.Service/Dockerfile
3133
imageName: service
3234
- dockerfile: ./src/Digdir.Domain.Dialogporten.ChangeDataCapture/Dockerfile

docker-compose.yml

+36-2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ services:
55
dialogporten-webapi-ingress:
66
image: nginx:1.25.4
77
ports:
8-
- "7214:80"
8+
- "7217:80"
99
volumes:
10-
- ./nginx.conf:/etc/nginx/nginx.conf
10+
- ./nginx-webapi.conf:/etc/nginx/nginx.conf
1111
depends_on:
1212
- dialogporten-webapi
1313
restart: always
@@ -36,3 +36,37 @@ services:
3636
volumes:
3737
- ./.aspnet/https:/https
3838

39+
dialogporten-graphql-ingress:
40+
image: nginx:1.25.4
41+
ports:
42+
- "7215:80"
43+
volumes:
44+
- ./nginx-graphql.conf:/etc/nginx/nginx.conf
45+
depends_on:
46+
- dialogporten-graphql
47+
restart: always
48+
49+
dialogporten-graphql:
50+
scale: 1
51+
ports:
52+
- "7220:8080"
53+
build:
54+
context: .
55+
dockerfile: src/Digdir.Domain.Dialogporten.GraphQL/Dockerfile
56+
restart: always
57+
depends_on:
58+
dialogporten-postgres:
59+
condition: service_healthy
60+
dialogporten-redis:
61+
condition: service_healthy
62+
dialogporten-migrations:
63+
condition: service_completed_successfully
64+
environment:
65+
- Infrastructure:Redis:ConnectionString=dialogporten-redis:6379
66+
- Infrastructure:DialogDbConnectionString=${DB_CONNECTION_STRING}
67+
- Application:Dialogporten:BaseUri=http://localhost:7214
68+
- Serilog__WriteTo__0__Name=Console
69+
- Serilog__MinimumLevel__Default=Debug
70+
- ASPNETCORE_ENVIRONMENT=Development
71+
volumes:
72+
- ./.aspnet/https:/https

nginx-graphql.conf

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# this nginx config is for the load balancer: dialogporten-graphql-ingress in docker-compose.yml
2+
events {}
3+
4+
http {
5+
upstream graphql {
6+
least_conn;
7+
server dialogporten-graphql:8080;
8+
}
9+
10+
server {
11+
listen 80;
12+
13+
location / {
14+
proxy_pass http://graphql;
15+
proxy_set_header Host $host;
16+
proxy_set_header X-Real-IP $remote_addr;
17+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
18+
proxy_set_header X-Forwarded-Proto $scheme;
19+
}
20+
}
21+
}

nginx.conf nginx-webapi.conf

File renamed without changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
FROM mcr.microsoft.com/dotnet/aspnet:8.0.3@sha256:9470bf16cb8566951dfdb89d49a4de73ceb31570b3cdb59059af44fe53b19547 AS base
2+
WORKDIR /app
3+
EXPOSE 8080
4+
5+
FROM mcr.microsoft.com/dotnet/sdk:8.0.204@sha256:7861b15f318949cf9214d9ad5382b680a0e22e3b8673180707aa0c594ab75656 AS build
6+
WORKDIR /src
7+
# Main project
8+
COPY ["src/Digdir.Domain.Dialogporten.GraphQL/Digdir.Domain.Dialogporten.GraphQL.csproj", "src/Digdir.Domain.Dialogporten.GraphQL/"]
9+
# Dependencies
10+
COPY ["src/Digdir.Domain.Dialogporten.Application/Digdir.Domain.Dialogporten.Application.csproj", "src/Digdir.Domain.Dialogporten.Application/"]
11+
COPY ["src/Digdir.Domain.Dialogporten.Domain/Digdir.Domain.Dialogporten.Domain.csproj", "src/Digdir.Domain.Dialogporten.Domain/"]
12+
COPY ["src/Digdir.Library.Entity.Abstractions/Digdir.Library.Entity.Abstractions.csproj", "src/Digdir.Library.Entity.Abstractions/"]
13+
COPY ["src/Digdir.Library.Entity.EntityFrameworkCore/Digdir.Library.Entity.EntityFrameworkCore.csproj", "src/Digdir.Library.Entity.EntityFrameworkCore/"]
14+
COPY ["src/Digdir.Domain.Dialogporten.Infrastructure/Digdir.Domain.Dialogporten.Infrastructure.csproj", "src/Digdir.Domain.Dialogporten.Infrastructure/"]
15+
# Restore project
16+
RUN dotnet restore "src/Digdir.Domain.Dialogporten.GraphQL/Digdir.Domain.Dialogporten.GraphQL.csproj"
17+
# Copy source
18+
COPY ["src/", "."]
19+
# Publish
20+
WORKDIR "/src/Digdir.Domain.Dialogporten.GraphQL"
21+
RUN dotnet publish "Digdir.Domain.Dialogporten.GraphQL.csproj" -c Release -o /app/publish /p:UseAppHost=false
22+
23+
FROM base AS final
24+
WORKDIR /app
25+
USER $APP_UID
26+
COPY --from=build /app/publish .
27+
ENTRYPOINT ["dotnet", "Digdir.Domain.Dialogporten.GraphQL.dll"]

src/Digdir.Domain.Dialogporten.GraphQL/Program.cs

+12-18
Original file line numberDiff line numberDiff line change
@@ -92,34 +92,28 @@ static void BuildAndRun(string[] args)
9292
.AddSorting()
9393
.RegisterDbContext<DialogDbContext>()
9494
.AddQueryType<DialogQueries>()
95-
.Services
95+
.Services
9696

97-
// Auth
98-
.AddDialogportenAuthentication(builder.Configuration)
99-
.AddAuthorization();
97+
// Auth
98+
.AddDialogportenAuthentication(builder.Configuration)
99+
.AddAuthorization();
100100

101101
var app = builder.Build();
102102

103103
app.UseJwtSchemeSelector();
104104
app.UseAuthentication();
105105
app.UseAuthorization();
106106

107-
if (app.Environment.IsDevelopment())
108-
{
109-
// GUI endpoint
110-
app.MapBananaCakePop("/bcp");
111-
}
112-
113107
app.MapGraphQL()
114-
.RequireAuthorization()
115-
.WithOptions(new GraphQLServerOptions
108+
.RequireAuthorization()
109+
.WithOptions(new GraphQLServerOptions
110+
{
111+
EnableSchemaRequests = true,
112+
Tool =
116113
{
117-
EnableSchemaRequests = true,
118-
Tool =
119-
{
120-
Enable = false
121-
}
122-
});
114+
Enable = true
115+
}
116+
});
123117

124118
app.Run();
125119
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
},
8+
"Infrastructure": {
9+
"Redis": {
10+
"Enabled": true,
11+
"ConnectionString": "TODO: Add to local secrets"
12+
},
13+
"DialogDbConnectionString": "TODO: Add to local secrets",
14+
// Settings from appsettings.json, environment variables or other configuration providers.
15+
// The first three are always mandatory for all client definitions types
16+
"Maskinporten": {
17+
// 1. Valid values are test and prod
18+
"Environment": "test",
19+
// 2. Client Id/integration as configured in Maskinporten
20+
"ClientId": "TODO: Add to local secrets",
21+
// 3. Scope(s) requested, space seperated. Must be provisioned on supplied client id.
22+
"Scope": "altinn:events.publish altinn:events.publish.admin altinn:register/partylookup.admin altinn:authorization:pdp",
23+
// --------------------------
24+
// Any additional settings are specific for the selected client definition type.
25+
// See below for examples using other types.
26+
"EncodedJwk": "TODO: Add to local secrets"
27+
},
28+
"Altinn": {
29+
"BaseUri": "https://platform.tt02.altinn.no/",
30+
"EventsBaseUri": "Secret webhook sink URL in source key vault",
31+
"SubscriptionKey": "TODO: Add to local secrets"
32+
}
33+
},
34+
"Application": {
35+
"Dialogporten": {
36+
"BaseUri": "https://altinn-dev-api.azure-api.net/dialogporten",
37+
"Ed25519KeyPairs": {
38+
"Primary": {
39+
"Kid": "TODO: Add to local secrets",
40+
"PrivateComponent": "TODO: Add to local secrets",
41+
"PublicComponent": "TODO: Add to local secrets"
42+
},
43+
"Secondary": {
44+
"Kid": "TODO: Add to local secrets",
45+
"PrivateComponent": "TODO: Add to local secrets",
46+
"PublicComponent": "TODO: Add to local secrets"
47+
}
48+
}
49+
}
50+
},
51+
"GraphQl": {
52+
"Authentication": {
53+
"JwtBearerTokenSchemas": [
54+
{
55+
"Name": "Maskinporten",
56+
"WellKnown": "https://test.maskinporten.no/.well-known/oauth-authorization-server/"
57+
},
58+
{
59+
"Name": "MaskinportenAuxiliary",
60+
"WellKnown": "https://ver2.maskinporten.no/.well-known/oauth-authorization-server/"
61+
},
62+
{
63+
"Name": "Altinn",
64+
"WellKnown": "https://platform.tt02.altinn.no/authentication/api/v1/openid/.well-known/openid-configuration"
65+
},
66+
{
67+
"Name": "Idporten",
68+
"WellKnown": "https://test.idporten.no/.well-known/openid-configuration"
69+
}
70+
]
71+
}
72+
}
73+
}

0 commit comments

Comments
 (0)