add openhack files
This commit is contained in:
124
support/resources/KUSTO.md
Normal file
124
support/resources/KUSTO.md
Normal file
@ -0,0 +1,124 @@
|
||||
# Kusto tutorial with Log Analytics
|
||||
|
||||
This tutorial will guide you through the first steps with the Kusto query language in the context of the DevOps OpenHack.
|
||||
You create a graph that display how may trips have been completed by the simulator each half hour during the last 24 hours.
|
||||
|
||||
## Pre-requisites
|
||||
|
||||
In order to walk through this tutorial, it is expected that you have created and configured an Azure Log Analytics workspace to collect the logs from your AKS cluster.
|
||||
|
||||
## Your first query
|
||||
|
||||
Open the query editor in your analytics workspace by selecting **Logs** in the log analytics menu and enter the following code:
|
||||
|
||||
```kusto
|
||||
ContainerLog
|
||||
| search "simulator"
|
||||
```
|
||||
|
||||
Click the **Run** button.
|
||||
|
||||
This query uses **ContainerLog** as data source and searches for the entries that contain the word "simulator". The rows matching the criteria are returned.
|
||||
|
||||
Expand the first entry by clicking on the arrow on the left of the line. The key/value pairs will help understand the fields of each returned row.
|
||||
|
||||
Look more specifically at the following fields:
|
||||
- TimeGenerated\[UTC\]: This is the time at which the log entry has been generated
|
||||
- Image: This is the name of the container image that has generated the entry, copy the name of the image we will use it in the next step. It should be similar to this "openhackcj19acr.azurecr.io/devopsoh/simulator:latest"
|
||||
- LogEntry: The text that was written on the stdout of the container
|
||||
|
||||
## Filter the data
|
||||
|
||||
The command `where` allows to filter the data based on specific criteria like the generation time. The following query will apply a first filter on the data.
|
||||
|
||||
```kusto
|
||||
ContainerLog
|
||||
| where TimeGenerated > ago(24h)
|
||||
```
|
||||
|
||||
Let's now filter the content to only have the entries generated by the simulator container.
|
||||
Replace the query with the following and click **Run** :
|
||||
|
||||
```kusto
|
||||
ContainerLog
|
||||
| where TimeGenerated > ago(24h)
|
||||
| where Image contains "devopsoh/simulator:latest"
|
||||
```
|
||||
|
||||
When the simulator completes a trip an entry similar to the following is generated:
|
||||
```
|
||||
Trip Completed at : 11/01/2018 04:42:03.
|
||||
```
|
||||
|
||||
Let's update our query to select only those entries:
|
||||
|
||||
```kusto
|
||||
ContainerLog
|
||||
| where TimeGenerated > ago(24h)
|
||||
| where Image contains "devopsoh/simulator:latest"
|
||||
| where LogEntry contains "Trip Completed"
|
||||
```
|
||||
|
||||
Click **Run** to see the result.
|
||||
|
||||
The `where` clause also understands regular expressions(RE). REs can be used to extract a specific value from a log entry and write this value into an additional column using `extend`. More information on the use of `extend` is available on this page: https://docs.microsoft.com/en-us/azure/log-analytics/query-language/get-started-queries?toc=/azure/azure-monitor/toc.json#project-and-extend-select-and-compute-columns
|
||||
|
||||
## Reduce the number of columns
|
||||
|
||||
Update the query to get only the information that interest us: time of generation, the log entry and the containerId.
|
||||
|
||||
```kusto
|
||||
ContainerLog
|
||||
| where TimeGenerated > ago(24h)
|
||||
| where Image contains "devopsoh/simulator:latest"
|
||||
| where LogEntry contains "Trip Completed"
|
||||
| project TimeGenerated, LogEntry, ContainerID
|
||||
```
|
||||
|
||||
The `project` command will display only the comma seperated list of column names in the result of the query.
|
||||
|
||||
Click **Run** to see the result.
|
||||
|
||||
## Count the number of entries per time slot
|
||||
|
||||
We will use the `summarize` command to perform the aggregation according to the time of generation.
|
||||
The `bin` function will round a value to its bin size; `bin(TimeGenerated, 1m)` round the TimeGenerated to the minute.
|
||||
Our query becomes:
|
||||
|
||||
```kusto
|
||||
ContainerLog
|
||||
| where TimeGenerated > ago(24h)
|
||||
| where Image contains "devopsoh/simulator:latest"
|
||||
| where LogEntry contains "Trip Completed"
|
||||
| project TimeGenerated, LogEntry, ContainerID
|
||||
| summarize count(LogEntry) by bin(TimeGenerated, 30m)
|
||||
```
|
||||
|
||||
Click **Run** to see the result.
|
||||
|
||||
## A picture worth thousand words
|
||||
|
||||
To represent the data on a graph we will use the `render` function and define the format, in our case we want a `timechart`
|
||||
|
||||
```kusto
|
||||
ContainerLog
|
||||
| where TimeGenerated > ago(24h)
|
||||
| where Image contains "devopsoh/simulator:latest"
|
||||
| where LogEntry contains "Trip Completed"
|
||||
| project TimeGenerated, LogEntry, ContainerID
|
||||
| summarize count(LogEntry) by bin(TimeGenerated, 30m)
|
||||
| render timechart
|
||||
```
|
||||
|
||||
[Render operator](https://docs.microsoft.com/en-us/azure/kusto/query/renderoperator)
|
||||
[Creating Charts and Diagrams From Log Analytics](https://docs.microsoft.com/en-us/azure/log-analytics/query-language/charts?toc=/azure/azure-monitor/toc.json)
|
||||
|
||||
Click **Run** to render the graph.
|
||||
|
||||
## Reference documents
|
||||
|
||||
- Getting stated with log analytics portal: https://docs.microsoft.com/en-us/azure/log-analytics/query-language/get-started-analytics-portal
|
||||
- Getting started with query: https://docs.microsoft.com/en-us/azure/log-analytics/query-language/get-started-analytics-portal
|
||||
- String operations with Kusto: https://docs.microsoft.com/en-us/azure/log-analytics/query-language/string-operations
|
||||
- Search queries in Log Analytics: https://docs.microsoft.com/en-us/azure/log-analytics/query-language/search-queries
|
||||
- Add or select new column in a query: https://docs.microsoft.com/en-us/azure/log-analytics/query-language/get-started-queries?toc=/azure/azure-monitor/toc.json#project-and-extend-select-and-compute-columns
|
140
support/resources/MyDrivingTest.jmx
Normal file
140
support/resources/MyDrivingTest.jmx
Normal file
@ -0,0 +1,140 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.1 r1853635">
|
||||
<hashTree>
|
||||
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="MyDriving Load Test" enabled="true">
|
||||
<stringProp name="TestPlan.comments"></stringProp>
|
||||
<boolProp name="TestPlan.functional_mode">false</boolProp>
|
||||
<boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>
|
||||
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
|
||||
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||
<collectionProp name="Arguments.arguments"/>
|
||||
</elementProp>
|
||||
<stringProp name="TestPlan.user_define_classpath"></stringProp>
|
||||
</TestPlan>
|
||||
<hashTree>
|
||||
<Arguments guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||
<collectionProp name="Arguments.arguments">
|
||||
<elementProp name="UserJavaUri" elementType="Argument">
|
||||
<stringProp name="Argument.name">UserJavaUri</stringProp>
|
||||
<stringProp name="Argument.value">${__BeanShell(System.getenv("UserJavaUri"))}</stringProp>
|
||||
<stringProp name="Argument.metadata">=</stringProp>
|
||||
</elementProp>
|
||||
<elementProp name="UserProfileUri" elementType="Argument">
|
||||
<stringProp name="Argument.name">UserProfileUri</stringProp>
|
||||
<stringProp name="Argument.value">${__BeanShell(System.getenv("UserProfileUri"))}</stringProp>
|
||||
<stringProp name="Argument.metadata">=</stringProp>
|
||||
</elementProp>
|
||||
<elementProp name="TripsURI" elementType="Argument">
|
||||
<stringProp name="Argument.name">TripsURI</stringProp>
|
||||
<stringProp name="Argument.value">${__BeanShell(System.getenv("TripsURI"))}</stringProp>
|
||||
<stringProp name="Argument.metadata">=</stringProp>
|
||||
</elementProp>
|
||||
<elementProp name="PoiURI" elementType="Argument">
|
||||
<stringProp name="Argument.name">PoiURI</stringProp>
|
||||
<stringProp name="Argument.value">${__BeanShell(System.getenv("PoiURI"))}</stringProp>
|
||||
<stringProp name="Argument.metadata">=</stringProp>
|
||||
</elementProp>
|
||||
</collectionProp>
|
||||
</Arguments>
|
||||
<hashTree/>
|
||||
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
|
||||
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
|
||||
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
|
||||
<boolProp name="LoopController.continue_forever">false</boolProp>
|
||||
<intProp name="LoopController.loops">-1</intProp>
|
||||
</elementProp>
|
||||
<stringProp name="ThreadGroup.num_threads">5</stringProp>
|
||||
<stringProp name="ThreadGroup.ramp_time">10</stringProp>
|
||||
<boolProp name="ThreadGroup.scheduler">true</boolProp>
|
||||
<stringProp name="ThreadGroup.duration">120</stringProp>
|
||||
<stringProp name="ThreadGroup.delay">5</stringProp>
|
||||
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
|
||||
</ThreadGroup>
|
||||
<hashTree>
|
||||
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="POI Healthcheck" enabled="true">
|
||||
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||
<collectionProp name="Arguments.arguments">
|
||||
<elementProp name="" elementType="HTTPArgument">
|
||||
<boolProp name="HTTPArgument.always_encode">false</boolProp>
|
||||
<stringProp name="Argument.value"></stringProp>
|
||||
<stringProp name="Argument.metadata">=</stringProp>
|
||||
<boolProp name="HTTPArgument.use_equals">true</boolProp>
|
||||
</elementProp>
|
||||
</collectionProp>
|
||||
</elementProp>
|
||||
<stringProp name="HTTPSampler.domain">${PoiURI}</stringProp>
|
||||
<stringProp name="HTTPSampler.port"></stringProp>
|
||||
<stringProp name="HTTPSampler.protocol">https</stringProp>
|
||||
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||
<stringProp name="HTTPSampler.path">/api/healthcheck/poi/</stringProp>
|
||||
<stringProp name="HTTPSampler.method">GET</stringProp>
|
||||
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
|
||||
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
|
||||
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
|
||||
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
|
||||
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
||||
</HTTPSamplerProxy>
|
||||
<hashTree/>
|
||||
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Trips Healthcheck" enabled="true">
|
||||
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||
<collectionProp name="Arguments.arguments"/>
|
||||
</elementProp>
|
||||
<stringProp name="HTTPSampler.domain">${TripsURI}</stringProp>
|
||||
<stringProp name="HTTPSampler.port"></stringProp>
|
||||
<stringProp name="HTTPSampler.protocol">https</stringProp>
|
||||
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||
<stringProp name="HTTPSampler.path">/api/healthcheck/trips</stringProp>
|
||||
<stringProp name="HTTPSampler.method">GET</stringProp>
|
||||
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
|
||||
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
|
||||
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
|
||||
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
|
||||
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
||||
</HTTPSamplerProxy>
|
||||
<hashTree/>
|
||||
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="UserJava Healthcheck" enabled="true">
|
||||
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||
<collectionProp name="Arguments.arguments"/>
|
||||
</elementProp>
|
||||
<stringProp name="HTTPSampler.domain">${UserJavaUri}</stringProp>
|
||||
<stringProp name="HTTPSampler.port"></stringProp>
|
||||
<stringProp name="HTTPSampler.protocol">https</stringProp>
|
||||
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||
<stringProp name="HTTPSampler.path">/api/healthcheck/user-java</stringProp>
|
||||
<stringProp name="HTTPSampler.method">GET</stringProp>
|
||||
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
|
||||
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
|
||||
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
|
||||
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
|
||||
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
||||
</HTTPSamplerProxy>
|
||||
<hashTree/>
|
||||
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="UserProfile Healthcheck" enabled="true">
|
||||
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||
<collectionProp name="Arguments.arguments"/>
|
||||
</elementProp>
|
||||
<stringProp name="HTTPSampler.domain">${UserProfileUri}</stringProp>
|
||||
<stringProp name="HTTPSampler.port"></stringProp>
|
||||
<stringProp name="HTTPSampler.protocol">https</stringProp>
|
||||
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
|
||||
<stringProp name="HTTPSampler.path">/api/healthcheck/user</stringProp>
|
||||
<stringProp name="HTTPSampler.method">GET</stringProp>
|
||||
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
|
||||
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
|
||||
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
|
||||
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
|
||||
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
|
||||
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
|
||||
<stringProp name="HTTPSampler.response_timeout"></stringProp>
|
||||
</HTTPSamplerProxy>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
</hashTree>
|
||||
</hashTree>
|
||||
</jmeterTestPlan>
|
25
support/resources/generic_endpoint.sh
Normal file
25
support/resources/generic_endpoint.sh
Normal file
@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Set AZURE_DEVOPS_EXT_PAT with your PAT befor running this script
|
||||
# export export AZURE_DEVOPS_EXT_PAT="YourPat"
|
||||
|
||||
# ./generic_endpoint.sh AdoOrganization AdoProject SvcEndpointName SvcEndpointUrl
|
||||
|
||||
AZURE_DEVOPS_ORGANIZATION=$1 # $(System.CollectionUri)
|
||||
AZURE_DEVOPS_PROJECT=$2 # $(System.TeamProject)
|
||||
SVC_ENDPOINT_NAME=$3
|
||||
SVC_ENDPOINT_URL=$4
|
||||
|
||||
az devops configure --defaults organization="${AZURE_DEVOPS_ORGANIZATION}" project="${AZURE_DEVOPS_PROJECT}"
|
||||
|
||||
id=$(az devops service-endpoint list --query "[?name == '${SVC_ENDPOINT_NAME}'].id | join(', ', @)" --output tsv)
|
||||
if [[ ${#id} == 0 ]]; then
|
||||
payload=$(cat generic_endpoint_template.json)
|
||||
echo $payload | jq -c -r '.name = "'${SVC_ENDPOINT_NAME}'" | .url = "'${SVC_ENDPOINT_URL}'"' > _endpoint.temp.json
|
||||
id=$(az devops service-endpoint create --service-endpoint-configuration _endpoint.temp.json --output tsv --query id)
|
||||
rm -f _endpoint.temp.json
|
||||
az devops service-endpoint update --id ${id} --enable-for-all true > /dev/null
|
||||
echo "${SVC_ENDPOINT_NAME} service endpoint creadted! id: ${id}"
|
||||
else
|
||||
echo "${SVC_ENDPOINT_NAME} service endpoint already exists!"
|
||||
fi
|
22
support/resources/generic_endpoint_template.json
Normal file
22
support/resources/generic_endpoint_template.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"administratorsGroup": null,
|
||||
"authorization": {
|
||||
"scheme": "UsernamePassword",
|
||||
"parameters": {
|
||||
"username": "",
|
||||
"password": ""
|
||||
}
|
||||
},
|
||||
"createdBy": null,
|
||||
"data": {},
|
||||
"description": "",
|
||||
"groupScopeId": null,
|
||||
"name": "NAME",
|
||||
"operationStatus": null,
|
||||
"readersGroup": null,
|
||||
"serviceEndpointProjectReferences": [],
|
||||
"type": "generic",
|
||||
"url": "URL",
|
||||
"isShared": false,
|
||||
"owner": "library"
|
||||
}
|
59
support/resources/healthcheck_polling.sh
Normal file
59
support/resources/healthcheck_polling.sh
Normal file
@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
|
||||
declare -i duration=5
|
||||
declare endpoint
|
||||
declare key
|
||||
declare value
|
||||
|
||||
usage() {
|
||||
cat <<END
|
||||
healthcheck.sh endpoint key value
|
||||
|
||||
Report the health status of the endpoint. Exit 0 then OK.
|
||||
END
|
||||
}
|
||||
|
||||
if [[ $1 ]]; then
|
||||
endpoint=$1
|
||||
else
|
||||
echo "Please specify the endpoint to scan"
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $2 ]]; then
|
||||
key=$2
|
||||
else
|
||||
echo "Please specify the key that has to be queried"
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $3 ]]; then
|
||||
value=$3
|
||||
else
|
||||
echo "Please specify the value that has to be expected"
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
query() {
|
||||
local endpoint=$1
|
||||
local key=$2
|
||||
|
||||
result=$(curl --max-time 5 --silent --location "${endpoint}")
|
||||
|
||||
if jq -e . >/dev/null 2>&1 <<<"${result}"; then
|
||||
echo "${result}" | jq -c -r '.'"${key}"''
|
||||
fi
|
||||
}
|
||||
|
||||
while [[ true ]]; do
|
||||
result=$(query "${endpoint}" "${key}")
|
||||
|
||||
if [[ "${result}" == "${value}" ]]; then
|
||||
echo true
|
||||
exit 0
|
||||
fi
|
||||
sleep ${duration}
|
||||
done
|
64
support/resources/polling.sh
Normal file
64
support/resources/polling.sh
Normal file
@ -0,0 +1,64 @@
|
||||
#!/bin/bash
|
||||
|
||||
declare -i duration=1
|
||||
declare hasUrl=""
|
||||
declare endpoint
|
||||
|
||||
usage() {
|
||||
cat <<END
|
||||
polling.sh [-i] [-h] endpoint
|
||||
|
||||
Report the health status of the endpoint
|
||||
-i: include Uri for the format
|
||||
-h: help
|
||||
END
|
||||
}
|
||||
|
||||
while getopts "ih" opt; do
|
||||
case $opt in
|
||||
i)
|
||||
hasUrl=true
|
||||
;;
|
||||
h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
\?)
|
||||
echo "Unknown option: -${OPTARG}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
shift $((OPTIND -1))
|
||||
|
||||
if [[ $1 ]]; then
|
||||
endpoint=$1
|
||||
else
|
||||
echo "Please specify the endpoint."
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
healthcheck() {
|
||||
declare url=$1
|
||||
result=$(curl --silent --location --head --output /dev/null --write-out "%{http_code}" "${url}")
|
||||
echo $result
|
||||
}
|
||||
|
||||
while [[ true ]]; do
|
||||
result=`healthcheck ${endpoint}`
|
||||
declare status
|
||||
if [[ -z ${result} ]]; then
|
||||
status="N/A"
|
||||
else
|
||||
status=${result}
|
||||
fi
|
||||
timestamp=$(date "+%Y%m%d-%H%M%S")
|
||||
if [[ -z ${hasUrl} ]]; then
|
||||
echo "${timestamp} | ${status} "
|
||||
else
|
||||
echo "${timestamp} | ${status} | ${endpoint} "
|
||||
fi
|
||||
sleep ${duration}
|
||||
done
|
60
support/resources/send_msg.sh
Normal file
60
support/resources/send_msg.sh
Normal file
@ -0,0 +1,60 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
usage() { echo "Usage: send_msg -e <recipientEmail> -c <chatConnectionString> -q <chatMessageQueue> -m <message>" 1>&2; exit 1; }
|
||||
|
||||
declare recipientEmail=""
|
||||
declare chatConnectionString=""
|
||||
declare chatMessageQueue=""
|
||||
declare message=""
|
||||
|
||||
# Initialize parameters specified from command line
|
||||
while getopts ":e:c:q:m:" arg; do
|
||||
case "${arg}" in
|
||||
e)
|
||||
recipientEmail=${OPTARG}
|
||||
;;
|
||||
c)
|
||||
chatConnectionString=${OPTARG}
|
||||
;;
|
||||
q)
|
||||
chatMessageQueue=${OPTARG}
|
||||
;;
|
||||
m)
|
||||
message=${OPTARG}
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND-1))
|
||||
|
||||
if [[ -z "$recipientEmail" ]]; then
|
||||
echo "recipientEmail was not set properly"
|
||||
read recipientEmail
|
||||
fi
|
||||
|
||||
if [[ -z "$chatConnectionString" ]]; then
|
||||
echo "chatConnectionString was not set properly"
|
||||
read chatConnectionString
|
||||
fi
|
||||
|
||||
if [[ -z "$chatMessageQueue" ]]; then
|
||||
echo "chatMessageQueue was not set properly"
|
||||
read chatMessageQueue
|
||||
fi
|
||||
|
||||
if [[ -z "$message" ]]; then
|
||||
echo "message was not set properly"
|
||||
read message
|
||||
fi
|
||||
|
||||
cd /home/azureuser/openhack-devops-proctor/provision-team/svcbusclient
|
||||
|
||||
# Restore the ServiceBus Package
|
||||
dotnet add package Microsoft.Azure.ServiceBus --version 3.1.0
|
||||
|
||||
# build the servicebus client app
|
||||
dotnet build
|
||||
|
||||
# send the message to servicebus
|
||||
dotnet run $chatConnectionString $chatMessageQueue $recipientEmail $message
|
85
support/resources/service_check.sh
Normal file
85
support/resources/service_check.sh
Normal file
@ -0,0 +1,85 @@
|
||||
#!/bin/bash
|
||||
|
||||
# set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
|
||||
usage() { echo "Usage: service_check.sh -d <dns host Url> -n <teamName> " 1>&2; exit 1; }
|
||||
|
||||
declare dnsUrl=""
|
||||
declare teamName=""
|
||||
|
||||
# Initialize parameters specified from command line
|
||||
while getopts ":d:n:" arg; do
|
||||
case "${arg}" in
|
||||
d)
|
||||
dnsUrl=${OPTARG}
|
||||
;;
|
||||
n)
|
||||
teamName=${OPTARG}
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND-1))
|
||||
|
||||
#Prompt for parameters is some required parameters are missing
|
||||
if [[ -z "$dnsUrl" ]]; then
|
||||
echo "Public DNS address where the API will be hosted behind."
|
||||
echo "Enter public DNS name."
|
||||
read dnsUrl
|
||||
[[ "${dnsUrl:?}" ]]
|
||||
fi
|
||||
|
||||
if [[ -z "$teamName" ]]; then
|
||||
echo "Enter a team name to be used in app provisioning:"
|
||||
read teamName
|
||||
fi
|
||||
|
||||
echo "Checking services for ([X] = PASSED):"
|
||||
echo "Team Name:" $teamName
|
||||
echo -e "DNS Url:" $dnsUrl"\n"
|
||||
|
||||
poi_URL=$dnsUrl"/api/healthcheck/poi"
|
||||
user_URL=$dnsUrl"/api/healthcheck/user"
|
||||
trips_URL=$dnsUrl"/api/healthcheck/trips"
|
||||
user_java_URL=$dnsUrl"/api/healthcheck/user-java"
|
||||
|
||||
echo -e "Checking POI:\t"$poi_URL
|
||||
echo -e "Checking USER:\t"$user_URL
|
||||
echo -e "Checking TRIPS:\t"$trips_URL
|
||||
echo -e "Checking USER JAVA:\t"$user_java_URL"\n"
|
||||
|
||||
status_code_poi=`curl -sL -w "%{http_code}\\n" "$poi_URL" -o /dev/null`
|
||||
|
||||
if [[ "$status_code_poi" == "200" ]]; then
|
||||
echo "poi [X]"
|
||||
else
|
||||
echo "poi [ ]"
|
||||
fi
|
||||
|
||||
status_code_user=`curl -sL -w "%{http_code}\\n" "$user_URL" -o /dev/null`
|
||||
|
||||
if [[ "$status_code_user" == "200" ]]; then
|
||||
echo "user [X]"
|
||||
else
|
||||
echo "user [ ]"
|
||||
fi
|
||||
|
||||
status_code_trips=`curl -sL -w "%{http_code}\\n" "$trips_URL" -o /dev/null`
|
||||
|
||||
if [[ "$status_code_trips" == "200" ]]; then
|
||||
echo "trips [X]"
|
||||
else
|
||||
echo "trips [ ]"
|
||||
fi
|
||||
|
||||
status_code_user_java=`curl -sL -w "%{http_code}\\n" "$user_java_URL" -o /dev/null`
|
||||
|
||||
if [[ "$status_code_user_java" == "200" ]]; then
|
||||
echo "user-java [X]"
|
||||
else
|
||||
echo "user-java [ ]"
|
||||
fi
|
||||
|
||||
if [[ "$status_code_poi" == "200" ]] && [[ "$status_code_user" == "200" ]] && [[ "$status_code_trips" == "200" ]] && [[ "$status_code_user_java" == "200" ]]; then
|
||||
echo "All checks passed"
|
||||
fi
|
129
support/resources/validate-deployment.ps1
Normal file
129
support/resources/validate-deployment.ps1
Normal file
@ -0,0 +1,129 @@
|
||||
<#
|
||||
|
||||
.SYNOPSIS
|
||||
This script can be used to check the status of a classroom after is has been started in the Cloud Sandbox.
|
||||
|
||||
This script has specficially been authored to check the lab microsoft-open-hack-devops and will not currently validate any other OpenHack labs.
|
||||
|
||||
.DESCRIPTION
|
||||
To use this script, you will need to navigate to a classroom in the Cloud Sandbox and enter the lab view.
|
||||
|
||||
From the lab view, click the List Credentials button, and then export the CSV.
|
||||
|
||||
This script will take the path of that script as in input and use the credentials to enumerate all of the subscriptions within it.
|
||||
|
||||
.EXAMPLE
|
||||
./validate-deployment.ps1 -LabCredentialsFilePath $env:HOMEPATH\Downloads\credentials.csv
|
||||
|
||||
.NOTES
|
||||
This script should only be run at one hour after you have initiated the lab. Running it prior to that will certainly lead to results which lead you to believe the lab has not provisioned successfully, when in fact it is probably just still spinning up.
|
||||
|
||||
.LINK
|
||||
https://github.com/Azure-Samples/openhack-devops-proctor/
|
||||
|
||||
#>
|
||||
|
||||
Param (
|
||||
[Parameter(Mandatory=$false)]
|
||||
[String]
|
||||
$LabCredentialsFilePath = "credentials.csv",
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[String]
|
||||
$OutputFilePath = "classroom_checkresults.csv",
|
||||
|
||||
[switch]
|
||||
$Force
|
||||
)
|
||||
|
||||
if (-Not (Test-Path $LabCredentialsFilePath -PathType Leaf)) {
|
||||
Write-Error -Message "Unable to find CSV at the path provided." -Category InvalidData
|
||||
}
|
||||
|
||||
$InputFile = @(Import-Csv -Path $LabCredentialsFilePath -Header "PortalUsername","PortalPassword","AzureSubscriptionId","AzureDisplayName","AzureUsername","AzurePassword" | Where-Object AzureUserName -like "hacker*" | Sort-Object AzureSubscriptionId -Unique)
|
||||
|
||||
if (Test-Path $OutputFilePath -PathType Leaf) {
|
||||
if ($Force) {
|
||||
Remove-Item -Path $OutputFilePath
|
||||
} else {
|
||||
$_ = Read-Host "Found previous output. Would you like to delete it? (y/n)?"
|
||||
|
||||
if ($_.ToLower() -eq "y") {
|
||||
Remove-Item -Path $OutputFilePath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Storing validation results at $OutputFilePath" -ForegroundColor Green
|
||||
|
||||
Add-Content -Path $OutputFilePath -Value '"SiteFound","POIFound","TripsFound","UserFound","UserJavaFound","TripViewerUrl","AzureUsername","AzurePassword","SubscriptionId","TenantURL"'
|
||||
|
||||
for ($i = 0; $i -lt $InputFile.Count; $i++) {
|
||||
$_ = $InputFile[$i]
|
||||
|
||||
if ($_.AzureUsername -eq "Azure UserName" -and $_.AzurePassword -eq "Azure Password") {
|
||||
continue;
|
||||
}
|
||||
|
||||
$PortalUsername = $_.PortalUsername
|
||||
$PortalPassword = $_.PortalPassword
|
||||
$AzureUsername = $_.AzureUsername
|
||||
$AzurePassword = $_.AzurePassword
|
||||
$AzureSubscriptionId = $_.AzureSubscriptionId
|
||||
$AzureDisplayName = $_.AzureDisplayName
|
||||
|
||||
$AzureSecurePassword = ConvertTo-SecureString $AzurePassword -AsPlainText -Force
|
||||
$Credential = New-Object System.Management.Automation.PSCredential ($AzureUsername, $AzureSecurePassword)
|
||||
$TenantDomain = $AzureUsername.Split("@")[1]
|
||||
$TenantUrl = "https://portal.azure.com/$TenantDomain"
|
||||
|
||||
Write-Host "Processing record for $AzureUsername" -ForegroundColor Yellow
|
||||
|
||||
$Account = Connect-AzAccount -Credential $Credential -Subscription $AzureSubscriptionId
|
||||
|
||||
$ResourceGroup = Get-AzResourceGroup | Where-Object ResourceGroupName -like "openhack*" | Select-Object -first 1
|
||||
$ResourceGroupName = $ResourceGroup.ResourceGroupName
|
||||
$TeamName = $ResourceGroupName -Replace ".{2}$"
|
||||
|
||||
$RowToAppend = '"True",'
|
||||
|
||||
$_poi = Get-AzWebApp -ResourceGroupName $ResourceGroupName | Where-Object { $_.Name -eq "$($TeamName)poi" }
|
||||
$_trips = Get-AzWebApp -ResourceGroupName $ResourceGroupName | Where-Object { $_.Name -eq "$($TeamName)trips" }
|
||||
$_userprofile = Get-AzWebApp -ResourceGroupName $ResourceGroupName | Where-Object { $_.Name -eq "$($TeamName)userprofile" }
|
||||
$_userjava = Get-AzWebApp -ResourceGroupName $ResourceGroupName | Where-Object { $_.Name -eq "$($TeamName)userjava" }
|
||||
$_tripviewer = Get-AzWebApp -ResourceGroupName $ResourceGroupName | Where-Object { $_.Name -eq "$($TeamName)tripviewer" }
|
||||
|
||||
$_status = Invoke-WebRequest "http://$($_poi.DefaultHostName)/api/healthcheck/poi" | % {$_.StatusCode}
|
||||
if ($_status -eq 200) {
|
||||
$RowToAppend += '"True",'
|
||||
} else {
|
||||
$RowToAppend += '"False",'
|
||||
}
|
||||
|
||||
$_status = Invoke-WebRequest "http://$($_trips.DefaultHostName)/api/healthcheck/trips" | % {$_.StatusCode}
|
||||
if ($_status -eq 200) {
|
||||
$RowToAppend += '"True",'
|
||||
} else {
|
||||
$RowToAppend += '"False",'
|
||||
}
|
||||
|
||||
$_status = Invoke-WebRequest "http://$($_userprofile.DefaultHostName)/api/healthcheck/user" | % {$_.StatusCode}
|
||||
if ($_status -eq 200) {
|
||||
$RowToAppend += '"True",'
|
||||
} else {
|
||||
$RowToAppend += '"False",'
|
||||
}
|
||||
|
||||
$_status = Invoke-WebRequest "http://$($_userjava.DefaultHostName)/api/healthcheck/user-java" | % {$_.StatusCode}
|
||||
if ($_status -eq 200) {
|
||||
$RowToAppend += '"True",'
|
||||
} else {
|
||||
$RowToAppend += '"False",'
|
||||
}
|
||||
|
||||
$RowToAppend += "`"http://$($_tripviewer.DefaultHostName)`",`"$PortalUsername`",`"$PortalPassword`",`"$AzureSubscriptionId`",`"$TenantUrl`""
|
||||
|
||||
Add-Content -Path $OutputFilePath -Value $RowToAppend
|
||||
|
||||
Write-Host "Done for $AzureUsername"
|
||||
}
|
112
support/resources/validate-deployment.sh
Normal file
112
support/resources/validate-deployment.sh
Normal file
@ -0,0 +1,112 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script verifies the successfull deployment of the resources needed for the DevOps OpenHack.
|
||||
# You need to provide the CSV file with all the credentials of the Azure subscriptions from the classroom management portal and a private / public SSH keypair that will be used to access the provisioning VMs
|
||||
# The error log file is where will be logged the informations regarding the failed deployments. If not provided, it defaults to error.log.
|
||||
#
|
||||
# EXAMPLE
|
||||
# ./validate-deployment.sh ./credentials.csv
|
||||
# ./validate-deployment.sh ./credentials.csv --force
|
||||
|
||||
OLD_IFS=$IFS
|
||||
CREDENTIALS_FILE_PATH=$1
|
||||
FORCE_OVERWRITE=$2
|
||||
RESULTS_FILE_PATH="./classcheckresults.csv"
|
||||
IFS=','
|
||||
|
||||
[ ! -f $CREDENTIALS_FILE_PATH ] && { echo "$CREDENTIALS_FILE_PATH file not found"; exit 99; }
|
||||
|
||||
[ -f $RESULTS_FILE_PATH ] && {
|
||||
[ -z $FORCE_OVERWRITE ] && {
|
||||
echo "Found previous output ($RESULTS_FILE_PATH). Would you like to delete it?"
|
||||
select yn in "Yes" "No"; do
|
||||
case $yn in
|
||||
Yes ) rm $RESULTS_FILE_PATH; break;;
|
||||
No ) break;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
[ ! -z $FORCE_OVERWRITE ] && {
|
||||
rm $RESULTS_FILE_PATH
|
||||
}
|
||||
}
|
||||
|
||||
echo "Storing validation results at $RESULTS_FILE_PATH"
|
||||
|
||||
echo '"SiteFound","POIFound","TripsFound","UserFound","UserJavaFound","TripViewerUrl","AzureUsername","AzurePassword","SubscriptionId","TenantURL"' >> $RESULTS_FILE_PATH
|
||||
|
||||
while read PortalUsername PortalPassword AzureSubscriptionId AzureDisplayName AzureUsername AzurePassword
|
||||
do
|
||||
if [[ $PortalUsername = *Portal* ]]
|
||||
then
|
||||
echo "This is the header, skipping..."
|
||||
elif [[ $AzureUserName = *hacker* ]]
|
||||
then
|
||||
echo "This is a hacker user, skipping..."
|
||||
else
|
||||
echo "PortalUsername $PortalUsername"
|
||||
echo "PortalPassword $PortalPassword"
|
||||
echo "AzureSubscriptionId $AzureSubscriptionId"
|
||||
echo "AzureDisplayName $AzureDisplayName"
|
||||
echo "AzureUsername $AzureUsername"
|
||||
echo "AzurePassword $AzurePassword"
|
||||
|
||||
az login -u $AzureUsername -p $AzurePassword --output none
|
||||
|
||||
TENANT_URL="https://portal.azure.com/"
|
||||
TENANT_URL+=`echo "$AzureUsername" | awk -F"@" '{print $2}'`
|
||||
|
||||
RESOURCE_GROUP_NAME=`az group list --query "[?starts_with(name,'openhack')]|[0]" | jq -r '.name'`
|
||||
|
||||
TEAM_NAME=${RESOURCE_GROUP_NAME%??}
|
||||
|
||||
ROW_TO_APPEND="\"True\","
|
||||
|
||||
FQDN_POI=`az webapp show --resource-group "$RESOURCE_GROUP_NAME" --name "${TEAM_NAME}poi" --query "[].{hostName:defaultHostName}|[0]" --output tsv`
|
||||
FQDN_TRIPS=`az webapp list --resource-group "$RESOURCE_GROUP_NAME" --name "${TEAM_NAME}trips" --query "[].{hostName:defaultHostName}|[0]" --output tsv`
|
||||
FQDN_USER_JAVA=`az webapp list --resource-group "$RESOURCE_GROUP_NAME" --name "${TEAM_NAME}userjava" --query "[].{hostName:defaultHostName}|[0]" --output tsv`
|
||||
FQDN_USER_PROFILE=`az webapp list --resource-group "$RESOURCE_GROUP_NAME" --name "${TEAM_NAME}userprofile" --query "[].{hostName:defaultHostName}|[0]" --output tsv`
|
||||
|
||||
FQDN_TRIP_VIEWER=`az webapp list --resource-group "$RESOURCE_GROUP_NAME" --name "${TEAM_NAME}tripviewer" --query "[].{hostName:defaultHostName}|[0]" --output tsv`
|
||||
|
||||
STATUS=$(curl --write-out %{http_code} --silent --output /dev/null $FQDN_POI/api/healthcheck/poi)
|
||||
if [ $STATUS -eq 200 ]
|
||||
then
|
||||
ROW_TO_APPEND+="\"True\","
|
||||
else
|
||||
ROW_TO_APPEND+="\"False\","
|
||||
fi
|
||||
|
||||
STATUS=$(curl --write-out %{http_code} --silent --output /dev/null $FQDN_TRIPS/api/healthcheck/trips)
|
||||
if [ $STATUS -eq 200 ]
|
||||
then
|
||||
ROW_TO_APPEND+="\"True\","
|
||||
else
|
||||
ROW_TO_APPEND+="\"False\","
|
||||
fi
|
||||
|
||||
STATUS=$(curl --write-out %{http_code} --silent --output /dev/null $FQDN_USER_PROFILE/api/healthcheck/user)
|
||||
if [ $STATUS -eq 200 ]
|
||||
then
|
||||
ROW_TO_APPEND+="\"True\","
|
||||
else
|
||||
ROW_TO_APPEND+="\"False\","
|
||||
fi
|
||||
|
||||
STATUS=$(curl --write-out %{http_code} --silent --output /dev/null $FQDN_USER_JAVA/api/healthcheck/user-java)
|
||||
if [ $STATUS -eq 200 ]
|
||||
then
|
||||
ROW_TO_APPEND+="\"True\","
|
||||
else
|
||||
ROW_TO_APPEND+="\"False\","
|
||||
fi
|
||||
|
||||
ROW_TO_APPEND+="\"$FQDN_TRIP_VIEWER\",\"$PortalUsername\",\"$PortalPassword\",\"$AzureSubscriptionId\",\"$TENANT_URL\""
|
||||
|
||||
echo $ROW_TO_APPEND >> $RESULTS_FILE_PATH
|
||||
|
||||
echo "Done for $AzureUsername"
|
||||
fi
|
||||
|
||||
done < $CREDENTIALS_FILE_PATH
|
||||
IFS=$OLD_IFS
|
Reference in New Issue
Block a user