diff --git a/gallery/app/app.js b/gallery/app/app.js index 8f133a0b0..7d47b8bcf 100644 --- a/gallery/app/app.js +++ b/gallery/app/app.js @@ -15,6 +15,11 @@ if (process.env.MOUNT_LOCATION || existsSync("/mnt/bucket")) { GALLERY_PATH = process.env.MOUNT_LOCATION || "/mnt/bucket"; } +function sendResponse(res, statusCode, responseMessage) { + res.statusCode = statusCode; + return res.end(responseMessage); +} + function getFunctionEndpoint() { if (!process.env.COLORIZER) { return undefined; @@ -57,13 +62,9 @@ async function handleHttpReq(req, res) { pageContent = await readFile(`${basePath}/page.html`); } catch (err) { console.log(`Error reading page: ${err.message}`); - res.statusCode = 503; - res.end(`Error reading page: ${err.message}`); - return; + return sendResponse(res, 503, `Error reading page: ${err.message}`); } - res.statusCode = 200; - res.end(pageContent); - return; + return sendResponse(res, 200, pageContent); } // This handler exposes the set of features that are available @@ -91,9 +92,7 @@ async function handleHttpReq(req, res) { if (reqPath === "/change-colors") { req.on("error", (err) => { console.log(`Error reading body: ${err.message}`); - res.statusCode = 503; - res.end(`Error reading body: ${err.message}`); - return; + return sendResponse(res, 503, `Error reading body: ${err.message}`); }); let bodyBuf = Buffer.alloc(0); req.on("data", (chunkBuf) => { @@ -107,14 +106,15 @@ async function handleHttpReq(req, res) { invokeColorizeFunction(payload.imageId) .then((response) => { console.log(`Colorizer function has been invoked successfully: '${JSON.stringify(response)}'`); - res.statusCode = 200; - res.end(); + return sendResponse(res, 200); }) - .catch((reason) => { - console.error(`Error colorizing image '${payload.imageId}'`, reason); - res.statusCode = 503; - res.end(`Error changing color of image '${payload.imageId}': ${reason}`); - return; + .catch((err) => { + console.error(`Error colorizing image '${payload.imageId}'`, err); + + if (err.message.indexOf("Access is denied due to invalid credentials") > -1) { + return sendResponse(res, 403, `Failed due to access permission issues`); + } + return sendResponse(res, 503, `Error changing color of image '${payload.imageId}': ${err.message}`); }); }); return; @@ -125,9 +125,7 @@ async function handleHttpReq(req, res) { console.info("Uploading to COS ..."); req.on("error", (err) => { console.log(`Error reading body: ${err.message}`); - res.statusCode = 503; - res.end(`Error reading body: ${err.message}`); - return; + return sendResponse(res, 503, `Error reading body: ${err.message}`); }); let bodyBuf = Buffer.alloc(0); req.on("data", (chunkBuf) => { @@ -138,14 +136,10 @@ async function handleHttpReq(req, res) { try { await writeFile(`${GALLERY_PATH}/gallery-pic-${Date.now()}.png`, bodyBuf, {}); res.setHeader("Content-Type", "application/json"); - res.statusCode = 200; - res.end(`{"done": "true"}`); - return; + return sendResponse(res, 200, `{"done": "true"}`); } catch (err) { console.log(`Error uploading picture: ${err}`); - res.statusCode = 503; - res.end(`Error uploading picture: ${err}`); - return; + return sendResponse(res, 503, `Error uploading picture: ${err}`); } }); return; @@ -165,14 +159,11 @@ async function handleHttpReq(req, res) { }); res.setHeader("Content-Type", "application/json"); - res.statusCode = 200; - res.end(JSON.stringify(galleryContents)); + return sendResponse(res, 200, JSON.stringify(galleryContents)); } catch (err) { console.log(`Error listing gallery content: ${err}`); - res.statusCode = 503; - res.end(`Error listing gallery content: ${err}`); + return sendResponse(res, 503, `Error listing gallery content: ${err}`); } - return; } // Handler for deleting all items in the gallery @@ -184,12 +175,10 @@ async function handleHttpReq(req, res) { await unlink(`${GALLERY_PATH}/${file}`); } res.setHeader("Content-Type", "application/json"); - res.statusCode = 200; - res.end(`{"done": "true"}`); + return sendResponse(res, 200, `{"done": "true"}`); } catch (err) { console.log(`Error deleting gallery content: ${err}`); - res.statusCode = 503; - res.end(`Error deleting gallery content: ${err}`); + return sendResponse(res, 503, `Error deleting gallery content: ${err}`); } return; } @@ -206,9 +195,7 @@ async function handleHttpReq(req, res) { return fd.createReadStream().pipe(res); } catch (err) { console.error(`Error streaming gallery content '${pictureId}'`, err); - res.statusCode = 503; - res.end(`Error streaming gallery content: ${err}`); - return; + return sendResponse(res, 503, `Error streaming gallery content: ${err}`); } } @@ -218,9 +205,7 @@ async function handleHttpReq(req, res) { } if (reqPath.includes("..")) { console.log(`Bad path "${reqPath}"`); - res.statusCode = 404; - res.end("Bad path"); - return; + return sendResponse(res, 404, "Bad path"); } // serve file at basePath/reqPath let pageContent; @@ -228,13 +213,10 @@ async function handleHttpReq(req, res) { pageContent = await readFile(`${basePath}/${reqPath}`); } catch (err) { console.log(`Error reading file: ${err.message}`); - res.statusCode = 404; - res.end(`Error reading file: ${err.message}`); - return; + return sendResponse(res, 404, `Error reading file: ${err.message}`); } res.setHeader("Content-Type", mimetypeByExtension[(reqPath.match(/\.([^.]+)$/) || [])[1]] || "text/plain"); - res.statusCode = 200; - res.end(pageContent); + return sendResponse(res, 200, pageContent); } const server = createServer(handleHttpReq); diff --git a/gallery/app/page.html b/gallery/app/page.html index bffc2ceab..164de476d 100644 --- a/gallery/app/page.html +++ b/gallery/app/page.html @@ -48,7 +48,10 @@

Code Engine Gallery Demo

My Gallery - + diff --git a/gallery/app/page.js b/gallery/app/page.js index bb2d19394..a5350dc1c 100644 --- a/gallery/app/page.js +++ b/gallery/app/page.js @@ -1,4 +1,4 @@ -const ne = ["file_input", "add", "picture_preview", "progress", "upload", "clear"].reduce((p, c) => { +const ne = ["file_input", "add", "picture_preview", "progress", "upload", "clear", "colorizer"].reduce((p, c) => { p[c] = document.querySelector(`[data-${c.replace("_", "-")}]`); return p; }, Object.create(null)); @@ -178,6 +178,7 @@ function listGalleryContent() { console.info(`setting onclick handler for '${id}'`); img.onclick = async () => { console.info(`colorizer for '${id}'`); + ne.colorizer.innerText = "Colorizing ..."; const currentImage = document.getElementById(id); @@ -185,15 +186,21 @@ function listGalleryContent() { currentImage.className = "gallery-pic colorizable disabled"; try { // trigger the color change - await fetch("/change-colors", { + const colorizeResp = await fetch("/change-colors", { method: "POST", headers: { "Content-Type": "application/json", }, body: `{"imageId": "${id}"}`, }); + if (colorizeResp.status != 200) { + ne.colorizer.innerText = await colorizeResp.text(); + } else { + ne.colorizer.innerText = ""; + } } catch (err) { console.error(`Failed to change color of image '${id}'`, err); + ne.colorizer.innerText = err.message; } finally { currentImage.className = "gallery-pic colorizable"; } diff --git a/gallery/function/function.js b/gallery/function/function.js index 8cbda9018..509ed7d23 100644 --- a/gallery/function/function.js +++ b/gallery/function/function.js @@ -93,9 +93,9 @@ async function main(args) { console.log(`Uploaded updated '${imageId}'`); return sendJSONResponse(200, `{"success": "true"}`); - } catch (reason) { - console.error(`Error changing colors of ${imageId}`, reason); - return sendJSONResponse(503, `{"error":"Error changing colors: ${reason}"}`); + } catch (err) { + console.error(`Error changing colors of ${imageId}`, err); + return sendJSONResponse(503, `{"error":"Error changing colors: ${err.message}"}`); } } diff --git a/private-path-to-vpc-vsi/ce-app/go.mod b/private-path-to-vpc-vsi/ce-app/go.mod index 3f6dc51b8..bff2499af 100644 --- a/private-path-to-vpc-vsi/ce-app/go.mod +++ b/private-path-to-vpc-vsi/ce-app/go.mod @@ -1,4 +1,4 @@ -module github.com/IBM/CodeEngine/ce-satellite-connector +module github.com/IBM/CodeEngine/ce-private-path go 1.23.0 diff --git a/private-path-to-vpc-vsi/ce-app/main.go b/private-path-to-vpc-vsi/ce-app/main.go index 5c08a58a8..814993819 100644 --- a/private-path-to-vpc-vsi/ce-app/main.go +++ b/private-path-to-vpc-vsi/ce-app/main.go @@ -9,6 +9,7 @@ import ( "net/http" "os" "os/signal" + "strings" "syscall" "time" @@ -17,7 +18,7 @@ import ( var dbClient = connectToDb() -type Friendship struct { +type GuestbookEntry struct { Name string `json:"name"` Created int64 `json:"created"` Greeting string `json:"greeting"` @@ -41,39 +42,49 @@ func connectToDb() *sql.DB { // This func will handle all incoming HTTP requests func HandleHTTP(w http.ResponseWriter, r *http.Request) { - friendships := []Friendship{} + guestbookEntries := []GuestbookEntry{} var ( name string created_at int64 greeting string ) - Debug("Fetching all friendship records ...") - rows, sqlErr := dbClient.Query("SELECT name, created_at, greeting FROM myfriendships") + Debug("Fetching all guestbook entries ...") + rows, sqlErr := dbClient.Query("SELECT name, created_at, greeting FROM guestbook") if sqlErr != nil { - log.Printf("Retrieving friendship records failed - err: " + sqlErr.Error()) + log.Printf("Retrieving guestbook entries failed - err: " + sqlErr.Error()) + if strings.Contains(sqlErr.Error(), "dial tcp") { + w.WriteHeader(502) + fmt.Fprintf(w, "Can't reach '%s'", os.Getenv("PGHOST")) + return + } w.WriteHeader(500) + fmt.Fprintf(w, "%s", sqlErr.Error()) + return } 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()) + log.Printf("Scanning guestbook entries failed - err: " + err.Error()) w.WriteHeader(500) + fmt.Fprintf(w, "%s", err.Error()) + return } - log.Println("Retrieved friendship records", name, created_at, greeting) - friendships = append(friendships, Friendship{Name: name, Created: created_at, Greeting: greeting}) + log.Println("Retrieved guestbook records", name, created_at, greeting) + guestbookEntries = append(guestbookEntries, GuestbookEntry{Name: name, Created: created_at, Greeting: greeting}) } - Debug("Fetched %d friendship records", len(friendships)) - bytes, err := json.Marshal(&friendships) + Debug("Fetched %d guestbook entries", len(guestbookEntries)) + bytes, err := json.Marshal(&guestbookEntries) if err != nil { log.Printf("Failed to marshal response - err: " + err.Error()) w.WriteHeader(500) + fmt.Fprintf(w, "%s", err.Error()) + return } w.Header().Add("Content-Type", "application/json") fmt.Fprintf(w, "%s", string(bytes)) - } func main() { @@ -96,6 +107,7 @@ func main() { <-signals Debug("shutting down server") + dbClient.Close() if err := srv.Shutdown(ctx); err != nil { log.Fatalf("failed to shutdown server: %v", err) } diff --git a/private-path-to-vpc-vsi/ce-job/job.mjs b/private-path-to-vpc-vsi/ce-job/job.mjs index a5bcec4e9..b7811df5c 100644 --- a/private-path-to-vpc-vsi/ce-job/job.mjs +++ b/private-path-to-vpc-vsi/ce-job/job.mjs @@ -4,29 +4,46 @@ import { LoremIpsum } from "lorem-ipsum"; const { Client } = pkg; console.log("Connecting to PostgreSQL instance..."); + +const client = new Client({ + user: process.env.PGUSER, + password: process.env.PGPASSWORD, + host: process.env.PGHOST, + database: process.env.PGDATABASE, + port: process.env.PGPORT, +}); 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("Creating guestbook table if it does not exist..."); + await client.query( + "CREATE TABLE IF NOT EXISTS guestbook (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);", [ + console.log("Writing into guestbook table..."); + await client.query("INSERT INTO guestbook (name,created_at,greeting) VALUES ($1,$2,$3);", [ process.env.HOSTNAME, Date.now(), new LoremIpsum().generateWords(5), ]); + if (process.env.ACTION === "cleanup") { + console.log("Cleaning up table content..."); + await client.query("DELETE FROM guestbook;"); + } + await client.end(); console.log("Done!"); } catch (err) { console.error("Failed to connect to PostgreSQL instance", err); + + if(client) { + try { + await client.end(); + } catch(error) { + // just do it + } + } + process.exit(1); } 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 index 241ee66b5..4baa75b22 100644 --- 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 @@ -1,6 +1,6 @@ - + @@ -244,42 +244,9 @@ - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - diff --git a/private-path-to-vpc-vsi/run b/private-path-to-vpc-vsi/run index 311247799..f41f88ae8 100755 --- a/private-path-to-vpc-vsi/run +++ b/private-path-to-vpc-vsi/run @@ -11,8 +11,8 @@ 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_job_name="guestbook-writer" +ce_app_name="guestbook-api" ce_db_credentials="db-credentials" vpc_name="${NAME_PREFIX}--is-vpc" vsi_originserver_name="${NAME_PREFIX}--is-vsi-originserver" @@ -387,7 +387,7 @@ ibmcloud ce project select --name $ce_project_name --kubecfg # # Create the private path integration -ce_vpegatewayconnection_name=friendship-book-api-integration +ce_vpegatewayconnection_name=guestbook-integration kubectl apply -f - <