Skip to content

Commit

Permalink
feat: Add kn-py-slack Python example
Browse files Browse the repository at this point in the history
Signed-off-by: Scottie Ray <sray@vmware.com>
  • Loading branch information
Scottie Ray committed May 25, 2021
1 parent 198dc4f commit e3b02a5
Show file tree
Hide file tree
Showing 10 changed files with 268 additions and 0 deletions.
1 change: 1 addition & 0 deletions examples/knative/python/kn-py-slack/Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: FLASK_ENV=development FLASK_APP=handler.py python3 -m flask run --host=0.0.0.0 --port=$PORT
115 changes: 115 additions & 0 deletions examples/knative/python/kn-py-slack/README.md
Original file line number Diff line number Diff line change
@@ -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=<docker-username>/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 <docker-username>/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/<your slack token will appear> 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 <docker-username>/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
```
40 changes: 40 additions & 0 deletions examples/knative/python/kn-py-slack/function.yaml
Original file line number Diff line number Diff line change
@@ -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

56 changes: 56 additions & 0 deletions examples/knative/python/kn-py-slack/handler.py
Original file line number Diff line number Diff line change
@@ -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()
2 changes: 2 additions & 0 deletions examples/knative/python/kn-py-slack/pyvenv.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
home = /usr/local/bin
include-system-site-packages = false
3 changes: 3 additions & 0 deletions examples/knative/python/kn-py-slack/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
flask==1.1.2
cloudevents==1.2.0
requests==2.7.0
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions examples/knative/python/kn-py-slack/slack_secret.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<ENTER YOUR SLACK WEBHOOK URL>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SLACK_SECRET=<YOUR SLACK WEBHOOK URL>
49 changes: 49 additions & 0 deletions examples/knative/python/kn-py-slack/test/testevent.json
Original file line number Diff line number Diff line change
@@ -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"
}

0 comments on commit e3b02a5

Please sign in to comment.