-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #926 from DFE-Digital/feat/228964/automate-content…
…ful-backups feat: Add process for automated contentful backups
- Loading branch information
Showing
17 changed files
with
293 additions
and
556 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
name: Contentful Backup | ||
on: | ||
schedule: | ||
- cron: 0 0 * * 1 | ||
workflow_dispatch: | ||
inputs: | ||
environment: | ||
description: Which Contentful environment to backup | ||
required: true | ||
type: choice | ||
options: [ 'Dev', 'Tst', 'Staging', 'Production' ] | ||
default: 'Staging' | ||
|
||
jobs: | ||
backup_content: | ||
runs-on: ubuntu-latest | ||
environment: ${{ inputs.environment || 'Staging' }} | ||
name: Backup content for ${{ inputs.environment || 'Staging' }} | ||
env: | ||
az_keyvault_name: ${{ secrets.AZ_ENVIRONMENT }}${{ secrets.DFE_PROJECT_NAME }}-kv | ||
az_resource_group_name: ${{ secrets.AZ_ENVIRONMENT }}${{ secrets.DFE_PROJECT_NAME }} | ||
az_sql_database_server_name: ${{ secrets.AZ_ENVIRONMENT }}${{ secrets.DFE_PROJECT_NAME }} | ||
az_keyvault_contentful_environment: contentful--environment | ||
az_keyvault_contentful_space_id: contentful--spaceid | ||
az_keyvault_contentful_delivery_token: contentful--deliveryapikey | ||
az_keyvault_contentful_backup_storage_key: contentful--backupstoragekey | ||
az_keyvault_contentful_management_token: contentful--managementtoken | ||
SQL_IP_NAME: export-processor | ||
USE_PREVIEW: true | ||
SAVE_FILE: true | ||
OUTPUT_FILE_DIR: "./output/" | ||
|
||
steps: | ||
- name: Checkout Repository | ||
uses: actions/checkout@v4 | ||
|
||
- name: Login with AZ | ||
uses: ./.github/actions/azure-login | ||
with: | ||
az_tenant_id: ${{ secrets.AZ_TENANT_ID }} | ||
az_subscription_id: ${{ secrets.AZ_SUBSCRIPTION_ID }} | ||
az_client_id: ${{ secrets.AZ_CLIENT_ID }} | ||
az_client_secret: ${{ secrets.AZ_CLIENT_SECRET }} | ||
|
||
- name: Get workflow IP address | ||
id: whats-my-ip | ||
uses: ./.github/actions/whats-my-ip-address | ||
|
||
- name: Add Azure firewall rules | ||
uses: ./.github/actions/azure-ip-whitelist | ||
with: | ||
ip_address: ${{ steps.whats-my-ip.outputs.ip }} | ||
verb: "add" | ||
az_keyvault_name: ${{ env.az_keyvault_name }} | ||
az_ip_name: ${{ env.SQL_IP_NAME }} | ||
az_resource_group: ${{ env.az_resource_group_name}} | ||
az_sql_database_server_name: ${{ env.az_sql_database_server_name }} | ||
|
||
- name: Get secrets | ||
id: get-contentful-export-secrets | ||
shell: bash | ||
run: | | ||
environment=$(az keyvault secret show --name ${{ env.az_keyvault_contentful_environment }} --vault-name ${{ env.az_keyvault_name }} --query value -o tsv) | ||
space_id=$(az keyvault secret show --name ${{ env.az_keyvault_contentful_space_id }} --vault-name ${{ env.az_keyvault_name }} --query value -o tsv) | ||
delivery_token=$(az keyvault secret show --name ${{ env.az_keyvault_contentful_delivery_token }} --vault-name ${{ env.az_keyvault_name }} --query value -o tsv) | ||
backup_storage_key=$(az keyvault secret show --name ${{ env.az_keyvault_contentful_backup_storage_key }} --vault-name ${{ env.az_keyvault_name }} --query value -o tsv) | ||
management_token=$(az keyvault secret show --name ${{ env.az_keyvault_contentful_management_token }} --vault-name ${{ env.az_keyvault_name }} --query value -o tsv) | ||
echo "::add-mask::$environment" | ||
echo "::add-mask::$space_id" | ||
echo "::add-mask::$delivery_token" | ||
echo "::add-mask::$backup_storage_key" | ||
echo "::add-mask::$management_token" | ||
echo "ENVIRONMENT=$environment" >> $GITHUB_ENV | ||
echo "SPACE_ID=$space_id" >> $GITHUB_ENV | ||
echo "DELIVERY_TOKEN=$delivery_token" >> $GITHUB_ENV | ||
echo "BACKUP_STORAGE_KEY=$backup_storage_key" >> $GITHUB_ENV | ||
echo "MANAGEMENT_TOKEN=$management_token" >> $GITHUB_ENV | ||
- name: Install contentful-exporter | ||
working-directory: ./contentful/export-processor | ||
run: npm i | ||
|
||
- name: Export contentful data | ||
working-directory: ./contentful/export-processor | ||
run: npm run export-all-only | ||
|
||
- name: Upload export to Azure storage container | ||
working-directory: ./contentful/export-processor | ||
run: | | ||
json_files=(output/**/*.json) | ||
backup="${json_files[0]}" | ||
account_name=$(echo "${az_resource_group_name}content" | sed 's/-//g') | ||
az storage blob upload \ | ||
--account-name "$account_name" \ | ||
--account-key "$BACKUP_STORAGE_KEY" \ | ||
--container-name backups-container \ | ||
--file "$backup" | ||
- name: Remove Azure Firewall Rules | ||
if: always() | ||
uses: ./.github/actions/azure-ip-whitelist | ||
with: | ||
ip_address: ${{ steps.what-is-my-ip.outputs.ip }} | ||
verb: "remove" | ||
az_keyvault_name: ${{ env.az_keyvault_name }} | ||
az_ip_name: ${{ env.SQL_IP_NAME }} | ||
az_resource_group: ${{ env.az_resource_group_name}} | ||
az_sql_database_server_name: ${{ env.az_sql_database_server_name }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
require("dotenv/config"); | ||
const getClient = require("./helpers/get-client"); | ||
const validateEnvironment = require("./helpers/validate-environment"); | ||
const deleteContentfulContent = require("./helpers/delete-all-content-and-content-types"); | ||
|
||
async function deleteAllContentfulData() { | ||
const client = await getClient(); | ||
const contentTypesToDelete = process.env.CONTENT_TYPES_TO_DELETE?.split(","); | ||
|
||
console.log(`Deleting ${process.env.CONTENT_TYPES_TO_DELETE ?? 'all data'} from the following environment: ${process.env.ENVIRONMENT}`) | ||
|
||
try { | ||
await deleteContentfulContent({ client, contentTypesToDelete }); | ||
console.log(`Content deleted successfully for ${process.env.ENVIRONMENT}`); | ||
} catch (error) { | ||
console.error('Error during deletion:', error); | ||
throw error; | ||
} | ||
} | ||
|
||
deleteAllContentfulData(); |
65 changes: 65 additions & 0 deletions
65
contentful/content-management/helpers/delete-all-content-and-content-types.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/** | ||
* Deletes content from Contentful based on the given client and content types. | ||
* | ||
* @param {Object} options - The options for deleting content. | ||
* @param {contentful.ClientAPI} options.client - The Contentful client. | ||
* @param {import('./types').ContentfulEnvironmentOptions} options.envOptions - Environment options for the Contentful client. | ||
* @param {(string[] | undefined)} options.contentTypes - The array of content types to delete. If undefined, will delete _all_ content | ||
* @return {Promise} A promise that resolves when the content is deleted. | ||
*/ | ||
module.exports = async function deleteContentfulContent({ client, contentTypes }) { | ||
if (!client) { | ||
throw `Contentful client not provided`; | ||
} | ||
|
||
if (!contentTypes || !Array.isArray(contentTypes) || contentTypes.length == 0) { | ||
return await deleteContentFromContentful({ client }); | ||
} | ||
else { | ||
return await deleteContentByContentTypes({ client, contentTypes }); | ||
} | ||
} | ||
|
||
async function deleteContentByContentTypes({ client, contentTypes }) { | ||
for (const contentType of contentTypes) { | ||
console.log(`Deleting content for ${contentType}`); | ||
await deleteContentFromContentful({ client, query: { content_type: contentType } }); | ||
} | ||
} | ||
|
||
async function deleteContentFromContentful({ client, envOptions, query }) { | ||
let limit = 100; | ||
|
||
while (true) { | ||
const entries = await client.entry.getMany({ | ||
query: { | ||
skip: 0, | ||
limit: limit, | ||
...query | ||
}, | ||
}); | ||
|
||
if (entries.items.length == 0) { | ||
break; | ||
} | ||
|
||
console.log(`Deleting ${entries.items.length} items out of ${entries.total}`); | ||
|
||
for (const entry of entries.items) { | ||
console.log(entry) | ||
await unpublishAndDeleteEntry({ entry, client }); | ||
} | ||
} | ||
} | ||
|
||
async function unpublishAndDeleteEntry({ entry, client }) { | ||
if (entry.sys.publishedVersion) { | ||
await client.entry.unpublish({ | ||
entryId: entry.sys.id, | ||
}); | ||
} | ||
|
||
await client.entry.delete({ | ||
entryId: entry.sys.id | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
contentful/content-management/helpers/validate-environment.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
/** | ||
* Verifies that the environment specified in .env is a valid contentful environment | ||
* This is important because if it isn't, the management api fails silently | ||
* and falls back to using the master environment | ||
* | ||
* @param {ClientAPI | null} client | ||
*/ | ||
module.exports = async function validateEnvironment(client) { | ||
const environments = await client.environment.getMany({ | ||
spaceId: process.env.SPACE_ID, | ||
}); | ||
const validNames = environments.items.map((env) => env.name); | ||
if (!validNames.includes(process.env.ENVIRONMENT)) { | ||
throw new Error(`Invalid Contentful environment`); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.