Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Bram Daams
sch
Commits
df2281a8
Commit
df2281a8
authored
Jan 20, 2020
by
Bram Daams
Browse files
geen registry, maar hashes en id's in tags van hc opslaan
parent
7ee5ed0a
Pipeline
#36608
passed with stage
in 51 seconds
Changes
3
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
README.md
View file @
df2281a8
...
...
@@ -15,20 +15,17 @@ For now, create a file `test.py`
from
hc
import
hc
,
hcCred
,
hcRegistry
cred
=
hcCred
(
'https://hc.example.com/api/v1/'
,
'mysecretapikey'
)
registry
=
hcRegistry
(
cred
,
'doc/hcregistry.json'
)
h
=
hc
(
cred
)
h
.
print_status
()
h
=
Healthchecks
(
cred
)
h
.
PrintStatus
()
# scan jobs that want to use SCH
jobs
=
CronTabs
().
all
.
find_command
(
'JOB_ID'
)
for
job
in
jobs
:
print
(
"
job
:"
)
print
(
job
)
hc_id
=
registry
.
get_id
(
job
)
print
(
hc_id
)
print
(
"--------------"
)
check
=
h
.
FindCheck
(
job
)
if
check
:
h
.
UpdateCheck
(
check
,
job
)
else
:
h
.
NewCheck
(
job
)
```
And run it within the virtual environment
...
...
hc.py
View file @
df2281a8
"""
module for interfacing a healthchecks.io compatible service
"""
import
sys
import
click
import
arrow
import
json
import
hashlib
import
platform
import
re
...
...
@@ -10,6 +12,9 @@ import tzlocal
class
hcCred
:
"""
Object to store the healthchecks api url and key
"""
def
__init__
(
self
,
url
,
api_key
):
self
.
url
=
url
self
.
api_key
=
api_key
...
...
@@ -19,110 +24,163 @@ class hcCred:
to {url}"""
.
format
(
url
=
self
.
url
)
class
hcRegistry
:
def
__init__
(
self
,
cred
,
registry
):
self
.
registry
=
registry
self
.
hc
=
hc
(
cred
)
# read file
try
:
with
open
(
self
.
registry
,
'r'
)
as
myfile
:
data
=
myfile
.
read
()
class
Healthchecks
:
def
__init__
(
self
,
cred
):
self
.
cred
=
cred
self
.
auth_headers
=
{
'X-Api-Key'
:
self
.
cred
.
api_key
}
self
.
checks
=
self
.
get_checks
()
# parse file
self
.
data
=
json
.
loads
(
data
)
except
Exception
:
print
(
"could not load registry"
)
def
get_checks
(
self
):
"""Returns a list of checks from the HC API"""
url
=
"{}checks/"
.
format
(
self
.
cred
.
url
)
def
get_hash
(
self
,
job
):
"""Returns the unique hash for given cron job"""
md5
=
hashlib
.
md5
()
md5
.
update
(
platform
.
node
().
encode
(
'utf-8'
))
md5
.
update
(
str
(
job
.
slices
).
encode
(
'utf-8'
))
md5
.
update
(
job
.
command
.
encode
(
'utf-8'
))
return
md5
.
hexdigest
()
try
:
response
=
requests
.
get
(
url
,
headers
=
self
.
auth_headers
)
response
.
raise_for_status
()
except
requests
.
exceptions
.
HTTPError
as
err
:
print
(
err
)
sys
.
exit
(
1
)
if
response
:
return
response
.
json
()[
'checks'
]
def
get_jobid
(
self
,
job
):
"""Returns the value of environment variable JOB_ID if specified in the cron job"""
regex
=
r
".*JOB_ID=(\w*)"
match
=
re
.
match
(
regex
,
job
.
command
)
if
match
:
return
match
.
group
(
1
)
raise
Exception
(
'fetching cron checks failed'
)
def
get_tags
(
self
,
job
):
"""Returns the tags specified in the environment variable JOB_TAGS in the cron job"""
def
FindCheck
(
self
,
job
):
"""
Find a check in Healthchecks for the host and given job
"""
job_id
=
self
.
GetJobId
(
job
)
tag_for_job
=
'job_id={job_id}'
.
format
(
job_id
=
job_id
)
tag_for_host
=
'host={hostname}'
.
format
(
hostname
=
platform
.
node
())
# see if there's a check with tags matching both this host
# and the job_id
for
check
in
self
.
checks
:
found_job
=
False
found_host
=
False
for
tag
in
check
[
'tags'
].
split
(
' '
):
if
tag
==
tag_for_job
:
found_job
=
True
elif
tag
==
tag_for_host
:
found_host
=
True
if
found_job
and
found_host
:
return
check
return
None
def
GetJobTags
(
self
,
job
):
"""
Returns the tags specified in the environment variable
JOB_TAGS in the cron job
"""
regex
=
r
'.*JOB_TAGS=([\w,]*)'
m
=
re
.
match
(
regex
,
job
.
command
)
if
m
:
return
m
.
group
(
1
).
replace
(
','
,
' '
)
return
""
def
find_by_hash
(
self
,
job
):
"""Find a job in the registry by hash"""
h
=
self
.
get_hash
(
job
)
return
next
((
elem
for
elem
in
self
.
data
if
elem
[
'hash'
]
==
h
),
False
)
def
GetJobId
(
self
,
job
):
"""
Returns the value of environment variable JOB_ID if specified
in the cron job
"""
regex
=
r
".*JOB_ID=(\w*)"
match
=
re
.
match
(
regex
,
job
.
command
)
if
match
:
return
match
.
group
(
1
)
def
find_by_jobid
(
self
,
job
):
"""Find a job in the registry by job_id"""
j
=
self
.
get_jobid
(
job
)
return
next
((
elem
for
elem
in
self
.
data
if
elem
[
'JOB_ID'
]
==
j
),
False
)
return
None
def
get_id
(
self
,
job
):
"""Get the HC id for the given cron job"""
r
=
self
.
find_by_hash
(
job
)
if
r
:
return
r
[
'HC_ID'
]
def
GenerateJobHash
(
self
,
job
):
"""Returns the unique hash for given cron job"""
md5
=
hashlib
.
md5
()
md5
.
update
(
platform
.
node
().
encode
(
'utf-8'
))
md5
.
update
(
str
(
job
.
slices
).
encode
(
'utf-8'
))
md5
.
update
(
job
.
command
.
encode
(
'utf-8'
))
return
md5
.
hexdigest
()
r
=
self
.
find_by_jobid
(
job
)
if
r
:
# hash has changed, let's update the details
self
.
hc
.
update_check
(
r
,
job
,
self
.
get_tags
(
job
))
return
r
[
'HC_ID'
]
def
GetCheckHash
(
self
,
check
):
regex
=
r
"hash=(\w*)"
hash_search
=
re
.
search
(
regex
,
check
[
'tags'
])
return
False
if
hash_search
:
return
hash_search
.
group
(
1
)
def
register
(
self
,
id
):
pass
return
None
def
UpdateCheck
(
self
,
check
,
job
):
job_hash
=
self
.
GenerateJobHash
(
job
)
check_hash
=
self
.
GetCheckHash
(
check
)
class
hc
:
def
__init__
(
self
,
cred
)
:
self
.
cred
=
cred
self
.
auth_headers
=
{
'X-Api-Key'
:
self
.
cred
.
api_key
}
if
check_hash
:
if
job_hash
==
check_hash
:
# hash did not change: no need to update checks' details
return
True
def
get_checks
(
self
):
"""Returns a list of checks from the HC API"""
url
=
"{}checks/"
.
format
(
self
.
cred
.
url
)
# let's really update the check
print
(
"about to update check:"
,
check
)
url
=
check
[
'update_url'
]
# gather all the jobs' metadata
data
=
{
'schedule'
:
job
.
slices
.
render
(),
'desc'
:
job
.
comment
,
'grace'
:
3600
,
'tz'
:
tzlocal
.
get_localzone
().
zone
,
'tags'
:
'sch host={host} job_id={job_id} '
'hash={hash} {tags}'
.
format
(
host
=
platform
.
node
(),
job_id
=
self
.
GetJobId
(
job
),
hash
=
job_hash
,
tags
=
self
.
GetJobTags
(
job
)
)
}
# post the data
try
:
response
=
requests
.
get
(
url
,
headers
=
self
.
auth_headers
)
response
=
requests
.
post
(
url
=
url
,
headers
=
self
.
auth_headers
,
json
=
data
)
response
.
raise_for_status
()
except
requests
.
exceptions
.
HTTPError
as
err
:
print
(
"ERROR"
)
print
(
err
)
sys
.
exit
(
1
)
if
response
:
return
response
.
json
()[
'checks'
]
return
False
r
aise
Exception
(
'fetching cron checks failed'
)
r
eturn
True
def
update_check
(
self
,
registration
,
job
,
tags
):
url
=
"{apiurl}checks/{code}"
.
format
(
apiurl
=
self
.
cred
.
url
,
code
=
registration
[
'HC_ID'
]
)
def
NewCheck
(
self
,
job
):
job_hash
=
self
.
GenerateJobHash
(
job
)
# gather all the jobs' metadata
data
=
{
'schedule'
:
job
.
slices
.
render
(),
'desc'
:
job
.
comment
,
'grace'
:
3600
,
'tz'
:
tzlocal
.
get_localzone
().
zone
,
'tags'
:
'sch host_{} {}'
.
format
(
platform
.
node
(),
tags
)
}
'name'
:
'new check'
,
'schedule'
:
job
.
slices
.
render
(),
'desc'
:
job
.
comment
,
'grace'
:
3600
,
'channels'
:
'*'
,
# all available notification channels
'tz'
:
tzlocal
.
get_localzone
().
zone
,
'tags'
:
'sch host={host} job_id={job_id} '
'hash={hash} {tags}'
.
format
(
host
=
platform
.
node
(),
job_id
=
self
.
GetJobId
(
job
),
hash
=
job_hash
,
tags
=
self
.
GetJobTags
(
job
)
)
}
# post the data
try
:
response
=
requests
.
post
(
url
=
url
,
headers
=
self
.
auth_headers
,
json
=
data
)
url
=
'{}/checks/'
.
format
(
self
.
cred
.
url
)
,
headers
=
self
.
auth_headers
,
json
=
data
)
response
.
raise_for_status
()
except
requests
.
exceptions
.
HTTPError
as
err
:
...
...
@@ -130,11 +188,11 @@ class hc:
print
(
err
)
return
False
print
(
'check created'
)
return
True
def
p
rint
_s
tatus
(
self
,
status_filter
=
""
):
def
P
rint
S
tatus
(
self
,
status_filter
=
""
):
"""Show status of monitored cron jobs"""
checks
=
self
.
get_checks
()
click
.
secho
(
"{status:<6} {last_ping:<15} {name:<40}"
.
format
(
status
=
"Status"
,
name
=
"Name"
,
...
...
@@ -146,7 +204,7 @@ class hc:
last_ping
=
""
))
for
i
in
checks
:
for
i
in
self
.
checks
:
if
status_filter
and
i
[
'status'
]
!=
status_filter
:
continue
...
...
setup.py
View file @
df2281a8
...
...
@@ -9,5 +9,6 @@ setup(
'click'
,
'python-crontab'
,
'requests'
,
'tzlocal'
,
],
)
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment