Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Api deployment #863

Merged
merged 3 commits into from
Feb 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/publish-postgres-docker-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ on:
branches:
- 'main'
- 'develop'
- 'infrastructure/data-layer'
paths:
- 'postgresql/**'
- '.github/**'
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,10 @@ Azure resources, like a [Resource Group](https://docs.microsoft.com/en-us/azure/
or a [Storage Account](https://docs.microsoft.com/en-us/azure/storage/common/storage-account-overview)
to store the "main" Terraform remote state
- Apply the "main" `infrastructure` folder, which contains all the Azure
resources necessary to host a functioning Marxan application.
resources necessary to host a functioning [Azure AKS cluster](https://azure.microsoft.com/en-us/services/kubernetes-service/).
- Apply the "main" `kubernetes` folder, which contains all the
resources necessary to host a functioning Marxan application within the AKS
cluster provisioned above.

## Bugs

Expand Down
11 changes: 6 additions & 5 deletions api/apps/geoprocessing/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,19 @@ import { AppService } from './app.service';
import { AdminAreasModule } from '@marxan-geoprocessing/modules/admin-areas/admin-areas.module';
import { ProtectedAreasModule } from '@marxan-geoprocessing/modules/protected-areas/protected-areas.module';
import { PlanningUnitsModule } from '@marxan-geoprocessing/modules/planning-units/planning-units.module';
import { TileModule } from './modules/tile/tile.module';
import { TileModule } from '@marxan-geoprocessing/modules/tile/tile.module';
import { FeaturesModule } from '@marxan-geoprocessing/modules/features/features.module';
import { ApiEventsModule } from './modules/api-events/api-events.module';
import { SurfaceCostModule } from './modules/surface-cost/surface-cost.module';
import { ApiEventsModule } from '@marxan-geoprocessing/modules/api-events/api-events.module';
import { SurfaceCostModule } from '@marxan-geoprocessing/modules/surface-cost/surface-cost.module';
import { ScenarioPlanningUnitsInclusionModule } from '@marxan-geoprocessing/modules/scenario-planning-units-inclusion/scenario-planning-units-inclusion.module';
import { CostTemplateModule } from '@marxan-geoprocessing/modules/scenarios';
import { PlanningAreaModule } from '@marxan-geoprocessing/modules/planning-area/planning-area.module';
import { ScenariosModule } from '@marxan-geoprocessing/modules/scenarios/scenarios.module';
import { ScenarioProtectedAreaCalculationModule } from '@marxan-geoprocessing/modules/scenario-protected-area-calculation/scenario-protected-area-calculation.module';
import { ScenarioPlanningUnitsFeaturesAggregateModule } from '@marxan-geoprocessing/modules/scenario-planning-units-features-aggregate/scenario-planning-units-features-aggregate.module';
import { ExportModule } from '@marxan-geoprocessing/export/export.module';
import { ImportModule } from './import/import.module';
import { ImportModule } from '@marxan-geoprocessing/import/import.module';
import { PingController } from '@marxan-geoprocessing/modules/ping/ping.controller';

@Module({
imports: [
Expand Down Expand Up @@ -46,7 +47,7 @@ import { ImportModule } from './import/import.module';
ExportModule,
ImportModule,
],
controllers: [AppController],
controllers: [AppController, PingController],
providers: [AppService],
})
export class AppModule {}
19 changes: 19 additions & 0 deletions api/apps/geoprocessing/src/modules/ping/ping.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Test, TestingModule } from '@nestjs/testing';

import { PingController } from './ping.controller';

describe('Ping Controller', () => {
let controller: PingController;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [PingController],
}).compile();

controller = module.get<PingController>(PingController);
});

it('should be defined', () => {
expect(controller).toBeDefined();
});
});
9 changes: 9 additions & 0 deletions api/apps/geoprocessing/src/modules/ping/ping.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Controller, Get } from '@nestjs/common';

@Controller('ping')
export class PingController {
@Get()
ping(): { ping: string } {
return { ping: 'pong' };
}
}
5 changes: 5 additions & 0 deletions infrastructure/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ module "kubernetes" {
(module.network.aks_vnet_name) = module.network.aks_vnet_id
}
acr_id = module.container_registry.azurerm_container_registry_id

aks_vnet_id = module.network.aks_vnet_id
aks_vnet_name = module.network.aks_vnet_name

gateway_subnet_id = module.network.app_gateway_subnet_id
}

module "data_node_pool" {
Expand Down
9 changes: 0 additions & 9 deletions infrastructure/modules/container-registry/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,3 @@ resource "azuread_application_federated_identity_credential" "github-actions-acc
issuer = "https://token.actions.githubusercontent.com"
subject = "repo:Vizzuality/marxan-cloud:ref:refs/heads/main"
}

resource "azuread_application_federated_identity_credential" "github-actions-access-temp" {
application_object_id = azuread_application.github-actions-access.object_id
display_name = "github-actions-access-temp"
description = "Deployments from github actions"
audiences = ["api://AzureADTokenExchange"]
issuer = "https://token.actions.githubusercontent.com"
subject = "repo:Vizzuality/marxan-cloud:ref:refs/heads/infrastructure/data-layer"
}
11 changes: 11 additions & 0 deletions infrastructure/modules/kubernetes/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ resource "azurerm_kubernetes_cluster" "k8s_cluster" {

private_dns_zone_id = azurerm_private_dns_zone.private_dns_zone.id

addon_profile {
ingress_application_gateway {
enabled = true
gateway_name = "${var.project_name}KubernetesIngress"
subnet_id = var.gateway_subnet_id
}
http_application_routing {
enabled = true
}
}

network_profile {
network_plugin = "azure"
network_policy = "azure"
Expand Down
12 changes: 12 additions & 0 deletions infrastructure/modules/kubernetes/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@ variable "kubernetes_version" {
default = "1.22.4"
}

variable "gateway_subnet_id" {
type = string
}

variable "aks_vnet_id" {
type = string
}

variable "aks_vnet_name" {
type = string
}

variable "aks_subnet_id" {
description = "(Optional) The ID of a Subnet where the Kubernetes Node Pool should exist. Changing this forces a new resource to be created."
type = string
Expand Down
14 changes: 14 additions & 0 deletions infrastructure/modules/network/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,20 @@ resource "azurerm_subnet" "bastion_subnet" {
enforce_private_link_service_network_policies = false
}


###
# Application Gateway subnet
###
resource "azurerm_subnet" "app_gateway_subnet" {
name = "AzureAppGatewaySubnet"
resource_group_name = var.resource_group.name
virtual_network_name = azurerm_virtual_network.core_vnet.name
address_prefixes = ["10.1.2.0/24"]

enforce_private_link_endpoint_network_policies = true
enforce_private_link_service_network_policies = false
}

# Create network security group and SSH rule for bastion subnet.
resource "azurerm_network_security_group" "bastion_nsg" {
name = "${var.project_name}-bastion-nsg"
Expand Down
4 changes: 4 additions & 0 deletions infrastructure/modules/network/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,7 @@ output "bastion_subnet_id" {
output "firewall_subnet_id" {
value = azurerm_subnet.firewall_subnet.id
}

output "app_gateway_subnet_id" {
value = azurerm_subnet.app_gateway_subnet.id
}
4 changes: 4 additions & 0 deletions infrastructure/modules/node_pool/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,8 @@ resource "azurerm_kubernetes_cluster_node_pool" "node_pool" {
vnet_subnet_id = var.subnet_id

node_labels = var.node_labels

lifecycle {
ignore_changes = [node_count]
}
}
104 changes: 101 additions & 3 deletions kubernetes/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,17 @@ module "k8s_namespaces" {
k8s_cluster_ca_certificate = local.k8s_cluster_ca_certificate
}

####
# Production
####
module "key_vault_production" {
source = "./modules/key_vault"
namespace = "production"
resource_group = data.azurerm_resource_group.resource_group
project_name = var.project_name
}

module "k8s_api_database" {
module "k8s_api_database_production" {
source = "./modules/database"
k8s_host = local.k8s_host
k8s_client_certificate = local.k8s_client_certificate
Expand All @@ -65,7 +68,7 @@ module "k8s_api_database" {
key_vault_id = module.key_vault_production.key_vault_id
}

module "k8s_geoprocessing_database" {
module "k8s_geoprocessing_database_production" {
source = "./modules/database"
k8s_host = local.k8s_host
k8s_client_certificate = local.k8s_client_certificate
Expand All @@ -89,7 +92,18 @@ module "api_production" {
deployment_name = "api"
}

module "api_production_secret" {
module "geoprocessing_production" {
source = "./modules/geoprocessing"
k8s_host = local.k8s_host
k8s_client_certificate = local.k8s_client_certificate
k8s_client_key = local.k8s_client_key
k8s_cluster_ca_certificate = local.k8s_cluster_ca_certificate
namespace = "production"
image = "marxan.azurecr.io/marxan-geoprocessing:production"
deployment_name = "geoprocessing"
}

module "production_secrets" {
source = "./modules/secrets"
k8s_host = local.k8s_host
k8s_client_certificate = local.k8s_client_certificate
Expand All @@ -102,3 +116,87 @@ module "api_production_secret" {
redis_host = data.terraform_remote_state.core.outputs.redis_url
redis_password = data.terraform_remote_state.core.outputs.redis_password
}

module "ingress_production" {
source = "./modules/ingress"
namespace = "production"
k8s_host = local.k8s_host
k8s_client_certificate = local.k8s_client_certificate
k8s_client_key = local.k8s_client_key
k8s_cluster_ca_certificate = local.k8s_cluster_ca_certificate
resource_group = data.azurerm_resource_group.resource_group
project_name = var.project_name
}


####
# Staging
####
module "key_vault_staging" {
source = "./modules/key_vault"
namespace = "staging"
resource_group = data.azurerm_resource_group.resource_group
project_name = var.project_name
}

module "k8s_api_database_staging" {
source = "./modules/database"
k8s_host = local.k8s_host
k8s_client_certificate = local.k8s_client_certificate
k8s_client_key = local.k8s_client_key
k8s_cluster_ca_certificate = local.k8s_cluster_ca_certificate
resource_group = data.azurerm_resource_group.resource_group
project_name = var.project_name
namespace = "staging"
name = "api"
key_vault_id = module.key_vault_staging.key_vault_id
}

module "k8s_geoprocessing_database_staging" {
source = "./modules/database"
k8s_host = local.k8s_host
k8s_client_certificate = local.k8s_client_certificate
k8s_client_key = local.k8s_client_key
k8s_cluster_ca_certificate = local.k8s_cluster_ca_certificate
resource_group = data.azurerm_resource_group.resource_group
project_name = var.project_name
namespace = "staging"
name = "geoprocessing"
key_vault_id = module.key_vault_staging.key_vault_id
}

module "api_staging" {
source = "./modules/api"
k8s_host = local.k8s_host
k8s_client_certificate = local.k8s_client_certificate
k8s_client_key = local.k8s_client_key
k8s_cluster_ca_certificate = local.k8s_cluster_ca_certificate
namespace = "staging"
image = "marxan.azurecr.io/marxan-api:staging"
deployment_name = "api"
}

module "geoprocessing_staging" {
source = "./modules/geoprocessing"
k8s_host = local.k8s_host
k8s_client_certificate = local.k8s_client_certificate
k8s_client_key = local.k8s_client_key
k8s_cluster_ca_certificate = local.k8s_cluster_ca_certificate
namespace = "staging"
image = "marxan.azurecr.io/marxan-geoprocessing:staging"
deployment_name = "geoprocessing"
}

module "staging_secrets" {
source = "./modules/secrets"
k8s_host = local.k8s_host
k8s_client_certificate = local.k8s_client_certificate
k8s_client_key = local.k8s_client_key
k8s_cluster_ca_certificate = local.k8s_cluster_ca_certificate
project_name = var.project_name
namespace = "staging"
name = "api"
key_vault_id = module.key_vault_staging.key_vault_id
redis_host = data.terraform_remote_state.core.outputs.redis_url
redis_password = data.terraform_remote_state.core.outputs.redis_password
}
60 changes: 35 additions & 25 deletions kubernetes/modules/api/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,16 @@ resource "kubernetes_deployment" "api_deployment" {
}
}

env {
name = "REDIS_PASSWORD"
value_from {
secret_key_ref {
name = "api"
key = "REDIS_PASSWORD"
}
}
}

env {
name = "API_SERVICE_PORT"
value = 3000
Expand All @@ -182,31 +192,31 @@ resource "kubernetes_deployment" "api_deployment" {
}
}

# liveness_probe {
# http_get {
# path = "/health"
# port = 3000
# scheme = "HTTP"
# }
#
# success_threshold = 1
# timeout_seconds = 5
# initial_delay_seconds = 15
# period_seconds = 15
# }
#
# readiness_probe {
# http_get {
# path = "/health"
# port = 3000
# scheme = "HTTP"
# }
#
# success_threshold = 1
# timeout_seconds = 5
# initial_delay_seconds = 30
# period_seconds = 15
# }
liveness_probe {
http_get {
path = "/ping"
port = 3000
scheme = "HTTP"
}

success_threshold = 1
timeout_seconds = 5
initial_delay_seconds = 15
period_seconds = 15
}

readiness_probe {
http_get {
path = "/ping"
port = 3000
scheme = "HTTP"
}

success_threshold = 1
timeout_seconds = 5
initial_delay_seconds = 30
period_seconds = 15
}
}
}
}
Expand Down
Loading