Skip to content

Commit

Permalink
Merge branch 'prod'
Browse files Browse the repository at this point in the history
  • Loading branch information
haapamakim committed Oct 3, 2022
2 parents 794bb7a + 6ffae91 commit 953de61
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 81 deletions.
8 changes: 5 additions & 3 deletions .husky/pre-push
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

export ENVIRONMENT=feature
npm run lint
npm run husky:test
if [ "$(git rev-parse --abbrev-ref HEAD)" != "prod" ]; then
export ENVIRONMENT=feature
npm run lint
npm run husky:test
fi
17 changes: 17 additions & 0 deletions deployment/bin/pushECRImagesToProd.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash

ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)

aws ecr get-login-password --region eu-west-1 | docker login --username AWS --password-stdin $ACCOUNT_ID.dkr.ecr.eu-west-1.amazonaws.com

LOCALSTACK_VERSION=0.14.1
aws ecr create-repository --repository-name localstack || true
docker tag localstack/localstack:$LOCALSTACK_VERSION $ACCOUNT_ID.dkr.ecr.eu-west-1.amazonaws.com/localstack:$LOCALSTACK_VERSION
docker push $ACCOUNT_ID.dkr.ecr.eu-west-1.amazonaws.com/localstack:$LOCALSTACK_VERSION

# Create buildimage to provide faster builds
BUILD_IMAGE_VERSION=1.0.3
REPO_TAG=$ACCOUNT_ID.dkr.ecr.eu-west-1.amazonaws.com/hassu-buildimage:$BUILD_IMAGE_VERSION
aws ecr create-repository --repository-name hassu-buildimage || true
docker tag hassu-buildimage:$BUILD_IMAGE_VERSION $REPO_TAG
docker push $REPO_TAG
41 changes: 41 additions & 0 deletions deployment/lib/buildspec/buildspec-prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
version: 0.2

env:
parameter-store:
ROCKET_CHAT_TOKEN: /RocketChatToken
ROCKET_CHAT_USER_ID: /RocketChatUserId
secrets-manager:
GITHUB_TOKEN: github-token

phases:
install:
runtime-versions:
java: corretto11
nodejs: 14
commands:
- ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
- aws ecr get-login-password --region eu-west-1 | docker login --username AWS --password-stdin "$ACCOUNT_ID.dkr.ecr.eu-west-1.amazonaws.com"

- nohup docker pull "$ACCOUNT_ID.dkr.ecr.eu-west-1.amazonaws.com/hassu-buildimage:1.0.3" &

- npm install -g npm@8.1.3
- npm ci

- touch .env.test
- npm run generate
build:
commands:
- npm run get-next-version
- npm run deploy:database
- npm run deploy:backend
- npm run deploy:frontend
- npm run release
post_build:
on-failure: ABORT
commands:
- ./deployment/bin/reportBuildStatus.sh -t "$ROCKET_CHAT_TOKEN" -u "$ROCKET_CHAT_USER_ID" -r "$CODEBUILD_BUILD_SUCCEEDING" -m "$ENVIRONMENT build" -d "CodeBuild $CODEBUILD_BUILD_URL"
cache:
paths:
- "/root/.cache/**/*"
- "/root/.npm/**/*"
- "/root/.gradle/**/*"
3 changes: 3 additions & 0 deletions deployment/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ export class Config extends BaseConfig {
this.frontendDomainName = (await readFrontendStackOutputs()).CloudfrontPrivateDNSName || "please-re-run-backend-deployment";
} else {
this.frontendDomainName = await this.getSecureInfraParameter("FrontendDomainName");
if (!this.frontendDomainName) {
throw new Error("/" + Config.env + "/FrontendDomainName SSM Parameter not found! Maybe logged in to wrong account?");
}
}
log.info("frontendDomainName", this.frontendDomainName);
};
Expand Down
2 changes: 1 addition & 1 deletion deployment/lib/hassu-account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class HassuAccountStack extends cdk.Stack {
enableVersionUpgrade: true,
capacity: {
masterNodes: 0,
dataNodes: 1,
dataNodes: 2,
dataNodeInstanceType: "t3.small.search",
},
removalPolicy: RemovalPolicy.RETAIN,
Expand Down
23 changes: 8 additions & 15 deletions deployment/lib/hassu-database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import * as backup from "@aws-cdk/aws-backup";
import * as events from "@aws-cdk/aws-events";
import { Effect, PolicyStatement } from "@aws-cdk/aws-iam";
import { IConstruct } from "@aws-cdk/core/lib/construct-compat";
import { BackupPlanRuleProps } from "@aws-cdk/aws-backup/lib/rule";

// These should correspond to CfnOutputs produced by this stack
export type DatabaseStackOutputs = {
Expand Down Expand Up @@ -127,6 +126,12 @@ export class HassuDatabaseStack extends cdk.Stack {
}

private createUploadBucket() {
let allowedOrigins: string[];
if (Config.isDeveloperEnvironment()) {
allowedOrigins = ["http://localhost:3000", "https://" + this.config.frontendDomainName];
} else {
allowedOrigins = ["https://" + this.config.frontendDomainName];
}
return new Bucket(this, "UploadBucket", {
bucketName: Config.uploadBucketName,
blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
Expand All @@ -135,7 +140,7 @@ export class HassuDatabaseStack extends cdk.Stack {
cors: [
{
allowedMethods: [HttpMethods.PUT],
allowedOrigins: ["http://localhost:3000", "https://" + this.config.frontendDomainName],
allowedOrigins: allowedOrigins,
allowedHeaders: ["*"],
},
],
Expand Down Expand Up @@ -208,24 +213,12 @@ export class HassuDatabaseStack extends cdk.Stack {
const backupPlanName = "Plan-" + Config.env;
const backupVaultName = "Vault-" + Config.env;

let backupPlanRuleProps: BackupPlanRuleProps;
if (Config.isProductionEnvironment()) {
backupPlanRuleProps = {
moveToColdStorageAfter: Duration.days(35),
deleteAfter: Duration.days(365),
};
} else {
backupPlanRuleProps = {
deleteAfter: Duration.days(35),
};
}

const plan = new backup.BackupPlan(this, backupPlanName, {
backupPlanName,
backupVault: new backup.BackupVault(this, backupVaultName, { backupVaultName }),
backupPlanRules: [
new backup.BackupPlanRule({
...backupPlanRuleProps,
deleteAfter: Duration.days(35),
ruleName: "Daily",
startWindow: Duration.hours(1),
completionWindow: Duration.hours(2),
Expand Down
144 changes: 85 additions & 59 deletions deployment/lib/hassu-pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as codebuild from "@aws-cdk/aws-codebuild";
import { BuildEnvironmentVariableType, ComputeType, LocalCacheMode } from "@aws-cdk/aws-codebuild";
import { Config } from "./config";
import { BuildSpec } from "@aws-cdk/aws-codebuild/lib/build-spec";
import { LinuxBuildImage } from "@aws-cdk/aws-codebuild/lib/project";
import { BuildEnvironmentVariable, LinuxBuildImage } from "@aws-cdk/aws-codebuild/lib/project";
import { Effect, PolicyStatement } from "@aws-cdk/aws-iam";
import { GitHubSourceProps } from "@aws-cdk/aws-codebuild/lib/source";
import { Repository } from "@aws-cdk/aws-ecr/lib/repository";
Expand Down Expand Up @@ -42,7 +42,11 @@ export class HassuPipelineStack extends Stack {
}

if (Config.isPermanentEnvironment()) {
await this.createPipeline(env, config, "./deployment/lib/buildspec/buildspec.yml");
if (Config.isProdAccount()) {
await this.createPipeline(env, config, "./deployment/lib/buildspec/buildspec-prod.yml");
} else {
await this.createPipeline(env, config, "./deployment/lib/buildspec/buildspec.yml");
}
} else {
await this.createPipeline(env, config, "./deployment/lib/buildspec/buildspec-feature.yml");
}
Expand All @@ -53,11 +57,13 @@ export class HassuPipelineStack extends Stack {
let webhookFilters;
let reportBuildStatus: boolean;
const branch = config.getBranch();
if (branch === "main" && env == "dev") {
if ((branch === "main" && env == "dev") || (branch === "prod" && env == "prod")) {
// GitHub creds only once per account
new codebuild.GitHubSourceCredentials(this, "CodeBuildGitHubCreds", {
accessToken: SecretValue.secretsManager("github-token"),
});
}
if (branch === "main" && env == "dev") {
// Common bucket for test reports
const reportBucket = new Bucket(this, "reportbucket", { bucketName: Config.reportBucketName });

Expand Down Expand Up @@ -93,7 +99,72 @@ export class HassuPipelineStack extends Stack {
cloneDepth: 0,
};
const gitHubSource = codebuild.Source.gitHub(sourceProps);
new codebuild.Project(this, "HassuProject", {
let environmentVariables: { [name: string]: BuildEnvironmentVariable } = {
ENVIRONMENT: { value: env },
VELHO_AUTH_URL: {
value: config.getInfraParameterPath("VelhoAuthenticationUrl", config.velhoEnv),
type: BuildEnvironmentVariableType.PARAMETER_STORE,
},
VELHO_API_URL: {
value: config.getInfraParameterPath("VelhoApiUrl", config.velhoEnv),
type: BuildEnvironmentVariableType.PARAMETER_STORE,
},
VELHO_USERNAME: { value: await config.getSecureInfraParameter("VelhoUsername", config.velhoEnv) },
VELHO_PASSWORD: { value: await config.getSecureInfraParameter("VelhoPassword", config.velhoEnv) },

PERSON_SEARCH_API_ACCOUNT_TYPES: {
value: config.getInfraParameterPath("PersonSearchApiAccountTypes"),
type: BuildEnvironmentVariableType.PARAMETER_STORE,
},
NEXT_PUBLIC_VAYLA_EXTRANET_URL: {
value: config.getInfraParameterPath("ExtranetHomePageUrl"),
type: BuildEnvironmentVariableType.PARAMETER_STORE,
},
NEXT_PUBLIC_VELHO_BASE_URL: {
value: config.getInfraParameterPath("VelhoBaseUrl", config.velhoEnv),
type: BuildEnvironmentVariableType.PARAMETER_STORE,
},
NODE_OPTIONS: {
value: "--max_old_space_size=4096 --max-old-space-size=4096",
type: BuildEnvironmentVariableType.PLAINTEXT,
},
};

if (env == "prod") {
environmentVariables = {
...environmentVariables,
PERSON_SEARCH_API_URL_PROD: {
value: "/PersonSearchApiURLProd",
type: BuildEnvironmentVariableType.PARAMETER_STORE,
},
PERSON_SEARCH_API_USERNAME_PROD: {
value: "/PersonSearchApiUsernameProd",
type: BuildEnvironmentVariableType.PARAMETER_STORE,
},
PERSON_SEARCH_API_PASSWORD_PROD: {
value: "/PersonSearchApiPasswordProd",
type: BuildEnvironmentVariableType.PARAMETER_STORE,
},
};
} else {
environmentVariables = {
...environmentVariables,
PERSON_SEARCH_API_URL: {
value: "/PersonSearchApiURL",
type: BuildEnvironmentVariableType.PARAMETER_STORE,
},
PERSON_SEARCH_API_USERNAME: {
value: "/PersonSearchApiUsername",
type: BuildEnvironmentVariableType.PARAMETER_STORE,
},
PERSON_SEARCH_API_PASSWORD: {
value: "/PersonSearchApiPassword",
type: BuildEnvironmentVariableType.PARAMETER_STORE,
},
};
}

const project = new codebuild.Project(this, "HassuProject", {
projectName: "Hassu-" + env,
buildSpec: BuildSpec.fromSourceFilename(buildspecFileName),
source: gitHubSource,
Expand All @@ -102,70 +173,25 @@ export class HassuPipelineStack extends Stack {
buildImage: LinuxBuildImage.STANDARD_5_0,
privileged: true,
computeType: ComputeType.MEDIUM,
environmentVariables: {
ENVIRONMENT: { value: env },
VELHO_AUTH_URL: {
value: config.getInfraParameterPath("VelhoAuthenticationUrl", config.velhoEnv),
type: BuildEnvironmentVariableType.PARAMETER_STORE,
},
VELHO_API_URL: {
value: config.getInfraParameterPath("VelhoApiUrl", config.velhoEnv),
type: BuildEnvironmentVariableType.PARAMETER_STORE,
},
VELHO_USERNAME: { value: await config.getSecureInfraParameter("VelhoUsername", config.velhoEnv) },
VELHO_PASSWORD: { value: await config.getSecureInfraParameter("VelhoPassword", config.velhoEnv) },

PERSON_SEARCH_API_URL: {
value: "/PersonSearchApiURL",
type: BuildEnvironmentVariableType.PARAMETER_STORE,
},
PERSON_SEARCH_API_URL_PROD: {
value: "/PersonSearchApiURLProd",
type: BuildEnvironmentVariableType.PARAMETER_STORE,
},
PERSON_SEARCH_API_USERNAME: {
value: "/PersonSearchApiUsername",
type: BuildEnvironmentVariableType.PARAMETER_STORE,
},
PERSON_SEARCH_API_PASSWORD: {
value: "/PersonSearchApiPassword",
type: BuildEnvironmentVariableType.PARAMETER_STORE,
},
PERSON_SEARCH_API_USERNAME_PROD: {
value: "/PersonSearchApiUsernameProd",
type: BuildEnvironmentVariableType.PARAMETER_STORE,
},
PERSON_SEARCH_API_PASSWORD_PROD: {
value: "/PersonSearchApiPasswordProd",
type: BuildEnvironmentVariableType.PARAMETER_STORE,
},
PERSON_SEARCH_API_ACCOUNT_TYPES: {
value: config.getInfraParameterPath("PersonSearchApiAccountTypes"),
type: BuildEnvironmentVariableType.PARAMETER_STORE,
},
NEXT_PUBLIC_VAYLA_EXTRANET_URL: {
value: config.getInfraParameterPath("ExtranetHomePageUrl"),
type: BuildEnvironmentVariableType.PARAMETER_STORE,
},
NEXT_PUBLIC_VELHO_BASE_URL: {
value: config.getInfraParameterPath("VelhoBaseUrl", config.velhoEnv),
type: BuildEnvironmentVariableType.PARAMETER_STORE,
},
NODE_OPTIONS: {
value: "--max_old_space_size=4096 --max-old-space-size=4096",
type: BuildEnvironmentVariableType.PLAINTEXT,
},
},
environmentVariables: environmentVariables,
},
grantReportGroupPermissions: true,
badge: true,
}).addToRolePolicy(
});
project.addToRolePolicy(
new PolicyStatement({
effect: Effect.ALLOW,
actions: ["s3:*", "cloudformation:*", "sts:*", "ecr:*", "ssm:*", "secretsmanager:GetSecretValue", "codebuild:StartBuild"],
resources: ["*"],
})
);
project.addToRolePolicy(
new PolicyStatement({
effect: Effect.ALLOW,
actions: ["iam:CreateServiceLinkedRole"],
resources: ["arn:aws:iam::*:role/aws-service-role/*"],
})
);
}

private async createE2eTestPipeline(env: string, config: Config) {
Expand Down
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,10 @@
"build": "next build",
"start": "next start",
"cypress": "cypress",
"generate:velhoapi": "MSYS_NO_PATHCONV=1 cross-env docker run --rm --env-file .env.test -e VELHO_AUTH_URL -e VELHO_API_URL -e VELHO_USERNAME -e VELHO_PASSWORD -e VELHO_ACCESS_TOKEN=\"$(ts-node --project=tsconfig.cdk.json -r dotenv/config ./tools/velho/authenticate.ts dotenv_config_path=.env.test)\" -v $INIT_CWD:/work 283563576583.dkr.ecr.eu-west-1.amazonaws.com/hassu-buildimage:1.0.3 /work/tools/velho/gradlew -p /work/tools/velho --info",
"generate:velhoapi": "MSYS_NO_PATHCONV=1 cross-env docker run --rm --env-file .env.test -e VELHO_AUTH_URL -e VELHO_API_URL -e VELHO_USERNAME -e VELHO_PASSWORD -e VELHO_ACCESS_TOKEN=\"$(ts-node --project=tsconfig.cdk.json -r dotenv/config ./tools/velho/authenticate.ts dotenv_config_path=.env.test)\" -v $INIT_CWD:/work ${ACCOUNT_ID:=283563576583}.dkr.ecr.eu-west-1.amazonaws.com/hassu-buildimage:1.0.3 /work/tools/velho/gradlew -p /work/tools/velho --info",
"postgenerate:velhoapi": "ts-node --project=tsconfig.cdk.json ./tools/velho/processMetaData.ts ./backend/src/velho/metadata.json",
"pregenerate:api": "graphql-schema-utilities -s \"{./graphql/types.graphql,./graphql/inputs.graphql,./graphql/operations.graphql}\" -o schema.graphql",
"generate:api": "cross-env docker run --rm -v $INIT_CWD:/work 283563576583.dkr.ecr.eu-west-1.amazonaws.com/hassu-buildimage:1.0.3 bash -c \"cd /work && amplify codegen\"",
"generate:api": "cross-env docker run --rm -v $INIT_CWD:/work ${ACCOUNT_ID:=283563576583}.dkr.ecr.eu-west-1.amazonaws.com/hassu-buildimage:1.0.3 bash -c \"cd /work && amplify codegen\"",
"generate": "npm-run-all --aggregate-output --parallel generate:*",
"lint:backend": "eslint --cache --ignore-path .gitignore --ext .ts backend",
"lint:app": "next lint",
Expand Down Expand Up @@ -197,9 +197,11 @@
"deploy:frontend": "cdk $NODE_DEBUG_OPTION --app \"ts-node --project=tsconfig.cdk.json ./deployment/bin/hassu\" deploy frontend --require-approval never",
"postdeploy:frontend": "npm run setupenvironment",
"deploy:account:dev": "cross-env ENVIRONMENT=dev cdk --app \"ts-node --project=tsconfig.cdk.json ./deployment/bin/hassu-account\" deploy account --require-approval never",
"deploy:account:prod": "cross-env ENVIRONMENT=prod cdk --app \"ts-node --project=tsconfig.cdk.json ./deployment/bin/hassu-account\" deploy account --require-approval never",
"deploy:pipeline": "cdk --app \"ts-node --project=tsconfig.cdk.json ./deployment/bin/hassu-pipeline\" deploy hassu-pipeline --require-approval never",
"deploy:pipeline:main": "cross-env ENVIRONMENT=dev BUILD_BRANCH=main cdk --app \"ts-node --project=tsconfig.cdk.json ./deployment/bin/hassu-pipeline\" deploy hassu-pipeline --require-approval never",
"deploy:pipeline:test": "cross-env ENVIRONMENT=test BUILD_BRANCH=test cdk --app \"ts-node --project=tsconfig.cdk.json ./deployment/bin/hassu-pipeline\" deploy hassu-pipeline --require-approval never",
"deploy:pipeline:prod": "cross-env ENVIRONMENT=prod BUILD_BRANCH=prod cdk --app \"ts-node --project=tsconfig.cdk.json ./deployment/bin/hassu-pipeline\" deploy hassu-pipeline --require-approval never",
"deploy:pipeline:feature": "cross-env ENVIRONMENT=feature cdk --app \"ts-node --project=tsconfig.cdk.json ./deployment/bin/hassu-pipeline\" deploy hassu-pipeline --require-approval never",
"pipeline:bootstrap": "ACCOUNT_ARN=\"$(ts-node --project=tsconfig.cdk.json deployment/bin/get-account-arn.ts)\" && cdk bootstrap --app \"ts-node --project=tsconfig.cdk.json ./deployment/bin/hassu-pipeline\" --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess $ACCOUNT_ARN",
"login": "aws sso login",
Expand All @@ -213,7 +215,7 @@
"deleteVelhoProjekti": "ts-node --project=backend/tsconfig.json -r dotenv/config ./backend/bin/deleteVelhoProjekti dotenv_config_path=.env.test",
"e2e:test:run": "cypress run -b chrome",
"e2e:test:open": "cypress open",
"ecrLogin": "aws ecr get-login-password --region eu-west-1 | docker login --username AWS --password-stdin 283563576583.dkr.ecr.eu-west-1.amazonaws.com",
"ecrLogin": "aws ecr get-login-password --region eu-west-1 | docker login --username AWS --password-stdin ${ACCOUNT_ID:=283563576583}.dkr.ecr.eu-west-1.amazonaws.com",
"updateBuildimage": "./deployment/bin/updateECRImages.sh",
"log:backend": "aws logs tail --follow --since 1h /aws/lambda/hassu-backend-$ENVIRONMENT",
"log:backend:dev": "aws logs tail --follow --since 1h /aws/lambda/hassu-backend-dev",
Expand Down

0 comments on commit 953de61

Please sign in to comment.