Skip to content

Commit ea01433

Browse files
authored
Merge pull request #180 from IBM/cos-to-sql
Sample that demonstrates how to convert CSV files hosted on COS to data stored in a RDBMS
2 parents 5bb70cb + b95212a commit ea01433

16 files changed

+2918
-2
lines changed

.gitignore

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
*.out
2-
.vscode
3-
node_modules
2+
node_modules
3+
.DS_Store
4+
.vscode

cos-to-sql/.ceignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
samples
3+
docs
4+
.DS_Store

cos-to-sql/.dockerignore

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.dockerignore
2+
.gitignore
3+
build
4+
Dockerfile
5+
node_modules
6+
samples
7+
build

cos-to-sql/Dockerfile

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Download dependencies in builder stage
2+
FROM registry.access.redhat.com/ubi9/nodejs-22:latest AS builder
3+
4+
COPY --chown=${CNB_USER_ID}:${CNB_GROUP_ID} package.json package-lock.json /app/
5+
WORKDIR /app
6+
RUN npm ci --omit=dev
7+
8+
9+
# Use a small distroless image for as runtime image
10+
FROM gcr.io/distroless/nodejs22
11+
12+
COPY --chown=1001:0 --from=builder /app/node_modules /app/node_modules
13+
COPY --chown=1001:0 . /app/
14+
15+
USER 1001:0
16+
WORKDIR /app
17+
EXPOSE 8080
18+
19+
CMD ["app.mjs"]

cos-to-sql/README.md

+200
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
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+
![Architecture overview](./docs/trusted-profiles-part2-arch-overview.png)
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

Comments
 (0)