diff --git a/code-samples/eventing/bookstore-sample-app/solution/ML-sentiment-analysis/README.md b/code-samples/eventing/bookstore-sample-app/solution/ML-sentiment-analysis/README.md index 115f9f6d525..f71bd55cce8 100644 --- a/code-samples/eventing/bookstore-sample-app/solution/ML-sentiment-analysis/README.md +++ b/code-samples/eventing/bookstore-sample-app/solution/ML-sentiment-analysis/README.md @@ -20,10 +20,10 @@ The function's output will be only from --- ### Prerequisite 1: Install Knative `func` CLI -Knative Function enables you to easily create, build, and deploy stateless, event-driven functions as [Knative Services](https://knative.dev/docs/serving/services/#:~:text=Knative%20Services%20are%20used%20to,the%20Service%20to%20be%20configured) by using the func CLI. +Knative Function enables you to easily create, build, and deploy stateless, event-driven functions as [Knative Services](https://knative.dev/docs/serving/services/#:~:text=Knative%20Services%20are%20used%20to,the%20Service%20to%20be%20configured){:target="_blank"} by using the func CLI. In order to do so, you need to install the `func` CLI. -You can follow the [official documentation](https://knative.dev/docs/getting-started/install-func/) to install the `func` CLI. +You can follow the [official documentation](https://knative.dev/docs/getting-started/install-func/){:target="_blank"} to install the `func` CLI. Running `func version` in your terminal to verify the installation, and you should see the version of the `func` CLI you installed. @@ -55,7 +55,7 @@ func create -l python sentiment-analysis This command will create a new directory with the name `sentiment-analysis` and a bunch of files in it. The `func` CLI will generate a basic function template for you to start with. -You can find all the supported languages templates [here](https://knative.dev/docs/functions/). +You can find all the supported languages templates [here](https://knative.dev/docs/functions/){:target="_blank"}. The file tree will look like this: ```bash @@ -175,7 +175,7 @@ setup( ### Step 5: Try to build and run your Knative Function on your local machine -In knative function, there are two ways to build: using the [pack build](https://github.com/knative/func/blob/8f3f718a5a036aa6b6eaa9f70c03aeea740015b9/docs/reference/func_build.md?plain=1#L46) or using the [source-to-image (s2i) build](https://github.com/knative/func/blob/4f48549c8ad4dad34bf750db243d81d503f0090f/docs/reference/func_build.md?plain=1#L43). +In knative function, there are two ways to build: using the [pack build](https://github.com/knative/func/blob/8f3f718a5a036aa6b6eaa9f70c03aeea740015b9/docs/reference/func_build.md?plain=1#L46){:target="_blank"} or using the [source-to-image (s2i) build](https://github.com/knative/func/blob/4f48549c8ad4dad34bf750db243d81d503f0090f/docs/reference/func_build.md?plain=1#L43){:target="_blank"}. Currently. only the **s2i** build is supported if you need to run setup.py. When building with s2i, the `setup.py` file will be executed automatically after the dependencies have been installed. @@ -255,7 +255,7 @@ Knative function also have an easy way to simulate the CloudEvent, you can use t func invoke -f=cloudevent --data='{"input": "I love Knative so much"}' --content-type=application/json --type="new-comment" -v ``` where the `-f` flag indicates the type of the data, is either `HTTP` or `cloudevent`, and the `--data` flag is the input text. -You can read more about `func invoke` [here](https://github.com/knative/func/blob/main/docs/reference/func_invoke.md). +You can read more about `func invoke` [here](https://github.com/knative/func/blob/main/docs/reference/func_invoke.md){:target="_blank"}. In this case, you will get the full CloudEvent response: @@ -302,7 +302,7 @@ NAMESPACE NAME URL default sentiment-analysis-app http://sentiment-analysis-app.default.10.99.46.8.sslip.io sentiment-analysis-app-00002 sentiment-analysis-app-00002 True ``` -Please note: if your URL ends with .svc.cluster.local, that means you can only access the function from within the cluster. You probably forget to configure the network or [start the tunnel](https://knative.dev/docs/getting-started/quickstart-install/#__tabbed_3_2) if you are using minikube. +Please note: if your URL ends with .svc.cluster.local, that means you can only access the function from within the cluster. You probably forget to configure the network or [start the tunnel](https://knative.dev/docs/getting-started/quickstart-install/#__tabbed_3_2){:target="_blank"} if you are using minikube. ### Step 7: Verify the Deployment After deployment, the `func` CLI provides a URL to access your function. You can verify the function's operation by sending a request with a sample review comment. @@ -340,8 +340,8 @@ Recall note box: you can get the URL to the function by running the following co kubectl get kservice -A ``` --- -Another option is to use curl to send a CloudEvent to the function. -Using curl command to send a CloudEvent to the broker: +Another option is to use curl to send a CloudEvents to the function. +Using curl command to send a CloudEvents to the Broker: ```bash [root@curler:/]$ curl -v "http://sentiment-analysis-app.default.10.99.46.8.sslip.io" \ -X POST \ diff --git a/code-samples/eventing/bookstore-sample-app/solution/create-workspace.md b/code-samples/eventing/bookstore-sample-app/solution/create-workspace.md index 5027b8df32e..3d937ca3d68 100644 --- a/code-samples/eventing/bookstore-sample-app/solution/create-workspace.md +++ b/code-samples/eventing/bookstore-sample-app/solution/create-workspace.md @@ -4,12 +4,12 @@ To create a Slack workspace, follow these steps: -- Visit [https://slack.com/get-started#/createnew](https://slack.com/get-started#/createnew). +- Visit [https://slack.com/get-started#/createnew](https://slack.com/get-started#/createnew){:target="_blank"}. - Enter your email address and click Continue, or continue with Apple or Google. - Check your email for a confirmation code. - Enter the code on the Slack website. - Click "Create a Workspace" and follow the prompts. -- For detailed instructions for androids and , refer to [Create a workspace](https://slack.com/help/articles/206845317-Create-a-Slack-workspace). +- For detailed instructions how to create a workspace via the Desktop App, on Android or iOS, refer to [Create a Slack workspace](https://slack.com/help/articles/206845317-Create-a-Slack-workspace){:target="_blank"}. ## Adding Channels to Your Workspace @@ -17,13 +17,13 @@ After creating your Slack workspace, you can add channels by following these ste - Click the '+' icon next to 'Channels' on the sidebar of your Slack workspace. - Choose "Create a channel" and specify the channel name and purpose. -- For enterprise grid organizations, refer to [Slack's help article](https://slack.com/help/articles/115001399587-Add-a-channel-to-multiple-workspaces-in-your-Enterprise-Grid-organization) for instructions. +- For enterprise grid organizations, refer to [Slack's help article](https://slack.com/help/articles/115001399587-Add-a-channel-to-multiple-workspaces-in-your-Enterprise-Grid-organization){:target="_blank"} for instructions. ## Setting Up a Webhook for Notifications To set up a webhook for sending notifications to your Slack workspace: -- Create a [Slack app](https://api.slack.com/apps/new), following the instructions by signing in, adding name and workspace to put the app in. +- Create a [Slack app](https://api.slack.com/apps/new){:target="_blank"}, following the instructions by signing in, adding name and workspace to put the app in. - After this, you'll be redirected to the setting pages of your new app. Choose **Incoming Webhook** and toggle **Activate Webhook**. - Scroll down to click the option **Add New Webhook to Workspace**. ![Alt text](./screenshots/select-channel.png) @@ -33,4 +33,4 @@ To set up a webhook for sending notifications to your Slack workspace: ![Alt text](./screenshots/Postman.png) - Afterwards, the text channel should have a new message ![Alt text](./screenshots/result.png) -- Go to [Slack's webhook documentation](https://api.slack.com/messaging/webhooks) to learn more about setting up webhooks. +- Go to [Slack's webhook documentation](https://api.slack.com/messaging/webhooks){:target="_blank"} to learn more about setting up webhooks. diff --git a/code-samples/eventing/bookstore-sample-app/solution/db-service/README.md b/code-samples/eventing/bookstore-sample-app/solution/db-service/README.md index 0cfcb7eaa24..ad10e638feb 100644 --- a/code-samples/eventing/bookstore-sample-app/solution/db-service/README.md +++ b/code-samples/eventing/bookstore-sample-app/solution/db-service/README.md @@ -89,20 +89,20 @@ If the output lists the `BookReviews` table as follows, your database has been c ## Question & Discussion 1. Why did we choose to deploy our PostgreSQL database using a StatefulSet instead of a Knative Service? -We use [StatefulSet](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/) for databases instead of Knative Service mainly because databases need to remember data (like a notebook that keeps your notes). StatefulSets are good at remembering things because they can save data and have a special name and place where they live. This is very important for databases. +We use [StatefulSet](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/){:target="_blank"} for databases instead of Knative Service mainly because databases need to remember data (like a notebook that keeps your notes). StatefulSets are good at remembering things because they can save data and have a special name and place where they live. This is very important for databases. Knative Services are more like notebooks that you use and then throw away when you're done. They're great for tasks that don't need to keep data for a long time. You can make them go away when you don't need them and come back when you do. But databases need to always remember information, so they can't just disappear and come back. Also, databases often talk in their own special language, not the usual web language (HTTP) that Knative Services are really good at understanding. Because of this, Knative Services aren't the best choice for databases. That's why we choose StatefulSet for databases in Kubernetes. --- -Note box: However, Knative Service supports Volumes and Persistent Volumes, which can be used to store data. You can read more [here](https://knative.dev/docs/serving/services/storage/) about how to use Volumes and Persistent Volumes with Knative Services specially for your use case. +Note box: However, Knative Service supports Volumes and Persistent Volumes, which can be used to store data. You can read more [here](https://knative.dev/docs/serving/services/storage/){:target="_blank"} about how to use Volumes and Persistent Volumes with Knative Services specially for your use case. --- 2. When should I use Knative Service, and what would be the best use case for it? -You can read more about the best use cases for Knative Service [here](https://knative.dev/docs/serving/samples/)! +You can read more about the best use cases for Knative Service [here](https://knative.dev/docs/serving/samples/){:target="_blank"}! ## Conclusion By following this guide, you have successfully deployed a PostgreSQL server on a Kubernetes cluster, set up persistent storage, and initialized your database using a Kubernetes job. Congratulations! Your bookstore now has the database service. \ No newline at end of file diff --git a/code-samples/eventing/bookstore-sample-app/solution/frontend/README.md b/code-samples/eventing/bookstore-sample-app/solution/frontend/README.md index af304a94822..02f22bb1310 100644 --- a/code-samples/eventing/bookstore-sample-app/solution/frontend/README.md +++ b/code-samples/eventing/bookstore-sample-app/solution/frontend/README.md @@ -18,7 +18,7 @@ pnpm dev bun dev ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +Open [http://localhost:3000](http://localhost:3000){:target="_blank"} with your browser to see the result. # Project Structures @@ -33,7 +33,7 @@ This repository contains a Next.js application that utilizes next-themes and Tai ## Prerequisites -- Docker installed on your machine. You can download and install Docker from [here](https://www.docker.com/get-started). +- Docker installed on your machine. You can download and install Docker from [here](https://www.docker.com/get-started){:target="_blank"}. ## Dockerization Steps diff --git a/code-samples/eventing/bookstore-sample-app/solution/node-server/config/100-event-display.yaml b/code-samples/eventing/bookstore-sample-app/solution/node-server/config/100-event-display.yaml index b4e8ce41758..0d21f8b4cf8 100644 --- a/code-samples/eventing/bookstore-sample-app/solution/node-server/config/100-event-display.yaml +++ b/code-samples/eventing/bookstore-sample-app/solution/node-server/config/100-event-display.yaml @@ -1,4 +1,3 @@ - apiVersion: apps/v1 kind: Deployment metadata: diff --git a/code-samples/eventing/bookstore-sample-app/solution/node-server/config/300-sinkbinding.yaml b/code-samples/eventing/bookstore-sample-app/solution/node-server/config/300-sinkbinding.yaml index 6d206375a7c..364d0093228 100644 --- a/code-samples/eventing/bookstore-sample-app/solution/node-server/config/300-sinkbinding.yaml +++ b/code-samples/eventing/bookstore-sample-app/solution/node-server/config/300-sinkbinding.yaml @@ -10,7 +10,7 @@ spec: selector: matchLabels: app: node-server - sink: # In this case, the sink is our broker, which is the eventing service that will receive the events + sink: # In this case, the sink is our Broker, which is the eventing service that will receive the events ref: apiVersion: eventing.knative.dev/v1 kind: Broker diff --git a/code-samples/eventing/bookstore-sample-app/solution/node-server/index.js b/code-samples/eventing/bookstore-sample-app/solution/node-server/index.js index 9480cf684e5..1a171a3c17d 100644 --- a/code-samples/eventing/bookstore-sample-app/solution/node-server/index.js +++ b/code-samples/eventing/bookstore-sample-app/solution/node-server/index.js @@ -98,7 +98,7 @@ app.post('/add', async (req, res) => { const brokerURI = process.env.K_SINK; if (receivedEvent.type === 'new-review-comment') { - // Forward the event to the broker with the necessary CloudEvent headers + // Forward the event to the Broker with the necessary CloudEvent headers const response = await fetch(brokerURI, { method: 'POST', headers: { diff --git a/code-samples/eventing/bookstore-sample-app/solution/sequence/config/100-create-sequence.yaml b/code-samples/eventing/bookstore-sample-app/solution/sequence/config/100-create-sequence.yaml index e1d8a67e0b9..289b8376f9d 100644 --- a/code-samples/eventing/bookstore-sample-app/solution/sequence/config/100-create-sequence.yaml +++ b/code-samples/eventing/bookstore-sample-app/solution/sequence/config/100-create-sequence.yaml @@ -15,7 +15,7 @@ spec: apiVersion: serving.knative.dev/v1 kind: Service name: sentiment-analysis-app - reply: # This is the last step of the sequence, it will send the event back to the broker as reply + reply: # This is the last step of the sequence, it will send the event back to the Broker as reply ref: kind: Broker apiVersion: eventing.knative.dev/v1 diff --git a/code-samples/eventing/bookstore-sample-app/solution/setup.sh b/code-samples/eventing/bookstore-sample-app/solution/setup.sh index b2f3339fdc7..9aff9683d5e 100755 --- a/code-samples/eventing/bookstore-sample-app/solution/setup.sh +++ b/code-samples/eventing/bookstore-sample-app/solution/setup.sh @@ -21,10 +21,10 @@ kubectl apply -f https://github.com/knative/eventing/releases/download/knative-v kubectl apply -f https://github.com/knative/eventing/releases/download/knative-v1.14.0/eventing-core.yaml echo "Knative Eventing installed successfully." -# Install Knative imc broker +# Install Knative imc Broker kubectl apply -f https://github.com/knative/eventing/releases/download/knative-v1.14.0/in-memory-channel.yaml kubectl apply -f https://github.com/knative/eventing/releases/download/knative-v1.14.0/mt-channel-broker.yaml -echo "Knative in-memory channel and broker installed successfully." +echo "Knative in-memory Channel and Broker installed successfully." # Detect whether the user has knative function "func" installed if ! command -v func &> /dev/null diff --git a/code-samples/eventing/bookstore-sample-app/solution/slack-sink/README.md b/code-samples/eventing/bookstore-sample-app/solution/slack-sink/README.md index 737e6cf59c8..cb87caa581f 100644 --- a/code-samples/eventing/bookstore-sample-app/solution/slack-sink/README.md +++ b/code-samples/eventing/bookstore-sample-app/solution/slack-sink/README.md @@ -11,7 +11,7 @@ When a CloudEvent with the type `new-review-comment` is sent to the Knative Even ## Install prerequisites ### Prerequisite 1: Install Camel CLI -Install the Camel K CLI (`kamel`) on your local machine. You can find the installation instructions [here](https://camel.apache.org/camel-k/2.2.x/cli/cli.html). +Install the Camel K CLI (`kamel`) on your local machine. You can find the installation instructions [here](https://camel.apache.org/camel-k/2.2.x/cli/cli.html){:target="_blank"}. **Troubleshot**: If after installation you run `kamel version` and you get an error message, you may need to add the `kamel` binary to your system's PATH. You can do this by moving the `kamel` binary to a directory that is already in your PATH, or by adding the directory where `kamel` is located to your PATH. @@ -29,7 +29,7 @@ $ kamel install --registry docker.io --organization --regist Replace the placeholders with your actual Docker registry information. -If you are using other container registries, you may need to read more [here](https://camel.apache.org/camel-k/2.2.x/installation/registry/registry.html) for the installation. +If you are using other container registries, you may need to read more [here](https://camel.apache.org/camel-k/2.2.x/installation/registry/registry.html){:target="_blank"} for the installation. You will see this message if the installation is successful: @@ -43,19 +43,19 @@ Follow the instruction here on how to create the slack workspace and generate an ## Implementation ### Step 1: Create the Broker -This broker is created solely for testing purposes and is intended for temporary use during this part of the tutorial only. +This Broker is created solely for testing purposes and is intended for temporary use during this part of the tutorial only. -**Method 1**: Initialize a broker within your Kubernetes cluster using the Knative CLI: +**Method 1**: Initialize a Broker within your Kubernetes cluster using the Knative CLI: ```bash $ kn broker create book-review-broker ``` -You will see this message if the broker is created successfully: +You will see this message if the Broker is created successfully: ``` -Broker ‘book-review-broker’ successfully created in namespace ‘default’. +Broker 'book-review-broker' successfully created in namespace 'default'. ``` -**Method 2**: You can create a new YAML file to create the broker: +**Method 2**: You can create a new YAML file to create the Broker: *new-knative-broker.yaml* ```yaml @@ -70,7 +70,7 @@ After you saved the file, you can apply the configuration to your Kubernetes clu ```bash $ kubectl apply -f new-knative-broker.yaml ``` -You will see this message if the broker is created successfully: +You will see this message if the Broker is created successfully: ``` broker.eventing.knative.dev/book-review-broker created @@ -139,9 +139,9 @@ slack-sink-pipe Ready 1 To trigger notifications, you'll need to simulate an event that matches the criteria set in your Slack sink configuration. For example, submitting a book review could be an event of type `new-review-comment`. -Directly sending CloudEvents to a broker using curl from an external machine (like your local computer) is typically **constrained** due to the networking and security configurations of Kubernetes clusters. +Directly sending CloudEvents to a Broker using curl from an external machine (like your local computer) is typically **constrained** due to the networking and security configurations of Kubernetes clusters. -Therefore, you need to create a new pod in your Kubernetes cluster to send a CloudEvent to the broker. You can use the following command to create a new pod: +Therefore, you need to create a new pod in your Kubernetes cluster to send a CloudEvent to the Broker. You can use the following command to create a new pod: ```bash $ kubectl run curler --image=radial/busyboxplus:curl -it --restart=Never @@ -154,9 +154,9 @@ If you don't see a command prompt, try pressing enter. ``` -Using curl command to send a CloudEvent to the broker: +Using curl command to send a CloudEvent to the Broker: ```bash -[root@curler:/]$ curl -v "" \ +[root@curler:/]$ curl -v "" \ -X POST \ -H "Ce-Id: review1" \ -H "Ce-Specversion: 1.0" \ @@ -166,7 +166,7 @@ Using curl command to send a CloudEvent to the broker: -d 'Hello from Knative!' ``` -You can find the URI to your broker by running the following command: +You can find the URI to your Broker by running the following command: ```bash $ kubectl get broker book-review-broker diff --git a/code-samples/eventing/bookstore-sample-app/start/db-service/README.md b/code-samples/eventing/bookstore-sample-app/start/db-service/README.md index 0cfcb7eaa24..32d1853ea0e 100644 --- a/code-samples/eventing/bookstore-sample-app/start/db-service/README.md +++ b/code-samples/eventing/bookstore-sample-app/start/db-service/README.md @@ -89,20 +89,20 @@ If the output lists the `BookReviews` table as follows, your database has been c ## Question & Discussion 1. Why did we choose to deploy our PostgreSQL database using a StatefulSet instead of a Knative Service? -We use [StatefulSet](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/) for databases instead of Knative Service mainly because databases need to remember data (like a notebook that keeps your notes). StatefulSets are good at remembering things because they can save data and have a special name and place where they live. This is very important for databases. +We use [StatefulSet](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/){:target="_blank"} for databases instead of Knative Service mainly because databases need to remember data (like a notebook that keeps your notes). StatefulSets are good at remembering things because they can save data and have a special name and place where they live. This is very important for databases. Knative Services are more like notebooks that you use and then throw away when you're done. They're great for tasks that don't need to keep data for a long time. You can make them go away when you don't need them and come back when you do. But databases need to always remember information, so they can't just disappear and come back. Also, databases often talk in their own special language, not the usual web language (HTTP) that Knative Services are really good at understanding. Because of this, Knative Services aren't the best choice for databases. That's why we choose StatefulSet for databases in Kubernetes. --- -Note box: However, Knative Service supports Volumes and Persistent Volumes, which can be used to store data. You can read more [here](https://knative.dev/docs/serving/services/storage/) about how to use Volumes and Persistent Volumes with Knative Services specially for your use case. +Note box: However, Knative Service supports Volumes and Persistent Volumes, which can be used to store data. You can read more [here](https://knative.dev/docs/serving/services/storage/){:target="_blank"} about how to use Volumes and Persistent Volumes with Knative Services specially for your use case. --- 2. When should I use Knative Service, and what would be the best use case for it? -You can read more about the best use cases for Knative Service [here](https://knative.dev/docs/serving/samples/)! +You can read more about the best use cases for Knative Service [here](https://knative.dev/docs/serving/samples/{:target="_blank"}! ## Conclusion By following this guide, you have successfully deployed a PostgreSQL server on a Kubernetes cluster, set up persistent storage, and initialized your database using a Kubernetes job. Congratulations! Your bookstore now has the database service. \ No newline at end of file diff --git a/code-samples/eventing/bookstore-sample-app/start/frontend/README.md b/code-samples/eventing/bookstore-sample-app/start/frontend/README.md index af304a94822..02f22bb1310 100644 --- a/code-samples/eventing/bookstore-sample-app/start/frontend/README.md +++ b/code-samples/eventing/bookstore-sample-app/start/frontend/README.md @@ -18,7 +18,7 @@ pnpm dev bun dev ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +Open [http://localhost:3000](http://localhost:3000){:target="_blank"} with your browser to see the result. # Project Structures @@ -33,7 +33,7 @@ This repository contains a Next.js application that utilizes next-themes and Tai ## Prerequisites -- Docker installed on your machine. You can download and install Docker from [here](https://www.docker.com/get-started). +- Docker installed on your machine. You can download and install Docker from [here](https://www.docker.com/get-started){:target="_blank"}. ## Dockerization Steps diff --git a/code-samples/eventing/bookstore-sample-app/start/node-server/index.js b/code-samples/eventing/bookstore-sample-app/start/node-server/index.js index 9480cf684e5..1a171a3c17d 100644 --- a/code-samples/eventing/bookstore-sample-app/start/node-server/index.js +++ b/code-samples/eventing/bookstore-sample-app/start/node-server/index.js @@ -98,7 +98,7 @@ app.post('/add', async (req, res) => { const brokerURI = process.env.K_SINK; if (receivedEvent.type === 'new-review-comment') { - // Forward the event to the broker with the necessary CloudEvent headers + // Forward the event to the Broker with the necessary CloudEvent headers const response = await fetch(brokerURI, { method: 'POST', headers: { diff --git a/code-samples/eventing/bookstore-sample-app/start/setup.sh b/code-samples/eventing/bookstore-sample-app/start/setup.sh index 4fefbcd4b4e..984cabc7812 100755 --- a/code-samples/eventing/bookstore-sample-app/start/setup.sh +++ b/code-samples/eventing/bookstore-sample-app/start/setup.sh @@ -21,10 +21,10 @@ kubectl apply -f https://github.com/knative/eventing/releases/download/knative-v kubectl apply -f https://github.com/knative/eventing/releases/download/knative-v1.14.0/eventing-core.yaml echo "Knative Eventing installed successfully." -# Install Knative imc broker +# Install Knative imc Broker kubectl apply -f https://github.com/knative/eventing/releases/download/knative-v1.14.0/in-memory-channel.yaml kubectl apply -f https://github.com/knative/eventing/releases/download/knative-v1.14.0/mt-channel-broker.yaml -echo "Knative in-memory channel and broker installed successfully." +echo "Knative in-memory Channel and Broker installed successfully." # Detect whether the user has knative function "func" installed if ! command -v func &> /dev/null diff --git a/code-samples/eventing/bookstore-sample-app/start/slack-sink/README.md b/code-samples/eventing/bookstore-sample-app/start/slack-sink/README.md index 737e6cf59c8..cdf11964215 100644 --- a/code-samples/eventing/bookstore-sample-app/start/slack-sink/README.md +++ b/code-samples/eventing/bookstore-sample-app/start/slack-sink/README.md @@ -11,7 +11,7 @@ When a CloudEvent with the type `new-review-comment` is sent to the Knative Even ## Install prerequisites ### Prerequisite 1: Install Camel CLI -Install the Camel K CLI (`kamel`) on your local machine. You can find the installation instructions [here](https://camel.apache.org/camel-k/2.2.x/cli/cli.html). +Install the Camel K CLI (`kamel`) on your local machine. You can find the installation instructions [here](https://camel.apache.org/camel-k/2.2.x/cli/cli.html){:target="_blank"}. **Troubleshot**: If after installation you run `kamel version` and you get an error message, you may need to add the `kamel` binary to your system's PATH. You can do this by moving the `kamel` binary to a directory that is already in your PATH, or by adding the directory where `kamel` is located to your PATH. @@ -29,7 +29,7 @@ $ kamel install --registry docker.io --organization --regist Replace the placeholders with your actual Docker registry information. -If you are using other container registries, you may need to read more [here](https://camel.apache.org/camel-k/2.2.x/installation/registry/registry.html) for the installation. +If you are using other container registries, you may need to read more [here](https://camel.apache.org/camel-k/2.2.x/installation/registry/registry.html){:target="_blank"} for the installation. You will see this message if the installation is successful: @@ -43,19 +43,19 @@ Follow the instruction here on how to create the slack workspace and generate an ## Implementation ### Step 1: Create the Broker -This broker is created solely for testing purposes and is intended for temporary use during this part of the tutorial only. +This Broker is created solely for testing purposes and is intended for temporary use during this part of the tutorial only. -**Method 1**: Initialize a broker within your Kubernetes cluster using the Knative CLI: +**Method 1**: Initialize a Broker within your Kubernetes cluster using the Knative CLI: ```bash $ kn broker create book-review-broker ``` -You will see this message if the broker is created successfully: +You will see this message if the Broker is created successfully: ``` -Broker ‘book-review-broker’ successfully created in namespace ‘default’. +Broker 'book-review-broker' successfully created in namespace 'default'. ``` -**Method 2**: You can create a new YAML file to create the broker: +**Method 2**: You can create a new YAML file to create the Broker: *new-knative-broker.yaml* ```yaml @@ -70,7 +70,7 @@ After you saved the file, you can apply the configuration to your Kubernetes clu ```bash $ kubectl apply -f new-knative-broker.yaml ``` -You will see this message if the broker is created successfully: +You will see this message if the Broker is created successfully: ``` broker.eventing.knative.dev/book-review-broker created @@ -79,7 +79,7 @@ broker.eventing.knative.dev/book-review-broker created ### Step 2: Configure the Slack Sink -We use a feature called "Pipe" in Camel K to link event sources and destinations. Specifically, the Pipe connects events from a Broker, our source, to a Slack channel through a Slack sink Kamelet, our destination. This setup automatically sends notifications to Slack whenever new events occur, streamlining the flow of information. +We use a feature called "Pipe" in Apache Camel K to link event sources and destinations. Specifically, the Pipe connects events from a Broker, our source, to a Slack channel through a Slack sink Kamelet, our destination. This setup automatically sends notifications to Slack whenever new events occur, streamlining the flow of information. 1. Create a Slack app and generate an incoming webhook URL for your designated channel where notifications will be sent. Refer to Slack documentation for how to do this. @@ -139,9 +139,9 @@ slack-sink-pipe Ready 1 To trigger notifications, you'll need to simulate an event that matches the criteria set in your Slack sink configuration. For example, submitting a book review could be an event of type `new-review-comment`. -Directly sending CloudEvents to a broker using curl from an external machine (like your local computer) is typically **constrained** due to the networking and security configurations of Kubernetes clusters. +Directly sending CloudEvents to a Broker using curl from an external machine (like your local computer) is typically **constrained** due to the networking and security configurations of Kubernetes clusters. -Therefore, you need to create a new pod in your Kubernetes cluster to send a CloudEvent to the broker. You can use the following command to create a new pod: +Therefore, you need to create a new pod in your Kubernetes cluster to send a CloudEvent to the Broker. You can use the following command to create a new pod: ```bash $ kubectl run curler --image=radial/busyboxplus:curl -it --restart=Never @@ -154,7 +154,7 @@ If you don't see a command prompt, try pressing enter. ``` -Using curl command to send a CloudEvent to the broker: +Using curl command to send a CloudEvents to the Broker: ```bash [root@curler:/]$ curl -v "" \ -X POST \ @@ -166,7 +166,7 @@ Using curl command to send a CloudEvent to the broker: -d 'Hello from Knative!' ``` -You can find the URI to your broker by running the following command: +You can find the URI to your Broker by running the following command: ```bash $ kubectl get broker book-review-broker diff --git a/code-samples/eventing/bookstore-sample-app/start/slack-sink/application.properties b/code-samples/eventing/bookstore-sample-app/start/slack-sink/application.properties index 264065539b2..022b56a1eb8 100644 --- a/code-samples/eventing/bookstore-sample-app/start/slack-sink/application.properties +++ b/code-samples/eventing/bookstore-sample-app/start/slack-sink/application.properties @@ -16,4 +16,4 @@ # slack.channel=#bookstore-owner -slack.webhook.url= \ No newline at end of file +slack.webhook.url=https://hooks.slack.com/services/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 \ No newline at end of file diff --git a/code-samples/eventing/bookstore-sample-app/start/slack-sink/create-workspace.md b/code-samples/eventing/bookstore-sample-app/start/slack-sink/create-workspace.md index 5027b8df32e..b108150adc2 100644 --- a/code-samples/eventing/bookstore-sample-app/start/slack-sink/create-workspace.md +++ b/code-samples/eventing/bookstore-sample-app/start/slack-sink/create-workspace.md @@ -4,12 +4,12 @@ To create a Slack workspace, follow these steps: -- Visit [https://slack.com/get-started#/createnew](https://slack.com/get-started#/createnew). +- Visit [https://slack.com/get-started#/createnew](https://slack.com/get-started#/createnew){:target="_blank"}. - Enter your email address and click Continue, or continue with Apple or Google. - Check your email for a confirmation code. - Enter the code on the Slack website. - Click "Create a Workspace" and follow the prompts. -- For detailed instructions for androids and , refer to [Create a workspace](https://slack.com/help/articles/206845317-Create-a-Slack-workspace). +- For detailed instructions for androids and , refer to [Create a workspace](https://slack.com/help/articles/206845317-Create-a-Slack-workspace){:target="_blank"}. ## Adding Channels to Your Workspace @@ -17,13 +17,13 @@ After creating your Slack workspace, you can add channels by following these ste - Click the '+' icon next to 'Channels' on the sidebar of your Slack workspace. - Choose "Create a channel" and specify the channel name and purpose. -- For enterprise grid organizations, refer to [Slack's help article](https://slack.com/help/articles/115001399587-Add-a-channel-to-multiple-workspaces-in-your-Enterprise-Grid-organization) for instructions. +- For enterprise grid organizations, refer to [Slack's help article](https://slack.com/help/articles/115001399587-Add-a-channel-to-multiple-workspaces-in-your-Enterprise-Grid-organization){:target="_blank"} for instructions. ## Setting Up a Webhook for Notifications To set up a webhook for sending notifications to your Slack workspace: -- Create a [Slack app](https://api.slack.com/apps/new), following the instructions by signing in, adding name and workspace to put the app in. +- Create a [Slack app](https://api.slack.com/apps/new){:target="_blank"}, following the instructions by signing in, adding name and workspace to put the app in. - After this, you'll be redirected to the setting pages of your new app. Choose **Incoming Webhook** and toggle **Activate Webhook**. - Scroll down to click the option **Add New Webhook to Workspace**. ![Alt text](./screenshots/select-channel.png) diff --git a/config/nav.yml b/config/nav.yml index 313e634e015..db0623f98d6 100644 --- a/config/nav.yml +++ b/config/nav.yml @@ -20,24 +20,38 @@ nav: # Getting started ############################################################################### - Tutorial: - - Tutorial introduction: getting-started/README.md - - Install Knative using quickstart: getting-started/quickstart-install.md - - Knative Functions: - - About Knative Functions: getting-started/about-knative-functions.md - - Installing Knative Functions: getting-started/install-func.md - - Creating a function: getting-started/create-a-function.md - - Building, running, or deploying a function: getting-started/build-run-deploy-func.md - - Knative Serving: - - Deploying a Knative Service: getting-started/first-service.md - - Autoscaling: getting-started/first-autoscale.md - - Traffic splitting: getting-started/first-traffic-split.md - - Knative Eventing: - - About Knative Eventing: getting-started/getting-started-eventing.md - - Sources, Brokers, and Triggers: getting-started/first-broker.md - - Using a Knative Service as a source: getting-started/first-source.md - - Using Triggers and sinks: getting-started/first-trigger.md - - What's Next?: getting-started/next-steps.md - - Clean Up: getting-started/clean-up.md + - Overview: getting-started/tutorial.md + - Quickstart: + - Tutorial introduction: getting-started/README.md + - Install Knative using quickstart: getting-started/quickstart-install.md + - Knative Functions: + - About Knative Functions: getting-started/about-knative-functions.md + - Installing Knative Functions: getting-started/install-func.md + - Creating a function: getting-started/create-a-function.md + - Building, running, or deploying a function: getting-started/build-run-deploy-func.md + - Knative Serving: + - Deploying a Knative Service: getting-started/first-service.md + - Autoscaling: getting-started/first-autoscale.md + - Traffic splitting: getting-started/first-traffic-split.md + - Knative Eventing: + - About Knative Eventing: getting-started/getting-started-eventing.md + - Sources, Brokers, and Triggers: getting-started/first-broker.md + - Using a Knative Service as a source: getting-started/first-source.md + - Using Triggers and sinks: getting-started/first-trigger.md + - What's Next?: getting-started/next-steps.md + - Clean Up: getting-started/clean-up.md + - "E2E tutorial: Knative Bookstore": + - Overview: bookstore/page-0/welcome-knative-bookstore-tutorial.md + - Disclaimer: bookstore/disclaimer/README.md + - Environment Setup: bookstore/page-0.5/environment-setup.md + - 1 - Send Comments to Broker: bookstore/page-1/send-review-comment-to-broker.md + - 2 - Create Sentiment Service: bookstore/page-2/sentiment-analysis-service-for-bookstore-reviews.md + - 3 - Create Bad Word Service: bookstore/page-3/create-bad-word-filter-service.md + - 4 - Create Sequence: bookstore/page-4/create-sequence-to-streamline-ML-workflows.md + - 5 - Create DB service: bookstore/page-5/deploy-database-service.md + - 6 - Advanced event filtering: bookstore/page-6/advanced-event-filtering.md + - 7 - Connect Slack via Camel-K: bookstore/page-7/slack-sink-learning-knative-eventing-and-apache-camel-K-integration.md + - 8 - Extra Challenges: bookstore/extra-challenge/README.md ############################################################################### # Installing ############################################################################### diff --git a/docs/bookstore/create-slack-workspace/README.md b/docs/bookstore/create-slack-workspace/README.md new file mode 100644 index 00000000000..9768a751b16 --- /dev/null +++ b/docs/bookstore/create-slack-workspace/README.md @@ -0,0 +1,46 @@ +# Creating a Slack Workspace + +![Image](images/image5.png) + +To create a Slack workspace, follow these steps: + +1. Visit [https://slack.com/get-started#/createnew](https://slack.com/get-started#/createnew){:target="_blank"}. +2. Enter your email address and click Continue, or continue with Apple or Google. +3. Check your email for a confirmation code. +4. Enter the code on the Slack website. +5. Click "Create a Workspace" and follow the prompts. +6. For detailed instructions how to create a workspace via the Desktop App, Android or iOS, refer to [Create a Slack workspace](https://slack.com/help/articles/206845317-Create-a-Slack-workspace){:target="_blank"}. + +## **Adding Channels to Your Workspace** + +After creating your Slack workspace, you can add channels by following these steps: + +1. Click the '+' icon next to 'Channels' on the sidebar of your Slack workspace. +2. Choose "Create a channel" and specify the channel name and purpose. +3. For enterprise grid organizations, refer to [Slack's help article](https://slack.com/help/articles/115001399587-Add-a-channel-to-multiple-workspaces-in-your-Enterprise-Grid-organization){:target="_blank"} for instructions. + +## **Setting Up a Webhook for Notifications** + +To set up a webhook for sending notifications to your Slack workspace: + +- 1: Create a [Slack app](https://api.slack.com/apps/new){:target="_blank"}, following the instructions by signing in, adding name and workspace to put the app in. + +- 2: After this, you'll be redirected to the setting pages of your new app. Choose Incoming Webhook and toggle Activate Webhook. + +- 3: Scroll down to click the option Add New Webhook to Workspace. + +![Image](images/image3.png) + +- 4: The page will give you a link associated with the app you created. This link will be where API requests are sent to send messages to your Slack workspace. + +![Image](images/image2.png) + +- 5: To test if the webhook works as needed, you can try sending an API request to this link to see if the message goes through. I used Postman to test this. Remember that this is an `application/json` file. + +![Image](images/image4.png) + +- 6: Afterwards, the text channel should have a new message. + +![Image](images/image1.png) + +- 7: Go to [Slack's webhook documentation](https://api.slack.com/messaging/webhooks){:target="_blank"} to learn more about setting up webhooks. \ No newline at end of file diff --git a/docs/bookstore/create-slack-workspace/images/image1.png b/docs/bookstore/create-slack-workspace/images/image1.png new file mode 100644 index 00000000000..850efabdfb8 Binary files /dev/null and b/docs/bookstore/create-slack-workspace/images/image1.png differ diff --git a/docs/bookstore/create-slack-workspace/images/image2.png b/docs/bookstore/create-slack-workspace/images/image2.png new file mode 100644 index 00000000000..01b797928d5 Binary files /dev/null and b/docs/bookstore/create-slack-workspace/images/image2.png differ diff --git a/docs/bookstore/create-slack-workspace/images/image3.png b/docs/bookstore/create-slack-workspace/images/image3.png new file mode 100644 index 00000000000..93546ea06d1 Binary files /dev/null and b/docs/bookstore/create-slack-workspace/images/image3.png differ diff --git a/docs/bookstore/create-slack-workspace/images/image4.png b/docs/bookstore/create-slack-workspace/images/image4.png new file mode 100644 index 00000000000..aa21dceccd5 Binary files /dev/null and b/docs/bookstore/create-slack-workspace/images/image4.png differ diff --git a/docs/bookstore/create-slack-workspace/images/image5.png b/docs/bookstore/create-slack-workspace/images/image5.png new file mode 100644 index 00000000000..d80ba2f99e1 Binary files /dev/null and b/docs/bookstore/create-slack-workspace/images/image5.png differ diff --git a/docs/bookstore/disclaimer/README.md b/docs/bookstore/disclaimer/README.md new file mode 100644 index 00000000000..83885b6dee7 --- /dev/null +++ b/docs/bookstore/disclaimer/README.md @@ -0,0 +1,57 @@ +# **Disclaimer** + +![image](images/image2.png) + +Hello dear Knative Learners, + +Before you begin this tutorial, **please take note of the following disclaimer**. In the spirit of open source, we strive for transparency. This tutorial incorporates many other open source projects, and we are grateful to all the incredible contributors. Your amazing work has made this tutorial possible. + +If you're interested in getting started with open source, check out our guide: Get Started with Open Source at Knative. + +Thank you, and happy learning! + +### **Open Source Libraries Used** + +This tutorial makes use of several open source libraries to build the bookstore application. Below are the libraries used along with their respective documentation and project links: + +- [Profanity Check](https://pypi.org/project/profanity-check/) +- [Flask](https://flask.palletsprojects.com/en/3.0.x/) +- [CloudEvents](https://cloudevents.io/) +- [TextBlob](https://textblob.readthedocs.io/en/dev/) +- [Node.js](https://nodejs.org/en) +- [Next.js](https://nextjs.org/) +- [PostgreSQL](https://www.postgresql.org/) +- [Apache Camel](https://camel.apache.org/) + +### **Music Used in the Demo Video** + +The music used in the demo video is sourced from Uppbeat, which provides free music for creators: + +- Music: [Trendsetter](https://uppbeat.io/t/mood-maze/trendsetter) by Mood Maze +- License Code: JHDMCOJT3ZC4EVUB + +### **Graphic Creation License** + +Graphics used in this tutorial were created using Canva: + +- Canva License: [Content License Agreement](https://www.canva.com/policies/content-license-agreement) + +### **Results of ML Services in This Tutorial** + +The results of the sentiment analysis and inappropriate language filter (a.k.a bad word filter) are generated using the open source libraries mentioned above. Knative **is not responsible** for the accuracy or reliability of the output generated by these libraries. + +### **General Disclaimer** + +This tutorial is provided for educational purposes only. While every effort has been made to ensure the accuracy and reliability of the information provided, the authors and contributors of this tutorial make no guarantees, representations, or warranties of any kind, express or implied, about the completeness, accuracy, reliability, suitability, or availability of the information contained within the tutorial for any purpose. + +By using this tutorial, you acknowledge that: + +- You are responsible for verifying the information and results obtained from the tutorial. +- The tutorial may contain links to external websites and resources; the authors and contributors of this tutorial are not responsible for the content, privacy policies, or practices of these external websites and resources. +- The open source libraries used in this tutorial are third-party products; the authors and contributors of this tutorial are not responsible for the performance, accuracy, or reliability of these libraries. + +### **Contact** + +![image](images/image1.png) + +If you have any questions or concerns regarding this disclaimer or the tutorial, please feel free to reach out to the Knative community via the Cloud Native Computing Foundation (CNCF) Slack, particularly the [#knative](https://cloud-native.slack.com/archives/C04LGHDR9K7){:target="_blank"} channel. \ No newline at end of file diff --git a/docs/bookstore/disclaimer/images/image1.png b/docs/bookstore/disclaimer/images/image1.png new file mode 100644 index 00000000000..7e8a728d744 Binary files /dev/null and b/docs/bookstore/disclaimer/images/image1.png differ diff --git a/docs/bookstore/disclaimer/images/image2.png b/docs/bookstore/disclaimer/images/image2.png new file mode 100644 index 00000000000..35cde3bddd1 Binary files /dev/null and b/docs/bookstore/disclaimer/images/image2.png differ diff --git a/docs/bookstore/extra-challenge/README.md b/docs/bookstore/extra-challenge/README.md new file mode 100644 index 00000000000..b0548187e68 --- /dev/null +++ b/docs/bookstore/extra-challenge/README.md @@ -0,0 +1,130 @@ +# **Extra Challenges** + +![image](images/image5.png) + +Congratulations on completing the bookstore tutorial! Kubernetes and Knative have a high learning curve due to their complexity, but you did it! You should be proud of your accomplishment. + +Now it's time to test your mastery of the skills you've learned with Kuack. We've prepared a few extra challenges for you to explore. Take a look and have fun! + +## **Challenge 1: Integrate with Another Notification Service as Sink (Telegram)** + +![image](images/image6.png) + +**Description** + +Add Telegram notifications for new comments using Apache Camel K. + +**Objective** + +Send Telegram notifications for new comments. + +**Steps** + +1. Create a new event type or filter attribute for Telegram notifications. +2. Set up a Trigger to route these events to a Camel K integration service. +3. Use Apache Camel K to send messages to a Telegram chat by setting up the telegram-sink. + +**What You Will Learn** + +- Integrating with external notification services using Apache Camel K. +- Using Triggers to route specific events. +- Working with the Telegram API. + +**Estimated Time** + +0.5-1 hours + +## **Challenge 2: Implement Comment Deletion** + +![image](images/image1.png) + +**Description** + +Add functionality to delete comments from the database using an event-driven approach. + +**Objective** + +Allow users to delete the selected comment. + +**Steps** + +1. Create a new event type for comment deletion. +2. Set up a Trigger to listen for deletion events. +3. Implement a function in the node-server to handle the deletion and update the database. + +**What You will Learn:** + +- Creating and handling new event types. +- Setting up Triggers for specific events. +- Performing database operations based on events. + +**Estimated Time** + +1-2 hours + +## **Challenge 3: Format the Output for the Sink** + +![image](images/image4.png) + +**Description** + +Learn how to create an event consumer app that formats event data before sending it to a sink. + +**Objective** + +Transform event data and send it to the appropriate sink. + +**Steps** + +1. Create a new function in node-server to consume and transform events. +2. Set up a Trigger to route events (Event X - unformatted) to this transformation service. +3. Implement the transformation logic to convert Event X into Event Y - formatted. +4. Configure the service to send the transformed event (Event Y) to the Slack sink. +5. Ensure the function replies with the transformed event. + +**What You Will Learn** + +- Creating an event consumer app. +- Adding a new step in a Sequence to process events. +- Transforming events within a service and routing the transformed events to a sink. +- Using Triggers and functions with replies in an event-driven architecture. + +**Estimated Time** + +1-2 hours + +## **Challenge 4: Submit Comments via Telegram** + +![image](images/image3.png) + +**Description** + +Enable users to submit new comments through a Telegram bot using Apache Camel K. + +**Objective** + +Allow users to send comments via Telegram. + +**Steps** + +1. Create a Telegram bot and obtain the bot token. +2. Set up an [Apache Camel K Telegram source](https://camel.apache.org/camel-kamelets/4.4.x/telegram-source.html){:target="_blank"} to listen for messages from the Telegram bot. +3. Create a new event type for comments received via Telegram. +4. Set up a Trigger to process these events and route them to the comment handling service. +5. Implement a function in the node-server to handle the new comments and update the database. + +**What You Will Learn** + +- Creating and configuring a Telegram bot. +- Using Apache Camel K to integrate Telegram as an event source with your application. +- Handling new event types and processing user input from external sources. + +**Estimated Time** + +2-3 hours + +## **Help** + +![image](images/image2.png) + +Join the supportive Knative community via the Cloud Native Computing Foundation (CNCF) Slack, particularly the [#knative](https://cloud-native.slack.com/archives/C04LGHDR9K7){:target="_blank"} channel. Before posting your questions, please search to see if they've already been answered. Your feedback on this tutorial is invaluable, so don't hesitate to reach out with suggestions or questions. diff --git a/docs/bookstore/extra-challenge/images/image1.png b/docs/bookstore/extra-challenge/images/image1.png new file mode 100644 index 00000000000..e2a7aa3a036 Binary files /dev/null and b/docs/bookstore/extra-challenge/images/image1.png differ diff --git a/docs/bookstore/extra-challenge/images/image2.png b/docs/bookstore/extra-challenge/images/image2.png new file mode 100644 index 00000000000..7e8a728d744 Binary files /dev/null and b/docs/bookstore/extra-challenge/images/image2.png differ diff --git a/docs/bookstore/extra-challenge/images/image3.png b/docs/bookstore/extra-challenge/images/image3.png new file mode 100644 index 00000000000..2dbcb658012 Binary files /dev/null and b/docs/bookstore/extra-challenge/images/image3.png differ diff --git a/docs/bookstore/extra-challenge/images/image4.png b/docs/bookstore/extra-challenge/images/image4.png new file mode 100644 index 00000000000..a07c14e0ecb Binary files /dev/null and b/docs/bookstore/extra-challenge/images/image4.png differ diff --git a/docs/bookstore/extra-challenge/images/image5.png b/docs/bookstore/extra-challenge/images/image5.png new file mode 100644 index 00000000000..0571200fe77 Binary files /dev/null and b/docs/bookstore/extra-challenge/images/image5.png differ diff --git a/docs/bookstore/extra-challenge/images/image6.png b/docs/bookstore/extra-challenge/images/image6.png new file mode 100644 index 00000000000..62af99d288e Binary files /dev/null and b/docs/bookstore/extra-challenge/images/image6.png differ diff --git a/docs/bookstore/page-0.5/environment-setup.md b/docs/bookstore/page-0.5/environment-setup.md new file mode 100644 index 00000000000..90bd2feb51f --- /dev/null +++ b/docs/bookstore/page-0.5/environment-setup.md @@ -0,0 +1,306 @@ +# Environment Setup + +![Image](images/image20.png) + +In this page, we will be discussing how to set up your environment, and make sure to run up the UI front end and the Book Review Service’s node server. + +## **What does the final deliverable for this section look like?** + +- You have a running Kubernetes (k8s) cluster on your local machine, with Knative installed. +- You have your front end application deployed as Kubernetes deployment with port-forwarding to localhost:3000 +- You have your Node.js application deployed as Kubernetes deployment with port-forwarding to localhost:8080 + +We will be fulfilling each requirement with the order above. + +## **Clone the Repository** +![Next Step Image](images/image22.png) + +```sh +git clone https://github.com/knative/docs.git +``` +???+ bug "Troubleshooting" + Having issue with cloning your repo? Check [here](https://docs.github.com/en/repositories/creating-and-managing-repositories/troubleshooting-cloning-errors){:target="_blank"} for help. + +!!! warning + + **IMPORTANT**: Please take some time to read the [Disclaimer](../disclaimer/README.md){:target="_blank"} before moving to the next page. + + +## **File Structure** + +![Image](images/image21.png) + +The code for the sample app is in `docs/code-samples/eventing/bookstore-sample-app` + +Under `bookstore-sample-app` folder, there are 2 folders: + +* **/solution**: this folder contains all the yaml files, and the code you needed. Check it when you got stuck. + +* **/start**: this folder contains the necessary files for you to get started. Save there all the configuration files yourself following the tutorial! + +![Image](images/image16.png) + +!!! tip + Kuack suggests you to start from **/start**, write all the configuration files as you go over the tutorial, and check solutions when you got stuck. + +![Image](images/image1.png) + +Always `cd` back to the root directory, which is `/start`, before running any commands. + +All the commands in the tutorial are written **assuming you are in the `/start` directory**. + + +## **Shortcut** + +![Image](images/image10.png) + +Running `docs/code-samples/eventing/bookstore-sample-app/start/setup.sh` will automatically complete all tasks in this section. + +!!! warning + + However, if you are not familiar with the process, we recommend reviewing the steps below. + +## **Instructions** + +### **Task 1: Set Up a Running Kubernetes Cluster with Knative Installed** + +![Image](images/image13.png) + +Please follow the instructions [here](https://knative.dev/docs/install/){:target="_blank"} to spin up your cluster with Knative installed! + +???+ success "Verify" + + You should see the pods in the `knative-eventing` and `knative-serving` namespaces running before proceeding. + + ``` + NAMESPACE NAME READY STATUS RESTARTS AGE + knative-eventing eventing-controller-7576f555d5-7c2p2 1/1 Running 0 4m50s + knative-eventing eventing-webhook-5874bb8445-cqcn9 1/1 Running 0 4m50s + knative-eventing imc-controller-8c5d5ddb5-m249l 1/1 Running 0 4m49s + knative-eventing imc-dispatcher-76d9f7464b-dphd6 1/1 Running 0 4m49s + knative-eventing mt-broker-controller-8d8f8d48f-rvlcv 1/1 Running 0 4m48s + knative-eventing mt-broker-filter-85c457f879-dvhnj 1/1 Running 0 4m48s + knative-eventing mt-broker-ingress-5688f4cd68-nm8cc 1/1 Running 0 4m48s + knative-serving activator-55d856fccd-g5qpw 1/1 Running 0 4m53s + knative-serving autoscaler-5fb49c64c7-hrjng 1/1 Running 0 4m53s + knative-serving controller-ddbb9d4f-khttq 1/1 Running 0 4m53s + knative-serving net-kourier-controller-68d89f78d5-hw8r6 1/1 Running 0 4m52s + knative-serving webhook-85b9744fc5-6w9sg 1/1 Running 0 4m53s + kourier-system 3scale-kourier-gateway-dbc5b88f5-7g29n 1/1 Running 0 4m52s + kube-system coredns-5dd5756b68-49xsj 1/1 Running 0 12m + kube-system etcd-minikube 1/1 Running 0 12m + kube-system kube-apiserver-minikube 1/1 Running 0 12m + kube-system kube-controller-manager-minikube 1/1 Running 0 12m + kube-system kube-proxy-tqcvx 1/1 Running 0 12m + kube-system kube-scheduler-minikube 1/1 Running 0 12m + kube-system storage-provisioner 1/1 Running 0 12m + ``` + +#### **Extra Step for Minikube Users:** + +![Image](images/image3.png) + +Attention! In case you're not using the Knative Quick Start, set up the tunnel manually to connect to services of type `LoadBalancer`: + +Run the following command and keep the terminal open: + +```shell +minikube tunnel +``` + +???+ success "Verify" + If there aren't any error messages, it means you have set up the tunnel successfully. + +### **Task 2: Running the Bookstore Web App** + +![Image](images/image12.png) + +The Next.js frontend app is located in the `docs/code-samples/eventing/bookstore-sample-app/start/frontend` folder. + +Ensure that port 3000 on your local machine is not being used by another application. + +#### **Deploy the Frontend App** + +You can either [build the image locally](https://docs.docker.com/get-started/02_our_app/){:target="_blank"} or use our pre-built image. If you are using the pre-built image, you can proceed to the next step. + +When ready, run the following command to deploy the frontend app: + +```shell +kubectl apply -f frontend/config/100-front-end-deployment.yaml +``` + +This will create the Deployment and expose it with a Service of type LoadBalancer to receive external traffic: + +``` +deployment.apps/bookstore-frontend created +service/bookstore-frontend-svc created +``` + +???+ success "Verify" + Run the following command to check if the pod is running: + + ```shell + kubectl get pods + ``` + + You will see that your front end pod is running. + + ``` + NAME READY STATUS RESTARTS AGE + bookstore-frontend-7b879ffb78-9bln6 1/1 Running 0 4m37s + ``` + + + +#### **Port Forwarding (Optional under condition)** + +![Image](images/image9.png) + +You might need to set up port forwarding to access the app from your local machine. + +Check if port forwarding is necessary by running: + +```shell +kubectl get services +``` + +And you will see the following console output: +``` +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +bookstore-frontend-svc LoadBalancer 10.99.187.173 3000:31600/TCP 27m +kubernetes ClusterIP 10.96.0.1 443/TCP 39m +``` + + + +!!! note + If the `EXTERNAL-IP` for your frontend service is `127.0.0.1`, port forwarding is not needed. + +If port forwarding is required, run the following command: + +```shell +kubectl port-forward svc/bookstore-frontend-svc 3000:3000 +``` + +You should see the following output: + +``` +Forwarding from 127.0.0.1:3000 -> 3000 +Forwarding from [::1]:3000 -> 3000 +``` + + +**Don't close the terminal when port-forwarding is established.** Start a new terminal to run the next command. + +???+ success "Verify" + + Visit [http://localhost:3000](http://localhost:3000){:target="_blank"} in your browser. The UI page should appear! + + ![Image](images/image19.png) + +### **Task 3: Running the Book Review Service** + +![Image](images/image6.png) + +The Node.js server is located in the `node-server` folder. + +!!! warning + + Ensure that port 8080 on your local machine is not being used by another application. + +#### **Deploy the Book Review Service: Node.js Server** + +You can either [build the image locally](https://docs.docker.com/get-started/02_our_app/){:target="_blank"} or use our pre-built image. If you are using the pre-built image, you can proceed to the next step. + +When ready, run the following command to deploy the Node.js server: + +```shell +kubectl apply -f node-server/config/100-deployment.yaml +``` + +This command will pull the image and deploy it to your cluster as a Deployment. It will also expose it as a LoadBalancer to receive external traffic. + +``` +deployment.apps/node-server created +service/node-server-svc created +``` + +???+ success "Verify" + + Run the following command to check if the pod is running: + + ```shell + kubectl get pods + ``` + + You will see that your Node.js server (node-server) pod is running. + ``` + NAME READY STATUS RESTARTS AGE + bookstore-frontend-7b879ffb78-9bln6 1/1 Running 0 39m + node-server-68bf98cdf4-skjmh 1/1 Running 0 38m + ``` + + +#### **Port Forwarding (optional under condition)** + +![Image](images/image9.png) + +You might need to set up port forwarding to access the app from your local machine. + +Check if port forwarding is necessary by running: + +```shell +kubectl get services +``` +And you will see the following console output: +``` +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +bookstore-frontend-svc LoadBalancer 10.99.187.173 3000:31600/TCP 73m +kubernetes ClusterIP 10.96.0.1 443/TCP 85m +node-server-svc LoadBalancer 10.101.90.35 80:31792/TCP 73m +``` + +!!! note + If the `EXTERNAL-IP` for your Node.js service is `127.0.0.1`, port forwarding is not needed. If you failed to visit the page `localhost:8080`, you can try to set up port forwarding. + +If port forwarding is required, open a new terminal and run: + +```shell +kubectl port-forward svc/node-server-svc 8080:80 +``` +You should see the following output: + +``` +Forwarding from 127.0.0.1:8080 > 8000 +Forwarding from [::1]:8080 > 8000 +``` + +**Don't close the terminal when port-forwarding is established.** Start a new terminal to run the next command. + + +???+ success "Verify" + + Visit [http://localhost:8080](http://localhost:8080){:target="_blank"} in your browser. The Node.js service should be up and running. + + And in your front end page, you should see the status turns green and say "Connected to node server". + + ![Image](images/image18.png) + +## **Troubleshooting** +If you encounter any issues during the setup process, refer to the troubleshooting section in the documentation or check the logs of your Kubernetes pods for more details. +???+ bug "Troubleshooting" + + To check the logs, use the following command: + + ```shell + kubectl logs + ``` + + Replace `` with the name of the pod you want to check. + +## **Next Step** +![Image](images/image5.png) + +You have successfully set up the cluster with Knative installed, and running your front end app and node server. You are all set to start learning. Your journey begins from here. + +[Go to Lesson 1 - Send Review Comment to Broker :fontawesome-solid-paper-plane:](../page-1/send-review-comment-to-broker.md){ .md-button .md-button--primary } diff --git a/docs/bookstore/page-0.5/images/image1.png b/docs/bookstore/page-0.5/images/image1.png new file mode 100644 index 00000000000..ced2abeecf9 Binary files /dev/null and b/docs/bookstore/page-0.5/images/image1.png differ diff --git a/docs/bookstore/page-0.5/images/image10.png b/docs/bookstore/page-0.5/images/image10.png new file mode 100644 index 00000000000..1493e6247cc Binary files /dev/null and b/docs/bookstore/page-0.5/images/image10.png differ diff --git a/docs/bookstore/page-0.5/images/image12.png b/docs/bookstore/page-0.5/images/image12.png new file mode 100644 index 00000000000..091a77d4fe2 Binary files /dev/null and b/docs/bookstore/page-0.5/images/image12.png differ diff --git a/docs/bookstore/page-0.5/images/image13.png b/docs/bookstore/page-0.5/images/image13.png new file mode 100644 index 00000000000..eb1bb59abcb Binary files /dev/null and b/docs/bookstore/page-0.5/images/image13.png differ diff --git a/docs/bookstore/page-0.5/images/image16.png b/docs/bookstore/page-0.5/images/image16.png new file mode 100644 index 00000000000..8029436e5a5 Binary files /dev/null and b/docs/bookstore/page-0.5/images/image16.png differ diff --git a/docs/bookstore/page-0.5/images/image18.png b/docs/bookstore/page-0.5/images/image18.png new file mode 100644 index 00000000000..7db04a82d7a Binary files /dev/null and b/docs/bookstore/page-0.5/images/image18.png differ diff --git a/docs/bookstore/page-0.5/images/image19.png b/docs/bookstore/page-0.5/images/image19.png new file mode 100644 index 00000000000..3dc000be410 Binary files /dev/null and b/docs/bookstore/page-0.5/images/image19.png differ diff --git a/docs/bookstore/page-0.5/images/image20.png b/docs/bookstore/page-0.5/images/image20.png new file mode 100644 index 00000000000..6833b4cd344 Binary files /dev/null and b/docs/bookstore/page-0.5/images/image20.png differ diff --git a/docs/bookstore/page-0.5/images/image21.png b/docs/bookstore/page-0.5/images/image21.png new file mode 100644 index 00000000000..140ec7a936e Binary files /dev/null and b/docs/bookstore/page-0.5/images/image21.png differ diff --git a/docs/bookstore/page-0.5/images/image22.png b/docs/bookstore/page-0.5/images/image22.png new file mode 100644 index 00000000000..72b829adad2 Binary files /dev/null and b/docs/bookstore/page-0.5/images/image22.png differ diff --git a/docs/bookstore/page-0.5/images/image3.png b/docs/bookstore/page-0.5/images/image3.png new file mode 100644 index 00000000000..af05338bae3 Binary files /dev/null and b/docs/bookstore/page-0.5/images/image3.png differ diff --git a/docs/bookstore/page-0.5/images/image5.png b/docs/bookstore/page-0.5/images/image5.png new file mode 100644 index 00000000000..4cc9daa6766 Binary files /dev/null and b/docs/bookstore/page-0.5/images/image5.png differ diff --git a/docs/bookstore/page-0.5/images/image6.png b/docs/bookstore/page-0.5/images/image6.png new file mode 100644 index 00000000000..f8732dc9130 Binary files /dev/null and b/docs/bookstore/page-0.5/images/image6.png differ diff --git a/docs/bookstore/page-0.5/images/image9.png b/docs/bookstore/page-0.5/images/image9.png new file mode 100644 index 00000000000..41650610ee1 Binary files /dev/null and b/docs/bookstore/page-0.5/images/image9.png differ diff --git a/docs/bookstore/page-0/images/1.png b/docs/bookstore/page-0/images/1.png new file mode 100644 index 00000000000..e6c74b04528 Binary files /dev/null and b/docs/bookstore/page-0/images/1.png differ diff --git a/docs/bookstore/page-0/images/10.png b/docs/bookstore/page-0/images/10.png new file mode 100644 index 00000000000..ef0164c088a Binary files /dev/null and b/docs/bookstore/page-0/images/10.png differ diff --git a/docs/bookstore/page-0/images/11.png b/docs/bookstore/page-0/images/11.png new file mode 100644 index 00000000000..4f091f8d841 Binary files /dev/null and b/docs/bookstore/page-0/images/11.png differ diff --git a/docs/bookstore/page-0/images/13.png b/docs/bookstore/page-0/images/13.png new file mode 100644 index 00000000000..2e557f8c4d5 Binary files /dev/null and b/docs/bookstore/page-0/images/13.png differ diff --git a/docs/bookstore/page-0/images/2.png b/docs/bookstore/page-0/images/2.png new file mode 100644 index 00000000000..23ed0859b8d Binary files /dev/null and b/docs/bookstore/page-0/images/2.png differ diff --git a/docs/bookstore/page-0/images/3.png b/docs/bookstore/page-0/images/3.png new file mode 100644 index 00000000000..b62460544d1 Binary files /dev/null and b/docs/bookstore/page-0/images/3.png differ diff --git a/docs/bookstore/page-0/images/4.png b/docs/bookstore/page-0/images/4.png new file mode 100644 index 00000000000..3394454e71f Binary files /dev/null and b/docs/bookstore/page-0/images/4.png differ diff --git a/docs/bookstore/page-0/images/5.png b/docs/bookstore/page-0/images/5.png new file mode 100644 index 00000000000..9e4ed9bbd3f Binary files /dev/null and b/docs/bookstore/page-0/images/5.png differ diff --git a/docs/bookstore/page-0/images/6.png b/docs/bookstore/page-0/images/6.png new file mode 100644 index 00000000000..a63dee641b3 Binary files /dev/null and b/docs/bookstore/page-0/images/6.png differ diff --git a/docs/bookstore/page-0/images/7.png b/docs/bookstore/page-0/images/7.png new file mode 100644 index 00000000000..b5d9d488e08 Binary files /dev/null and b/docs/bookstore/page-0/images/7.png differ diff --git a/docs/bookstore/page-0/images/8.png b/docs/bookstore/page-0/images/8.png new file mode 100644 index 00000000000..532db545dc5 Binary files /dev/null and b/docs/bookstore/page-0/images/8.png differ diff --git a/docs/bookstore/page-0/images/9.png b/docs/bookstore/page-0/images/9.png new file mode 100644 index 00000000000..d2a3c532f33 Binary files /dev/null and b/docs/bookstore/page-0/images/9.png differ diff --git a/docs/bookstore/page-0/welcome-knative-bookstore-tutorial.md b/docs/bookstore/page-0/welcome-knative-bookstore-tutorial.md new file mode 100644 index 00000000000..61a0f077a9b --- /dev/null +++ b/docs/bookstore/page-0/welcome-knative-bookstore-tutorial.md @@ -0,0 +1,139 @@ +# Welcome: Knative Bookstore Tutorial + +![Welcome Image](images/1.png) + +**Welcome to Knative's End-to-End Sample Application Tutorial!** + +- Are you completely new to Knative and unsure where to start? +- Are you considering converting your application to an event-driven architecture but don't know how? +- Are you curious to see what Knative can do in real life? +- Are you new to the cloud computing world and looking to get started with open source? + +!!! success "You found the right place" + + If any of these resonate with you, you've found the perfect starting point. + +In this tutorial, we will construct an online bookstore application. This interactive guide, suitable for beginners and experienced engineers alike, will take you through the steps of building, deploying, and monitoring an application using Knative's powerful features. For those familiar with the process, concise graphics are available to streamline your learning experience. + +## **What are we building?** + +![App Diagram](images/2.png) + +Our App is an online bookstore that sells a single book. Customers can post comments about the book anonymously, with each comment displayed alongside an emoji reflecting the sentiment of the comment. Comments with inappropriate content are automatically filtered out, discarded, and logged in the backend. + +As the bookstore owner, you'll receive notifications via Slack each time if a comment containing a "bad word" is submitted. + +## **Learning Goal** + +![Learning Goal Image](images/3.png) + +You will learn Event-Driven Architecture (EDA) and how it contrasts with traditional application designs that use microservices and REST APIs. You'll learn: + +- **The Fundamentals of EDA**: Explore the core principles of event-driven architecture and how it enhances responsiveness and scalability in applications. +- **Comparative Insights**: Understand the differences between EDA and traditional architectures, highlighting the benefits and use cases of each. +- **Practical Application**: Discover how to transition your existing applications to an event-driven model, utilizing the powerful features of Knative Eventing. + +![Knative Framework](images/4.png) + +Knative is a powerful framework that operates on top of Kubernetes. This tutorial will guide you through: + +- **Setting Up Your Cluster**: You'll start by spinning up your own Kubernetes cluster, which is the foundation for deploying and managing containers. +- **Exploring Knative**: Gain hands-on experience with key Knative concepts and components. +- **Some example use cases of Knative**. + +By the end of this tutorial, you will not only understand these concepts but also feel comfortable implementing them, empowering you to build robust, scalable event-driven applications with Knative. + +## **Bookstore Architecture** + +![Bookstore Architecture](images/5.png) + +The bookstore application consists of the following components: + +### User Interface + +A frontend Next.js application that interacts with these services. It is a web page where users can select a book to view its details, ratings and reviews. + +### Database Service + +An in-memory PostgreSQL instance on Kubernetes, storing all user comments. + +### Book Reviews Service + +A Node.js web server that will perform the event forwarding, database operation, and handling the websocket connection. + +### Notification Service + +An Apache Camel K pipe that connects our event-driven architecture with a third-party webhook: Slack. It receives the CloudEvent and sends it as a message to a Slack Workspace. + +### ML Models Service + +There are 2 Machine learning workflows that can conduct sentiment analysis on user's review comment and hateful word sanity check. You will be using a [Knative Sequence](https://knative.dev/docs/eventing/flows/sequence/){:target="_blank"} to make sure they are executed in order. + +### Book Store Broker + +It acts as the central brain of our event-driven architecture. It connects all the microservices together, receives the event, and makes sure all the events are safely delivered to the correct destination. + +### Bad Word Broker + +It acts as the bridge between the book store Broker and the Slack Sink, so we can send notification to your Slack when a comment containing "bad word" is submitted. + +## **Tutorial Page Structure** + + +We will be building the sample app in this order: + +0. [**Environment Setup**](../page-0.5/environment-setup.md){:target="_blank"}: Set up the environment for the tutorial. This includes installing the cluster, the frontend and the backend. + +1. [**Send comments to the Broker**](../page-1/send-review-comment-to-broker.md){:target="_blank"}: Pass reviews from the frontend to event-display via the Broker. This involves learning about Broker, SinkBinding and CloudEvents event types. + +2. [**Deploy Sentiment Analysis Service**](../page-2/sentiment-analysis-service-for-bookstore-reviews.md){:target="_blank"}: Gain knowledge on deploying a sentiment analysis service using Knative Function. + +3. [**Deploy Bad Word Filter Service**](../page-3/create-bad-word-filter-service.md){:target="_blank"}: Implement a bad word filter service using Knative Function yourself. + +4. [**Use a Sequence to Run the ML Workflows in order**](../page-4/create-sequence-to-streamline-ML-workflows.md){:target="_blank"}: Learn how to utilize a Knative Sequence to ensure your ML workflows executes in order. + +5. [**Database Deployment**](../page-5//deploy-database-service.md){:target="_blank"}: Understand the deployment of an in-memory PostgreSQL instance using a plain Kubernetes deployment. + +6. [**Advanced event filtering**](../page-6/advanced-event-filtering.md){:target="_blank"}: Integrate all components by receiving "analyzed reviews" via Broker (using a Trigger) and storing them into the database. This includes learning about Triggers and Filters. + +7. [**Connect with External Services/API**](../page-7/slack-sink-learning-knative-eventing-and-apache-camel-K-integration.md){:target="_blank"}: Learn how to connect your application with external services and APIs using Knative Eventing and Apache Camel K integrations. + +8. [**Extra Challenges**](../extra-challenge/README.md){:target="_blank"}: Additional challenges to test your understanding of the concepts learned in the tutorial. + +## **How to properly learn?** + +### Preview the Final Bookstore: 1-Minute Demo Video + +To help you visualize what you'll be creating, we've prepared a brief demo video. This two-minute preview showcases the final bookstore application, providing a clearer understanding of what you can expect to build. + + + +???+ bug "Video not working above?" + If the video is not working, you can watch it [here](https://www.youtube.com/watch?v=5D8pTcQSacw&ab_channel=Knative){:target="_blank"}. + +### Step-by-Step Guidance for Beginners + +![Beginner Guide](images/9.png) + +This tutorial is meticulously structured to be beginner-friendly, featuring detailed, step-by-step instructions for building the sample app. Simply follow the sequence we've laid out: each section builds upon the previous, guiding you through the construction of the application. Should you encounter any hurdles, the Knative community is a fantastic resource for support. Don't hesitate to ask questions and seek advice. Check the [Help](#help) section below! + +### Accelerated Learning Path for Advanced Users + +![Advanced Users Guide](images/10.png) + +If you find the tutorial too basic, or if it covers familiar territory, feel free to adjust your learning approach. Each section of the tutorial is accompanied by concise graphics that summarize key concepts. Advanced learners can choose to focus on these graphics to grasp the essentials faster, streamlining the learning experience without sacrificing depth or understanding. + +## **Help** + +![Help Image](images/11.png) + +Join the supportive Knative community via the Cloud Native Computing Foundation (CNCF) Slack, particularly the [#knative](https://cloud-native.slack.com/archives/C04LGHDR9K7){:target="_blank"} channel. Before posting your questions, please search to see if they've already been answered. Your feedback on this tutorial is invaluable, so don't hesitate to reach out with suggestions or questions. + +## **Next Step** + + +![Environment Setup](images/13.png) + +Let's set up the environment first. + +[Go to Environment Setup :fontawesome-solid-paper-plane:](../page-0.5/environment-setup.md){ .md-button .md-button--primary } diff --git a/docs/bookstore/page-1/images/image1.png b/docs/bookstore/page-1/images/image1.png new file mode 100644 index 00000000000..6a06d58c935 Binary files /dev/null and b/docs/bookstore/page-1/images/image1.png differ diff --git a/docs/bookstore/page-1/images/image11.png b/docs/bookstore/page-1/images/image11.png new file mode 100644 index 00000000000..21f1f28d2dd Binary files /dev/null and b/docs/bookstore/page-1/images/image11.png differ diff --git a/docs/bookstore/page-1/images/image12.png b/docs/bookstore/page-1/images/image12.png new file mode 100644 index 00000000000..f6c461e055a Binary files /dev/null and b/docs/bookstore/page-1/images/image12.png differ diff --git a/docs/bookstore/page-1/images/image13.png b/docs/bookstore/page-1/images/image13.png new file mode 100644 index 00000000000..2ea5184d757 Binary files /dev/null and b/docs/bookstore/page-1/images/image13.png differ diff --git a/docs/bookstore/page-1/images/image15.png b/docs/bookstore/page-1/images/image15.png new file mode 100644 index 00000000000..52b9c4d71f2 Binary files /dev/null and b/docs/bookstore/page-1/images/image15.png differ diff --git a/docs/bookstore/page-1/images/image17.png b/docs/bookstore/page-1/images/image17.png new file mode 100644 index 00000000000..bc8c57f1705 Binary files /dev/null and b/docs/bookstore/page-1/images/image17.png differ diff --git a/docs/bookstore/page-1/images/image18.png b/docs/bookstore/page-1/images/image18.png new file mode 100644 index 00000000000..4340aa0ae6d Binary files /dev/null and b/docs/bookstore/page-1/images/image18.png differ diff --git a/docs/bookstore/page-1/images/image2.png b/docs/bookstore/page-1/images/image2.png new file mode 100644 index 00000000000..197735cd13c Binary files /dev/null and b/docs/bookstore/page-1/images/image2.png differ diff --git a/docs/bookstore/page-1/images/image20.png b/docs/bookstore/page-1/images/image20.png new file mode 100644 index 00000000000..3d70bf369fd Binary files /dev/null and b/docs/bookstore/page-1/images/image20.png differ diff --git a/docs/bookstore/page-1/images/image21.png b/docs/bookstore/page-1/images/image21.png new file mode 100644 index 00000000000..0735e04d9ba Binary files /dev/null and b/docs/bookstore/page-1/images/image21.png differ diff --git a/docs/bookstore/page-1/images/image25.png b/docs/bookstore/page-1/images/image25.png new file mode 100644 index 00000000000..1d61abcc270 Binary files /dev/null and b/docs/bookstore/page-1/images/image25.png differ diff --git a/docs/bookstore/page-1/images/image3.png b/docs/bookstore/page-1/images/image3.png new file mode 100644 index 00000000000..fdbea017b90 Binary files /dev/null and b/docs/bookstore/page-1/images/image3.png differ diff --git a/docs/bookstore/page-1/images/image4.png b/docs/bookstore/page-1/images/image4.png new file mode 100644 index 00000000000..001e0230650 Binary files /dev/null and b/docs/bookstore/page-1/images/image4.png differ diff --git a/docs/bookstore/page-1/images/image6.png b/docs/bookstore/page-1/images/image6.png new file mode 100644 index 00000000000..72a9a85b45d Binary files /dev/null and b/docs/bookstore/page-1/images/image6.png differ diff --git a/docs/bookstore/page-1/images/image8.png b/docs/bookstore/page-1/images/image8.png new file mode 100644 index 00000000000..96a877c97e3 Binary files /dev/null and b/docs/bookstore/page-1/images/image8.png differ diff --git a/docs/bookstore/page-1/images/image9.png b/docs/bookstore/page-1/images/image9.png new file mode 100644 index 00000000000..4d645849f08 Binary files /dev/null and b/docs/bookstore/page-1/images/image9.png differ diff --git a/docs/bookstore/page-1/send-review-comment-to-broker.md b/docs/bookstore/page-1/send-review-comment-to-broker.md new file mode 100644 index 00000000000..cc26bf1b4d8 --- /dev/null +++ b/docs/bookstore/page-1/send-review-comment-to-broker.md @@ -0,0 +1,423 @@ +# Send Review Comment to Broker + +![Image](images/image25.png) + +The main dashboard of our bookstore features a comment section where readers can view comments from others and submit their own through a text input area. While the process appears straightforward - click a button, and the comment is posted - the underlying mechanism is powered by a robust event-driven architecture. + +## **What Knative features will we learn about?** + +- Knative Eventing Broker +- Knative Eventing Sink +- Knative SinkBinding + +## **What does the deliverable look like?** + +![Image](images/image3.png) + +In simple words: after the user clicks on the submit button in the frontend, the comment will show up in the event-display service. + +## **Implementation** + +### **Step 0: Know the basics** + +![Image](images/image6.png) + +In the world of microservices and REST APIs, we often refer to requests as the primary method of communication between services. However, in an event-driven architecture, the smallest unit is an event. Knative Eventing adheres to the CloudEvents specification, making it essential to understand this concept before moving forward. Learn more about CloudEvents [here](https://cloudevents.io/){:target="_blank"} before proceeding! + +### **Step 1: Understand Book Review Service** + +![Image](images/image11.png) + +The book review service is our Node.js API server, playing a crucial role in our event-driven architecture. It's essential to understand how it operates, as it receives events and processes them appropriately. + +![Image](images/image2.png) + +**Key Concepts: Broker and SinkBinding** + +Before we dive into the code, let's clarify two important concepts: + +- **Broker**: Acts as the central point in the event-driven architecture, routing events to the correct destinations. +- **SinkBinding**: This Knative Eventing component automatically injects the Broker's address into the environment variable `K_SINK`, ensuring that the Node.js server always has the correct address without manual updates. + +You will get a deeper understanding along the way. + +![Image](images/image12.png) + +Let's examine the `node-server/index.js` file, starting with the `/add` function. When a user submits a comment through the frontend, it is first received by this endpoint. + +```javascript +app.post('/add', async (req, res) => { + try { + const receivedEvent = HTTP.toEvent({headers: req.headers, body: req.body}); + const brokerURI = process.env.K_SINK; + + if (receivedEvent.type === 'new-review-comment') { + const response = await fetch(brokerURI, { + method: 'POST', + headers: { + 'Content-Type': 'application/cloudevents+json', + 'ce-specversion': receivedEvent.specversion, + 'ce-type': receivedEvent.type, + 'ce-source': receivedEvent.source, + 'ce-id': receivedEvent.id, + }, + body: JSON.stringify(receivedEvent.data), + }); + } + } catch (err) { + console.error(err); + } +}); +``` + +**Receiving Events**: The `/add` endpoint receives events from the frontend. It uses CloudEvents' SDK to convert the incoming request into a CloudEvent object: + +```javascript +const receivedEvent = HTTP.toEvent({headers: req.headers, body: req.body}); +``` + +**Determining Broker Address**: The Broker's address is dynamically assigned in the cluster. The Node.js server retrieves this address from the environment variable `K_SINK`: + +```javascript +const brokerURI = process.env.K_SINK; +``` + +You may wonder who told the environment variable about the address? That’s Knative SinkBinding. + +**Event Filtering**: The service checks the event type. If it's a `new-review-comment`, it forwards the event to the Broker: + +```javascript +if (receivedEvent.type === 'new-review-comment') { + // logic that forwards the event, see below +} +``` + +**Forwarding Events Logic**: The event is forwarded to the Broker with the appropriate CloudEvent headers: + +```javascript +const response = await fetch(brokerURI, { + method: 'POST', + headers: { + 'Content-Type': 'application/cloudevents+json', + 'ce-specversion': receivedEvent.specversion, + 'ce-type': receivedEvent.type, + 'ce-source': receivedEvent.source, + 'ce-id': receivedEvent.id, + }, + body: JSON.stringify(receivedEvent.data), +}); +``` + +![Image](images/image21.png) + +**Exploring Other Functions** + +The Node.js server contains other functions that follow a similar pattern, with detailed comments explaining their functionality: + +- `/insert`: Receives CloudEvents and inserts the payload into the PostgreSQL database. +- `/comment`: Creates a WebSocket connection with the frontend to transport comments from the database. + +### **Step 2: Create Broker** + +![Image](images/image17.png) + +The Broker acts as a router in your event-driven application, receiving events and routing them to the correct destination. + + + +- 1: Create a new YAML file named `node-server/config/200-broker.yaml` and add the following content: + +???+ abstract "_node-server/config/200-broker.yaml_" + ```yaml + apiVersion: eventing.knative.dev/v1 + kind: Broker + metadata: + name: bookstore-broker + ``` + +- 2: Apply the YAML file: + +```bash +kubectl apply -f node-server/config/200-broker.yaml +``` + +You will see the following output: +``` +broker.eventing.knative.dev/bookstore-broker created +``` + +Alternatively, use the [Knative CLI `kn`](https://knative.dev/docs/client/#kn){:target="_blank"} to create the Broker: + +```bash +kn broker create bookstore-broker +``` + +You will see the following output: +``` +Broker 'bookstore-broker' successfully created in namespace 'default'. +``` + +???+ success "Verify" + Running the following command to list the Brokers: + ```bash + kubectl get brokers + ``` + + + You should see the Broker `bookstore-broker` with `READY` status as `True`. + + ``` + NAME URL AGE READY REASON + bookstore-broker http://broker-ingress.knative-eventing.svc.cluster.local/default/bookstore-broker 7m30s True + ``` + + + + +???+ bug "Troubleshooting" + + If there are issues, use the following command to diagnose: + + ```bash + kubectl describe broker bookstore-broker + ``` + +### **Step 3: Create a SinkBinding between the Node.js server and Broker** + +![Image](images/image15.png) + +Hardcoding URLs to connect with Kubernetes services in your application can be limiting and inflexible. A SinkBinding dynamically injects the URL of the Kubernetes service into your application. + +Learn more about SinkBinding [here](https://knative.dev/docs/eventing/custom-event-source/sinkbinding/){:target="_blank"} and the [spec schema](https://knative.dev/docs/eventing/custom-event-source/sinkbinding/reference/){:target="_blank"}! + +**Create a SinkBinding:** + +- 1: Create a new YAML file named `300-sinkbinding.yaml` in the `node-server/config` folder and add the following content: + +???+ abstract "_node-server/config/300-sinkbinding.yaml_" + ```yaml + apiVersion: sources.knative.dev/v1 + kind: SinkBinding + metadata: + name: node-sinkbinding + spec: + subject: + apiVersion: apps/v1 + kind: Deployment + selector: + matchLabels: + app: node-server + sink: # In this case, the sink is our Broker, which is the eventing service that will receive the events + ref: + apiVersion: eventing.knative.dev/v1 + kind: Broker + name: bookstore-broker + + ``` + +- 2: Apply the YAML file: + +```bash +kubectl apply -f node-server/config/300-sinkbinding.yaml +``` + +You will see the following output: +``` +sinkbinding.sources.knative.dev/node-sinkbinding created +``` + +???+ success "Verify" + Running the following command to list the sinkbindings: + ```bash + kubectl get sinkbindings + + ``` + You should see the sinkbinding `node-sinkbinding` with `READY` status as `True`. + + ``` + NAME SINK AGE READY REASON + node-sinkbinding http://broker-ingress.knative-eventing.svc.cluster.local/default/bookstore-broker 2m43s True + + ``` + + +### **Step 4: Create event-display service** + +![Image](images/image1.png) + +Event display is a debugging tool in Knative Eventing that allows you to use it as a temporary destination (a.k.a sink) for your event to go. + +**Create an Event Display Service:** + +- 1: Create a new YAML file named `100-event-display.yaml` in the `node-server/config` folder and add the following content: + +???+ abstract "_node-server/config/100-event-display.yaml_" + + ```yaml + apiVersion: apps/v1 + kind: Deployment + metadata: + name: event-display + spec: + replicas: 1 + selector: + matchLabels: + app: event-display + template: + metadata: + labels: + app: event-display + spec: + containers: + - name: event-display + image: gcr.io/knative-releases/knative.dev/eventing-contrib/cmd/event_display + ports: + - containerPort: 8080 + + --- + apiVersion: v1 + kind: Service + metadata: + name: event-display + spec: + selector: + app: event-display + ports: + - protocol: TCP + port: 80 + targetPort: 8080 + type: ClusterIP + + ``` + +- 2: Apply the YAML file: + +```bash +kubectl apply -f node-server/config/100-event-display.yaml +``` + +You will see the following output: +``` +deployment.apps/event-display created +service/event-display created +``` + +???+ success "Verify" + Running the following command to list the pods: + ```bash + kubectl get pods + ``` + + You should see the pod `event-display-XXXXXXX-XXXXX` in "Running" status. + + ``` + NAME READY STATUS RESTARTS AGE + bookstore-frontend-7b879ffb78-9bln6 1/1 Running 0 91m + event-display-55967c745d-bxrgh 1/1 Running 0 4m44s + node-server-644795d698-r9zlr 1/1 Running 0 4m43s + ``` + + + +### **Step 5: Create a Trigger that connects the Broker and event display** + +![Image](images/image9.png) + +A Trigger is able to forward the event to the correct destination based on the [CloudEvent's attributes](https://knative.dev/docs/eventing/#:~:text=Knative%20Eventing%20uses%20standard%20HTTP%20POST%20requests%20to%20send%20and%20receive%20events%20between%20event%20producers%20and%20sinks.%20These%20events%20conform%20to%20the%20CloudEvents%20specifications%2C%20which%20enables%20creating%2C%20parsing%2C%20sending%2C%20and%20receiving%20events%20in%20any%20programming%20language.){:target="_blank"}. It is the connector between the Broker and the event destination. + +A Filter in the Trigger will **filter the events based on the filter condition**. You will specify your filter condition in the Trigger’s YAML file. **If no filter is specified, the Trigger will forward all the events that the Broker received.** + +![Image](images/image18.png) + +There is also a concept called [Channel](https://knative.dev/docs/eventing/channels/){:target="_blank"} in Knative, and generally speaking, you can treat Broker & Trigger without filter the same as Channel & Subscription. + +Learn more about Broker & Trigger [here](https://knative.dev/docs/eventing/brokers/){:target="_blank"}! + +**Create a Trigger:** + +![Image](images/image20.png) + +Here we are creating a Trigger that will send all the events to event-display. + +![Image](images/image4.png) + +- 1: Create a new YAML file named `200-log-trigger.yaml` and add the following content: + +???+ abstract "_node-server/config/200-log-trigger.yaml_" + ```yaml + # This Trigger subscribes to the Broker and will forward all the events that it received to event-display. + apiVersion: eventing.knative.dev/v1 + kind: Trigger + metadata: + name: log-trigger + spec: + broker: bookstore-broker + subscriber: + ref: + apiVersion: v1 + kind: Service + name: event-display + ``` + +- 2: Apply the YAML file: + +```bash +kubectl apply -f node-server/config/200-log-trigger.yaml +``` + +You will see the following output: +``` +trigger.eventing.knative.dev/log-trigger created +``` + +???+ success "Verify" + Running the following command to list the Triggers: + ```bash + kubectl get triggers + ``` + The Trigger `log-trigger` should have `READY` status as `True`. + + + ``` + NAME BROKER SUBSCRIBER_URI AGE READY REASON + log-trigger bookstore-broker http://event-display.default.svc.cluster.local 6m2s True + ``` + + +### **Validate** + +![Image](images/image8.png) + +Open the logs of the event-display with the following command: + +```bash +kubectl logs -l=app=event-display -f +``` + +???+ success "Verify" + Type something in the comment box in the UI and click the submit button. The comment should appear in the event-display service with the following output: + + ```plaintext + ☁️ cloudevents.Event + Validation: valid + Context Attributes, + specversion: 1.0 + type: new-review-comment + source: bookstore-eda + id: unique-comment-id + datacontenttype: application/json + Extensions, + knativearrivaltime: 2024-05-19T05:27:36.232562628Z + Data, + { + "reviewText": "test" + } + ``` + +### **Next Step** + +![Image](images/image13.png) + +Please make sure you pass the Validate test before proceeding. + +[Go to Deploy ML workflow: Sentiment Analysis :fontawesome-solid-paper-plane:](../page-2/sentiment-analysis-service-for-bookstore-reviews.md){ .md-button .md-button--primary } diff --git a/docs/bookstore/page-2/images/image1.png b/docs/bookstore/page-2/images/image1.png new file mode 100644 index 00000000000..ec0049661c3 Binary files /dev/null and b/docs/bookstore/page-2/images/image1.png differ diff --git a/docs/bookstore/page-2/images/image10.png b/docs/bookstore/page-2/images/image10.png new file mode 100644 index 00000000000..3c6c460d08b Binary files /dev/null and b/docs/bookstore/page-2/images/image10.png differ diff --git a/docs/bookstore/page-2/images/image11.png b/docs/bookstore/page-2/images/image11.png new file mode 100644 index 00000000000..cf959f41747 Binary files /dev/null and b/docs/bookstore/page-2/images/image11.png differ diff --git a/docs/bookstore/page-2/images/image12.png b/docs/bookstore/page-2/images/image12.png new file mode 100644 index 00000000000..a3b74747f62 Binary files /dev/null and b/docs/bookstore/page-2/images/image12.png differ diff --git a/docs/bookstore/page-2/images/image13.png b/docs/bookstore/page-2/images/image13.png new file mode 100644 index 00000000000..435414aae5e Binary files /dev/null and b/docs/bookstore/page-2/images/image13.png differ diff --git a/docs/bookstore/page-2/images/image14.png b/docs/bookstore/page-2/images/image14.png new file mode 100644 index 00000000000..26f088364c1 Binary files /dev/null and b/docs/bookstore/page-2/images/image14.png differ diff --git a/docs/bookstore/page-2/images/image16.png b/docs/bookstore/page-2/images/image16.png new file mode 100644 index 00000000000..0e55fa6c269 Binary files /dev/null and b/docs/bookstore/page-2/images/image16.png differ diff --git a/docs/bookstore/page-2/images/image17.png b/docs/bookstore/page-2/images/image17.png new file mode 100644 index 00000000000..51750bd8258 Binary files /dev/null and b/docs/bookstore/page-2/images/image17.png differ diff --git a/docs/bookstore/page-2/images/image2.png b/docs/bookstore/page-2/images/image2.png new file mode 100644 index 00000000000..371e9a794e0 Binary files /dev/null and b/docs/bookstore/page-2/images/image2.png differ diff --git a/docs/bookstore/page-2/images/image3.png b/docs/bookstore/page-2/images/image3.png new file mode 100644 index 00000000000..51596393246 Binary files /dev/null and b/docs/bookstore/page-2/images/image3.png differ diff --git a/docs/bookstore/page-2/images/image4.png b/docs/bookstore/page-2/images/image4.png new file mode 100644 index 00000000000..d9269c80fe2 Binary files /dev/null and b/docs/bookstore/page-2/images/image4.png differ diff --git a/docs/bookstore/page-2/images/image5.png b/docs/bookstore/page-2/images/image5.png new file mode 100644 index 00000000000..d356735fba4 Binary files /dev/null and b/docs/bookstore/page-2/images/image5.png differ diff --git a/docs/bookstore/page-2/images/image8.png b/docs/bookstore/page-2/images/image8.png new file mode 100644 index 00000000000..03ccf44d381 Binary files /dev/null and b/docs/bookstore/page-2/images/image8.png differ diff --git a/docs/bookstore/page-2/images/image9.png b/docs/bookstore/page-2/images/image9.png new file mode 100644 index 00000000000..a65e753d708 Binary files /dev/null and b/docs/bookstore/page-2/images/image9.png differ diff --git a/docs/bookstore/page-2/sentiment-analysis-service-for-bookstore-reviews.md b/docs/bookstore/page-2/sentiment-analysis-service-for-bookstore-reviews.md new file mode 100644 index 00000000000..a01ae9cac42 --- /dev/null +++ b/docs/bookstore/page-2/sentiment-analysis-service-for-bookstore-reviews.md @@ -0,0 +1,414 @@ +# Sentiment Analysis Service for Bookstore Reviews + +![Image1](images/image1.png) + +As a bookstore owner, you aim to receive instant notifications in a Slack channel whenever a customer submits a new negative review comment. By leveraging Knative Function, you can set up a serverless function that contains a simple sentiment analysis service to categorize review comments by sentiment. + +## **Which Knative features will we learn about?** + +The ease to use **Knative Function** to deploy your service, and make it be managed by Knative Serving, which gives you the ability to **auto-scale your service to zero**, and scale up to handle the demand. + +## **What does the final deliverable look like?** + +![Image8](images/image8.png) + +A running serverless Knative Function that contains a python application that receives the new review comments as CloudEvent and returns the sentiment classification of the input text as CloudEvent. + +The function's output will be only from: + +- Positive + +- Neutral + +- Negative + +## **Install Prerequisites** + +### **Prerequisite 1: Install Knative func CLI** + +![Image12](images/image12.png) + +Knative Function enables you to easily create, build, and deploy stateless, event-driven functions as [Knative Services](https://knative.dev/docs/serving/services/#:~:text=Knative%20Services%20are%20used%20to,the%20Service%20to%20be%20configured){:target="_blank"} by using the func CLI. + +In order to do so, you need to install the func CLI. You can follow the [official documentation](https://knative.dev/docs/getting-started/install-func/){:target="_blank"} to install the func CLI. + +???+ success "Verify" + Running `func version` in your terminal to verify the installation, and you should see the version of the func CLI you installed. + +???+ bug "Troubleshooting" + If you see `command not found`, you may need to add the func CLI to your PATH. + +## **Implementation** + +![Image3](images/image3.png) + +The process is straightforward: + +1. Begin by utilizing the `func create` command to generate your code template. + +2. Next, incorporate your unique code into this template. + +3. Finally, execute `func deploy` to deploy your application seamlessly to the Kubernetes cluster. + +This workflow ensures a smooth transition from development to deployment within the Knative Functions ecosystem. + +### **Step 1: Create a Knative Function template** + +![Image17](images/image17.png) + +Create a new function using the func CLI: + +``` +func create -l +``` + +In this case, we are creating a Python function, so the command will be: + +``` +func create -l python sentiment-analysis-app +``` + +This command will create a new directory with the name `sentiment-analysis-app` and a bunch of files in it. The func CLI will generate a basic function template for you to start with. + +You can find all the supported language templates [here](https://knative.dev/docs/functions/){:target="_blank"}. + +???+ success "Verify" + + The file tree will look like this: + + ``` + start/sentiment-analysis-app + ├── func.yaml + ├── .funcignore + ├── .gitignore + ├── requirements.txt + ├── app.sh + ├── test_func.py + ├── README.md + ├── Procfile + └── func.py + ``` + +### **Step 2: Replace the generated code with the sentiment analysis logic** + +![Image14](images/image14.png) + +`func.py` is the file that contains the code for the function. You can replace the generated code with the sentiment analysis logic. You can use the following code as a starting point: + +???+ abstract "_sentiment-analysis-app/func.py_" + + ```python + from parliament import Context + from flask import Request, request, jsonify + import json + from textblob import TextBlob + from time import sleep + from cloudevents.http import CloudEvent, to_structured + + # The function to convert the sentiment analysis result into a CloudEvent + def create_cloud_event(inputText, badWordResult, data): + attributes = { + "type": "moderated-comment", + "source": "sentiment-analysis", + "datacontenttype": "application/json", + "sentimentResult": data, + "badwordfilter": badWordResult, + } + + # Put the sentiment analysis result into a dictionary + data = { + "reviewText": inputText, + "badWordResult": badWordResult, + "sentimentResult": data, + } + + # Create a CloudEvent object + event = CloudEvent(attributes, data) + return event + + def analyze_sentiment(text): + analysis = TextBlob(text["reviewText"]) + sentiment = "neutral" + + if analysis.sentiment.polarity > 0: + sentiment = "positive" + elif analysis.sentiment.polarity < 0: + sentiment = "negative" + + badWordResult = "" + try: + badWordResult = text["badWordResult"] + except: + pass + + # Convert the sentiment into a CloudEvent + sentiment = create_cloud_event(text["reviewText"], badWordResult, sentiment) + return sentiment + + def main(context: Context): + """ + Function template + The context parameter contains the Flask request object and any + CloudEvent received with the request. + """ + + print("Sentiment Analysis Received CloudEvent: ", context.cloud_event) + + # Add your business logic here + return analyze_sentiment(context.cloud_event.data) + ``` + +### **Step 3: Configure the dependencies** + +![Image9](images/image9.png) + +The `requirements.txt` file contains the dependencies for the function. You can add the following dependencies to the `requirements.txt` file: +???+ abstract "_sentiment-analysis-app/requirements.txt_" + + ``` + Flask==3.0.2 + textblob==0.18.0.post0 + parliament-functions==0.1.0 + cloudevents==1.10.1 + ``` + +Knative Function will automatically install the dependencies listed here when you build the function. + +### **Step 4: Configure the pre-built environment** + +![Image11](images/image11.png) + +In order to properly use the `textblob` library, you need to download the corpora, which is a large collection of text data that is used to train the sentiment analysis model. You can do this by creating a new file called `setup.py`, Knative Function will ensure that the `setup.py` file is executed after the dependencies have been installed. + +The `setup.py` file should contain the following code for your bookstore: + +???+ abstract "_sentiment-analysis-app/setup.py_" + ```python + from setuptools import setup, find_packages + from setuptools.command.install import install + import subprocess + + class PostInstallCommand(install): + """Post-installation for installation mode.""" + def run(self): + # Call the superclass run method + install.run(self) + # Run the command to download the TextBlob corpora + subprocess.call(['python', '-m', 'textblob.download_corpora', 'lite']) + + setup( + name="download_corpora", + version="1.0", + packages=find_packages(), + cmdclass={ + 'install': PostInstallCommand, + } + ) + ``` + +### **Step 5: Build and run your Knative Function locally (Optional)** +??? info "Click here to expand" + + + ![Image4](images/image4.png) + + In Knative Function, there are two ways to build: using the [pack build](https://github.com/knative/func/blob/8f3f718a5a036aa6b6eaa9f70c03aeea740015b9/docs/reference/func_build.md?plain=1#L46){:target="_blank"} or using the [source-to-image (s2i) build](https://github.com/knative/func/blob/4f48549c8ad4dad34bf750db243d81d503f0090f/docs/reference/func_build.md?plain=1#L43){:target="_blank"}. + + Currently only the s2i build is supported if you need to run `setup.py`. When building with s2i, the `setup.py` file will be executed automatically after the dependencies have been installed. + + Before we get started, configure the container registry to push the image to the container registry. You can use the following command to configure the container registry: + + ``` + export FUNC_REGISTRY= + ``` + + In this case, we will use the s2i build by adding the flag `-b=s2i`, and `-v` to see the verbose output. + + ``` + func build -b=s2i -v + ``` + + When the build is complete, you will see the following output: + + ``` + 🙌 Function built: /sentiment-analysis-app:latest + ``` + + This command will build the function and push the image to the container registry. After the build is complete, you can run the function using the following command: + + --- + + **Troubleshooting** + + `❗Error: '/home/Kuack/Documents/knative/docs/code-samples' does not contain an initialized function` + + **Solution: You may want to check whether you are in the correct directory. You can use the following command to check the current directory. If you are in the right directory, and the error still occurs, try to check your `func.yaml`, as it has to contain the field `created` and the right timestamp to be treated as a valid Knative Function.** + + --- + + ``` + func run -b=s2i -v + ``` + + In the future, you can skip the step of `func build`, because `func run` will automatically build the function for you. + + You will see the following output if the function is running successfully: + + ``` + ❗function up-to-date. Force rebuild with --build + Running + + on host port 8080 + ---> Running application from script (app.sh) ... + ``` + + Knative Function has an easy way to simulate the CloudEvent, you can use the following command to simulate the CloudEvent and test your function out: + + ``` + func invoke -f=cloudevent --data='{"reviewText": "I love Knative so much"}' --content-type=application/json --type="new-review-comment" -v + ``` + + where the `-f` flag indicates the type of the data, is either `HTTP` or `cloudevent`, and the `--data` flag is the input text. You can read more about `func invoke` [here](https://github.com/knative/func/blob/main/docs/reference/func_invoke.md){:target="_blank"}. + + In this case, you will get the full CloudEvent response: + + ``` + Context Attributes, + specversion: 1.0 + type: new-review-comment + source: book-review-broker + id: ebbcd761-3a78-4c44-92e3-de575d1f2d38 + time: 2024-05-27T04:44:07.549303Z + datacontenttype: application/json + Extensions, + badwordfilter: good + Data, + { + "reviewText": "I love Knative so much", + "badWordResult": "", + "sentimentResult": "positive" + } + ``` + +### **Step 6: Deploy the function to the cluster** + +![Image10](images/image10.png) + +!!! note + Please enter `/sentiment-analysis-app` when you are executing the following commands. + +After you have finished the code, you can deploy the function to the cluster using the following command: + +```bash +func deploy -b=s2i -v +``` +???+ success "Verify" + + When the deployment is complete, you will see the following output: + + ``` + Function deployed in namespace "default" and exposed at URL: + http://sentiment-analysis-app.default.svc.cluster.local + ``` + +!!! tip + You can find the URL of the Knative Function (Knative Service) by running the following command: + + ```bash + kubectl get kservice + ``` + + You will see the URL in the output: + + ``` + NAME URL LATESTCREATED LATESTREADY READY REASON + sentiment-analysis-app http://sentiment-analysis-app.default.svc.cluster.local sentiment-analysis-app-00001 sentiment-analysis-app-00001 True + ``` + + + +## **Knative Serving: scale down to zero** + +![Image13](images/image13.png) + +If you use the following command to query all the pods in the cluster, you will see that the pod is running: + +```bash +kubectl get pods +``` + +where `-A` is the flag to query all the pods in all namespaces. + +And you will find that your sentiment analysis app is running: + +``` +NAMESPACE NAME READY STATUS RESTARTS AGE +default sentiment-analysis-app-00002-deployment 2/2 Running 0 2m +``` + +But if you wait for a while without sending any CloudEvent to your function, and query the pods again, you will find that the pod that has your sentiment analysis app **disappeared**! + +This is because Knative Serving's autoscaler will **automatically scale down to zero** if there is no request to the function! Learn more at [Knative Autoscaling](https://knative.dev/docs/serving/autoscaling/){:target="_blank"}. + +--- + +## **Verify** + +![Image2](images/image2.png) + +After deployment, the `func` CLI provides a URL to access your function. You can verify the function's operation by sending a request with a sample review comment. + +Simply use Knative Function's command `func invoke` to directly send a CloudEvent to the function on your cluster: + +```bash +func invoke -f=cloudevent --data='{"reviewText":"I love Knative so much"}' -v +``` + +- `-f` flag indicates the type of the data, is either `HTTP` or `cloudevent` +- `--data` flag is the input text +- `-t` flag is the URI to the Knative Function. + +???+ success "Verify" + + If you see the response, it means that the function is running successfully. + + ``` + Context Attributes, + specversion: 1.0 + type: moderated-comment + source: sentiment-analysis + id: 0c2d0659-a30e-4efd-bcce-803f15ff5cc5 + time: 2024-06-11T15:12:43.795405Z + datacontenttype: application/json + Extensions, + badwordfilter: + sentimentresult: positive + Data, + { + "reviewText": "I love Knative so much", + "badWordResult": "", + "sentimentResult": "positive" + } + ``` + +![Image16](images/image16.png) + +In this tutorial, you learned how to create a serverless function for a simple sentiment analysis service with Knative. + +## **Next Step** + +![Image5](images/image5.png) + +Next, we'll deploy another ML service following the same procedure. We encourage you to try it yourself! + +!!! tip + Don't forget to `cd` into the root directory `/start` before proceeding. + + +If you feel comfortable deploying the other ML service yourself, follow this **simplified guide**: + +[Go to Deploy ML workflow: Bad word filter :fontawesome-solid-paper-plane:](../page-3/create-bad-word-filter-service.md){ .md-button .md-button--primary } + +If you encounter any issues, don't worry—we have a detailed tutorial ready for you. + +[Solution - Go to Deploy ML workflow: Bad word filter :fontawesome-solid-paper-plane:](../page-3/solution-create-bad-word-filter-service.md){ .md-button .md-button--primary } diff --git a/docs/bookstore/page-3/create-bad-word-filter-service.md b/docs/bookstore/page-3/create-bad-word-filter-service.md new file mode 100644 index 00000000000..020d4059547 --- /dev/null +++ b/docs/bookstore/page-3/create-bad-word-filter-service.md @@ -0,0 +1,178 @@ +# Create Bad Word Filter Service + +![Image 4](images/image4.png) + +As a bookstore owner, you aim to receive instant notifications in a Slack channel whenever a customer submits a new negative review comment. By leveraging Knative Function, you can set up a serverless function that contains a simple bad word filter service to tell whether the text contains any hateful/insultive speech. + +If you ever get stuck, check the solution here. + +[Solution - Go to Deploy ML workflow: Bad word filter :fontawesome-solid-paper-plane:](../page-3/solution-create-bad-word-filter-service.md){ .md-button .md-button--primary } + +## **What Knative features will we learn about?** + +- The easiness to use Knative Function to deploy your service, and make it be managed by Knative Serving, which gives you the ability to auto-scale your service to zero, and scale up to handle the demand. + +## **What does the final deliverable look like?** + +![Image 2](images/image2.png) + +A running serverless Knative Function that contains a python application that receives the new review comments as CloudEvent and returns the result that tells your input text contains any inappropriate languages or not. The result is sent back as CloudEvent. + +!!! info + We are using the `profanity_check` library to detect the bad words in the text. It is a open source library. Please see the disclaimer here. The result may not be 100% accurate. + +The function's output will be only from: + +- good +- bad + +## **Implementation** + +![Image 10](images/image10.png) + +The process is straightforward: + +1. Begin by utilizing the `func create` command to generate your code template. +2. Next, incorporate your unique code into this template. +3. Finally, execute `func deploy` to deploy your application seamlessly to the Kubernetes cluster. + +This workflow ensures a smooth transition from development to deployment within the Knative Functions ecosystem. + +--- + +### **Step 1: Create a Knative Function template** + +![Image 6](images/image6.png) + +???+ success "Verify" + + The file tree will look like this: + + ``` + /start/bad-word-filter + ├── func.yaml + ├── .funcignore + ├── .gitignore + ├── requirements.txt + ├── app.sh + ├── test_func.py + ├── README.md + └── Procfile + └── func.py + ``` + +### **Step 2: Replace the generated code with the bad word filter logic** + +![Image 5](images/image5.png) + +`func.py` is the file that contains the code for the function. You can replace the generated code with the bad word filter logic. You can use the following code as a starting point: + +???+ abstract "_bad-word-filter/func.py_" + ```python + from parliament import Context + from profanity_check import predict + from cloudevents.http import CloudEvent + + # The function to convert the bad word filter result into a CloudEvent + def create_cloud_event(inputText, data): + attributes = { + "type": "new-review-comment", + "source": "book-review-broker", + "datacontenttype": "application/json", + "badwordfilter": data, + } + + # Put the bad word filter result into a dictionary + data = {"reviewText": inputText, "badWordResult": data} + + # Create a CloudEvent object + event = CloudEvent(attributes, data) + return event + + def inappropriate_language_filter(text): + profanity_result = predict([text["reviewText"]]) + result = "good" + if profanity_result[0] == 1: + result = "bad" + + profanity_event = create_cloud_event(text["reviewText"], result) + return profanity_event + + def main(context: Context): + """ + Function template + The context parameter contains the Flask request object and any + CloudEvent received with the request. + """ + print("Received CloudEvent: ", context.cloud_event) + + # Add your business logic here + return inappropriate_language_filter(context.cloud_event.data) + ``` + +### **Step 3: Configure the dependencies** + +![Image 8](images/image8.png) + +???+ abstract "_bad-word-filter/requirements.txt_" + ```plaintext + parliament-functions==0.1.0 + alt-profanity-check==1.4.1.post1 + cloudevents==1.10.1 + ``` + +### **Step 4: Deploy the function to the cluster** + +![Image 1](images/image1.png) +!!! note + Please enter `/bad-word-filter` when you are executing the following commands. + +```plaintext +func deploy -b=s2i -v +``` +???+ success "Verify" + Expect to see the following message: + ``` + Function deployed in namespace "default" and exposed at URL: + http://bad-word-filter.default.svc.cluster.local + ``` + +## **Verify** + +![Image 7](images/image7.png) + +```plaintext +func invoke -f=cloudevent --data='{"reviewText":"I love Knative so much"}' -v +``` + +???+ success "Verify" + Expect to receive a CloudEvent response: + + ```plaintext + Context Attributes, + specversion: 1.0 + type: new-review-comment + source: book-review-broker + id: ebbcd761-3a78-4c44-92e3-de575d1f2d38 + time: 2024-05-27T04:44:07.549303Z + datacontenttype: application/json + Extensions, + badwordfilter: good + Data, + { + "reviewText": "I love Knative so much", + "badWordResult": "good" + } + ``` + +If you see the response, it means that the function is running successfully. + +## **Next Step** + +![Image 9](images/image9.png) + +In this tutorial, you learned how to create a serverless function for a simple service that can detect inappropriate languages in text with Knative. + +Next, we'll be learning how to use Knative Sequence to connect the 2 ML workflows and make sure they are executed in the order you want. + +[Go to Create Knative Sequence :fontawesome-solid-paper-plane:](../page-4/create-sequence-to-streamline-ML-workflows.md){ .md-button .md-button--primary } \ No newline at end of file diff --git a/docs/bookstore/page-3/images/image1.png b/docs/bookstore/page-3/images/image1.png new file mode 100644 index 00000000000..58f842eba9e Binary files /dev/null and b/docs/bookstore/page-3/images/image1.png differ diff --git a/docs/bookstore/page-3/images/image10.png b/docs/bookstore/page-3/images/image10.png new file mode 100644 index 00000000000..40f9e4eba29 Binary files /dev/null and b/docs/bookstore/page-3/images/image10.png differ diff --git a/docs/bookstore/page-3/images/image2.png b/docs/bookstore/page-3/images/image2.png new file mode 100644 index 00000000000..9f7b25ec3b1 Binary files /dev/null and b/docs/bookstore/page-3/images/image2.png differ diff --git a/docs/bookstore/page-3/images/image4.png b/docs/bookstore/page-3/images/image4.png new file mode 100644 index 00000000000..b207a358d16 Binary files /dev/null and b/docs/bookstore/page-3/images/image4.png differ diff --git a/docs/bookstore/page-3/images/image5.png b/docs/bookstore/page-3/images/image5.png new file mode 100644 index 00000000000..41150607194 Binary files /dev/null and b/docs/bookstore/page-3/images/image5.png differ diff --git a/docs/bookstore/page-3/images/image6.png b/docs/bookstore/page-3/images/image6.png new file mode 100644 index 00000000000..87f4255411f Binary files /dev/null and b/docs/bookstore/page-3/images/image6.png differ diff --git a/docs/bookstore/page-3/images/image7.png b/docs/bookstore/page-3/images/image7.png new file mode 100644 index 00000000000..96a877c97e3 Binary files /dev/null and b/docs/bookstore/page-3/images/image7.png differ diff --git a/docs/bookstore/page-3/images/image8.png b/docs/bookstore/page-3/images/image8.png new file mode 100644 index 00000000000..f231603bc24 Binary files /dev/null and b/docs/bookstore/page-3/images/image8.png differ diff --git a/docs/bookstore/page-3/images/image9.png b/docs/bookstore/page-3/images/image9.png new file mode 100644 index 00000000000..0e55fa6c269 Binary files /dev/null and b/docs/bookstore/page-3/images/image9.png differ diff --git a/docs/bookstore/page-3/solution-create-bad-word-filter-service.md b/docs/bookstore/page-3/solution-create-bad-word-filter-service.md new file mode 100644 index 00000000000..0bd16a03289 --- /dev/null +++ b/docs/bookstore/page-3/solution-create-bad-word-filter-service.md @@ -0,0 +1,207 @@ +# Solution - Create Bad Word Filter Service + +![image](images/image4.png) + +As a bookstore owner, you aim to receive instant notifications in a Slack channel whenever a customer submits a new negative review comment. By leveraging Knative Function, you can set up a serverless function that contains a simple bad word filter service to tell whether the text contains any hateful/insultive speech. + +## **What Knative features will we learn about?** + +- The easiness to use Knative Function to deploy your service, and make it be managed by Knative Serving, which give you the ability to auto-scale your service to zero, and scale up to handle the demand. + +## **What does the final deliverable look like?** + +![image](images/image2.png) + +A running serverless Knative Function that contains a python application that receives the new review comments as CloudEvent and returns the result that tells your input text contains any inappropriate languages or not. The result is sent back as CloudEvent. + +!!! info + We are using the `profanity_check` library to detect the bad words in the text. It is a open source library. Please see the disclaimer here. The result may not be 100% accurate. + +The function's output will be only from: + +- good +- bad + +## **Implementation** + +![image](images/image10.png) + +The process is straightforward: + +1. Begin by utilizing the `func create` command to generate your code template. +2. Next, incorporate your unique code into this template. +3. Finally, execute `func deploy` to deploy your application seamlessly to the Kubernetes cluster. + +This workflow ensures a smooth transition from development to deployment within the Knative Functions ecosystem. + +???+ bug "Troubleshooting" + If you see `command not found`, you may need to add the func CLI to your PATH.) + +### **Step 1: Create a Knative Function template** + +![image](images/image6.png) + +Create a new function using the `func` CLI: + +```shell +func create -l +``` + +In this case, we are creating a python function, so the command will be: + +```shell +func create -l python bad-word-filter +``` + +This command will create a new directory with the name `bad-word-filter` and a bunch of files in it. The `func` CLI will generate a basic function template for you to start with. + +You can find all the supported language templates [here](https://knative.dev/docs/functions/){:target="_blank"}. + +???+ success "Verify" + The file tree will look like this: + + ``` + /start/bad-word-filter + ├── func.yaml + ├── .funcignore + ├── .gitignore + ├── requirements.txt + ├── app.sh + ├── test_func.py + ├── README.md + ├── Procfile + └── func.py + ``` + +### **Step 2: Replace the generated code with the bad word filter logic** + +![image](images/image5.png) + +`func.py` is the file that contains the code for the function. You can replace the generated code with the bad word filter logic. You can use the following code as a starting point: + + + +???+ abstract "_bad-word-filter/func.py_" + ```python + from parliament import Context + from profanity_check import predict + from cloudevents.http import CloudEvent + + # The function to convert the bad word filter result into a CloudEvent + def create_cloud_event(inputText, data): + attributes = { + "type": "new-review-comment", + "source": "book-review-broker", + "datacontenttype": "application/json", + "badwordfilter": data, + } + + # Put the bad word filter result into a dictionary + data = {"reviewText": inputText, "badWordResult": data} + + # Create a CloudEvent object + event = CloudEvent(attributes, data) + return event + + def inappropriate_language_filter(text): + profanity_result = predict([text["reviewText"]]) + result = "good" + if profanity_result[0] == 1: + result = "bad" + + profanity_event = create_cloud_event(text["reviewText"], result) + return profanity_event + + def main(context: Context): + """ + Function template + The context parameter contains the Flask request object and any + CloudEvent received with the request. + """ + print("Received CloudEvent: ", context.cloud_event) + + # Add your business logic here + return inappropriate_language_filter(context.cloud_event.data) + ``` + +### **Step 3: Configure the dependencies** + +![image](images/image8.png) + +The `requirements.txt` file contains the dependencies for the function. You can add the following dependencies to the `requirements.txt` file: + +???+ abstract "bad-word-filter/requirements.txt_" + ```plaintext + parliament-functions==0.1.0 + alt-profanity-check==1.4.1.post1 + cloudevents==1.10.1 + ``` + +Knative Function will automatically install the dependencies listed here when you build the function. + +### **Step 4: Deploy the function to the cluster** + +![image](images/image1.png) + +!!! note + Please enter `/bad-word-filter` when you are executing the following commands. + +After you have finished the code, you can deploy the function to the cluster using the following command: + +```shell +func deploy -b=s2i -v +``` + +???+ success "Verify" + + When the deployment is complete, you will see the following output: + + ``` + Function deployed in namespace "default" and exposed at URL: + http://bad-word-filter.default.svc.cluster.local + ``` +## **Verify** + +![image](images/image7.png) + +After deployment, the `func` CLI provides a URL to access your function. You can verify the function's operation by sending a request with a sample review comment. + +Simply use Knative Function's command `func invoke` to directly send a CloudEvent to the function on your cluster: + +```shell +func invoke -f=cloudevent --data='{"reviewText":"I love Knative so much"}' -v +``` + +- `-f` flag indicates the type of the data, is either `HTTP` or `cloudevent` +- `--data` flag is the input text +- You can use `-t` flag to specify the URI to the Knative Function. + +???+ success "Verify" + If the function is running successfully, you will see the following output: + + ``` + Context Attributes, + specversion: 1.0 + type: new-review-comment + source: book-review-broker + id: ebbcd761-3a78-4c44-92e3-de575d1f2d38 + time: 2024-05-27T04:44:07.549303Z + datacontenttype: application/json + Extensions, + badwordfilter: good + Data, + { + "reviewText": "I love Knative so much", + "badWordResult": "good" + } + ``` + +## **Next Step** + +![image](images/image9.png) + +In this tutorial, you learned how to create a serverless function for a simple service that can detect the inappropriate languages in text with Knative. + +Next, we'll be learning how to use Knative Sequence to connect the 2 ML workflow and make sure they are executed in the order you want. + +[Go to Create Knative Sequence :fontawesome-solid-paper-plane:](../page-4/create-sequence-to-streamline-ML-workflows.md){ .md-button .md-button--primary } \ No newline at end of file diff --git a/docs/bookstore/page-4/create-sequence-to-streamline-ML-workflows.md b/docs/bookstore/page-4/create-sequence-to-streamline-ML-workflows.md new file mode 100644 index 00000000000..264d05f19cc --- /dev/null +++ b/docs/bookstore/page-4/create-sequence-to-streamline-ML-workflows.md @@ -0,0 +1,214 @@ +# **Create Knative Sequence to Streamline ML Workflows** + +![image](images/image8.png) + +## **What Knative features will we learn about?** + +- Knative Sequence + +## **What does the final deliverable look like?** + +![image](images/image1.png) + +- Create a Knative Sequence with bad word filter service as step 1 and sentiment analysis service as step 2 +- The final result is sent back to Broker as reply of the Sequence + +## **Implementation** + +### **Step 0: Learn Sequence** + +Sequence provides a way to define an in-order list of functions that will be invoked. Each step can modify, filter or create a new kind of an event. + +If you hope your event to pass through different services **in an order you like**, Knative Sequence is your choice. + +![image](images/image3.png) + + +```yaml +apiVersion: flows.knative.dev/v1 +kind: Sequence +metadata: + name: sequence +spec: + channelTemplate: + apiVersion: messaging.knative.dev/v1 + kind: InMemoryChannel + steps: + - ref: + apiVersion: serving.knative.dev/v1 + kind: Service + name: first + - ref: + apiVersion: serving.knative.dev/v1 + kind: Service + name: second + reply: + ref: + kind: Service + apiVersion: serving.knative.dev/v1 + name: event-display +``` + +### **Step 1: Create the Sequence** + +![image](images/image9.png) + +???+ abstract "sequence/config/100-create-sequence.yaml" + ```yaml + apiVersion: flows.knative.dev/v1 + kind: Sequence + metadata: + name: sequence + spec: + channelTemplate: # Under the hood, the Sequence will create a Channel for each step in the Sequence + apiVersion: messaging.knative.dev/v1 + kind: InMemoryChannel + steps: + - ref: # This is the first step of the Sequence, it will send the event to the bad-word-filter service + apiVersion: serving.knative.dev/v1 + kind: Service + name: bad-word-filter + - ref: # This is the second step of the Sequence, it will send the event to the sentiment-analysis-app service + apiVersion: serving.knative.dev/v1 + kind: Service + name: sentiment-analysis-app + reply: # This is the last step of the Sequence, it will send the event back to the Broker as reply + ref: + kind: Broker + apiVersion: eventing.knative.dev/v1 + name: bookstore-broker + ``` + +Create the Sequence yaml file and apply it to your cluster. + +``` +kubectl apply -f sequence/config/100-create-sequence.yaml +``` + +After applying the configuration, you should see the following output: + +``` +sequence.flows.knative.dev/sequence created +``` + + +???+ success "Verify" + + You can verify the status of the Sequence very easily + + ```bash + kubectl get sequences + ``` + + You should expect the Ready state for `sequence` to be True. + ``` + NAME URL AGE READY REASON + sequence http://sequence-kn-sequence-0-kn-channel.default.svc.cluster.local 159m True + ``` + +### **Step 2: Create the Trigger that pass the event to Sequence** + +![image](images/image7.png) + +As the Sequence is ready to accept the request now, we need to tell the Broker to forward the events to the Sequence, so that new comments will go through our ML workflows. + + +???+ abstract "sequence/config/200-create-trigger.yaml" + + ```yaml + apiVersion: eventing.knative.dev/v1 + kind: Trigger + metadata: + name: sequence-trigger + spec: + broker: bookstore-broker + filter: + attributes: + type: new-review-comment # This is the filter that will be applied to the event, only events with the ce-type new-review-comment will be processed + subscriber: + ref: + apiVersion: flows.knative.dev/v1 + kind: Sequence + name: sequence + ``` + +Create the Trigger yaml file and apply it to your cluster. + +``` +kubectl apply -f sequence/config/200-create-trigger.yaml +``` + +And you should see the following output: + +``` +trigger.eventing.knative.dev/sequence-trigger created +``` + + +???+ success "Verify" + You can verify the status of the Trigger very easily + + ```bash + kubectl get triggers + ``` + + You should see the Trigger in ready state. + + ``` + NAME BROKER SUBSCRIBER_URI AGE READY REASON + sequence-trigger bookstore-broker http://sequence-kn-sequence-0-kn-channel.default.svc.cluster.local 162m True + log-trigger bookstore-broker http://event-display.default.svc.cluster.local 164m True + ``` + + And until this point, **your cluster should have the following Triggers** that are created by you. + ![image](images/image12.png) + + + +### **Verification** + +![image](images/image11.png) + +Open the log for event-display with the following command: + +```bash +kubectl logs event-display-XXXXX -f +``` + +Type something in the comment box in the UI and click the submit button. All the events that the bookstore-broker received will be displayed in the event-display. + +???+ success "Verify" + + The comment should appear in the event-display service with the following output: + + ```yaml + ☁️cloudevents.Event + Validation: valid + Context Attributes, + specversion: 1.0 + type: moderated-comment + source: sentiment-analysis + id: 2f703218-15d4-4ff8-b2bc-11200e209315 + time: 2024-04-21T01:26:27.608365Z + datacontenttype: application/json + Extensions, + badwordfilter: bad + knativearrivaltime: 2024-04-21T01:26:27.617405597Z + sentimentresult: negative + Data, + { + "reviewText": "XXXXXXXXXXXX", + "badWordResult": "bad", + "sentimentResult": "negative" + } + ``` + +## **Next Step** + +![image](images/image4.png) + +In this tutorial, you learned how to create a Sequence to build a ML pipeline. + +Next, we'll be learning how to spin up book store's database services, while learning what will be the best case to use Knative Serving. + +[Go to Deploy Database Service :fontawesome-solid-paper-plane:](../page-5/deploy-database-service.md){ .md-button .md-button--primary } diff --git a/docs/bookstore/page-4/images/image1.png b/docs/bookstore/page-4/images/image1.png new file mode 100644 index 00000000000..0fc0198ef2b Binary files /dev/null and b/docs/bookstore/page-4/images/image1.png differ diff --git a/docs/bookstore/page-4/images/image11.png b/docs/bookstore/page-4/images/image11.png new file mode 100644 index 00000000000..96a877c97e3 Binary files /dev/null and b/docs/bookstore/page-4/images/image11.png differ diff --git a/docs/bookstore/page-4/images/image12.png b/docs/bookstore/page-4/images/image12.png new file mode 100644 index 00000000000..c1a870b4982 Binary files /dev/null and b/docs/bookstore/page-4/images/image12.png differ diff --git a/docs/bookstore/page-4/images/image3.png b/docs/bookstore/page-4/images/image3.png new file mode 100644 index 00000000000..01e12abd2ec Binary files /dev/null and b/docs/bookstore/page-4/images/image3.png differ diff --git a/docs/bookstore/page-4/images/image4.png b/docs/bookstore/page-4/images/image4.png new file mode 100644 index 00000000000..0e55fa6c269 Binary files /dev/null and b/docs/bookstore/page-4/images/image4.png differ diff --git a/docs/bookstore/page-4/images/image7.png b/docs/bookstore/page-4/images/image7.png new file mode 100644 index 00000000000..56e2de64eb6 Binary files /dev/null and b/docs/bookstore/page-4/images/image7.png differ diff --git a/docs/bookstore/page-4/images/image8.png b/docs/bookstore/page-4/images/image8.png new file mode 100644 index 00000000000..8edc0f30072 Binary files /dev/null and b/docs/bookstore/page-4/images/image8.png differ diff --git a/docs/bookstore/page-4/images/image9.png b/docs/bookstore/page-4/images/image9.png new file mode 100644 index 00000000000..7390ed50c47 Binary files /dev/null and b/docs/bookstore/page-4/images/image9.png differ diff --git a/docs/bookstore/page-5/deploy-database-service.md b/docs/bookstore/page-5/deploy-database-service.md new file mode 100644 index 00000000000..ef7f868a852 --- /dev/null +++ b/docs/bookstore/page-5/deploy-database-service.md @@ -0,0 +1,88 @@ +# **Deploy the Database Service** + +![image1](images/image1.png) + +## **What Knative features will we learn about?** + +- What is a good usecase to use Knative Service + +## **What does the final deliverable look like?** + +- Running a PostgreSQL StatefulSet that contains the table and some sample data + +## **Concept Learning** +![image4](images/image4.png) + +**Knative Services** are a powerful feature within the Knative ecosystem, designed to handle a wide range of use cases, especially in modern cloud-native applications, it can be controlled by Knative Serving, and achieve scale to 0. Here's an expanded explanation of when and why you should consider using Knative Services: + +???+ danger "Stateless Workloads" + - **Definition:** Stateless applications do not store any data locally between requests. Each request is independent and does not rely on any previous interaction. + - **Use Case:** Examples include web servers, APIs, and microservices where the state is managed externally, such as in a database or a cache. + - **Benefits:** Simplifies scaling and failover because any instance can handle any request without requiring session persistence. + +???+ example "Event-Driven Workloads" + - **Definition:** Event-driven architectures respond to events or Triggers, such as HTTP requests, messages in a queue, or changes in a database. + - **Use Case:** Use Knative Services to deploy functions that react to events, such as processing incoming data, triggering workflows, or integrating with third-party APIs. + - **Benefits:** Efficient resource utilization, as services can scale down to zero when not handling events, reducing costs and improving performance. + +![image6](images/image6.png) + +Try to ask in the Knative Slack community [#knative](https://cloud-native.slack.com/archives/C04LGHDR9K7){:target="_blank"} whether it is the best use case or not. + +## **Implementation** + +### **Step 1: apply all the config yaml files** + +![image9](images/image9.png) + +In this section, we will just be simply running a PostgreSQL service. We have all config files ready. Simply run the following command to apply all yamls at once. + +```sh +kubectl apply -f db-service +``` + +???+ success "Verify" + + You should see the following output: + ``` + configmap/sql-configmap created + secret/postgresql-secret created + persistentvolumeclaim/postgresql-pvc created + statefulset.apps/postgresql created + service/postgresql created + job.batch/postgresql-job created + ``` + + Wait a moment until all the pods become available and the database migration job is completed. If you see some job pods are failing and **having errors, don't worry**, please wait until at least one job becomes "**Completed**". + + ``` + NAME READY STATUS RESTARTS AGE + bookstore-frontend-7b879ffb78-9bln6 1/1 Running 0 6h53m + camel-k-operator-7989475884-7wk6z 1/1 Running 0 5h26m + event-display-55967c745d-bxrgh 1/1 Running 0 5h26m + node-server-644795d698-r9zlr 1/1 Running 0 5h26m + postgresql-0 1/1 Running 0 5h23m + postgresql-job-55dlv 0/1 Completed 0 10s + postgresql-job-fnv8m 0/1 Error 0 73s + ``` + +## **Verification** + +![image3](images/image3.png) + +Open the UI page at [http://localhost:3000](http://localhost:3000){:target="_blank"}, you should see some new comments popping up at the bottom of the page. + +![image2](images/image2.png) + +???+ bug "Troubleshoot" + If you see "No comments available", that means your database is not initialized yet. Check the health of the database service pods and figure out what happened. + +## **Next Step** + +![image7](images/image7.png) + +You have successfully set up the database services, and it is ready to receive requests and store user comments. + +Next, we'll complete our event-driven architecture by connecting all the components you created. This is where the magic happens. + +[Go to Implement Advanced Event Filtering :fontawesome-solid-paper-plane:](../page-6/advanced-event-filtering.md){ .md-button .md-button--primary } diff --git a/docs/bookstore/page-5/images/image1.png b/docs/bookstore/page-5/images/image1.png new file mode 100644 index 00000000000..b5b1453a433 Binary files /dev/null and b/docs/bookstore/page-5/images/image1.png differ diff --git a/docs/bookstore/page-5/images/image2.png b/docs/bookstore/page-5/images/image2.png new file mode 100644 index 00000000000..15118a33639 Binary files /dev/null and b/docs/bookstore/page-5/images/image2.png differ diff --git a/docs/bookstore/page-5/images/image3.png b/docs/bookstore/page-5/images/image3.png new file mode 100644 index 00000000000..96a877c97e3 Binary files /dev/null and b/docs/bookstore/page-5/images/image3.png differ diff --git a/docs/bookstore/page-5/images/image4.png b/docs/bookstore/page-5/images/image4.png new file mode 100644 index 00000000000..effdae2408c Binary files /dev/null and b/docs/bookstore/page-5/images/image4.png differ diff --git a/docs/bookstore/page-5/images/image6.png b/docs/bookstore/page-5/images/image6.png new file mode 100644 index 00000000000..b9420ea73cd Binary files /dev/null and b/docs/bookstore/page-5/images/image6.png differ diff --git a/docs/bookstore/page-5/images/image7.png b/docs/bookstore/page-5/images/image7.png new file mode 100644 index 00000000000..0e55fa6c269 Binary files /dev/null and b/docs/bookstore/page-5/images/image7.png differ diff --git a/docs/bookstore/page-5/images/image8.png b/docs/bookstore/page-5/images/image8.png new file mode 100644 index 00000000000..8da55e337f9 Binary files /dev/null and b/docs/bookstore/page-5/images/image8.png differ diff --git a/docs/bookstore/page-5/images/image9.png b/docs/bookstore/page-5/images/image9.png new file mode 100644 index 00000000000..62f56cab1ed Binary files /dev/null and b/docs/bookstore/page-5/images/image9.png differ diff --git a/docs/bookstore/page-6/advanced-event-filtering.md b/docs/bookstore/page-6/advanced-event-filtering.md new file mode 100644 index 00000000000..266fcb3f74a --- /dev/null +++ b/docs/bookstore/page-6/advanced-event-filtering.md @@ -0,0 +1,94 @@ +# **Advanced Event Filtering** + +![Image](images/image4.png) + +## **What Knative features will we learn about?** + +- Trigger and Broker +- How Knative magically connects everything together + +## **What does the final deliverable look like?** + +![Image](images/image6.png) + +- The "analyzed reviews" will be sent back to Broker (using a Trigger) and they will be stored into a database. +- The comments that don't contain bad words will show up in the UI, and the sentiment will be displayed as emoji. + +## **Implementation** + +### **Step 1: Create the Trigger for database insertion** + +![Image](images/image3.png) + +Append the following Trigger configuration to the existing `200-broker.yaml` file in `node-server/config/200-broker.yaml` and then apply: + +???+ abstract "**Append** to _node-server/config/200-broker.yaml_" + ```yaml + --- + apiVersion: eventing.knative.dev/v1 + kind: Trigger + metadata: + name: db-insert-trigger + spec: + broker: bookstore-broker + filter: + attributes: # Trigger will filter events based on BOTH the type and badwordfilter attribute + type: moderated-comment # This is the filter that will be applied to the event, only events with the ce-type moderated-comment will be processed + badwordfilter: good # This is the filter that will be applied to the event, only events with the ce-extension badwordfilter: good will be processed + subscriber: + ref: + apiVersion: v1 + kind: Service + name: node-server-svc + uri: /insert # This is the path where the event will be sent to the subscriber, see /insert in node-server code: index.js + ``` + +```shell +kubectl apply -f node-server/config/200-broker.yaml +``` + +After applying the configuration, you should see + +``` +broker.eventing.knative.dev/bookstore-broker unchanged +trigger.eventing.knative.dev/db-insert-trigger created +``` + +So far, the Triggers you created in your cluster should look like the following: + +![Image](images/image5.png) + +???+ success "Verify" + Run the following command to check if the Trigger is created successfully: + + ```shell + kubectl get triggers + ``` + + You should see the Trigger in ready state: + ``` + NAME BROKER SUBSCRIBER_URI AGE READY REASON + db-insert-trigger bookstore-broker http://node-server-svc.default.svc.cluster.local/insert 5h32m True + sequence-trigger bookstore-broker http://sequence-kn-sequence-0-kn-channel.default.svc.cluster.local 5h30m True + log-trigger bookstore-broker http://event-display.default.svc.cluster.local 5h32m True + ``` + + +## **Verification** + +![Image](images/image1.png) + +Now, it's magic time. Everything is connected automatically. Try interacting with the UI! + +- **Normal Comments**: When you send a normal comment without any "bad word", it will be displayed properly in the comment area. +- **"Bad Word" Comments**: Comments containing offensive or hateful speech will be filtered out and eventually it will be redirected to Slack (we will cover in the next section). + +Simple, isn't it? That's why Knative Eventing is so helpful! You only need to focus on developing each component, while Knative Eventing handles the connections and communication between services. Each service can focus on its responsibilities without worrying about message delivery. + +## **Next Step** + +![Image](images/image8.png) + +You've built your event-driven architecture. Now it's time to connect it to external services to further enhance your bookstore application. In the next section, we will enable the bookstore to send notifications to your Slack workspace! + +[Go to Connect Slack with Camel K :fontawesome-solid-paper-plane:](../page-7/slack-sink-learning-knative-eventing-and-apache-camel-K-integration.md){ .md-button .md-button--primary } diff --git a/docs/bookstore/page-6/images/image1.png b/docs/bookstore/page-6/images/image1.png new file mode 100644 index 00000000000..96a877c97e3 Binary files /dev/null and b/docs/bookstore/page-6/images/image1.png differ diff --git a/docs/bookstore/page-6/images/image3.png b/docs/bookstore/page-6/images/image3.png new file mode 100644 index 00000000000..4c5d7e6b54c Binary files /dev/null and b/docs/bookstore/page-6/images/image3.png differ diff --git a/docs/bookstore/page-6/images/image4.png b/docs/bookstore/page-6/images/image4.png new file mode 100644 index 00000000000..fde00788262 Binary files /dev/null and b/docs/bookstore/page-6/images/image4.png differ diff --git a/docs/bookstore/page-6/images/image5.png b/docs/bookstore/page-6/images/image5.png new file mode 100644 index 00000000000..40400ecd46d Binary files /dev/null and b/docs/bookstore/page-6/images/image5.png differ diff --git a/docs/bookstore/page-6/images/image6.png b/docs/bookstore/page-6/images/image6.png new file mode 100644 index 00000000000..6c2cb93f402 Binary files /dev/null and b/docs/bookstore/page-6/images/image6.png differ diff --git a/docs/bookstore/page-6/images/image7.png b/docs/bookstore/page-6/images/image7.png new file mode 100644 index 00000000000..edc8f442667 Binary files /dev/null and b/docs/bookstore/page-6/images/image7.png differ diff --git a/docs/bookstore/page-6/images/image8.png b/docs/bookstore/page-6/images/image8.png new file mode 100644 index 00000000000..0e55fa6c269 Binary files /dev/null and b/docs/bookstore/page-6/images/image8.png differ diff --git a/docs/bookstore/page-7/images/image1.png b/docs/bookstore/page-7/images/image1.png new file mode 100644 index 00000000000..98abc22b854 Binary files /dev/null and b/docs/bookstore/page-7/images/image1.png differ diff --git a/docs/bookstore/page-7/images/image10.png b/docs/bookstore/page-7/images/image10.png new file mode 100644 index 00000000000..781b02f2064 Binary files /dev/null and b/docs/bookstore/page-7/images/image10.png differ diff --git a/docs/bookstore/page-7/images/image11.png b/docs/bookstore/page-7/images/image11.png new file mode 100644 index 00000000000..dec9ea86ac2 Binary files /dev/null and b/docs/bookstore/page-7/images/image11.png differ diff --git a/docs/bookstore/page-7/images/image12.png b/docs/bookstore/page-7/images/image12.png new file mode 100644 index 00000000000..4d645849f08 Binary files /dev/null and b/docs/bookstore/page-7/images/image12.png differ diff --git a/docs/bookstore/page-7/images/image13.png b/docs/bookstore/page-7/images/image13.png new file mode 100644 index 00000000000..30d20059f8c Binary files /dev/null and b/docs/bookstore/page-7/images/image13.png differ diff --git a/docs/bookstore/page-7/images/image14.png b/docs/bookstore/page-7/images/image14.png new file mode 100644 index 00000000000..a530ce6b9bb Binary files /dev/null and b/docs/bookstore/page-7/images/image14.png differ diff --git a/docs/bookstore/page-7/images/image15.png b/docs/bookstore/page-7/images/image15.png new file mode 100644 index 00000000000..cd929651c4a Binary files /dev/null and b/docs/bookstore/page-7/images/image15.png differ diff --git a/docs/bookstore/page-7/images/image16.png b/docs/bookstore/page-7/images/image16.png new file mode 100644 index 00000000000..a4da708ce6e Binary files /dev/null and b/docs/bookstore/page-7/images/image16.png differ diff --git a/docs/bookstore/page-7/images/image17.png b/docs/bookstore/page-7/images/image17.png new file mode 100644 index 00000000000..ed27dd1a232 Binary files /dev/null and b/docs/bookstore/page-7/images/image17.png differ diff --git a/docs/bookstore/page-7/images/image19.png b/docs/bookstore/page-7/images/image19.png new file mode 100644 index 00000000000..bc8c57f1705 Binary files /dev/null and b/docs/bookstore/page-7/images/image19.png differ diff --git a/docs/bookstore/page-7/images/image2.png b/docs/bookstore/page-7/images/image2.png new file mode 100644 index 00000000000..57a838ce633 Binary files /dev/null and b/docs/bookstore/page-7/images/image2.png differ diff --git a/docs/bookstore/page-7/images/image20.png b/docs/bookstore/page-7/images/image20.png new file mode 100644 index 00000000000..0571200fe77 Binary files /dev/null and b/docs/bookstore/page-7/images/image20.png differ diff --git a/docs/bookstore/page-7/images/image21.png b/docs/bookstore/page-7/images/image21.png new file mode 100644 index 00000000000..d80ba2f99e1 Binary files /dev/null and b/docs/bookstore/page-7/images/image21.png differ diff --git a/docs/bookstore/page-7/images/image22.png b/docs/bookstore/page-7/images/image22.png new file mode 100644 index 00000000000..30c6494808c Binary files /dev/null and b/docs/bookstore/page-7/images/image22.png differ diff --git a/docs/bookstore/page-7/images/image4.png b/docs/bookstore/page-7/images/image4.png new file mode 100644 index 00000000000..96a877c97e3 Binary files /dev/null and b/docs/bookstore/page-7/images/image4.png differ diff --git a/docs/bookstore/page-7/images/image6.png b/docs/bookstore/page-7/images/image6.png new file mode 100644 index 00000000000..096ebf7543f Binary files /dev/null and b/docs/bookstore/page-7/images/image6.png differ diff --git a/docs/bookstore/page-7/images/image9.png b/docs/bookstore/page-7/images/image9.png new file mode 100644 index 00000000000..0e55fa6c269 Binary files /dev/null and b/docs/bookstore/page-7/images/image9.png differ diff --git a/docs/bookstore/page-7/slack-sink-learning-knative-eventing-and-apache-camel-K-integration.md b/docs/bookstore/page-7/slack-sink-learning-knative-eventing-and-apache-camel-K-integration.md new file mode 100644 index 00000000000..e131c9ce304 --- /dev/null +++ b/docs/bookstore/page-7/slack-sink-learning-knative-eventing-and-apache-camel-K-integration.md @@ -0,0 +1,395 @@ +# **Slack sink - Learning Knative Eventing and the Apache Camel K integration** + +![image](images/image2.png) + +As a bookstore owner, you aim to receive instant notifications in a Slack channel whenever a customer submits a new review comment. By leveraging Knative Eventing and Apache Camel K, you can set up an event-driven service that automates these notifications, ensuring you're always informed. + +## **What Knative features will we learn about?** + +- Knative's ability to connect with third-party services, such as Slack, through event-driven integration using **Apache Camel K**. + +## **What does the final deliverable look like?** + +When a CloudEvent with the type `moderated-comment` and with `ce-bad-word-filter` set to `bad` is sent, it triggers a message to be sent in a designated Slack channel. + +## **Install prerequisites** + +### **Prerequisite 1: Install Apache Camel CLI** + +![image](images/image16.png) + +Install the Apache Camel K CLI (`kamel`) on your local machine. You can find the installation instructions [here](https://camel.apache.org/camel-k/2.2.x/cli/cli.html){:target="_blank"}. + +???+ bug "Troubleshooting" + + If after installation you run `kamel version` and you get an error message, you may need to add the `kamel` binary to your system's PATH. You can do this by moving the `kamel` binary to a directory that is already in your PATH, or by adding the directory where `kamel` is located to your PATH. + + ```sh + $ export PATH=$PATH: + ``` + +### **Prerequisite 2: Install Apache Camel-Kamelets** + +![image](images/image13.png) + +Next, install Apache Camel K on your cluster using the Apache Camel K CLI: + +```sh +$ kamel install --registry docker.io --organization --registry-auth-username --registry-auth-password +``` + +Replace the placeholders with your actual Docker registry information. + +If you are using other container registries, you may need to read more [here](https://camel.apache.org/camel-k/2.2.x/installation/registry/registry.html){:target="_blank"} for the installation. + +???+ success "Verify" + + You will see this message if the installation is successful: + + ```sh + 📦 OLM is not available in the cluster. Fallback to regular installation. + 🐪 Camel K installed in namespace default + ``` + +### **Prerequisite 3: Create a Slack App and Generate an Incoming Webhook URL** + +![image](images/image21.png) + +Follow the instructions [here](../create-slack-workspace/README.md){:target="_blank"} on how to create the Slack workspace and generate an incoming webhook URL for your designated channel where notifications will be sent to. + +???+ success "Verify" + + You should have a webhook URL that looks like this: + + ```sh + https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX + ``` + + Save this URL as you will need it later. + +### **Prerequisite 4: Create a Secret that stores your Slack Credentials** + +![image](images/image22.png) + +We are storing the webhook URL as a secret. Copy and paste your webhook URL into the file `application.properties` + +???+ abstract "_/slack-sink/application.properties_" + + ``` + slack.channel=#bookstore-owner + slack.webhook.url=https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX + ``` + +Then run the following command to create the secret in the `/slack-sink` directory: + +```sh +kubectl create secret generic slack-credentials --from-file=application.properties +``` + +???+ success "Verify" + + You should see this message if the secret is created successfully: + + ```sh + secret/slack-credentials created + ``` + +## **Implementation** + +### **Step 0: Learn about Pipe** + +![image](images/image14.png) + +We use a feature called ["Pipe"](https://camel.apache.org/camel-k/2.3.x/apis/camel-k.html#_camel_apache_org_v1_Pipe){:target="_blank"} (a.k.a [KameletBinding](https://github.com/apache/camel-k/issues/2625){:target="_blank"}) in Apache Camel K to link event sources and destinations. Specifically, the Pipe connects events from our Broker, our source, to the Slack channel through a Slack sink [Kamelet](https://camel.apache.org/camel-k/2.3.x/kamelets/kamelets.html){:target="_blank"}, our destination. + +![image](images/image10.png) + +From the sample YAML below, you can see we are telling the pipe to filter on the events that have type "moderated-comment". Pipe will create a Trigger under the hood and route your event to slack-sink. + +```yaml +apiVersion: camel.apache.org/v1 +kind: Pipe +metadata: + name: pipe +spec: + source: + ref: + kind: Broker + apiVersion: eventing.knative.dev/v1 + name: badword-broker + properties: + type: moderated-comment + sink: + ... +``` + +If you hope to learn more about it, check out the article [Event Sourcing with Apache Camel K and Knative Eventing by Matthias Weßendorf](https://knative.dev/blog/articles/knative-meets-apache-camel){:target="_blank"}! + +### **Step 1: Create the Broker that can route "bad word" comments to Slack** + +![image](images/image19.png) + +In the current implementation using Apache Camel K, we **can only filter based on the CloudEvent's type**, such as moderated-comment. Filtering based on event extensions, such as `badwordfilter: good`, is not yet supported. This feature will be available in a future update of Apache Camel K. But we can still use an alternative way to achieve this! + +![image](images/image11.png) + +Here, we will be connecting `book-review-broker` with a new Broker called `badword-broker`. And we will be creating a Trigger that helps us perform the filtering with the extension `badwordfilter: good`. + +- 1: Append the following content to your `node-server/config/200-broker.yaml`: + +???+ abstract "_node-server/config/200-broker.yaml_" + + ```yaml + --- + apiVersion: eventing.knative.dev/v1 + kind: Broker + metadata: + name: badword-broker + ``` + +- 2: Apply the YAML file: + + ```sh + kubectl apply -f 200-broker.yaml + ``` + +You should see this message if the Broker is created successfully: + +``` +broker.eventing.knative.dev/badword-broker created +``` + +Alternatively, use the [Knative CLI `kn`](https://knative.dev/docs/client/#kn){:target="_blank"} to create the broker: + +```sh +kn broker create badword-broker +``` + +You should see this message if the Broker is created successfully: + +``` +Broker 'badword-broker' successfully created in namespace 'default'. +``` + +???+ success "Verify" + + Run the following command to list the Brokers: + ```sh + kubectl get brokers + ``` + + You should see the `badword-broker` listed. + ``` + NAME URL AGE READY REASON + badword-broker http://broker-ingress.knative-eventing.svc.cluster.local/default/badword-broker 3s True + bookstore-broker http://broker-ingress.knative-eventing.svc.cluster.local/default/bookstore-broker 5h38m True + ``` + + + +???+ bug "Troubleshooting" + + If there are issues, use the following command to diagnose: + + ```sh + kubectl describe broker badword-broker + ``` + +### **Step 2: Create Trigger that filters for bad word comments to badword-broker** + +![image](images/image12.png) + +We are creating the Trigger to process the events that have type moderated-comment, and the extension `badwordfilter: bad` and route them to badword-broker. + +**Create a Trigger:** + +![image](images/image17.png) + +- 1: Create a new YAML file named `node-server/config/badword-noti-trigger.yaml` and add the following content: + +???+ abstract "_node-server/config/badword-noti-trigger.yaml_" + + ```yaml + --- + apiVersion: eventing.knative.dev/v1 + kind: Trigger + metadata: + name: badword-noti-trigger + spec: + broker: bookstore-broker + filter: + attributes: # Trigger will filter events based on BOTH the type and badwordfilter attribute + type: moderated-comment # This is the filter that will be applied to the event, only events with the ce-type moderated-comment will be processed + badwordfilter: bad # This is the filter that will be applied to the event, only events with the ce-extension badwordfilter: bad will be processed + subscriber: + ref: + apiVersion: eventing.knative.dev/v1 + kind: Broker + name: badword-broker + ``` + +- 2: Apply the YAML file: + + ```sh + kubectl apply -f node-server/config/badword-noti-trigger.yaml + ``` + + You should see this message if the Trigger is created successfully: + + ``` + trigger.eventing.knative.dev/badword-noti-trigger created + ``` + +???+ success "Verify" + + ```sh + kubectl get triggers + ``` + + The Trigger `badword-noti-trigger` should have `READY` status as `True`. + + ``` + NAME BROKER SUBSCRIBER_URI AGE READY REASON + db-insert-trigger bookstore-broker http://node-server-svc.default.svc.cluster.local/insert 5h41m True + seq-reply-trigger bookstore-broker http://event-display.default.svc.cluster.local 5h39m True + sequence-trigger bookstore-broker http://sequence-kn-sequence-0-kn-channel.default.svc.cluster.local 5h39m True + log-trigger bookstore-broker http://event-display.default.svc.cluster.local 5h41m True + badword-noti-triggerbookstore-broker http://broker-ingress.knative-eventing.svc.cluster.local/default/badword-broker 5h41m True + ``` + +### **Step 3: Build the Pipe** + +This setup automatically sends notifications to Slack whenever a new comment that contains "bad word" occur, streamlining the flow of information. + +- 1: Make sure you have your k8s secret that contains your Slack webhook Url ready. If not, refer to the [Prerequisite 3](#prerequisite-3-create-a-slack-app-and-generate-an-incoming-webhook-url) section. + +- 2: Prepare the YAML configuration for the Slack sink, which will forward events to your Slack channel: + +![image](images/image15.png) + +Create a new file named `slack-sink.yaml` and add the following content: + +???+ abstract "_slack-sink/config/slack-sink.yaml_" + + ```yaml + apiVersion: camel.apache.org/v1 + kind: Pipe + metadata: + name: pipe + annotations: + trait.camel.apache.org/mount.configs: "secret:slack-credentials" + spec: + source: + ref: + kind: Broker + apiVersion: eventing.knative.dev/v1 + name: bad-word-broker + properties: + type: moderated-comment + sink: + ref: + kind: Kamelet + apiVersion: camel.apache.org/v1 + name: slack-sink + properties: + channel: ${slack.channel} + webhookUrl: ${slack.webhook.url} + ``` + +3. Apply the configuration to your Kubernetes cluster: + +```sh +$ kubectl apply -f slack-sink/slack-sink.yaml +``` + +???+ success "Verify" + You will see this message if the configuration is created successfully: + + ```sh + pipe.camel.apache.org/slack-sink-pipe created + ``` + + But this process will take a few seconds to complete. You can check the status of the pipe by running the following command: + + ```sh + $ kubectl get pipe slack-sink-pipe + ``` + + ```sh + NAME PHASE REPLICAS + slack-sink-pipe Ready 1 + ``` + +### **Step 4: Modify the Knative Services to disable scale to zero** + +![image](images/image6.png) + +In this step, we'll configure the notification delivery service to prevent it from [scaling down to zero](https://knative.dev/docs/serving/autoscaling/scale-to-zero/){:target="_blank"}, ensuring timely notifications. + +!!! note + `ksvc` stands for [Knative Service](https://knative.dev/docs/serving/services/){:target="_blank"}. + +1. **Check Existing Knative Services:** + +```sh +$ kubectl get ksvc +``` + +You should see a service named `pipe` listed: + +```sh +NAME URL LATESTCREATED LATESTREADY READY REASON +pipe http://pipe.default.svc.cluster.local pipe-00002 pipe-00002 True +``` + +2. **Edit the Knative Service:** + +To prevent the notification service from scaling down to zero, set the minimum number of pods to keep running. + +```sh +$ kubectl edit ksvc pipe +``` + +Add the following annotation: + +```yaml +spec: + template: + metadata: + annotations: + autoscaling.knative.dev/min-scale: "1" +``` + +This configuration ensures that Knative will always maintain at least one instance of the service running. + +???+ success "Verify" + + ```sh + $ kubectl get pods + ``` + + Periodically check the status of the pipe-deployment pods, and see whether they will disappear! If they stay there, then we are good! + +### **Verification** + +![image](images/image4.png) + +Now, you have completed building the sample app. When you submit a comment, you should always receive a notification in your test Slack workspace, achieving the same result as shown in the demo video. + +## **Conclusion** + +In this tutorial, you learned how to set up an event-driven service that automates notifications to a Slack channel using Knative Eventing and Apache Camel K. By leveraging these technologies, you can seamlessly connect your applications to third-party services and facilitate real-time information exchange between them. + +## **Next Step** + +![image](images/image9.png) + +Congratulations on successfully completing the bookstore sample app tutorial! If you want to deepen your understanding of Knative, open your bookstore front end, the demo book we used is a great starting point! Check the book ["Building Serverless Applications on Knative" by Evan Anderson.](https://www.oreilly.com/library/view/building-serverless-applications/9781098142063/){:target="_blank"} + +![image](images/image20.png) + +We've prepared additional challenges that build on top of the existing bookstore app for you to tackle. Some solutions are provided, while others are left open to encourage you to explore your own solutions. + + + +[Go to Extra Challenges :fontawesome-solid-paper-plane:](../extra-challenge/README.md){ .md-button .md-button--primary } diff --git a/docs/getting-started/tutorial.md b/docs/getting-started/tutorial.md new file mode 100644 index 00000000000..9e2f321621f --- /dev/null +++ b/docs/getting-started/tutorial.md @@ -0,0 +1,14 @@ +# Welcome to the Knative Tutorial + +### **Quick Start Tutorial** +Quickstart tutorial provides a simplified local installation to explore Knative Serving and Eventing features. + +[Get Started :octicons-arrow-right-24:](../getting-started/README.md){ .md-button } + +### **New: Knative's End-to-End Sample Application Tutorial** +Good starting point to learn Knative from scratch. Build an online bookstore application with Knative's powerful features. + +[Get Started :octicons-arrow-right-24:](../bookstore/page-0/welcome-knative-bookstore-tutorial.md){ .md-button } + + +![image1](../bookstore/page-0/images/1.png)