├── Alert ├── requirements.txt ├── README.md ├── Installguide.md └── main.py ├── Retry ├── requirements.txt ├── README.md ├── Installguide.md └── main.py ├── .DS_Store ├── PubSubFunction ├── requirements.txt ├── .DS_Store ├── README.md ├── Installguide.md └── main.py ├── images ├── GCS.png ├── Assets.png ├── PubSub.png └── Metrics.png ├── GCS ├── requirements.txt ├── README.md ├── Installguide.md └── main.py ├── Metrics ├── requirements.txt ├── README.md ├── Installguide.md └── main.py ├── Assets ├── requirements.txt ├── props.conf ├── README.md ├── main.py └── Installguide.md ├── LICENSE ├── Examples ├── Example-3-GCS.md ├── Troubleshooting.md ├── Examples-cleanup.md ├── Example-2b-Metrics.md ├── Example-2a-Metrics.md ├── Example-1-PubSub.md ├── Example-4-Assets.md └── README.md └── README.md /Alert/requirements.txt: -------------------------------------------------------------------------------- 1 | # Function dependencies 2 | google-cloud-pubsub==1.0.0 -------------------------------------------------------------------------------- /Retry/requirements.txt: -------------------------------------------------------------------------------- 1 | # Function dependencies: 2 | google-cloud-pubsub==1.0.0 -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/splunk/splunk-gcp-functions/HEAD/.DS_Store -------------------------------------------------------------------------------- /PubSubFunction/requirements.txt: -------------------------------------------------------------------------------- 1 | # Function dependencies 2 | google-cloud-pubsub==1.0.0 -------------------------------------------------------------------------------- /images/GCS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/splunk/splunk-gcp-functions/HEAD/images/GCS.png -------------------------------------------------------------------------------- /images/Assets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/splunk/splunk-gcp-functions/HEAD/images/Assets.png -------------------------------------------------------------------------------- /images/PubSub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/splunk/splunk-gcp-functions/HEAD/images/PubSub.png -------------------------------------------------------------------------------- /images/Metrics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/splunk/splunk-gcp-functions/HEAD/images/Metrics.png -------------------------------------------------------------------------------- /GCS/requirements.txt: -------------------------------------------------------------------------------- 1 | # Function dependencies 2 | 3 | google-cloud-storage==1.19.1 4 | google-cloud-pubsub==1.0.0 -------------------------------------------------------------------------------- /PubSubFunction/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/splunk/splunk-gcp-functions/HEAD/PubSubFunction/.DS_Store -------------------------------------------------------------------------------- /Metrics/requirements.txt: -------------------------------------------------------------------------------- 1 | # Function dependencies 2 | google-cloud-monitoring==0.31.1 3 | google-cloud-pubsub==1.7.0 -------------------------------------------------------------------------------- /Assets/requirements.txt: -------------------------------------------------------------------------------- 1 | # Function dependencies 2 | 3 | google-cloud-storage==1.31.2 4 | google-cloud-asset==2.1.0 5 | google-cloud-resource-manager==0.30.2 6 | google-cloud-pubsub==1.7.0 -------------------------------------------------------------------------------- /Assets/props.conf: -------------------------------------------------------------------------------- 1 | [google:gcp:assets] 2 | category = Custom 3 | pulldown_type = 1 4 | DATETIME_CONFIG = CURRENT 5 | INDEXED_EXTRACTIONS = json 6 | LINE_BREAKER = ([\r\n]+) 7 | AUTO_KV_JSON = false 8 | KV_MODE=none 9 | NO_BINARY_CHECK = true 10 | disabled = false 11 | SHOULD_LINEMERGE = false 12 | TRANSFORMS-sourcetype_splunk_gcp_compute_instance=gcp_compute_instance 13 | TRUNCATE=70000 -------------------------------------------------------------------------------- /Alert/README.md: -------------------------------------------------------------------------------- 1 | # GCP Functions Library for Ingesting into Splunk 2 | 3 | **Alert Function** 4 | 5 | This function will be triggered by a Stackdriver Alert event that has been configured to send to a Webhook that is the url of this function, and packages it up into a Splunk event. The event is then sent to the Http Event Collector (HEC). 6 | If any faiures occur during sending the message to HEC, the event is posted back to a Pub-Sub Topic. A recovery function is provided in the library which is executed via a Cloud Scheduler trigger (PubSub). The recovery function will attempt to clear out the PubSub retry topic and send these events into HEC. 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Metrics/README.md: -------------------------------------------------------------------------------- 1 | # GCP Functions Library for Ingesting into Splunk 2 | 3 | 4 | **Metrics Function** 5 | 6 | This function is triggered by a Cloud Scheduler trigger (via PubSub). The function calls Stackdriver Monitoring APIs to retrieve the metrics (metrics request list, and poll frequency set in environment variable). These metrics are then sent to Splunk HEC. Two formats are supported - one to be compatible with the Add-on for GCP, sending the metrics as events into Splunk, the second is sent as a metric into Splunk's Metrics index. 7 | As with the PubSub Function, any failed messages are sent into a PubSub topic for retry. A recovery function will attempt to resend periodically. 8 | 9 | ![Metrics Function overview](../images/Metrics.png) -------------------------------------------------------------------------------- /PubSubFunction/README.md: -------------------------------------------------------------------------------- 1 | # GCP Functions Library for Ingesting into Splunk 2 | 3 | **PubSub Function** 4 | 5 | This function pulls any event that is posted into PubSub and packages it up into a Splunk event. The event is then sent to the Http Event Collector (HEC). The function is written such that the event format can be sent compatible with Splunk's Add-On for Google Cloud Platform (https://splunkbase.splunk.com/app/3088/). 6 | If any faiures occur during sending the message to HEC, the event is posted back to a Pub-Sub Topic. A recovery function is provided which is executed via a Cloud Scheduler trigger (PubSub). The recovery function will attempt to clear out the PubSub retry topic and send these events into HEC. 7 | 8 | 9 | ![PubSub Function overview](../images/PubSub.png) 10 | -------------------------------------------------------------------------------- /Assets/README.md: -------------------------------------------------------------------------------- 1 | # GCP Functions Library for Ingesting into Splunk 2 | 3 | **Assets Functions** 4 | 5 | This function periodically requests Assets inventory configurations (API Call) and publishes it to a GCS Bucket. The GCS Function can then ingest this content into Splunk via HEC. 6 | The trigger for the Assets function would be done by a Cloud Schedule trigger to a PubSub Topic. 7 | 8 | The assets function will push an event type of google:gcp:assets by default. This sourcetype is provided in the props.conf file 9 | 10 | Any messages that failed to be sent to HEC are sent into a PubSub topic for retry. A recovery function will attempt to resend periodically. 11 | 12 | This function requires Cloud Assets API to be enabled on the Project you will be requesting the Asset inventory from. 13 | 14 | ![Assets Inventory Function overview](../images/Assets.png) -------------------------------------------------------------------------------- /Assets/main.py: -------------------------------------------------------------------------------- 1 | # Assets 1.0 2 | # Called from PubSub Topic 3 | # Create CRON schedule to send a PubSub to call the Function to refresh the asset inventory 4 | # Use GCS function template to read from GCS into HEC 5 | 6 | import os 7 | import time 8 | 9 | def hello_pubsub(event, context): 10 | 11 | from google.cloud import asset_v1 12 | 13 | parent_id = os.environ['PARENT'] 14 | 15 | dump_file_path = os.environ['GCS_FILE_PATH'] 16 | now = time.time() 17 | 18 | client = asset_v1.AssetServiceClient() 19 | output_config = asset_v1.OutputConfig() 20 | output_config.gcs_destination.uri = dump_file_path+str(now) 21 | content_type = asset_v1.ContentType.RESOURCE 22 | 23 | response = client.export_assets( 24 | request={ 25 | "parent": parent_id, 26 | "content_type": content_type, 27 | "output_config": output_config 28 | } 29 | ) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Splunk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /GCS/README.md: -------------------------------------------------------------------------------- 1 | # GCP Functions Library for Ingesting into Splunk 2 | 3 | **Google Cloud Storage** 4 | 5 | This function triggers on objects being written to a GCS Bucket. The bucket is set in when defining the function settings. All of the contents from the bucket is sent to HEC in batches of events – an event delimiter/breaker regex should be set in a function environment variable so that the function can break up the batches in the appropriate places. The batches are formed so that it can achieve an even event distribution across indexers (otherwise all of the content would go into 1 indexer). Code variables can be configured within the template to adjust the batch size. The Splunk HEC token should be created with the appropriate sourcetype for the events, as well as the index, as the events are not re-formatted/procecessed in any way. 6 | Any messages that failed to be sent to HEC are sent into a PubSub topic for retry. A recovery function will attempt to resend periodically. 7 | 8 | Note- 9 | Due to the memory capacity limits for GCP Functions, this function has a limitation of sending log files that are smaller than 1GB. Log files larger than 1GB will cause the function to chunk up the file into smaller temporary files approx 900M each. These are cleaned up after they are copied into Splunk. 10 | 11 | ![GCS Function overview](../images/GCS.png) 12 | 13 | 14 | -------------------------------------------------------------------------------- /Retry/README.md: -------------------------------------------------------------------------------- 1 | # GCP Functions Library for Ingesting into Splunk 2 | 3 | **Retry Functions** 4 | 5 | This function periodically requests any failed events that were sent to a PubSub Retry Topic, and re-tries sending those events/metrics to HEC. The retry function can be collectively used for all of the functions, regardless of the source. If there is a subsequent failure to send to Splunk, the functions will not acknowledge the pull from PubSub, and therefore will be re-tried at a later attempt. 6 | Each time the function has a successful pull / send to Splunk (i.e. when there are events in the pubsub topic), it also triggers another retry function to exectute to ensure messages are resent back to Splunk in the shortest time possible. As each function will itself trigger another function (only one), the number of functions executing a recovery will grow in parallel until all of the messages have been consumed by Splunk. Each function will terminate as soon as it fails to pull any new messages from the retry pubsub topic, or fails to send to Splunk. 7 | If for any reason your Splunk Environment or connectivity has completely failed, you can use an over-ride feature to re-direct the events to a different instance. This is a fail-safe option. To do this, use the Over-ride environment variable settings on the function (tokens and URL). Note you may need to change the event type to fit the message types. 8 | -------------------------------------------------------------------------------- /Examples/Example-3-GCS.md: -------------------------------------------------------------------------------- 1 | # Example 3: GCS Function 2 | 3 | This example will create 2 PubSub Topics, create the GCS Function with a Retry Function, and a GCS example bucket. A Cloud Schedule is also created to trigger the Retry Function (via PubSub Topic). Note that the Schedule and Retry Trigger and Retry Topic is common between all of examples and doesn't need to be repeated if you build more than one example. 4 | 5 | 6 | #### PubSub Topics Created: 7 | 8 | **ExamplePubSubRetryTopic** : This topic can be common between all functions. This topic will collect failed writes from ExamplePubSub to HEC 9 | 10 | **ExampleRetryTrigger** : This topic can be common between all functions and triggers retries based on Cloud Schedule 11 | 12 | #### GCP Functions Created: 13 | 14 | **ExampleGCS** : GCS Function pulling from an ExampleBucket 15 | 16 | **ExampleRetry** : Retry Function to pull any failed messages from ExamplePubSub (can be re-used across all examples) 17 | 18 | ## GCS Bucket 19 | 20 | **example-bucket-xxxx** : Example GCS Bucket - note you will need to change the name to make sure that the bucket name is globally unique. 21 | 22 | 23 | ## CLI Example Scripts 24 | (run in bash or the Cloud Shell) 25 | 26 | **Note that you will need to change values in bold in the scripts below to identify your project id, GCS Bucket, HEC URL and HEC Token** 27 | You can also change the OS environment variables in the first section to fit your needs 28 | Note to use your Project ID, and not Project Name / Number 29 | 30 | When running the scripts the first time in a new project, if asked, accept the queries to create/initialise services 31 | 32 |
33 | 
34 | #set OS environment variables for script. Change these for your deployment
35 | 
36 | MY_PROJECT=MY_PROJECT
37 | GCS_FUNCTION=ExampleGCSFunction
38 | 
39 | GCS_BUCKET=example-bucket-xxxx/
40 | 
41 | HEC_URL=URL-OR-IP-AND-PORT-FOR-HEC
42 | GCS_TOKEN=TOKEN-0000-0000-0000-0000
43 | 
44 | RETRY_FUNCTON=ExamplePubSubRetry
45 | RETRY_TOPIC=ExamplePubSubRetryTopic
46 | RETRY_SUBSCRIPTION=ExamplePubSubRetryTopic-sub
47 | RETRY_TRIGGER_PUBSUB=ExampleRetryTrigger
48 | RETRY_SCHEDULE=ExampleRetrySchedule
49 | 
50 | #this section is specific for this example only; give the bucket a global unique id
51 | 
52 | gsutil mb gs://$GCS_BUCKET
53 | 
54 | 
55 | #the clone command only needs to be done once for all of the examples
56 | git clone https://github.com/splunk/splunk-gcp-functions.git
57 | 
58 | cd splunk-gcp-functions/GCS
59 | 
60 | #create function
61 | 
62 | gcloud functions deploy $GCS_FUNCTION --runtime python37 \
63 |   --trigger-bucket=$GCS_BUCKET --entry-point=hello_gcs \
64 |   --allow-unauthenticated --timeout=300 --memory=2048MB\
65 |   --set-env-vars=HEC_URL=$HEC_URL,HEC_TOKEN=$GCS_TOKEN,PROJECTID=$MY_PROJECT,RETRY_TOPIC=$RETRY_TOPIC
66 | 
67 | 
68 | #This is a common section for all examples
69 | #Doesn't need to be repeated for all unless you wish to have separate PubSub Topics for retrying different events.
70 | 
71 | gcloud pubsub topics create $RETRY_TOPIC
72 | 
73 | gcloud pubsub subscriptions create --topic $RETRY_TOPIC $RETRY_SUBSCRIPTION --ack-deadline=240
74 | cd ../Retry
75 | 
76 | #create Retry function
77 | 
78 | gcloud functions deploy $RETRY_FUNCTON --runtime python37 \
79 |  --trigger-topic=$RETRY_TRIGGER_PUBSUB --entry-point=hello_pubsub --allow-unauthenticated --timeout=240\
80 |  --set-env-vars=HEC_URL=PROJECTID=$MY_PROJECT,SUBSCRIPTION=$RETRY_SUBSCRIPTION,RETRY_TRIGGER_TOPIC=$RETRY_TRIGGER_PUBSUB
81 | 
82 | gcloud pubsub topics create $RETRY_TRIGGER_PUBSUB
83 | 
84 | gcloud scheduler jobs create pubsub $RETRY_SCHEDULE --schedule "*/10 * * * *" --topic $RETRY_TRIGGER_PUBSUB --message-body "Retry" --project $MY_PROJECT
85 | 
86 | 
87 | -------------------------------------------------------------------------------- /Alert/Installguide.md: -------------------------------------------------------------------------------- 1 | # GCP Cloud Functions – Installation / Setup Guide 2 | 3 | ## Alert Function 4 | (version 0.1) 5 | 6 | ## **Function Flow process** 7 | 8 | **Normal Flow:** 9 | Stackdriver Alert -> WebHook -> GCP Function -> HEC 10 | 11 | **Error Flow:** 12 | Stackdriver Alert -> WebHook -> GCP Function -> PubSub Topic (error:RetryTopic) 13 | Cloud Schedule -> PubSub Topic (Trigger) -> GCP Function(->Pull from PubSub Retry Topic)-> HEC 14 | 15 | ## **Pre-requisites** 16 | 17 | HEC set-up on a Splunk instance (load balancer needed for a cluster) 18 | HEC token/input MUST allow access to all indexes noted in the environment variables if the default token index is being over-ridden 19 | This function requires a sourcetype of google:gcp:alert to be set-up on the Splunk instance (see below) 20 | This function requires Cloud Functions API to be enabled. 21 | Set up Stackdriver Alert; create a Notification as a Web Hook to the URL of the Function 22 | Set up a PubSub Topic for error messages (Note the name of the topic - this will be used in the Environment variables later) 23 | 24 | ## **Function Dependencies:** 25 | Alert Function requires the Retry Function. Install and set up the Retry Function first 26 | 27 | 28 | ## Install with gcloud CLI 29 | 30 | This is a beta release. Cloud CLI scripts to follow shortly 31 | 32 | 33 | ## **Manual Setup** 34 | 1. Create a new Cloud Function 35 | 2. Name your function – note the url for the function - you will need it later for the Stackdriver Alert 36 | 3. Set the Trigger to be HTTP 37 | 4. Add the code: 38 | 5. Select Inline editor as source 39 | 6. Select the Runtime as Python 3.7 40 | 7. Copy the function code into the main.py 41 | 8. Copy the content of requirements.txt into the requirements.txt tab 42 | 9. Click on “Show variables like environment, networking, timeouts and more” to open up more options 43 | 10. Select the region where you want the function to run 44 | 11. Click on the + Add variable to open up the Environment variables entry 45 | 12. Add the Environment variables and values described in the table below 46 | 13. Click Deploy 47 | 14. You will need to install the Retry function if you wish to have a recovery for any events that failed to write to Splunk. See install guide for that function. 48 | 15. Create your Alert in Stackdriver. Set the Notification to send webhook to the url of the function 49 | 50 | ## **Function Environment Variables** 51 | 52 | 53 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
VariableValue
HEC_URLHostname/IP address and port number for URL for Splunk HEC (Load balancer required for cluster) 54 | e.g. mysplunkinstance.splunk.com:8088 or 113.114.115.192:8088
HEC_TOKENHEC Token for the input. Generate on Splunk instance.
PROJECTIDProject ID for where the Retry Topic exists
HOSTHost value that Splunk will assign for the Alert event. Defaults to GCP_Alert_Function
SPLUNK_SOURCETYPESourcetype that will be given to the event (defaults to google:gcp:alert)
SPLUNK_SOURCEIf set, this will be assigned to the “Source” of the event. If not set, defaults to "Stackdriver Alert:policyname"
INDEXIf this is set, its value can be set to over-ride the HEC token index. (defaults to no value – i.e. HEC token set index name)
RETRY_TOPICName of Topic to send event to on any failure scenario for the function
63 | 64 | 65 | 66 | ## **Sourcetype definition** 67 | 68 | Add this stanza to your Splunk props.conf 69 |
70 | [google:gcp:alert]
71 | category = Custom
72 | pulldown_type = 1
73 | DATETIME_CONFIG = 
74 | INDEXED_EXTRACTIONS = json
75 | LINE_BREAKER = ([\r\n]+)
76 | AUTO_KV_JSON = false
77 | KV_MODE=none
78 | NO_BINARY_CHECK = true
79 | disabled = false
80 | TRUNCATE=0
81 | 
82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /Assets/Installguide.md: -------------------------------------------------------------------------------- 1 | # GCP Cloud Functions – Installation / Setup Guide 2 | 3 | # Assets Function 4 | (0.2) 5 | 6 | ## **Pre-requisites** 7 | HEC set-up on a Splunk instance (load balancer needed for a cluster) 8 | (note that if the over-ride token/URL has been set in the function environment variables, the destination for this must match where the source of the failed function originated) 9 | This function will require a sourcetype to be created on your Splunk instance. An example sourcetype is available in props.conf in this folder. 10 | This function requires Cloud Functions API to be enabled. 11 | This function requires Cloud Assets API to be enabled on the Project you will be requesting the Asset inventory from. (https://cloud.google.com/asset-inventory/docs/quickstart and click on ) 12 | This function requires Cloud Scheduler API to be enabled on the Project. (https://cloud.google.com/scheduler/docs/setup and click on the link on the bottom ). Also make sure Cloud Scheduler has an assigned default region. 13 | Set up a PubSub Topic for error/re-try of messages from the functions. Note the name of the topic - this will be used for Environment variables for the functions. 14 | Set up a PubSub Trigger Topic (note that this topic is only going to be used as a trigger, with no events being sent there) 15 | Create a Cloud Schedule, triggering the Assets PubSub Topic. Schedule this for how frequent you wish to request an Asset Inventory (e.g. 2hrs, 24hrs) 16 | 17 | 18 | ## **Function Dependencies:** 19 | 20 | This function needs to be used with the GCS Function to read the Asset Inventory into Splunk HEC 21 | 22 | ## **Install with gcloud CLI** 23 | 24 | 25 | *Suggestion: It may be easier to start with the full Example script provided in the Examples folder as it will create most of the pre-requisites and supporting entities - https://github.com/splunk/splunk-gcp-functions/blob/master/Examples/Example-4-Assets.md* 26 | 27 | 28 | (run in bash or the Cloud Shell) 29 | 30 | git clone https://github.com/splunk/splunk-gcp-functions.git 31 | 32 | cd splunk-gcp-functions/Asset 33 | 34 | gcloud functions deploy **myAssetFunction** --runtime python37 --trigger-topic=**ASSETS_TRIGGER_TOPIC** --entry-point=hello_pubsub --allow-unauthenticated --timeout=120 --set-env-vars=PARENT='**ParentID**',GCS_FILE_PATH='**Path-and-prefix-to-GCS-Bucket**' 35 | 36 | ** *Update the bold values with your own settings* ** 37 | 38 | *Note that the above example does not identify or send the data to Splunk HEC - the GCS Function should be added to do this* 39 | 40 | 41 | 42 | ## **Manual Setup** 43 | 44 | 1. Create a new Cloud Function 45 | 2. Name your function 46 | 3. Set the Trigger to be Cloud Pub Sub 47 | 4. Select the Asset Trigger Trigger Topic from PubSub 48 | 5. Add the code: 49 | 6. Select Inline editor as source 50 | 7. Select the Runtime as Python 3.7 51 | 8. Copy the function code into the main.py 52 | 9. Copy the requirements.txt contents into the requirements.txt tab 53 | 10. Click on “Show variables like environment, networking, timeouts and more” to open up more options 54 | 11. Select the region where you want the function to run 55 | 12. Increase the timeout for this function to 120 56 | 13. Click on the + Add variable to open up the Environment variables entry 57 | 14. Add the Environment variables and values described in the table below 58 | 15. Click Deploy 59 | 60 | ## **Function Environment Variables** 61 | 62 | 63 | 64 | 67 |
VariableValue
PARENTProject ID, Organisation ID or Folder to collect the assets information - This can only be an organization number (such as "organizations/123"), a project ID (such as "projects/my-project-id"), a project number (such as "projects/12345"), or a folder number (such as "folders/123")
GCS_FILE_PATHGCS path to bucket where the Assets inventory will be written. 65 |
Enter the full path and initial prefix to this bucket and object - eg. gs://my_asset_bucket_for_project/asset_file
66 | Note that unixtime will be added to the filename on writing from the function
68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Examples/Troubleshooting.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting / Common Issues 2 | 3 | **When running the scripts first time, there is an error *ERROR: (gcloud.scheduler.jobs.create.pubsub) Could not determine the location for the project. Please try again.*** 4 | 5 | This error will occur if your Cloud Scheduler hasn't been run/enabled before. Go to the console and create a new schedule - it will ask for a region you wish to run the Project in. Once this is done, clean up the example and re-try the example scripts. 6 | 7 | **I'm not getting any data into Splunk** 8 | 9 | 1) Check that the URL and HEC token are valid. 10 | Try running this curl command (with the relevant url/token) to see if you can send into Splunk HEC 11 |
curl -k "https://mysplunkserver.example.com:8088/services/collector" \
12 |     -H "Authorization: Splunk CF179AE4-3C99-45F5-A7CC-3284AA91CF67" \
13 |     -d '{"event": "Hello, world!", "sourcetype": "manual"}' 
14 | 2) Check that your HEC_URL environment variable is correct. You don't need /services/collector for example. Just use IPaddress:Port or Hostname:Port. Note that for Splunk Cloud customers, there is a specific URL for HEC - this is usually in the format of http-inputs-mysplunkcloud.splunkcloud.com. (There is no need for a port number). 15 | 3) Do not use the ACK on the HEC settings 16 | 17 | **My PubSub Function is hitting maximum executions limit - what's gone wrong?** 18 | 19 | This is likely to be caused by the PubSub Function ingesting its own logs. This will cause an infinate loop / race. To stop this, edit the Log Export / Sink that the PubSub topic is subscribing to and make sure the filter excludes the PubSub Function from its logs. An easy way to resolve this is by using the filter resource.labels.function_name!=ExamplePubSubFunction (changing the name to your function name). 20 | Another possibilty is that your log export filter is too broad, and the number of of events is very large. Consider your filter design, and create more than one pubsub function if necessary to read from different topics/log exports to reduce the load on one function. 21 | 22 | 23 | **My metrics has a gap between groups of metrics in Splunk** 24 | 25 | This is normally caused by the Metrics Schedule and the Interval setting (TIME_INTERVAL) for the Metrics functions not being the same. For example, the schedule is 10mins whereas the metrics interval is 5. The TIME_INTERVAL setting should match that of the Schedule period. 26 | If the settings are the same, then examine the function log and search for errors - if you see function timeouts or memory limit exceeded, this indicates that you need to increase the memory allocated to the function and function timeout (usually due to a large number of metrics being requested). Alternatively, reduce the time interval, and the number of metrics for the function (for example, split the list over more than one function). 27 | 28 | **I have no metrics arriving in Splunk** 29 | 30 | If you want to sent your metrics to a metrics index, make sure that your HEC input specifies a metrics index. 31 | Also note the previous issue, where increasing the memory allocation and timeout for your function may resolve the issue (and/or reduce TIME_INTERVAL). 32 | 33 | **Some of my events in Splunk are not complete / truncated** 34 | 35 | This usually occurs due to the size of the event coming from Stackdriver - if they are very large, they will be truncated if you have only the default settings for TRUNCATE on the sourcetype (set to 10000). Some of the container logs for example can be 16K. You should update your sourcetype to add TRUNCATE=0. 36 | 37 | **My events from GCS are not being split properly in Splunk** 38 | 39 | This is usually down to your sourcetype for the HEC input not being set properly, or you have multiple sourcetypes going into the same GCS bucket. The Function currently only supports one sourcetype per GCS Bucket. Make sure you have the correct sourcetype on the HEC input setting. 40 | The other potential issue is that you have not set the LINE_BREAKER regex environment variable in the function settings. By default, it will break events up from the file by newline only. If you have multi-line events, make sure you set the LINE_BREAKER to have the same regex values as the Splunk sourcetype's settings in props.conf (you may need to consult with your Splunk admin). It is important also to make sure to set BEFORE=TRUE if the break is done before the LINE_BREAKER regex. 41 | 42 | **My events are not evenly distributed across my indexer cluster** 43 | 44 | This is typically down to 2 reasons: 45 | 1) Your Load Balancer has been set to have sticky sessions. Disable this if possible 46 | 2) You are only sending events to one indexer (one HEC URL which is one of the Indexers). If you don't have a Load balancer, consider using a Heavy Forwarder in front of your Indexer cluster, as the functions currently only support sending to 1 HEC URL per function. 47 | 48 | -------------------------------------------------------------------------------- /Examples/Examples-cleanup.md: -------------------------------------------------------------------------------- 1 | # Example Cleanup 2 | 3 | The script below cleans up (destructively) the examples created. 4 | 5 | ## Warning 6 | **THIS CANNOT BE UNDONE!!!** 7 | Make sure you don't want to keep any remaining data!!! 8 | The following commands also deletes all objects stored within the buckets. These objects cannot be recovered. If you want to avoid accidentally deleting objects, use the replace ``gsutil rm`` with the ``gsutil rb`` command below, which only deletes a bucket if the bucket is empty. 9 | 10 | 11 | Update the Highlighted items, and save as a shell script (e.g. cleanup.sh). 12 | Before running the script make sure you set permissions eg ``chmod +x cleanup.sh`` 13 | To run the script, use the example number as the script argument : 14 | For example 1 use ``./cleanup.sh 1``
15 | For example 2a use ``./cleanup.sh 2b``
16 | Both example 2a and 2b use ``./cleanup.sh 2``
17 | Example 3 ``./cleanup.sh 3`` etc.
18 | Using only the number as above will not clear out the Retry. To include retry, also include a second parameter of ``R``
19 | For example ``./cleanup.sh 1 R`` will clear out example 1 and the Retry function, pub sub etc
20 | To clean up all in one go, use ``./cleanup.sh ALL`` or just ``./cleanup.sh`` 21 | 22 | 23 |
 24 | #run this script from where you originally ran the original examples from
 25 | #use the same environment variable values as for the example builds
 26 | 
 27 | #Example-1 Start
 28 | PUBSUB_FUNCTION=ExamplePubSubFunction
 29 | 
 30 | PUBSUB_TOPIC=ExamplePubSubLogsTopic
 31 | PUBSUB_SINK1=ExampleSinkForFunctions
 32 | PUBSUB_SINK2=ExampleSinkNoFunctions
 33 | #Example-1 End (note the retry needs to be cleaned up below also)
 34 | 
 35 | #Example-2a/b Start
 36 | METRICS_FUNCTIONa=ExampleMetricsEventsFunction
 37 | METRICS_TRIGGER=ExampleMetricsTriggerTopic
 38 | METRICS_SCHEDULE=ExampleMetricsSchedule
 39 | METRICS_FUNCTIONb=ExampleMetricsFunction
 40 | #Example 2a/b End
 41 | 
 42 | #Example 3 Start
 43 | GCS_FUNCTION=ExampleGCSFunction
 44 | GCS_BUCKET=example-bucket-xxxx/
 45 | #Example 3 End
 46 | 
 47 | #Example 4 Start
 48 | ASSETS_FUNCTION=ExampleAssetsFunction
 49 | 
 50 | GCS_ASSETS_BUCKET=example-assets-bucket-xxxx/
 51 | GCS_FUNCTION=ExampleGCSAssetsFunction
 52 | 
 53 | ASSETS_SCHEDULE=ExampleAssetsSchedule
 54 | ASSETS_TRIGGER_PUBSUB=ExampleAssetsTrigger
 55 | #Example 4 End
 56 | 
 57 | #Common for all examples#
 58 | RETRY_FUNCTON=ExamplePubSubRetry
 59 | RETRY_TOPIC=ExamplePubSubRetryTopic
 60 | RETRY_SUBSCRIPTION=ExamplePubSubRetryTopic-sub
 61 | RETRY_TRIGGER_PUBSUB=ExampleRetryTrigger
 62 | RETRY_SCHEDULE=ExampleRetrySchedule
 63 | #End of common
 64 | 
 65 | #remove git project clone (all examples)
 66 | rm -r splunk-gcp-functions
 67 | 
 68 | 
 69 | case $1 in
 70 | 	1) 
 71 | 		CLEAN=1
 72 | 		;;
 73 | 	2a)
 74 |     	CLEAN=2
 75 |     	;;
 76 |     2b)
 77 |     	CLEAN=3
 78 |     	;;
 79 |     2)
 80 |     	CLEAN=4
 81 |     	;;
 82 |     3)
 83 |     	CLEAN=5
 84 |     	;;
 85 |     4)
 86 |     	CLEAN=6
 87 |     	;;
 88 |     ALL)
 89 |     	CLEAN=0
 90 |     	;;
 91 |     *)
 92 |     	CLEAN=0
 93 |     	;;
 94 | esac
 95 | 
 96 | #Example 1
 97 | if [ $CLEAN -eq 1 ] || [ $CLEAN -eq 0 ]
 98 | then
 99 | 	gcloud functions delete $PUBSUB_FUNCTION --quiet
100 | 	gcloud logging sinks delete $PUBSUB_SINK1 --quiet
101 | 	gcloud logging sinks delete $PUBSUB_SINK2 --quiet
102 | 	gcloud pubsub topics delete $PUBSUB_TOPIC --quiet
103 | fi
104 | 
105 | #Example 2a
106 | if [ $CLEAN -eq 2 ] || [ $CLEAN -eq 4 ] || [ $CLEAN -eq 0 ]
107 | then
108 | 	gcloud functions delete $METRICS_FUNCTIONa --quiet
109 | fi
110 | 
111 | #Example 2b
112 | if [ $CLEAN -eq 3 ] || [ $CLEAN -eq 4 ] || [ $CLEAN -eq 0 ]
113 | then
114 | 	gcloud functions delete $METRICS_FUNCTIONb --quiet
115 | fi
116 | 
117 | #Examples 2a/2b
118 | if [ $CLEAN -eq 2 ] || [ $CLEAN -eq 3 ] || [ $CLEAN -eq 4 ] || [ $CLEAN -eq 0 ]
119 | then
120 | 	gcloud pubsub topics delete $METRICS_TRIGGER --quiet
121 | 	gcloud scheduler jobs delete $METRICS_SCHEDULE --quiet
122 | fi
123 | 
124 | #Example 3
125 | if [ $CLEAN -eq 5 ] || [ $CLEAN -eq 0 ]
126 | then
127 | 	gcloud functions delete $GCS_FUNCTION --quiet
128 | 	gsutil rm -r gs://$GCS_BUCKET
129 | fi
130 | 
131 | #Example 4
132 | if [ $CLEAN -eq 6 ] || [ $CLEAN -eq 0 ]
133 | then
134 | 	gcloud functions delete $ASSETS_FUNCTION --quiet
135 | 	gcloud pubsub topics delete $ASSETS_TRIGGER_PUBSUB --quiet
136 | 	gcloud scheduler jobs delete $ASSETS_SCHEDULE --quiet
137 | 	gsutil rm -r gs://$GCS_ASSETS_BUCKET 
138 | fi
139 | 
140 | #Common for All
141 | if [ $# -eq 2 ]
142 | then
143 |   	if [ $2 == 'R' ]
144 |   	then
145 |   		CLEAN=0
146 |   	fi
147 | fi
148 | #Common for All
149 | if [ $CLEAN -eq 0 ]
150 | then
151 | 	gcloud functions delete $RETRY_FUNCTON --quiet
152 | 	gcloud scheduler jobs delete $RETRY_SCHEDULE --quiet
153 | 	gcloud pubsub subscriptions delete $RETRY_SUBSCRIPTION --quiet
154 | 	gcloud pubsub topics delete $RETRY_TOPIC --quiet
155 | 	gcloud pubsub topics delete $RETRY_TRIGGER_PUBSUB --quiet
156 | fi
157 | 
158 | 
159 | 
160 | 161 | -------------------------------------------------------------------------------- /Examples/Example-2b-Metrics.md: -------------------------------------------------------------------------------- 1 | # Example 2b Metrics Collection (Metrics Index) 2 | 3 | This example will create a Cloud Schedule which triggers the Metrics Function (via a PubSub Topic). The function will send the metrics into Splunk HEC as a metric format (into an metric index). The script will also create a retry PubSub Topic, and set up a Function to retry any failed messages to HEC. 4 | (If you have already created any other examples, the Cloud Schedule and PubSub Trigger topic doesn't need to be re-created) 5 | 6 | #### PubSub Topics: 7 | 8 | **ExampleRetryTrigger** : This topic is common between all functions and triggers retries based on Cloud Schedule 9 | 10 | **ExamplePubSubRetryTopic** : This topic can be common between all functions. This topic will collect failed writes from the Functions to HEC 11 | 12 | **ExampleRetryTrigger** : This topic can be common between all functions and triggers retries based on Cloud Schedule 13 | 14 | 15 | #### GCP Functions: 16 | 17 | **ExampleMetricsFunction** : Function to pull sample of metrics from compute. Formatted as a metric into metrics index via HEC 18 | 19 | **ExampleMetricRetryTopic** : Retry Function to pull any failed messages from ExampleMetricsFunction 20 | 21 | 22 | #### Cloud Scheduler 23 | 24 | **ExampleMetricsSchedule** : Schedule for Running Events (5mins) - Common for all metrics examples 25 | **ExampleRetry** : Retry Schedule (10mins) - common for all examples 26 | 27 | 28 | ## CLI Example 29 | 30 | (run in bash or the Cloud Shell) 31 | 32 | **Note that you will need to change values in bold in the scripts below to identify your project id, Log-Sink Service Account, HEC URL and HEC Token** 33 | You can also change the OS environment variables in the first section to fit your needs 34 | Note to use your Project ID, and not Project Name / Number 35 | 36 | When running the scripts the first time in a new project, if asked, accept the queries to create/initialise services 37 | 38 |
 39 | 
 40 | 
 41 | #set OS environment variables for script. Change these for your deployment
 42 | 
 43 | MY_PROJECT=MY_PROJECT
 44 | METRICS_FUNCTION=ExampleMetricsFunction
 45 | METRICS_TRIGGER=ExampleMetricsTriggerTopic
 46 | METRICS_SCHEDULE=ExampleMetricsSchedule
 47 | 
 48 | HEC_URL=URL-OR-IP-AND-PORT-FOR-HEC
 49 | METRICS_TOKEN=TOKEN-0000-0000-0000-0000
 50 | 
 51 | RETRY_FUNCTON=ExamplePubSubRetry
 52 | RETRY_TOPIC=ExamplePubSubRetryTopic
 53 | RETRY_SUBSCRIPTION=ExamplePubSubRetryTopic-sub
 54 | RETRY_TRIGGER_PUBSUB=ExampleRetryTrigger
 55 | RETRY_SCHEDULE=ExampleRetrySchedule
 56 | 
 57 | 
 58 | #This Schedule and topic only needs to be created once for all metrics functions unless you want different schedules. 
 59 | #Note:Match the schedule to the value in the TIME_INTERVAL environment variable below
 60 | #This example assumes a 5 minute schedule
 61 | 
 62 | gcloud pubsub topics create $METRICS_TRIGGER
 63 | 
 64 | gcloud scheduler jobs create pubsub $METRICS_SCHEDULE --schedule "*/5 * * * *" --topic $METRICS_TRIGGER --message-body "RunMetric" --project $MY_PROJECT
 65 | 
 66 | # ..End of common Metric trigger section
 67 | 
 68 | 
 69 | #this command only needs to be done once for all of the examples
 70 | git clone https://github.com/splunk/splunk-gcp-functions.git
 71 | 
 72 | cd splunk-gcp-functions/Metrics
 73 | 
 74 | #create function
 75 | 
 76 | #this could be replaced by a static yaml file with the env variables set:
 77 | 
 78 | echo -e "HEC_URL: $HEC_URL\\nHEC_TOKEN: $METRICS_TOKEN\\nPROJECTID: $MY_PROJECT\\nTIME_INTERVAL: '5'\\nRETRY_TOPIC: $RETRY_TOPIC\\nMETRIC_INDEX_TYPE: METRICS\\nMETRICS_LIST: '[\"compute.googleapis.com/instance/cpu/utilization\",\"compute.googleapis.com/instance/disk/read_ops_count\",\"compute.googleapis.com/instance/disk/write_bytes_count\",\"compute.googleapis.com/instance/disk/write_ops_count\",\"compute.googleapis.com/instance/network/received_bytes_count\",\"compute.googleapis.com/instance/network/received_packets_count\",\"compute.googleapis.com/instance/network/sent_bytes_count\",\"compute.googleapis.com/instance/network/sent_packets_count\",\"compute.googleapis.com/instance/uptime\"]'" > EnvMVars.yaml
 79 | 
 80 | gcloud functions deploy $METRICS_FUNCTION --runtime python37 \
 81 | --trigger-topic=$METRICS_TRIGGER --entry-point=hello_pubsub --allow-unauthenticated \
 82 | --env-vars-file EnvMVars.yaml
 83 | 
 84 | 
 85 | #This is a common section for all examples
 86 | #Doesn't need to be repeated for all unless you wish to have separate PubSub Topics for retrying different events.
 87 | 
 88 | gcloud pubsub topics create $RETRY_TOPIC
 89 | 
 90 | gcloud pubsub subscriptions create --topic $RETRY_TOPIC $RETRY_SUBSCRIPTION --ack-deadline=240
 91 | cd ../Retry
 92 | 
 93 | #create Retry function
 94 | 
 95 | gcloud functions deploy $RETRY_FUNCTON --runtime python37 \
 96 |  --trigger-topic=$RETRY_TRIGGER_PUBSUB --entry-point=hello_pubsub --allow-unauthenticated --timeout=120\
 97 |  --set-env-vars=PROJECTID=$MY_PROJECT,SUBSCRIPTION=$RETRY_SUBSCRIPTION,RETRY_TRIGGER_TOPIC=$RETRY_TRIGGER_PUBSUB
 98 | 
 99 | gcloud pubsub topics create $RETRY_TRIGGER_PUBSUB
100 | 
101 | gcloud scheduler jobs create pubsub $RETRY_SCHEDULE --schedule "*/10 * * * *" --topic $RETRY_TRIGGER_PUBSUB --message-body "Retry" --project $MY_PROJECT
102 | 
103 | 
104 | 
-------------------------------------------------------------------------------- /Examples/Example-2a-Metrics.md: -------------------------------------------------------------------------------- 1 | # Example 2a Metrics Collection (Event Index) 2 | 3 | This example will create a Cloud Schedule which triggers the Metrics Function (via a PubSub Topic). The function will send the metrics into Splunk HEC as an Event format (into an Event index). The script will also create a retry PubSub Topic, and set up a Function to retry any failed messages to HEC. 4 | (If you have already created any other examples, the Cloud Schedule and PubSub Trigger topic doesn't need to be re-created) 5 | 6 | #### PubSub Topics: 7 | 8 | **ExampleRetryTrigger** : This topic is common between all functions and triggers retries based on Cloud Schedule 9 | 10 | **ExamplePubSubRetryTopic** : This topic can be common between all functions. This topic will collect failed writes from the Functions to HEC 11 | 12 | **ExampleRetryTrigger** : This topic can be common between all functions and triggers retries based on Cloud Schedule 13 | 14 | 15 | 16 | #### GCP Functions: 17 | 18 | **ExampleMetricsEventFunction** : Function to pull sample of metrics from compute. Formatted as an Event into HEC 19 | 20 | **ExampleEventsRetryTopic** : Retry Function to pull any failed messages from ExampleMetricsFunction 21 | 22 | 23 | #### Cloud Scheduler 24 | 25 | **ExampleMetricsSchedule** : Schedule for Running Events (5mins) 26 | **ExampleRetry** : Retry Schedule (10mins) 27 | 28 | 29 | ## CLI Example 30 | 31 | (run in bash or the Cloud Shell) 32 | 33 | **Note that you will need to change values in bold in the scripts below to identify your project id, Log-Sink Service Account, HEC URL and HEC Token** 34 | You can also change the OS environment variables in the first section to fit your needs 35 | Note to use your Project ID, and not Project Name / Number 36 | 37 | When running the scripts the first time in a new project, if asked, accept the queries to create/initialise services 38 | 39 |
 40 | 
 41 | #set OS environment variables for script. Change these for your deployment
 42 | 
 43 | MY_PROJECT=MY_PROJECT
 44 | METRICS_FUNCTION=ExampleMetricsEventsFunction
 45 | METRICS_TRIGGER=ExampleMetricsTriggerTopic
 46 | METRICS_SCHEDULE=ExampleMetricsSchedule
 47 | 
 48 | HEC_URL=URL-OR-IP-AND-PORT-FOR-HEC
 49 | METRICS_TOKEN=TOKEN-0000-0000-0000-0000
 50 | 
 51 | RETRY_FUNCTON=ExamplePubSubRetry
 52 | RETRY_TOPIC=ExamplePubSubRetryTopic
 53 | RETRY_SUBSCRIPTION=ExamplePubSubRetryTopic-sub
 54 | RETRY_TRIGGER_PUBSUB=ExampleRetryTrigger
 55 | RETRY_SCHEDULE=ExampleRetrySchedule
 56 | 
 57 | 
 58 | #This Schedule and topic only needs to be created once for all metrics functions unless you want different schedules. 
 59 | #Note:Match the schedule to the value in the TIME_INTERVAL environment variable below
 60 | #This example assumes a 5 minute schedule
 61 | 
 62 | #This Schedule and topic only needs to be created once for all metrics functions unless you want different schedules. 
 63 | #Note:Match the schedule to the value in the TIME_INTERVAL environment variable below
 64 | #This example assumes a 5 minute schedule
 65 | 
 66 | gcloud pubsub topics create $METRICS_TRIGGER
 67 | 
 68 | gcloud scheduler jobs create pubsub $METRICS_SCHEDULE --schedule "*/5 * * * *" --topic $METRICS_TRIGGER --message-body "RunMetric" --project $MY_PROJECT
 69 | 
 70 | # ..End of common Metric trigger section
 71 | 
 72 | #the clone command only needs to be done once for all of the examples
 73 | git clone https://github.com/splunk/splunk-gcp-functions.git
 74 | 
 75 | 
 76 | cd splunk-gcp-functions/Metrics
 77 | 
 78 | #create function
 79 | 
 80 | #this could be replaced by a static yaml file with the env variables set:
 81 | 
 82 | echo -e "HEC_URL: $HEC_URL\\nHEC_TOKEN: $METRICS_TOKEN\\nPROJECTID: $MY_PROJECT\\nTIME_INTERVAL: '5'\\nRETRY_TOPIC: $RETRY_TOPIC\\nMETRICS_LIST: '[\"compute.googleapis.com/instance/cpu/utilization\",\"compute.googleapis.com/instance/disk/read_ops_count\",\"compute.googleapis.com/instance/disk/write_bytes_count\",\"compute.googleapis.com/instance/disk/write_ops_count\",\"compute.googleapis.com/instance/network/received_bytes_count\",\"compute.googleapis.com/instance/network/received_packets_count\",\"compute.googleapis.com/instance/network/sent_bytes_count\",\"compute.googleapis.com/instance/network/sent_packets_count\",\"compute.googleapis.com/instance/uptime\"]'" > EnvEVars.yaml
 83 | 
 84 | gcloud functions deploy $METRICS_FUNCTION --runtime python37 \
 85 | --trigger-topic=$METRICS_TRIGGER --entry-point=hello_pubsub --allow-unauthenticated \
 86 | --env-vars-file EnvEVars.yaml
 87 | 
 88 | 
 89 | #This is a common section for all examples
 90 | #Doesn't need to be repeated for all unless you wish to have separate PubSub Topics for retrying different events.
 91 | 
 92 | gcloud pubsub topics create $RETRY_TOPIC
 93 | 
 94 | gcloud pubsub subscriptions create --topic $RETRY_TOPIC $RETRY_SUBSCRIPTION --ack-deadline=240
 95 | cd ../Retry
 96 | 
 97 | #create Retry function
 98 | 
 99 | gcloud functions deploy $RETRY_FUNCTON --runtime python37 \
100 |  --trigger-topic=$RETRY_TRIGGER_PUBSUB --entry-point=hello_pubsub --allow-unauthenticated --timeout=240\
101 |  --set-env-vars=PROJECTID=$MY_PROJECT,SUBSCRIPTION=$RETRY_SUBSCRIPTION,RETRY_TRIGGER_TOPIC=$RETRY_TRIGGER_PUBSUB
102 | 
103 | gcloud pubsub topics create $RETRY_TRIGGER_PUBSUB
104 | 
105 | gcloud scheduler jobs create pubsub $RETRY_SCHEDULE --schedule "*/10 * * * *" --topic $RETRY_TRIGGER_PUBSUB --message-body "Retry" --project $MY_PROJECT
106 | 
107 | 
108 | 
-------------------------------------------------------------------------------- /Alert/main.py: -------------------------------------------------------------------------------- 1 | #GCP - AlertFunction v0.1.0 2 | 3 | '''MIT License 4 | Copyright (c) 2019 Splunk 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 11 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 12 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 13 | SOFTWARE. ''' 14 | 15 | import base64 16 | import argparse 17 | import os 18 | import pprint 19 | import time 20 | import json 21 | import re 22 | 23 | from datetime import datetime 24 | from datetime import date 25 | 26 | import time 27 | import requests 28 | from requests.adapters import HTTPAdapter 29 | import urllib3 30 | ##turns off the warning that is generated below because using self signed ssl cert 31 | urllib3.disable_warnings() 32 | 33 | 34 | def hello_world(request): 35 | """Responds to HTTP request from Alert Webhook. 36 | Args: 37 | request (flask.Request): HTTP request object. 38 | Returns: 39 | The response text or any set of values that can be turned into a 40 | Response object using 41 | `make_response `. 42 | """ 43 | now_time = round(time.time(),3) 44 | 45 | request_json = request.get_json() 46 | 47 | if request_json and 'incident' in request_json: 48 | incident= request_json['incident'] 49 | name = incident['policy_name'] 50 | if str(incident['ended_at'])=='None': 51 | incident['ended_at']="None" 52 | request_json['incident']=incident 53 | 54 | payload=str(request_json) 55 | else: 56 | print('unknown alert message') 57 | return 58 | 59 | 60 | try: 61 | host=os.environ['HOST'] 62 | except: 63 | host='GCP_Alert_Function' 64 | try: 65 | source=os.environ['SPLUNK_SOURCE'] 66 | except: 67 | source="Stackdriver Alert:"+name 68 | try: 69 | sourcetype=os.environ['SPLUNK_SOURCETYPE'] 70 | except: 71 | sourcetype='google:gcp:alert' 72 | try: 73 | indexname=os.environ['INDEX'] 74 | except: 75 | indexname='' 76 | 77 | 78 | if indexname!='': 79 | indexname='"index":"'+indexname+'",' 80 | 81 | splunkmessage='{"time":'+str(now_time)+',"host":"'+host+'","source":"'+source+'","sourcetype":"'+sourcetype+'",'+indexname 82 | 83 | payload=payload.replace("'",'"') 84 | splunkmessage=splunkmessage+'"event":'+payload+'}' 85 | #print('payload is:',splunkmessage) 86 | splunkHec(splunkmessage,source) 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | def splunkHec(logdata,source): 95 | url = 'https://'+os.environ['HEC_URL']+'/services/collector/event' 96 | token = os.environ['HEC_TOKEN'] 97 | s = requests.Session() 98 | s.mount( 'http://' , HTTPAdapter(max_retries= 3 )) 99 | s.mount( 'https://' , HTTPAdapter(max_retries= 3 )) 100 | 101 | authHeader = {'Authorization': 'Splunk '+ token} 102 | 103 | try: 104 | 105 | r = s.post(url, headers=authHeader, data=logdata.encode("utf-8"), verify=False, timeout=2) 106 | r.raise_for_status() 107 | except requests.exceptions.HTTPError as errh: 108 | print ("Http Error:",errh) 109 | if errh.response.status_code<500: 110 | print(r.json()) 111 | errorHandler(logdata,source,url,token) 112 | except requests.exceptions.ConnectionError as errc: 113 | print ("Error Connecting:",errc) 114 | errorHandler(logdata,source,url,token) 115 | except requests.exceptions.Timeout as errt: 116 | print ("Timeout Error:",errt) 117 | errorHandler(logdata,source,url,token) 118 | except requests.exceptions.RequestException as err: 119 | print ("Error: ",err) 120 | errorHandler(logdata,source,url,token) 121 | except: 122 | print("unknown Error in http post >> message content:") 123 | print(logdata.replace('\n','')) 124 | errorHandler(logdata,source,url,token) 125 | 126 | 127 | 128 | 129 | 130 | def errorHandler(logdata,source,url,token): 131 | """Publishes failed messages to Pub/Sub topic to Retry later.""" 132 | 133 | from google.cloud import pubsub_v1 134 | 135 | 136 | project_id = os.environ['PROJECTID'] 137 | topic_name = os.environ['RETRY_TOPIC'] 138 | publisher = pubsub_v1.PublisherClient() 139 | topic_path = publisher.topic_path(project_id, topic_name) 140 | 141 | 142 | data = logdata.encode('utf-8') 143 | # Add two attributes, origin and username, to the message 144 | future = publisher.publish(topic_path, data, url=url, token=token, origin=source, source='gcpSplunkAlertFunction') 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GCP Functions Library for Ingesting into Splunk 2 | 3 | This library contains GCP Function Templates to read from: 4 | - PubSub 5 | - Metrics 6 | - Google Cloud Storage 7 | - Assets 8 | 9 | **PubSub Function** 10 | 11 | This function pulls any event that is posted into PubSub and packages it up into a Splunk event. The event is then sent to the Http Event Collector (HEC). The function is written such that the event format can be sent compatible with Splunk's Add-On for Google Cloud Platform (https://splunkbase.splunk.com/app/3088/). 12 | If any failures occur during sending the message to HEC, the event is posted back to a Pub-Sub Topic. A recovery function is provided which is executed via a Cloud Scheduler trigger (PubSub). The recovery function will attempt to clear out the PubSub retry topic and send these events into HEC. 13 | 14 | **Metrics Function** 15 | 16 | This function is triggered by a Cloud Scheduler trigger (via PubSub). The function calls Stackdriver Monitoring APIs to retrieve the metrics (metrics request list, and poll frequency set in environment variable). These metrics are then sent to Splunk HEC. Two formats are supported - one to be compatible with the Add-on for GCP, sending the metrics as events into Splunk, the second is sent as a metric into Splunk's Metrics index. 17 | As with the PubSub Function, any failed messages are sent into a PubSub topic for retry. A recovery function will attempt to resend periodically. 18 | 19 | **Google Cloud Storage** 20 | 21 | This function triggers on objects being written to a GCS Bucket. The bucket is set in when defining the function settings. All of the contents from the bucket is sent to HEC in batches of events – an event delimiter/breaker regex should be set in a function environment variable so that the function can break up the batches in the appropriate places. The batches are formed so that it can achieve an even event distribution across indexers (otherwise all of the content would go into 1 indexer). Code variables can be configured within the template to adjust the batch size. The Splunk HEC token should be created with the appropriate sourcetype for the events, as well as the index, as the events are not re-formatted/procecessed in any way. 22 | Any messages that failed to be sent to HEC are sent into a PubSub topic for retry. A recovery function will attempt to resend periodically. 23 | 24 | **Assets** 25 | 26 | This function is periodically triggered via a Cloud Scheduler push to a PubSub topic. The function calls GCP’s API to retrieve the cloud asset list. The list is written to a GCS bucket. (bucket location is defined in an environment variable). 27 | Once in the GCS Bucket, the GCS function above can be used to read in the contents of the asset list into Splunk. 28 | 29 | **Alerts** 30 | 31 | This function will can be triggered by a Stackdriver Alert (via webhook), sending the alert details to HEC. 32 | 33 | **Billing** 34 | Billing can be read in via the GCS function. To trigger a billing report, there is a function here to do this: 35 | https://github.com/raddaoui/billing_export_bq_gcs/blob/master/main.py 36 | 37 | **Retry Function** 38 | 39 | This function periodically requests any failed events that were sent to a PubSub Topic, and re-tries sending to HEC. If there is a subsequent failure to sending to Splunk, the function will not acknowledge the pull from PubSub, and therefore will be re-tried at a later attempt. 40 | 41 | ## Getting started 42 | 43 | The Functions library here has a set of pre-defined Examples for each of the Functions (contained in the Examples folder in this repo). The examples contains scripts that can be executed to create a sample function and its components such as PubSub topics. This is a great place to start to understand how these functions work, and how to set them up in your environment. 44 | 45 | Note that the examples have a common retry function/configuration. This section will only need to be run once. In a similar way, the two metrics examples have a common trigger that can be reused for both examples. 46 | 47 | To run the examples, you can either run directly from the Cloud Shell in the GCP console (click **>_** to activate Cloud Shell), or by downloading the SDK or Quickstart onto your host/local machine (see here - https://cloud.google.com/sdk/install) 48 | 49 | Make sure you have installed git on the host running the example scripts (GCP's Cloud Shell already has this installed). 50 | 51 | You will need a Splunk instance that has a public facing URL for HEC. To get up and running with Splunk, visit here https://www.splunk.com/en_us/software/splunk-cloud.html and set up a Splunk Cloud Instance. 52 | 53 | (HEC can be configured using the instructions here - https://docs.splunk.com/Documentation/SplunkCloud/8.0.1/Data/UsetheHTTPEventCollector) 54 | 55 | ## Troubleshooting 56 | 57 | For common issues with this library, head to the Examples folder and read the troubleshooting guide 58 | 59 | ## Dashboards 60 | 61 | There is a template Splunk App available that provides dashboards to visualise the logs/metrics from these functions. This can be found/downloaded here - https://github.com/pauld-splunk/gcp_dashboards 62 | 63 | ## Terraform Automation 64 | 65 | A Terraform Project for automating deployment of these functions is available here - https://github.com/nstonesplunk/cumulostrata 66 | The project contains automation for configuring all parts of a data collection pipeline (Pub/Sub topics, schedulers, IAM service accounts, etc.) for GCS, Stackdriver Logging, and Stackdriver Metrics. 67 | 68 | ## Support 69 | These functions are currently open source and maintained by [pauld-splunk](https://github.com/pauld-splunk). This is not a Splunk supported solution. 70 | -------------------------------------------------------------------------------- /Examples/Example-1-PubSub.md: -------------------------------------------------------------------------------- 1 | # Example 1: PubSub 2 | 3 | This example will create 2 example Log Export Sinks, 3 PubSub Topics and use the PubSub Function with a Retry Function. A Cloud Schedule is also created to trigger the Retry Function (via PubSub Topic). Note that the Schedule and Retry Trigger and Retry Topic is common between all of examples and doesn't need to be repeated if you build more than one example. 4 | 5 | #### Log export Sinks Created: 6 | 7 | 8 | 9 |
SinkDescriptionFilter
ExampleSinkFunctionsSelects all GCP Function logs. Important note that it filters out the PubSub Function!!resource.labels.function_name!="ExamplePubSub"
ExampleSinkNoFunctionsSelects all Kubernetes/containers logsprotoPayload.serviceName="container.googleapis.com"
10 | 11 | **Caution: With aggregated export sinks, you can export a very large number of log entries. Design your logs query carefully.** 12 | 13 | 14 | #### PubSub Topics Created: 15 | 16 | **ExamplePubSubLogsTopic** : This topic will collect logs from the export sinks 17 | 18 | **ExamplePubSubRetryTopic** : This topic can be common between all functions. This topic will collect failed writes from ExamplePubSub to HEC 19 | 20 | **ExampleRetryTrigger** : This topic can be common between all functions and triggers retries based on Cloud Schedule 21 | 22 | #### GCP Functions Created: 23 | 24 | **ExamplePubSub** : PubSub Function pulling from ExamplePubSubLogsTopic 25 | 26 | **ExampleRetry** : Retry Function to pull any failed messages from ExamplePubSub (can be re-used across all examples) 27 | 28 | 29 | ## CLI Example Scripts 30 | (run in bash or the Cloud Shell) 31 | 32 | **Note that you will need to change values in bold in the scripts below to identify your project id, HEC URL and HEC Token** 33 | You can also change the OS environment variables in the first section to fit your needs 34 | Note to use your Project ID, and not Project Name / Number 35 | 36 | When running the scripts the first time in a new project, if asked, accept the queries to create/initialise services 37 | 38 |
 39 | 
 40 | #set OS environment variables for script. Change these for your deployment
 41 | 
 42 | MY_PROJECT=MY_PROJECT
 43 | PUBSUB_FUNCTION=ExamplePubSubFunction
 44 | 
 45 | PUBSUB_TOPIC=ExamplePubSubLogsTopic
 46 | PUBSUB_SINK1=ExampleSinkForFunctions
 47 | PUBSUB_SINK2=ExampleSinkNoFunctions
 48 | 
 49 | HEC_URL=URL-OR-IP-AND-PORT-FOR-HEC
 50 | PUBSUB_TOKEN=TOKEN-0000-0000-0000-0000
 51 | 
 52 | RETRY_FUNCTON=ExamplePubSubRetry
 53 | RETRY_TOPIC=ExamplePubSubRetryTopic
 54 | RETRY_SUBSCRIPTION=ExamplePubSubRetryTopic-sub
 55 | RETRY_TRIGGER_PUBSUB=ExampleRetryTrigger
 56 | RETRY_SCHEDULE=ExampleRetrySchedule
 57 | 
 58 | 
 59 | 
 60 | #this section is specific for this example only
 61 | 
 62 | gcloud pubsub topics create $PUBSUB_TOPIC
 63 | 
 64 | #create log-sinks...
 65 | 
 66 | #MAKE NOTE OF THIS SINK - IT ENSURES THAT THERE IS NO RECORDING OF THE FUNCTIONS OWN LOGS!!!
 67 | 
 68 | gcloud logging sinks create $PUBSUB_SINK1 \
 69 |   pubsub.googleapis.com/projects/$MY_PROJECT/topics/$PUBSUB_TOPIC \
 70 |   --log-filter="resource.labels.function_name!=$PUBSUB_FUNCTION"
 71 | 
 72 | LOG_SINK_SERVICE_ACCOUNT=`gcloud logging sinks describe $PUBSUB_SINK1 --format="value(writerIdentity)"`
 73 | 
 74 | #the last command will return the LOG_SINK_SERVICE_ACCOUNT 
 75 | gcloud pubsub topics add-iam-policy-binding $PUBSUB_TOPIC \
 76 |   --member $LOG_SINK_SERVICE_ACCOUNT  --role roles/pubsub.publisher
 77 | 
 78 | # THIS SINK WILL GET ALL LOGS OTHER THAN CLOUD FUNCTIONS - BEWARE IT MAY HAVE HIGH VOLUME!!!
 79 | 
 80 | gcloud logging sinks create $PUBSUB_SINK2 \
 81 |   pubsub.googleapis.com/projects/$MY_PROJECT/topics/$PUBSUB_TOPIC \
 82 |   --log-filter="resource.type!=cloud_function"
 83 | 
 84 | LOG_SINK_SERVICE_ACCOUNT=`gcloud logging sinks describe $PUBSUB_SINK2 --format="value(writerIdentity)"`
 85 | 
 86 | #the last command will return the LOG_SINK_SERVICE_ACCOUNT 
 87 | gcloud pubsub topics add-iam-policy-binding $PUBSUB_TOPIC \
 88 |   --member $LOG_SINK_SERVICE_ACCOUNT  --role roles/pubsub.publisher
 89 | 
 90 | #the clone command only needs to be done once for all of the examples
 91 | git clone https://github.com/splunk/splunk-gcp-functions.git
 92 | 
 93 | cd splunk-gcp-functions/PubSubFunction
 94 | 
 95 | #create function
 96 | 
 97 | gcloud functions deploy $PUBSUB_FUNCTION --runtime python37 \
 98 |   --trigger-topic=$PUBSUB_TOPIC --entry-point=hello_pubsub \
 99 |   --allow-unauthenticated \
100 |   --set-env-vars=HEC_URL=$HEC_URL,HEC_TOKEN=$PUBSUB_TOKEN,PROJECTID=$MY_PROJECT,RETRY_TOPIC=$RETRY_TOPIC
101 | 
102 | 
103 | #This is a common section for all examples
104 | #Doesn't need to be repeated for all unless you wish to have separate PubSub Topics for retrying different events.
105 | 
106 | gcloud pubsub topics create $RETRY_TOPIC
107 | 
108 | gcloud pubsub subscriptions create --topic $RETRY_TOPIC $RETRY_SUBSCRIPTION --ack-deadline=240
109 | cd ../Retry
110 | 
111 | #create Retry function
112 | 
113 | gcloud functions deploy $RETRY_FUNCTON --runtime python37 \
114 |  --trigger-topic=$RETRY_TRIGGER_PUBSUB --entry-point=hello_pubsub --allow-unauthenticated --timeout=240\
115 |  --set-env-vars=PROJECTID=$MY_PROJECT,SUBSCRIPTION=$RETRY_SUBSCRIPTION,RETRY_TRIGGER_TOPIC=$RETRY_TRIGGER_PUBSUB
116 | 
117 | gcloud pubsub topics create $RETRY_TRIGGER_PUBSUB
118 | 
119 | gcloud scheduler jobs create pubsub $RETRY_SCHEDULE --schedule "*/5 * * * *" --topic $RETRY_TRIGGER_PUBSUB --message-body "Retry" --project $MY_PROJECT
120 | 
121 | 
122 | -------------------------------------------------------------------------------- /Examples/Example-4-Assets.md: -------------------------------------------------------------------------------- 1 | # Example 4: Assets Function 2 | 3 | This example will create 3 PubSub Topics, create the Assets and a GCS Function with a Retry Function, a GCS example bucket. 2 Cloud Schedules are also created to trigger the Assets and Retry Functions (via PubSub Topic). Note that the Retry Schedule and Retry Trigger and Retry Topic is common between all of examples and doesn't need to be repeated if you build more than one example. 4 | 5 | Note: This function requires Cloud Assets API to be enabled on the Project you will be requesting the Asset inventory from. Do this before deploying the functions. 6 | Also, ensure that the Cloud Schedule is enabled before running the script. (create a dummy schedule to confirm this beforehand) 7 | 8 | #### PubSub Topics Created: 9 | 10 | **ExampleAssetsTopic** : This topic is created as a trigger for the function 11 | 12 | **ExamplePubSubRetryTopic** : This topic can be common between all functions. This topic will collect failed writes from ExamplePubSub to HEC 13 | 14 | **ExampleRetryTrigger** : This topic can be common between all functions and triggers retries based on Cloud Schedule 15 | 16 | #### GCP Functions Created: 17 | 18 | **ExampleAssets** : Function to call the Assets API 19 | 20 | **ExampleGCSAssets** : GCS Function pulling from an ExampleAssetsBucket 21 | 22 | **ExampleRetry** : Retry Function to pull any failed messages from ExamplePubSub (can be re-used across all examples) 23 | 24 | ## GCS Bucket 25 | 26 | **example-assets-bucket-xxxx** : Example GCS Bucket to store the Assets files - note you will need to change the name to make sure that the bucket name is globally unique. 27 | 28 | 29 | ## CLI Example Scripts 30 | (run in bash or the Cloud Shell) 31 | 32 | **Note that you will need to change values in bold in the scripts below to identify your project id, HEC URL, token and GCS Bucket** 33 | You can also change the OS environment variables in the first section to fit your needs 34 |
35 | Note:
36 | Use your Project ID for MY_PROJECT and not Project Name / Number 37 |
38 | Change the value of PARENT to your organisation id if you want to use Organization level, eg "organization/orgid" (see install guide for more details) 39 |
40 | When running the scripts the first time in a new project, if asked, accept the queries to create/initialise services 41 | 42 |
 43 | 
 44 | #set OS environment variables for script. Change these for your deployment
 45 | 
 46 | MY_PROJECT=MY_PROJECT
 47 | PARENT=projects/$MY_PROJECT
 48 | ASSETS_FUNCTION=ExampleAssetsFunction
 49 | # remember to give the bucket a global unique id. The file bath contains the object prefix for the object created by the asset function
 50 | GCS_ASSETS_BUCKET=example-assets-bucket-xxxx
 51 | GCS_FILE_PATH=gs://$GCS_ASSETS_BUCKET/example-assets
 52 | GCS_FUNCTION=ExampleGCSAssetsFunction
 53 | 
 54 | HEC_URL=URL-OR-IP-AND-PORT-FOR-HEC
 55 | ASSETS_TOKEN=TOKEN-0000-0000-0000-0000
 56 | 
 57 | ASSETS_SCHEDULE=ExampleAssetsSchedule
 58 | ASSETS_TRIGGER_PUBSUB=ExampleAssetsTrigger
 59 | 
 60 | RETRY_FUNCTON=ExamplePubSubRetry
 61 | RETRY_TOPIC=ExamplePubSubRetryTopic
 62 | RETRY_SUBSCRIPTION=ExamplePubSubRetryTopic-sub
 63 | RETRY_TRIGGER_PUBSUB=ExampleRetryTrigger
 64 | RETRY_SCHEDULE=ExampleRetrySchedule
 65 | 
 66 | #this section is specific for this example only; 
 67 | 
 68 | #make sure that the function has access to view the assets.
 69 | gcloud projects add-iam-policy-binding $MY_PROJECT \
 70 |   --member serviceAccount:$MY_PROJECT@appspot.gserviceaccount.com \
 71 |   --role roles/cloudasset.viewer
 72 | 
 73 | gsutil mb gs://$GCS_ASSETS_BUCKET
 74 | 
 75 | #the clone command only needs to be done once for all of the examples
 76 | git clone https://github.com/splunk/splunk-gcp-functions.git
 77 | 
 78 | cd splunk-gcp-functions/Assets
 79 | 
 80 | #create triggers
 81 | gcloud pubsub topics create $ASSETS_TRIGGER_PUBSUB
 82 | 
 83 | gcloud scheduler jobs create pubsub $ASSETS_SCHEDULE --schedule "0 */6 * * *" --topic $ASSETS_TRIGGER_PUBSUB --message-body "Assets" --project $MY_PROJECT
 84 | 
 85 | 
 86 | #create function
 87 | gcloud functions deploy $ASSETS_FUNCTION --runtime python37 \
 88 |   --trigger-topic=$ASSETS_TRIGGER_PUBSUB --entry-point=hello_pubsub \
 89 |   --allow-unauthenticated \
 90 |   --set-env-vars=PARENT=$PARENT,GCS_FILE_PATH=$GCS_FILE_PATH
 91 | 
 92 | 
 93 | cd ../GCS
 94 | 
 95 | #create function
 96 | 
 97 | gcloud functions deploy $GCS_FUNCTION --runtime python37 \
 98 |   --trigger-bucket=$GCS_ASSETS_BUCKET --entry-point=hello_gcs --timeout=120\
 99 |   --allow-unauthenticated --timeout=120\
100 |   --set-env-vars=HEC_URL=$HEC_URL,HEC_TOKEN=$ASSETS_TOKEN,PROJECTID=$MY_PROJECT,RETRY_TOPIC=$RETRY_TOPIC,BEFORE=TRUE,LINE_BREAKER='{\"name\":\"\/\/'
101 | 
102 | 
103 | #This is a common section for all examples
104 | #Doesn't need to be repeated for all unless you wish to have separate PubSub Topics for retrying different events.
105 | 
106 | gcloud pubsub topics create $RETRY_TOPIC
107 | 
108 | gcloud pubsub subscriptions create --topic $RETRY_TOPIC $RETRY_SUBSCRIPTION --ack-deadline=240
109 | cd ../Retry
110 | 
111 | #create Retry function
112 | 
113 | gcloud functions deploy $RETRY_FUNCTON --runtime python37 \
114 |  --trigger-topic=$RETRY_TRIGGER_PUBSUB --entry-point=hello_pubsub --allow-unauthenticated --timeout=240\
115 |  --set-env-vars=PROJECTID=$MY_PROJECT,SUBSCRIPTION=$RETRY_SUBSCRIPTION,RETRY_TRIGGER_TOPIC=$RETRY_TRIGGER_PUBSUB
116 | 
117 | gcloud pubsub topics create $RETRY_TRIGGER_PUBSUB
118 | 
119 | gcloud scheduler jobs create pubsub $RETRY_SCHEDULE --schedule "*/10 * * * *" --topic $RETRY_TRIGGER_PUBSUB --message-body "Retry" --project $MY_PROJECT
120 | 
121 | 
122 | -------------------------------------------------------------------------------- /Examples/README.md: -------------------------------------------------------------------------------- 1 | # Example Configuration builds 2 | 3 | The files here contain scripts can be executed to build a full sample configurations using all of the functions in this library. The following configurations are created: 4 | (Note that there are some common sections for these examples, which do not need to be re-run if one of the other examples has been created. This is noted in the scripts) 5 | 6 | To run the examples, you can either run directly from the Cloud Shell in the GCP console (click **>_** to activate Cloud Shell), or by downloading the SDK or Quickstart onto your host/local machine (see here - https://cloud.google.com/sdk/install) 7 | 8 | Make sure you have installed git on the host running the example scripts (GCP's Cloud Shell already has this installed). 9 | 10 | Please refer to the individual function documentation for any pre-requisites before running the examples. 11 | 12 | ## Example 1: PubSub 13 | 14 | This example will create 2 example Log Export Sinks, 2 PubSub Topics and use the PubSub Function with a Retry Function. A Cloud Schedule is also created to trigger the Retry Function (via PubSub Topic). Note that this Schedule and Topic is common between all of examples and doesn't need to be repeated if you build more than one example. 15 | 16 | ## Example 2a: Metrics Collection (Event Index) 17 | 18 | This example will create a Cloud Schedule which triggers the Metrics Function (via a PubSub Topic). The function will send the metrics into Splunk HEC as an Event format (into an Event index). The script will also create a retry PubSub Topic, and set up a Function to retry any failed messages to HEC. 19 | If you have already created any other examples, the Cloud Schedule and PubSub Trigger topic doesn't need to be re-created. 20 | 21 | ## Example 2b: Metrics Collection (Metrics Index) 22 | 23 | This example is a clone of example 2a, but this function will send the metrics into Splunk's Metrics Index. It creates a Cloud Schedule which triggers the Metrics Function (via a PubSub Topic). The script will also create a retry PubSub Topic, and set up a Function to retry any failed messages to HEC. 24 | Note that in practice, only one Cloud Schedule would be needed for metrics unless there is a need to have different schedules/intervals. If you want to run both examples, the section to create the Cloud Schedule for Metrics and its trigger PubSub Topic can be ignored. In the same way, if you have already created any other examples, the Cloud Schedule and PubSub Trigger topic doesn't need to be re-created. 25 | 26 | 27 | ## Example 3: GCS 28 | 29 | This example creates a Function that is trigged by an object being created in GCS. The script also creates a Retry Topic for any failed messages to Splunk HEC. A Retry Function is created to send any failed messages. It will also create a Cloud Schedule and PubSub Trigger - if you have already created any other examples, these don't need to be re-created. 30 | 31 | 32 | ## Example 4: Assets 33 | 34 | The example creates a function to collect asset information periodically, writing this into a GCS Bucket. The function is triggered by a PubSub Topic (called via Cloud Schedule). The example also builds a GCS Function as per Exanmple 3 to collect this asset data and post to Splunk. 35 | 36 | ## Example Cleanup 37 | 38 | The Examples can be cleaned up by copying and saving the script in the cleanup page. Update the variables (bucket names) highlighted in the script. Note that if you have changed the variables in any way, remember to change these for the cleanup, otherwise you may have services or components remaining after runing the script. **Note that this is a destructive process that cannot be undone - take care not to delete buckets or topics that contain data you wish to keep.** 39 | 40 | ## What the Scripts create... 41 | 42 | #### Log export Sinks: 43 | 44 | 45 | 46 |
SinkDescriptionFilter
ExampleSinkFunctionsSelects all GCP Function logs. Important note that it filters out the PubSub Function!!resource.labels.function_name!="ExamplePubSub"
ExampleSinkNoFunctionsSelects all Kubernetes/containers logsprotoPayload.serviceName="container.googleapis.com"
47 | 48 | **Caution: With aggregated export sinks, you can export a very large number of log entries. Design your logs query carefully.** 49 | 50 | 51 | #### PubSub Topics: 52 | 53 | **ExamplePubSubLogsTopic** : This topic will collect logs from the export sinks 54 | 55 | **ExamplePubSubRetryTopic** : This topic will collect failed writes from ExamplePubSub to HEC 56 | 57 | **ExampleMetricsRetryTopic** : This topic will collect failed writes from ExampleMetricsFunction to HEC 58 | 59 | **ExampleEventsRetryTopic** : This topic will collect failed writes from ExampleMetricsEventsFunction and ExampleAssets to HEC 60 | 61 | **ExampleRawRetryTopic** : This topic will collect failed writes from ExampleGCSFunction to HEC 62 | 63 | **ExampleAssetsRetryTopic** : This topic will collect failed writes from ExampleAssetFunction to HEC 64 | 65 | #### GCP Functions: 66 | 67 | **ExamplePubSub** : PubSub Function pulling from ExamplePubSubLogsTopic 68 | 69 | **ExamplePubSubRetry** : Retry Function to pull any failed messages from Functions 70 | 71 | **ExampleMetricsFunction** : Function to pull sample of metrics from compute. Formatted as metrics index into HEC 72 | 73 | **ExampleMetricsEventsFunction** : Mirror function to ExampleMetricsFunction, but sending metrics into a Splunk event index 74 | 75 | **ExampleGCSFunction** : Function to pull sample objects from a bucket 76 | 77 | **ExampleAssetFunction** : Function to pull asset information into HEC 78 | 79 | #### Cloud Scheduler 80 | 81 | **ExampleRetry** : Retry Schedule (10mins) 82 | **ExampleAsset** : Schedule to run Asset list (12hrs) 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /GCS/Installguide.md: -------------------------------------------------------------------------------- 1 | # GCS Function 2 | (Version 0.2.7) 3 | 4 | 5 | ## **Function Flow process** 6 | 7 | **Normal Flow:** 8 | GCS Object -> GCP Function -> HEC 9 | 10 | **Error Flow:** 11 | GCS Object -> GCP Function -> PubSub Retry Topic 12 | 13 | Cloud Schedule -> PubSub Topic (Trigger) -> GCP Function(->Pull from PubSub Retry Topic)-> HEC 14 | 15 | 16 | ## **Pre-requisites –** 17 | HEC set-up on a Splunk instance (load balancer needed for a cluster) 18 | HEC token/input MUST allow access to an index and specify a sourcetype for the log type being ingested. Note that all objects in the GCS bucket will be assigned both sourcetype and index per the token. 19 | Splunk: sourcetype (event break/time) must be set on the receiving indexers. (Note – you will need to use the event breaker regex for this function setup) 20 | This function requires Cloud Functions API to be enabled. 21 | Set up a PubSub Topic for error messages (RETRY_TOPIC). Note the name of the topic - this will be used in the Environment variables later. 22 | The Batch recovery function must be used for this function (not event), set with EVENT_TYPE as RAW – note also that the PubSub error topic needs to be ONLY for errors from functions with the same sourcetype/index HEC token; 23 | e.g. if the logs in bucket A has sourcetype B and the destination is a HEC destination C, and error PubSub Topic of D. The recovery function must use the same destination HEC destination C. If Bucket X also has the same sourcetype B, then it can also use the same PubSub error topic and recovery function. However, if another bucket Y has a sourcetype Z (or needs to go into a different index), it will need a separate error PubSub Topic and Recovery function. 24 | 25 | ## **Function Dependencies:** 26 | 27 | PubSub Function requires the Retry Function 28 | 29 | ## **Function Limits:** 30 | 31 | Due to the memory capacity limits for GCP Functions, this function has a limitation of sending log files that are smaller than 1GB. Log files larger than 1GB will cause the function to chunk up the file into smaller temporary files approx 900M each. These are cleaned up after they are copied into Splunk. 32 | 33 | ## **Install with gcloud CLI** 34 | 35 | *Suggestion: It may be easier to start with the full Example script provided in the Examples folder as it will create most of the pre-requisites and supporting entities - https://github.com/splunk/splunk-gcp-functions/blob/master/Examples/Example-3-GCS.md* 36 | 37 | (run in bash or the Cloud Shell) 38 | 39 | git clone https://github.com/splunk/splunk-gcp-functions.git 40 | 41 | cd splunk-gcp-functions/GCS 42 | 43 | gcloud functions deploy **myGCSFunction** --runtime python37 --trigger-bucket=**TRIGGER_BUCKET** --entry-point=hello_gcs --allow-unauthenticated --timeout=300 --memory=2048MB --set-env-vars=HEC_URL='**HOSTNAME_OR_IP_FOR_HEC**',HEC_TOKEN='**0000-0000-0000-0000**',PROJECTID='**Project-id**',RETRY_TOPIC='**Retry_Topic**' 44 | 45 | ***Update the bold values with your own settings*** 46 | 47 | (The command above uses the basic list of environment variables, with newline breaker) 48 | 49 | ## **Manual Setup** 50 | 51 | 1. Create a new Cloud Function 52 | 2. Name your function – note the name – see important note below on the log export 53 | 3. Set the Trigger to be Cloud Storage 54 | 4. Set Event Type as Finalize/Create 55 | 5. Select a Bucket from GCS 56 | 6. Add the code: 57 | 7. Select Inline editor as source 58 | 8. Select the Runtime as Python 3.7 59 | 9. Copy the function code into the main.py 60 | 10. Copy the requirements.txt contents into the requirements.txt tab 61 | 11. Click on “Show variables like environment, networking, timeouts and more” to open up more options 62 | 12. Select the region where you want the function to run 63 | 13. Click on the + Add variable to open up the Environment variables entry 64 | 14. Add the Environment variables and values described in the table below 65 | 15. Make sure you select the appropriate size'd Function Memory Allocation - if you are going to ingest large files, set to Max 2GB 66 | 16. Click Deploy 67 | 17. You will need to install the RetryBatch function if you wish to have a recovery for any events that failed to write to Splunk. See install guide for that function. 68 | 69 | ## **Function Environment Variables** 70 | 71 | 72 | 74 | 76 | 77 | 79 | 81 | 82 | 83 | 84 | 85 | 86 |
VariableValue
HEC_URLHostname/IP address and port number for URL for Splunk HEC (Load balancer required for cluster) 73 | e.g. mysplunkinstance.splunk.com:8088 or 113.114.115.192:8088
HEC_TOKENHEC Token for the input. Generate on Splunk instance. 75 | (make note of HEC token requirements above)
HOSTSet the host metadata for the events. Defaults to Host set by the Splunk HEC Token settings if not defined. Default is not set
LINE_BREAKEREnter the regex for the line breaking for the events in the bucket objects. 78 | Defaults to \n (newline)
BEFORESet this to TRUE if you want to break BEFORE the line breaker, or FALSE if you want to break After the line breaker. 80 | Defaults to FALSE
PROJECTIDProject ID for where the Retry Topic exists
RETRY_TOPICName of Topic to send event to on any failure scenario for the function
BATCHSize of Batch to send to HEC. Reduce this if you want less events per batch to be sent to Splunk. Increasing can improve throughput, but keep within HEC limits. Default 70k (if not set). Note that this needs to be AT LEAST the size of the largest event in the data being sent, otherwise the function will crash
THREADSNumber of worker threads to send payload to HEC. Use only if issues with overload on HEC. Default 127 (i.e. 128 threads)
EXCLUDEIf set, this should contain the Regex for any object key (filename) to exclude/ignore in the bucket. By default this is not set. e.g \d{9}.json would exclude all files ending with 9 numbers and .json
87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /PubSubFunction/Installguide.md: -------------------------------------------------------------------------------- 1 | # GCP Cloud Functions – Installation / Setup Guide 2 | 3 | ## PubSub Function 4 | (version 0.1.6) 5 | 6 | ## **Function Flow process** 7 | 8 | **Normal Flow:** 9 | Stackdriver Logging -> Logging Export -> PubSub Topic -> GCP Function -> HEC 10 | 11 | **Error Flow:** 12 | Stackdriver Logging -> Logging Export -> PubSub Topic -> GCP Function -> PubSub Topic (error:RetryTopic) 13 | Cloud Schedule -> PubSub Topic (Trigger) -> GCP Function(->Pull from PubSub Retry Topic)-> HEC 14 | 15 | ## **Pre-requisites** 16 | 17 | HEC set-up on a Splunk instance (load balancer needed for a cluster) 18 | HEC token/input MUST allow access to all indexes noted in the environment variables if the default token index is being over-ridden 19 | Install GCP Add-On https://splunkbase.splunk.com/app/3088/ (uses the same sourcetypes defined in the add-on) 20 | This function requires Cloud Functions API to be enabled. 21 | This function requires Cloud Scheduler API to be enabled on the Project. (https://cloud.google.com/scheduler/docs/setup and click on the link on the bottom ). Also make sure Cloud Scheduler has an assigned default region. 22 | Set up Stackdriver logs; create an export(s) and subscription to a PubSub Topic (see important note below) 23 | Set up a PubSub Topic for error messages (Note the name of the topic - this will be used in the Environment variables later) 24 | 25 | ## **Function Dependencies:** 26 | PubSub Function requires the Retry Function. Install and set up the Retry Function first 27 | 28 | 29 | ## Install with gcloud CLI 30 | 31 | *Suggestion: It may be easier to start with the full Example script provided in the Examples folder as it will create most of the pre-requisites and supporting entities - https://github.com/splunk/splunk-gcp-functions/blob/master/Examples/Example-1-PubSub.md* 32 | 33 | 34 | (run in bash or the Cloud Shell) 35 | 36 | git clone https://github.com/splunk/splunk-gcp-functions.git 37 | 38 | cd splunk-gcp-functions/PubSubFunction 39 | 40 | gcloud functions deploy **myPubSubFunction** --runtime python37 --trigger-topic=**TRIGGER_TOPIC** --entry-point=hello_pubsub --allow-unauthenticated --set-env-vars=HEC_URL='**HOSTNAME_OR_IP_FOR_HEC**',HEC_TOKEN='**0000-0000-0000-0000**',PROJECTID='**Project-id**',RETRY_TOPIC='**Retry_Topic**' 41 | 42 | ***Update the bold values with your own settings*** 43 | 44 | (The command above uses the basic list of environment variables) 45 | 46 | 47 | ## **Manual Setup** 48 | 1. Create a new Cloud Function 49 | 2. Name your function – note the name – see important note below on the log export 50 | 3. Set the Trigger to be Cloud Pub Sub 51 | 4. Select a Topic from PubSub 52 | 5. Add the code: 53 | 6. Select Inline editor as source 54 | 7. Select the Runtime as Python 3.7 55 | 8. Copy the function code into the main.py 56 | 9. Copy the content of requirements.txt into the requirements.txt tab 57 | 10. Click on “Show variables like environment, networking, timeouts and more” to open up more options 58 | 11. Select the region where you want the function to run 59 | 12. Click on the + Add variable to open up the Environment variables entry 60 | 13. Add the Environment variables and values described in the table below 61 | 14. In another browser window, check that the log export that is subscribed by the PubSub Topic has eliminated the name of the function. (see below) 62 | 15. Click Deploy 63 | 16. You will need to install the Retry function if you wish to have a recovery for any events that failed to write to Splunk. See install guide for that function. 64 | 65 | ## **Function Environment Variables** 66 | 67 | 68 | 70 | 71 | 72 | 73 | 74 | 75 | 80 | 85 | 86 | 87 |
VariableValue
HEC_URLHostname/IP address and port number for URL for Splunk HEC (Load balancer required for cluster) 69 | e.g. mysplunkinstance.splunk.com:8088 or 113.114.115.192:8088
HEC_TOKENHEC Token for the input. Generate on Splunk instance.
PROJECTIDProject ID for where the Retry Topic exists
HOSTHost value that Splunk will assign for the PubSub event. Defaults to GCPFunction
SPLUNK_SOURCETYPESourcetype that will be given to the event (defaults to google:gcp:pubsub:message)
SPLUNK_SOURCEIf set, this will be assigned to the “Source” of the event. If not set, defaults to PubSub topic
INDEXIf this is set, its value can be set to over-ride the HEC token index. If this is set to LOGNAME then another environment variable with the name of the log needs to be set with an index name e.g. if you want all logs from “cloudaudit.googleapis.com%2Factivity” to be sent to index ActivityIX, you need to create an environment variable with the name “activity” with the value of ActivityIX. 76 | Note to use the value after “%2F”, or if the log doesn’t have that, use the value after /logs/ (eg. A logname of projects/projname/logs/OSConfigAgent would have variable set to OSConfigAgent) 77 | Notes:HEC Token must have set access to the indexes noted here 78 | Wildcard values are not accepted 79 | (defaults to no value – i.e. HEC token set index name)
lognameA variable with a log name (ending only) will override the HEC token index for the event. Note that INDEX needs to be set to LOGNAME for this to be used. Use logname after /logs/ or if name has “%2F” in the name, use the logname after “%2F” 81 | Examples: 82 | cloudaudit.googleapis.com%2Factivity -> use activity 83 | /logs/OSConfigAgent -> use OSConfigAgent 84 | (defaults to no value)
COMPATIBLESet this to TRUE to maintain compatibility with Add-On. If not TRUE, event payload will be exact copy of PubSub event. Default is TRUE
RETRY_TOPICName of Topic to send event to on any failure scenario for the function
88 | 89 | 90 | 91 | 92 | ## **PUB-SUB FUNCTION: IMPORTANT USAGE NOTE** 93 | 94 | As the cloud function executes within GCP’s environment, its own logs are collected in Stacktdriver logs. If your Log Export collects logs from Cloud Functions **MAKE SURE YOU ELIMINATE THE FUNCTION NAME FROM THE EXPORT**. Logs for this function cannot be collected by itself! You will need another Function and log subscription to do this (i.e. one function monitoring the other) 95 | 96 | For example, if your function name is GCP-Pub-Sub, and you wish to collect logs from other functions, then the Export Filter needs to include resource.labels.function_name!="GCP-Pub-Sub" 97 | 98 | **Failure to do this will cause the function to race and max out function execution capacity in your project. (it is essentially logging itself, which then causes more logs to be created, causing a feedback race loop)** 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /Retry/Installguide.md: -------------------------------------------------------------------------------- 1 | # GCP Cloud Functions – Installation / Setup Guide 2 | 3 | # RetryEvent Function 4 | (0.1.5) 5 | 6 | ### **Pre-requisites** 7 | HEC set-up on a Splunk instance (load balancer needed for a cluster) 8 | (note that if the over-ride token/URL has been set in the function environment variables, the destination for this must match where the source of the failed function originated) 9 | Install GCP Add-On https://splunkbase.splunk.com/app/3088/ (uses the same sourcetypes defined in the add-on) 10 | Set up a PubSub Topic for error/re-try of messages from the functions. Note the name of the topic - this will be used for Environment variables for the functions. 11 | Set up Stackdriver log subscription for the PubSub error Topic 12 | Create a Retry Trigger PubSub Topic (note that this topic is only going to be used as a trigger, with no events being sent there) 13 | Create a Cloud Schedule, triggering the Retry PubSub Topic. Schedule this for how frequent you wish to “flush” out any events that failed to send to Splunk (e.g. 10 or 15mins) 14 | 15 | ### **Setup** 16 | 17 | 1. Create a new Cloud Function 18 | 2. Name your function 19 | 3. Set the Trigger to be Cloud Pub Sub 20 | 4. Select the Retry Trigger Topic from PubSub 21 | 5. Add the code: 22 | 6. Select Inline editor as source 23 | 7. Select the Runtime as Python 3.7 24 | 8. Copy the function code into the main.py 25 | 9. Copy the requirements.txt contents into the requirements.txt tab 26 | 10. Click on “Show variables like environment, networking, timeouts and more” to open up more options 27 | 11. Select the region where you want the function to run 28 | 12. Increase the timeout for this function to 120 29 | 13. Click on the + Add variable to open up the Environment variables entry 30 | 14. Add the Environment variables and values described in the table below 31 | 15. Click Deploy 32 | 33 | ### **Function Environment Variables** 34 | 35 | 36 | 37 | 38 | 39 | 43 | 46 | 51 | 52 | 53 |
VariableValue
PROJECTIDProject ID for where the Retry Topic exists
SUBSCRIPTIONName of the subscription that pulls from the Retry/Error PubSub Topic.
RETRY_TRIGGER_TOPICName of the Retry Trigger Topic that triggers the function. Used to spawn more re-tries.
HEC_URLOVER-RIDE Hostname/IP address and port number for URL for Splunk HEC (Load balancer required for cluster) 40 | e.g. mysplunkinstance.splunk.com:8088 or 113.114.115.192:8088 41 | This will point the destination of the message to a different URL to the originating message 42 | Default is original message URL. Do not set if you wish to keep original destination
HEC_TOKENHEC Token to OVER-RIDE the original destination for the event. Generate on Splunk instance. 44 | Note that this should be set on the Splunk side to be the same Index-type the token used for the function that is using this as a retry i.e. if a metrics index was the destination of the original, this over-ride token should indicate a metrics index also 45 | Default is original message Token. Do not set if you wish to keep original destination
EVENT_TYPEOnly Set if HEC_TOKEN and HEC_URL are set to over-ride the original event. Valid values: METRICS, EVENT, RAW 47 |
METRIC : use this for Metrics going into Metrics index 48 |
EVENT : use this for Metrics going into Event index 49 |
RAW : use this for GCS Function re-try. 50 |
Do not set if you wish to keep to the original destination
BATCHNumber of events to pull from Retry PubSub in one call. Note that higher numbers here will potentially result in a higher execution time. Take care not to set this too high - you may need to make the pubsub subcription Acknowledgement deadline for the retry topic higher if this is set to a large number (>200) and also adjust the function timeout to accomodate. General guidance is the function can recall approx 40k pubsub events in 5 minutes function call with a 256M Function allocation (multiple spawned functions)
Default = 100 (Recommended)
TIMEOUTTime in seconds for the function to stop pulling from Retry PubSub in one call (unless there are no events to retry). Note that if this is set higher than the function timeout, the function potentially will exit with a timeout - this could also result in some messages being sent more than once to Splunk. Guideance is to be same value as function timeout. Note: To avoid function timeouts where possible, the actual max execution will generally aim to be a few seconds less than the value set here.
Default = 240
54 | 55 | ## Usage Note 56 | This function may require tuning of settings to ensure a timely recovery of failed events. 57 | The main settings to consider are: 58 | - Function Timeout. Make sure that this is set large enough to give time for the function to retrieve the messages and send to Splunk. 59 | - PubSub Subcription Acknowledgement deadline. Make sure that this is set long enough so that it accepts acknowledgement of sending to Splunk. Setting this too small may result in duplication of events being sent to Splunk. Recommendation of 2 mins. 60 | - BATCH Variable. This determines how many events are retrieved from the PubSub Topic in one API call. As the API can be expensive in execution time. Setting this too large can result in a limited amount of API calls in the timeframe that the function has to execute (Function Timeout), but setting too small can result in loss of time taken to execute the API call. 61 | - TIMEOUT. This setting is useful to allow the function to know when the Function Timeout will occur (the function itself has no understanding of the function timeout setting). The function will attempt to maximise the time available without reaching the function timeout, which will save duplication of retrieving/sending and end result of duplicates events in Splunk. 62 | - Memory Usage. This is the least impacting setting. Increasing this will only benefit due to the CPU capacity given to the function. Improves slightly the time taken to send to HEC. 63 | 64 | ## Install with gcloud CLI 65 | 66 | *Suggestion: It may be easier to start with one of the full Example scripts provided in the Examples folder as they will create most of the pre-requisites and supporting entities - https://github.com/splunk/splunk-gcp-functions/blob/master/Examples* 67 | 68 | (run in bash or the Cloud Shell) 69 | 70 | git clone https://github.com/pauld-splunk/splunk-gcp-functions.git 71 | 72 | cd splunk-gcp-functions/Retry 73 | 74 | gcloud functions deploy **myRetryEventFunction** --runtime python37 --trigger-topic=**RETRY_TRIGGER_TOPIC** --entry-point=hello_pubsub --allow-unauthenticated --timeout=120 --set-env-vars=PROJECTID='**Project-id**',SUBSCRIPTION='**Retry_PubSub_Subscription**',RETRY_TRIGGER_TOPIC='**RETRY_TRIGGER_TOPIC**' 75 | 76 | ** *Update the bold values with your own settings* ** 77 | 78 | *Note that the above example does not over-ride the destination URL or HEC token* 79 | 80 | -------------------------------------------------------------------------------- /PubSubFunction/main.py: -------------------------------------------------------------------------------- 1 | #GCP - PubSubFunction v0.1.11 2 | 3 | '''MIT License 4 | Copyright (c) 2020 Splunk 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 11 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 12 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 13 | SOFTWARE. ''' 14 | 15 | import base64 16 | import argparse 17 | import os 18 | import pprint 19 | import time 20 | import json 21 | import re 22 | 23 | from datetime import datetime 24 | from datetime import date 25 | 26 | import time 27 | import requests 28 | from requests.adapters import HTTPAdapter 29 | import urllib3 30 | ##turns off the warning that is generated below because using self signed ssl cert 31 | urllib3.disable_warnings() 32 | 33 | 34 | def hello_pubsub(event, context): 35 | 36 | """Triggered from a message on a Cloud Pub/Sub topic. 37 | Args: 38 | event (dict): Event payload. 39 | context (google.cloud.functions.Context): Metadata for the event. 40 | """ 41 | now_time = round(time.time(),3) 42 | pubsub_message = base64.b64decode(event['data']).decode('utf-8') 43 | 44 | pattern='"timestamp":\s*"(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d*Z)' 45 | pos=re.search(pattern,pubsub_message) 46 | 47 | timestamp=pubsub_message[pos.start()+13:pos.end()] 48 | 49 | #get epoch time for timestamp from the event timestamp 50 | brokentime=timestamp[0:len(timestamp)-1].split(".") 51 | date_time_obj = datetime.strptime(brokentime[0], '%Y-%m-%dT%H:%M:%S') 52 | if len(brokentime)>1: 53 | nanos=brokentime[1] 54 | epoch=date_time_obj.strftime('%s')+'.'+nanos 55 | else: 56 | epoch=date_time_obj.strftime('%s') 57 | 58 | try: 59 | host=os.environ['HOST'] 60 | except: 61 | host='GCPFunction' 62 | try: 63 | sourcetype=os.environ['SPLUNK_SOURCETYPE'] 64 | except: 65 | sourcetype='google:gcp:pubsub:message' 66 | try: 67 | source=os.environ['SPLUNK_SOURCE'] 68 | except: 69 | source=context.resource.get("name") 70 | try: 71 | indexing=os.environ['INDEX'] 72 | except: 73 | indexing='False' 74 | 75 | indexname='' 76 | 77 | if indexing!='False': 78 | if indexing=='LOGNAME': 79 | #find the position of the logname 80 | st=pubsub_message.find('"logName":"') 81 | #make sure logname is in the event 82 | if st>0: 83 | #find end of the logname 84 | end=pubsub_message.find('",',st) 85 | #find the tail end of the standard lognames 86 | st_log=pubsub_message.find('%2F',st,end) 87 | if st_log==-1: 88 | #wasn't a standard logname, use all logname 89 | st_log=pubsub_message.find('/logs/',st,end) 90 | #final check if logname exists 91 | if st_log>0: 92 | #a logname is found, get the logname 93 | logname=pubsub_message[st_log+6:end] 94 | else: 95 | logname='NULL' 96 | else: 97 | #get the logname after %2F 98 | logname=pubsub_message[st_log+3:end] 99 | print(logname) 100 | if logname!='NULL': 101 | try: 102 | indexname=os.environ[logname] #get the index name from the environment variable 103 | except: 104 | indexname='' #variable not set, so default to empty string - use the token default index, or index set in another env variable 105 | else: 106 | indexname=indexing #if env variable INDEX is any value other than LOGNAME, then the value here is the index name 107 | 108 | if indexname!='': 109 | indexname='"index":"'+indexname+'",' 110 | 111 | 112 | source=context.resource['name'] 113 | splunkmessage='{"time":'+ epoch +',"host":"'+host+'","source":"'+source+'","sourcetype":"'+sourcetype+'",'+indexname 114 | 115 | 116 | try: 117 | COMPATIBLE=os.environ['COMPATIBLE'] 118 | except: 119 | COMPATIBLE='TRUE' 120 | 121 | if COMPATIBLE=='TRUE': 122 | payload='{"publish_time":'+str(now_time)+', "data":'+pubsub_message+', "attributes": {"logging.googleapis.com/timestamp":"'+timestamp+'"}}' 123 | else: 124 | #over-ride to allow raw payload through without original Splunk GCP Add-on wrapper 125 | payload=pubsub_message 126 | 127 | splunkmessage=splunkmessage+'"event":'+payload+'}' 128 | splunkHec(splunkmessage,source) 129 | 130 | 131 | 132 | def splunkHec(logdata,source): 133 | url = 'https://'+os.environ['HEC_URL']+'/services/collector/event' 134 | token = os.environ['HEC_TOKEN'] 135 | s = requests.Session() 136 | s.mount( 'http://' , HTTPAdapter(max_retries= 3 )) 137 | s.mount( 'https://' , HTTPAdapter(max_retries= 3 )) 138 | 139 | authHeader = {'Authorization': 'Splunk '+ token} 140 | 141 | try: 142 | 143 | r = s.post(url, headers=authHeader, data=logdata.encode("utf-8"), verify=False, timeout=2) 144 | r.raise_for_status() 145 | except requests.exceptions.HTTPError as errh: 146 | print ("Http Error:",errh) 147 | if errh.response.status_code<500: 148 | print(r.json()) 149 | errorHandler(logdata,source,url,token) 150 | except requests.exceptions.ConnectionError as errc: 151 | print ("Error Connecting:",errc) 152 | errorHandler(logdata,source,url,token) 153 | except requests.exceptions.Timeout as errt: 154 | print ("Timeout Error:",errt) 155 | errorHandler(logdata,source,url,token) 156 | except requests.exceptions.RequestException as err: 157 | print ("Error: ",err) 158 | errorHandler(logdata,source,url,token) 159 | except: 160 | print("unknown Error in http post >> message content:") 161 | print(logdata.replace('\n','')) 162 | errorHandler(logdata,source,url,token) 163 | 164 | 165 | 166 | def errorHandler(logdata,source,url,token): 167 | """Publishes failed messages to Pub/Sub topic to Retry later.""" 168 | 169 | from google.cloud import pubsub_v1 170 | 171 | 172 | project_id = os.environ['PROJECTID'] 173 | topic_name = os.environ['RETRY_TOPIC'] 174 | publisher = pubsub_v1.PublisherClient() 175 | topic_path = publisher.topic_path(project_id, topic_name) 176 | 177 | 178 | data = logdata.encode('utf-8') 179 | # Add url, token and source attributes to the message 180 | future = publisher.publish(topic_path, data, url=url, token=token, origin=source, source='gcpSplunkPubSubFunction') 181 | 182 | 183 | -------------------------------------------------------------------------------- /Retry/main.py: -------------------------------------------------------------------------------- 1 | #RetryAll0.2.1.py 2 | '''MIT License 3 | Copyright (c) 2019 Splunk 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 6 | copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 12 | SOFTWARE. ''' 13 | 14 | import os 15 | import requests 16 | from requests.adapters import HTTPAdapter 17 | import urllib3 18 | import time 19 | import threading 20 | from threading import Thread 21 | from queue import Queue 22 | ##turns off the warning that is generated below because using self signed ssl cert 23 | urllib3.disable_warnings() 24 | 25 | 26 | def hello_pubsub(event, context): 27 | """Triggered from a message on a Cloud Pub/Sub topic. 28 | Args: 29 | event (dict): Event payload. 30 | context (google.cloud.functions.Context): Metadata for the event. 31 | """ 32 | try: 33 | TIMEOUT=int(os.environ['TIMEOUT'])-20 34 | except: 35 | TIMEOUT=220 #default timeout for pulling from pub-sub. 36 | 37 | startTime = time.time() 38 | messageCount=1 39 | spawned=0 40 | while messageCount!=0: 41 | try: 42 | messageCount=synchronous_pull(os.environ['PROJECTID'],os.environ['SUBSCRIPTION']) 43 | except: 44 | messageCount=0 45 | if (time.time()-startTime)>TIMEOUT: 46 | messageCount=0 47 | if (messageCount>0) and (spawned==0): 48 | retrypushHandler() 49 | spawned=1 #only fire another retry once 50 | 51 | 52 | 53 | def synchronous_pull(project_id, subscription_name): 54 | """Pulling messages synchronously.""" 55 | # [START pubsub_subscriber_sync_pull] 56 | from google.cloud import pubsub_v1 57 | 58 | try: 59 | NUM_MESSAGES=int(os.environ['BATCH']) 60 | except: 61 | NUM_MESSAGES=100 #default pull from pub-sub 62 | 63 | subscriber = pubsub_v1.SubscriberClient() 64 | subscription_path = subscriber.subscription_path(project_id, subscription_name) 65 | 66 | # The subscriber pulls a specific number of messages. 67 | response = subscriber.pull(subscription_path, max_messages=NUM_MESSAGES) 68 | 69 | ack_ids=AckMessages() 70 | incount=0 71 | outcount=0 72 | 73 | queue = Queue() 74 | threadcount=10 75 | 76 | if len(response.received_messages)0: 97 | subscriber.acknowledge(subscription_path, ack_ids.ack_ids) 98 | outcount=len(ack_ids.ack_ids) 99 | 100 | print('in:'+str(incount)+' success:'+str(outcount)) 101 | return outcount 102 | 103 | 104 | 105 | #threadsafe ack list 106 | class AckMessages: 107 | def __init__(self): 108 | self.ack_ids = [] 109 | self._lock = threading.Lock() 110 | 111 | def locked_update(self, ack_id): 112 | with self._lock: 113 | self.ack_ids.append(ack_id) 114 | 115 | #thread worker - calls hec function 116 | class ThreadWorker(Thread): 117 | 118 | def __init__(self, queue): 119 | Thread.__init__(self) 120 | self.queue = queue 121 | 122 | def run(self): 123 | while True: 124 | # Get the payloads from the queue and expand the queue 125 | url, token, received_message, ack_ids = self.queue.get() 126 | try: 127 | if splunkHec(url,token,received_message.message.data): 128 | ack_ids.locked_update(received_message.ack_id) 129 | finally: 130 | self.queue.task_done() 131 | 132 | def splunkHec(url,token,logdata): 133 | #check for empty log 134 | if len(logdata)==0: 135 | return True 136 | #test for over-rides. All 3 over-ride variables must be available to over-ride. 137 | try: 138 | url_o = 'https://'+os.environ['HEC_URL']+'/services/collector' 139 | except: 140 | url_o = 'x' 141 | try: 142 | token_o = os.environ['HEC_TOKEN'] 143 | except: 144 | token_o = 'x' 145 | try: 146 | index_type=os.environ['EVENT_TYPE'] 147 | except: 148 | index_type='x' 149 | 150 | if (url_o!='x' and token_o!='x' and index_type!='x'): 151 | token=token_o 152 | if index_type=='METRIC': 153 | url = 'https://'+os.environ['HEC_URL']+'/services/collector' 154 | elif index_type=='EVENT': 155 | url = 'https://'+os.environ['HEC_URL']+'/services/collector/event' 156 | else: 157 | url = 'https://'+os.environ['HEC_URL']+'/services/collector/raw' 158 | 159 | s = requests.Session() 160 | s.mount( 'http://' , HTTPAdapter(max_retries= 3 )) 161 | s.mount( 'https://' , HTTPAdapter(max_retries= 3 )) 162 | 163 | authHeader = {'Authorization': 'Splunk '+ token} 164 | 165 | try: 166 | r = s.post(url, headers=authHeader, data=logdata, verify=False, timeout=2) 167 | r.raise_for_status() 168 | except requests.exceptions.HTTPError as errh: 169 | print ("Http Error:",errh) 170 | if errh.response.status_code<500: 171 | print(r.json()) 172 | return False 173 | except requests.exceptions.ConnectionError as errc: 174 | print ("Error Connecting:",errc) 175 | return False 176 | except requests.exceptions.Timeout as errt: 177 | print ("Timeout Error:",errt) 178 | return False 179 | except requests.exceptions.RequestException as err: 180 | print ("Error: ",err) 181 | return False 182 | return True 183 | 184 | 185 | def retrypushHandler(): 186 | """Publishes a message to Pub/Sub topic to fire another Retry""" 187 | 188 | from google.cloud import pubsub_v1 189 | 190 | print('spawning another handler') 191 | project_id = os.environ['PROJECTID'] 192 | topic_name = os.environ['RETRY_TRIGGER_TOPIC'] 193 | publisher = pubsub_v1.PublisherClient() 194 | topic_path = publisher.topic_path(project_id, topic_name) 195 | future = publisher.publish(topic_path, 'SelfSpawn'.encode("utf-8")) -------------------------------------------------------------------------------- /GCS/main.py: -------------------------------------------------------------------------------- 1 | #GCSfunction v0.2.8 2 | 3 | '''MIT License 4 | Copyright (c) 2020 Splunk 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 11 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 12 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 13 | SOFTWARE. ''' 14 | 15 | import logging 16 | import os 17 | from google.cloud import storage 18 | 19 | import threading 20 | from threading import Thread 21 | from queue import Queue 22 | 23 | import base64 24 | import argparse 25 | import pprint 26 | import re 27 | import urllib.parse 28 | import requests 29 | from requests.adapters import HTTPAdapter 30 | import urllib3 31 | ##turns off the warning that is generated below because using self signed ssl cert 32 | urllib3.disable_warnings() 33 | 34 | objectname="" 35 | source="" 36 | contents="" 37 | positions=[[]] 38 | 39 | def hello_gcs(event, context): 40 | """Triggered by a change to a Cloud Storage bucket. 41 | Args: 42 | event (dict): Event payload. 43 | context (google.cloud.functions.Context): Metadata for the event. 44 | """ 45 | file = event 46 | try: 47 | exclude=os.environ['EXCLUDE'] 48 | except: 49 | exclude='!settonotexcludeanything!' 50 | if re.search(exclude, file['name']) != None : 51 | print(f"Skipping file: {file['name']}. Object name matched exclusion") 52 | return 53 | 54 | print(f"Processing file: {file['name']}.") 55 | read_file(file) 56 | 57 | 58 | def read_file(file): 59 | # batch size balance between time to create http connection vs distribution of events across indexers (#characters/bytes per batch) 60 | try: 61 | batch=os.environ['BATCH'] #non-mandatory env variable. Default is 70000. If this is set too low, then it can crash the function - make this AT LEAST as large as the largest single event size in the object 62 | except: 63 | batch=70000 64 | # number of threads to copy to Splunk HEC - default 128 65 | try: 66 | threadcount=os.environ['THREADS'] #non-mandatory env variable. Default is 127 67 | except: 68 | threadcount=127 69 | 70 | try: 71 | linebrk=os.environ['LINE_BREAKER'] 72 | except: 73 | linebrk='\n' 74 | try: 75 | before=os.environ['BEFORE'] #non-mandatory env variable. Default is to break after 76 | if before not in ['TRUE','FALSE']: #validate - default to after if not TRUE or FALSE 77 | before='FALSE' 78 | except: 79 | before='FALSE' 80 | 81 | global objectname 82 | global contents 83 | global positions 84 | global source 85 | 86 | storage_client = storage.Client() 87 | objectname=file['bucket']+'/'+file['name'] 88 | pos=objectname.find('.tmp_chnk_.') 89 | if pos>-1: 90 | source=urllib.parse.quote(objectname[0:pos]) 91 | else: 92 | source=urllib.parse.quote(objectname) 93 | 94 | bucket = storage_client.get_bucket(file['bucket']) 95 | blob = bucket.get_blob(file['name']) 96 | 97 | try: 98 | blobsize = blob.size 99 | except: 100 | #exception happens when partial uploads/file not complete. Drop out of the function gracefully 101 | print('Info: Nothing sent to Splunk yet - the file in GCS has not completed upload. Will re-execute on full write') 102 | return 103 | #blobsize = blob.size 104 | 105 | maxsize=209715200 #200M chunks (can be tuned - but note that memory limits on CF will limit this) 106 | 107 | print(f"Object size: {blobsize}") 108 | 109 | if blobsize>maxsize+1 and not (".tmp_chnk_." in file['name']): 110 | print('Object size is too big for 1 pass. Splitting into sub-objects (temporary)') 111 | chunk_s=0 112 | chunk_e=maxsize 113 | counter=0 114 | write_client = storage.Client() 115 | lastpattern="(?s:.*)"+linebrk 116 | while chunk_ebatch: 173 | #more than one batch 174 | while endpt> message content:") 290 | print(logdata.replace('\n','')) 291 | errorHandler(logdata,objectname,url,token) 292 | 293 | 294 | 295 | def errorHandler(logdata,source,url,token): 296 | """Publishes failed messages to Pub/Sub topic to Retry later.""" 297 | 298 | from google.cloud import pubsub_v1 299 | 300 | 301 | project_id = os.environ['PROJECTID'] 302 | topic_name = os.environ['RETRY_TOPIC'] 303 | publisher = pubsub_v1.PublisherClient() 304 | topic_path = publisher.topic_path(project_id, topic_name) 305 | 306 | 307 | data = logdata.encode('utf-8') 308 | # Add url, token and source attributes to the message 309 | future = publisher.publish(topic_path, data, url=url, token=token, origin=source, source='gcpSplunkGCSFunction') 310 | -------------------------------------------------------------------------------- /Metrics/Installguide.md: -------------------------------------------------------------------------------- 1 | # GCP Cloud Functions – Installation / Setup Guide 2 | 3 | ## Metrics Function 4 | (version 0.5.8) 5 | 6 | ## **Function Flow process** 7 | 8 | **Normal Flow:** 9 | Cloud Schedule -> PubSub Topic (Trigger) -> GCP Function(->Pull from Stackdriver API)-> HEC 10 | 11 | **Error Flow:** 12 | Cloud Schedule -> PubSub Topic (Trigger) -> GCP Function(->Pull from Stackdriver API)-> PubSub Retry Topic 13 | Cloud Schedule -> PubSub Topic (Trigger) -> GCP Function(->Pull from PubSub Retry Topic)-> HEC 14 | 15 | 16 | ## **Pre-requisites –** 17 | HEC set-up on a Splunk instance (load balancer needed for a cluster) 18 | HEC token/input MUST allow access to an appropriate index – if the function is creating event metrics, an event index is needed, or if the function is to send to metrics index, the token must be associated with a metrics index. 19 | Install GCP Add-On https://splunkbase.splunk.com/app/3088/ (uses the same sourcetypes defined in the add-on) 20 | This function requires Cloud Functions API to be enabled. 21 | This function requires Cloud Scheduler API to be enabled on the Project. (https://cloud.google.com/scheduler/docs/setup and click on the link on the bottom ). Also make sure Cloud Scheduler has an assigned default region. 22 | Set up a PubSub Topic for error messages from an event based functions (PubSub Function, Metrics Events Function) OR if this will be generating metrics, a PubSub for metrics Note the name of the topic - this will be used for Environment variables for the functions. 23 | Set up Stackdriver log subscription for the PubSub error Topic 24 | Create a Retry Trigger PubSub Topic (note that this topic is only going to be used as a trigger, with no events being sent there) 25 | Create a Cloud Schedule, triggering the Retry PubSub Topic. Schedule this for how frequent you wish to “flush” out any events that failed to send to Splunk (e.g. 15mins) 26 | 27 | ## **Function Dependencies:** 28 | 29 | Metrics Function requires the Retry Function. 30 | 31 | 32 | ## **Install with gcloud CLI** 33 | 34 | *Suggestion: It may be easier to start with one of the full Example scripts provided in the Examples folder as they will create most of the pre-requisites and supporting entities - 35 | https://github.com/splunk/splunk-gcp-functions/blob/master/Examples/Example-2a-Metrics.md OR 36 | https://github.com/splunk/splunk-gcp-functions/blob/master/Examples/Example-2b-Metrics.md* 37 | 38 | (run in bash or the Cloud Shell) 39 | 40 | git clone https://github.com/splunk/splunk-gcp-functions.git 41 | 42 | cd splunk-gcp-functions/Metrics 43 | 44 | gcloud functions deploy **myMetricsFunction** --runtime python37 --trigger-topic=**METRICS_TRIGGER_TOPIC** --entry-point=hello_pubsub --allow-unauthenticated --env-vars-file **EnvironmentVariablesFile.yaml** 45 | 46 | ***Update the bold values with your own settings*** 47 | (The command above uses the basic list of environment variables, using defaults) 48 | 49 | ## **Manual Setup** 50 | 51 | 1. Create a new Cloud Function 52 | 2. Name your function 53 | 3. Set the Trigger to be Cloud Pub Sub 54 | 4. Select the Retry Trigger Topic from PubSub 55 | 5. Add the code: 56 | 6. Select Inline editor as source 57 | 7. Select the Runtime as Python 3.7 58 | 8. Copy the function code into the main.py 59 | 9. Copy the requirements.txt contents into the requirements.txt tab 60 | 10. Click on “Show variables like environment, networking, timeouts and more” to open up more options 61 | 11. Select the region where you want the function to run 62 | 12. Click on the + Add variable to open up the Environment variables entry 63 | 13. Add the Environment variables and values described in the table below 64 | 14. Click Deploy 65 | 66 | ## **Function Environment Variables** 67 | 68 | 69 | 71 | 74 | 75 | 78 | 79 | 82 | 85 | 88 | 89 |
VariableValue
HEC_URLHostname/IP address and port number for URL for Splunk HEC (Load balancer required for cluster) 70 | e.g. mysplunkinstance.splunk.com:8088 or 113.114.115.192:8088
HEC_TOKENHEC Token for the input. Generate on Splunk instance. 72 | Ideally this should be the same as the token used for the function that is using this as a retry 73 |
PROJECTIDProject ID for where the Retry Topic exists
METRICS_LISTA list of metrics for the function to pull. Enclose the comma separated list with square brackets. Use full names for the metrics. For example: 76 | ["compute.googleapis.com/instance/cpu/utilization","compute.googleapis.com/instance/disk/read_ops_count"] 77 |
TIME_INTERVALTime interval for the function to retrieve metrics for (in minutes). This is retrospective – i.e a setting of 5 will retrieve metrics from the last 5 minutes. Running 5, 10 or 15 minute intervals is a recommended setting; larger values may cause function timeouts, in which case you will need to adjust the function timeout setting
HOSTHostname you wish to give the event 80 | Defaults to GCPMetricsFunction 81 |
SPLUNK_SOURCETYPESourcetype to assign to the events. Note that this is only used if the metric is going into an event index. 83 | Defaults to google:gcp:monitoring 84 |
METRIC_INDEX_TYPESets the type of metrics index that is being sent to. This should be METRICS for metrics index, or EVENT for event index.The event format is compatible with the GCP Add-On metrics. 86 | Defaults to EVENT 87 |
RETRY_TOPICName of Topic to send event/metric to on any failure scenario for the function
90 | 91 | Note that if you have a long metrics list for one single Function, you may need to increase the memory allocated to the function. This may also require the function timeout to be increased, which also may be needed if the TIME_INTERVAL is long. 92 | 93 | 94 | 95 | If a CLI is used for this function, the configuration for the environment variables needs to be put into a configuration yaml file due to the list of metrics. The example below assumes that the variables have been set in a file, whereas the examples include a script to create that file. 96 | 97 | 98 | ## Example Metrics lists 99 | 100 | There are a significant number of metrics available from GCP. The example (Metrics 2a and 2b) will set up a simple list of compute metrics, but the Function environment variables can be edited to include many more. For a full list of metrics, have a look at the google cloud documentation here: https://cloud.google.com/monitoring/api/metrics_gcp 101 | Here are some samples that can be used for the METRICS_LIST variable: 102 | 103 | ### Compute: 104 | 105 |
106 | ["compute.googleapis.com/instance/cpu/utilization","compute.googleapis.com/instance/disk/read_ops_count","compute.googleapis.com/instance/disk/read_bytes_count","compute.googleapis.com/instance/disk/write_bytes_count","compute.googleapis.com/instance/disk/write_ops_count","compute.googleapis.com/instance/network/received_bytes_count","compute.googleapis.com/instance/network/received_packets_count","compute.googleapis.com/instance/network/sent_bytes_count","compute.googleapis.com/instance/network/sent_packets_count","compute.googleapis.com/instance/uptime","compute.googleapis.com/firewall/dropped_bytes_count","compute.googleapis.com/firewall/dropped_packets_count"]
107 | 
108 | 109 | ### Cloud Functions: 110 | 111 |
112 | ["cloudfunctions.googleapis.com/function/active_instances","cloudfunctions.googleapis.com/function/execution_count","cloudfunctions.googleapis.com/function/execution_times","cloudfunctions.googleapis.com/function/network_egress","cloudfunctions.googleapis.com/function/user_memory_bytes"]
113 | 
114 | 115 | ### Containers / Kubernetes 116 | 117 |
118 | ["container.googleapis.com/container/cpu/utilization","container.googleapis.com/container/disk/bytes_used","container.googleapis.com/container/accelerator/duty_cycle","container.googleapis.com/container/accelerator/memory_total","container.googleapis.com/container/accelerator/memory_used","container.googleapis.com/container/accelerator/request","container.googleapis.com/container/cpu/reserved_cores","container.googleapis.com/container/cpu/usage_time","container.googleapis.com/container/disk/bytes_total","container.googleapis.com/container/disk/bytes_used","container.googleapis.com/container/disk/inodes_free","container.googleapis.com/container/disk/inodes_total","container.googleapis.com/container/memory/bytes_total","container.googleapis.com/container/memory/bytes_used","container.googleapis.com/container/uptime"]
119 | 
120 | ["kubernetes.io/container/accelerator/duty_cycle", "kubernetes.io/container/accelerator/memory_total", "kubernetes.io/container/accelerator/memory_used", "kubernetes.io/container/accelerator/request", "kubernetes.io/container/cpu/core_usage_time", "kubernetes.io/container/cpu/limit_cores", "kubernetes.io/container/cpu/limit_utilization", "kubernetes.io/container/cpu/request_cores", "kubernetes.io/container/cpu/request_utilization", "kubernetes.io/container/ephemeral_storage/limit_bytes", "kubernetes.io/container/ephemeral_storage/request_bytes", "kubernetes.io/container/ephemeral_storage/used_bytes", "kubernetes.io/container/memory/limit_bytes", "kubernetes.io/container/memory/limit_utilization", "kubernetes.io/container/memory/page_fault_count", "kubernetes.io/container/memory/request_bytes", "kubernetes.io/container/memory/request_utilization", "kubernetes.io/container/memory/used_bytes", "kubernetes.io/container/restart_count", "kubernetes.io/container/uptime"]
121 | 
122 | ["kubernetes.io/node/cpu/allocatable_cores", "kubernetes.io/node/cpu/allocatable_utilization", "kubernetes.io/node/cpu/core_usage_time", "kubernetes.io/node/cpu/total_cores", "kubernetes.io/node/ephemeral_storage/allocatable_bytes", "kubernetes.io/node/ephemeral_storage/inodes_free", "kubernetes.io/node/ephemeral_storage/inodes_total", "kubernetes.io/node/ephemeral_storage/total_bytes", "kubernetes.io/node/ephemeral_storage/used_bytes", "kubernetes.io/node/memory/allocatable_bytes", "kubernetes.io/node/memory/allocatable_utilization", "kubernetes.io/node/memory/total_bytes", "kubernetes.io/node/memory/used_bytes", "kubernetes.io/node/network/received_bytes_count", "kubernetes.io/node/network/sent_bytes_count", "kubernetes.io/node/pid_limit", "kubernetes.io/node/pid_used", "kubernetes.io/node_daemon/cpu/core_usage_time", "kubernetes.io/node_daemon/memory/used_bytes"]
123 | 
124 | ["kubernetes.io/pod/network/received_bytes_count", "kubernetes.io/pod/network/sent_bytes_count", "kubernetes.io/pod/volume/total_bytes", "kubernetes.io/pod/volume/used_bytes", "kubernetes.io/pod/volume/utilization"]
125 | 
126 | 
127 | 128 | ### Storage 129 |
130 | [storage.googleapis.com/api/request_count",storage.googleapis.com/network/received_bytes_count",storage.googleapis.com/network/sent_bytes_count",storage.googleapis.com/storage/object_count"]
131 | 
132 | 133 | ### Logging 134 | 135 |
136 | ["logging.googleapis.com/billing/bytes_ingested","logging.googleapis.com/billing/monthly_bytes_ingested","logging.googleapis.com/byte_count","logging.googleapis.com/exports/byte_count","logging.googleapis.com/exports/error_count","logging.googleapis.com/exports/log_entry_count","logging.googleapis.com/log_entry_count","logging.googleapis.com/logs_based_metrics_error_count","logging.googleapis.com/metric_throttled","logging.googleapis.com/time_series_count"]
137 | 
138 | 139 | ### Monitoring 140 | 141 |
142 | ["monitoring.googleapis.com/billing/bytes_ingested","monitoring.googleapis.com/stats/num_time_series","monitoring.googleapis.com/uptime_check/content_mismatch","monitoring.googleapis.com/uptime_check/error_code","monitoring.googleapis.com/uptime_check/http_status","monitoring.googleapis.com/uptime_check/request_latency"]
143 | 
144 | 145 | ### PubSub 146 | 147 |
148 | ["pubsub.googleapis.com/snapshot/backlog_bytes","pubsub.googleapis.com/snapshot/backlog_bytes_by_region","pubsub.googleapis.com/snapshot/config_updates_count","pubsub.googleapis.com/snapshot/num_messages","pubsub.googleapis.com/snapshot/num_messages_by_region","pubsub.googleapis.com/snapshot/oldest_message_age","pubsub.googleapis.com/snapshot/oldest_message_age_by_region","pubsub.googleapis.com/subscription/ack_message_count","pubsub.googleapis.com/subscription/backlog_bytes","pubsub.googleapis.com/subscription/byte_cost","pubsub.googleapis.com/subscription/config_updates_count","pubsub.googleapis.com/subscription/mod_ack_deadline_message_count","pubsub.googleapis.com/subscription/mod_ack_deadline_message_operation_count","pubsub.googleapis.com/subscription/mod_ack_deadline_request_count","pubsub.googleapis.com/subscription/num_outstanding_messages","pubsub.googleapis.com/subscription/num_undelivered_messages","pubsub.googleapis.com/subscription/oldest_unacked_message_age_by_region","pubsub.googleapis.com/subscription/pull_ack_message_operation_count","pubsub.googleapis.com/subscription/pull_ack_request_count","pubsub.googleapis.com/subscription/pull_message_operation_count","pubsub.googleapis.com/subscription/pull_message_operation_count","pubsub.googleapis.com/subscription/push_request_count","pubsub.googleapis.com/subscription/push_request_latencies","pubsub.googleapis.com/subscription/sent_message_count","pubsub.googleapis.com/topic/message_sizes","pubsub.googleapis.com/topic/num_unacked_messages_by_region","pubsub.googleapis.com/topic/oldest_unacked_message_age_by_region"]
149 | 
150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /Metrics/main.py: -------------------------------------------------------------------------------- 1 | #GCPMetricsFunction v0.7.2 2 | #All-in-one metrics function 3 | 4 | '''MIT License 5 | Copyright (c) 2019 Splunk 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 12 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 13 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 14 | SOFTWARE. ''' 15 | 16 | import base64 17 | import argparse 18 | import os 19 | import pprint 20 | import time 21 | import json 22 | import re 23 | import threading 24 | from threading import Thread 25 | from queue import Queue 26 | 27 | from google.cloud import monitoring_v3 28 | from datetime import datetime 29 | 30 | from datetime import date 31 | 32 | import time 33 | import requests 34 | from requests.adapters import HTTPAdapter 35 | import urllib3 36 | ##turns off the warning that is generated below because using self signed ssl cert 37 | urllib3.disable_warnings() 38 | 39 | 40 | #threadsafe HEC Events list 41 | class HECMessages: 42 | def __init__(self): 43 | self.HECevents = [] 44 | self._lock = threading.Lock() 45 | 46 | def locked_update(self, HECevent): 47 | with self._lock: 48 | self.HECevents.append(HECevent) 49 | 50 | """Triggered from a message on a Cloud Pub/Sub topic. 51 | Args: 52 | event (dict): Event payload. 53 | context (google.cloud.functions.Context): Metadata for the event. 54 | These values are ignored as used only as a Trigger for this function 55 | """ 56 | 57 | def hello_pubsub(event, context): 58 | 59 | HEC_Pack_size=20 # number of events per http post to HEC. Max size = 5MB by default on HEC 60 | now = time.time() 61 | #HECevents=[] 62 | HECevents=HECMessages() #create threadsafe message list 63 | 64 | metricslist=json.loads(os.environ['METRICS_LIST']) 65 | try: 66 | payloadType=os.environ['METRIC_INDEX_TYPE'] 67 | except: 68 | payloadType='EVENT' 69 | #print(metricslist) 70 | 71 | workers=len(metricslist) 72 | if workers>8: 73 | workers=8 74 | 75 | metricsq=Queue() 76 | for x in range(workers): 77 | worker = BuilderThreadWorker(metricsq) 78 | # Set as daemon thread 79 | worker.daemon = True 80 | worker.start() 81 | 82 | for metric in metricslist: 83 | metricsq.put((metric, now, HECevents,payloadType)) 84 | 85 | #wait for all of the builds to complete 86 | metricsq.join() 87 | 88 | message_counter=0 89 | package='' 90 | flushed=0 91 | 92 | workers=int(round(len(HECevents.HECevents)/HEC_Pack_size)) 93 | queue = Queue() 94 | threadcount=10 95 | if workersHEC_Pack_size: 108 | #splunkHec(package); 109 | queue.put(package) 110 | message_counter=0; 111 | package='' 112 | 113 | if len(package)>0: 114 | #splunkHec(package); 115 | queue.put(package) 116 | 117 | # wait for the queue to finish processing all the tasks 118 | queue.join() 119 | 120 | 121 | class BuilderThreadWorker(Thread): 122 | 123 | def __init__(self, queue): 124 | Thread.__init__(self) 125 | self.queue = queue 126 | 127 | def run(self): 128 | while True: 129 | # Get the parameters from the queue and expand the queue 130 | metric, now, HECevents,payloadType = self.queue.get() 131 | try: 132 | MetricBuilder(metric, now, HECevents,payloadType) 133 | finally: 134 | self.queue.task_done() 135 | 136 | def MetricBuilder(metric,now,HECevents,payloadType): 137 | 138 | one_time_series = list_time_series(os.environ['PROJECTID'], metric, now, int(os.environ['TIME_INTERVAL'])) 139 | 140 | source=os.environ['PROJECTID']+':'+metric 141 | 142 | 143 | for data in one_time_series: 144 | 145 | pointsStrList=str(data.points) 146 | 147 | strdata=str(data) 148 | metricKindPart=get_metric_kind(strdata) 149 | valueTypePart=get_value_type(strdata) 150 | metricPart=str(data.metric) 151 | resourcePart=str(data.resource) 152 | 153 | pointsList=pullPointsList(pointsStrList) 154 | resourcePart=pull_labels(resourcePart,'"resource":{',1,1) 155 | metricPart=pull_labels(metricPart,'"metric":{',1,1) 156 | 157 | numPoints=len(pointsList)/3 158 | ix=0 159 | getevent='NULL' 160 | while ix0: #distribution value - need to extract key:value pairs for metrics index 225 | start_pt=in_str.find('"count"',start_pt) 226 | end_pt=in_str.find(',',start_pt) 227 | ret_string=in_str[start_pt:end_pt] 228 | start_pt=in_str.find('"exponentialBuckets":{',end_pt)+23 229 | end_pt=in_str.find('}},',start_pt)-2 230 | ret_string=ret_string+in_str[start_pt] 231 | else: 232 | ret_string='' 233 | return ret_string 234 | 235 | 236 | def list_time_series(project_id, metric_type, now_time, timelength): 237 | 238 | client = monitoring_v3.MetricServiceClient() 239 | project_name = client.project_path(project_id) 240 | interval = monitoring_v3.types.TimeInterval() 241 | now = now_time #time.time() 242 | interval.end_time.seconds = int(now) 243 | interval.end_time.nanos = int( 244 | (now - interval.end_time.seconds) * 10**9) 245 | interval.start_time.seconds = int(now - timelength*60) - 180 246 | interval.start_time.nanos = interval.end_time.nanos 247 | metric_string = 'metric.type = ' + '"'+metric_type+'"' 248 | 249 | results = client.list_time_series( 250 | project_name, 251 | metric_string, 252 | interval, 253 | monitoring_v3.enums.ListTimeSeriesRequest.TimeSeriesView.FULL) 254 | return results 255 | 256 | 257 | def uxtime(unixtime): 258 | return datetime.utcfromtimestamp(unixtime).strftime('%Y-%m-%dT%H:%M:%SZ') 259 | 260 | 261 | def pull_labels(in_string,initialstr,event,flag): 262 | #extract the labels from the payload, return json format 263 | returning=initialstr 264 | #get the type line 265 | start_type=in_string.find('type:')+5 266 | end_type=in_string.find('"',start_type+3)+1 267 | typestr=in_string[start_type:end_type] 268 | typestr='"type":'+typestr 269 | 270 | #get the key/value pairs from labels, cut them out of string 271 | cutstr=in_string 272 | start_k = cutstr.find('key:') + 4 273 | 274 | #make sure it has labels, if not, then only has type 275 | if (start_k>3): 276 | returning=returning+'"labels":{' 277 | typeonly=1 278 | 279 | while (start_k!=3): 280 | end_k = cutstr.find('value:')-1 281 | start_v = end_k+8 282 | end_v= cutstr.find('}')-1 283 | returning=returning+cutstr[start_k:end_k]+':'+cutstr[start_v:end_v] 284 | cutstr=cutstr[end_v+2:] 285 | start_k = cutstr.find('key:') + 4 286 | if start_k>3: #if more keys, then add comma, otherwise, end of labels 287 | returning=returning+',' 288 | else: 289 | returning=returning+'},' 290 | returning=returning+typestr+'},' 291 | 292 | return returning 293 | 294 | 295 | def str_type(type): 296 | #switch type from API value to equivalent used by Splunk GCP Add-On for compatability 297 | switcher={ 298 | 'bool_value:':'"boolValue"', 299 | 'int64_value:':'"int64Value"', 300 | 'double_value:':'"doubleValue"', 301 | 'string_value:':'"stringValue"', 302 | 'distribution_value':'"distributionValue"' 303 | } 304 | return switcher.get(type,'"TYPE_UNSPECIFIED"') 305 | 306 | def pullPointsList(in_str): 307 | #extract the points from the list of values returned by the API call. Return dict with json, _time, and metric value 308 | #in the case of distribution values, the value is taken from the mean value 309 | 310 | header='"points": [{"interval": {"endTime":' 311 | 312 | retarr={} 313 | count=0 314 | 315 | start_t = in_str.find('seconds:',1) + 8 316 | while start_t>7: 317 | end_t = in_str.find('}',start_t) 318 | strtime=in_str[start_t:end_t] 319 | nanos_t = strtime.find('nanos') 320 | if nanos_t>0: 321 | strtime=strtime[0:nanos_t] #some points have nanos. 322 | 323 | starttime=uxtime(int(strtime)) 324 | 325 | start_t2 = in_str.find('seconds:',end_t) + 8 326 | end_t2 = in_str.find('}',start_t2) 327 | endtimeNum = in_str[start_t2:end_t2-3] 328 | nanos_t = endtimeNum.find('nanos') 329 | if nanos_t>0: 330 | endtimeNum=endtimeNum[0:nanos_t] #some points have nanos. 331 | 332 | endtime=uxtime(int(endtimeNum)) 333 | 334 | start_vt = in_str.find('value {',start_t) + 10 335 | end_vt = in_str.find(' ',start_vt) 336 | valuet = str_type(in_str[start_vt:end_vt]) 337 | 338 | if valuet=='"distributionValue"': 339 | end_val = in_str.find('}\n}',end_vt) 340 | 341 | value='{' + getDistribution(in_str[end_vt+5:end_val-5]) 342 | retarr[count,0]=header + '"' + endtime+'",'+' "startTime": "' + starttime + '"},"value": {' + valuet + ':' + value + '}}}]' 343 | mean_st=value.find('"mean":')+7 344 | if mean_st<7: #some distributions return with empty datasets; we will ignore those later 345 | value='-' 346 | else: 347 | mean_end=value.find(',',mean_st)-1 348 | value=value[mean_st:mean_end] 349 | else: 350 | end_val = in_str.find('}',end_vt) -1 351 | value = in_str[end_vt+1:end_val] 352 | if value=='': 353 | value='0' 354 | retarr[count,0]=header + '"' + endtime+'",'+' "startTime": "' + starttime + '"},"value": {' 355 | retarr[count,0]=retarr[count,0] + valuet + ': "' + value + '"}}]' 356 | 357 | retarr[count,1]= endtimeNum 358 | retarr[count,2]= value 359 | count=count+1 360 | start_t = in_str.find('seconds:',end_val) + 8 361 | #end while 362 | 363 | return retarr 364 | 365 | def get_metric_kind(in_str): 366 | #pull out the metric Kind details, return in json format 367 | start_kind=in_str.find('metric_kind')+13 368 | end_kind=in_str.find('\n',start_kind) 369 | metricKind='"metricKind": "' + in_str[start_kind:end_kind] + '",' 370 | return metricKind 371 | 372 | def get_value_type(in_str): 373 | #pull out the value type and return in json format 374 | start_type=in_str.find('value_type')+12 375 | end_type=in_str.find('\n',start_type) 376 | valueType='"valueType": "' + in_str[start_type:end_type] + '"' 377 | return valueType 378 | 379 | def getDistribution(in_str): 380 | #for distribution values, need to re-format the payload into a json format compatible with the Splunk GCP Add-On 381 | 382 | in_str=in_str.replace('count:','"count":') 383 | in_str=in_str.replace(' mean:',',"mean":') 384 | in_str=in_str.replace(' sum_of_squared_deviation:',',"sumOfSquaredDeviation":') 385 | in_str=in_str.replace(' bucket_options ',',"bucketOptions":') 386 | in_str=in_str.replace('exponential_buckets','"exponentialBuckets":') 387 | in_str=in_str.replace('num_finite_buckets:','"numFiniteBuckets":') 388 | in_str=in_str.replace(' growth_factor:',',"growthFactor":') 389 | in_str=in_str.replace(' scale',',"scale"') 390 | 391 | first_bucket=in_str.find('bucket_counts')-1 392 | if first_bucket>0: 393 | buckets=in_str[first_bucket:] 394 | buckets=buckets.replace('bucket_counts:', '') 395 | bucketvals=re.sub("(\d)",r'"\1",',buckets) 396 | in_str=in_str[0:first_bucket-1]+', "bucketCounts":['+bucketvals+']' 397 | in_str=re.sub(",\s*]",']',in_str) #replace the last comma 398 | in_str=in_str.replace(' ','') 399 | 400 | return in_str 401 | 402 | 403 | class HECThreadWorker(Thread): 404 | def __init__(self, queue): 405 | Thread.__init__(self) 406 | self.queue = queue 407 | 408 | def run(self): 409 | while True: 410 | # Get the log from the queue 411 | logdata = self.queue.get() 412 | try: 413 | splunkHec(logdata) 414 | finally: 415 | self.queue.task_done() 416 | 417 | 418 | def splunkHec(logdata): 419 | #post to HEC 420 | url = 'https://'+os.environ['HEC_URL'] 421 | try: 422 | ix_type=os.environ['METRIC_INDEX_TYPE'] 423 | except: 424 | ix_type='EVENT' 425 | 426 | if ix_type=='EVENT': 427 | url=url+'/services/collector/event' 428 | else: 429 | url=url+'/services/collector' 430 | token = os.environ['HEC_TOKEN'] 431 | s = requests.Session() 432 | #s.config['keep_alive'] = False #HEC performance is improved by keepalive, but event distribution is affected. Setting to false provides better event distribution across indexers 433 | s.mount( 'http://' , HTTPAdapter(max_retries= 3 )) 434 | s.mount( 'https://' , HTTPAdapter(max_retries= 3 )) 435 | 436 | authHeader = {'Authorization': 'Splunk '+ token} 437 | 438 | try: 439 | r = s.post(url, headers=authHeader, data=logdata, verify=False, timeout=2) 440 | r.raise_for_status() 441 | except requests.exceptions.HTTPError as errh: 442 | print ("Http Error:",errh) 443 | print(errh.response.status_code) 444 | if errh.response.status_code<500: 445 | print(r.json()) 446 | errorHandler(logdata,url,token) 447 | except requests.exceptions.ConnectionError as errc: 448 | print ("Error Connecting:",errc) 449 | errorHandler(logdata,url,token) 450 | except requests.exceptions.Timeout as errt: 451 | print ("Timeout Error:",errt) 452 | errorHandler(logdata,url,token) 453 | except requests.exceptions.RequestException as err: 454 | print ("Error: ",err) 455 | errorHandler(logdata,url,token) 456 | except: 457 | print("unknown Error in http post >> message content:") 458 | print(logdata.replace('\n','')) 459 | errorHandler(logdata,url,token) 460 | 461 | 462 | 463 | 464 | def errorHandler(logdata,url,token): 465 | """Publishes failed messages to RETRY Pub/Sub topic.""" 466 | 467 | from google.cloud import pubsub_v1 468 | 469 | project_id = os.environ['PROJECTID'] 470 | topic_name = os.environ['RETRY_TOPIC'] 471 | publisher = pubsub_v1.PublisherClient() 472 | topic_path = publisher.topic_path(project_id, topic_name) 473 | 474 | 475 | data = logdata.encode('utf-8') 476 | future = publisher.publish(topic_path, data, url=url, token=token, source='MetricsFunction') 477 | print(future.result()) 478 | print('Published messages into PubSub') 479 | --------------------------------------------------------------------------------