From 60efc8f59d5756b3352eb97765ae2252865a6967 Mon Sep 17 00:00:00 2001 From: Enrico Regge Date: Mon, 4 Aug 2025 00:04:27 +0200 Subject: [PATCH 1/3] adding a private path sample --- .gitignore | 6 +- cloudant-change-listener/Dockerfile | 4 +- cloudant-change-listener/job/package.json | 4 +- private-path-to-vpc-vsi/README.md | 57 ++ private-path-to-vpc-vsi/build | 23 + private-path-to-vpc-vsi/ce-app/Dockerfile | 10 + private-path-to-vpc-vsi/ce-app/go.mod | 5 + private-path-to-vpc-vsi/ce-app/go.sum | 2 + private-path-to-vpc-vsi/ce-app/main.go | 103 ++++ private-path-to-vpc-vsi/ce-job/Dockerfile | 14 + private-path-to-vpc-vsi/ce-job/job.mjs | 32 ++ .../ce-job/package-lock.json | 176 +++++++ private-path-to-vpc-vsi/ce-job/package.json | 18 + ...ne-private-path---component-diagram.drawio | 286 ++++++++++ ...ngine-private-path---component-diagram.png | Bin 0 -> 116985 bytes ...de-engine-private-path---database-flow.png | Bin 0 -> 73149 bytes private-path-to-vpc-vsi/run | 496 ++++++++++++++++++ .../userdata-vsi-originserver.sh | 38 ++ 18 files changed, 1269 insertions(+), 5 deletions(-) create mode 100644 private-path-to-vpc-vsi/README.md create mode 100755 private-path-to-vpc-vsi/build create mode 100644 private-path-to-vpc-vsi/ce-app/Dockerfile create mode 100644 private-path-to-vpc-vsi/ce-app/go.mod create mode 100644 private-path-to-vpc-vsi/ce-app/go.sum create mode 100644 private-path-to-vpc-vsi/ce-app/main.go create mode 100644 private-path-to-vpc-vsi/ce-job/Dockerfile create mode 100644 private-path-to-vpc-vsi/ce-job/job.mjs create mode 100644 private-path-to-vpc-vsi/ce-job/package-lock.json create mode 100644 private-path-to-vpc-vsi/ce-job/package.json create mode 100644 private-path-to-vpc-vsi/docs/code-engine-private-path---component-diagram.drawio create mode 100644 private-path-to-vpc-vsi/docs/code-engine-private-path---component-diagram.png create mode 100644 private-path-to-vpc-vsi/docs/code-engine-private-path---database-flow.png create mode 100755 private-path-to-vpc-vsi/run create mode 100755 private-path-to-vpc-vsi/userdata-vsi-originserver.sh diff --git a/.gitignore b/.gitignore index 0739eb8a7..ff7161e57 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,8 @@ *.out node_modules .DS_Store -.vscode \ No newline at end of file +.vscode + +# draw.io temp files +.$*.bkp +.$*.dtmp \ No newline at end of file diff --git a/cloudant-change-listener/Dockerfile b/cloudant-change-listener/Dockerfile index 865c194a4..d2a4f8650 100644 --- a/cloudant-change-listener/Dockerfile +++ b/cloudant-change-listener/Dockerfile @@ -1,10 +1,10 @@ -FROM registry.access.redhat.com/ubi9/nodejs-20:latest AS build-env +FROM registry.access.redhat.com/ubi9/nodejs-22:latest AS build-env WORKDIR /opt/app-root/src COPY --chown=default:root job/* . RUN npm install # Use a small distroless image for as runtime image -FROM gcr.io/distroless/nodejs20-debian12 +FROM gcr.io/distroless/nodejs22 COPY --from=build-env /opt/app-root/src /app WORKDIR /app ENTRYPOINT ["job.mjs"] \ No newline at end of file diff --git a/cloudant-change-listener/job/package.json b/cloudant-change-listener/job/package.json index 5a6f9f1b0..9415f813c 100644 --- a/cloudant-change-listener/job/package.json +++ b/cloudant-change-listener/job/package.json @@ -4,8 +4,8 @@ "description": "Change event listener for cloudant DB", "main": "job.mjs", "engines": { - "node": "^20", - "npm": "^10" + "node": "^22", + "npm": "^11" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" diff --git a/private-path-to-vpc-vsi/README.md b/private-path-to-vpc-vsi/README.md new file mode 100644 index 000000000..b996dba1b --- /dev/null +++ b/private-path-to-vpc-vsi/README.md @@ -0,0 +1,57 @@ +# Connect your Code Engine workload to IBM VPC infrastructure using Private Path + +The script provided in this folder installs an end-to-end working solution, which showcases how workload deployed on IBM Cloud Code Engine can connect to backends (e.g. databases, nginx, docling-serve) hosted on IBM VPC infrastructure. + +Mainly, this sample covers the following typical use case: +* As a user I want to access my database backend on TCP port XYZ from workload deployed on Code Engine, without exposing my VPC to the public internet. + +**Use case: Database backend** + +To simulate the database use case the script deploys a PostgreSQL database on the origin server VSI, a Code Engine app (see [Golang source code](./ce-app/main.go)), and a Code Engine job (see [Node.js source code](./ce-job/job.mjs)). Each job run instance will issue a single SQL insert statement storing a random greeting message in the database. The purpose of the application is to query all existing records and expose them as a JSON object through its HTTPS endpoint. The request flows look as depicted in the diagram below: + +![Database request flow](./docs/code-engine-private-path---database-flow.png) + +_Please note: The custom domain mentioned in the flow is not part of this scripted sample, but is a worthful addition in real-world solutions. Custom domains can be easily configured through Code Engine domain mappings (see https://cloud.ibm.com/docs/codeengine?topic=codeengine-app-domainmapping for further information)_ + +Following diagram depicts the component overview of the sample solution: +![Component diagram](./docs/code-engine-private-path---component-diagram.png) + +To learn more about Private Path services, please refer to https://cloud.ibm.com/docs/vpc?topic=vpc-private-path-service-intro + +## Lets get started + +To run this end-to-end sample, open a terminal, [login into your IBM Cloud account using the IBM Cloud CLI](https://cloud.ibm.com/docs/codeengine?topic=codeengine-install-cli) and execute the following command: +``` +./run +``` + +The script deletes all created resources right after the sample scenario has been verified by the script. However, for playing around with the setup, the cleanup can be skipped using the environment variable `CLEANUP_ON_SUCCESS`: +``` +CLEANUP_ON_SUCCESS=false ./run +``` + +Per default, the script will tear up all resources in the IBM Cloud location Washington (`eu-es`). To change the region, utilize the environment variable `REGION` +``` +REGION=eu-de ./run +``` + +To adjust naming of all IBM Cloud resources, the following environment variable `NAME_PREFIX`can be overriden (default: `ce-to-private-path`): +``` +NAME_PREFIX=my-prefix ./run +``` + +To analyze issues that may appear in your account, it could be useful to skip the deletion of IBM Cloud resources by setting environment variable `CLEANUP_ON_ERROR` to `false`: +``` +CLEANUP_ON_ERROR=false ./run +``` + +To clean up all IBM Cloud resources, that have been created as part of the provided script, run: +``` +./run clean +``` + +In order to connect to the VSI via ssh, you can specify the name of an VPC SSH key using the env variable `VPC_SSH_KEY` to configured enroll it on the created VSI. Furthermore, you'll need to set the env variable `DEBUG=true`, which will make sure that the VPC Floating IP will remain attached to the originserver VSI. + +``` +VPC_SSH_KEY= DEBUG=true CLEANUP_ON_SUCCESS=false ./run +``` diff --git a/private-path-to-vpc-vsi/build b/private-path-to-vpc-vsi/build new file mode 100755 index 000000000..5e505b69f --- /dev/null +++ b/private-path-to-vpc-vsi/build @@ -0,0 +1,23 @@ +#!/bin/bash + +# Env Vars: +# REGISTRY: name of the image registry/namespace to store the images +# +# NOTE: to run this you MUST set the REGISTRY environment variable to +# your own image registry/namespace otherwise the `docker push` commands +# will fail due to an auth failure. Which means, you also need to be logged +# into that registry before you run it. + +set -ex +export REGISTRY=${REGISTRY:-icr.io/codeengine} + +# Build and push the image +cd ce-app/ +KO_DOCKER_REPO="${REGISTRY}/ce-to-private-path/app" ko build . --bare --image-user 1001 --platform linux/amd64 --sbom=none +cd .. + +# Build and push the job +cd ce-job/ +docker build ${NOCACHE} -t ${REGISTRY}/ce-to-private-path/job . --platform linux/amd64 +docker push ${REGISTRY}/ce-to-private-path/job +cd .. \ No newline at end of file diff --git a/private-path-to-vpc-vsi/ce-app/Dockerfile b/private-path-to-vpc-vsi/ce-app/Dockerfile new file mode 100644 index 000000000..7ae1e0829 --- /dev/null +++ b/private-path-to-vpc-vsi/ce-app/Dockerfile @@ -0,0 +1,10 @@ +FROM quay.io/projectquay/golang:1.23 AS build-env +WORKDIR /go/src/app +COPY . . + +RUN CGO_ENABLED=0 go build -o /go/bin/app . + +# Copy the exe into a smaller base image +FROM gcr.io/distroless/static-debian12 +COPY --from=build-env /go/bin/app / +ENTRYPOINT ["/app"] diff --git a/private-path-to-vpc-vsi/ce-app/go.mod b/private-path-to-vpc-vsi/ce-app/go.mod new file mode 100644 index 000000000..3f6dc51b8 --- /dev/null +++ b/private-path-to-vpc-vsi/ce-app/go.mod @@ -0,0 +1,5 @@ +module github.com/IBM/CodeEngine/ce-satellite-connector + +go 1.23.0 + +require github.com/lib/pq v1.10.9 diff --git a/private-path-to-vpc-vsi/ce-app/go.sum b/private-path-to-vpc-vsi/ce-app/go.sum new file mode 100644 index 000000000..aeddeae36 --- /dev/null +++ b/private-path-to-vpc-vsi/ce-app/go.sum @@ -0,0 +1,2 @@ +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= diff --git a/private-path-to-vpc-vsi/ce-app/main.go b/private-path-to-vpc-vsi/ce-app/main.go new file mode 100644 index 000000000..5c08a58a8 --- /dev/null +++ b/private-path-to-vpc-vsi/ce-app/main.go @@ -0,0 +1,103 @@ +package main + +import ( + "context" + "database/sql" + "encoding/json" + "fmt" + "log" + "net/http" + "os" + "os/signal" + "syscall" + "time" + + _ "github.com/lib/pq" +) + +var dbClient = connectToDb() + +type Friendship struct { + Name string `json:"name"` + Created int64 `json:"created"` + Greeting string `json:"greeting"` +} + +func Debug(format string, args ...interface{}) { + format = time.Now().Format("2006-01-02 15:04:05 ") + format + "\n" + fmt.Fprintf(os.Stderr, format, args...) +} + +func connectToDb() *sql.DB { + Debug("Connecting to PostgreSQL instance ...") + dbClient, err := sql.Open("postgres", "postgres://"+os.Getenv("PGUSER")+":"+os.Getenv("PGPASSWORD")+"@"+os.Getenv("PGHOST")+":"+os.Getenv("PGPORT")+"/"+os.Getenv("PGDATABASE")+"?sslmode=disable") + if err != nil { + log.Panicf("Cannot open connection to database: %v", err) + } + Debug("Connecting to PostgreSQL instance [done]") + return dbClient +} + +// This func will handle all incoming HTTP requests +func HandleHTTP(w http.ResponseWriter, r *http.Request) { + + friendships := []Friendship{} + var ( + name string + created_at int64 + greeting string + ) + Debug("Fetching all friendship records ...") + rows, sqlErr := dbClient.Query("SELECT name, created_at, greeting FROM myfriendships") + if sqlErr != nil { + log.Printf("Retrieving friendship records failed - err: " + sqlErr.Error()) + w.WriteHeader(500) + } + defer rows.Close() + for rows.Next() { + err := rows.Scan(&name, &created_at, &greeting) + if err != nil { + log.Printf("Scanning friendship record failed - err: " + err.Error()) + w.WriteHeader(500) + } + log.Println("Retrieved friendship records", name, created_at, greeting) + friendships = append(friendships, Friendship{Name: name, Created: created_at, Greeting: greeting}) + } + + Debug("Fetched %d friendship records", len(friendships)) + bytes, err := json.Marshal(&friendships) + if err != nil { + log.Printf("Failed to marshal response - err: " + err.Error()) + w.WriteHeader(500) + } + + w.Header().Add("Content-Type", "application/json") + fmt.Fprintf(w, "%s", string(bytes)) + +} + +func main() { + ctx := context.Background() + signals := make(chan os.Signal, 1) + signal.Notify(signals, os.Interrupt, syscall.SIGTERM) + + srv := &http.Server{Addr: ":8080"} + + // Debug the http handler for all requests + http.HandleFunc("/", HandleHTTP) + + go func() { + Debug("Listening on port 8080") + + if err := srv.ListenAndServe(); err != http.ErrServerClosed { + log.Fatalf("failed to start server: %v", err) + } + }() + + <-signals + Debug("shutting down server") + if err := srv.Shutdown(ctx); err != nil { + log.Fatalf("failed to shutdown server: %v", err) + } + Debug("shutdown done") +} diff --git a/private-path-to-vpc-vsi/ce-job/Dockerfile b/private-path-to-vpc-vsi/ce-job/Dockerfile new file mode 100644 index 000000000..dbe44d0ac --- /dev/null +++ b/private-path-to-vpc-vsi/ce-job/Dockerfile @@ -0,0 +1,14 @@ +FROM registry.access.redhat.com/ubi9/nodejs-22:latest AS build-env +WORKDIR /job + +# Define which files should be copied into the container image +COPY --chown=default:root *.mjs *.json . + +# Load all dependencies +RUN npm install + +# Use a small distroless image for as runtime image +FROM gcr.io/distroless/nodejs22 +COPY --from=build-env /job /job +WORKDIR /job +CMD ["job.mjs"] diff --git a/private-path-to-vpc-vsi/ce-job/job.mjs b/private-path-to-vpc-vsi/ce-job/job.mjs new file mode 100644 index 000000000..a5bcec4e9 --- /dev/null +++ b/private-path-to-vpc-vsi/ce-job/job.mjs @@ -0,0 +1,32 @@ +import pkg from "pg"; +import { LoremIpsum } from "lorem-ipsum"; + +const { Client } = pkg; + +console.log("Connecting to PostgreSQL instance..."); +try { + const client = new Client({ + user: process.env.PGUSER, + password: process.env.PGPASSWORD, + host: process.env.PGHOST, + database: process.env.PGDATABASE, + port: process.env.PGPORT, + }); + await client.connect(); + + console.log("Creating myfriendships table if it does not exist..."); + await client.query("CREATE TABLE IF NOT EXISTS myfriendships (id SERIAL PRIMARY KEY, name varchar(256) NOT NULL, created_at bigint NOT NULL, greeting text);"); + + console.log("Writing into myfriendships table..."); + await client.query("INSERT INTO myfriendships (name,created_at,greeting) VALUES ($1,$2,$3);", [ + process.env.HOSTNAME, + Date.now(), + new LoremIpsum().generateWords(5), + ]); + + await client.end(); + console.log("Done!"); +} catch (err) { + console.error("Failed to connect to PostgreSQL instance", err); + process.exit(1); +} diff --git a/private-path-to-vpc-vsi/ce-job/package-lock.json b/private-path-to-vpc-vsi/ce-job/package-lock.json new file mode 100644 index 000000000..f03ee7702 --- /dev/null +++ b/private-path-to-vpc-vsi/ce-job/package-lock.json @@ -0,0 +1,176 @@ +{ + "name": "ce-job", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ce-job", + "version": "1.0.0", + "license": "Apache-2.0", + "dependencies": { + "lorem-ipsum": "^2.0.8", + "pg": "^8.11.5" + }, + "engines": { + "node": "^20.*", + "npm": "^10.*" + } + }, + "node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/lorem-ipsum": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/lorem-ipsum/-/lorem-ipsum-2.0.8.tgz", + "integrity": "sha512-5RIwHuCb979RASgCJH0VKERn9cQo/+NcAi2BMe9ddj+gp7hujl6BI+qdOG4nVsLDpwWEJwTVYXNKP6BGgbcoGA==", + "dependencies": { + "commander": "^9.3.0" + }, + "bin": { + "lorem-ipsum": "dist/bin/lorem-ipsum.bin.js" + }, + "engines": { + "node": ">= 8.x", + "npm": ">= 5.x" + } + }, + "node_modules/pg": { + "version": "8.11.5", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.5.tgz", + "integrity": "sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==", + "dependencies": { + "pg-connection-string": "^2.6.4", + "pg-pool": "^3.6.2", + "pg-protocol": "^1.6.1", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz", + "integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz", + "integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.1.tgz", + "integrity": "sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + } + } +} diff --git a/private-path-to-vpc-vsi/ce-job/package.json b/private-path-to-vpc-vsi/ce-job/package.json new file mode 100644 index 000000000..85d6caf13 --- /dev/null +++ b/private-path-to-vpc-vsi/ce-job/package.json @@ -0,0 +1,18 @@ +{ + "name": "ce-job", + "version": "1.0.0", + "description": "Code Engine job written in Node.js that connects to a PostgreSQL instance", + "scripts": { + "start": "node ./job.mjs" + }, + "author": "IBM", + "license": "Apache-2.0", + "dependencies": { + "lorem-ipsum": "^2.0.8", + "pg": "^8.16.3" + }, + "engines": { + "node": "^22.*", + "npm": "^11.*" + } +} diff --git a/private-path-to-vpc-vsi/docs/code-engine-private-path---component-diagram.drawio b/private-path-to-vpc-vsi/docs/code-engine-private-path---component-diagram.drawio new file mode 100644 index 000000000..241ee66b5 --- /dev/null +++ b/private-path-to-vpc-vsi/docs/code-engine-private-path---component-diagram.drawio @@ -0,0 +1,286 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/private-path-to-vpc-vsi/docs/code-engine-private-path---component-diagram.png b/private-path-to-vpc-vsi/docs/code-engine-private-path---component-diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..f1c7801b5d3030e33ce130e97fed6b82ecfec3fc GIT binary patch literal 116985 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

Z#EooD>5lE8_%#1H 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 R2P)ua%0#yb0>>pG{{l{ZT~+`9 literal 0 HcmV?d00001 diff --git a/private-path-to-vpc-vsi/docs/code-engine-private-path---database-flow.png b/private-path-to-vpc-vsi/docs/code-engine-private-path---database-flow.png new file mode 100644 index 0000000000000000000000000000000000000000..783b183422f0933579aa2eb9283b4ee195ba7b32 GIT binary patch literal 73149 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 E9}y-o-v9sr literal 0 HcmV?d00001 diff --git a/private-path-to-vpc-vsi/run b/private-path-to-vpc-vsi/run new file mode 100755 index 000000000..311247799 --- /dev/null +++ b/private-path-to-vpc-vsi/run @@ -0,0 +1,496 @@ +#!/bin/bash + +# Env vars +CLEANUP_ON_ERROR=${CLEANUP_ON_ERROR:=false} +CLEANUP_ON_SUCCESS=${CLEANUP_ON_SUCCESS:=false} +REGION="${REGION:=eu-es}" +NAME_PREFIX="${NAME_PREFIX:=ce-to-private-path}" +VPC_SSH_KEY="${VPC_SSH_KEY:=}" +DEBUG_MODE="${DEBUG_MODE:=false}" + +# Dependent variables +resource_group_name="${NAME_PREFIX}--rg" +ce_project_name="${NAME_PREFIX}--ce-project" +ce_job_name="friendship-book-writer" +ce_app_name="friendship-book-api" +ce_db_credentials="db-credentials" +vpc_name="${NAME_PREFIX}--is-vpc" +vsi_originserver_name="${NAME_PREFIX}--is-vsi-originserver" + +# ============================== +# COMMON FUNCTIONS +# ============================== +RED="\033[31m" +BLUE="\033[94m" +GREEN="\033[32m" +ENDCOLOR="\033[0m" + +function print_error { + echo -e "${RED}\n==========================================${ENDCOLOR}" + echo -e "${RED} FAILED${ENDCOLOR}" + echo -e "${RED}==========================================\n${ENDCOLOR}" + echo -e "${RED}$1${ENDCOLOR}" + echo "" +} +function print_msg { + echo -e "${BLUE}$1${ENDCOLOR}" +} +function print_success { + echo -e "${GREEN}$1${ENDCOLOR}" +} + +# Helper function to check whether prerequisites are installed +function check_prerequisites { + # Ensure that jq tool is installed + if ! command -v jq &>/dev/null; then + print_error "'jq' tool is not installed" + exit 1 + fi +} + +# helper function to check whether IBM Cloud CLI plugins should get updated, or not +function ensure_plugin_is_up_to_date() { + echo "Checking $1 ..." + # check whether plugin is installed + if ! ibmcloud plugin show $1 -q >/dev/null; then + # install it + ibmcloud plugin install $1 -f --quiet + else + # check whether there is an update available + ibmcloud plugin update $1 -f --quiet + fi +} + + +# Clean up previous run +function clean() { + ( + rm -f userdata-vsi-agent.sh + + ibmcloud is floating-ip-release $vsi_originserver_name-ip --force 2>/dev/null + ibmcloud is instance-delete $vsi_originserver_name --force 2>/dev/null + while [ $? == 0 ]; do + sleep 2 + ibmcloud is instance $vsi_originserver_name >/dev/null 2>&1 + done + ibmcloud is private-path-service-gateway-delete $vpc_name-pps --force + ibmcloud is load-balancer-delete $vpc_name-ppnlb --force + ibmcloud is subnet-delete $vpc_name-subnet --force + ibmcloud is network-acl-delete $vpc_name-acl --force + ibmcloud is public-gateway-delete $vpc_name-gateway --force + ibmcloud is security-group-delete $vpc_name-group --force + ibmcloud is vpc-delete $vpc_name --force + while [ $? == 0 ]; do + sleep 2 + ibmcloud is vpc $vpc_name + done + + ibmcloud ce project select --name $ce_project_name --quiet 2>/dev/null + if [ $? == 0 ]; then + ibmcloud ce project delete --name $ce_project_name --force --hard --no-wait + fi + + ibmcloud resource group $resource_group_name --quiet 2>/dev/null + if [[ $? == 0 ]]; then + COUNTER=0 + # some resources (e.g. boot volumes) are deleted with some delay. Hence, the script waits before exiting with an error + while (( "$(ibmcloud resource service-instances --type all -g $resource_group_name --output json | jq -r '. | length')" > 0 )); do + sleep 5 + COUNTER=$((COUNTER + 1)) + if ((COUNTER > 30)); then + print_error "Cleanup failed! Please make sure to delete remaining resources manually to avoid unwanted charges." + ibmcloud resource service-instances --type all -g $resource_group_name + exit 1 + fi + done + fi + + ibmcloud resource group-delete $resource_group_name --force 2>/dev/null + ) +} + +function abortScript() { + if [[ "${CLEANUP_ON_ERROR}" == true ]]; then + clean + else + print_msg "\nSkipping deletion of the created IBM Cloud resources. Please be aware that the created resources will occur costs in your account." + echo "$ ibmcloud resource service-instances --type all -g $resource_group_name" + ibmcloud resource service-instances --type all -g $resource_group_name + fi + exit 1 +} + +# ============================== +# MAIN SCRIPT FLOW +# ============================== + +print_msg "\n======================================================" +print_msg " Setting up \"Code Engine -> private backend\" sample" +print_msg "======================================================\n" + +echo "" +echo "Please note: This script will install various IBM Cloud resources within the resource group '$resource_group_name'." + +print_msg "\nChecking prerequisites ..." +check_prerequisites + +# Ensure that latest versions of used IBM Cloud ClI is installed +print_msg "\nPulling latest IBM Cloud CLI release ..." +ibmcloud update --force + +# Ensure that latest versions of used IBM Cloud CLI plugins are installed +print_msg "\nInstalling required IBM Cloud CLI plugins ..." +ensure_plugin_is_up_to_date code-engine +ensure_plugin_is_up_to_date vpc-infrastructure + +print_msg "\nCleaning up the remains of previous executions ..." +clean +[[ "$1" == "clean" ]] && print_success "\n==========================================\n DONE\n==========================================\n" && exit 0 + +print_msg "\nTargetting IBM Cloud region '$REGION' ..." +ibmcloud target -r $REGION + +# +# Create the resource group, if it does not exist +ibmcloud resource group $resource_group_name --quiet +if [ $? != 0 ]; then + print_msg "\nCreating resource group '$resource_group_name' ..." + ibmcloud resource group-create $resource_group_name +fi +print_msg "\nTargetting resource group '$resource_group_name' ..." +ibmcloud target -g $resource_group_name + +# +# Create the VPC +print_msg "Creating the VPC '$vpc_name' ..." +ibmcloud is vpc-create $vpc_name --resource-group-name $resource_group_name +if [ $? -ne 0 ]; then + print_error "VPC creation failed!" + abortScript +fi + +# +# Wait for the VPC to become available +print_msg "\nWaiting for the VPC $vpc_name to become available ..." +COUNTER=0 +while ! [[ $(ibmcloud is vpc $vpc_name --output json | jq -r '.status') == "available" ]]; do + sleep 2 + COUNTER=$((COUNTER + 1)) + if ((COUNTER > 10)); then + echo $(ibmcloud is vpc $vpc_name) + print_error "The VPC does not became ready as expected.\nRun 'ibmcloud is vpc $vpc_name' for further insights" + abortScript + fi +done +echo "VPC '$vpc_name' is now available, now!" + +# +# Create the Public gateway +print_msg "\nCreating the VPC Public gateway '$vpc_name-gateway' ..." +ibmcloud is public-gateway-create $vpc_name-gateway $vpc_name $REGION-1 --resource-group-name $resource_group_name +if [ $? -ne 0 ]; then + print_error "VPC Public gateway creation failed!" + abortScript +fi + +# +# Create the Network ACL +print_msg "\nCreating the VPC Network ACL '$vpc_name-acl' ..." +ibmcloud is network-acl-create $vpc_name-acl $vpc_name --rules '[{ "name": "egress", "action": "allow", "destination": "0.0.0.0/0", "direction": "outbound", "source": "0.0.0.0/0", "protocol": "all" }, { "name": "ingress", "action": "allow", "destination": "0.0.0.0/0", "direction": "inbound", "source": "0.0.0.0/0", "protocol": "all" }]' +if [ $? -ne 0 ]; then + print_error "VPC Network ACL creation failed!" + abortScript +fi + +# +# Create the VPC subnet +print_msg "\nCreating the VPC Subnet '$vpc_name-subnet' ..." +ibmcloud is subnet-create $vpc_name-subnet $vpc_name --zone $REGION-1 --resource-group-name $resource_group_name --ipv4-address-count 16 --pgw $vpc_name-gateway --acl $vpc_name-acl +if [ $? -ne 0 ]; then + print_error "VPC Subnet creation failed!" + abortScript +fi + +# Create the security group and its rules +print_msg "\nCreating the VPC Security group '$vpc_name-group' ..." +ibmcloud is security-group-create $vpc_name-group $vpc_name +if [ $? -ne 0 ]; then + print_error "VPC Security group creation failed!" + abortScript +fi + +print_msg "\nCreating required VPC Security group rules ..." +ibmcloud is security-group-rule-add $vpc_name-group outbound tcp --port-min 443 --port-max 443 --vpc $vpc_name >/dev/null +ibmcloud is security-group-rule-add $vpc_name-group outbound udp --port-min 53 --port-max 53 --vpc $vpc_name >/dev/null +ibmcloud is security-group-rule-add $vpc_name-group outbound tcp --port-min 22 --port-max 22 --vpc $vpc_name >/dev/null +ibmcloud is security-group-rule-add $vpc_name-group outbound icmp --icmp-type 8 --vpc $vpc_name >/dev/null +ibmcloud is security-group-rule-add $vpc_name-group outbound all --remote 166.9.0.0/16 --vpc $vpc_name >/dev/null +# from https://cloud.ibm.com/docs/vpc?topic=vpc-service-endpoints-for-vpc +ibmcloud is security-group-rule-add $vpc_name-group outbound all --remote 161.26.0.0/16 --vpc $vpc_name >/dev/null +ibmcloud is security-group-rule-add $vpc_name-group inbound tcp --port-min 22 --port-max 22 --vpc $vpc_name >/dev/null +ibmcloud is security-group-rule-add $vpc_name-group inbound tcp --port-min 80 --port-max 80 --vpc $vpc_name >/dev/null +ibmcloud is security-group-rule-add $vpc_name-group inbound icmp --icmp-type 8 --vpc $vpc_name >/dev/null +echo "Done" + +print_msg "\nPrinting the VPC Security group '$vpc_name-group' ..." +ibmcloud is security-group $vpc_name-group + +# +# Create the origin server VSI +print_msg "\nCreating the VPC VSI '$vsi_originserver_name', which acts as the origin server ..." +ibmcloud is instance-create $vsi_originserver_name $vpc_name $REGION-1 cx2-2x4 $vpc_name-subnet \ + --image ibm-centos-stream-9-amd64-6 \ + --boot-volume "{\"name\": \"boot-vol-attachment-name\", \"volume\": {\"name\": \"$vsi_originserver_name-boot-vol\", \"capacity\": 100, \"profile\": {\"name\": \"general-purpose\"}}, \"delete_volume_on_instance_delete\": true}" \ + --resource-group-name $resource_group_name \ + --host-failure-policy restart \ + --primary-network-interface "{\"name\": \"eth0\", \"allow_ip_spoofing\": false, \"auto_delete\": true, \"subnet\": {\"name\":\"${vpc_name}-subnet\"}, \"primary_ip\": {\"auto_delete\": true}, \"security_groups\": [{\"name\": \"${vpc_name}-group\"}]}" \ + --user-data @userdata-vsi-originserver.sh \ + --keys "$VPC_SSH_KEY" +if [ $? -ne 0 ]; then + print_error "VPC VSI creation failed!" + abortScript +fi + +print_msg "\nWaiting for the VSI '$vsi_originserver_name' to start ..." +COUNTER=0 +while ! [[ $(ibmcloud is instance $vsi_originserver_name --output json | jq -r '.status') == "running" ]]; do + sleep 2 + COUNTER=$((COUNTER + 1)) + if ((COUNTER > 10)); then + print_error "The VSI does not became ready as expected. Perform 'ibmcloud is instance $vsi_originserver_name' for further details." + abortScript + fi +done +echo "VSI '$vsi_originserver_name' is running, now!" + +# +# Assign the floating IP +print_msg "\nAssigning a VPC Floating IP to the primary network interface of VSI '$vsi_originserver_name' ..." +ibmcloud is floating-ip-reserve $vsi_originserver_name-ip --nic eth0 --in $vsi_originserver_name +if [ $? -ne 0 ]; then + print_error "VPC Floating IP assignment failed!" + abortScript +fi +public_ip_address=$(ibmcloud is instance $vsi_originserver_name --output json | jq -r '.primary_network_interface|.floating_ips|.[0]|.address') +private_ip_address=$(ibmcloud is instance $vsi_originserver_name --output json | jq -r '.primary_network_interface|.primary_ip|.address') + +# +# Verify that the originserver VSI exposes a HTTP server +print_msg "\nWaiting for the VSI '$vsi_originserver_name' to be fully initialized (This can take several minutes) ..." +COUNTER=0 +while ! [[ $(curl -s -o /dev/null -w "%{http_code}" http://$public_ip_address:80) == "200" ]]; do + sleep 10 + COUNTER=$((COUNTER + 1)) + if ((COUNTER > 50)); then + print_error "The VSI does not serve any HTTP traffic on port 80" + abortScript + fi + echo "Checking curl http://$public_ip_address:80 ..." +done +echo "VSI $vsi_originserver_name is fully initialized, now!" + +print_msg "\nVSI serves following payload on endpoint 'http://$public_ip_address:80':" +curl http://$public_ip_address:80 + +if [[ "${DEBUG_MODE}" != true ]]; then + # + # Detaching floating ip address + print_msg "\nDetaching VPC Floating IP '$vsi_originserver_name-ip' from the VSI '$vsi_originserver_name' ..." + ibmcloud is floating-ip-release $vsi_originserver_name-ip --force +fi + + +# +# Create the Private Path Service, the Private Path Load balancer and configure the origin pool +# + +# Create Private Path network load balancer +# see: https://cloud.ibm.com/docs/vpc?topic=vpc-ppnlb-ui-creating-private-path-network-load-balancer&interface=cli +print_msg "\nCreating the VPC Private Path network load balancer '$vpc_name-ppnlb' ..." +ibmcloud is load-balancer-create $vpc_name-ppnlb private-path --family network --subnet $vpc_name-subnet +if [ $? -ne 0 ]; then + print_error "VPC Private Path network load balancer creation failed!" + abortScript +fi + +# Create a LB pool +print_msg "\nCreating the VPC Network load balancer pool '$vpc_name-ppnlb-pg-pool' ..." +ibmcloud is load-balancer-pool-create $vpc_name-ppnlb-pg-pool $vpc_name-ppnlb weighted_round_robin tcp 10 2 5 tcp +if [ $? -ne 0 ]; then + print_error "VPC Network load balancer pool creation failed!" + abortScript +fi + +# Create a LB member +print_msg "\nAdd the VSI '$vsi_originserver_name' as a member to the load balancer pool '$vpc_name-ppnlb-pg-pool' ..." +ibmcloud is load-balancer-pool-member-create $vpc_name-ppnlb $vpc_name-ppnlb-pg-pool 5432 $vsi_originserver_name --weight 70 +if [ $? -ne 0 ]; then + print_error "Adding the VSI '$vsi_originserver_name' as a member to the load balancer pool failed!" + abortScript +fi + +# Obtain the ID of the default backend pool +print_msg "\nObtaining the ID of the default backend pool '$vpc_name-pool' ..." +ppnlb_pg_pool=$(ibmcloud is load-balancer-pool $vpc_name-ppnlb $vpc_name-ppnlb-pg-pool --output JSON) +ppnlb_pg_pool_id=$(echo "$ppnlb_pg_pool" | jq -r '.id') +echo "ppnlb_pg_pool_id: '$ppnlb_pg_pool_id'" + +# Create a LB listener +print_msg "\nCreating the listener for VPC Network load balancer '$vpc_name-ppnlb' ..." +ibmcloud is load-balancer-listener-create $vpc_name-ppnlb --port-min 5432 --port-max 5432 --protocol tcp --default-pool $ppnlb_pg_pool_id +if [ $? -ne 0 ]; then + print_error "VPC Network load balancer front-end listener creation for port 5432 failed!" + abortScript +fi + +# Create the Private Path service +# see: https://cloud.ibm.com/docs/vpc?topic=vpc-private-path-service-about&interface=cli +random_chars=$(openssl rand -hex 6) +print_msg "\nCreating the VPC Private Path service '$vpc_name-pps' for the service endpoint 'api.$random_chars.intra' ..." +pps_service_endpoint="api.ce-$random_chars.intra" +ibmcloud is private-path-service-gateway-create --name $vpc_name-pps --default-access-policy permit --zonal-affinity true --service-endpoints $pps_service_endpoint --load-balancer $vpc_name-ppnlb +if [ $? -ne 0 ]; then + print_error "VPC Private Path service creation failed!" + abortScript +fi + +# Obtain the Private Path service CRN +pps_instance=$(ibmcloud is private-path-service-gateway $vpc_name-pps --output JSON) +pps_instance_crn=$(echo "$pps_instance" | jq -r '.crn') +pps_instance_id=$(echo "$pps_instance" | jq -r '.id') +echo "pps_instance_crn: '$pps_instance_crn', pps_instance_id: '$pps_instance_id'" + +# Publish the Private Path service +# see: https://cloud.ibm.com/docs/vpc?topic=vpc-pps-activating&interface=cli +print_msg "\nPublish VPC Private Path service '$vpc_name-pps' so that it can be accessed from outside of the current account ..." +ibmcloud is private-path-service-gateway-publish $vpc_name-pps +if [ $? -ne 0 ]; then + print_error "Publishing the Private Path service '$vpc_name-pps' failed!" + abortScript +fi + + +# +# Create the Code Engine project +print_msg "\nCreating the Code Engine project '$ce_project_name' ..." +ibmcloud ce project create --name $ce_project_name +if [ $? -ne 0 ]; then + print_error "Code Engine project creation failed!" + abortScript +fi +project_guid=$(ibmcloud ce project current --output json | jq -r '.guid') + +# +# Obtain the kube context of the current project +print_msg "\nObtain the kube context of the Code Engine project '$ce_project_name' ..." +ibmcloud ce project select --name $ce_project_name --kubecfg + +# +# Create the private path integration +ce_vpegatewayconnection_name=friendship-book-api-integration +kubectl apply -f - < 30)); then + kubectl get vpegatewayconnection $ce_vpegatewayconnection_name -o YAML + print_error "The Private Path integration does not became ready as expected. Perform 'kubectl get vpegatewayconnection $ce_vpegatewayconnection_name -o yaml' for further details." + abortScript + fi +done +echo "Private Path integration '$ce_vpegatewayconnection_name' is ready, now!" + +# +# Creating a secret that contains the PostgreSQL credentials +print_msg "\nCreating a Code Engine secret '$ce_db_credentials' to store the database credentials ..." +ibmcloud ce secret create --name $ce_db_credentials --format generic \ + --from-literal PGHOST=$pps_service_endpoint \ + --from-literal PGPORT=5432 \ + --from-literal PGUSER=dbuser \ + --from-literal PGPASSWORD=myPassw0rd! \ + --from-literal PGDATABASE=friendshipdb +if [ $? -ne 0 ]; then + print_error "Code Engine secret creation failed!" + abortScript +fi + +print_msg "\nCreating a Code Engine job '$ce_job_name' that will connect to the database ..." +ibmcloud ce job create --name $ce_job_name \ + --build-source ./ce-job \ + --env-from-secret $ce_db_credentials \ + --memory 0.5G \ + --cpu 0.25 \ + --wait +if [ $? -ne 0 ]; then + print_error "Code Engine job creation failed!" + abortScript +fi + +print_msg "\nPrinting source code of the deployed job:" +cat ce-job/job.mjs +echo "" + +print_msg "\nSubmitting a single job run that starts 10 instances, to store some records in the database ..." +ibmcloud ce jobrun submit --job $ce_job_name --array-size 10 --wait + +print_msg "\nListing submitted job runs..." +ibmcloud ce jobrun list + +print_msg "\nCreating a Code Engine app '$ce_app_name' that retrieve records from the database ..." +ibmcloud ce app create --name $ce_app_name \ + --build-source ./ce-app \ + --env-from-secret $ce_db_credentials \ + --memory 0.5G \ + --cpu 0.25 +if [ $? -ne 0 ]; then + print_error "Code Engine app creation failed!" + abortScript +fi + +ce_app_endpoint=$(ibmcloud ce app get --name $ce_app_name -o url) + +# +# Verifying the end-to-end flow +print_msg "\nInvoking the Code Engine app by using 'curl $ce_app_endpoint'." +print_msg "The app will perform a SQL query towards the database hosted on the origin server and passthrough the result as JSON response payload ..." +curl --silent $ce_app_endpoint | jq + +if [[ $(curl -s -o /dev/null -w "%{http_code}" $ce_app_endpoint) != "200" ]]; then + print_error "Code Engine app could not get invoked properly!" + abortScript +fi + +print_msg "\nBefore cleaning up, this end-to-end sample created the following set of IBM Cloud resources:" +ibmcloud resource service-instances --type all -g $resource_group_name + +if [[ "${CLEANUP_ON_SUCCESS}" == true ]]; then + print_msg "\nCleaning up the created IBM Cloud resources ..." + clean +else + print_msg "\nSkipping deletion of the created IBM Cloud resources. Please be aware that the created resources will occur costs in your account." + echo "$ ibmcloud resource service-instances --type all -g $resource_group_name" + ibmcloud resource service-instances --type all -g $resource_group_name + + print_msg "\nFollowing commands can be used to further play around with the sample setup:" + echo "1. Submit another job run: 'ibmcloud ce jobrun submit --job $ce_job_name --array-size 10'" + echo "2. Invoke the app: 'curl $ce_app_endpoint'" + echo "3. Private Path service configuration https://cloud.ibm.com/infrastructure/network/privatePathServices/${REGION}~${pps_instance_id}/overview" + echo "4. Inspect the Code Engine project setup https://cloud.ibm.com/codeengine/project/$REGION/$project_guid" + echo "5. Tear down the sample setup: './run clean'" +fi + +print_success "\n==========================================" +print_success " SUCCESS" +print_success "==========================================\n" diff --git a/private-path-to-vpc-vsi/userdata-vsi-originserver.sh b/private-path-to-vpc-vsi/userdata-vsi-originserver.sh new file mode 100755 index 000000000..40e254c75 --- /dev/null +++ b/private-path-to-vpc-vsi/userdata-vsi-originserver.sh @@ -0,0 +1,38 @@ +#!/bin/bash +touch /tmp/init_started +# ========================== +# PostgreSQL installation +# ========================== +yum update -y +yum install postgresql-server postgresql-contrib -y +postgresql-setup initdb +systemctl start postgresql +systemctl enable postgresql +echo "host all all 0.0.0.0/0 md5" >> /var/lib/pgsql/data/pg_hba.conf +echo "listen_addresses = '*'" >> /var/lib/pgsql/data/postgresql.conf +sudo systemctl restart postgresql +# ========================== +# PostgreSQL init +# ========================== +useradd dbuser +sudo -i -u postgres bash << EOF +createuser dbuser +createdb friendshipdb -O dbuser +psql -c "ALTER USER dbuser PASSWORD 'myPassw0rd!';" +EOF +touch /tmp/postgresql_done + +# ========================== +# nginx installation +# ========================== +dnf -y update +dnf -y install nginx +rm -f /usr/share/nginx/html/index.html +echo "Hello world from `hostname`" > /usr/share/nginx/html/index.html +chmod go+r /usr/share/nginx/html/index.html +systemctl enable nginx +systemctl start nginx +systemctl status nginx +touch /tmp/nginx_done + +touch /tmp/init_done \ No newline at end of file From 945c35786de1b5c24e3bb31454e54d3501df7466 Mon Sep 17 00:00:00 2001 From: Enrico Regge Date: Mon, 4 Aug 2025 00:08:59 +0200 Subject: [PATCH 2/3] reworked gallery example to comply with COS mounts and TP enhancements --- gallery/README.md | 43 +++++------- gallery/app/app.js | 78 ++++++--------------- gallery/function/cos-service.js | 120 ++++++++++++++++++-------------- gallery/function/function.js | 91 +++++++++--------------- gallery/function/package.json | 2 +- gallery/job/Dockerfile | 4 +- 6 files changed, 145 insertions(+), 193 deletions(-) diff --git a/gallery/README.md b/gallery/README.md index e25eb7288..816226c95 100644 --- a/gallery/README.md +++ b/gallery/README.md @@ -53,6 +53,13 @@ We'll need it later on to configure the bucket. ``` $ export CE_PROJECT_GUID=$(ibmcloud ce project current --output json|jq -r '.guid') $ echo "CE_PROJECT_GUID: $CE_PROJECT_GUID" + +CE_PROJECT_GUID: 91efff97-1001-4144-997a-744ec8009303 + +$ export CE_PROJECT_CRN=$(ibmcloud ce project get --name gallery --output json|jq -r '.crn') +$ echo "CE_PROJECT_CRN: $CE_PROJECT_CRN" + +CE_PROJECT_CRN: crn:v1:bluemix:public:codeengine:eu-de:a/7658687ea07db8386963ebe2b8f1897a:91efff97-1001-4144-997a-744ec8009303:: ``` Once the project has become active, you are good to proceed with the next step. @@ -99,7 +106,7 @@ OK Service instance gallery-cos was created. Name: gallery-cos -ID: crn:v1:bluemix:public:cloud-object-storage:global:a/7658687ea07db8396963ebe2b8e1897d:c0f324be-33fd-4989-a4af-376a13abb316:: +ID: crn:v1:bluemix:public:cloud-object-storage:global:a/7658687ea07db8386963ebe2b8f1897a:c0f324be-33fd-4989-a4af-376a13abb316:: GUID: c0f324be-33fd-4989-a4af-376a13abb316 Location: global State: active @@ -217,10 +224,14 @@ Utilize local build capabilities, which is able to take your local source code a ``` $ ibmcloud ce fn create --name change-color \ --build-source . \ - --runtime nodejs-20 \ + --runtime nodejs-22 \ --memory 4G \ --cpu 1 \ - --env BUCKET=$BUCKET + --env TRUSTED_PROFILE_NAME=ce-gallery-to-cos \ + --env COS_BUCKET=$BUCKET \ + --env COS_REGION=$REGION \ + --trusted-profiles-enabled \ + --visibility project Preparing function 'change-color' for build push... Creating function 'change-color'... @@ -239,33 +250,15 @@ Run 'ibmcloud ce function get -n change-color' to see more details. https://change-color.172utxcdky5l.eu-de.codeengine.appdomain.cloud ``` -In order to allow the function to read and write to the bucket, we'll need to create a binding between the COS instance and the function to expose the Object Storage credentials to the functions code. As we already created such credentials for the application, we'll want to make sure to re-use it, as opposed to create new ones. +In order to allow the function to read and write to the bucket, we'll need to create an IAM trusted profile between the COS instance and the function to expose the Object Storage credentials to the functions code. -List all service credentials of the Object Storage instance: ``` -$ ibmcloud resource service-keys --instance-id $COS_ID +$ ibmcloud iam trusted-profile-create ce-gallery-to-cos -Retrieving all service keys in resource group default under account John Does's Account as abc@ibm.com... -OK -Name State Created At -gallery-ce-service-binding-prw1t active Fri Sep 8 07:56:19 UTC 2023 -``` +$ ibmcloud iam trusted-profile-link-create ce-gallery-to-cos --name ce-fn-change-color --cr-type CE --link-crn ${CE_PROJECT_CRN} --link-component-type function --link-component-name change-color -Extract the name of the service access secret, that has been created for the app -``` -$ export COS_SERVICE_CREDENTIAL=$(ibmcloud resource service-keys --instance-id $COS_ID --output json|jq -r '.[0].name') -$ echo "COS_SERVICE_CREDENTIAL: $COS_SERVICE_CREDENTIAL" -``` +$ ibmcloud iam trusted-profile-policy-create ce-gallery-to-cos --roles "Writer" --service-name cloud-object-storage --service-instance ${COS_INSTANCE_ID} --resource-type bucket --resource ${BUCKET} -Finally expose the COS credentials to the function by binding the service access secret to the function -``` -$ ibmcloud ce function bind --name change-color \ - --service-instance gallery-cos \ - --service-credential $COS_SERVICE_CREDENTIAL - -Binding service instance... -Status: Done -OK ``` In order to complete this step, we'll update the app and make it aware that there is a function that allows to change the colors of individual images. diff --git a/gallery/app/app.js b/gallery/app/app.js index 94b669f92..ec08b0158 100644 --- a/gallery/app/app.js +++ b/gallery/app/app.js @@ -6,37 +6,20 @@ const { CosService } = require("./cos-service"); const basePath = __dirname; // serving files from here -const getCosConfig = () => { - const endpoint = - process.env.CLOUD_OBJECT_STORAGE_ENDPOINT || - "s3.eu-de.cloud-object-storage.appdomain.cloud"; - const serviceInstanceId = - process.env.CLOUD_OBJECT_STORAGE_RESOURCE_INSTANCE_ID; - const apiKeyId = process.env.CLOUD_OBJECT_STORAGE_APIKEY; - console.log( - `getCosConfig - endpoint: '${endpoint}', serviceInstanceId: ${serviceInstanceId}, apiKeyId: '${ - apiKeyId && "*****" - }'` - ); - - return { - endpoint, - apiKeyId, - serviceInstanceId, - }; -}; +let GALLERY_PATH = "/app/tmp"; -// Init COS -let cosService; -if (process.env.CLOUD_OBJECT_STORAGE_APIKEY) { - cosService = new CosService(getCosConfig()); +// if the optional env var 'MOUNT_LOCATION' is not set, but a bucket has been mounted to /mnt/bucket assume it is a COS mount +let isCosEnabled = false; +if (process.env.MOUNT_LOCATION || existsSync("/mnt/bucket")) { + isCosEnabled = true; + GALLERY_PATH = process.env.MOUNT_LOCATION || "/mnt/bucket"; } function getFunctionEndpoint() { if (!process.env.COLORIZER) { return undefined; } - return `https://${process.env.COLORIZER}.${process.env.CE_SUBDOMAIN}.${process.env.CE_DOMAIN}`; + return `http://${process.env.COLORIZER}.${process.env.CE_SUBDOMAIN}.function.cluster.local`; } async function invokeColorizeFunction(imageId) { @@ -62,7 +45,7 @@ const mimetypeByExtension = Object.assign(Object.create(null), { png: "image/png", }); -function handleHttpReq(req, res) { +async function handleHttpReq(req, res) { let reqPath = req.url; if (reqPath.startsWith("//")) { reqPath = reqPath.slice(1); @@ -89,10 +72,9 @@ function handleHttpReq(req, res) { const enabledFeatures = {}; - if (process.env.BUCKET && process.env.CLOUD_OBJECT_STORAGE_APIKEY) { - enabledFeatures.cos = { - bucket: process.env.BUCKET, - interval: parseInt(process.env.CHECK_INTERVAL) || 1_000, + if (existsSync(GALLERY_PATH)) { + enabledFeatures.fs = { + cos: isCosEnabled, }; } if (process.env.COLORIZER) { @@ -207,32 +189,18 @@ function handleHttpReq(req, res) { if (reqPath === "/delete-bucket-content") { console.info("Delete entire bucket content ..."); - cosService - .getBucketContents(process.env.BUCKET, "") - .then((bucketContents) => { - return bucketContents.map((obj) => { - return { - Key: obj.Key, - }; - }); - }) - .then((toBeDeletedObjects) => { - return cosService.deleteBucketObjects( - process.env.BUCKET, - toBeDeletedObjects - ); - }) - .then(() => { - res.setHeader("Content-Type", "application/json"); - res.statusCode = 200; - res.end(`{"done": "true"}`); - }) - .catch((reason) => { - console.log(`Error deleting bucket content: ${reason}`); - res.statusCode = 503; - res.end(`Error deleting bucket content: ${reason}`); - return; - }); + try { + for (const file of await readdir(GALLERY_PATH)) { + await unlink(`${GALLERY_PATH}/${file}`); + } + res.setHeader("Content-Type", "application/json"); + res.statusCode = 200; + res.end(`{"done": "true"}`); + } catch (err) { + console.log(`Error deleting gallery content: ${err}`); + res.statusCode = 503; + res.end(`Error deleting gallery content: ${err}`); + } return; } diff --git a/gallery/function/cos-service.js b/gallery/function/cos-service.js index e467f3a00..81eeaef63 100644 --- a/gallery/function/cos-service.js +++ b/gallery/function/cos-service.js @@ -6,23 +6,38 @@ * disclosure restricted by GSA ADP Schedule Contract with IBM Corp. ******************************************************************************/ -const ibm = require("ibm-cos-sdk"); +const { ContainerAuthenticator } = require("ibm-cloud-sdk-core"); +const { Readable } = require('node:stream'); +const responseToReadable = (response) => { + const reader = response.body.getReader(); + const rs = new Readable(); + rs._read = async () => { + const result = await reader.read(); + if (!result.done) { + rs.push(Buffer.from(result.value)); + } else { + rs.push(null); + return; + } + }; + return rs; +}; class CosService { - cos; config; + authenticator; constructor(config) { const fn = "constructor"; this.config = config; - this.cos = new ibm.S3(config); - console.debug( - `${fn}- initialized! instance: '${config.serviceInstanceId}'` - ); - } - getServiceInstanceId() { - return this.config.serviceInstanceId; + // create an authenticator based on a trusted profile + this.authenticator = new ContainerAuthenticator({ + iamProfileName: config.trustedProfileName, + }); + console.log( + `CosService init - region: '${this.config.cosRegion}', bucket: ${this.config.cosBucket}, trustedProfileName: '${this.config.trustedProfileName}'` + ); } getContentTypeFromFileName(fileName) { @@ -60,61 +75,64 @@ class CosService { /** * https://ibm.github.io/ibm-cos-sdk-js/AWS/S3.html#putObject-property */ - createObject(bucket, id, dataToUpload, mimeType, contentLength) { + async createObject(id, dataToUpload, mimeType, contentLength) { const fn = "createObject "; console.debug(`${fn}> id: '${id}', mimeType: '${mimeType}', contentLength: '${contentLength}'`); - return this.cos - .putObject({ - Bucket: bucket, - Key: id, - Body: dataToUpload, - ContentType: mimeType, - ContentLength: contentLength, - }) - .promise() - .then((obj) => { - console.debug(`${fn}< done`); - return true; - }) - .catch((err) => { - console.error(err); - console.debug(`${fn}< failed`); - throw err; - }); - } + // prepare the request to create the object files in the bucket + const requestOptions = { + method: "PUT", + body: dataToUpload, + headers: { + "Content-Type": mimeType, + "Content-Length": contentLength, + }, + }; - /** - * https://cloud.ibm.com/docs/cloud-object-storage?topic=cloud-object-storage-node#node-examples-list-objects - */ - getBucketContents(bucketName, prefix) { - const fn = "getBucketContents "; - console.debug(`${fn}> bucket: '${bucketName}', prefix: '${prefix}'`); - return this.cos - .listObjects({ Bucket: bucketName, Prefix: prefix }) - .promise() - .then((data) => { - console.debug(`${fn}< done`); - if (data != null && data.Contents != null) { - return data.Contents; - } - }) - .catch((err) => { - console.error(err); - console.debug(`${fn}< failed`); - return undefined; - }); + // authenticate the request + await this.authenticator.authenticate(requestOptions); + + // perform the request + const response = await fetch( + `https://s3.direct.${this.config.cosRegion}.cloud-object-storage.appdomain.cloud/${this.config.cosBucket}/${id}`, + requestOptions + ); + + if (response.status !== 200) { + console.error(`Unexpected status code: ${response.status}`); + throw new Error(`Failed to upload image: '${response.status}'`); + } + return; } /** * https://ibm.github.io/ibm-cos-sdk-js/AWS/S3.html#getObject-property * @param id */ - getObjectAsStream(bucket, id) { + async getObjectAsStream(id) { const fn = "getObjectAsStream "; console.debug(`${fn}> id: '${id}'`); - return this.cos.getObject({ Bucket: bucket, Key: id }).createReadStream(); + // prepare the request to list the files in the bucket + const requestOptions = { + method: "GET", + }; + + // authenticate the request + await this.authenticator.authenticate(requestOptions); + + // perform the request + return fetch( + `https://s3.direct.${this.config.cosRegion}.cloud-object-storage.appdomain.cloud/${this.config.cosBucket}/${id}`, + requestOptions + ).then((response) => { + if (!response.ok) { + console.error(`${fn}< HTTP error, status = ${response.status}`); + throw new Error(`HTTP error, status = ${response.status}`); + } + console.debug(`${fn}< receiving response as readable stream`); + return responseToReadable(response); + }); } } diff --git a/gallery/function/function.js b/gallery/function/function.js index 05b9426e2..8cbda9018 100644 --- a/gallery/function/function.js +++ b/gallery/function/function.js @@ -3,24 +3,23 @@ const { changeColors } = require("./colorizer"); const { CosService } = require("./cos-service"); +// helper function to craft a proper function result object +function sendJSONResponse(statusCode, responseBody) { + return { + statusCode: statusCode, + headers: { + "Content-Type": "application/json", + }, + body: responseBody, + }; +} + // helper function to craft a proper COS config const getCosConfig = () => { - const endpoint = - process.env.CLOUD_OBJECT_STORAGE_ENDPOINT || - "s3.eu-de.cloud-object-storage.appdomain.cloud"; - const serviceInstanceId = - process.env.CLOUD_OBJECT_STORAGE_RESOURCE_INSTANCE_ID; - const apiKeyId = process.env.CLOUD_OBJECT_STORAGE_APIKEY; - console.log( - `getCosConfig - endpoint: '${endpoint}', serviceInstanceId: ${serviceInstanceId}, apiKeyId: '${ - apiKeyId && "*****" - }'` - ); - return { - endpoint, - apiKeyId, - serviceInstanceId, + cosBucket: process.env.COS_BUCKET, + cosRegion: process.env.COS_REGION || process.env.CE_REGION, + trustedProfileName: process.env.TRUSTED_PROFILE_NAME, }; }; @@ -40,66 +39,52 @@ const streamToBuffer = (inputStream) => { // initialize the COS service let cosService; -if (process.env.CLOUD_OBJECT_STORAGE_APIKEY) { +if (process.env.COS_BUCKET && process.env.TRUSTED_PROFILE_NAME) { cosService = new CosService(getCosConfig()); console.log(`Initialized COS Service`); } -const bucket = process.env.BUCKET; -console.log(`Target bucket: '${bucket}'`); async function main(args) { // Check whether COS has been configured properly - if (!cosService || !bucket) { + if (!cosService) { console.log( - `Aborting. COS has not been configured properly. Either the binding with the prefix 'CLOUD_OBJECT_STORAGE_' or the env var 'BUCKET' are missing.` + `Aborting. COS has not been configured properly. The env variables 'COS_BUCKET' and 'TRUSTED_PROFILE_NAME' must be set properly.` + ); + return sendJSONResponse( + 401, + `{"error":"COS has not been configured properly. The env variables 'COS_BUCKET' and 'TRUSTED_PROFILE_NAME' must be set properly"}` ); - return { - statusCode: 401, - headers: { - "Content-Type": "application/json", - }, - body: `{"error":"Target IBM Cloud Object Storage instance has not been bound properly"}`, - }; } // Obtain the COS ID of the image that should be transformed const imageId = args.imageId; if (!imageId) { - console.log( - `Aborting. Payload parameter imageId is not set properly` - ); - return { - statusCode: 400, - headers: { - "Content-Type": "application/json", - }, - body: `{"error":"Payload parameter imageId is not set properly"}`, - }; + console.log(`Aborting. Payload parameter imageId is not set properly`); + return sendJSONResponse(400, `{"error":"Payload parameter imageId is not set properly"}`); } console.log(`Changing colors of '${imageId}'`); try { + // // Fetch the object that should get transformed - const fileStream = await cosService.getObjectAsStream(bucket, imageId); + const fileStream = await cosService.getObjectAsStream(imageId); console.log(`Downloaded '${imageId}'`); + // // Convert the image stream to a buffer const imageBuf = await streamToBuffer(fileStream); console.log( `Converted to a buffer of size ${(imageBuf.length / 1024).toFixed(1)} KB` ); + // // Change the color tokens of the image const updatedImageBuf = await changeColors(imageBuf); - console.log( - `Adjusted colors of '${imageId}' - new size ${( - updatedImageBuf.length / 1024 - ).toFixed(1)} KB` - ); + console.log(`Adjusted colors of '${imageId}' - new size ${(updatedImageBuf.length / 1024).toFixed(1)} KB`); - // SIXTH upload the adjusted image back into the COS bucket + // + // Upload the adjusted image back into the COS bucket await cosService.createObject( - bucket, imageId, updatedImageBuf, cosService.getContentTypeFromFileName(imageId), @@ -107,22 +92,10 @@ async function main(args) { ); console.log(`Uploaded updated '${imageId}'`); - return { - statusCode: 200, - headers: { - "Content-Type": "application/json", - }, - body: `{"success": "true"}`, - }; + return sendJSONResponse(200, `{"success": "true"}`); } catch (reason) { console.error(`Error changing colors of ${imageId}`, reason); - return { - statusCode: 503, - headers: { - "Content-Type": "application/json", - }, - body: `{"error":"Error changing colors: ${reason}"}`, - }; + return sendJSONResponse(503, `{"error":"Error changing colors: ${reason}"}`); } } diff --git a/gallery/function/package.json b/gallery/function/package.json index 07fa9bdca..289a5873d 100644 --- a/gallery/function/package.json +++ b/gallery/function/package.json @@ -9,6 +9,6 @@ "author": "", "license": "MIT", "dependencies": { - "ibm-cos-sdk": "^1.13.1" + "ibm-cloud-sdk-core": "^5.4.2" } } diff --git a/gallery/job/Dockerfile b/gallery/job/Dockerfile index 8e3eba4d8..a6e8c1299 100644 --- a/gallery/job/Dockerfile +++ b/gallery/job/Dockerfile @@ -1,4 +1,4 @@ -FROM registry.access.redhat.com/ubi9/nodejs-20:latest AS build-env +FROM registry.access.redhat.com/ubi9/nodejs-22:latest AS build-env WORKDIR /app # Define which files should be copied into the container image @@ -9,7 +9,7 @@ COPY package.json . RUN npm install # Use a small distroless image for as runtime image -FROM gcr.io/distroless/nodejs20-debian12 +FROM gcr.io/distroless/nodejs22 COPY --from=build-env /app /app WORKDIR /app CMD ["job.js"] From a45750daf4fb89a2c7c2b823b72c95e597a6e886 Mon Sep 17 00:00:00 2001 From: Enrico Regge Date: Mon, 4 Aug 2025 00:09:11 +0200 Subject: [PATCH 3/3] dependency updates --- hello/Dockerfile | 4 ++-- satellite-connector-to-vpc-vsi/ce-job/Dockerfile | 4 ++-- satellite-connector-to-vpc-vsi/ce-job/package.json | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/hello/Dockerfile b/hello/Dockerfile index 71207c024..e0951421a 100644 --- a/hello/Dockerfile +++ b/hello/Dockerfile @@ -1,10 +1,10 @@ -FROM registry.access.redhat.com/ubi9/nodejs-20:latest AS build-env +FROM registry.access.redhat.com/ubi9/nodejs-22:latest AS build-env WORKDIR /app RUN npm init -f && npm install COPY server.js . # Use a small distroless image for as runtime image -FROM gcr.io/distroless/nodejs20-debian12 +FROM gcr.io/distroless/nodejs22 COPY --from=build-env /app /app WORKDIR /app EXPOSE 8080 diff --git a/satellite-connector-to-vpc-vsi/ce-job/Dockerfile b/satellite-connector-to-vpc-vsi/ce-job/Dockerfile index 07d585883..dbe44d0ac 100644 --- a/satellite-connector-to-vpc-vsi/ce-job/Dockerfile +++ b/satellite-connector-to-vpc-vsi/ce-job/Dockerfile @@ -1,4 +1,4 @@ -FROM registry.access.redhat.com/ubi9/nodejs-20:latest AS build-env +FROM registry.access.redhat.com/ubi9/nodejs-22:latest AS build-env WORKDIR /job # Define which files should be copied into the container image @@ -8,7 +8,7 @@ COPY --chown=default:root *.mjs *.json . RUN npm install # Use a small distroless image for as runtime image -FROM gcr.io/distroless/nodejs20-debian12 +FROM gcr.io/distroless/nodejs22 COPY --from=build-env /job /job WORKDIR /job CMD ["job.mjs"] diff --git a/satellite-connector-to-vpc-vsi/ce-job/package.json b/satellite-connector-to-vpc-vsi/ce-job/package.json index 96845d63a..85d6caf13 100644 --- a/satellite-connector-to-vpc-vsi/ce-job/package.json +++ b/satellite-connector-to-vpc-vsi/ce-job/package.json @@ -9,10 +9,10 @@ "license": "Apache-2.0", "dependencies": { "lorem-ipsum": "^2.0.8", - "pg": "^8.11.5" + "pg": "^8.16.3" }, "engines": { - "node": "^20.*", - "npm": "^10.*" + "node": "^22.*", + "npm": "^11.*" } }