diff --git a/examples/knative/python/kn-py-slack/Procfile b/examples/knative/python/kn-py-slack/Procfile new file mode 100644 index 00000000..760d3af1 --- /dev/null +++ b/examples/knative/python/kn-py-slack/Procfile @@ -0,0 +1 @@ +web: FLASK_ENV=development FLASK_APP=handler.py python3 -m flask run --host=0.0.0.0 --port=$PORT \ No newline at end of file diff --git a/examples/knative/python/kn-py-slack/README.md b/examples/knative/python/kn-py-slack/README.md new file mode 100644 index 00000000..fbeefe69 --- /dev/null +++ b/examples/knative/python/kn-py-slack/README.md @@ -0,0 +1,115 @@ +# kn-py-slack +Example Knative Python function for sending to a Slack webhook when a Virtual Machine is powered off. + +# Step 1 - Build with `pack` + +[Buildpacks](https://buildpacks.io) are used to create the container image. + +```bash +IMAGE=/kn-py-slack:1.0 +pack build -B gcr.io/buildpacks/builder:v1 ${IMAGE} +``` + + +# Step 2 - Test + +Verify the container image works by executing it locally. + +Change into the `test` directory +```console +cd test +``` + +Update the `docker-test-env-variable` file with your Slack webook URL. + +Start the container image by running the following command: + +```console +docker run -e PORT=8080 --env-file docker-test-env-variable -it --rm -p 8080:8080 /kn-py-slack:1.0 + +Serving Flask app "handler.py" (lazy loading) + * Environment: development + * Debug mode: on + * Running on all addresses. + WARNING: This is a development server. Do not use it in a production deployment. + * Running on http://172.17.0.2:8080/ (Press CTRL+C to quit) + * Restarting with stat + * Debugger is active! + ``` + +In a separate terminal window, go to the test directory and use the `testevent.json` file to validate the function is working. You should see output similar to this below. + +``` +curl -i -d@testevent.json localhost:8080 + +HTTP/1.1 100 Continue + +HTTP/1.0 200 OK +Content-Type: application/json +Content-Length: 3 +Server: Werkzeug/1.0.1 Python/3.9.0 +Date: Tue, 25 May 2021 13:59:31 GMT +``` +Return to the previous terminal window where you started the docker image, and you should see something similar to the following: + +``` +2021-05-25 08:59:28,400 INFO werkzeug MainThread : * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) +2021-05-25 08:59:31,423 DEBUG urllib3.connectionpool Thread-1 : Starting new HTTPS connection (1): hooks.slack.com:443 +2021-05-25 08:59:31,614 DEBUG urllib3.connectionpool Thread-1 : https://hooks.slack.com:443 "POST /services/ HTTP/1.1" 200 22 +2021-05-25 08:59:31,618 INFO werkzeug Thread-1 : 127.0.0.1 - - [25/May/2021 08:59:31] "POST / HTTP/1.1" 200 - +``` + + +Finally, check your Slack channel to see if the test event posted. + +![](screenshots/Test_Event.png) + +# Step 3 - Deploy + +> **Note:** The following steps assume a working Knative environment using the +`default` Rabbit `broker`. The Knative `service` and `trigger` will be installed in the +`vmware-functions` Kubernetes namespace, assuming that the `broker` is also available there. + +Push your container image to an accessible registry such as Docker once you're done developing and testing your function logic. + +```console +docker push /kn-py-slack:1.0 +``` + +Update the `slack_secret.json` file with your Slack webhook configurations and then create the kubernetes secret which can then be accessed from within the function by using the environment variable named called `SLACK_SECRET`. + +```console +# create secret +kubectl -n vmware-functions create secret generic slack-secret --from-file=SLACK_SECRET=slack_secret.txt + +# update label for secret to show up in VEBA UI +kubectl -n vmware-functions label secret slack-secret app=veba-ui +``` + +Edit the `function.yaml` file with the name of the container image from Step 1 if you made any changes. If not, the default VMware container image will suffice. By default, the function deployment will filter on the `VmPoweredOffEvent` vCenter Server Event. If you wish to change this, update the `subject` field within `function.yaml` to the desired event type. + + +Deploy the function to the VMware Event Broker Appliance (VEBA). + +```console +# deploy function +kubectl -n vmware-functions apply -f function.yaml +``` + +For testing purposes, the `function.yaml` contains the following annotations, which will ensure the Knative Service Pod will always run **exactly** one instance for debugging purposes. Functions deployed through through the VMware Event Broker Appliance UI defaults to scale to 0, which means the pods will only run when it is triggered by an vCenter Event. + +```yaml +annotations: + autoscaling.knative.dev/maxScale: "1" + autoscaling.knative.dev/minScale: "1" +``` + +# Step 4 - Undeploy + +```console +# undeploy function +kubectl -n vmware-functions delete -f function.yaml + +# delete secret +kubectl -n vmware-functions delete secret slack-secret +``` diff --git a/examples/knative/python/kn-py-slack/function.yaml b/examples/knative/python/kn-py-slack/function.yaml new file mode 100644 index 00000000..2363714d --- /dev/null +++ b/examples/knative/python/kn-py-slack/function.yaml @@ -0,0 +1,40 @@ +apiVersion: serving.knative.dev/v1 +kind: Service +metadata: + name: kn-py-slack + labels: + app: veba-ui + template: + metadata: + annotations: + autoscaling.knative.dev/maxScale: "1" + autoscaling.knative.dev/minScale: "1" +spec: + template: + metadata: + spec: + containers: + - image: projects.registry.vmware.com/veba/kn-py-slack:1.0 + envFrom: + - secretRef: + name: slack-secret + +--- +apiVersion: eventing.knative.dev/v1 +kind: Trigger +metadata: + name: veba-py-slack-trigger + labels: + app: veba-ui +spec: + broker: default + filter: + attributes: + type: com.vmware.event.router/event + subject: VmPoweredOffEvent + subscriber: + ref: + apiVersion: serving.knative.dev/v1 + kind: Service + name: kn-py-slack + diff --git a/examples/knative/python/kn-py-slack/handler.py b/examples/knative/python/kn-py-slack/handler.py new file mode 100644 index 00000000..16ac1ddb --- /dev/null +++ b/examples/knative/python/kn-py-slack/handler.py @@ -0,0 +1,56 @@ +from flask import Flask, request, jsonify +import os +import requests +from cloudevents.http import from_http +import logging,json + +logging.basicConfig(level=logging.DEBUG,format='%(asctime)s %(levelname)s %(name)s %(threadName)s : %(message)s') + +app = Flask(__name__) +#Change the value to match the secret key in the VEBA appliance where you enter the Slack webook url information +#url = os.environ.get('SLACK_SECRET') + +@app.route('/', methods=['POST']) +def slack(): + + try: + event = from_http(request.headers, request.get_data(),None) + + data = event.data + attrs = event._attributes + + #this section uses the Slack formatting to present the events in the format you would like. You can modify as needed to add, remove or re-order the elements in a message + payload = { "text": f"*CLOUDEVENT_ID*:\n {attrs['id']}\n\n Source: {attrs['source']}\n Type: {attrs['type']}\n *Subject*: *{attrs['subject']}*\n Time: {attrs['time']}\n\n *EVENT DATA*:\n key: {data['Key']}\n user: {data['UserName']}\n datacenter: {data['Datacenter']['Name']}\n Host: {data['Host']['Name']}\n VM: {data['Vm']['Name']}\n\n Message: {data['FullFormattedMessage']}" } + + requests.post(url=url, json=payload) + + # app.logger.info(f'"***cloud event*** {json.dumps(e)}') + return {}, 200 + + except KeyError as e: + sc = 400 + msg = f'could not decode cloud event: {e}' + app.logger.error(msg) + message = { + 'status': sc, + 'error': msg, + } + resp = jsonify(message) + resp.status_code = sc + return resp + + except Exception as e: + sc = 500 + msg = f'could not send message: {e}' + app.logger.error(msg) + message = { + 'status': sc, + 'error': msg, + } + resp = jsonify(message) + resp.status_code = sc + return resp + +# hint: run with FLASK_ENV=development FLASK_APP=handler.py flask run +if __name__ == "__main__": + app.run() diff --git a/examples/knative/python/kn-py-slack/pyvenv.cfg b/examples/knative/python/kn-py-slack/pyvenv.cfg new file mode 100644 index 00000000..44b2a58c --- /dev/null +++ b/examples/knative/python/kn-py-slack/pyvenv.cfg @@ -0,0 +1,2 @@ +home = /usr/local/bin +include-system-site-packages = false \ No newline at end of file diff --git a/examples/knative/python/kn-py-slack/requirements.txt b/examples/knative/python/kn-py-slack/requirements.txt new file mode 100644 index 00000000..d4ac1ed0 --- /dev/null +++ b/examples/knative/python/kn-py-slack/requirements.txt @@ -0,0 +1,3 @@ +flask==1.1.2 +cloudevents==1.2.0 +requests==2.7.0 \ No newline at end of file diff --git a/examples/knative/python/kn-py-slack/screenshots/Test_Event.png b/examples/knative/python/kn-py-slack/screenshots/Test_Event.png new file mode 100644 index 00000000..224ec1d1 Binary files /dev/null and b/examples/knative/python/kn-py-slack/screenshots/Test_Event.png differ diff --git a/examples/knative/python/kn-py-slack/slack_secret.txt b/examples/knative/python/kn-py-slack/slack_secret.txt new file mode 100644 index 00000000..6983a0a5 --- /dev/null +++ b/examples/knative/python/kn-py-slack/slack_secret.txt @@ -0,0 +1 @@ + diff --git a/examples/knative/python/kn-py-slack/test/docker-test-env-variable b/examples/knative/python/kn-py-slack/test/docker-test-env-variable new file mode 100644 index 00000000..4c429654 --- /dev/null +++ b/examples/knative/python/kn-py-slack/test/docker-test-env-variable @@ -0,0 +1 @@ +SLACK_SECRET= diff --git a/examples/knative/python/kn-py-slack/test/testevent.json b/examples/knative/python/kn-py-slack/test/testevent.json new file mode 100644 index 00000000..8507c32f --- /dev/null +++ b/examples/knative/python/kn-py-slack/test/testevent.json @@ -0,0 +1,49 @@ +{ + "id": "08179137-b8e0-4973-b05f-8f212bf5003b", + "source": "https://10.0.0.1:443/sdk", + "specversion": "1.0", + "type": "com.vmware.event.router/event", + "subject": "VmPoweredOffEvent", + "time": "2020-02-11T21:29:54.9052539Z", + "data": { + "Key": 9902, + "ChainId": 9895, + "CreatedTime": "2020-02-11T21:28:23.677595Z", + "UserName": "VSPHERE.LOCAL\\Administrator", + "Datacenter": { + "Name": "testDC", + "Datacenter": { + "Type": "Datacenter", + "Value": "datacenter-2" + } + }, + "ComputeResource": { + "Name": "cls", + "ComputeResource": { + "Type": "ClusterComputeResource", + "Value": "domain-c7" + } + }, + "Host": { + "Name": "10.185.22.74", + "Host": { + "Type": "HostSystem", + "Value": "host-21" + } + }, + "Vm": { + "Name": "test-01", + "Vm": { + "Type": "VirtualMachine", + "Value": "vm-56" + } + }, + "Ds": null, + "Net": null, + "Dvs": null, + "FullFormattedMessage": "test-01 on 10.0.0.1 in testDC is powered off", + "ChangeTag": "", + "Template": false + }, + "datacontenttype": "application/json" +}