|
| 1 | +# IBM Cloud Code Engine - Integrate Cloud Object Storage and PostgreSQL through a app and an event subscription |
| 2 | + |
| 3 | +This sample demonstrates how to read CSV files hosted on a IBM Cloud Object Storage and save their contents line by line into relational PostgreSQL database, leveraging IAM trusted profiles. |
| 4 | + |
| 5 | + |
| 6 | + |
| 7 | +## Prerequisites |
| 8 | + |
| 9 | +Make sure the [IBM Cloud CLI](https://cloud.ibm.com/docs/cli/reference/ibmcloud?topic=cloud-cli-getting-started) and the following list of plugins are installed |
| 10 | +- `ibmcloud plugin install code-engine` |
| 11 | +- `ibmcloud plugin install cloud-object-storage` |
| 12 | +- `ibmcloud plugin install secrets-manager` |
| 13 | + |
| 14 | +Install `jq`. On MacOS, you can use following [brew formulae](https://formulae.brew.sh/formula/jq) to do a `brew install jq`. |
| 15 | + |
| 16 | +## Setting up all IBM Cloud Service instances |
| 17 | + |
| 18 | +* Login to IBM Cloud via the CLI and target the `ca-tor` region: |
| 19 | + ``` |
| 20 | + REGION=ca-tor |
| 21 | + RESOURCE_GROUP=Default |
| 22 | + ibmcloud login -r ${REGION} -g ${RESOURCE_GROUP} |
| 23 | + ``` |
| 24 | +
|
| 25 | +* Create the Code Engine project |
| 26 | + ``` |
| 27 | + CE_INSTANCE_NAME=cos-to-sql--ce |
| 28 | + ibmcloud code-engine project create --name ${CE_INSTANCE_NAME} |
| 29 | +
|
| 30 | + CE_INSTANCE_GUID=$(ibmcloud ce project current -o json | jq -r .guid) |
| 31 | + CE_INSTANCE_ID=$(ibmcloud resource service-instance ${CE_INSTANCE_NAME} --output json | jq -r '.[0] | .id') |
| 32 | + ``` |
| 33 | +
|
| 34 | +* Create the COS instance |
| 35 | + ``` |
| 36 | + COS_INSTANCE_NAME=cos-to-sql--cos |
| 37 | + ibmcloud resource service-instance-create ${COS_INSTANCE_NAME} cloud-object-storage standard global |
| 38 | +
|
| 39 | + COS_INSTANCE_ID=$(ibmcloud resource service-instance ${COS_INSTANCE_NAME} --output json | jq -r '.[0] | .id') |
| 40 | + ``` |
| 41 | +
|
| 42 | +* Create a COS bucket |
| 43 | + ``` |
| 44 | + ibmcloud cos config crn --crn ${COS_INSTANCE_ID} --force |
| 45 | + ibmcloud cos config auth --method IAM |
| 46 | + ibmcloud cos config region --region ${REGION} |
| 47 | + ibmcloud cos config endpoint-url --url s3.${REGION}.cloud-object-storage.appdomain.cloud |
| 48 | + COS_BUCKET_NAME=${CE_INSTANCE_GUID}-csv-to-sql |
| 49 | + ibmcloud cos bucket-create \ |
| 50 | + --class smart \ |
| 51 | + --bucket $COS_BUCKET_NAME |
| 52 | + ``` |
| 53 | +
|
| 54 | +* Create the PostgreSQL instance |
| 55 | + ``` |
| 56 | + DB_INSTANCE_NAME=cos-to-sql--pg |
| 57 | + ibmcloud resource service-instance-create $DB_INSTANCE_NAME databases-for-postgresql standard ${REGION} --service-endpoints private -p \ |
| 58 | + '{ |
| 59 | + "disk_encryption_instance_crn": "none", |
| 60 | + "disk_encryption_key_crn": "none", |
| 61 | + "members_cpu_allocation_count": "0 cores", |
| 62 | + "members_disk_allocation_mb": "10240MB", |
| 63 | + "members_host_flavor": "multitenant", |
| 64 | + "members_members_allocation_count": 2, |
| 65 | + "members_memory_allocation_mb": "8192MB", |
| 66 | + "service-endpoints": "private", |
| 67 | + "version": "16" |
| 68 | + }' |
| 69 | +
|
| 70 | + DB_INSTANCE_ID=$(ibmcloud resource service-instance $DB_INSTANCE_NAME --location ${REGION} --output json | jq -r '.[0] | .id') |
| 71 | + ``` |
| 72 | +
|
| 73 | +* Create the Secrets Manager instance. **Note:** To be able to create secret through the CLI running on your local workstation, we are creating the SecretsManager instance with private and public endpoints enabled. For production use, we strongly recommend to specify `allowed_network: private-only` |
| 74 | + ``` |
| 75 | + SM_INSTANCE_NAME=cos-to-sql--sm |
| 76 | + ibmcloud resource service-instance-create $SM_INSTANCE_NAME secrets-manager 7713c3a8-3be8-4a9a-81bb-ee822fcaac3d ${REGION} -p \ |
| 77 | + '{ |
| 78 | + "allowed_network": "public-and-private" |
| 79 | + }' |
| 80 | +
|
| 81 | + SM_INSTANCE_ID=$(ibmcloud resource service-instance $SM_INSTANCE_NAME --location ${REGION} --output json | jq -r '.[0] | .id') |
| 82 | + SM_INSTANCE_GUID=$(ibmcloud resource service-instance $SM_INSTANCE_NAME --location ${REGION} --output json | jq -r '.[0] | .guid') |
| 83 | + SECRETS_MANAGER_URL_PRIVATE=https://${SM_INSTANCE_GUID}.private.${REGION}.secrets-manager.appdomain.cloud |
| 84 | + ``` |
| 85 | +
|
| 86 | +* Create a S2S policy "Key Manager" between SM and the DB |
| 87 | + ``` |
| 88 | + ibmcloud iam authorization-policy-create secrets-manager databases-for-postgresql \ |
| 89 | + "Key Manager" \ |
| 90 | + --source-service-instance-id $SM_INSTANCE_ID \ |
| 91 | + --target-service-instance-id $DB_INSTANCE_ID |
| 92 | + ``` |
| 93 | +
|
| 94 | +* Create the service credential to access the PostgreSQL instance |
| 95 | + ``` |
| 96 | + SM_SECRET_FOR_PG_NAME=pg-access-credentials |
| 97 | + ibmcloud secrets-manager secret-create \ |
| 98 | + --secret-type="service_credentials" \ |
| 99 | + --secret-name="$SM_SECRET_FOR_PG_NAME" \ |
| 100 | + --secret-source-service="{\"instance\": {\"crn\": \"$DB_INSTANCE_ID\"},\"parameters\": {},\"role\": {\"crn\": \"crn:v1:bluemix:public:iam::::serviceRole:Writer\"}}" |
| 101 | +
|
| 102 | + SM_SECRET_FOR_PG_ID=$(ibmcloud sm secret-by-name --name $SM_SECRET_FOR_PG_NAME --secret-type service_credentials --secret-group-name default --instance-id $SM_INSTANCE_GUID --region $REGION --output JSON|jq -r '.id') |
| 103 | + ``` |
| 104 | +
|
| 105 | +* Create the Code Engine app: |
| 106 | + ``` |
| 107 | + CE_APP_NAME=csv-to-sql |
| 108 | + TRUSTED_PROFILE_FOR_COS_NAME=cos-to-sql--ce-to-cos-access |
| 109 | + TRUSTED_PROFILE_FOR_SM_NAME=cos-to-sql--ce-to-sm-access |
| 110 | +
|
| 111 | + ibmcloud code-engine app create \ |
| 112 | + --name ${CE_APP_NAME} \ |
| 113 | + --build-source https://github.com/IBM/CodeEngine \ |
| 114 | + --build-context-dir cos-to-sql/ \ |
| 115 | + --trusted-profiles-enabled="true" \ |
| 116 | + --probe-ready type=http \ |
| 117 | + --probe-ready path=/readiness \ |
| 118 | + --probe-ready interval=30 \ |
| 119 | + --env COS_REGION=${REGION} \ |
| 120 | + --env COS_TRUSTED_PROFILE_NAME=${TRUSTED_PROFILE_FOR_COS_NAME} \ |
| 121 | + --env SM_TRUSTED_PROFILE_NAME=${TRUSTED_PROFILE_FOR_SM_NAME} \ |
| 122 | + --env SM_SERVICE_URL=${SECRETS_MANAGER_URL_PRIVATE} \ |
| 123 | + --env SM_PG_SECRET_ID=${SM_SECRET_FOR_PG_ID} |
| 124 | + ``` |
| 125 | +
|
| 126 | +## Trusted Profile setup |
| 127 | +
|
| 128 | +* Create a trusted profile that grants a Code Engine app access to your COS bucket |
| 129 | + ``` |
| 130 | + ibmcloud iam trusted-profile-create ${TRUSTED_PROFILE_FOR_COS_NAME} |
| 131 | + ibmcloud iam trusted-profile-link-create ${TRUSTED_PROFILE_FOR_COS_NAME} \ |
| 132 | + --name ce-app-${CE_APP_NAME} \ |
| 133 | + --cr-type CE --link-crn ${CE_INSTANCE_ID} \ |
| 134 | + --link-component-type application \ |
| 135 | + --link-component-name ${CE_APP_NAME} |
| 136 | + ibmcloud iam trusted-profile-policy-create ${TRUSTED_PROFILE_FOR_COS_NAME} \ |
| 137 | + --roles "Content Reader" \ |
| 138 | + --service-name cloud-object-storage \ |
| 139 | + --service-instance ${COS_INSTANCE_ID} \ |
| 140 | + --resource-type bucket \ |
| 141 | + --resource ${COS_BUCKET_NAME} |
| 142 | + ``` |
| 143 | +
|
| 144 | +
|
| 145 | +* Create the trusted profile to access Secrets Manager |
| 146 | + ``` |
| 147 | + ibmcloud iam trusted-profile-create ${TRUSTED_PROFILE_FOR_SM_NAME} |
| 148 | + ibmcloud iam trusted-profile-link-create ${TRUSTED_PROFILE_FOR_SM_NAME} \ |
| 149 | + --name ce-app-${CE_APP_NAME} \ |
| 150 | + --cr-type CE --link-crn ${CE_INSTANCE_ID} \ |
| 151 | + --link-component-type application \ |
| 152 | + --link-component-name ${CE_APP_NAME} |
| 153 | + ibmcloud iam trusted-profile-policy-create ${TRUSTED_PROFILE_FOR_SM_NAME} \ |
| 154 | + --roles "SecretsReader" \ |
| 155 | + --service-name secrets-manager \ |
| 156 | + --service-instance ${SM_INSTANCE_ID} |
| 157 | + ``` |
| 158 | +
|
| 159 | +## Setting up eventing |
| 160 | +
|
| 161 | +* Create an authorization policy to allow the Code Engine project receive events from COS: |
| 162 | + ``` |
| 163 | + ibmcloud iam authorization-policy-create codeengine cloud-object-storage \ |
| 164 | + "Notifications Manager" \ |
| 165 | + --source-service-instance-id ${CE_INSTANCE_ID} \ |
| 166 | + --target-service-instance-id ${COS_INSTANCE_ID} |
| 167 | + ``` |
| 168 | +
|
| 169 | +* Create the subscription for COS events of type "write": |
| 170 | + ``` |
| 171 | + ibmcloud ce sub cos create \ |
| 172 | + --name "coswatch-${CE_APP_NAME}" \ |
| 173 | + --bucket ${COS_BUCKET_NAME} \ |
| 174 | + --event-type "write" \ |
| 175 | + --destination ${CE_APP_NAME} \ |
| 176 | + --destination-type app \ |
| 177 | + --path /cos-to-sql |
| 178 | + ``` |
| 179 | +
|
| 180 | +## Verify the solution |
| 181 | +
|
| 182 | +* Upload a CSV file to COS, to initate an event that leads to a job execution: |
| 183 | + ``` |
| 184 | + curl --silent --location --request GET 'https://raw.githubusercontent.com/IBM/CodeEngine/main/cos-to-sql/samples/users.csv' > CodeEngine-sample-users.csv |
| 185 | +
|
| 186 | + cat CodeEngine-sample-users.csv |
| 187 | +
|
| 188 | + ibmcloud cos object-put \ |
| 189 | + --bucket ${COS_BUCKET_NAME} \ |
| 190 | + --key users.csv \ |
| 191 | + --body ./CodeEngine-sample-users.csv \ |
| 192 | + --content-type text/csv |
| 193 | + ``` |
| 194 | +
|
| 195 | +* Inspect the app execution by opening the logs: |
| 196 | + ``` |
| 197 | + ibmcloud code-engine app logs \ |
| 198 | + --name ${CE_APP_NAME} \ |
| 199 | + --follow |
| 200 | + ``` |
0 commit comments