diff --git a/.github/workflows/flow-gcs-test.yaml b/.github/workflows/flow-gcs-test.yaml index ba339c3a6..4fc2d1b68 100644 --- a/.github/workflows/flow-gcs-test.yaml +++ b/.github/workflows/flow-gcs-test.yaml @@ -68,6 +68,10 @@ jobs: export BUCKET_NAME=${{ steps.jobs.outputs.job_id }}-solo-streams gcloud storage buckets create gs://${BUCKET_NAME} --project=${{ vars.GCP_S3_PROJECT_ID }} echo "BUCKET_NAME=${BUCKET_NAME}" >> $GITHUB_ENV + + export BACKUP_BUCKET_NAME=${{ steps.jobs.outputs.job_id }}-solo-backups + gcloud storage buckets create gs://${BACKUP_BUCKET_NAME} --project=${{ vars.GCP_S3_PROJECT_ID }} + echo "BACKUP_BUCKET_NAME=${BACKUP_BUCKET_NAME}" >> $GITHUB_ENV - name: Setup Node uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 @@ -99,15 +103,18 @@ jobs: - name: Compile Project run: npm run build - - name: Run GCS Test Script for type ${{ matrix.channel }} + - name: Run GCS Test Script for type ${{ matrix.storageType }} env: GCS_ACCESS_KEY: ${{ secrets.GCP_S3_ACCESS_KEY }} GCS_SECRET_KEY: ${{ secrets.GCP_S3_SECRET_KEY }} BUCKET_NAME: ${{ env.BUCKET_NAME }} + BACKUP_BUCKET_NAME: ${{ env.BACKUP_BUCKET_NAME }} STORAGE_TYPE: ${{ matrix.storageType }} + GCP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.GCP_SERVICE_ACCOUNT_TOKEN }} run: | .github/workflows/script/gcs_test.sh - name: Delete Bucket after Test run: | gcloud storage rm --recursive gs://${BUCKET_NAME} --project=${{ vars.GCP_S3_PROJECT_ID }} + gcloud storage rm --recursive gs://${BACKUP_BUCKET_NAME} --project=${{ vars.GCP_S3_PROJECT_ID }} diff --git a/.github/workflows/script/gcs_test.sh b/.github/workflows/script/gcs_test.sh index 1a773134c..298457eed 100755 --- a/.github/workflows/script/gcs_test.sh +++ b/.github/workflows/script/gcs_test.sh @@ -19,12 +19,25 @@ else streamBucket=${BUCKET_NAME} fi +if [ -z "${BACKUP_BUCKET_NAME}" ]; then + streamBackupBucket="solo-ci-backups" +else + streamBackupBucket=${BACKUP_BUCKET_NAME} +fi + if [ -z "${STORAGE_TYPE}" ]; then storageType="gcs_and_minio" else storageType=${STORAGE_TYPE} fi +if [ -z "${GCP_SERVICE_ACCOUNT_TOKEN}" ]; then + echo "GCP_SERVICE_ACCOUNT_TOKEN is not set. Exiting..." + exit 1 +fi + +echo "${GCP_SERVICE_ACCOUNT_TOKEN}" > gcp_service_account.json + echo "Using bucket name: ${streamBucket}" echo "Test storage type: ${storageType}" @@ -41,7 +54,9 @@ npm run solo-test -- node keys --gossip-keys --tls-keys -i node1 npm run solo-test -- network deploy -i node1 -n "${SOLO_NAMESPACE}" \ --storage-endpoint "https://storage.googleapis.com" \ --storage-access-key "${GCS_ACCESS_KEY}" --storage-secrets "${GCS_SECRET_KEY}" \ - --storage-type "${storageType}" --storage-bucket "${streamBucket}" + --storage-type "${storageType}" --storage-bucket "${streamBucket}" \ + --backup-bucket "${streamBackupBucket}" \ + --google-credential gcp_service_account.json npm run solo-test -- node setup -i node1 -n "${SOLO_NAMESPACE}" npm run solo-test -- node start -i node1 -n "${SOLO_NAMESPACE}" @@ -58,3 +73,14 @@ cd ..; create_test_account ; cd - node examples/create-topic.js npm run solo-test -- node stop -i node1 -n "${SOLO_NAMESPACE}" + +# manually call script "backup.sh" from container backup-uploader since it only runs every 5 minutes +kubectl exec network-node1-0 -c backup-uploader -n solo-e2e -- /backup.sh > /dev/null 2>&1 + +# retrieve logs and check if it include the message type "error" +# example : {"level":"error","msg":"Updated modification time ......} +kubectl logs network-node1-0 -c backup-uploader -n solo-e2e > backup-uploader.log +if grep -q \""error\"" backup-uploader.log; then + echo "Backup uploader logs contain error message" + exit 1 +fi diff --git a/docs/content/User/SDK.md b/docs/content/User/SDK.md index cbb5ef1f8..af390873e 100644 --- a/docs/content/User/SDK.md +++ b/docs/content/User/SDK.md @@ -3,7 +3,6 @@ First, please follow solo repository README to install solo and Docker Desktop. You also need to install the Taskfile tool following the instructions [here](https://taskfile.dev/installation/). - Then we start with launching a local Solo network with the following commands: ```bash diff --git a/src/commands/flags.ts b/src/commands/flags.ts index e5f54b9a7..2c92ed4cd 100644 --- a/src/commands/flags.ts +++ b/src/commands/flags.ts @@ -1675,6 +1675,28 @@ export class Flags { prompt: undefined, }; + static readonly backupBucket: CommandFlag = { + constName: 'backupBucket', + name: 'backup-bucket', + definition: { + defaultValue: '', + describe: 'name of bucket for backing up state files', + type: 'string', + }, + prompt: undefined, + }; + + static readonly googleCredential: CommandFlag = { + constName: 'googleCredential', + name: 'google-credential', + definition: { + defaultValue: '', + describe: 'path of google credential file in json format', + type: 'string', + }, + prompt: undefined, + }; + static readonly loadBalancerEnabled: CommandFlag = { constName: 'loadBalancerEnabled', name: 'load-balancer', @@ -1769,6 +1791,8 @@ export class Flags { Flags.storageSecrets, Flags.storageEndpoint, Flags.storageBucket, + Flags.backupBucket, + Flags.googleCredential, Flags.tlsClusterIssuerType, Flags.tlsPrivateKey, Flags.tlsPublicKey, diff --git a/src/commands/network.ts b/src/commands/network.ts index 573875fa4..351448ac6 100644 --- a/src/commands/network.ts +++ b/src/commands/network.ts @@ -76,6 +76,8 @@ export interface NetworkDeployConfigClass { storageSecrets: string; storageEndpoint: string; storageBucket: string; + backupBucket: string; + googleCredential: string; } export class NetworkCommand extends BaseCommand { @@ -144,6 +146,8 @@ export class NetworkCommand extends BaseCommand { flags.storageSecrets, flags.storageEndpoint, flags.storageBucket, + flags.backupBucket, + flags.googleCredential, ]; } @@ -207,6 +211,23 @@ export class NetworkCommand extends BaseCommand { `failed to create Kubernetes secret for storage credentials of type '${config.storageType}'`, ); } + // generate backup uploader secret + if (config.googleCredential) { + const backupData = {}; + const googleCredential = fs.readFileSync(config.googleCredential, 'utf8'); + backupData['saJson'] = Base64.encode(googleCredential); + const isBackupSecretCreated = await this.k8.createSecret( + constants.BACKUP_SECRET_NAME, + namespace, + 'Opaque', + backupData, + undefined, + true, + ); + if (!isBackupSecretCreated) { + throw new SoloError(`failed to create Kubernetes secret for backup uploader of type '${config.storageType}'`); + } + } } catch (e: Error | any) { const errorMessage = 'failed to create Kubernetes storage secret '; this.logger.error(errorMessage, e); @@ -231,6 +252,8 @@ export class NetworkCommand extends BaseCommand { storageSecrets: string; storageEndpoint: string; storageBucket: string; + backupBucket: string; + googleCredential: string; loadBalancerEnabled: boolean; }) { let valuesArg = config.chartDirectory @@ -279,6 +302,12 @@ export class NetworkCommand extends BaseCommand { valuesArg += ` --set cloud.buckets.streamBucket=${config.storageBucket}`; valuesArg += ` --set minio-server.tenant.buckets[0].name=${config.storageBucket}`; } + + if (config.backupBucket) { + valuesArg += ' --set defaults.sidecars.backupUploader.enabled=true'; + valuesArg += ` --set defaults.sidecars.backupUploader.config.backupBucket=${config.backupBucket}`; + } + const profileName = this.configManager.getFlag(flags.profileName) as string; this.profileValuesFile = await this.profileManager.prepareValuesForSoloChart(profileName); if (this.profileValuesFile) { diff --git a/src/core/constants.ts b/src/core/constants.ts index 9daaaef33..4f1371a28 100644 --- a/src/core/constants.ts +++ b/src/core/constants.ts @@ -215,6 +215,7 @@ export const IGNORED_NODE_ACCOUNT_ID = '0.0.0'; export const UPLOADER_SECRET_NAME = 'uploader-mirror-secrets'; export const MINIO_SECRET_NAME = 'minio-secrets'; +export const BACKUP_SECRET_NAME = 'backup-uploader-secrets'; export const enum StorageType { MINIO_ONLY = 'minio_only', diff --git a/test/e2e/commands/network.test.ts b/test/e2e/commands/network.test.ts index 4c5acba64..71570d36c 100644 --- a/test/e2e/commands/network.test.ts +++ b/test/e2e/commands/network.test.ts @@ -105,6 +105,7 @@ describe('NetworkCommand', () => { flags.storageAccessKey.constName, flags.storageSecrets.constName, flags.storageEndpoint.constName, + flags.googleCredential.constName, ]); } catch (e) { networkCmd.logger.showUserError(e); diff --git a/test/test_util.ts b/test/test_util.ts index 4107cdaf2..11309101a 100644 --- a/test/test_util.ts +++ b/test/test_util.ts @@ -292,6 +292,7 @@ export function e2eTestSuite( flags.storageAccessKey.constName, flags.storageSecrets.constName, flags.storageEndpoint.constName, + flags.googleCredential.constName, ]); }).timeout(Duration.ofMinutes(5).toMillis()); diff --git a/version.ts b/version.ts index 6f63efbcc..f55f68c8f 100644 --- a/version.ts +++ b/version.ts @@ -20,7 +20,7 @@ */ export const HELM_VERSION = 'v3.14.2'; -export const SOLO_CHART_VERSION = '0.41.0'; +export const SOLO_CHART_VERSION = '0.42.0'; export const HEDERA_PLATFORM_VERSION = 'v0.58.1'; export const MIRROR_NODE_VERSION = '0.118.1'; export const HEDERA_EXPLORER_VERSION = '0.2.1';