Merge branch 'jenkins-build-21' into 'master'
Auto-merge for build 21 See merge request puppet/puppet_cd!18
This commit is contained in:
@@ -1670,17 +1670,7 @@
|
||||
238
|
||||
239
|
||||
240
|
||||
241
|
||||
242
|
||||
243
|
||||
244
|
||||
245
|
||||
246
|
||||
247
|
||||
248
|
||||
249
|
||||
250
|
||||
251</pre>
|
||||
241</pre>
|
||||
</td>
|
||||
<td>
|
||||
<pre class="code"><span class="info file"># File 'manifests/params.pp', line 88</span>
|
||||
@@ -1801,9 +1791,6 @@ class puppet_cd::params (
|
||||
$pt_puppetdb_ssl = "${pt_puppetdb_main}/ssl"
|
||||
$pt_puppetdb_log = '/var/log/puppetlabs/puppetdb'
|
||||
$pt_puppetdb_var_dir = '/opt/puppetlabs/server/data/puppetdb'
|
||||
## r10k
|
||||
$pt_r10k_dir = "${pt_main_dir}/r10k"
|
||||
$pt_r10k_webhook_dir = '/etc/r10k-webhook'
|
||||
|
||||
# files
|
||||
## puppet
|
||||
@@ -1830,14 +1817,7 @@ class puppet_cd::params (
|
||||
$pt_puppetdb_repl_ini = "${pt_puppetdb_conf_d}/repl.ini"
|
||||
$pt_puppetdb_repl_erb = 'puppet_cd/puppetdb/repl.ini.erb'
|
||||
## r10k
|
||||
$pt_r10k_file = "${pt_r10k_dir}/r10k.yaml"
|
||||
$pt_r10k_erb = 'puppet_cd/r10k/r10k.yaml.erb'
|
||||
$pt_r10k_webhook_file = "${pt_r10k_webhook_dir}/webhook_server.py"
|
||||
$pt_r10k_webhook_erb = 'puppet_cd/r10k/webhook.py.erb'
|
||||
$pt_r10k_req_file = "${pt_r10k_webhook_dir}/requirements.txt"
|
||||
$pt_r10k_req_erb = 'puppet_cd/r10k/requirements.txt.erb'
|
||||
$pt_r10k_wh_config_file = "${pt_r10k_webhook_dir}/config.json"
|
||||
$pt_r10k_wh_config_erb = 'puppet_cd/r10k/r10k_webhook_config.erb'
|
||||
$pt_r10k_hook_file = '/usr/local/bin/webhook'
|
||||
|
||||
# service
|
||||
$pt_server_service = 'puppetserver'
|
||||
|
||||
@@ -125,55 +125,7 @@
|
||||
27
|
||||
28
|
||||
29
|
||||
30
|
||||
31
|
||||
32
|
||||
33
|
||||
34
|
||||
35
|
||||
36
|
||||
37
|
||||
38
|
||||
39
|
||||
40
|
||||
41
|
||||
42
|
||||
43
|
||||
44
|
||||
45
|
||||
46
|
||||
47
|
||||
48
|
||||
49
|
||||
50
|
||||
51
|
||||
52
|
||||
53
|
||||
54
|
||||
55
|
||||
56
|
||||
57
|
||||
58
|
||||
59
|
||||
60
|
||||
61
|
||||
62
|
||||
63
|
||||
64
|
||||
65
|
||||
66
|
||||
67
|
||||
68
|
||||
69
|
||||
70
|
||||
71
|
||||
72
|
||||
73
|
||||
74
|
||||
75
|
||||
76
|
||||
77
|
||||
78</pre>
|
||||
30</pre>
|
||||
</td>
|
||||
<td>
|
||||
<pre class="code"><span class="info file"># File 'manifests/r10k/webhook.pp', line 6</span>
|
||||
@@ -182,73 +134,25 @@ class puppet_cd::r10k::webhook (
|
||||
|
||||
) inherits puppet_cd::params {
|
||||
if ($pt_pm_fqdn == $fqdn) and ($pt_use_r10k_webhook == true) {
|
||||
# install packages
|
||||
package { $pt_r10k_webhook_pkg:
|
||||
ensure => $pt_pkg_ensure,
|
||||
}
|
||||
|
||||
# create the webhook dir
|
||||
file { $pt_r10k_webhook_dir:
|
||||
ensure => directory,
|
||||
# create the webhook binary
|
||||
file { $pt_r10k_hook_file:
|
||||
ensure => file,
|
||||
owner => 'root',
|
||||
group => 'root',
|
||||
mode => '0755',
|
||||
selrange => s0,
|
||||
selrole => object_r,
|
||||
seltype => etc_t,
|
||||
seluser => system_u,
|
||||
seltype => bin_t,
|
||||
seluser => unconfined_u,
|
||||
source => 'puppet:///module/puppet_cd/webhook',
|
||||
}
|
||||
|
||||
# create the requirements file
|
||||
file { $pt_r10k_req_file:
|
||||
ensure => file,
|
||||
owner => 'puppet',
|
||||
group => 'puppet',
|
||||
mode => '0644',
|
||||
selrange => s0,
|
||||
selrole => object_r,
|
||||
seltype => etc_t,
|
||||
seluser => system_u,
|
||||
content => template($pt_r10k_req_erb),
|
||||
}
|
||||
|
||||
# create the webhook config file
|
||||
file { $pt_r10k_wh_config_file:
|
||||
ensure => file,
|
||||
owner => 'root',
|
||||
group => 'root',
|
||||
mode => '0644',
|
||||
selrange => s0,
|
||||
selrole => object_r,
|
||||
seltype => etc_t,
|
||||
seluser => system_u,
|
||||
content => template($pt_r10k_wh_config_erb),
|
||||
require => File[$pt_r10k_webhook_dir],
|
||||
}
|
||||
|
||||
|
||||
# install pip dependencies
|
||||
exec { 'pip_install_r10k_webhook':
|
||||
command => 'pip3 install --user -r /opt/r10k-webhook/requirements.txt',
|
||||
user => 'puppet',
|
||||
require => [Package[$pt_r10k_webhook_pkg],File[$pt_r10k_req_file]],
|
||||
unless => 'pip3 show fastapi', # Idempotent check
|
||||
}
|
||||
|
||||
# establish exec systemd reload
|
||||
exec { 'systemctl_daemon_reload':
|
||||
command => 'systemctl daemon-reload',
|
||||
path => ['/bin', '/usr/bin'],
|
||||
require => Exec['pip_install_r10k_webhook'],
|
||||
refreshonly => true,
|
||||
}
|
||||
|
||||
# manage service
|
||||
service { 'r10k-webhook':
|
||||
ensure => 'running',
|
||||
enable => true,
|
||||
subscribe => File[$pt_r10k_wh_config_file],
|
||||
}
|
||||
# # manage service
|
||||
# service { 'r10k-webhook':
|
||||
# ensure => 'running',
|
||||
# enable => true,
|
||||
# subscribe => File[$pt_r10k_wh_config_file],
|
||||
# }
|
||||
}
|
||||
}</pre>
|
||||
</td>
|
||||
|
||||
BIN
files/webhook
Normal file
BIN
files/webhook
Normal file
Binary file not shown.
@@ -201,9 +201,6 @@ class puppet_cd::params (
|
||||
$pt_puppetdb_ssl = "${pt_puppetdb_main}/ssl"
|
||||
$pt_puppetdb_log = '/var/log/puppetlabs/puppetdb'
|
||||
$pt_puppetdb_var_dir = '/opt/puppetlabs/server/data/puppetdb'
|
||||
## r10k
|
||||
$pt_r10k_dir = "${pt_main_dir}/r10k"
|
||||
$pt_r10k_webhook_dir = '/etc/r10k-webhook'
|
||||
|
||||
# files
|
||||
## puppet
|
||||
@@ -230,14 +227,7 @@ class puppet_cd::params (
|
||||
$pt_puppetdb_repl_ini = "${pt_puppetdb_conf_d}/repl.ini"
|
||||
$pt_puppetdb_repl_erb = 'puppet_cd/puppetdb/repl.ini.erb'
|
||||
## r10k
|
||||
$pt_r10k_file = "${pt_r10k_dir}/r10k.yaml"
|
||||
$pt_r10k_erb = 'puppet_cd/r10k/r10k.yaml.erb'
|
||||
$pt_r10k_webhook_file = "${pt_r10k_webhook_dir}/webhook_server.py"
|
||||
$pt_r10k_webhook_erb = 'puppet_cd/r10k/webhook.py.erb'
|
||||
$pt_r10k_req_file = "${pt_r10k_webhook_dir}/requirements.txt"
|
||||
$pt_r10k_req_erb = 'puppet_cd/r10k/requirements.txt.erb'
|
||||
$pt_r10k_wh_config_file = "${pt_r10k_webhook_dir}/config.json"
|
||||
$pt_r10k_wh_config_erb = 'puppet_cd/r10k/r10k_webhook_config.erb'
|
||||
$pt_r10k_hook_file = '/usr/local/bin/webhook'
|
||||
|
||||
# service
|
||||
$pt_server_service = 'puppetserver'
|
||||
|
||||
@@ -7,72 +7,24 @@ class puppet_cd::r10k::webhook (
|
||||
|
||||
) inherits puppet_cd::params {
|
||||
if ($pt_pm_fqdn == $fqdn) and ($pt_use_r10k_webhook == true) {
|
||||
# install packages
|
||||
package { $pt_r10k_webhook_pkg:
|
||||
ensure => $pt_pkg_ensure,
|
||||
}
|
||||
|
||||
# create the webhook dir
|
||||
file { $pt_r10k_webhook_dir:
|
||||
ensure => directory,
|
||||
# create the webhook binary
|
||||
file { $pt_r10k_hook_file:
|
||||
ensure => file,
|
||||
owner => 'root',
|
||||
group => 'root',
|
||||
mode => '0755',
|
||||
selrange => s0,
|
||||
selrole => object_r,
|
||||
seltype => etc_t,
|
||||
seluser => system_u,
|
||||
seltype => bin_t,
|
||||
seluser => unconfined_u,
|
||||
source => 'puppet:///module/puppet_cd/webhook',
|
||||
}
|
||||
|
||||
# create the requirements file
|
||||
file { $pt_r10k_req_file:
|
||||
ensure => file,
|
||||
owner => 'puppet',
|
||||
group => 'puppet',
|
||||
mode => '0644',
|
||||
selrange => s0,
|
||||
selrole => object_r,
|
||||
seltype => etc_t,
|
||||
seluser => system_u,
|
||||
content => template($pt_r10k_req_erb),
|
||||
}
|
||||
|
||||
# create the webhook config file
|
||||
file { $pt_r10k_wh_config_file:
|
||||
ensure => file,
|
||||
owner => 'root',
|
||||
group => 'root',
|
||||
mode => '0644',
|
||||
selrange => s0,
|
||||
selrole => object_r,
|
||||
seltype => etc_t,
|
||||
seluser => system_u,
|
||||
content => template($pt_r10k_wh_config_erb),
|
||||
require => File[$pt_r10k_webhook_dir],
|
||||
}
|
||||
|
||||
|
||||
# install pip dependencies
|
||||
exec { 'pip_install_r10k_webhook':
|
||||
command => 'pip3 install --user -r /opt/r10k-webhook/requirements.txt',
|
||||
user => 'puppet',
|
||||
require => [Package[$pt_r10k_webhook_pkg],File[$pt_r10k_req_file]],
|
||||
unless => 'pip3 show fastapi', # Idempotent check
|
||||
}
|
||||
|
||||
# establish exec systemd reload
|
||||
exec { 'systemctl_daemon_reload':
|
||||
command => 'systemctl daemon-reload',
|
||||
path => ['/bin', '/usr/bin'],
|
||||
require => Exec['pip_install_r10k_webhook'],
|
||||
refreshonly => true,
|
||||
}
|
||||
|
||||
# manage service
|
||||
service { 'r10k-webhook':
|
||||
ensure => 'running',
|
||||
enable => true,
|
||||
subscribe => File[$pt_r10k_wh_config_file],
|
||||
}
|
||||
# # manage service
|
||||
# service { 'r10k-webhook':
|
||||
# ensure => 'running',
|
||||
# enable => true,
|
||||
# subscribe => File[$pt_r10k_wh_config_file],
|
||||
# }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#fastapi==0.115.0
|
||||
#uvicorn==0.30.6
|
||||
#pydantic==2.8.2
|
||||
r10k-webhook
|
||||
@@ -1,198 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Custom r10k Webhook Server for Puppet Control Repo
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
import os
|
||||
import subprocess
|
||||
import logging
|
||||
import hmac
|
||||
import hashlib
|
||||
|
||||
from fastapi import FastAPI, Request, HTTPException, BackgroundTasks
|
||||
from fastapi.responses import JSONResponse
|
||||
import uvicorn
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler('/var/log/r10k-webhook.log'),
|
||||
logging.StreamHandler()
|
||||
]
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
app = FastAPI(title="r10k Webhook Server")
|
||||
|
||||
|
||||
class WebhookPayload(BaseModel):
|
||||
"""Data model for webhook payload"""
|
||||
ref: str
|
||||
project: dict
|
||||
commits: list
|
||||
|
||||
|
||||
def run_r10k_deploy() -> bool:
|
||||
"""Run r10k deploy command"""
|
||||
try:
|
||||
cmd = [
|
||||
'/usr/bin/r10k', 'deploy',
|
||||
'-v',
|
||||
'-c', '/etc/puppetlabs/r10k/r10k.conf'
|
||||
]
|
||||
|
||||
logger.info("Starting r10k deploy...")
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True
|
||||
)
|
||||
|
||||
logger.info("r10k deploy successful!")
|
||||
logger.debug("r10k stdout: %s", result.stdout)
|
||||
if result.stderr:
|
||||
logger.warning("r10k stderr: %s", result.stderr)
|
||||
return True
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
logger.error("r10k deploy failed: %s", e)
|
||||
logger.error("stdout: %s", e.stdout)
|
||||
logger.error("stderr: %s", e.stderr)
|
||||
return False
|
||||
except FileNotFoundError:
|
||||
logger.error("r10k binary not found")
|
||||
return False
|
||||
except PermissionError:
|
||||
logger.error("Permission denied running r10k")
|
||||
return False
|
||||
|
||||
|
||||
def validate_signature(payload: bytes, signature: str, secret: str) -> bool:
|
||||
"""Validate webhook signature"""
|
||||
if not secret:
|
||||
return True
|
||||
|
||||
expected = hmac.new(
|
||||
secret.encode(),
|
||||
payload,
|
||||
hashlib.sha256
|
||||
).hexdigest()
|
||||
|
||||
if signature.startswith('sha256='):
|
||||
return hmac.compare_digest(signature, f'sha256={expected}')
|
||||
|
||||
return hmac.compare_digest(signature, expected)
|
||||
|
||||
|
||||
@app.post("/webhook")
|
||||
async def webhook_handler(
|
||||
request: Request,
|
||||
background_tasks: BackgroundTasks
|
||||
):
|
||||
"""Handle incoming webhook requests"""
|
||||
|
||||
body = await request.body()
|
||||
headers = dict(request.headers)
|
||||
event_type = headers.get(
|
||||
'x-gitlab-event',
|
||||
headers.get('x-github-event', 'unknown')
|
||||
)
|
||||
signature = headers.get(
|
||||
'x-gitlab-token',
|
||||
headers.get('x-hub-signature-256', '')
|
||||
)
|
||||
|
||||
print(
|
||||
f"DEBUG: Received webhook: event_type={event_type}, "
|
||||
f"headers={headers}"
|
||||
)
|
||||
logger.info(
|
||||
"Received webhook: event_type=%s, headers=%s",
|
||||
event_type,
|
||||
headers
|
||||
)
|
||||
|
||||
webhook_secret = os.getenv('R10K_WEBHOOK_SECRET', '')
|
||||
is_valid = validate_signature(body, signature, webhook_secret)
|
||||
if webhook_secret and not is_valid:
|
||||
logger.warning("Invalid webhook signature")
|
||||
raise HTTPException(status_code=403, detail="Invalid signature")
|
||||
|
||||
try:
|
||||
payload = await request.json()
|
||||
ref = payload.get('ref', '')
|
||||
branch = ref.split('/')[-1] if '/' in ref else ref
|
||||
print(
|
||||
f"DEBUG: Parsed payload: ref={ref}, "
|
||||
f"branch={branch}"
|
||||
)
|
||||
logger.info("Parsed payload: ref=%s, branch=%s", ref, branch)
|
||||
|
||||
if branch not in ['main', 'master']:
|
||||
logger.info("Ignoring non-main branch: %s", branch)
|
||||
return JSONResponse({
|
||||
"status": "ignored",
|
||||
"branch": branch
|
||||
})
|
||||
|
||||
# Match GitLab event types explicitly
|
||||
valid_events = [
|
||||
'push hook', 'merge request hook',
|
||||
'push', 'Push', 'Push Hook'
|
||||
]
|
||||
normalized_event = event_type.lower().strip()
|
||||
print(f"DEBUG: Normalized event: {normalized_event}")
|
||||
logger.info("Normalized event: %s", normalized_event)
|
||||
if normalized_event in valid_events:
|
||||
logger.info("Triggering r10k for %s on %s", event_type, branch)
|
||||
background_tasks.add_task(run_r10k_deploy)
|
||||
return JSONResponse({
|
||||
"status": "accepted",
|
||||
"message": "r10k deploy triggered",
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
"branch": branch
|
||||
})
|
||||
|
||||
logger.info("Ignoring event type: %s", event_type)
|
||||
return JSONResponse({
|
||||
"status": "ignored",
|
||||
"event": event_type
|
||||
})
|
||||
|
||||
except ValueError as e:
|
||||
logger.error("Webhook processing error: %s", e)
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Invalid payload"
|
||||
) from e
|
||||
|
||||
|
||||
@app.get("/health")
|
||||
async def health_check():
|
||||
"""Health check endpoint"""
|
||||
result = subprocess.run(
|
||||
['r10k', '--version'],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True
|
||||
)
|
||||
return {
|
||||
"status": "healthy",
|
||||
"r10k_version": result.stdout.strip()
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
uvicorn.run(
|
||||
"webhook_server:app",
|
||||
host="0.0.0.0",
|
||||
port=8080,
|
||||
log_level="info"
|
||||
)
|
||||
Reference in New Issue
Block a user