Skip to content
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
909caf5
pipeline dashboardin
JacobCoffee Mar 14, 2026
b5dd133
fix: handle empty chart state in pipeline dashboard
JacobCoffee Mar 14, 2026
a33eb06
make the dashboard more useful
JacobCoffee Mar 14, 2026
1ed7ab1
paginate long things
JacobCoffee Mar 14, 2026
8f5769e
fix the compact headerr mode for pipeline tab
JacobCoffee Mar 14, 2026
5eff4ec
fmt
JacobCoffee Mar 14, 2026
f4d7776
fix the bandit fails
JacobCoffee Mar 14, 2026
b20fc6e
fix: pass CI lint and security checks
JacobCoffee Mar 14, 2026
091476b
fix: resolve mypy type error in metrics push_to_gateway call
JacobCoffee Mar 14, 2026
699879e
use what we have!
JacobCoffee Mar 15, 2026
2ee8cbc
Merge branch 'main' into pipeline-dashboard
JacobCoffee Mar 28, 2026
47f3480
Remove pushgateway telemetry; pipeline dashboard uses DB queries
JacobCoffee Mar 28, 2026
1168c93
Enable Pipeline link in Observe dropdown
JacobCoffee Mar 28, 2026
2f64efe
Seed realistic pipeline timing data for dashboard testing
JacobCoffee Mar 28, 2026
cf8e945
Fix seed timestamps: use raw SQL to bypass onupdate handlers
JacobCoffee Mar 28, 2026
c94fc41
add some spacin
JacobCoffee Mar 28, 2026
b2c8281
Right-align sha and date columns in slowest table
JacobCoffee Mar 28, 2026
76105ed
fix numnbers clipping
JacobCoffee Mar 28, 2026
d461e33
Seed 15 pipeline runs over 90 days with 2 failures
JacobCoffee Mar 28, 2026
a9f6d8f
Fix CI: exclude scripts from bandit, fix black formatting
JacobCoffee Mar 28, 2026
f47b3ce
Remove mimir-loki-conf workarounds from deploy.py and config.py
JacobCoffee Mar 28, 2026
ff7aab6
fmt
JacobCoffee Mar 28, 2026
8ff0ba1
fixup lints
JacobCoffee Mar 28, 2026
da47ba6
Set timestamps directly on model objects instead of raw SQL fixups
JacobCoffee Mar 28, 2026
429ac15
Remove dead CSS (.pipe-kpi-sub, .u-legend override)
JacobCoffee Mar 28, 2026
1aecfdd
Remove unused trigger_type from Image and Release models
JacobCoffee Mar 28, 2026
eaf4d61
Simplify seed timestamps: single raw SQL pass after commit
JacobCoffee Mar 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cabotage/celery/tasks/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -1657,6 +1657,7 @@ def run_image_build(image_id=None, buildkit=False):
image.image_id = build_metadata["image_id"]
image.processes = build_metadata["processes"]
image.built = True
image.completed_at = datetime.datetime.utcnow()
image.image_metadata = {
**(image.image_metadata or {}),
"dockerfile_env_vars": build_metadata["dockerfile_env_vars"],
Expand Down
272 changes: 167 additions & 105 deletions cabotage/celery/tasks/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -1229,7 +1229,10 @@ def render_image_pull_secrets(release):
".dockerconfigjson": b64encode(
release.image_pull_secrets(
registry_auth_secret,
registry_urls=[current_app.config["REGISTRY_PULL"]],
registry_urls=[
current_app.config.get("REGISTRY_PULL_K8S")
or current_app.config["REGISTRY_PULL"]
],
).encode()
).decode(),
},
Expand Down Expand Up @@ -1468,11 +1471,14 @@ def render_cabotage_sidecar_tls_container(release, unix=True, tcp=False):
def render_process_container(
release, process_name, datadog_tags, with_tls=True, unix=True
):
volume_mounts = [
kubernetes.client.V1VolumeMount(
name="vault-secrets", mount_path="/var/run/secrets/vault"
),
]
testing = current_app.config.get("TESTING")
volume_mounts = []
if not testing:
volume_mounts.append(
kubernetes.client.V1VolumeMount(
name="vault-secrets", mount_path="/var/run/secrets/vault"
),
)
if unix:
volume_mounts.append(
kubernetes.client.V1VolumeMount(
Expand All @@ -1484,11 +1490,24 @@ def render_process_container(
process_name, DEFAULT_POD_CLASS
)
pod_class = pod_classes[process_pod_cls]
return kubernetes.client.V1Container(
name=process_name,
image=f'{current_app.config["REGISTRY_PULL"]}/{release.repository_name}:release-{release.version}',
image_pull_policy="Always",
env=[

registry = (
current_app.config.get("REGISTRY_PULL_K8S")
or current_app.config["REGISTRY_PULL"]
)

if testing:
Comment thread
JacobCoffee marked this conversation as resolved.
Outdated
# TESTING mode: use default CMD, no envconsul, no vault/consul env
env = [
kubernetes.client.V1EnvVar(
name="DATADOG_TAGS",
value=",".join([f"{k}:{v}" for k, v in datadog_tags.items()]),
),
kubernetes.client.V1EnvVar(name="SOURCE_COMMIT", value=release.commit_sha),
]
args = None
else:
env = [
kubernetes.client.V1EnvVar(
name="VAULT_ADDR", value="https://vault.cabotage.svc.cluster.local"
),
Expand All @@ -1507,12 +1526,19 @@ def render_process_container(
value=",".join([f"{k}:{v}" for k, v in datadog_tags.items()]),
),
kubernetes.client.V1EnvVar(name="SOURCE_COMMIT", value=release.commit_sha),
],
args=[
]
args = [
"envconsul",
"-kill-signal=SIGTERM",
f"-config=/etc/cabotage/envconsul-{process_name}.hcl",
],
]

return kubernetes.client.V1Container(
name=process_name,
image=f"{registry}/{release.repository_name}:release-{release.version}",
image_pull_policy="Always",
env=env,
args=args,
resources=kubernetes.client.V1ResourceRequirements(
limits={
"memory": pod_class["memory"]["limits"],
Expand All @@ -1523,7 +1549,7 @@ def render_process_container(
"cpu": pod_class["cpu"]["requests"],
},
),
volume_mounts=volume_mounts,
volume_mounts=volume_mounts or None,
)


Expand Down Expand Up @@ -1596,6 +1622,7 @@ def render_datadog_container(dd_api_key, datadog_tags):


def render_podspec(release, process_name, service_account_name):
testing = current_app.config.get("TESTING")
datadog_tags = {
"organization": release.application.project.organization.slug,
"project": release.application.project.slug,
Expand All @@ -1604,112 +1631,145 @@ def render_podspec(release, process_name, service_account_name):
"app": k8s_label_value(release),
"release": str(release.version),
}
volumes = [
kubernetes.client.V1Volume(
name="vault-secrets",
empty_dir=kubernetes.client.V1EmptyDirVolumeSource(
medium="Memory", size_limit="1M"
),
),
]
volumes = []
init_containers = []
containers = []
restart_policy = None

if process_name.startswith("web"):
if testing:
Comment thread
JacobCoffee marked this conversation as resolved.
Outdated
# TESTING mode: skip all sidecar/enroller/TLS init containers
unix = process_name.startswith("web")
if unix:
volumes.append(
kubernetes.client.V1Volume(
name="cabotage-sock",
empty_dir=kubernetes.client.V1EmptyDirVolumeSource(
medium="Memory", size_limit="1M"
),
)
)
containers.append(
render_process_container(
release, process_name, datadog_tags, with_tls=False, unix=unix
)
)
if process_name.startswith("release") or process_name.startswith("postdeploy"):
restart_policy = "Never"
else:
volumes.append(
kubernetes.client.V1Volume(
name="cabotage-sock",
name="vault-secrets",
empty_dir=kubernetes.client.V1EmptyDirVolumeSource(
medium="Memory", size_limit="1M"
),
)
)
init_containers.append(
render_cabotage_enroller_container(release, process_name, with_tls=True)
)
init_containers.append(
render_cabotage_sidecar_container(release, with_tls=True)
)
init_containers.append(
render_cabotage_sidecar_tls_container(release, unix=True)
),
)
containers.append(
render_process_container(
release, process_name, datadog_tags, with_tls=True, unix=True

if process_name.startswith("web"):
volumes.append(
kubernetes.client.V1Volume(
name="cabotage-sock",
empty_dir=kubernetes.client.V1EmptyDirVolumeSource(
medium="Memory", size_limit="1M"
),
)
)
)
elif process_name.startswith("tcp"):
init_containers.append(
render_cabotage_enroller_container(release, process_name, with_tls=True)
)
init_containers.append(
render_cabotage_sidecar_container(release, with_tls=True)
)
init_containers.append(
render_cabotage_sidecar_tls_container(release, unix=False, tcp=True)
)
containers.append(
render_process_container(
release, process_name, datadog_tags, with_tls=True, unix=False
init_containers.append(
render_cabotage_enroller_container(release, process_name, with_tls=True)
)
)
elif process_name.startswith("worker"):
init_containers.append(
render_cabotage_enroller_container(release, process_name, with_tls=False)
)
init_containers.append(
render_cabotage_sidecar_container(release, with_tls=False)
)
containers.append(
render_process_container(
release, process_name, datadog_tags, with_tls=False, unix=False
init_containers.append(
render_cabotage_sidecar_container(release, with_tls=True)
)
)
elif process_name.startswith("release"):
init_containers.append(
render_cabotage_enroller_container(release, process_name, with_tls=False)
)
containers.append(
render_process_container(
release, process_name, datadog_tags, with_tls=False, unix=False
init_containers.append(
render_cabotage_sidecar_tls_container(release, unix=True)
)
)
restart_policy = "Never"
elif process_name.startswith("postdeploy"):
init_containers.append(
render_cabotage_enroller_container(release, process_name, with_tls=False)
)
containers.append(
render_process_container(
release, process_name, datadog_tags, with_tls=False, unix=False
containers.append(
render_process_container(
release, process_name, datadog_tags, with_tls=True, unix=True
)
)
)
restart_policy = "Never"
else:
init_containers.append(
render_cabotage_enroller_container(release, process_name, with_tls=False)
)
containers.append(
render_process_container(
release, process_name, datadog_tags, with_tls=False
elif process_name.startswith("tcp"):
init_containers.append(
render_cabotage_enroller_container(release, process_name, with_tls=True)
)
init_containers.append(
render_cabotage_sidecar_container(release, with_tls=True)
)
init_containers.append(
render_cabotage_sidecar_tls_container(release, unix=False, tcp=True)
)
containers.append(
render_process_container(
release, process_name, datadog_tags, with_tls=True, unix=False
)
)
elif process_name.startswith("worker"):
init_containers.append(
render_cabotage_enroller_container(
release, process_name, with_tls=False
)
)
init_containers.append(
render_cabotage_sidecar_container(release, with_tls=False)
)
containers.append(
render_process_container(
release, process_name, datadog_tags, with_tls=False, unix=False
)
)
elif process_name.startswith("release"):
init_containers.append(
render_cabotage_enroller_container(
release, process_name, with_tls=False
)
)
containers.append(
render_process_container(
release, process_name, datadog_tags, with_tls=False, unix=False
)
)
restart_policy = "Never"
elif process_name.startswith("postdeploy"):
init_containers.append(
render_cabotage_enroller_container(
release, process_name, with_tls=False
)
)
containers.append(
render_process_container(
release, process_name, datadog_tags, with_tls=False, unix=False
)
)
restart_policy = "Never"
else:
init_containers.append(
render_cabotage_enroller_container(
release, process_name, with_tls=False
)
)
containers.append(
render_process_container(
release, process_name, datadog_tags, with_tls=False
)
)
)

if (
not (
process_name.startswith("release") or process_name.startswith("postdeploy")
)
and "DD_API_KEY" in release.configuration_objects
):
try:
dd_api_key = release.configuration_objects["DD_API_KEY"].read_value(
config_writer
if (
not (
process_name.startswith("release")
or process_name.startswith("postdeploy")
)
except KeyError:
print("unable to read DD_API_KEY")
if dd_api_key:
init_containers.append(render_datadog_container(dd_api_key, datadog_tags))
and "DD_API_KEY" in release.configuration_objects
):
try:
dd_api_key = release.configuration_objects["DD_API_KEY"].read_value(
config_writer
)
except KeyError:
print("unable to read DD_API_KEY")
if dd_api_key:
init_containers.append(
render_datadog_container(dd_api_key, datadog_tags)
)

app_env = release.application_environment
env = app_env.environment if app_env.k8s_identifier is not None else None
Expand All @@ -1735,9 +1795,9 @@ def render_podspec(release, process_name, service_account_name):
service_account_name=service_account_name,
node_selector=node_selector,
tolerations=tolerations,
init_containers=init_containers,
init_containers=init_containers or None,
containers=containers,
volumes=volumes,
volumes=volumes or None,
restart_policy=restart_policy,
)

Expand Down Expand Up @@ -2793,7 +2853,9 @@ def fake_deploy_release(deployment):
)
deploy_log.append(yaml.dump(remove_none(deployment_object.to_dict())))
deployment.deploy_log = "\n".join(deploy_log)
deployment.complete = True
db.session.commit()

if (
deployment.deploy_metadata
and "installation_id" in deployment.deploy_metadata
Expand Down
3 changes: 2 additions & 1 deletion cabotage/client/templates/_macros.html
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ <h1 class="page-title text-balance">{{ title }}</h1>
{% else %}
<li class="disabled opacity-30"><span>{{ icon('file-text', 'w-3.5 h-3.5') }} Logs</span></li>
{% endif %}
<li class="disabled opacity-30"><span>{{ icon('bar-chart-2', 'w-3.5 h-3.5') }} Pipeline <span class="text-[0.6rem] opacity-40">(Soon)</span></span></li>
<li><a href="{{ url_for('user.project_application_pipeline', org_slug=org_slug, project_slug=project_slug, app_slug=app_slug, env_slug=env_slug) }}"
class="{% if active == 'pipeline' %}active{% endif %}">{{ icon('bar-chart-2', 'w-3.5 h-3.5') }} Pipeline</a></li>
</ul>
</div>
<a href="{{ url_for('user.application_images', org_slug=org_slug, project_slug=project_slug, app_slug=app_slug, env_slug=env_slug) }}"
Expand Down
Loading
Loading