mirror of
https://github.com/kerberos-io/helm-charts.git
synced 2026-11-06 20:30:23 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a802b56954 |
@@ -1,28 +0,0 @@
|
||||
name: HTTP port migration
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
migration:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: azure/setup-helm@v4.3.1
|
||||
- uses: helm/kind-action@v1
|
||||
- name: Validate migration against Kubernetes
|
||||
env:
|
||||
SERVER_VALIDATION: "true"
|
||||
run: bash scripts/check-http-port-migration.sh
|
||||
- name: Validate chart runtime compatibility
|
||||
run: |
|
||||
helm lint charts/hub
|
||||
bash scripts/check-admin-runtime.sh
|
||||
bash scripts/check-hardened-workers.sh
|
||||
bash scripts/check-frontend-runtime.sh
|
||||
bash scripts/check-hub-api-runtime.sh
|
||||
- name: Smoke test hardened frontend images and Service routing
|
||||
env:
|
||||
CLUSTER_SMOKE: "true"
|
||||
run: bash scripts/check-frontend-runtime.sh
|
||||
@@ -0,0 +1,31 @@
|
||||
name: Workflows queue consistency
|
||||
|
||||
# Fails the build if the analysis producer, the workflows engine and the stage
|
||||
# workers would render onto different WORKFLOWS_QUEUE names — the silent
|
||||
# producer/consumer queue-name drift that leaves runs piling up with no
|
||||
# consumer. Pure `helm template` render check, no cluster required.
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'charts/hub/**'
|
||||
- 'scripts/check-workflows-queue-consistency.sh'
|
||||
- '.github/workflows/workflows-queue-consistency.yaml'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Helm
|
||||
uses: azure/setup-helm@v4
|
||||
with:
|
||||
version: v3.16.2
|
||||
|
||||
- name: Check WORKFLOWS_QUEUE consistency
|
||||
run: ./scripts/check-workflows-queue-consistency.sh charts/hub
|
||||
@@ -16,7 +16,7 @@ type: application
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.176.0
|
||||
version: 0.138.0
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
|
||||
+12
-1353
File diff suppressed because it is too large
Load Diff
@@ -44,8 +44,7 @@
|
||||
"incomplete": "Enter all 4 characters to continue.",
|
||||
"unlock": "Unlock encrypted data",
|
||||
"switchAccount": "or switch account",
|
||||
"ariaCharacter": "Passphrase character",
|
||||
"rememberCode": "Remember code for 24 hours on this device"
|
||||
"ariaCharacter": "Passphrase character"
|
||||
},
|
||||
"darkMode": {
|
||||
"switchToLight": "Switch to light mode",
|
||||
@@ -125,7 +124,6 @@
|
||||
"recording": "Recording",
|
||||
"dayOverview": "Day overview",
|
||||
"analytics": "Analytics",
|
||||
"markers": "Markers",
|
||||
"watchlist": "Watchlist",
|
||||
"cases": "Cases",
|
||||
"alerts": "Alerts",
|
||||
@@ -136,257 +134,7 @@
|
||||
"allOf": "All {{name}}s",
|
||||
"accounts": "Accounts",
|
||||
"roles": "Roles",
|
||||
"audit": "Audit events",
|
||||
"settings": "Settings",
|
||||
"context": {
|
||||
"label": "Workspace",
|
||||
"title": "Switch workspace",
|
||||
"open": "Choose organisation and project",
|
||||
"resize": "Resize organisation column"
|
||||
},
|
||||
"projects": {
|
||||
"label": "Project",
|
||||
"open": "Show projects",
|
||||
"search": "Search projects",
|
||||
"noResults": "No matching projects",
|
||||
"fallback": "Project 1",
|
||||
"empty": "No projects",
|
||||
"unavailable": "Projects unavailable",
|
||||
"loading": "Loading projects..."
|
||||
},
|
||||
"organisations": {
|
||||
"label": "Organisation",
|
||||
"search": "Search organisations",
|
||||
"noResults": "No matching organisations",
|
||||
"current": "Current organisation",
|
||||
"none": "No organisation",
|
||||
"select": "Select organisation",
|
||||
"unavailable": "Organisation unavailable",
|
||||
"loading": "Loading organisations...",
|
||||
"empty": "No organisations yet",
|
||||
"create": "Create organisation",
|
||||
"name": "Organisation name",
|
||||
"modal": {
|
||||
"title": "Create organisation",
|
||||
"subtitle": "Add the essentials now. Company and contact details can be completed later.",
|
||||
"create": "Create organisation",
|
||||
"tabs": {
|
||||
"general": "General",
|
||||
"company": "Company",
|
||||
"contact": "Contact & location"
|
||||
},
|
||||
"fields": {
|
||||
"name": "Organisation name *",
|
||||
"description": "Description",
|
||||
"domain": "Email domain",
|
||||
"legalName": "Legal name",
|
||||
"tradingName": "Trading name",
|
||||
"industry": "Industry",
|
||||
"website": "Website",
|
||||
"email": "Contact email",
|
||||
"phone": "Contact phone",
|
||||
"street": "Street address",
|
||||
"street2": "Address line 2",
|
||||
"city": "City",
|
||||
"region": "State / region",
|
||||
"postalCode": "Postal code",
|
||||
"country": "Country"
|
||||
},
|
||||
"placeholders": {
|
||||
"name": "For example, Acme Security",
|
||||
"description": "What this organisation is used for",
|
||||
"domain": "example.com",
|
||||
"website": "https://example.com",
|
||||
"email": "contact@example.com"
|
||||
},
|
||||
"errors": {
|
||||
"form": "Please complete all the missing fields.",
|
||||
"nameRequired": "Enter an organisation name.",
|
||||
"nameLength": "Use at least 2 characters.",
|
||||
"email": "Enter a valid email address."
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"title": "Organisation settings",
|
||||
"open": "Open organisation settings",
|
||||
"loading": "Loading organisation...",
|
||||
"loadFailed": "The organisation could not be loaded.",
|
||||
"save": "Save changes",
|
||||
"saving": "Saving...",
|
||||
"tabs": {
|
||||
"general": "General",
|
||||
"company": "Company",
|
||||
"address": "Billing address",
|
||||
"contacts": "Contacts",
|
||||
"regional": "Regional",
|
||||
"users": "Users",
|
||||
"subscription": "Subscription"
|
||||
},
|
||||
"cards": {
|
||||
"userTitle": "{{count}} user",
|
||||
"usersTitle": "{{count}} users",
|
||||
"usersSubtitle": "People with access to this organisation",
|
||||
"usersFooter": "Organisation users",
|
||||
"viewUsers": "View users"
|
||||
},
|
||||
"subscription": {
|
||||
"currentPlan": "Current plan",
|
||||
"noPlan": "No active plan",
|
||||
"description": "Manage the plan, billing method, limits, and integrations for this organisation.",
|
||||
"manage": "Manage subscription",
|
||||
"status": {
|
||||
"no-plan": "No subscription is currently active.",
|
||||
"active": "The subscription is active.",
|
||||
"cancelled": "The subscription has been cancelled.",
|
||||
"reached-limit": "The subscription limit has been reached."
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"title": "Organisation users",
|
||||
"descriptionSingle": "{{count}} person currently has access to this organisation.",
|
||||
"description": "{{count}} people currently have access to this organisation.",
|
||||
"manage": "Manage users",
|
||||
"inactive": "Inactive",
|
||||
"empty": "No users found",
|
||||
"emptyDescription": "Add users to give them access to this organisation."
|
||||
},
|
||||
"contacts": {
|
||||
"primary": "Primary contact",
|
||||
"financial": "Financial contact",
|
||||
"technical": "Technical contact"
|
||||
},
|
||||
"fields": {
|
||||
"name": "Organisation name *",
|
||||
"description": "Description",
|
||||
"domain": "Email domain",
|
||||
"legalName": "Legal name",
|
||||
"tradingName": "Trading name",
|
||||
"registrationNumber": "Registration number",
|
||||
"vatNumber": "VAT number",
|
||||
"industry": "Industry",
|
||||
"website": "Website",
|
||||
"email": "Contact email",
|
||||
"phone": "Contact phone",
|
||||
"street": "Street address",
|
||||
"street2": "Address line 2",
|
||||
"city": "City",
|
||||
"region": "State / region",
|
||||
"postalCode": "Postal code",
|
||||
"country": "Country",
|
||||
"timezone": "Timezone",
|
||||
"locale": "Locale",
|
||||
"currency": "Currency",
|
||||
"contactName": "Name",
|
||||
"contactEmail": "Email",
|
||||
"contactPhone": "Phone"
|
||||
},
|
||||
"placeholders": {
|
||||
"domain": "example.com",
|
||||
"website": "https://example.com",
|
||||
"email": "contact@example.com",
|
||||
"timezone": "Europe/Brussels",
|
||||
"locale": "en-US",
|
||||
"currency": "EUR"
|
||||
},
|
||||
"errors": {
|
||||
"form": "Please correct the highlighted fields.",
|
||||
"nameRequired": "Enter an organisation name.",
|
||||
"nameLength": "Use at least 2 characters.",
|
||||
"email": "Enter a valid email address."
|
||||
}
|
||||
}
|
||||
},
|
||||
"projects": {
|
||||
"label": "Project",
|
||||
"current": "Current project",
|
||||
"none": "No project",
|
||||
"select": "Select project",
|
||||
"unavailable": "Project unavailable",
|
||||
"loading": "Loading projects...",
|
||||
"empty": "No projects yet",
|
||||
"search": "Search projects",
|
||||
"create": "Create project",
|
||||
"name": "Project name",
|
||||
"settings": {
|
||||
"title": "Project settings",
|
||||
"details": "Project details",
|
||||
"tabs": {
|
||||
"general": "General",
|
||||
"credentials": "Credentials"
|
||||
},
|
||||
"encryption": "Encryption keys",
|
||||
"publicKey": "RSA public key",
|
||||
"privateKey": "RSA private key",
|
||||
"legacyPublicKey": "Agent public key",
|
||||
"legacyPrivateKey": "Agent private key",
|
||||
"encryptionKey": "Encryption key",
|
||||
"secretKeys": "Secret keys",
|
||||
"generate": "Generate credentials",
|
||||
"regenerate": "Regenerate credentials",
|
||||
"generating": "Generating keys...",
|
||||
"generateHint": "Generate keys for this project.",
|
||||
"oneTimeHint": "Copy these keys now. They will not be shown again.",
|
||||
"copyPublicKey": "Copy RSA public key",
|
||||
"copyPrivateKey": "Copy RSA private key",
|
||||
"copyLegacyPublicKey": "Copy Agent public key",
|
||||
"copyLegacyPrivateKey": "Copy Agent private key",
|
||||
"copyEncryptionKey": "Copy encryption key",
|
||||
"pincode": {
|
||||
"generateTitle": "Generate project credentials?",
|
||||
"generateMessage": "Generate new keys for this project.",
|
||||
"label": "PIN code (optional)",
|
||||
"hint": "Enter exactly 4 digits to encrypt the stored encryption key. The PIN is never saved.",
|
||||
"error": "Enter exactly 4 digits or leave the PIN empty.",
|
||||
"statusLabel": "PIN protection",
|
||||
"enabled": "Enabled",
|
||||
"disabled": "Disabled"
|
||||
},
|
||||
"import": {
|
||||
"open": "Bring your own keys",
|
||||
"title": "Bring your own keys",
|
||||
"description": "Import an RSA-4096 public key generated outside the platform and provide your encryption key.",
|
||||
"replacementWarning": "This replaces the current credentials. Cameras using them may disconnect.",
|
||||
"formatHint": "The public key may be OpenSSL PEM or single-line Base64 DER. The encryption key must contain exactly 32 hexadecimal characters.",
|
||||
"encryptionKeyError": "Enter exactly 32 hexadecimal characters.",
|
||||
"cancel": "Cancel",
|
||||
"confirm": "Import credentials",
|
||||
"importing": "Importing..."
|
||||
},
|
||||
"regenerateWarning": {
|
||||
"title": "Regenerate project credentials?",
|
||||
"message": "This immediately replaces the current public, private and encryption keys. This action cannot be undone.",
|
||||
"cameraImpact": "Cameras using the current credentials may disconnect and must be configured with the new credentials.",
|
||||
"cancel": "Cancel",
|
||||
"confirm": "Regenerate credentials"
|
||||
}
|
||||
},
|
||||
"modal": {
|
||||
"title": "Create project",
|
||||
"subtitle": "A project keeps its devices, sites and recordings separate from the rest of the organisation.",
|
||||
"create": "Create project",
|
||||
"fields": {
|
||||
"name": "Project name *",
|
||||
"slug": "Handle *",
|
||||
"description": "Description"
|
||||
},
|
||||
"placeholders": {
|
||||
"name": "For example, Warehouse North",
|
||||
"slug": "warehouse-north",
|
||||
"description": "What this project is used for"
|
||||
},
|
||||
"hints": {
|
||||
"slug": "Lowercase letters, numbers and single hyphens. Used to reference the project later."
|
||||
},
|
||||
"errors": {
|
||||
"form": "Please complete all the missing fields.",
|
||||
"nameRequired": "Enter a project name.",
|
||||
"nameLength": "Use at least 2 characters.",
|
||||
"slugRequired": "Enter a handle.",
|
||||
"slugFormat": "Use lowercase letters, numbers and single hyphens only.",
|
||||
"slugLength": "Use at most 63 characters."
|
||||
}
|
||||
}
|
||||
},
|
||||
"plansIntegrations": "Plans & integrations",
|
||||
"dataUploaded": "Data uploaded",
|
||||
"dailyLimit": "{{percent}}% – {{usage}}/{{max}} GB daily limit",
|
||||
@@ -460,57 +208,6 @@
|
||||
"Vault": "Vault"
|
||||
},
|
||||
"pages": {
|
||||
"audit": {
|
||||
"title": "Audit events",
|
||||
"refresh": "Refresh",
|
||||
"loadError": "Audit events could not be loaded. Try again.",
|
||||
"metadata": "Metadata",
|
||||
"noMetadata": "No metadata was recorded for this event.",
|
||||
"changes": "Changes",
|
||||
"filters": {
|
||||
"actor": "Actor",
|
||||
"actorPlaceholder": "Name or ID",
|
||||
"action": "Action",
|
||||
"target": "Target",
|
||||
"targetPlaceholder": "Name or ID",
|
||||
"operation": "Operation",
|
||||
"permission": "Permission",
|
||||
"category": "Category",
|
||||
"outcome": "Outcome",
|
||||
"search": "Search all fields",
|
||||
"searchPlaceholder": "Actor, action, target, device...",
|
||||
"from": "From",
|
||||
"to": "To",
|
||||
"any": "Any",
|
||||
"clear": "Clear filters"
|
||||
},
|
||||
"table": {
|
||||
"timestamp": "Timestamp",
|
||||
"actor": "Actor",
|
||||
"action": "Action",
|
||||
"target": "Target",
|
||||
"outcome": "Outcome",
|
||||
"details": "Event details",
|
||||
"showDetails": "Show metadata",
|
||||
"hideDetails": "Hide metadata"
|
||||
},
|
||||
"authorization": "Authorization",
|
||||
"request": "Request",
|
||||
"policy": "Policy",
|
||||
"granted": "Granted",
|
||||
"denied": "Denied",
|
||||
"noResults": {
|
||||
"title": "No audit events found",
|
||||
"description": "Events will appear here as activity is recorded."
|
||||
},
|
||||
"pagination": {
|
||||
"showing": "Showing {{start}}–{{end}} of {{total}}",
|
||||
"rows": "Rows",
|
||||
"previous": "Previous",
|
||||
"next": "Next",
|
||||
"page": "Page {{page}}"
|
||||
}
|
||||
},
|
||||
"floorplans": {
|
||||
"noneUploaded": "No floor plans uploaded yet",
|
||||
"uploadFirstHint": "Upload your first floor plan to visualize your devices on this site",
|
||||
@@ -563,13 +260,6 @@
|
||||
"completeAllFields": "Please complete all the missing fields",
|
||||
"nameExists": "This name already exists",
|
||||
"badgeColor": "Badge color (HEX)",
|
||||
"fineTuneLocation": "Fine-tune map position",
|
||||
"fineTuneInstructions": "Move the map so the pin marks the exact site location.",
|
||||
"latitude": "Latitude",
|
||||
"longitude": "Longitude",
|
||||
"invalidCoordinates": "Enter valid coordinates.",
|
||||
"selectAddressBeforeFineTune": "Select an address before fine-tuning its map position.",
|
||||
"resetLocation": "Reset position",
|
||||
"vaultSaveToUse": "Save changes to use this Vault",
|
||||
"vaultCheckCredentials": "Check credentials and try again"
|
||||
},
|
||||
@@ -614,85 +304,6 @@
|
||||
"analytics": {
|
||||
"manageAlerts": "Manage alerts",
|
||||
"subscribePrompt": "Subscribe to a plan to generate notifications",
|
||||
"markers": {
|
||||
"title": "Markers",
|
||||
"overview": "Overview",
|
||||
"searchPlaceholder": "Search marker names, e.g. a license plate",
|
||||
"clearFilters": "Clear filters",
|
||||
"loading": "Loading markers",
|
||||
"loadError": "Markers could not be loaded. Please try again.",
|
||||
"invalidDateRange": "The start date must be before the end date.",
|
||||
"openDetails": "Open marker details",
|
||||
"openMarkerDetails": "Open details for marker {{name}}",
|
||||
"openMedia": "Open recording",
|
||||
"noMedia": "No linked recording",
|
||||
"mediaErrorTitle": "Recording unavailable",
|
||||
"mediaErrorMessage": "The recording linked to this marker could not be opened.",
|
||||
"detail": {
|
||||
"title": "Marker details",
|
||||
"recording": "Marker recording",
|
||||
"loadingRecording": "Loading linked recording...",
|
||||
"videoUnavailable": "The linked recording has no playable video.",
|
||||
"markerPosition": "Marker starts {{offset}} into this recording",
|
||||
"openFullRecording": "Open full recording",
|
||||
"overview": "Overview",
|
||||
"start": "Start",
|
||||
"end": "End",
|
||||
"description": "Description",
|
||||
"classification": "Classification",
|
||||
"metadata": "Metadata",
|
||||
"source": "Source",
|
||||
"engine": "Engine",
|
||||
"modelVersion": "Model version",
|
||||
"boundingBox": "Bounding box",
|
||||
"rawMetadata": "Raw metadata",
|
||||
"comments": "Comments",
|
||||
"identifiers": "Identifiers",
|
||||
"markerId": "Marker ID",
|
||||
"deviceId": "Device ID",
|
||||
"siteId": "Site ID",
|
||||
"groupId": "Group ID",
|
||||
"organisationId": "Organisation ID",
|
||||
"mediaKey": "Media key",
|
||||
"audit": "Audit information"
|
||||
},
|
||||
"filters": {
|
||||
"categories": "Categories",
|
||||
"from": "From",
|
||||
"to": "To",
|
||||
"sort": "Sort markers"
|
||||
},
|
||||
"sort": {
|
||||
"latest": "Latest first",
|
||||
"oldest": "Oldest first",
|
||||
"nameAsc": "Name A-Z",
|
||||
"nameDesc": "Name Z-A"
|
||||
},
|
||||
"table": {
|
||||
"timestamp": "Timestamp",
|
||||
"marker": "Marker",
|
||||
"categories": "Categories",
|
||||
"device": "Device",
|
||||
"duration": "Duration",
|
||||
"source": "Source",
|
||||
"signals": "Events & tags",
|
||||
"events": "Events",
|
||||
"tags": "Tags",
|
||||
"confidence": "Confidence",
|
||||
"media": "Recording"
|
||||
},
|
||||
"noResults": {
|
||||
"title": "No markers found",
|
||||
"description": "Adjust the filters or search for another marker name."
|
||||
},
|
||||
"pagination": {
|
||||
"showing": "Showing {{start}}-{{end}}",
|
||||
"rows": "Rows",
|
||||
"previous": "Previous",
|
||||
"next": "Next",
|
||||
"page": "Page {{page}}"
|
||||
}
|
||||
},
|
||||
"noAlerts": {
|
||||
"description": "Create an alert or enable existing ones to start receiving analytics.",
|
||||
"button": "Alerts"
|
||||
@@ -729,19 +340,6 @@
|
||||
"placeholderDescription": "To create your first case go to the media section, and select a media item. Create a new case and assign it to the relevant person.",
|
||||
"goToMedia": "Go to Media",
|
||||
"myCases": "My cases",
|
||||
"loadError": "Something went wrong while loading the case. Please try again.",
|
||||
"autoplayNextOn": "Autoplay next: On",
|
||||
"autoplayNextOff": "Autoplay next: Off",
|
||||
"mediaProcessing": "Processing",
|
||||
"mediaFailed": "Failed",
|
||||
"a11y": {
|
||||
"toggleCase": "Toggle case details",
|
||||
"statusOpen": "Case status: open",
|
||||
"statusApproved": "Case status: approved",
|
||||
"statusRejected": "Case status: rejected",
|
||||
"loadingCases": "Loading cases",
|
||||
"caseThumbnail": "Case thumbnail"
|
||||
},
|
||||
"actions": {
|
||||
"reopenCase": "Reopen case",
|
||||
"approveCase": "Approve case",
|
||||
@@ -965,8 +563,6 @@
|
||||
"filter": {
|
||||
"date": "Date",
|
||||
"region": "Region",
|
||||
"regionExcluded": "Not in results",
|
||||
"regionExcludedHint": "No region drawn on this camera, so it is left out of the results. Draw a region to include it again.",
|
||||
"categories": "Categories",
|
||||
"markers": "Markers",
|
||||
"events": "Events",
|
||||
@@ -974,15 +570,7 @@
|
||||
"newest": "Newest",
|
||||
"oldest": "Oldest",
|
||||
"showAllRecordings": "Show all recordings",
|
||||
"showStarredRecordings": "Show starred recordings only",
|
||||
"synchronisedPlayer": "Synchronised player",
|
||||
"synchronisedPlayerHint": "Play all recordings of the selected region, one player per camera",
|
||||
"synchronisedPlayerRequirement": "Select a time region of maximum 60 minutes on the timeline to enable the synchronised player",
|
||||
"synchronisedPlayerPlay": "Play",
|
||||
"synchronisedPlayerPause": "Pause",
|
||||
"synchronisedPlayerNoFootage": "No recording at this moment",
|
||||
"synchronisedPlayerEmpty": "No playable recordings found in the selected time region.",
|
||||
"timelinePreviewPlayVideo": "Play video"
|
||||
"showStarredRecordings": "Show starred recordings only"
|
||||
},
|
||||
"emptyState": {
|
||||
"selectDayAndDevice": "Select day and device",
|
||||
@@ -998,12 +586,8 @@
|
||||
"timeline": {
|
||||
"title": "Timeline",
|
||||
"clearSelectedTime": "Clear selected time",
|
||||
"selectionStart": "Start of the selected time range",
|
||||
"selectionEnd": "End of the selected time range",
|
||||
"fetching": "Fetching timeline rows for selected devices.",
|
||||
"noRecordings": "No recordings are available for the selected date and devices.",
|
||||
"resizeHint": "Drag to resize the timeline",
|
||||
"rowHeight": "Drag to change the height of the recording bars"
|
||||
"noRecordings": "No recordings are available for the selected date and devices."
|
||||
},
|
||||
"chart": {
|
||||
"title": "Chart",
|
||||
@@ -1024,13 +608,8 @@
|
||||
"copyVideoUrl": "Copy Video URL",
|
||||
"verifyVideo": "Verify video",
|
||||
"shareRecording": "Share Recording",
|
||||
"runWorkflow": "Run workflow",
|
||||
"runWorkflowSubtitle": "Choose a workflow to run on this recording.",
|
||||
"noMediaWorkflows": "No workflows are available for recordings.",
|
||||
"newCase": "New case",
|
||||
"addToCase": "Add to case",
|
||||
"assignToCase": "Assign to case",
|
||||
"caseActions": "Case actions",
|
||||
"share": "Share",
|
||||
"save": "Save",
|
||||
"loading": "Loading...",
|
||||
@@ -1547,7 +1126,7 @@
|
||||
"k1": "Add a Dataset",
|
||||
"k2": "Are you sure you want to remove device",
|
||||
"k3": "Search dataset..",
|
||||
"k4": "Set up your first Agent",
|
||||
"k4": "Set up your first Kerberos Agent",
|
||||
"k5": "No devices are available",
|
||||
"k6": "Subscribe to a plan",
|
||||
"k7": "Dataset",
|
||||
@@ -1595,12 +1174,12 @@
|
||||
},
|
||||
"new": {
|
||||
"new": {
|
||||
"k1": "Before you will see your recordings shown up on Kerberos Hub, you first need to install and configure an Agent. An Agent is a piece of software that processes a device, and will make recordings when a portion of the video stream has changed. These recordings will then be send to, and processed by the Kerberos Hub application.",
|
||||
"k2": "After you've properly installed one or more Agents, you can link it to the Kerberos Hub application using your",
|
||||
"k1": "Before you will see your recordings shown up on Kerberos Hub, you first need to install and configure a Kerberos Agent. A Kerberos Agent is a piece of software that processes a device, and will make recordings when a portion of the video stream has changed. These recordings will then be send to, and processed by the Kerberos Hub application.",
|
||||
"k2": "After you've properly installed one or more Kerberos Agents, you can link it to the Kerberos Hub application using your",
|
||||
"k3": "cloud credentials",
|
||||
"k4": ". When linked successfully, your device will show up on the devices page.",
|
||||
"k5": "Get started",
|
||||
"k6": "We have different ways to get your Agent up and running. You can install the Agent using a prebuilt Docker container, inside your Kubernetes Cluster, on your Raspberry Pi (or any other compatible board) or on a simple VM. Please select the most appropriate solution to get started. Visit",
|
||||
"k6": "We have different ways to get your Kerberos Agent up and running. You can install the Kerberos Agent using a prebuilt Docker container, inside your Kubernetes Cluster, on your Raspberry Pi (or any other compatible board) or on a simple VM. Please select the most appropriate solution to get started. Visit",
|
||||
"k7": "our documentation website to learn more",
|
||||
"k8": "A Docker image (amd64, armv7, arm64) is",
|
||||
"k9": "available on the Docker Hub",
|
||||
@@ -1610,8 +1189,8 @@
|
||||
"k13": "Go to the plan page",
|
||||
"k14": "on your cloud account and copy your cloud credentials.",
|
||||
"k15": "When created successfully, you can navigate to the Kerberos Factory interface, and enter your cloud credentials in your deployment of global configuration.",
|
||||
"k16": "Your new Agent will show up on the devices page.",
|
||||
"k17": "If you want to manage Agents at scale with Kubernetes, then",
|
||||
"k16": "Your new Kerberos Agent will show up on the devices page.",
|
||||
"k17": "If you want to manage Kerberos Agents at scale with Kubernetes, then",
|
||||
"k18": "Kerberos Factory is what you are looking for",
|
||||
"k19": "Setup a Kubernetes cluster or use your existing Kubernetes cluster.",
|
||||
"k20": "Install the different components of",
|
||||
@@ -1620,7 +1199,7 @@
|
||||
"k23": "Go to the plans & integrations page",
|
||||
"k24": "on your Kerberos Hub account and copy your credentials.",
|
||||
"k25": "When configured Kerberos Factory successfully, you can modify the global settings, and enter your credentials.",
|
||||
"k26": "All Agent you will now add, will show up on your devices page.",
|
||||
"k26": "All Kerberos Agent you will now add, will show up on your devices page.",
|
||||
"k27": "KiOS is a custom linux OS, created for the Raspberry Pi, which runs Kerberos.io out-of-the-box. This is the most simple and basic installation procedure.",
|
||||
"k28": "Download",
|
||||
"k29": "the KiOS installer",
|
||||
@@ -2047,7 +1626,7 @@
|
||||
"k11": "Delete attachment",
|
||||
"k12": "Are you sure you want to delete this attachment? This action cannot be undone.",
|
||||
"k13": "Yes, delete",
|
||||
"k14": "Redact"
|
||||
"k14": "Redact faces"
|
||||
}
|
||||
},
|
||||
"media": {
|
||||
@@ -2107,7 +1686,7 @@
|
||||
"blocks": {
|
||||
"face_redaction": {
|
||||
"face_redaction": {
|
||||
"k1": "Redact video",
|
||||
"k1": "Edit video",
|
||||
"k2": "Edit video tools",
|
||||
"k3": "This editing tool is coming soon.",
|
||||
"k4": "No detections available",
|
||||
@@ -2123,7 +1702,9 @@
|
||||
"k14": "fps",
|
||||
"k15": "Detection run",
|
||||
"k16": "Select a detection run",
|
||||
"k17": "No detections"
|
||||
"k17": "No detections",
|
||||
"k18": "Automatic tracks unavailable",
|
||||
"k19": "Automatic face tracks are disabled. You can still draw redaction boxes manually on the player and submit."
|
||||
}
|
||||
},
|
||||
"motionmap": {
|
||||
@@ -2349,15 +1930,13 @@
|
||||
"k62": "Expiry date",
|
||||
"k63": "Open update access token",
|
||||
"k64": "No access tokens found",
|
||||
"k65": "time",
|
||||
"k65": "date, time",
|
||||
"k66": "ip",
|
||||
"k67": "device",
|
||||
"k68": "status",
|
||||
"k67": "browser",
|
||||
"k68": "os",
|
||||
"k69": "No login history found.",
|
||||
"k70": "My account",
|
||||
"k71": "Manage your profile, billing methods and invoices",
|
||||
"k72": "Success",
|
||||
"k73": "Failed"
|
||||
"k71": "Manage your profile, billing methods and invoices"
|
||||
}
|
||||
},
|
||||
"roles": {
|
||||
@@ -2566,9 +2145,9 @@
|
||||
"k20": "Kerberos Hub credentials",
|
||||
"k21": "Cloud credentials",
|
||||
"k22": "Media storage region",
|
||||
"k23": "Your media can be stored in one of the following regions. Different Agents can store in different regions.",
|
||||
"k23": "Your media can be stored in one of the following regions. Different Kerberos agents can store in different regions.",
|
||||
"k24": "Copy",
|
||||
"k25": "Use the below credentials to connect your Agents to your Kerberos Hub.",
|
||||
"k25": "Use the below credentials to connect your Kerberos Agents to your Kerberos Hub.",
|
||||
"k26": "Use the below credentials to connect your devices to our cloud.",
|
||||
"k27": "How to connect Agents to Kerberos Hub",
|
||||
"k28": "Public key",
|
||||
@@ -3007,7 +2586,7 @@
|
||||
"k2": "draft",
|
||||
"k3": "Device detail",
|
||||
"k4": "Kerberos Factory",
|
||||
"k5": "Agent",
|
||||
"k5": "Kerberos Agent",
|
||||
"k6": "n/a",
|
||||
"k7": "Remove device"
|
||||
}
|
||||
@@ -3243,7 +2822,7 @@
|
||||
"k2": "PREVIEW",
|
||||
"k3": "Switch to live [Shift + L]",
|
||||
"k4": "LIVE",
|
||||
"k5": "Make sure you are using the latest Agent and a Gold subscription",
|
||||
"k5": "Make sure you are using the latest Kerberos agent and a Gold subscription",
|
||||
"k6": "You are not allowed to watch a live stream.",
|
||||
"k7": "Turn sound off",
|
||||
"k8": "Turn sound on",
|
||||
@@ -3255,22 +2834,7 @@
|
||||
"k14": "Device Idle",
|
||||
"k15": "The agent is online, but the device stream seems to be offline",
|
||||
"k16": "No permissions",
|
||||
"k17": "You aren't allowed to view this livestream.",
|
||||
"k18": "Record",
|
||||
"k19": "Recording",
|
||||
"k20": "REC",
|
||||
"k21": "Stop recording",
|
||||
"k22": "Camera is set to continuous recording",
|
||||
"k23": "Show motion detection",
|
||||
"k24": "Hide motion detection",
|
||||
"k25": "Recent recordings",
|
||||
"k26": "No recordings available",
|
||||
"k27": "Back 5 seconds",
|
||||
"k28": "Play",
|
||||
"k29": "Pause",
|
||||
"k30": "Forward 5 seconds",
|
||||
"k31": "Previous time window",
|
||||
"k32": "Next time window"
|
||||
"k17": "You aren't allowed to view this livestream."
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
@@ -3330,13 +2894,6 @@
|
||||
"k3": "End time"
|
||||
}
|
||||
},
|
||||
"time_spinner": {
|
||||
"time_spinner": {
|
||||
"hours": "Hours",
|
||||
"minutes": "Minutes",
|
||||
"seconds": "Seconds"
|
||||
}
|
||||
},
|
||||
"video_js": {
|
||||
"video_js": {
|
||||
"k1": "To view this video please enable JavaScript, and consider upgrading to a web browser that",
|
||||
|
||||
@@ -2,15 +2,15 @@ Kerberos.io
|
||||
------------
|
||||
|
||||
Hey, {{user}}
|
||||
|
||||
One of your Kerberos Agents changed
|
||||
{{text}}
|
||||
|
||||
Agents go offline due to a variety of reasons. The machine, node, micro controller on which your Agent runs, gets corrupted or disconnected from the internet. The camera itself is broken, damaged or in the worst case tampered. Have a look into your Kerberos Hub account for the latest recordings and/or verify the connection and status of your Kerberos Agent.
|
||||
|
||||
Go to Kerberos Hub ->
|
||||
Welcome to Kerberos Hub
|
||||
Activate your account ->
|
||||
{{link}}
|
||||
|
||||
Kerberos Hub in a nutshell
|
||||
With Kerberos Hub you can access your surveillance media remotely. By subscribing to a plan, you get access to a set of features, from advanced filtering, notifications to machine learning.
|
||||
|
||||
However before you get started this amazing applications, please activate your account.
|
||||
|
||||
Get in touch
|
||||
------------
|
||||
support@kerberos.io
|
||||
|
||||
@@ -1,431 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1"
|
||||
/>
|
||||
<meta name="description" content="Kerberos.io Mailing">
|
||||
<style type="text/css">
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url("https://kerberos.io/dist/fonts/Inter-Regular.woff?v=/dist/fonts/Inter-Regular.woff2?v=3.183.18") format("woff2"),
|
||||
url("https://kerberos.io/dist/fonts/Inter-Regular.woff?v=/dist/fonts/Inter-Regular.woff2?v=3.183.18") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url("https://kerberos.io/dist/fonts/Inter-Medium.woff2?v=3.18") format("woff2"),
|
||||
url("https://kerberos.io/dist/fonts/Inter-Medium.woff?v=3.18") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url("https://kerberos.io/dist/fonts/Inter-SemiBold.woff2?v=3.18") format("woff2"),
|
||||
url("https://kerberos.io/dist/fonts/Inter-SemiBold.woff?v=3.18") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter var';
|
||||
font-weight: 100 900;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-named-instance: 'Regular';
|
||||
src: url("https://kerberos.io/dist/fonts/Inter-roman.var.woff2?v=3.18") format("woff2");
|
||||
}
|
||||
|
||||
body{
|
||||
background: #E5E5E5;
|
||||
margin-top:0;
|
||||
margin-bottom: 0;
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
padding-top: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
padding-bottom: 0;
|
||||
font-family: 'Inter';
|
||||
}
|
||||
a, a:hover, a:active {
|
||||
color: #262424;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.corner-td{
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
table {border-collapse:separate;max-width: 850px; margin: 0 auto; width: 100%;}
|
||||
.ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td {line-height: 100%;}
|
||||
.ExternalClass {width: 100%;}
|
||||
@media screen and (max-width:500px){
|
||||
.tab-td{
|
||||
padding-left: 10px!important;
|
||||
}
|
||||
.tab-td a h4{
|
||||
font-size: 14px!important;
|
||||
}
|
||||
.corner-td{
|
||||
width: 20px!important;
|
||||
}
|
||||
.company-name-td h3{
|
||||
font-size: 16px!important;
|
||||
}
|
||||
table.header-table{
|
||||
padding-top: 8px!important;
|
||||
padding-right: 0px!important;
|
||||
padding-bottom: 24px!important;
|
||||
padding-left: 0px!important;
|
||||
}
|
||||
.colored-card-td h4{
|
||||
font-size: 14px!important;
|
||||
}
|
||||
.colored-card-td h2{
|
||||
font-size: 20px!important;
|
||||
}
|
||||
.colored-card-td a p{
|
||||
font-size: 12px!important;
|
||||
width: 143px!important;
|
||||
}
|
||||
.colored-card-td{
|
||||
padding-top: 24px!important;
|
||||
padding-right: 24px!important;
|
||||
padding-bottom: 24px!important;
|
||||
padding-left: 24px!important;
|
||||
}
|
||||
.colorless-card-td{
|
||||
padding-top: 24px!important;
|
||||
padding-right: 24px!important;
|
||||
padding-bottom: 24px!important;
|
||||
padding-left: 24px!important;
|
||||
}
|
||||
.colorless-card-td h3{
|
||||
font-size: 18px!important;
|
||||
}
|
||||
.colorless-card-td p{
|
||||
font-size: 14px!important;
|
||||
}
|
||||
.colorless-card-table{
|
||||
margin-left: 0px!important;
|
||||
margin-right: 0px!important;
|
||||
margin-top: 24px!important;
|
||||
margin-bottom: 24px!important;
|
||||
}
|
||||
.footer-td{
|
||||
display: table-row!important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width:600px) {
|
||||
.footer-td{
|
||||
display: table-row!important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width:650px) {
|
||||
.footer-table{
|
||||
margin-left: 0px!important;
|
||||
margin-right: 0px!important;
|
||||
margin-top: 0px!important;
|
||||
margin-bottom: 36px!important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body height="100%" width="100%">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="E5E5E5" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tr>
|
||||
<td bgcolor="E5E5E5">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" height="36" class="header-table" style="padding-top: 36px ;padding-right: 0;padding-bottom: 36px;padding-left: 0;border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="corner-td" align="left"></td>
|
||||
<td width="48" height="36" align="left"><img alt="Kerberos.io" width="36" height="36" src="https://kerberos.io/images/email/kerberos.png"/></td>
|
||||
<td height="36" align="left" class="company-name-td">
|
||||
<h3 width="36" height="36" style=" font-family: Inter;
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 24px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color: #262424;">Kerberos.io</h3>
|
||||
</td>
|
||||
<td height="36" width="36" style="padding-left: 36px;" class="tab-td" align="right">
|
||||
<a style="text-decoration: none;color: none;" href={{tab1_href}}>
|
||||
<h4 style="font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 36px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: right;
|
||||
color: #6D6666;">{{tab1_title}}</h4>
|
||||
</a>
|
||||
</td>
|
||||
<td height="36" width="36" style="padding-left: 36px;" class="tab-td" align="right" >
|
||||
<a style="text-decoration: none;color: none;" href={{tab2_href}}>
|
||||
<h4 style="font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 36px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: right;
|
||||
color: #6D6666;">{{tab2_title}}</h4>
|
||||
</a>
|
||||
</td>
|
||||
<td class="corner-td" align="right"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="E5E5E5" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tr>
|
||||
<td bgcolor="E5E5E5">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;" >
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="corner-td" align="left"></td>
|
||||
<td class="colored-card-td" bgcolor="#57356B" style="padding-left: 48px;padding-right: 48px;padding-top: 48px;padding-bottom: 48px;border-radius: 4px;background-color:#57356B;">
|
||||
<h4 style="font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color:#b09fb9;">Hey, {{user}}</h4>
|
||||
<h2 style=" font-family: Inter;
|
||||
font-size: 24px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 36px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color:#FFFFFF;
|
||||
padding-top: 12px;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;">Several of your cameras changed status</h2>
|
||||
<h2 style=" font-family: Inter;
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 36px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color:#FFFFFF;
|
||||
padding-top: 0;
|
||||
padding-bottom: 24px;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
color:#b09fb9;">{{text}}</h2>
|
||||
</td>
|
||||
<td class="corner-td" align="right"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="E5E5E5" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tr>
|
||||
<td bgcolor="E5E5E5">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="colorless-card-table" style="margin-top: 36px;margin-bottom: 36px;margin-left: 0;margin-right: 0;border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;" >
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="corner-td" align="left"></td>
|
||||
<td class="colorless-card-td" bgcolor="#FFFFFF" style="background-color:#FFFFFF;padding-top: 36px;padding-right: 43px;padding-bottom: 25px;padding-left: 43px;border-radius: 4px;">
|
||||
<h3 style=" font-family: Inter;
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 36px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color: #262424;
|
||||
width: 480px">What changed</h3>
|
||||
{{sections}}
|
||||
<p style=" font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color: #6D6666;
|
||||
margin-top: 12px;">Cameras that change status together usually share a cause, such as a site losing or regaining its internet connection or power. Have a look into your Kerberos Hub account for the latest recordings and the status of your cameras.</p>
|
||||
|
||||
<a style="text-decoration: none;color: none;" href="{{link}}">
|
||||
<p style="font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
line-height: 24px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color:#FFFFFF;
|
||||
background-color: #84559F;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
padding-right: 16px;
|
||||
padding-left: 16px;
|
||||
width: 160px;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
cursor: pointer;">Go to Kerberos Hub -></p>
|
||||
</a>
|
||||
</td>
|
||||
<td class="corner-td" align="right"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="E5E5E5" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tr>
|
||||
<td bgcolor="E5E5E5">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="footer-table" style="margin-top: 0;border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;" >
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="corner-td" align="left"></td>
|
||||
<!--[if mso | IE]>
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td style="vertical-align:top;display:table-row !important">
|
||||
<![endif]-->
|
||||
<td height="146" width="190" class="footer-td" style="margin-bottom: 12px;" valign="top" align="left">
|
||||
<table style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<h4 style=" font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 36px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color:#6D6666;">Get in touch</h4>
|
||||
<a style="text-decoration: none;color: none;" href="mailto:support@verstraeten.io">
|
||||
<p style=" font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color: #A69D9D;">support@kerberos.io</p>
|
||||
</a>
|
||||
<p style=" font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color: #A69D9D;">9000 Ghent, BE</p>
|
||||
|
||||
<a style="text-decoration: none;color: none;" href="https://kerberos.io/">
|
||||
<p style=" font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color: #A69D9D;">https://kerberos.io</p>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
<!--[if mso | IE]>
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td style="vertical-align:top;display:table-row !important">
|
||||
<![endif]-->
|
||||
<td class="footer-td" style="border-radius: 4px;padding-left: 0;padding-right: 0;padding-top: 0;padding-bottom: 0; margin-bottom: 12px;" valign="top" align="left">
|
||||
<table style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<h4 style=" font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 36px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color:#6D6666;">About Kerberos</h4>
|
||||
<p style=" font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color: #A69D9D;">Welcome to the revolutionary video analytics and video management platform. Open, modular, and extensible for everyone, anywhere.</p>
|
||||
|
||||
|
||||
<p style="margin-top: 12px;">
|
||||
<a href="https://twitter.com/kerberosio" style="text-decoration: none;color: none;">
|
||||
<img width="24" height="24" alt="Twitter" src="https://kerberos.io/images/email/twitter.png"/>
|
||||
</a>
|
||||
<a href="https://reddit.com/r/kerberos_io" style="text-decoration: none;color: none;">
|
||||
<img g width="24" height="24" alt="Reddit" src="https://kerberos.io/images/email/reddit.png"/>
|
||||
</a>
|
||||
<a href="https://www.youtube.com/channel/UCnd9q7iRNNw4W95eQwQuECA" style="text-decoration: none;color: none;">
|
||||
<img g width="24" height="24" alt="Youtube" src="https://kerberos.io/images/email/youtube.png"/>
|
||||
</a>
|
||||
<a href="https://github.com/kerberos-io" style="text-decoration: none;color: none;">
|
||||
<img g width="24" height="24" alt="Github" src="https://kerberos.io/images/email/github.png"/>
|
||||
</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
<td class="corner-td" align="right"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tbody>
|
||||
<tr style="height: 50px">
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,22 +0,0 @@
|
||||
Kerberos.io
|
||||
------------
|
||||
|
||||
Hey, {{user}}
|
||||
|
||||
Several of your cameras changed status
|
||||
{{text}}
|
||||
|
||||
{{sectionsText}}
|
||||
|
||||
Go to Kerberos Hub ->
|
||||
{{link}}
|
||||
|
||||
Get in touch
|
||||
------------
|
||||
support@kerberos.io
|
||||
9000 Ghent, BE
|
||||
https://kerberos.io
|
||||
|
||||
About Kerberos
|
||||
------------
|
||||
Welcome to the revolutionary video analytics and video management platform. Open, modular, and extensible for everyone, anywhere.
|
||||
@@ -1,431 +0,0 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1"
|
||||
/>
|
||||
<meta name="description" content="Kerberos.io Mailing">
|
||||
<style type="text/css">
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url("https://kerberos.io/dist/fonts/Inter-Regular.woff?v=/dist/fonts/Inter-Regular.woff2?v=3.183.18") format("woff2"),
|
||||
url("https://kerberos.io/dist/fonts/Inter-Regular.woff?v=/dist/fonts/Inter-Regular.woff2?v=3.183.18") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url("https://kerberos.io/dist/fonts/Inter-Medium.woff2?v=3.18") format("woff2"),
|
||||
url("https://kerberos.io/dist/fonts/Inter-Medium.woff?v=3.18") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url("https://kerberos.io/dist/fonts/Inter-SemiBold.woff2?v=3.18") format("woff2"),
|
||||
url("https://kerberos.io/dist/fonts/Inter-SemiBold.woff?v=3.18") format("woff");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Inter var';
|
||||
font-weight: 100 900;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
font-named-instance: 'Regular';
|
||||
src: url("https://kerberos.io/dist/fonts/Inter-roman.var.woff2?v=3.18") format("woff2");
|
||||
}
|
||||
|
||||
body{
|
||||
background: #E5E5E5;
|
||||
margin-top:0;
|
||||
margin-bottom: 0;
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
padding-top: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
padding-bottom: 0;
|
||||
font-family: 'Inter';
|
||||
}
|
||||
a, a:hover, a:active {
|
||||
color: #262424;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.corner-td{
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
table {border-collapse:separate;max-width: 850px; margin: 0 auto; width: 100%;}
|
||||
.ExternalClass p, .ExternalClass span, .ExternalClass font, .ExternalClass td {line-height: 100%;}
|
||||
.ExternalClass {width: 100%;}
|
||||
@media screen and (max-width:500px){
|
||||
.tab-td{
|
||||
padding-left: 10px!important;
|
||||
}
|
||||
.tab-td a h4{
|
||||
font-size: 14px!important;
|
||||
}
|
||||
.corner-td{
|
||||
width: 20px!important;
|
||||
}
|
||||
.company-name-td h3{
|
||||
font-size: 16px!important;
|
||||
}
|
||||
table.header-table{
|
||||
padding-top: 8px!important;
|
||||
padding-right: 0px!important;
|
||||
padding-bottom: 24px!important;
|
||||
padding-left: 0px!important;
|
||||
}
|
||||
.colored-card-td h4{
|
||||
font-size: 14px!important;
|
||||
}
|
||||
.colored-card-td h2{
|
||||
font-size: 20px!important;
|
||||
}
|
||||
.colored-card-td a p{
|
||||
font-size: 12px!important;
|
||||
width: 143px!important;
|
||||
}
|
||||
.colored-card-td{
|
||||
padding-top: 24px!important;
|
||||
padding-right: 24px!important;
|
||||
padding-bottom: 24px!important;
|
||||
padding-left: 24px!important;
|
||||
}
|
||||
.colorless-card-td{
|
||||
padding-top: 24px!important;
|
||||
padding-right: 24px!important;
|
||||
padding-bottom: 24px!important;
|
||||
padding-left: 24px!important;
|
||||
}
|
||||
.colorless-card-td h3{
|
||||
font-size: 18px!important;
|
||||
}
|
||||
.colorless-card-td p{
|
||||
font-size: 14px!important;
|
||||
}
|
||||
.colorless-card-table{
|
||||
margin-left: 0px!important;
|
||||
margin-right: 0px!important;
|
||||
margin-top: 24px!important;
|
||||
margin-bottom: 24px!important;
|
||||
}
|
||||
.footer-td{
|
||||
display: table-row!important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width:600px) {
|
||||
.footer-td{
|
||||
display: table-row!important;
|
||||
}
|
||||
}
|
||||
@media screen and (max-width:650px) {
|
||||
.footer-table{
|
||||
margin-left: 0px!important;
|
||||
margin-right: 0px!important;
|
||||
margin-top: 0px!important;
|
||||
margin-bottom: 36px!important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body height="100%" width="100%">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="E5E5E5" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tr>
|
||||
<td bgcolor="E5E5E5">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" height="36" class="header-table" style="padding-top: 36px ;padding-right: 0;padding-bottom: 36px;padding-left: 0;border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="corner-td" align="left"></td>
|
||||
<td width="48" height="36" align="left"><img alt="Kerberos.io" width="36" height="36" src="https://kerberos.io/images/email/kerberos.png"/></td>
|
||||
<td height="36" align="left" class="company-name-td">
|
||||
<h3 width="36" height="36" style=" font-family: Inter;
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 24px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color: #262424;">Kerberos.io</h3>
|
||||
</td>
|
||||
<td height="36" width="36" style="padding-left: 36px;" class="tab-td" align="right">
|
||||
<a style="text-decoration: none;color: none;" href={{tab1_href}}>
|
||||
<h4 style="font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 36px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: right;
|
||||
color: #6D6666;">{{tab1_title}}</h4>
|
||||
</a>
|
||||
</td>
|
||||
<td height="36" width="36" style="padding-left: 36px;" class="tab-td" align="right" >
|
||||
<a style="text-decoration: none;color: none;" href={{tab2_href}}>
|
||||
<h4 style="font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 36px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: right;
|
||||
color: #6D6666;">{{tab2_title}}</h4>
|
||||
</a>
|
||||
</td>
|
||||
<td class="corner-td" align="right"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="E5E5E5" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tr>
|
||||
<td bgcolor="E5E5E5">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;" >
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="corner-td" align="left"></td>
|
||||
<td class="colored-card-td" bgcolor="#57356B" style="padding-left: 48px;padding-right: 48px;padding-top: 48px;padding-bottom: 48px;border-radius: 4px;background-color:#57356B;">
|
||||
<h4 style="font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color:#b09fb9;">Hey, {{user}}</h4>
|
||||
<h2 style=" font-family: Inter;
|
||||
font-size: 24px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 36px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color:#FFFFFF;
|
||||
padding-top: 12px;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;">Camera recordings</h2>
|
||||
<h2 style=" font-family: Inter;
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 36px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color:#FFFFFF;
|
||||
padding-top: 0;
|
||||
padding-bottom: 24px;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
color:#b09fb9;">{{text}}</h2>
|
||||
</td>
|
||||
<td class="corner-td" align="right"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="E5E5E5" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tr>
|
||||
<td bgcolor="E5E5E5">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="colorless-card-table" style="margin-top: 36px;margin-bottom: 36px;margin-left: 0;margin-right: 0;border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;" >
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="corner-td" align="left"></td>
|
||||
<td class="colorless-card-td" bgcolor="#FFFFFF" style="background-color:#FFFFFF;padding-top: 36px;padding-right: 43px;padding-bottom: 25px;padding-left: 43px;border-radius: 4px;">
|
||||
<h3 style=" font-family: Inter;
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 36px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color: #262424;
|
||||
width: 480px">Cameras</h3>
|
||||
{{sections}}
|
||||
<p style=" font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color: #6D6666;
|
||||
margin-top: 12px;">These cameras record continuously, so a new recording normally arrives every few minutes. When a camera is online but its recordings stop arriving, check that the account has not reached its upload limit, that the storage the recordings are sent to is reachable and not full, and that the Agent's cloud settings and credentials are still valid. Cameras that are recording again need no action.</p>
|
||||
|
||||
<a style="text-decoration: none;color: none;" href="{{link}}">
|
||||
<p style="font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
line-height: 24px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color:#FFFFFF;
|
||||
background-color: #84559F;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
padding-right: 16px;
|
||||
padding-left: 16px;
|
||||
width: 160px;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
cursor: pointer;">Go to Kerberos Hub -></p>
|
||||
</a>
|
||||
</td>
|
||||
<td class="corner-td" align="right"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" bgcolor="E5E5E5" style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tr>
|
||||
<td bgcolor="E5E5E5">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="footer-table" style="margin-top: 0;border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;" >
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="corner-td" align="left"></td>
|
||||
<!--[if mso | IE]>
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td style="vertical-align:top;display:table-row !important">
|
||||
<![endif]-->
|
||||
<td height="146" width="190" class="footer-td" style="margin-bottom: 12px;" valign="top" align="left">
|
||||
<table style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<h4 style=" font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 36px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color:#6D6666;">Get in touch</h4>
|
||||
<a style="text-decoration: none;color: none;" href="mailto:support@verstraeten.io">
|
||||
<p style=" font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color: #A69D9D;">support@kerberos.io</p>
|
||||
</a>
|
||||
<p style=" font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 16px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color: #A69D9D;">9000 Ghent, BE</p>
|
||||
|
||||
<a style="text-decoration: none;color: none;" href="https://kerberos.io/">
|
||||
<p style=" font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color: #A69D9D;">https://kerberos.io</p>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
<!--[if mso | IE]>
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td style="vertical-align:top;display:table-row !important">
|
||||
<![endif]-->
|
||||
<td class="footer-td" style="border-radius: 4px;padding-left: 0;padding-right: 0;padding-top: 0;padding-bottom: 0; margin-bottom: 12px;" valign="top" align="left">
|
||||
<table style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<h4 style=" font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 36px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color:#6D6666;">About Kerberos</h4>
|
||||
<p style=" font-family: Inter;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
mso-line-height-rule:exactly;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
color: #A69D9D;">Welcome to the revolutionary video analytics and video management platform. Open, modular, and extensible for everyone, anywhere.</p>
|
||||
|
||||
|
||||
<p style="margin-top: 12px;">
|
||||
<a href="https://twitter.com/kerberosio" style="text-decoration: none;color: none;">
|
||||
<img width="24" height="24" alt="Twitter" src="https://kerberos.io/images/email/twitter.png"/>
|
||||
</a>
|
||||
<a href="https://reddit.com/r/kerberos_io" style="text-decoration: none;color: none;">
|
||||
<img g width="24" height="24" alt="Reddit" src="https://kerberos.io/images/email/reddit.png"/>
|
||||
</a>
|
||||
<a href="https://www.youtube.com/channel/UCnd9q7iRNNw4W95eQwQuECA" style="text-decoration: none;color: none;">
|
||||
<img g width="24" height="24" alt="Youtube" src="https://kerberos.io/images/email/youtube.png"/>
|
||||
</a>
|
||||
<a href="https://github.com/kerberos-io" style="text-decoration: none;color: none;">
|
||||
<img g width="24" height="24" alt="Github" src="https://kerberos.io/images/email/github.png"/>
|
||||
</a>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
<td class="corner-td" align="right"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table style="border-collaps:collaps; mso-table-lspace:0pt; mso-table-rspace:0pt;">
|
||||
<tbody>
|
||||
<tr style="height: 50px">
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,24 +0,0 @@
|
||||
Kerberos.io
|
||||
------------
|
||||
|
||||
Hey, {{user}}
|
||||
|
||||
Camera recordings
|
||||
{{text}}
|
||||
|
||||
{{sectionsText}}
|
||||
|
||||
These cameras record continuously, so a new recording normally arrives every few minutes. When a camera is online but its recordings stop arriving, check that the account has not reached its upload limit, that the storage the recordings are sent to is reachable and not full, and that the Agent's cloud settings and credentials are still valid. Cameras that are recording again need no action.
|
||||
|
||||
Go to Kerberos Hub ->
|
||||
{{link}}
|
||||
|
||||
Get in touch
|
||||
------------
|
||||
support@kerberos.io
|
||||
9000 Ghent, BE
|
||||
https://kerberos.io
|
||||
|
||||
About Kerberos
|
||||
------------
|
||||
Welcome to the revolutionary video analytics and video management platform. Open, modular, and extensible for everyone, anywhere.
|
||||
@@ -1,5 +0,0 @@
|
||||
[
|
||||
{"key": "nose", "label": "Nose"},
|
||||
{"key": "left_shoulder", "label": "Left shoulder"},
|
||||
{"key": "right_shoulder", "label": "Right shoulder"}
|
||||
]
|
||||
@@ -1,24 +0,0 @@
|
||||
# Create these ConfigMaps in the release namespace before deploying the chart.
|
||||
# Keys contain complete contracts/catalogs; they are not merged with chart data.
|
||||
# Do not also set classificationCatalog or classificationCatalogExistingConfigMap.
|
||||
classificationCatalogEnabled: true
|
||||
workflowConfiguration:
|
||||
enabled: true
|
||||
includeDefaults: true
|
||||
configMaps:
|
||||
- name: deployment-workflow-overrides
|
||||
items:
|
||||
- key: start.yaml
|
||||
path: workflow-contracts/start.yaml
|
||||
replace: true
|
||||
- key: deployment-classifications.json
|
||||
path: catalogs/classifications.json
|
||||
replace: true
|
||||
- name: custom-workflow-contracts
|
||||
items:
|
||||
- key: pose.yaml
|
||||
path: workflow-contracts/pose.yaml
|
||||
- name: shared-pose-catalog
|
||||
items:
|
||||
- key: pose-keypoints.json
|
||||
path: catalogs/pose-keypoints.json
|
||||
@@ -1,13 +0,0 @@
|
||||
# Packaging only; does not activate contract loading or deploy a pose worker.
|
||||
workflowConfiguration:
|
||||
enabled: true
|
||||
includeDefaults: true
|
||||
configMaps:
|
||||
- name: custom-workflow-contracts
|
||||
items:
|
||||
- key: pose.yaml
|
||||
path: workflow-contracts/pose.yaml
|
||||
- name: shared-pose-catalog
|
||||
items:
|
||||
- key: pose-keypoints.json
|
||||
path: catalogs/pose-keypoints.json
|
||||
@@ -1,53 +0,0 @@
|
||||
# yaml-language-server: $schema=../../schemas/workflow-contract.schema.json
|
||||
# Hypothetical custom worker output; not an installed stage or universal pose schema.
|
||||
schemaVersion: 1
|
||||
stage: pose
|
||||
contractVersion: 1
|
||||
presentation:
|
||||
name: Detect poses
|
||||
description: Estimate body keypoints and posture for people in the recording.
|
||||
icon: person
|
||||
color: teal
|
||||
# Settings authors enter per node; the worker receives them as node data.
|
||||
params:
|
||||
- name: minConfidence
|
||||
label: Minimum keypoint confidence
|
||||
type: number
|
||||
default: 0.5
|
||||
- name: model
|
||||
label: Model
|
||||
type: select
|
||||
options: [fast, accurate]
|
||||
optionLabels: {fast: Fast, accurate: Accurate}
|
||||
default: fast
|
||||
fields:
|
||||
- id: keypoint
|
||||
label: Keypoint
|
||||
path: people.*.keypoints.*.name
|
||||
type: string
|
||||
operators: [in]
|
||||
choices:
|
||||
file: ../catalogs/pose-keypoints.json
|
||||
- id: posture
|
||||
label: Posture
|
||||
path: people.*.posture
|
||||
type: string
|
||||
operators: [in]
|
||||
choices:
|
||||
values: [standing, sitting, lying]
|
||||
- id: confidence
|
||||
label: Keypoint confidence
|
||||
path: people.*.keypoints.*.confidence
|
||||
type: number
|
||||
operators: [gt, gte, lt, lte]
|
||||
minimum: 0
|
||||
maximum: 1
|
||||
# Connections leaving this stage: one section with fixed inputs, then the
|
||||
# Extra conditions catch-all (appended collapsed when not placed explicitly).
|
||||
panel:
|
||||
sections:
|
||||
- id: posture
|
||||
label: Posture
|
||||
fields:
|
||||
- {field: posture, widget: multiselect}
|
||||
- {field: confidence, widget: number, operator: gte, label: Minimum confidence}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Hub shared choice catalog v1",
|
||||
"description": "Compatible with the existing classification JSON format. Keys must be unique; no references to other catalogs.",
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["key", "label"],
|
||||
"properties": {
|
||||
"key": {"type": "string", "minLength": 1},
|
||||
"label": {"type": "string", "minLength": 1},
|
||||
"icon": {"type": "string", "minLength": 1}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,321 +0,0 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Hub workflow editor contract v1",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schemaVersion", "stage", "contractVersion", "fields"],
|
||||
"properties": {
|
||||
"schemaVersion": {"const": 1},
|
||||
"stage": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z][a-z0-9-]*$",
|
||||
"description": "Stage type, not a node instance. start describes initial context."
|
||||
},
|
||||
"contractVersion": {"const": 1},
|
||||
"presentation": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"description": "How the editor shows this node. Icons and colours come from approved sets.",
|
||||
"properties": {
|
||||
"name": {"type": "string", "minLength": 1, "maxLength": 80},
|
||||
"description": {"type": "string", "minLength": 1, "maxLength": 500},
|
||||
"icon": {"enum": ["play", "camera", "person", "eye-crossed", "car", "clock", "mlModel", "share"]},
|
||||
"color": {
|
||||
"enum": ["blue", "teal", "rose", "amber", "indigo", "violet", "green", "grey"],
|
||||
"description": "Theme colour token."
|
||||
}
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"type": "array",
|
||||
"maxItems": 64,
|
||||
"description": "Worker-stage settings authors enter, stored per node. An explicit list (even empty) replaces kerberoshub.workflows.editorStages params. Not supported on start.",
|
||||
"items": {"$ref": "#/definitions/param"}
|
||||
},
|
||||
"fields": {
|
||||
"type": "array",
|
||||
"description": "Typed values conditions may test. Start: the selectors device, sites, groups and classifications plus condition-only fields whose path exists in the condition root schema (models.WorkflowConditionRootSchema), e.g. device.deviceName or inputs.classify.details.*.isStatic; other inputs.<operation> paths are worker-defined. Stage: outputs relative to results.<stage>. New conditions may only use declared fields. An empty array is valid.",
|
||||
"items": {"$ref": "#/definitions/field"},
|
||||
"maxItems": 128
|
||||
},
|
||||
"controls": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["id", "label"],
|
||||
"properties": {
|
||||
"id": {"enum": ["weeklySchedule", "conditions"]},
|
||||
"label": {"type": "string", "minLength": 1}
|
||||
}
|
||||
},
|
||||
"description": "Start only. Controls offered for new authoring; omitted offers none. conditions applies to edges leaving Start.",
|
||||
"maxItems": 16
|
||||
},
|
||||
"triggerModes": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"uniqueItems": true,
|
||||
"items": {"enum": ["automatic", "manual"]},
|
||||
"description": "Start only. Trigger modes offered for new authoring; omitted offers both."
|
||||
},
|
||||
"panel": {"$ref": "#/definitions/panel"}
|
||||
},
|
||||
"definitions": {
|
||||
"choices": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"source": {
|
||||
"type": "string",
|
||||
"description": "Built-in authorized project-resource resolver; not a database path.",
|
||||
"oneOf": [
|
||||
{
|
||||
"const": "project.devices",
|
||||
"description": "Device keys as values, device names as labels."
|
||||
},
|
||||
{"const": "project.sites", "description": "Site IDs as values, site names as labels."},
|
||||
{
|
||||
"const": "project.groups",
|
||||
"description": "Group IDs as values, group names as labels."
|
||||
}
|
||||
]
|
||||
},
|
||||
"file": {
|
||||
"type": "string",
|
||||
"pattern": "^\\.\\./catalogs/[A-Za-z0-9_-][A-Za-z0-9._-]*\\.json$",
|
||||
"description": "Direct shared catalog file relative to the contract; no URLs or nested references."
|
||||
},
|
||||
"values": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"uniqueItems": true,
|
||||
"items": {"type": ["string", "number", "boolean"]}
|
||||
}
|
||||
},
|
||||
"oneOf": [{"required": ["source"]}, {"required": ["file"]}, {"required": ["values"]}]
|
||||
},
|
||||
"field": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["id", "label", "path", "type", "operators"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^[A-Za-z][A-Za-z0-9_-]*$",
|
||||
"description": "Stable identifier, unique within this contract."
|
||||
},
|
||||
"label": {"type": "string", "minLength": 1},
|
||||
"path": {
|
||||
"type": "string",
|
||||
"pattern": "^[A-Za-z_][A-Za-z0-9_-]*(\\.(\\*|[A-Za-z_][A-Za-z0-9_-]*))*$",
|
||||
"description": "Safe Start-context path, or relative path inside this node instance's output. Wildcards select scalar candidates."
|
||||
},
|
||||
"type": {"enum": ["string", "integer", "number", "boolean"]},
|
||||
"operators": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"uniqueItems": true,
|
||||
"items": {"enum": ["eq", "ne", "in", "exists", "contains", "matches", "gt", "gte", "lt", "lte"]}
|
||||
},
|
||||
"optional": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Field may be absent even when the node completes; branch availability is determined separately."
|
||||
},
|
||||
"minimum": {"type": "number"},
|
||||
"maximum": {"type": "number"},
|
||||
"choices": {"$ref": "#/definitions/choices"}
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"if": {"properties": {"type": {"const": "string"}}},
|
||||
"then": {
|
||||
"properties": {
|
||||
"operators": {"items": {"enum": ["eq", "ne", "in", "exists", "contains", "matches"]}},
|
||||
"choices": {"properties": {"values": {"items": {"type": "string"}}}}
|
||||
},
|
||||
"not": {"anyOf": [{"required": ["minimum"]}, {"required": ["maximum"]}]}
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {"properties": {"type": {"const": "boolean"}}},
|
||||
"then": {
|
||||
"properties": {
|
||||
"operators": {"items": {"enum": ["eq", "ne", "in", "exists"]}},
|
||||
"choices": {"properties": {"values": {"items": {"type": "boolean"}}}}
|
||||
},
|
||||
"not": {"anyOf": [{"required": ["minimum"]}, {"required": ["maximum"]}]}
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {"properties": {"type": {"enum": ["integer", "number"]}}},
|
||||
"then": {
|
||||
"properties": {
|
||||
"operators": {"items": {"enum": ["eq", "ne", "in", "exists", "gt", "gte", "lt", "lte"]}},
|
||||
"choices": {"properties": {"values": {"items": {"type": "number"}}}}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {"properties": {"type": {"const": "integer"}}},
|
||||
"then": {"properties": {"choices": {"properties": {"values": {"items": {"type": "integer"}}}}}}
|
||||
},
|
||||
{
|
||||
"if": {"properties": {"type": {"enum": ["integer", "number", "boolean"]}}},
|
||||
"then": {
|
||||
"properties": {"choices": {"not": {"anyOf": [{"required": ["file"]}, {"required": ["source"]}]}}}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"param": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "type"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"pattern": "^[A-Za-z][A-Za-z0-9_]*$",
|
||||
"maxLength": 64,
|
||||
"description": "Key in node data; unique within the contract."
|
||||
},
|
||||
"label": {"type": "string", "minLength": 1, "maxLength": 160},
|
||||
"description": {"type": "string", "minLength": 1, "maxLength": 500},
|
||||
"group": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 80,
|
||||
"description": "Editor section; params sharing a group appear together."
|
||||
},
|
||||
"placeholder": {"type": "string", "minLength": 1, "maxLength": 160},
|
||||
"type": {"enum": ["string", "number", "boolean", "select", "multiselect", "secret"]},
|
||||
"required": {"type": "boolean"},
|
||||
"default": {
|
||||
"description": "Value the editor pre-fills and the API stores when a node leaves the setting unset; never for secrets. Typically the worker's own default."
|
||||
},
|
||||
"options": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 256,
|
||||
"uniqueItems": true,
|
||||
"items": {"type": "string", "minLength": 1, "maxLength": 160}
|
||||
},
|
||||
"optionLabels": {
|
||||
"type": "object",
|
||||
"additionalProperties": {"type": "string", "minLength": 1, "maxLength": 160}
|
||||
},
|
||||
"minimum": {"type": "number", "description": "Inclusive lower bound for a number setting."},
|
||||
"maximum": {"type": "number", "description": "Inclusive upper bound for a number setting."},
|
||||
"choices": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["file"],
|
||||
"properties": {
|
||||
"file": {"type": "string", "pattern": "^\\.\\./catalogs/[A-Za-z0-9_-][A-Za-z0-9._-]*\\.json$"}
|
||||
},
|
||||
"description": "Options for a select or multiselect setting from a shared catalog file (keys become options, labels become optionLabels). Use instead of options."
|
||||
}
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"if": {"properties": {"type": {"enum": ["select", "multiselect"]}}},
|
||||
"then": {"oneOf": [{"required": ["options"]}, {"required": ["choices"]}]},
|
||||
"else": {
|
||||
"not": {
|
||||
"anyOf": [{"required": ["options"]}, {"required": ["optionLabels"]}, {"required": ["choices"]}]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {"properties": {"type": {"const": "secret"}}},
|
||||
"then": {"not": {"required": ["default"]}}
|
||||
},
|
||||
{
|
||||
"if": {"properties": {"type": {"enum": ["string", "select"]}}},
|
||||
"then": {"properties": {"default": {"type": "string"}}}
|
||||
},
|
||||
{
|
||||
"if": {"properties": {"type": {"const": "multiselect"}}},
|
||||
"then": {
|
||||
"properties": {"default": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}}
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {"properties": {"type": {"const": "number"}}},
|
||||
"then": {"properties": {"default": {"type": "number"}}},
|
||||
"else": {"not": {"anyOf": [{"required": ["minimum"]}, {"required": ["maximum"]}]}}
|
||||
},
|
||||
{
|
||||
"if": {"properties": {"type": {"const": "boolean"}}},
|
||||
"then": {"properties": {"default": {"type": "boolean"}}}
|
||||
}
|
||||
]
|
||||
},
|
||||
"panel": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["sections"],
|
||||
"description": "Layout of connections leaving this node. Without it, a default panel is used. A section with picker: true positions the Extra conditions catch-all; without one it is appended collapsed. On start, a panel replaces the top-level controls list.",
|
||||
"properties": {
|
||||
"sections": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 16,
|
||||
"items": {"$ref": "#/definitions/panelSection"}
|
||||
}
|
||||
}
|
||||
},
|
||||
"panelSection": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["id", "label"],
|
||||
"properties": {
|
||||
"id": {"type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_-]*$", "maxLength": 80},
|
||||
"label": {"type": "string", "minLength": 1, "maxLength": 160},
|
||||
"collapsed": {"type": "boolean"},
|
||||
"picker": {
|
||||
"type": "boolean",
|
||||
"description": "This section is the Extra conditions catch-all over every declared field of Start and upstream stages; it holds no fields or controls."
|
||||
},
|
||||
"controls": {
|
||||
"type": "array",
|
||||
"items": {"const": "weeklySchedule"},
|
||||
"maxItems": 1,
|
||||
"description": "Start only."
|
||||
},
|
||||
"fields": {"type": "array", "maxItems": 32, "items": {"$ref": "#/definitions/panelField"}}
|
||||
},
|
||||
"if": {"properties": {"picker": {"const": true}}, "required": ["picker"]},
|
||||
"then": {"not": {"anyOf": [{"required": ["fields"]}, {"required": ["controls"]}]}}
|
||||
},
|
||||
"panelField": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["field", "widget"],
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string",
|
||||
"description": "ID of a field declared in this contract; each field is placed at most once."
|
||||
},
|
||||
"label": {"type": "string", "minLength": 1, "maxLength": 160},
|
||||
"widget": {
|
||||
"enum": ["multiselect", "select", "text", "number", "toggle"],
|
||||
"description": "multiselect: string with choices (in); select: choices (eq, ne); text: string (eq, contains, matches, ne, in); number: integer or number (gte, gt, lte, lt, eq, ne); toggle: boolean (eq, ne)."
|
||||
},
|
||||
"operator": {
|
||||
"enum": ["eq", "ne", "in", "contains", "matches", "gt", "gte", "lt", "lte"],
|
||||
"description": "Fixed operator; must be declared by the field and supported by the widget. Defaults to the first widget operator the field declares."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"if": {"properties": {"stage": {"const": "start"}}, "required": ["stage"]},
|
||||
"then": {"not": {"required": ["params"]}},
|
||||
"else": {"not": {"anyOf": [{"required": ["controls"]}, {"required": ["triggerModes"]}]}}
|
||||
},
|
||||
{"if": {"required": ["panel"]}, "then": {"not": {"required": ["controls"]}}}
|
||||
]
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
{{- define "hub.workflowConfiguration.bundledPaths" -}}
|
||||
{{- list "workflow-contracts/start.yaml" "workflow-contracts/anpr.yaml" "workflow-contracts/forwarder.yaml" "catalogs/classifications.json" | toYaml -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "hub.workflowConfiguration.anpr.configMapName" -}}
|
||||
{{- printf "%s-workflow-contract-anpr" (.Release.Name | trunc 40 | trimSuffix "-") -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "hub.workflowConfiguration.forwarder.configMapName" -}}
|
||||
{{- printf "%s-workflow-contract-forwarder" (.Release.Name | trunc 40 | trimSuffix "-") -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "hub.classificationCatalog.configMapName" -}}
|
||||
{{- $config := include "hub.workflowConfiguration.settings" . | fromYaml -}}
|
||||
{{- $replacement := index $config.replacements "catalogs/classifications.json" | default dict -}}
|
||||
{{- if $replacement -}}
|
||||
{{- $replacement.name -}}
|
||||
{{- else -}}
|
||||
{{- default "classification-catalog" .Values.classificationCatalogExistingConfigMap -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Normalize both interfaces without changing the caller's values. */}}
|
||||
{{- define "hub.workflowConfiguration.settings" -}}
|
||||
{{- $config := .Values.workflowConfiguration | default dict -}}
|
||||
{{- $unified := or (hasKey $config "includeDefaults") (hasKey $config "configMaps") -}}
|
||||
{{- $legacy := or (hasKey $config "existingConfigMap") (hasKey $config "items") (hasKey $config "extraConfigMaps") -}}
|
||||
{{- if and $unified $legacy -}}
|
||||
{{- fail "workflowConfiguration cannot mix includeDefaults/configMaps with existingConfigMap/items/extraConfigMaps" -}}
|
||||
{{- end -}}
|
||||
{{- $includeDefaults := true -}}
|
||||
{{- $sources := $config.extraConfigMaps | default list -}}
|
||||
{{- if $unified -}}
|
||||
{{- $sources = $config.configMaps | default list -}}
|
||||
{{- if hasKey $config "includeDefaults" -}}
|
||||
{{- $includeDefaults = $config.includeDefaults -}}
|
||||
{{- end -}}
|
||||
{{- else -}}
|
||||
{{- if $config.existingConfigMap -}}
|
||||
{{- if and $config.enabled (not $config.items) -}}
|
||||
{{- fail "workflowConfiguration.items is required with existingConfigMap" -}}
|
||||
{{- end -}}
|
||||
{{- $includeDefaults = false -}}
|
||||
{{- $sources = prepend $sources (dict "name" $config.existingConfigMap "items" ($config.items | default list)) -}}
|
||||
{{- else if and $config.enabled $config.items -}}
|
||||
{{- fail "workflowConfiguration.items requires existingConfigMap" -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- $enabled := $config.enabled | default false -}}
|
||||
{{- $replacements := dict -}}
|
||||
{{- $normalizedSources := list -}}
|
||||
{{- $bundledPaths := include "hub.workflowConfiguration.bundledPaths" . | fromYamlArray -}}
|
||||
{{- range $sources -}}
|
||||
{{- $name := .name -}}
|
||||
{{- $items := list -}}
|
||||
{{- range .items -}}
|
||||
{{- if and $enabled .replace -}}
|
||||
{{- if or (not $unified) (not $includeDefaults) -}}
|
||||
{{- fail "workflowConfiguration item replace: true requires configMaps with includeDefaults: true" -}}
|
||||
{{- end -}}
|
||||
{{- if not (has .path $bundledPaths) -}}
|
||||
{{- fail (printf "workflowConfiguration item replace: true targets unknown bundled path %q" .path) -}}
|
||||
{{- end -}}
|
||||
{{- if hasKey $replacements .path -}}
|
||||
{{- fail (printf "duplicate workflowConfiguration replacement path %q" .path) -}}
|
||||
{{- end -}}
|
||||
{{- $_ := set $replacements .path (dict "name" $name "key" .key) -}}
|
||||
{{- end -}}
|
||||
{{- $items = append $items (omit . "replace") -}}
|
||||
{{- end -}}
|
||||
{{- $normalizedSources = append $normalizedSources (dict "name" $name "items" $items) -}}
|
||||
{{- end -}}
|
||||
{{- if hasKey $replacements "catalogs/classifications.json" -}}
|
||||
{{- if .Values.classificationCatalogExistingConfigMap -}}
|
||||
{{- fail "a classifications.json replacement cannot be combined with classificationCatalogExistingConfigMap; choose one override mechanism" -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if not (kindIs "invalid" .Values.classificationCatalog) -}}
|
||||
{{- if not .Values.classificationCatalogEnabled -}}
|
||||
{{- fail "classificationCatalog requires classificationCatalogEnabled; remove the inline list to use the API fallback" -}}
|
||||
{{- end -}}
|
||||
{{- if .Values.classificationCatalogExistingConfigMap -}}
|
||||
{{- fail "classificationCatalog cannot be combined with classificationCatalogExistingConfigMap; choose one override mechanism" -}}
|
||||
{{- end -}}
|
||||
{{- if hasKey $replacements "catalogs/classifications.json" -}}
|
||||
{{- fail "classificationCatalog cannot be combined with a classifications.json replacement; choose one override mechanism" -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- dict "enabled" $enabled "includeDefaults" $includeDefaults "configMaps" $normalizedSources "replacements" $replacements | toYaml -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- define "hub.workflowConfiguration.validate" -}}
|
||||
{{- $config := include "hub.workflowConfiguration.settings" . | fromYaml -}}
|
||||
{{- if $config.enabled -}}
|
||||
{{- $paths := dict -}}
|
||||
{{- if $config.includeDefaults -}}
|
||||
{{- if not .Values.classificationCatalogEnabled -}}
|
||||
{{- fail "bundled workflowConfiguration requires classificationCatalogEnabled; disable includeDefaults and supply configMaps for a custom bundle" -}}
|
||||
{{- end -}}
|
||||
{{- range $path := (include "hub.workflowConfiguration.bundledPaths" . | fromYamlArray) -}}
|
||||
{{- if not (hasKey $config.replacements $path) -}}
|
||||
{{- $_ := set $paths $path true -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- else -}}
|
||||
{{- if not $config.configMaps -}}
|
||||
{{- fail "workflowConfiguration.configMaps must be nonempty when enabled without defaults" -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- range $config.configMaps -}}
|
||||
{{- if not .items -}}
|
||||
{{- fail (printf "workflowConfiguration ConfigMap %q requires nonempty items" .name) -}}
|
||||
{{- end -}}
|
||||
{{- range .items -}}
|
||||
{{- if not (regexMatch "^[A-Za-z0-9._-]+$" .key) -}}
|
||||
{{- fail "workflowConfiguration.items keys must be valid ConfigMap keys" -}}
|
||||
{{- end -}}
|
||||
{{- if or (not (regexMatch "^(workflow-contracts|catalogs)/[A-Za-z0-9_-][A-Za-z0-9._-]*\\.(yaml|json)$" .path)) (ne (clean .path) .path) -}}
|
||||
{{- fail "workflowConfiguration.items paths must be normalized relative files under workflow-contracts/ or catalogs/" -}}
|
||||
{{- end -}}
|
||||
{{- if hasKey $paths .path -}}
|
||||
{{- fail (printf "duplicate workflowConfiguration.items path %q" .path) -}}
|
||||
{{- end -}}
|
||||
{{- $_ := set $paths .path true -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
@@ -1,51 +0,0 @@
|
||||
{{/*
|
||||
The system tag catalog (deviceTags.system.catalog) uses the Hub shared choice
|
||||
catalog format: {key, label, description?}. values.schema.json validates each
|
||||
entry (key in stored device tag form, models.NormalizeDeviceTags: 1-64
|
||||
characters of a-z, 0-9, ".", "_", ":" and "-", starting and ending with a
|
||||
letter or digit) and that enabled is a boolean; keys must also be unique.
|
||||
*/}}
|
||||
{{- define "kerberoshub.deviceTags.system.validate" -}}
|
||||
{{- $seen := dict -}}
|
||||
{{- range $tag := (include "kerberoshub.deviceTags.system.catalog" . | fromJsonArray) -}}
|
||||
{{- $key := toString $tag.key -}}
|
||||
{{- if hasKey $seen $key -}}
|
||||
{{- fail (printf "deviceTags.system.catalog: duplicate key %q" $key) -}}
|
||||
{{- end -}}
|
||||
{{- $_ := set $seen $key true -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
The configured system tag catalog as a JSON array, whether or not system tags
|
||||
are enabled (missing values read as empty).
|
||||
*/}}
|
||||
{{- define "kerberoshub.deviceTags.system.catalog" -}}
|
||||
{{- $deviceTags := .Values.deviceTags | default dict -}}
|
||||
{{- $system := $deviceTags.system | default dict -}}
|
||||
{{- $system.catalog | default list | toJson -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Whether system tags are enabled (deviceTags.system.enabled): "true" or "".
|
||||
*/}}
|
||||
{{- define "kerberoshub.deviceTags.system.enabled" -}}
|
||||
{{- $deviceTags := .Values.deviceTags | default dict -}}
|
||||
{{- $system := $deviceTags.system | default dict -}}
|
||||
{{- if and (kindIs "bool" $system.enabled) $system.enabled -}}true{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
The system tag catalog file hub-api reads (DEVICE_SYSTEM_TAGS_FILE), rendered
|
||||
from deviceTags.system.catalog into the device-system-tags ConfigMap.
|
||||
*/}}
|
||||
{{- define "kerberoshub.deviceTags.system.json" -}}
|
||||
{{- include "kerberoshub.deviceTags.system.validate" . -}}
|
||||
{{- $tags := list -}}
|
||||
{{- range $tag := (include "kerberoshub.deviceTags.system.catalog" . | fromJsonArray) -}}
|
||||
{{- $entry := dict "key" $tag.key "label" $tag.label -}}
|
||||
{{- with $tag.description }}{{- $_ := set $entry "description" . -}}{{- end }}
|
||||
{{- $tags = append $tags $entry -}}
|
||||
{{- end -}}
|
||||
{{- $tags | toJson -}}
|
||||
{{- end -}}
|
||||
@@ -33,42 +33,6 @@
|
||||
{{- $uri -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
MongoDB URI with appName set to one workload, so the Atlas query profiler and
|
||||
currentOp attribute each operation to the service that issued it. Renders
|
||||
nothing when no URI is configured or mongodb.appNamePerService is false.
|
||||
Usage: include "hub.mongodb.appUri" (dict "root" $ "app" "hub-api")
|
||||
*/}}
|
||||
{{- define "hub.mongodb.appUri" -}}
|
||||
{{- $uri := include "hub.mongodb.uri" .root -}}
|
||||
{{- if and $uri .root.Values.mongodb.appNamePerService -}}
|
||||
{{- if regexMatch "(?i)[?&]appName=" $uri -}}
|
||||
{{- $uri = regexReplaceAll "(?i)([?&])appName=[^&]*" $uri (printf "${1}appName=%s" .app) -}}
|
||||
{{- else -}}
|
||||
{{- $separator := "&" -}}
|
||||
{{- if not (contains "?" $uri) -}}
|
||||
{{- $separator = "?" -}}
|
||||
{{- else if or (hasSuffix "?" $uri) (hasSuffix "&" $uri) -}}
|
||||
{{- $separator = "" -}}
|
||||
{{- end -}}
|
||||
{{- $uri = printf "%s%sappName=%s" $uri $separator .app -}}
|
||||
{{- end -}}
|
||||
{{- $uri -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Container env entry that overrides MONGODB_URI from mongodb-config with the
|
||||
workload's own appName. An explicit env entry takes precedence over envFrom.
|
||||
Usage: {{- include "hub.mongodb.appUriEnv" (dict "root" $ "app" "hub-api") | nindent 12 }}
|
||||
*/}}
|
||||
{{- define "hub.mongodb.appUriEnv" -}}
|
||||
{{- with include "hub.mongodb.appUri" . -}}
|
||||
- name: MONGODB_URI
|
||||
value: {{ . | quote }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/* Render the shared MongoDB CA Secret volume. */}}
|
||||
{{- define "hub.mongodb.tlsVolume" -}}
|
||||
- name: mongodb-tls
|
||||
@@ -81,91 +45,4 @@ Usage: {{- include "hub.mongodb.appUriEnv" (dict "root" $ "app" "hub-api") | nin
|
||||
- name: mongodb-tls
|
||||
mountPath: {{ .Values.mongodb.tls.mountPath }}
|
||||
readOnly: true
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
OpenTelemetry sampling env.
|
||||
"hub.opentelemetry.samplerEnv" is for the services that only continue traces
|
||||
started elsewhere (hub-workflows, pipe-*, workflow stages): they follow the
|
||||
parent's sampling decision and never start traces of their own.
|
||||
"hub.opentelemetry.apiSamplerEnv" renders hub-api's per-class sampling ratios
|
||||
(opentelemetry.apiSamplingRatios) as TRACE_SAMPLER_RATIO_<CLASS>; empty values
|
||||
are not rendered.
|
||||
Usage: {{- include "hub.opentelemetry.samplerEnv" $ | trim | nindent 8 }}
|
||||
*/}}
|
||||
{{- define "hub.opentelemetry.samplerEnv" -}}
|
||||
- name: OTEL_TRACES_SAMPLER
|
||||
value: "parentbased_always_off"
|
||||
{{- end -}}
|
||||
|
||||
{{- define "hub.opentelemetry.apiSamplerEnv" -}}
|
||||
{{- range $class, $ratio := (.Values.opentelemetry.apiSamplingRatios | default dict) }}
|
||||
{{- if ne (toString $ratio) "" }}
|
||||
- name: TRACE_SAMPLER_RATIO_{{ upper $class }}
|
||||
value: {{ toString $ratio | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
hub.audit.destinations resolves the audit destinations into JSON. Inline
|
||||
credentials (s3.accessKey / s3.secretKey on S3 destinations, bearerToken and
|
||||
secretHeaders.<name>.value on webhooks) are the only way to pass credentials.
|
||||
They are stored in a chart-created Secret named hub-audit-<id>-credentials;
|
||||
the internal credentialsSecret / bearerTokenSecret / secretHeaders entries
|
||||
below describe that Secret for the other templates.
|
||||
Use: fromJsonArray (include "hub.audit.destinations" .)
|
||||
*/}}
|
||||
{{- define "hub.audit.destinations" -}}
|
||||
{{- $resolved := list -}}
|
||||
{{- range $index, $original := .Values.kerberoshub.audit.destinations -}}
|
||||
{{- $destination := deepCopy $original -}}
|
||||
{{- $label := default (printf "%d" $index) $destination.id -}}
|
||||
{{- if hasKey $destination "credentialsSecret" -}}
|
||||
{{- fail (printf "audit destination %s: credentialsSecret is not supported; set s3.accessKey and s3.secretKey" $label) -}}
|
||||
{{- end -}}
|
||||
{{- if hasKey $destination "bearerTokenSecret" -}}
|
||||
{{- fail (printf "audit destination %s: bearerTokenSecret is not supported; set bearerToken" $label) -}}
|
||||
{{- end -}}
|
||||
{{- $s3 := default dict $destination.s3 -}}
|
||||
{{- if or (hasKey $s3 "accessKey") (hasKey $s3 "secretKey") -}}
|
||||
{{- $id := required (printf "id is required for audit destination %d" $index) $destination.id -}}
|
||||
{{- if not (and $s3.accessKey $s3.secretKey) -}}
|
||||
{{- fail (printf "audit destination %s must set both s3.accessKey and s3.secretKey, and neither may be empty" $id) -}}
|
||||
{{- end -}}
|
||||
{{- $_ := set $destination "credentialsSecret" (dict
|
||||
"name" (printf "hub-audit-%s-credentials" $id)
|
||||
"create" true
|
||||
"accessKeyId" $s3.accessKey
|
||||
"secretAccessKey" $s3.secretKey) -}}
|
||||
{{- $_ := unset $s3 "accessKey" -}}
|
||||
{{- $_ := unset $s3 "secretKey" -}}
|
||||
{{- end -}}
|
||||
{{- if hasKey $destination "bearerToken" -}}
|
||||
{{- $id := required (printf "id is required for audit destination %d" $index) $destination.id -}}
|
||||
{{- if not $destination.bearerToken -}}
|
||||
{{- fail (printf "audit destination %s sets an empty bearerToken" $id) -}}
|
||||
{{- end -}}
|
||||
{{- $_ := set $destination "bearerTokenSecret" (dict
|
||||
"name" (printf "hub-audit-%s-credentials" $id)
|
||||
"key" "bearer-token"
|
||||
"create" true
|
||||
"value" $destination.bearerToken) -}}
|
||||
{{- $_ := unset $destination "bearerToken" -}}
|
||||
{{- end -}}
|
||||
{{- range $header, $secret := (default dict $destination.secretHeaders) -}}
|
||||
{{- if or (not (kindIs "map" $secret)) (not (hasKey $secret "value")) (hasKey $secret "secretName") (hasKey $secret "secretKey") -}}
|
||||
{{- fail (printf "audit destination %s header %s: set secretHeaders.%s.value" $label $header $header) -}}
|
||||
{{- end -}}
|
||||
{{- $id := required (printf "id is required for audit destination %d" $index) $destination.id -}}
|
||||
{{- if not $secret.value -}}
|
||||
{{- fail (printf "audit destination %s header %s sets an empty value" $id $header) -}}
|
||||
{{- end -}}
|
||||
{{- $_ := set $secret "secretName" (printf "hub-audit-%s-credentials" $id) -}}
|
||||
{{- $_ := set $secret "secretKey" (printf "header-%s" (sha256sum $header | trunc 12)) -}}
|
||||
{{- $_ := set $secret "create" true -}}
|
||||
{{- end -}}
|
||||
{{- $resolved = append $resolved $destination -}}
|
||||
{{- end -}}
|
||||
{{- toJson $resolved -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
@@ -26,10 +26,6 @@ spec:
|
||||
- name: admin
|
||||
image: "{{ .Values.global.imageRegistry }}{{ .Values.admin.repository }}:{{ .Values.admin.tag }}"
|
||||
imagePullPolicy: {{ .Values.admin.pullPolicy }}
|
||||
{{- with .Values.admin.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.admin.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
@@ -40,16 +36,8 @@ spec:
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: {{ .Values.admin.containerPort | default 80 }}
|
||||
containerPort: 80
|
||||
protocol: TCP
|
||||
{{- with .Values.admin.readinessProbe }}
|
||||
readinessProbe:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.admin.livenessProbe }}
|
||||
livenessProbe:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
env:
|
||||
- name: LOG_LEVEL
|
||||
value: "{{ .Values.admin.logLevel }}"
|
||||
|
||||
@@ -10,7 +10,7 @@ spec:
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: http
|
||||
targetPort: 80
|
||||
name: http
|
||||
selector:
|
||||
app: admin
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
{{- define "hub.classificationCatalog.defaults" -}}
|
||||
[
|
||||
{"key": "animal", "label": "Animal", "icon": "animal"},
|
||||
{"key": "pedestrian", "label": "Pedestrian", "icon": "pedestrian"},
|
||||
{"key": "cyclist", "label": "Cyclist", "icon": "cyclist"},
|
||||
{"key": "motorbike", "label": "Motorbike", "icon": "motorbike"},
|
||||
{"key": "lorry", "label": "Lorry", "icon": "lorry"},
|
||||
{"key": "car", "label": "Car", "icon": "car"},
|
||||
{"key": "handbag", "label": "Handbag", "icon": "handbag"},
|
||||
{"key": "suitcase", "label": "Suitcase", "icon": "suitcase"},
|
||||
{"key": "cell phone", "label": "Cell phone", "icon": "cell phone"}
|
||||
]
|
||||
{{- end -}}
|
||||
{{/*
|
||||
The effective chart-rendered catalog: the inline classificationCatalog values
|
||||
list when set, otherwise the bundled defaults above. values.schema.json
|
||||
validates each entry ({key, label, icon?}); keys must also be unique.
|
||||
*/}}
|
||||
{{- define "hub.classificationCatalog.json" -}}
|
||||
{{- if kindIs "invalid" .Values.classificationCatalog -}}
|
||||
{{- include "hub.classificationCatalog.defaults" . | mustFromJson | toJson -}}
|
||||
{{- else if and (kindIs "slice" .Values.classificationCatalog) .Values.classificationCatalog -}}
|
||||
{{- $seen := dict -}}
|
||||
{{- range $entry := .Values.classificationCatalog -}}
|
||||
{{- $key := toString $entry.key -}}
|
||||
{{- if hasKey $seen $key -}}
|
||||
{{- fail (printf "classificationCatalog: duplicate key %q" $key) -}}
|
||||
{{- end -}}
|
||||
{{- $_ := set $seen $key true -}}
|
||||
{{- end -}}
|
||||
{{- .Values.classificationCatalog | toJson -}}
|
||||
{{- else -}}
|
||||
{{- fail "classificationCatalog must be omitted (bundled defaults) or a nonempty list; to use the API fallback set classificationCatalogEnabled: false" -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- $config := include "hub.workflowConfiguration.settings" . | fromYaml -}}
|
||||
{{- if and .Values.classificationCatalogEnabled (not .Values.classificationCatalogExistingConfigMap) (not (hasKey $config.replacements "catalogs/classifications.json")) }}
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: classification-catalog
|
||||
namespace: {{ .Release.Namespace }}
|
||||
data:
|
||||
classifications.json: |-
|
||||
{{- include "hub.classificationCatalog.json" . | mustFromJson | toPrettyJson | nindent 4 }}
|
||||
{{- end -}}
|
||||
@@ -1,12 +0,0 @@
|
||||
{{- if include "kerberoshub.deviceTags.system.enabled" . }}
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: device-system-tags
|
||||
namespace: {{ .Release.Namespace }}
|
||||
data:
|
||||
# Rendered from deviceTags.system.catalog; hub-api reads it through
|
||||
# DEVICE_SYSTEM_TAGS_FILE.
|
||||
system-tags.json: |-
|
||||
{{- include "kerberoshub.deviceTags.system.json" . | mustFromJson | toPrettyJson | nindent 4 }}
|
||||
{{- end -}}
|
||||
@@ -1,78 +0,0 @@
|
||||
{{- if .Values.audit.enabled -}}
|
||||
{{- $destinations := list -}}
|
||||
{{- $destinationIDs := dict -}}
|
||||
{{- range $index, $destination := fromJsonArray (include "hub.audit.destinations" .) -}}
|
||||
{{- $id := required (printf "id is required for audit destination %d" $index) $destination.id -}}
|
||||
{{- if not (regexMatch "^[a-z][a-z0-9-]{0,62}$" $id) -}}
|
||||
{{- fail (printf "audit destination id %q must match ^[a-z][a-z0-9-]{0,62}$" $id) -}}
|
||||
{{- end -}}
|
||||
{{- if hasKey $destinationIDs $id -}}
|
||||
{{- fail (printf "duplicate audit destination id %q" $id) -}}
|
||||
{{- end -}}
|
||||
{{- $_ := set $destinationIDs $id true -}}
|
||||
{{- $type := default "webhook" $destination.type -}}
|
||||
{{- $entry := dict
|
||||
"id" $id
|
||||
"type" $type
|
||||
"enabled" (ternary $destination.enabled true (hasKey $destination "enabled"))
|
||||
"timeoutSeconds" (default 10 $destination.timeoutSeconds)
|
||||
"batchSize" (default 100 $destination.batchSize)
|
||||
"maxAttempts" (default 10 $destination.maxAttempts)
|
||||
"startAt" (default "latest" $destination.startAt)
|
||||
"filters" (default dict $destination.filters) -}}
|
||||
{{- $secretRoot := printf "/var/run/secrets/audit-destinations/%s" $id -}}
|
||||
{{- if eq $type "webhook" -}}
|
||||
{{- if or $destination.s3 $destination.credentialsSecret -}}
|
||||
{{- fail (printf "audit destination %s is a webhook and cannot set s3 or credentialsSecret" $id) -}}
|
||||
{{- end -}}
|
||||
{{- $_ := set $entry "url" (required (printf "url is required for audit destination %s" $id) $destination.url) -}}
|
||||
{{- $_ := set $entry "headers" (default dict $destination.headers) -}}
|
||||
{{- $bearer := default dict $destination.bearerTokenSecret -}}
|
||||
{{- if gt (len $bearer) 0 -}}
|
||||
{{- $secretName := required (printf "bearerTokenSecret.name is required for destination %s" $id) $bearer.name -}}
|
||||
{{- $secretKey := default "token" $bearer.key -}}
|
||||
{{- $_ := set $entry "bearerTokenFile" (printf "%s/bearer-%s" $secretRoot (sha256sum (printf "%s:%s" $secretName $secretKey) | trunc 12)) -}}
|
||||
{{- end -}}
|
||||
{{- $headerFiles := dict -}}
|
||||
{{- range $headerName, $secret := (default dict $destination.secretHeaders) -}}
|
||||
{{- $secretName := required (printf "secretName is required for destination %s header %s" $id $headerName) $secret.secretName -}}
|
||||
{{- $secretKey := required (printf "secretKey is required for destination %s header %s" $id $headerName) $secret.secretKey -}}
|
||||
{{- $_ := set $headerFiles $headerName (printf "%s/header-%s" $secretRoot (sha256sum (printf "%s:%s:%s" $headerName $secretName $secretKey) | trunc 12)) -}}
|
||||
{{- end -}}
|
||||
{{- if gt (len $headerFiles) 0 -}}
|
||||
{{- $_ := set $entry "headerFiles" $headerFiles -}}
|
||||
{{- end -}}
|
||||
{{- else if eq $type "s3" -}}
|
||||
{{- if or $destination.url $destination.headers $destination.bearerTokenSecret $destination.secretHeaders -}}
|
||||
{{- fail (printf "audit destination %s is s3 and cannot set url, headers, bearerToken, bearerTokenSecret or secretHeaders" $id) -}}
|
||||
{{- end -}}
|
||||
{{- $s3 := required (printf "s3 is required for audit destination %s" $id) $destination.s3 -}}
|
||||
{{- $settings := dict
|
||||
"bucket" (required (printf "s3.bucket is required for audit destination %s" $id) $s3.bucket)
|
||||
"region" (required (printf "s3.region is required for audit destination %s" $id) $s3.region) -}}
|
||||
{{- range $key := list "prefix" "endpoint" "forcePathStyle" "allowUnlockedBucket" -}}
|
||||
{{- if hasKey $s3 $key -}}
|
||||
{{- $_ := set $settings $key (get $s3 $key) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- $credentials := default dict $destination.credentialsSecret -}}
|
||||
{{- if gt (len $credentials) 0 -}}
|
||||
{{- $secretName := required (printf "credentialsSecret.name is required for destination %s" $id) $credentials.name -}}
|
||||
{{- $_ := set $settings "accessKeyIdFile" (printf "%s/access-key-id-%s" $secretRoot (sha256sum (printf "%s:%s" $secretName (default "access-key-id" $credentials.accessKeyIdKey)) | trunc 12)) -}}
|
||||
{{- $_ := set $settings "secretAccessKeyFile" (printf "%s/secret-access-key-%s" $secretRoot (sha256sum (printf "%s:%s" $secretName (default "secret-access-key" $credentials.secretAccessKeyKey)) | trunc 12)) -}}
|
||||
{{- end -}}
|
||||
{{- $_ := set $entry "s3" $settings -}}
|
||||
{{- else -}}
|
||||
{{- fail (printf "audit destination %s has unsupported type %q (webhook or s3)" $id $type) -}}
|
||||
{{- end -}}
|
||||
{{- $destinations = append $destinations $entry -}}
|
||||
{{- end -}}
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: hub-audit
|
||||
namespace: {{ .Release.Namespace }}
|
||||
data:
|
||||
destinations.json: |
|
||||
{{ dict "version" 1 "destinations" $destinations | toPrettyJson | nindent 4 }}
|
||||
{{- end }}
|
||||
@@ -1,135 +0,0 @@
|
||||
{{/* Validate the rendered anpr.yaml payload against schemas/workflow-contract.schema.json in the chart root. */}}
|
||||
{{- define "hub.workflowConfiguration.anpr" -}}
|
||||
schemaVersion: 1
|
||||
stage: anpr
|
||||
contractVersion: 1
|
||||
presentation:
|
||||
name: Number plate recognition
|
||||
description: Read the number plates of vehicles in the recording.
|
||||
icon: car
|
||||
color: amber
|
||||
|
||||
# Node settings hub-anpr reads per run (see its README, "Node settings"). Each
|
||||
# overrides the matching ANPR_* environment variable; the defaults below equal
|
||||
# the worker's built-in defaults. A deployment that changes those variables
|
||||
# should change these defaults too, because new nodes store them on save.
|
||||
params:
|
||||
- name: plateClasses
|
||||
label: Read plates for
|
||||
description: Vehicle classes whose plates are read (ANPR_PLATE_CLASSES).
|
||||
type: multiselect
|
||||
choices: {file: ../catalogs/classifications.json}
|
||||
default: [car]
|
||||
group: Which vehicles
|
||||
- name: minMovement
|
||||
label: Minimum movement
|
||||
description: Skip vehicles that barely moved; 0 reads every vehicle (ANPR_READ_MOVEMENT_MIN).
|
||||
type: number
|
||||
minimum: 0
|
||||
default: 0
|
||||
group: Which vehicles
|
||||
- name: maxJump
|
||||
label: Maximum jump between frames
|
||||
description: Drop unstable tracks; 0 disables the check (ANPR_READ_MAX_JUMP).
|
||||
type: number
|
||||
minimum: 0
|
||||
default: 0
|
||||
group: Which vehicles
|
||||
- name: readFrames
|
||||
label: Frames to read per vehicle
|
||||
description: Frames OCR'd per vehicle; 0 disables OCR (ANPR_READ_FRAMES).
|
||||
type: number
|
||||
minimum: 0
|
||||
maximum: 10
|
||||
default: 1
|
||||
group: Recognition
|
||||
- name: readQuorum
|
||||
label: Agreeing reads required
|
||||
description: Reads that must agree before a plate is accepted; 0 accepts any read (ANPR_READ_QUORUM).
|
||||
type: number
|
||||
minimum: 0
|
||||
maximum: 10
|
||||
default: 0
|
||||
group: Recognition
|
||||
- name: minConfidence
|
||||
label: Minimum OCR confidence
|
||||
description: Ignore reads below this confidence (ANPR_READ_CONF_MIN).
|
||||
type: number
|
||||
minimum: 0
|
||||
maximum: 1
|
||||
default: 0
|
||||
group: Recognition
|
||||
|
||||
# Paths are relative to results.anpr (models.WorkflowAnprResult): one track and
|
||||
# one marker per vehicle. Plate, unread and class can be tied to the same
|
||||
# vehicle with "Must match the same item"; OCR confidence and engine live on
|
||||
# the markers and cannot be tied to a plate.
|
||||
fields:
|
||||
- id: plate
|
||||
label: Number plate
|
||||
path: detections.*.tracks.*.meta.plate
|
||||
type: string
|
||||
operators: [eq, in, contains, matches] # stored uppercase, letters and digits only, e.g. AB123CD
|
||||
optional: true
|
||||
- id: unread
|
||||
label: Plate could not be read
|
||||
path: detections.*.tracks.*.meta.unread
|
||||
type: boolean
|
||||
operators: [eq]
|
||||
optional: true
|
||||
- id: vehicleClass
|
||||
label: Vehicle class (from classification)
|
||||
path: detections.*.tracks.*.meta.classifiedAs
|
||||
type: string
|
||||
operators: [eq, in]
|
||||
optional: true
|
||||
choices: {file: ../catalogs/classifications.json}
|
||||
- id: ocrConfidence
|
||||
label: OCR confidence
|
||||
path: markers.*.metadata.confidence
|
||||
type: number
|
||||
operators: [gte, gt, lte, lt] # read plates only; about 0-1 for fast, 0.5 for tesseract
|
||||
minimum: 0
|
||||
optional: true
|
||||
- id: ocrEngine
|
||||
label: OCR engine
|
||||
path: markers.*.metadata.engine
|
||||
type: string
|
||||
operators: [eq, in] # read plates only
|
||||
optional: true
|
||||
choices: {values: [fast, tesseract, tesseract+opencv, http]}
|
||||
- id: secondsVisible
|
||||
label: Seconds visible
|
||||
path: markers.*.duration
|
||||
type: integer
|
||||
operators: [gte, lte]
|
||||
minimum: 0
|
||||
optional: true
|
||||
|
||||
# Connections leaving this stage; Extra conditions are appended collapsed.
|
||||
panel:
|
||||
sections:
|
||||
- id: plate
|
||||
label: Plate
|
||||
fields:
|
||||
- {field: plate, widget: text, operator: contains, label: Plate contains (e.g. AB123)}
|
||||
- {field: unread, widget: toggle, label: Plate could not be read}
|
||||
- id: recognition
|
||||
label: Recognition
|
||||
collapsed: true
|
||||
fields:
|
||||
- {field: ocrConfidence, widget: number, operator: gte, label: Minimum OCR confidence}
|
||||
- {field: vehicleClass, widget: multiselect}
|
||||
- {field: secondsVisible, widget: number, operator: gte, label: Visible for at least (seconds)}
|
||||
{{ end -}}
|
||||
{{- $config := include "hub.workflowConfiguration.settings" . | fromYaml -}}
|
||||
{{- if and $config.enabled $config.includeDefaults (not (hasKey $config.replacements "workflow-contracts/anpr.yaml")) (or (eq .Values.mode "all") (eq .Values.mode "ui")) }}
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "hub.workflowConfiguration.anpr.configMapName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
data:
|
||||
anpr.yaml: |-
|
||||
{{- include "hub.workflowConfiguration.anpr" . | nindent 4 }}
|
||||
{{- end }}
|
||||
@@ -1,95 +0,0 @@
|
||||
{{/* Validate the rendered forwarder.yaml payload against schemas/workflow-contract.schema.json in the chart root. */}}
|
||||
{{- define "hub.workflowConfiguration.forwarder" -}}
|
||||
schemaVersion: 1
|
||||
stage: forwarder
|
||||
contractVersion: 1
|
||||
presentation:
|
||||
name: Forward to my system
|
||||
description: Send the run's data to your own RabbitMQ destination.
|
||||
icon: share
|
||||
color: green
|
||||
|
||||
# Custom workflows supply their own connection; deployment credentials are never
|
||||
# inherited. These non-secret defaults are stored on save, not read from the worker.
|
||||
params:
|
||||
- name: broker
|
||||
label: Provider
|
||||
description: Custom workflows use their own RabbitMQ destination. Shared deployment integrations are reserved for global workflows.
|
||||
type: select
|
||||
required: true
|
||||
options: [rabbitmq]
|
||||
optionLabels: {rabbitmq: RabbitMQ}
|
||||
default: rabbitmq
|
||||
group: Delivery
|
||||
- name: mode
|
||||
label: Completion mode
|
||||
description: Complete after delivery, or wait for the external result. Callback requires a deployment-configured Hub callback URL.
|
||||
type: select
|
||||
required: true
|
||||
options: [delivered, callback]
|
||||
optionLabels: {delivered: Complete after delivery, callback: Wait for result}
|
||||
default: delivered
|
||||
group: Delivery
|
||||
- name: host
|
||||
label: Host
|
||||
description: Your RabbitMQ destination host. Required before enabling the workflow.
|
||||
type: string
|
||||
required: true
|
||||
placeholder: rabbitmq.example.com
|
||||
group: RabbitMQ destination
|
||||
- name: port
|
||||
label: Port
|
||||
description: Your RabbitMQ destination port, a whole number (usually 5671 with TLS or 5672 without TLS).
|
||||
type: number
|
||||
required: true
|
||||
minimum: 1
|
||||
maximum: 65535
|
||||
group: RabbitMQ destination
|
||||
- name: queue
|
||||
label: Queue
|
||||
description: Your destination queue. Its dead-letter queue is this name followed by .forwarder-dead-letter.
|
||||
type: string
|
||||
required: true
|
||||
group: RabbitMQ destination
|
||||
- name: vhost
|
||||
label: Virtual host
|
||||
description: Your RabbitMQ virtual host. Use / for the broker's default virtual host.
|
||||
type: string
|
||||
required: true
|
||||
placeholder: /
|
||||
group: RabbitMQ destination
|
||||
- name: tls
|
||||
label: Use TLS
|
||||
description: Encrypt the connection and verify the broker certificate using system trust roots. Deployment TLS overrides are never inherited.
|
||||
type: boolean
|
||||
required: true
|
||||
default: true
|
||||
group: RabbitMQ destination
|
||||
- name: username
|
||||
label: Username
|
||||
description: Your RabbitMQ destination username. Deployment credentials are never used.
|
||||
type: string
|
||||
required: true
|
||||
group: RabbitMQ credentials
|
||||
- name: password
|
||||
label: Password
|
||||
description: Your RabbitMQ destination password. Required before enabling the workflow; masked after saving.
|
||||
type: secret
|
||||
required: true
|
||||
group: RabbitMQ credentials
|
||||
|
||||
# It contributes no output fields: connections after it can still use Start and
|
||||
# earlier stages in Extra conditions.
|
||||
fields: []
|
||||
{{ end -}}
|
||||
{{- $config := include "hub.workflowConfiguration.settings" . | fromYaml -}}
|
||||
{{- if and $config.enabled $config.includeDefaults (not (hasKey $config.replacements "workflow-contracts/forwarder.yaml")) (or (eq .Values.mode "all") (eq .Values.mode "ui")) }}
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ include "hub.workflowConfiguration.forwarder.configMapName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
data:
|
||||
forwarder.yaml: |-
|
||||
{{- include "hub.workflowConfiguration.forwarder" . | nindent 4 }}
|
||||
{{- end }}
|
||||
@@ -1,75 +0,0 @@
|
||||
{{/* Validate the rendered start.yaml payload against schemas/workflow-contract.schema.json in the chart root. */}}
|
||||
{{- define "hub.workflowConfiguration.start" -}}
|
||||
schemaVersion: 1
|
||||
stage: start
|
||||
contractVersion: 1
|
||||
|
||||
# Paths refer to the safe initial context, not the full event or credentials.
|
||||
fields:
|
||||
- id: device
|
||||
label: Device
|
||||
path: device.deviceKey
|
||||
type: string
|
||||
operators: [eq, in]
|
||||
choices:
|
||||
source: project.devices
|
||||
- id: sites
|
||||
label: Sites
|
||||
path: device.siteIds.*
|
||||
type: string
|
||||
operators: [in]
|
||||
choices:
|
||||
source: project.sites
|
||||
- id: groups
|
||||
label: Groups
|
||||
path: device.groupIds.*
|
||||
type: string
|
||||
operators: [in]
|
||||
choices:
|
||||
source: project.groups
|
||||
- id: classifications
|
||||
label: Initial classifications
|
||||
path: inputs.classify.details.*.classified
|
||||
type: string
|
||||
operators: [in]
|
||||
optional: true
|
||||
choices:
|
||||
file: ../catalogs/classifications.json
|
||||
# Condition-only context fields (not selectors): advanced conditions on
|
||||
# Start connections may test only declared fields, under device. or inputs.
|
||||
- id: deviceName
|
||||
label: Camera name
|
||||
path: device.deviceName
|
||||
type: string
|
||||
operators: [eq, in, contains, matches]
|
||||
- id: objectClass
|
||||
label: Detected object
|
||||
path: inputs.classify.details.*.classified
|
||||
type: string
|
||||
operators: [eq, ne, in, exists]
|
||||
optional: true
|
||||
choices:
|
||||
file: ../catalogs/classifications.json
|
||||
|
||||
# Controls offered for new authoring; an omitted control is not offered.
|
||||
controls:
|
||||
- id: weeklySchedule
|
||||
label: Weekly schedule
|
||||
- id: conditions
|
||||
label: Advanced conditions
|
||||
|
||||
# Trigger modes offered for new authoring; omitted offers both.
|
||||
triggerModes: [automatic, manual]
|
||||
{{ end -}}
|
||||
{{- include "hub.workflowConfiguration.validate" . -}}
|
||||
{{- $config := include "hub.workflowConfiguration.settings" . | fromYaml -}}
|
||||
{{- if and $config.enabled $config.includeDefaults (not (hasKey $config.replacements "workflow-contracts/start.yaml")) (or (eq .Values.mode "all") (eq .Values.mode "ui")) }}
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ printf "%s-workflow-configuration" .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
data:
|
||||
start.yaml: |-
|
||||
{{- include "hub.workflowConfiguration.start" . | nindent 4 }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,8 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: classification-catalog
|
||||
namespace: {{ .Release.Namespace }}
|
||||
data:
|
||||
classifications.json: |-
|
||||
{{- toPrettyJson .Values.classificationCatalog | nindent 4 }}
|
||||
@@ -0,0 +1,51 @@
|
||||
{{- if and (or (eq .Values.mode "all") (eq .Values.mode "ui")) .Values.kerberoshub.audit.enabled -}}
|
||||
{{- $destinations := list -}}
|
||||
{{- $destinationIDs := dict -}}
|
||||
{{- range $index, $destination := .Values.kerberoshub.audit.destinations -}}
|
||||
{{- $id := required (printf "id is required for audit destination %d" $index) $destination.id -}}
|
||||
{{- if not (regexMatch "^[a-z][a-z0-9-]{0,62}$" $id) -}}
|
||||
{{- fail (printf "audit destination id %q must match ^[a-z][a-z0-9-]{0,62}$" $id) -}}
|
||||
{{- end -}}
|
||||
{{- if hasKey $destinationIDs $id -}}
|
||||
{{- fail (printf "duplicate audit destination id %q" $id) -}}
|
||||
{{- end -}}
|
||||
{{- $_ := set $destinationIDs $id true -}}
|
||||
{{- $url := required (printf "url is required for audit destination %s" $id) $destination.url -}}
|
||||
{{- $entry := dict
|
||||
"id" $id
|
||||
"type" (default "webhook" $destination.type)
|
||||
"enabled" $destination.enabled
|
||||
"url" $url
|
||||
"timeoutSeconds" (default 10 $destination.timeoutSeconds)
|
||||
"batchSize" (default 100 $destination.batchSize)
|
||||
"maxAttempts" (default 10 $destination.maxAttempts)
|
||||
"startAt" (default "latest" $destination.startAt)
|
||||
"headers" (default dict $destination.headers)
|
||||
"filters" (default dict $destination.filters) -}}
|
||||
{{- $secretRoot := printf "/var/run/secrets/audit-destinations/%s" $id -}}
|
||||
{{- $bearer := default dict $destination.bearerTokenSecret -}}
|
||||
{{- if gt (len $bearer) 0 -}}
|
||||
{{- $secretName := required (printf "bearerTokenSecret.name is required for destination %s" $id) $bearer.name -}}
|
||||
{{- $secretKey := default "token" $bearer.key -}}
|
||||
{{- $_ := set $entry "bearerTokenFile" (printf "%s/bearer-%s" $secretRoot (sha256sum (printf "%s:%s" $secretName $secretKey) | trunc 12)) -}}
|
||||
{{- end -}}
|
||||
{{- $headerFiles := dict -}}
|
||||
{{- range $headerName, $secret := (default dict $destination.secretHeaders) -}}
|
||||
{{- $secretName := required (printf "secretName is required for destination %s header %s" $id $headerName) $secret.secretName -}}
|
||||
{{- $secretKey := required (printf "secretKey is required for destination %s header %s" $id $headerName) $secret.secretKey -}}
|
||||
{{- $_ := set $headerFiles $headerName (printf "%s/header-%s" $secretRoot (sha256sum (printf "%s:%s:%s" $headerName $secretName $secretKey) | trunc 12)) -}}
|
||||
{{- end -}}
|
||||
{{- if gt (len $headerFiles) 0 -}}
|
||||
{{- $_ := set $entry "headerFiles" $headerFiles -}}
|
||||
{{- end -}}
|
||||
{{- $destinations = append $destinations $entry -}}
|
||||
{{- end -}}
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: hub-audit
|
||||
namespace: {{ .Release.Namespace }}
|
||||
data:
|
||||
destinations.json: |
|
||||
{{ dict "version" 1 "destinations" $destinations | toPrettyJson | nindent 4 }}
|
||||
{{- end }}
|
||||
@@ -1,6 +1,4 @@
|
||||
{{- if or (eq .Values.mode "all") (eq .Values.mode "ui") -}}
|
||||
{{- $workflowConfiguration := include "hub.workflowConfiguration.settings" . | fromYaml -}}
|
||||
{{- $catalogReplacement := index $workflowConfiguration.replacements "catalogs/classifications.json" | default dict -}}
|
||||
{{- if .Values.kerberoshub.api.serviceEnabled }}
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
@@ -121,29 +119,11 @@ spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
checksum/config-mongodb: {{ include (print $.Template.BasePath "/configMaps/configmap-mongodb.yaml") . | sha256sum }}
|
||||
{{- if and .Values.classificationCatalogEnabled (not .Values.classificationCatalogExistingConfigMap) (not $catalogReplacement) }}
|
||||
checksum/classification-catalog: {{ include (print $.Template.BasePath "/configMaps/catalogs/classifications.yaml") . | sha256sum }}
|
||||
{{- end }}
|
||||
{{- if include "kerberoshub.deviceTags.system.enabled" . }}
|
||||
checksum/device-system-tags: {{ include (print $.Template.BasePath "/configMaps/catalogs/device-system-tags.yaml") . | sha256sum }}
|
||||
{{- end }}
|
||||
{{- if and $workflowConfiguration.enabled $workflowConfiguration.includeDefaults (not (hasKey $workflowConfiguration.replacements "workflow-contracts/start.yaml")) }}
|
||||
checksum/workflow-configuration: {{ include (print $.Template.BasePath "/configMaps/workflow-contracts/start.yaml") . | sha256sum }}
|
||||
{{- end }}
|
||||
{{- if and $workflowConfiguration.enabled $workflowConfiguration.includeDefaults (not (hasKey $workflowConfiguration.replacements "workflow-contracts/anpr.yaml")) }}
|
||||
checksum/workflow-contract-anpr: {{ include (print $.Template.BasePath "/configMaps/workflow-contracts/anpr.yaml") . | sha256sum }}
|
||||
{{- end }}
|
||||
{{- if and $workflowConfiguration.enabled $workflowConfiguration.includeDefaults (not (hasKey $workflowConfiguration.replacements "workflow-contracts/forwarder.yaml")) }}
|
||||
checksum/workflow-contract-forwarder: {{ include (print $.Template.BasePath "/configMaps/workflow-contracts/forwarder.yaml") . | sha256sum }}
|
||||
{{- end }}
|
||||
checksum/config-mongodb: {{ include (print $.Template.BasePath "/configmap-mongodb.yaml") . | sha256sum }}
|
||||
checksum/classification-catalog: {{ include (print $.Template.BasePath "/configmap-classification-catalog.yaml") . | sha256sum }}
|
||||
labels:
|
||||
app: hub-api
|
||||
spec:
|
||||
{{- with .Values.kerberoshub.api.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.kerberoshub.serviceAccount.create }}
|
||||
serviceAccountName: {{ default (printf "%s-%s-sa" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-") .Values.kerberoshub.serviceAccount.name }}
|
||||
{{- end }}
|
||||
@@ -153,66 +133,10 @@ spec:
|
||||
{{- end }}
|
||||
{{- $serverTLS := .Values.kerberoshub.api.serverTLS }}
|
||||
{{- $mongodbTLS := .Values.mongodb.tls }}
|
||||
{{- $classificationCatalogEnabled := .Values.classificationCatalogEnabled }}
|
||||
{{- $deviceSystemTagsEnabled := include "kerberoshub.deviceTags.system.enabled" . }}
|
||||
{{- if or $workflowConfiguration.enabled $classificationCatalogEnabled $deviceSystemTagsEnabled .Values.kerberoshub.api.volumes (and $serverTLS.enabled $serverTLS.secretName) (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
|
||||
volumes:
|
||||
{{- if $workflowConfiguration.enabled }}
|
||||
- name: workflow-configuration
|
||||
projected:
|
||||
sources:
|
||||
{{- if $workflowConfiguration.includeDefaults }}
|
||||
{{- if not (hasKey $workflowConfiguration.replacements "workflow-contracts/start.yaml") }}
|
||||
- configMap:
|
||||
name: {{ printf "%s-workflow-configuration" .Release.Name | trunc 63 | trimSuffix "-" }}
|
||||
items:
|
||||
- key: start.yaml
|
||||
path: workflow-contracts/start.yaml
|
||||
{{- end }}
|
||||
{{- if not (hasKey $workflowConfiguration.replacements "workflow-contracts/anpr.yaml") }}
|
||||
- configMap:
|
||||
name: {{ include "hub.workflowConfiguration.anpr.configMapName" . }}
|
||||
items:
|
||||
- key: anpr.yaml
|
||||
path: workflow-contracts/anpr.yaml
|
||||
{{- end }}
|
||||
{{- if not (hasKey $workflowConfiguration.replacements "workflow-contracts/forwarder.yaml") }}
|
||||
- configMap:
|
||||
name: {{ include "hub.workflowConfiguration.forwarder.configMapName" . }}
|
||||
items:
|
||||
- key: forwarder.yaml
|
||||
path: workflow-contracts/forwarder.yaml
|
||||
{{- end }}
|
||||
{{- if not $catalogReplacement }}
|
||||
- configMap:
|
||||
name: {{ include "hub.classificationCatalog.configMapName" . }}
|
||||
items:
|
||||
- key: classifications.json
|
||||
path: catalogs/classifications.json
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- range $workflowConfiguration.configMaps }}
|
||||
- configMap:
|
||||
name: {{ .name | quote }}
|
||||
items:
|
||||
{{- toYaml .items | nindent 20 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if $classificationCatalogEnabled }}
|
||||
- name: classification-catalog
|
||||
configMap:
|
||||
name: {{ include "hub.classificationCatalog.configMapName" . }}
|
||||
{{- if or .Values.classificationCatalogExistingConfigMap $catalogReplacement }}
|
||||
items:
|
||||
- key: {{ default "classifications.json" $catalogReplacement.key | quote }}
|
||||
path: classifications.json
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if $deviceSystemTagsEnabled }}
|
||||
- name: device-system-tags
|
||||
configMap:
|
||||
name: device-system-tags
|
||||
{{- end }}
|
||||
name: classification-catalog
|
||||
{{- with .Values.kerberoshub.api.volumes }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
@@ -224,7 +148,6 @@ spec:
|
||||
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
|
||||
{{- include "hub.mongodb.tlsVolume" . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.kerberoshub.api.topologySpreadConstraints }}
|
||||
topologySpreadConstraints:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
@@ -233,10 +156,6 @@ spec:
|
||||
- name: hub-api
|
||||
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberoshub.api.repository }}:{{ .Values.kerberoshub.api.tag }}"
|
||||
imagePullPolicy: {{ .Values.kerberoshub.api.pullPolicy }}
|
||||
{{- with .Values.kerberoshub.api.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.kerberoshub.api.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
@@ -244,23 +163,10 @@ spec:
|
||||
ports:
|
||||
- containerPort: 80
|
||||
name: http
|
||||
{{- if or $workflowConfiguration.enabled $classificationCatalogEnabled $deviceSystemTagsEnabled .Values.kerberoshub.api.volumeMounts (and $serverTLS.enabled $serverTLS.secretName) (and $mongodbTLS.enabled $mongodbTLS.existingSecret) }}
|
||||
volumeMounts:
|
||||
{{- if $workflowConfiguration.enabled }}
|
||||
- name: workflow-configuration
|
||||
mountPath: /etc/kerberos/configuration
|
||||
readOnly: true
|
||||
{{- end }}
|
||||
{{- if $classificationCatalogEnabled }}
|
||||
- name: classification-catalog
|
||||
mountPath: /etc/kerberos/classifications
|
||||
readOnly: true
|
||||
{{- end }}
|
||||
{{- if $deviceSystemTagsEnabled }}
|
||||
- name: device-system-tags
|
||||
mountPath: /etc/kerberos/device-tags
|
||||
readOnly: true
|
||||
{{- end }}
|
||||
{{- with .Values.kerberoshub.api.volumeMounts}}
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
@@ -272,23 +178,13 @@ spec:
|
||||
{{- if and $mongodbTLS.enabled $mongodbTLS.existingSecret }}
|
||||
{{- include "hub.mongodb.tlsVolumeMount" . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
# Mongodb - loaded from ConfigMap
|
||||
envFrom:
|
||||
- configMapRef:
|
||||
name: mongodb-config
|
||||
env:
|
||||
{{- include "hub.mongodb.appUriEnv" (dict "root" $ "app" "hub-api") | nindent 12 }}
|
||||
{{- if $classificationCatalogEnabled }}
|
||||
- name: CLASSIFICATION_CATALOG_FILE
|
||||
value: /etc/kerberos/classifications/classifications.json
|
||||
{{- end }}
|
||||
{{- if $deviceSystemTagsEnabled }}
|
||||
# System tag catalog (deviceTags.system.catalog): the only system
|
||||
# tags owners can assign. Unset (disabled) disables system tags.
|
||||
- name: DEVICE_SYSTEM_TAGS_FILE
|
||||
value: /etc/kerberos/device-tags/system-tags.json
|
||||
{{- end }}
|
||||
- name: ENVIRONMENT
|
||||
value: "{{ .Values.environment }}"
|
||||
- name: READ_ONLY
|
||||
@@ -318,9 +214,6 @@ spec:
|
||||
{{- if and .Values.kerberoshub.frontend.demoEnabled .Values.kerberoshub.frontend.demoUrl }}
|
||||
{{- $corsOrigins = append $corsOrigins (printf "%s://%s" .Values.kerberoshub.frontend.schema .Values.kerberoshub.frontend.demoUrl) }}
|
||||
{{- end }}
|
||||
{{- range .Values.kerberoshub.api.corsAllowedOrigins }}
|
||||
{{- $corsOrigins = append $corsOrigins . }}
|
||||
{{- end }}
|
||||
- name: CORS_ALLOWED_ORIGINS
|
||||
value: {{ join "," $corsOrigins | quote }}
|
||||
{{ if .Values.isPrivate }}
|
||||
@@ -369,27 +262,6 @@ spec:
|
||||
- name: KERBEROS_JWT_SECRET
|
||||
value: "{{ .Values.kerberoshub.api.jwtSecret }}"
|
||||
|
||||
# Temporarily stop serving the polled GET /devices list (204, no DB reads).
|
||||
- name: DISABLE_AGENT_HEARTBEAT
|
||||
value: {{ .Values.kerberoshub.api.disableAgentHeartbeat | default false | quote }}
|
||||
|
||||
# Media proxy: hide Vault/storage endpoints behind hub-api URLs.
|
||||
{{- $mediaProxy := .Values.kerberoshub.api.mediaProxy | default dict }}
|
||||
- name: MEDIA_PROXY_ENABLED
|
||||
value: {{ $mediaProxy.enabled | default false | quote }}
|
||||
{{- if $mediaProxy.publicUrl }}
|
||||
- name: MEDIA_PROXY_PUBLIC_URL
|
||||
value: {{ $mediaProxy.publicUrl | quote }}
|
||||
{{- end }}
|
||||
{{- if $mediaProxy.key }}
|
||||
- name: MEDIA_PROXY_KEY
|
||||
value: {{ $mediaProxy.key | quote }}
|
||||
{{- end }}
|
||||
{{- if $mediaProxy.ttl }}
|
||||
- name: MEDIA_PROXY_TTL
|
||||
value: {{ $mediaProxy.ttl | quote }}
|
||||
{{- end }}
|
||||
|
||||
# SSO (OIDC) setup
|
||||
- name: SSO_DOMAINS
|
||||
value: "{{- range .Values.kerberoshub.api.sso }}{{ .domain }};{{- end }}"
|
||||
@@ -428,9 +300,6 @@ spec:
|
||||
# workflows (for example the one-stage case redaction modal flow).
|
||||
- name: WORKFLOW_STAGE_QUEUES
|
||||
value: {{ include "kerberoshub.workflows.stageQueues" . | quote }}
|
||||
# Stages tenants may place in the workflow editor (see workflows.editorStages).
|
||||
- name: WORKFLOW_EDITOR_STAGES
|
||||
value: {{ .Values.kerberoshub.workflows.editorStages | default list | toJson | quote }}
|
||||
|
||||
# Stripe for billing
|
||||
- name: STRIPE_KEY
|
||||
@@ -444,17 +313,6 @@ spec:
|
||||
- name: MFA_ISSUER
|
||||
value: "{{ .Values.kerberoshub.api.mfaIssuer }}"
|
||||
|
||||
{{- if .Values.kerberoshub.api.passkeys.enabled }}
|
||||
- name: FEATURE_PASSKEYS_ENABLED
|
||||
value: "true"
|
||||
- name: PASSKEY_RP_ID
|
||||
value: {{ required "kerberoshub.api.passkeys.rpId is required when passkeys are enabled" .Values.kerberoshub.api.passkeys.rpId | quote }}
|
||||
- name: PASSKEY_ORIGINS
|
||||
value: {{ required "kerberoshub.api.passkeys.origins is required when passkeys are enabled" .Values.kerberoshub.api.passkeys.origins | join "," | quote }}
|
||||
- name: PASSKEY_RP_NAME
|
||||
value: {{ .Values.kerberoshub.api.passkeys.rpName | quote }}
|
||||
{{- end }}
|
||||
|
||||
# AWS (Legacy, use Kerberos Vault instead)
|
||||
- name: AWS_REGION
|
||||
value: "{{ .Values.kerberoshub.api.aws.region }}"
|
||||
@@ -503,14 +361,6 @@ spec:
|
||||
value: "{{ .Values.rabbitmq.username }}"
|
||||
- name: RABBITMQ_PASSWORD
|
||||
value: "{{ .Values.rabbitmq.password }}"
|
||||
- name: AUDIT_ENABLED
|
||||
value: {{ and .Values.audit.enabled .Values.kerberoshub.api.audit.enabled | quote }}
|
||||
{{- if and .Values.audit.enabled .Values.kerberoshub.api.audit.enabled }}
|
||||
- name: AUDIT_QUEUE
|
||||
value: {{ .Values.audit.intake.queue | quote }}
|
||||
- name: AUDIT_DEADLETTER_QUEUE
|
||||
value: {{ .Values.audit.intake.deadletterQueue | quote }}
|
||||
{{- end }}
|
||||
|
||||
# Slack notifications (this will send events/logs to a specific channel).
|
||||
- name: SLACK_ENABLED
|
||||
@@ -561,10 +411,6 @@ spec:
|
||||
value: "{{ .Values.kerberoshub.api.defaultTaskRetentionDays }}"
|
||||
- name: CASES_MAX_RETENTION_DAYS
|
||||
value: "{{ .Values.kerberoshub.api.casesMaxRetentionDays }}"
|
||||
{{- with .Values.kerberoshub.api.auditExportMaxEventsPerFile }}
|
||||
- name: AUDIT_EXPORT_MAX_EVENTS_PER_FILE
|
||||
value: {{ . | int64 | toString | quote }}
|
||||
{{- end }}
|
||||
|
||||
# SMTP
|
||||
- name: SMTP_SERVER
|
||||
@@ -603,11 +449,11 @@ spec:
|
||||
- name: VAULT_SPRITE_SECRET_KEY
|
||||
value: "{{ .Values.kerberosvault.sprite.secretKey }}"
|
||||
- name: VAULT_SPRITE_INTERVAL
|
||||
value: "{{ .Values.kerberospipeline.sprite.interval }}"
|
||||
value: "{{ .Values.kerberosvault.sprite.interval }}"
|
||||
- name: VAULT_SPRITE_WIDTH
|
||||
value: "{{ .Values.kerberospipeline.sprite.width }}"
|
||||
value: "{{ .Values.kerberosvault.sprite.width }}"
|
||||
- name: VAULT_SPRITE_HEIGHT
|
||||
value: "{{ .Values.kerberospipeline.sprite.height }}"
|
||||
value: "{{ .Values.kerberosvault.sprite.height }}"
|
||||
{{- if .Values.kerberoshub.extraEnv }}
|
||||
{{- toYaml .Values.kerberoshub.extraEnv | nindent 12 }}
|
||||
{{- end }}
|
||||
@@ -620,6 +466,5 @@ spec:
|
||||
value: "{{ .Values.opentelemetry.routingEnabled }}"
|
||||
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
||||
value: "{{ .Values.opentelemetry.collector.endpoint }}"
|
||||
{{- include "hub.opentelemetry.apiSamplerEnv" $ | trim | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
{{- if .Values.audit.enabled }}
|
||||
{{- /*
|
||||
Secret the chart creates per audit destination from its inline credentials
|
||||
(s3.accessKey / s3.secretKey, bearerToken, secretHeaders.<name>.value).
|
||||
*/}}
|
||||
{{- $created := dict }}
|
||||
{{- range $index, $destination := fromJsonArray (include "hub.audit.destinations" .) }}
|
||||
{{- $data := dict }}
|
||||
{{- $name := "" }}
|
||||
{{- $credentials := default dict $destination.credentialsSecret }}
|
||||
{{- if $credentials.create }}
|
||||
{{- $name = required (printf "credentialsSecret.name is required for destination %s" $destination.id) $credentials.name }}
|
||||
{{- $accessKeyIdKey := default "access-key-id" $credentials.accessKeyIdKey }}
|
||||
{{- $secretAccessKeyKey := default "secret-access-key" $credentials.secretAccessKeyKey }}
|
||||
{{- if eq $accessKeyIdKey $secretAccessKeyKey }}
|
||||
{{- fail (printf "credentialsSecret.accessKeyIdKey and secretAccessKeyKey must differ for destination %s" $destination.id) }}
|
||||
{{- end }}
|
||||
{{- $_ := set $data $accessKeyIdKey (required (printf "credentialsSecret.accessKeyId is required for destination %s when credentialsSecret.create is true" $destination.id) $credentials.accessKeyId) }}
|
||||
{{- $_ := set $data $secretAccessKeyKey (required (printf "credentialsSecret.secretAccessKey is required for destination %s when credentialsSecret.create is true" $destination.id) $credentials.secretAccessKey) }}
|
||||
{{- end }}
|
||||
{{- $bearer := default dict $destination.bearerTokenSecret }}
|
||||
{{- if $bearer.create }}
|
||||
{{- $name = $bearer.name }}
|
||||
{{- $_ := set $data $bearer.key $bearer.value }}
|
||||
{{- end }}
|
||||
{{- range $header, $secret := (default dict $destination.secretHeaders) }}
|
||||
{{- if $secret.create }}
|
||||
{{- $name = $secret.secretName }}
|
||||
{{- $_ := set $data $secret.secretKey $secret.value }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if $name }}
|
||||
{{- if hasKey $created $name }}
|
||||
{{- fail (printf "audit destinations %s and %s both create Secret %q" (get $created $name) $destination.id $name) }}
|
||||
{{- end }}
|
||||
{{- $_ := set $created $name $destination.id }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ $name }}
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
app: hub-audit
|
||||
audit-destination: {{ $destination.id }}
|
||||
type: Opaque
|
||||
stringData:
|
||||
{{- range $key, $value := $data }}
|
||||
{{ $key | quote }}: {{ toString $value | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -1,16 +0,0 @@
|
||||
{{- if and .Values.audit.enabled (default dict .Values.kerberoshub.audit.serviceAccount).create }}
|
||||
{{- $serviceAccount := .Values.kerberoshub.audit.serviceAccount }}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ default "hub-audit" $serviceAccount.name }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- with $serviceAccount.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with $serviceAccount.labels }}
|
||||
labels:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -1,4 +1,4 @@
|
||||
{{- if and .Values.audit.enabled .Values.kerberoshub.audit.serviceMonitor.enabled -}}
|
||||
{{- if and (or (eq .Values.mode "all") (eq .Values.mode "ui")) .Values.kerberoshub.audit.enabled .Values.kerberoshub.audit.serviceMonitor.enabled -}}
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: ServiceMonitor
|
||||
metadata:
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
{{- if .Values.audit.enabled -}}
|
||||
{{- if ne (upper .Values.queueProvider) "RABBITMQ" -}}
|
||||
{{- fail "kerberoshub.audit currently requires queueProvider=RABBITMQ" -}}
|
||||
{{- end -}}
|
||||
{{- if and (or (eq .Values.mode "all") (eq .Values.mode "ui")) .Values.kerberoshub.audit.enabled -}}
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
@@ -21,17 +18,12 @@ spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
checksum/config-mongodb: {{ include (print $.Template.BasePath "/configMaps/configmap-mongodb.yaml") . | sha256sum }}
|
||||
checksum/config-mongodb: {{ include (print $.Template.BasePath "/configmap-mongodb.yaml") . | sha256sum }}
|
||||
labels:
|
||||
app: hub-audit
|
||||
spec:
|
||||
terminationGracePeriodSeconds: {{ .Values.kerberoshub.audit.terminationGracePeriodSeconds }}
|
||||
{{- $auditServiceAccount := default dict .Values.kerberoshub.audit.serviceAccount }}
|
||||
{{- if $auditServiceAccount.create }}
|
||||
serviceAccountName: {{ default "hub-audit" $auditServiceAccount.name }}
|
||||
{{- else if $auditServiceAccount.name }}
|
||||
serviceAccountName: {{ $auditServiceAccount.name }}
|
||||
{{- else if .Values.kerberoshub.serviceAccount.create }}
|
||||
{{- if .Values.kerberoshub.serviceAccount.create }}
|
||||
serviceAccountName: {{ default (printf "%s-%s-sa" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-") .Values.kerberoshub.serviceAccount.name }}
|
||||
{{- end }}
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
@@ -43,25 +35,13 @@ spec:
|
||||
- name: destinations
|
||||
configMap:
|
||||
name: hub-audit
|
||||
{{- range $index, $destination := fromJsonArray (include "hub.audit.destinations" $) }}
|
||||
{{- range $index, $destination := .Values.kerberoshub.audit.destinations }}
|
||||
{{- $bearer := default dict $destination.bearerTokenSecret }}
|
||||
{{- $secretHeaders := default dict $destination.secretHeaders }}
|
||||
{{- $credentials := default dict $destination.credentialsSecret }}
|
||||
{{- if or (gt (len $bearer) 0) (gt (len $secretHeaders) 0) (gt (len $credentials) 0) }}
|
||||
{{- if or (gt (len $bearer) 0) (gt (len $secretHeaders) 0) }}
|
||||
- name: audit-dest-{{ sha256sum $destination.id | trunc 12 }}
|
||||
projected:
|
||||
sources:
|
||||
{{- if gt (len $credentials) 0 }}
|
||||
{{- $accessKeyIdKey := default "access-key-id" $credentials.accessKeyIdKey }}
|
||||
{{- $secretAccessKeyKey := default "secret-access-key" $credentials.secretAccessKeyKey }}
|
||||
- secret:
|
||||
name: {{ required (printf "credentialsSecret.name is required for destination %s" $destination.id) $credentials.name | quote }}
|
||||
items:
|
||||
- key: {{ $accessKeyIdKey | quote }}
|
||||
path: access-key-id-{{ sha256sum (printf "%s:%s" $credentials.name $accessKeyIdKey) | trunc 12 }}
|
||||
- key: {{ $secretAccessKeyKey | quote }}
|
||||
path: secret-access-key-{{ sha256sum (printf "%s:%s" $credentials.name $secretAccessKeyKey) | trunc 12 }}
|
||||
{{- end }}
|
||||
{{- if gt (len $bearer) 0 }}
|
||||
- secret:
|
||||
name: {{ required (printf "bearerTokenSecret.name is required for destination %s" $destination.id) $bearer.name | quote }}
|
||||
@@ -112,11 +92,10 @@ spec:
|
||||
- name: destinations
|
||||
mountPath: /etc/hub-audit
|
||||
readOnly: true
|
||||
{{- range $index, $destination := fromJsonArray (include "hub.audit.destinations" $) }}
|
||||
{{- range $index, $destination := .Values.kerberoshub.audit.destinations }}
|
||||
{{- $bearer := default dict $destination.bearerTokenSecret }}
|
||||
{{- $secretHeaders := default dict $destination.secretHeaders }}
|
||||
{{- $credentials := default dict $destination.credentialsSecret }}
|
||||
{{- if or (gt (len $bearer) 0) (gt (len $secretHeaders) 0) (gt (len $credentials) 0) }}
|
||||
{{- if or (gt (len $bearer) 0) (gt (len $secretHeaders) 0) }}
|
||||
- name: audit-dest-{{ sha256sum $destination.id | trunc 12 }}
|
||||
mountPath: /var/run/secrets/audit-destinations/{{ $destination.id }}
|
||||
readOnly: true
|
||||
@@ -132,25 +111,6 @@ spec:
|
||||
- configMapRef:
|
||||
name: mongodb-config
|
||||
env:
|
||||
{{- include "hub.mongodb.appUriEnv" (dict "root" $ "app" "hub-audit") | nindent 12 }}
|
||||
- name: RABBITMQ_HOST
|
||||
value: {{ .Values.rabbitmq.host | quote }}
|
||||
- name: RABBITMQ_EXCHANGE
|
||||
value: {{ .Values.rabbitmq.exchange | quote }}
|
||||
- name: RABBITMQ_USERNAME
|
||||
value: {{ .Values.rabbitmq.username | quote }}
|
||||
- name: RABBITMQ_PASSWORD
|
||||
value: {{ .Values.rabbitmq.password | quote }}
|
||||
- name: AUDIT_QUEUE
|
||||
value: {{ .Values.audit.intake.queue | quote }}
|
||||
- name: AUDIT_DEADLETTER_QUEUE
|
||||
value: {{ .Values.audit.intake.deadletterQueue | quote }}
|
||||
- name: AUDIT_MAX_RETRIES
|
||||
value: {{ .Values.audit.intake.maxRetries | quote }}
|
||||
- name: AUDIT_PREFETCH_COUNT
|
||||
value: {{ .Values.audit.intake.prefetchCount | quote }}
|
||||
- name: AUDIT_PERSIST_TIMEOUT
|
||||
value: {{ .Values.audit.intake.persistTimeout | quote }}
|
||||
- name: DISPATCH_INTERVAL
|
||||
value: {{ .Values.kerberoshub.audit.dispatchInterval | quote }}
|
||||
- name: LEASE_DURATION
|
||||
|
||||
@@ -18,14 +18,10 @@ spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configMaps/configmap-mongodb.yaml") . | sha256sum }}
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configmap-mongodb.yaml") . | sha256sum }}
|
||||
labels:
|
||||
app: hub-cleanup
|
||||
spec:
|
||||
{{- with .Values.kerberoshub.cleanup.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.kerberoshub.serviceAccount.create }}
|
||||
serviceAccountName: {{ default (printf "%s-%s-sa" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-") .Values.kerberoshub.serviceAccount.name }}
|
||||
{{- end }}
|
||||
@@ -51,10 +47,6 @@ spec:
|
||||
- name: hub-cleanup
|
||||
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberoshub.cleanup.repository }}:{{ .Values.kerberoshub.cleanup.tag }}"
|
||||
imagePullPolicy: {{ .Values.kerberoshub.cleanup.pullPolicy }}
|
||||
{{- with .Values.kerberoshub.cleanup.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.kerberoshub.cleanup.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
@@ -74,7 +66,6 @@ spec:
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
env:
|
||||
{{- include "hub.mongodb.appUriEnv" (dict "root" $ "app" "hub-cleanup") | nindent 12 }}
|
||||
- name: MODE
|
||||
value: "{{ .Values.kerberoshub.cleanup.mode }}"
|
||||
- name: LOG_LEVEL
|
||||
@@ -119,8 +110,6 @@ spec:
|
||||
value: "{{ .Values.kerberoshub.cleanup.defaultTaskRetentionDays }}"
|
||||
- name: AUDIT_EVENT_RETENTION_DAYS
|
||||
value: "{{ .Values.kerberoshub.cleanup.auditEventRetentionDays }}"
|
||||
- name: WORKFLOW_RUN_RETENTION_DAYS
|
||||
value: "{{ .Values.kerberoshub.cleanup.workflowRunRetentionDays | default "0" }}"
|
||||
{{- if .Values.kerberoshub.extraEnv }}
|
||||
{{- toYaml .Values.kerberoshub.extraEnv | nindent 12 }}
|
||||
{{- end }}
|
||||
|
||||
@@ -10,7 +10,7 @@ spec:
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: http
|
||||
targetPort: 80
|
||||
name: http
|
||||
selector:
|
||||
app: hub-frontend-demo
|
||||
@@ -101,16 +101,12 @@ spec:
|
||||
- name: hub-frontend-demo
|
||||
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberoshub.frontend.repository }}:{{ .Values.kerberoshub.frontend.tag }}"
|
||||
imagePullPolicy: {{ .Values.kerberoshub.frontend.pullPolicy }}
|
||||
{{- with .Values.kerberoshub.frontend.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 10 }}
|
||||
{{- end }}
|
||||
{{- with .Values.kerberoshub.frontend.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- containerPort: {{ .Values.kerberoshub.frontend.containerPort | default 8080 }}
|
||||
- containerPort: 80
|
||||
name: http
|
||||
{{- with .Values.kerberoshub.frontend.volumeMounts}}
|
||||
volumeMounts:
|
||||
@@ -196,8 +192,6 @@ spec:
|
||||
# Google maps for using and displaying cameras, sites on a map
|
||||
- name: GOOGLEMAPS_KEY
|
||||
value: "{{ .Values.kerberoshub.frontend.googlemaps.apikey }}"
|
||||
- name: GOOGLEMAPS_PLACES_API
|
||||
value: "{{ .Values.kerberoshub.frontend.googlemaps.placesApi }}"
|
||||
|
||||
# features > chart
|
||||
- name: CHART_COLORS
|
||||
@@ -241,16 +235,10 @@ spec:
|
||||
value: "{{ .Values.kerberoshub.frontend.features.workflows.enabled }}"
|
||||
- name: FEATURE_AUDIT_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.audit.enabled }}"
|
||||
- name: FEATURE_PASSKEYS_ENABLED
|
||||
value: "{{ .Values.kerberoshub.api.passkeys.enabled }}"
|
||||
- name: FEATURE_DAY_OVERVIEW_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.dayOverview.enabled }}"
|
||||
- name: FEATURE_DEVICE_ANALYTICS_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.devices.analyticsEnabled }}"
|
||||
- name: FEATURE_DASHBOARD_SITES_FULLSCREEN_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.dashboard.sitesFullscreenEnabled }}"
|
||||
- name: FEATURE_DEVICE_DEVELOPER_TOOLS_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.devices.developerToolsEnabled }}"
|
||||
- name: FEATURE_ORGANISATIONS_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.organisations.enabled }}"
|
||||
- name: FEATURE_ORGANISATION_SWITCHER_ENABLED
|
||||
@@ -259,8 +247,6 @@ spec:
|
||||
value: "{{ .Values.kerberoshub.frontend.features.organisations.creationEnabled }}"
|
||||
- name: FEATURE_ORGANISATION_SETTINGS_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.organisations.settingsEnabled }}"
|
||||
- name: FEATURE_MEMBERS_ACCESS_PREVIEW_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.organisations.membersAccessPreviewEnabled }}"
|
||||
- name: FEATURE_PROJECTS_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.projects.enabled }}"
|
||||
- name: FEATURE_PROJECT_SWITCHER_ENABLED
|
||||
@@ -291,8 +277,6 @@ spec:
|
||||
value: "{{ .Values.kerberoshub.frontend.features.liveview.moqEnabled }}"
|
||||
- name: FEATURE_REMOTE_RECORDING_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.liveview.remoteRecordingEnabled }}"
|
||||
- name: FEATURE_LIVE_VIEW_DEVICE_STATUS_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.liveview.deviceStatusEnabled }}"
|
||||
- name: MOQ_RELAY_URL
|
||||
value: "{{ .Values.kerberoshub.frontend.features.liveview.moqRelayUrl }}"
|
||||
- name: MOQ_BROADCAST_PREFIX
|
||||
@@ -369,10 +353,6 @@ spec:
|
||||
value: "{{ .Values.kerberoshub.frontend.features.faceRedaction.classifierTracksEnabled }}"
|
||||
|
||||
# features > media
|
||||
- name: FEATURE_MEDIA_FORWARD_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.media.forward.enabled }}"
|
||||
- name: FEATURE_MEDIA_DETAIL_ANALYSIS_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.media.detail.analysis.enabled }}"
|
||||
- name: FEATURE_MEDIA_FILTER_DATE_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.media.filter.date.enabled }}"
|
||||
- name: FEATURE_MEDIA_FILTER_SITES_ENABLED
|
||||
@@ -385,8 +365,6 @@ spec:
|
||||
value: "{{ .Values.kerberoshub.frontend.features.media.filter.objectDetection.enabled }}"
|
||||
- name: FEATURE_MEDIA_FILTER_STAR_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.media.filter.star.enabled }}"
|
||||
- name: FEATURE_MEDIA_FILTER_ANALYSIS_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.media.filter.analysis.enabled }}"
|
||||
- name: FEATURE_MEDIA_FILTER_REGION_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.media.filter.region.enabled }}"
|
||||
- name: FEATURE_MEDIA_FILTER_SORT_ENABLED
|
||||
@@ -397,10 +375,6 @@ spec:
|
||||
value: "{{ .Values.kerberoshub.frontend.features.media.filter.markers.enabled }}"
|
||||
- name: FEATURE_MEDIA_FILTER_MARKER_OPTIONS_BY_DATE_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.media.filter.markerOptionsByDate.enabled }}"
|
||||
- name: FEATURE_MEDIA_FILTER_MARKER_CATEGORY_PRIORITY_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.media.filter.markerCategoryPriority.enabled }}"
|
||||
- name: FEATURE_MEDIA_FILTER_MARKER_CATEGORY_PRIORITY_CATEGORIES
|
||||
value: {{ .Values.kerberoshub.frontend.features.media.filter.markerCategoryPriority.categories | toJson | replace "\\" "\\\\" | replace "'" "\\u0027" | quote }}
|
||||
- name: FEATURE_MEDIA_FILTER_EVENTS_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.media.filter.events.enabled }}"
|
||||
- name: FEATURE_MEDIA_FILTER_TAGS_ENABLED
|
||||
|
||||
@@ -11,7 +11,7 @@ spec:
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: http
|
||||
targetPort: 80
|
||||
name: http
|
||||
selector:
|
||||
app: hub-frontend
|
||||
@@ -182,16 +182,12 @@ spec:
|
||||
- name: hub-frontend
|
||||
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberoshub.frontend.repository }}:{{ .Values.kerberoshub.frontend.tag }}"
|
||||
imagePullPolicy: {{ .Values.kerberoshub.frontend.pullPolicy }}
|
||||
{{- with .Values.kerberoshub.frontend.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 10 }}
|
||||
{{- end }}
|
||||
{{- with .Values.kerberoshub.frontend.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- containerPort: {{ .Values.kerberoshub.frontend.containerPort | default 8080 }}
|
||||
- containerPort: 80
|
||||
name: http
|
||||
{{- with .Values.kerberoshub.frontend.volumeMounts}}
|
||||
volumeMounts:
|
||||
@@ -279,8 +275,6 @@ spec:
|
||||
# Google maps for using and displaying cameras, sites on a map
|
||||
- name: GOOGLEMAPS_KEY
|
||||
value: "{{ .Values.kerberoshub.frontend.googlemaps.apikey }}"
|
||||
- name: GOOGLEMAPS_PLACES_API
|
||||
value: "{{ .Values.kerberoshub.frontend.googlemaps.placesApi }}"
|
||||
|
||||
# Zendesk for support
|
||||
- name: ZENDESK_URL
|
||||
@@ -320,16 +314,10 @@ spec:
|
||||
value: "{{ .Values.kerberoshub.frontend.features.workflows.enabled }}"
|
||||
- name: FEATURE_AUDIT_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.audit.enabled }}"
|
||||
- name: FEATURE_PASSKEYS_ENABLED
|
||||
value: "{{ .Values.kerberoshub.api.passkeys.enabled }}"
|
||||
- name: FEATURE_DAY_OVERVIEW_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.dayOverview.enabled }}"
|
||||
- name: FEATURE_DEVICE_ANALYTICS_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.devices.analyticsEnabled }}"
|
||||
- name: FEATURE_DASHBOARD_SITES_FULLSCREEN_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.dashboard.sitesFullscreenEnabled }}"
|
||||
- name: FEATURE_DEVICE_DEVELOPER_TOOLS_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.devices.developerToolsEnabled }}"
|
||||
- name: FEATURE_ORGANISATIONS_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.organisations.enabled }}"
|
||||
- name: FEATURE_ORGANISATION_SWITCHER_ENABLED
|
||||
@@ -338,8 +326,6 @@ spec:
|
||||
value: "{{ .Values.kerberoshub.frontend.features.organisations.creationEnabled }}"
|
||||
- name: FEATURE_ORGANISATION_SETTINGS_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.organisations.settingsEnabled }}"
|
||||
- name: FEATURE_MEMBERS_ACCESS_PREVIEW_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.organisations.membersAccessPreviewEnabled }}"
|
||||
- name: FEATURE_PROJECTS_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.projects.enabled }}"
|
||||
- name: FEATURE_PROJECT_SWITCHER_ENABLED
|
||||
@@ -372,8 +358,6 @@ spec:
|
||||
value: "{{ .Values.kerberoshub.frontend.features.liveview.moqEnabled }}"
|
||||
- name: FEATURE_REMOTE_RECORDING_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.liveview.remoteRecordingEnabled }}"
|
||||
- name: FEATURE_LIVE_VIEW_DEVICE_STATUS_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.liveview.deviceStatusEnabled }}"
|
||||
- name: MOQ_RELAY_URL
|
||||
value: "{{ .Values.kerberoshub.frontend.features.liveview.moqRelayUrl }}"
|
||||
- name: MOQ_BROADCAST_PREFIX
|
||||
@@ -461,10 +445,6 @@ spec:
|
||||
value: "{{ .Values.kerberoshub.frontend.features.faceRedaction.classifierTracksEnabled }}"
|
||||
|
||||
# features > media
|
||||
- name: FEATURE_MEDIA_FORWARD_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.media.forward.enabled }}"
|
||||
- name: FEATURE_MEDIA_DETAIL_ANALYSIS_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.media.detail.analysis.enabled }}"
|
||||
- name: FEATURE_MEDIA_FILTER_DATE_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.media.filter.date.enabled }}"
|
||||
- name: FEATURE_MEDIA_FILTER_SITES_ENABLED
|
||||
@@ -477,8 +457,6 @@ spec:
|
||||
value: "{{ .Values.kerberoshub.frontend.features.media.filter.objectDetection.enabled }}"
|
||||
- name: FEATURE_MEDIA_FILTER_STAR_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.media.filter.star.enabled }}"
|
||||
- name: FEATURE_MEDIA_FILTER_ANALYSIS_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.media.filter.analysis.enabled }}"
|
||||
- name: FEATURE_MEDIA_FILTER_REGION_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.media.filter.region.enabled }}"
|
||||
- name: FEATURE_MEDIA_FILTER_SORT_ENABLED
|
||||
@@ -489,10 +467,6 @@ spec:
|
||||
value: "{{ .Values.kerberoshub.frontend.features.media.filter.markers.enabled }}"
|
||||
- name: FEATURE_MEDIA_FILTER_MARKER_OPTIONS_BY_DATE_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.media.filter.markerOptionsByDate.enabled }}"
|
||||
- name: FEATURE_MEDIA_FILTER_MARKER_CATEGORY_PRIORITY_ENABLED
|
||||
value: "{{ .Values.kerberoshub.frontend.features.media.filter.markerCategoryPriority.enabled }}"
|
||||
- name: FEATURE_MEDIA_FILTER_MARKER_CATEGORY_PRIORITY_CATEGORIES
|
||||
value: {{ .Values.kerberoshub.frontend.features.media.filter.markerCategoryPriority.categories | toJson | replace "\\" "\\\\" | replace "'" "\\u0027" | quote }}
|
||||
- name: FEATURE_MEDIA_FILTER_DEFAULT_VIEW
|
||||
value: "{{ .Values.kerberoshub.frontend.features.media.filter.defaultView }}"
|
||||
- name: FEATURE_MEDIA_FILTER_EVENTS_ENABLED
|
||||
|
||||
@@ -18,14 +18,10 @@ spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configMaps/configmap-mongodb.yaml") . | sha256sum }}
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configmap-mongodb.yaml") . | sha256sum }}
|
||||
labels:
|
||||
app: hub-monitor-device
|
||||
spec:
|
||||
{{- with .Values.kerberoshub.monitordevice.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.kerberoshub.serviceAccount.create }}
|
||||
serviceAccountName: {{ default (printf "%s-%s-sa" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-") .Values.kerberoshub.serviceAccount.name }}
|
||||
{{- end }}
|
||||
@@ -51,10 +47,6 @@ spec:
|
||||
- name: hub-monitor-device
|
||||
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberoshub.monitordevice.repository }}:{{ .Values.kerberoshub.monitordevice.tag }}"
|
||||
imagePullPolicy: {{ .Values.kerberoshub.monitordevice.pullPolicy }}
|
||||
{{- with .Values.kerberoshub.monitordevice.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.kerberoshub.monitordevice.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
@@ -72,11 +64,8 @@ spec:
|
||||
- configMapRef:
|
||||
name: mongodb-config
|
||||
env:
|
||||
{{- include "hub.mongodb.appUriEnv" (dict "root" $ "app" "hub-monitor-device") | nindent 12 }}
|
||||
- name: LOG_LEVEL
|
||||
value: "{{ .Values.kerberoshub.monitordevice.logLevel }}"
|
||||
- name: DEVICE_SCAN_INTERVAL_SECONDS
|
||||
value: "{{ .Values.kerberoshub.monitordevice.scanIntervalSeconds | default 60 }}"
|
||||
- name: READ_ONLY
|
||||
value: "{{ .Values.readonly }}"
|
||||
# Mail settings
|
||||
@@ -92,21 +81,6 @@ spec:
|
||||
value: "{{ .Values.email.templates.device }}"
|
||||
- name: DEVICE_TITLE
|
||||
value: "{{ .Values.email.templates.deviceTitle }}"
|
||||
- name: DEVICE_GROUP_TEMPLATE
|
||||
value: "{{ .Values.email.templates.deviceGroup | default "device_group" }}"
|
||||
- name: DEVICE_GROUP_TITLE
|
||||
value: "{{ .Values.email.templates.deviceGroupTitle | default "[Device] Several cameras changed status" }}"
|
||||
{{- with .Values.email.templates.recording }}
|
||||
- name: RECORDING_TEMPLATE
|
||||
value: {{ . | quote }}
|
||||
{{- end }}
|
||||
{{- with .Values.email.templates.recordingTitle }}
|
||||
- name: RECORDING_TITLE
|
||||
value: {{ . | quote }}
|
||||
{{- end }}
|
||||
# Base URL for the link in grouped device notifications.
|
||||
- name: PUBLIC_URL
|
||||
value: "{{ .Values.kerberoshub.frontend.schema }}://{{ .Values.kerberoshub.frontend.url }}"
|
||||
|
||||
# - Plain SMTP
|
||||
- name: SMTP_SERVER
|
||||
|
||||
@@ -18,7 +18,7 @@ spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configMaps/configmap-mongodb.yaml") . | sha256sum }}
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configmap-mongodb.yaml") . | sha256sum }}
|
||||
labels:
|
||||
app: hub-reactivate-subscription
|
||||
spec:
|
||||
@@ -57,7 +57,6 @@ spec:
|
||||
- configMapRef:
|
||||
name: mongodb-config
|
||||
env:
|
||||
{{- include "hub.mongodb.appUriEnv" (dict "root" $ "app" "hub-reactivatesubscriptions") | nindent 12 }}
|
||||
- name: LOG_LEVEL
|
||||
value: "{{ .Values.kerberoshub.reactivate.logLevel }}"
|
||||
- name: READ_ONLY
|
||||
|
||||
@@ -1,86 +1,3 @@
|
||||
{{/*
|
||||
Reserve every effective identity, including disabled definitions, so enabling a
|
||||
workflow later cannot silently alias another workflow's runs or references.
|
||||
*/}}
|
||||
{{- define "kerberoshub.workflows.validateDefinitions" -}}
|
||||
{{- $identities := dict -}}
|
||||
{{- range $name, $wf := .Values.kerberoshub.workflows.definitions -}}
|
||||
{{- $id := sha256sum $name | trunc 24 -}}
|
||||
{{- if hasKey $wf "id" -}}
|
||||
{{- $invalid := printf "kerberoshub.workflows.definitions[%q].id must be a non-zero 24-character hexadecimal string (Mongo ObjectID); omit id for name-derived compatibility" $name -}}
|
||||
{{- if not (kindIs "string" $wf.id) -}}
|
||||
{{- fail $invalid -}}
|
||||
{{- end -}}
|
||||
{{- if or (not (regexMatch "^[0-9a-fA-F]{24}$" $wf.id)) (eq $wf.id "000000000000000000000000") -}}
|
||||
{{- fail $invalid -}}
|
||||
{{- end -}}
|
||||
{{- $id = lower $wf.id -}}
|
||||
{{- end -}}
|
||||
{{- if hasKey $identities $id -}}
|
||||
{{- fail (printf "kerberoshub.workflows.definitions: duplicate effective workflow id %q for %q and %q (including disabled definitions)" $id (index $identities $id) $name) -}}
|
||||
{{- end -}}
|
||||
{{- $_ := set $identities $id $name -}}
|
||||
{{- end -}}
|
||||
{{- include "kerberoshub.workflows.validateSystemTags" . -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Guard the definitions that can select devices by system tag. The engine reads
|
||||
JSON keys case-insensitively and ignores unknown ones, so a misspelled or
|
||||
re-cased key silently changes what a definition selects (a dropped
|
||||
systemTags scope makes a global workflow run on every device). Therefore:
|
||||
- definitions and triggers may only use the keys the engine knows, spelled
|
||||
exactly (definition: id, enabled, triggers, stages; trigger: the
|
||||
models.WorkflowTrigger fields);
|
||||
- while deviceTags.system.enabled is false, no definition may mention system
|
||||
tags anywhere (trigger systemTags, or a device.systemTags condition at any
|
||||
depth, in triggers, shared conditions or stage needs), matched
|
||||
case-insensitively on the whole definition;
|
||||
- every system tag a trigger selects must be in deviceTags.system.catalog,
|
||||
so a typo fails rendering instead of never matching a device.
|
||||
Disabled definitions are checked too, so enabling one cannot fail later.
|
||||
*/}}
|
||||
{{- define "kerberoshub.workflows.validateSystemTags" -}}
|
||||
{{- include "kerberoshub.deviceTags.system.validate" . -}}
|
||||
{{- $catalog := dict -}}
|
||||
{{- range $tag := (include "kerberoshub.deviceTags.system.catalog" . | fromJsonArray) -}}
|
||||
{{- $_ := set $catalog (toString $tag.key) true -}}
|
||||
{{- end -}}
|
||||
{{- $enabled := include "kerberoshub.deviceTags.system.enabled" . -}}
|
||||
{{- $definitionKeys := list "id" "enabled" "triggers" "stages" -}}
|
||||
{{- $triggerKeys := list "type" "edgeId" "sharedConditions" "devices" "siteIds" "groupIds" "systemTags" "classifications" "conditionMode" "conditions" "weeklySchedule" "surfaces" -}}
|
||||
{{- range $name, $wf := .Values.kerberoshub.workflows.definitions -}}
|
||||
{{- if $wf -}}
|
||||
{{- if not (kindIs "map" $wf) -}}
|
||||
{{- fail (printf "kerberoshub.workflows.definitions[%q] must be an object" $name) -}}
|
||||
{{- end -}}
|
||||
{{- if and (not $enabled) (contains "systemtags" (lower (toJson $wf))) -}}
|
||||
{{- fail (printf "kerberoshub.workflows.definitions[%q] selects devices by system tag (systemTags or a device.systemTags condition), but deviceTags.system.enabled is false" $name) -}}
|
||||
{{- end -}}
|
||||
{{- range $key, $_ := $wf -}}
|
||||
{{- if not (has $key $definitionKeys) -}}
|
||||
{{- fail (printf "kerberoshub.workflows.definitions[%q]: unknown key %q (allowed: %s). Keys are case-sensitive; a misspelled key would be ignored" $name $key (join ", " $definitionKeys)) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- range $i, $trigger := ($wf.triggers | default list) -}}
|
||||
{{- if not (kindIs "map" $trigger) -}}
|
||||
{{- fail (printf "kerberoshub.workflows.definitions[%q].triggers[%d] must be an object" $name $i) -}}
|
||||
{{- end -}}
|
||||
{{- range $key, $_ := $trigger -}}
|
||||
{{- if not (has $key $triggerKeys) -}}
|
||||
{{- fail (printf "kerberoshub.workflows.definitions[%q].triggers[%d]: unknown key %q (allowed: %s). Keys are case-sensitive; a misspelled key would be ignored and widen the trigger" $name $i $key (join ", " $triggerKeys)) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- range $tag := ($trigger.systemTags | default list) -}}
|
||||
{{- if not (hasKey $catalog (toString $tag)) -}}
|
||||
{{- fail (printf "kerberoshub.workflows.definitions[%q].triggers[%d].systemTags: %q is not in deviceTags.system.catalog; add it to the system tag catalog" $name $i (toString $tag)) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Assemble the deployment-global workflow definitions (WORKFLOW_DEFINITIONS) as a
|
||||
JSON array from every *enabled* workflow under kerberoshub.workflows.definitions.
|
||||
@@ -90,9 +7,9 @@ opens its own run and dispatches only its own stages. Organisation-scoped
|
||||
database workflows are discovered separately at runtime.
|
||||
|
||||
Each enabled definition contributes one workflow object:
|
||||
id optional explicit stable identity, normalized to lowercase. Omitted
|
||||
for legacy definitions; their effective id is SHA256(name)[:24].
|
||||
name the map key (mutable display name when an explicit id is supplied).
|
||||
name the map key (the workflow's human-readable name; also its identity —
|
||||
the engine derives a stable id from it when the definition carries
|
||||
no explicit id).
|
||||
enabled always true here (a disabled definition is skipped entirely).
|
||||
source "config" — provenance marking a Helm-defined, deployment-global,
|
||||
ops-managed workflow (read-only in the API, no owning organisation).
|
||||
@@ -111,7 +28,6 @@ Each enabled definition contributes one workflow object:
|
||||
needsMode conditional stages: "any" (default) | "all".
|
||||
*/}}
|
||||
{{- define "kerberoshub.workflows.workflowDefinitions" -}}
|
||||
{{- include "kerberoshub.workflows.validateDefinitions" . -}}
|
||||
{{- $defs := list -}}
|
||||
{{- $services := .Values.kerberoshub.services | default dict -}}
|
||||
{{- range $name, $wf := .Values.kerberoshub.workflows.definitions -}}
|
||||
@@ -127,7 +43,6 @@ Each enabled definition contributes one workflow object:
|
||||
{{- $stages = append $stages $entry -}}
|
||||
{{- end -}}
|
||||
{{- $def := dict "name" $name "enabled" true "source" "config" "triggers" (default (list (dict "type" "automatic")) $wf.triggers) "stages" $stages -}}
|
||||
{{- if hasKey $wf "id" }}{{- $_ := set $def "id" (lower $wf.id) -}}{{- end }}
|
||||
{{- $defs = append $defs $def -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
@@ -144,11 +59,9 @@ use this only to embed the same queue on a synthetic stage.
|
||||
{{- define "kerberoshub.workflows.stageQueues" -}}
|
||||
{{- $queues := dict -}}
|
||||
{{- range $operation, $service := (.Values.kerberoshub.services | default dict) -}}
|
||||
{{- if ne $operation "workflows" -}}
|
||||
{{- with $service.queue -}}
|
||||
{{- $_ := set $queues $operation . -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- $queues | toJson -}}
|
||||
{{- end -}}
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
redaction) declares its destination provider with the named field
|
||||
services.<name>.storageProvider, rendered as KERBEROS_STORAGE_PROVIDER; a
|
||||
read-only stage omits it and gets no provider env.
|
||||
|
||||
Worker-specific settings go in services.<name>.env (plain values) and
|
||||
services.<name>.secretEnv (references to keys in an existing Secret).
|
||||
*/ -}}
|
||||
{{- if and (or (eq .Values.mode "all") (eq .Values.mode "pipeline")) .Values.kerberoshub.workflows.enabled -}}
|
||||
{{- $root := . -}}
|
||||
@@ -50,10 +47,6 @@ spec:
|
||||
labels:
|
||||
app: hub-{{ $name }}
|
||||
spec:
|
||||
{{- with $svc.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if $root.Values.kerberoshub.serviceAccount.create }}
|
||||
serviceAccountName: {{ default (printf "%s-%s-sa" $root.Release.Name $root.Chart.Name | trunc 63 | trimSuffix "-") $root.Values.kerberoshub.serviceAccount.name }}
|
||||
{{- end }}
|
||||
@@ -73,10 +66,6 @@ spec:
|
||||
- name: hub-{{ $name }}
|
||||
image: "{{ $root.Values.global.imageRegistry }}{{ $svc.repository }}:{{ $svc.tag }}"
|
||||
imagePullPolicy: {{ $svc.pullPolicy | default "IfNotPresent" }}
|
||||
{{- with $svc.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 10 }}
|
||||
{{- end }}
|
||||
{{- with $svc.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
@@ -125,14 +114,6 @@ spec:
|
||||
- name: KERBEROS_STORAGE_PROVIDER
|
||||
value: {{ . | quote }}
|
||||
{{- end }}
|
||||
{{- if $root.Values.opentelemetry.enabled }}
|
||||
|
||||
# OpenTelemetry tracing: the stage continues the engine's dispatch span
|
||||
# (the run's traceparent) and follows its sampling decision.
|
||||
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
||||
value: "{{ $root.Values.opentelemetry.collector.endpoint }}"
|
||||
{{- include "hub.opentelemetry.samplerEnv" $root | trim | nindent 8 }}
|
||||
{{- end }}
|
||||
|
||||
# Per-stage tuning knobs. Any key/value under services.<name>.env is
|
||||
# rendered verbatim as container env, so a worker can be tuned from
|
||||
@@ -141,19 +122,6 @@ spec:
|
||||
{{- range $key, $value := $svc.env }}
|
||||
- name: {{ $key }}
|
||||
value: {{ $value | quote }}
|
||||
{{- end }} {{- with $svc.secretEnv }}
|
||||
|
||||
# Per-stage secret env. Each key under services.<name>.secretEnv maps a
|
||||
# container env var to a key in an existing Kubernetes Secret
|
||||
# ({name, key}), so credentials such as a model API key stay out of
|
||||
# values. The Secret must exist in the release namespace.
|
||||
{{- range $key, $ref := . }}
|
||||
- name: {{ $key }}
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ required (printf "kerberoshub.services.%s.secretEnv.%s.name is required" $name $key) $ref.name | quote }}
|
||||
key: {{ required (printf "kerberoshub.services.%s.secretEnv.%s.key is required" $name $key) $ref.key | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: v1
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
{{- include "kerberoshub.workflows.validateDefinitions" . -}}
|
||||
{{- if and (or (eq .Values.mode "all") (eq .Values.mode "pipeline")) .Values.kerberoshub.workflows.enabled -}}
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -19,14 +18,10 @@ spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configMaps/configmap-mongodb.yaml") . | sha256sum }}
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configmap-mongodb.yaml") . | sha256sum }}
|
||||
labels:
|
||||
app: hub-workflows
|
||||
spec:
|
||||
{{- with .Values.kerberoshub.services.workflows.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.kerberoshub.serviceAccount.create }}
|
||||
serviceAccountName: {{ default (printf "%s-%s-sa" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-") .Values.kerberoshub.serviceAccount.name }}
|
||||
{{- end }}
|
||||
@@ -52,10 +47,6 @@ spec:
|
||||
- name: hub-workflows
|
||||
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberoshub.services.workflows.repository }}:{{ .Values.kerberoshub.services.workflows.tag }}"
|
||||
imagePullPolicy: {{ .Values.kerberoshub.services.workflows.pullPolicy }}
|
||||
{{- with .Values.kerberoshub.services.workflows.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 10 }}
|
||||
{{- end }}
|
||||
{{- with .Values.kerberoshub.services.workflows.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
@@ -73,7 +64,6 @@ spec:
|
||||
- configMapRef:
|
||||
name: mongodb-config
|
||||
env:
|
||||
{{- include "hub.mongodb.appUriEnv" (dict "root" $ "app" "hub-workflows") | nindent 8 }}
|
||||
- name: LOG_LEVEL
|
||||
value: "{{ .Values.kerberoshub.services.workflows.logLevel }}"
|
||||
- name: QUEUE_SYSTEM
|
||||
@@ -85,15 +75,12 @@ spec:
|
||||
# under kerberoshub.workflows.definitions (see _workflows-helpers.tpl).
|
||||
# Definitions are the engine's boot-loaded config source and deployment
|
||||
# stage catalog. Organisation-scoped database workflows are read per
|
||||
# recording; an in-cluster engine refuses to start only when both the
|
||||
# config definitions and the stage-queue catalog below are empty.
|
||||
# recording; an in-cluster engine still requires at least one config
|
||||
# definition so an empty catalog cannot silently drop traffic.
|
||||
- name: WORKFLOWS_QUEUE
|
||||
value: "{{ .Values.kerberoshub.services.workflows.queue }}"
|
||||
- name: WORKFLOW_DEFINITIONS
|
||||
value: {{ include "kerberoshub.workflows.workflowDefinitions" . | quote }}
|
||||
# Operation→queue catalog so DB workflows reach workers no config workflow references.
|
||||
- name: WORKFLOW_STAGE_QUEUES
|
||||
value: {{ include "kerberoshub.workflows.stageQueues" . | quote }}
|
||||
|
||||
# RabbitMQ settings
|
||||
- name: RABBITMQ_HOST
|
||||
@@ -123,7 +110,6 @@ spec:
|
||||
value: "{{ .Values.opentelemetry.routingEnabled }}"
|
||||
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
||||
value: "{{ .Values.opentelemetry.collector.endpoint }}"
|
||||
{{- include "hub.opentelemetry.samplerEnv" $ | trim | nindent 8 }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: v1
|
||||
|
||||
@@ -4,10 +4,6 @@ kind: Deployment
|
||||
metadata:
|
||||
name: pipe-analysis
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- with .Values.kerberospipeline.analysis.deploymentAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
replicas: {{ .Values.kerberospipeline.analysis.replicas }}
|
||||
selector:
|
||||
@@ -22,7 +18,7 @@ spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configMaps/configmap-mongodb.yaml") . | sha256sum }}
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configmap-mongodb.yaml") . | sha256sum }}
|
||||
labels:
|
||||
app: pipe-analysis
|
||||
spec:
|
||||
@@ -68,7 +64,6 @@ spec:
|
||||
- configMapRef:
|
||||
name: mongodb-config
|
||||
env:
|
||||
{{- include "hub.mongodb.appUriEnv" (dict "root" $ "app" "hub-pipeline-analysis") | nindent 8 }}
|
||||
- name: LOG_LEVEL
|
||||
value: "{{ .Values.kerberospipeline.analysis.logLevel }}"
|
||||
- name: CLOUD_PROVIDER
|
||||
@@ -121,20 +116,6 @@ spec:
|
||||
# Sprites
|
||||
- name: SPRITE_ENABLED
|
||||
value: "{{ .Values.kerberospipeline.sprite.enabled }}"
|
||||
|
||||
# Dominant colors
|
||||
- name: DOMINANTCOLOR_ENABLED
|
||||
value: "{{ .Values.kerberospipeline.dominantColor.enabled }}"
|
||||
|
||||
# Thumbnail regeneration from classified frames (opt-in)
|
||||
- name: THUMBNAIL_REGENERATION_ENABLED
|
||||
value: {{ .Values.kerberospipeline.analysis.thumbnailRegeneration.enabled | default false | quote }}
|
||||
|
||||
# In-memory cache for hot-path MongoDB lookups
|
||||
- name: DB_CACHE_ENABLED
|
||||
value: {{ .Values.kerberospipeline.analysis.dbCache.enabled | default false | quote }}
|
||||
- name: DB_CACHE_TTL
|
||||
value: {{ .Values.kerberospipeline.analysis.dbCache.ttl | default "5m" | quote }}
|
||||
|
||||
{{- if .Values.opentelemetry.enabled }}
|
||||
# Open Telemetry tracing
|
||||
@@ -144,7 +125,6 @@ spec:
|
||||
value: "{{ .Values.opentelemetry.routingEnabled }}"
|
||||
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
||||
value: "{{ .Values.opentelemetry.collector.endpoint }}"
|
||||
{{- include "hub.opentelemetry.samplerEnv" $ | trim | nindent 8 }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: v1
|
||||
|
||||
@@ -4,10 +4,6 @@ kind: Deployment
|
||||
metadata:
|
||||
name: pipe-counting
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- with .Values.kerberospipeline.counting.deploymentAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
replicas: {{ .Values.kerberospipeline.counting.replicas }}
|
||||
selector:
|
||||
@@ -24,10 +20,6 @@ spec:
|
||||
labels:
|
||||
app: pipe-counting
|
||||
spec:
|
||||
{{- with .Values.kerberospipeline.counting.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.kerberoshub.serviceAccount.create }}
|
||||
serviceAccountName: {{ default (printf "%s-%s-sa" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-") .Values.kerberoshub.serviceAccount.name }}
|
||||
{{- end }}
|
||||
@@ -47,10 +39,6 @@ spec:
|
||||
- name: pipe-counting
|
||||
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberospipeline.counting.repository }}:{{ .Values.kerberospipeline.counting.tag }}"
|
||||
imagePullPolicy: {{ .Values.kerberospipeline.counting.pullPolicy }}
|
||||
{{- with .Values.kerberospipeline.counting.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 10 }}
|
||||
{{- end }}
|
||||
{{- with .Values.kerberospipeline.counting.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
@@ -97,7 +85,6 @@ spec:
|
||||
value: "{{ .Values.opentelemetry.routingEnabled }}"
|
||||
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
||||
value: "{{ .Values.opentelemetry.collector.endpoint }}"
|
||||
{{- include "hub.opentelemetry.samplerEnv" $ | trim | nindent 8 }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: v1
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
{{- if and (or (eq .Values.mode "all") (eq .Values.mode "pipeline")) (eq .Values.kerberospipeline.dominantColor.enabled true) -}}
|
||||
{{- if or (eq .Values.mode "all") (eq .Values.mode "pipeline") -}}
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: pipe-dominantcolor
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- with .Values.kerberospipeline.dominantColor.deploymentAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
replicas: {{ .Values.kerberospipeline.dominantColor.replicas }}
|
||||
selector:
|
||||
@@ -24,10 +20,6 @@ spec:
|
||||
labels:
|
||||
app: pipe-dominantcolor
|
||||
spec:
|
||||
{{- with .Values.kerberospipeline.dominantColor.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.kerberoshub.serviceAccount.create }}
|
||||
serviceAccountName: {{ default (printf "%s-%s-sa" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-") .Values.kerberoshub.serviceAccount.name }}
|
||||
{{- end }}
|
||||
@@ -47,10 +39,6 @@ spec:
|
||||
- name: pipe-dominantcolor
|
||||
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberospipeline.dominantColor.repository }}:{{ .Values.kerberospipeline.dominantColor.tag }}"
|
||||
imagePullPolicy: {{ .Values.kerberospipeline.dominantColor.pullPolicy }}
|
||||
{{- with .Values.kerberospipeline.dominantColor.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 10 }}
|
||||
{{- end }}
|
||||
{{- with .Values.kerberospipeline.dominantColor.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
@@ -97,7 +85,6 @@ spec:
|
||||
value: "{{ .Values.opentelemetry.routingEnabled }}"
|
||||
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
||||
value: "{{ .Values.opentelemetry.collector.endpoint }}"
|
||||
{{- include "hub.opentelemetry.samplerEnv" $ | trim | nindent 8 }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: v1
|
||||
|
||||
@@ -4,10 +4,6 @@ kind: Deployment
|
||||
metadata:
|
||||
name: pipe-event
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- with .Values.kerberospipeline.event.deploymentAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
replicas: {{ .Values.kerberospipeline.event.replicas }}
|
||||
selector:
|
||||
@@ -22,7 +18,7 @@ spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configMaps/configmap-mongodb.yaml") . | sha256sum }}
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configmap-mongodb.yaml") . | sha256sum }}
|
||||
labels:
|
||||
app: pipe-event
|
||||
spec:
|
||||
@@ -70,7 +66,6 @@ spec:
|
||||
- configMapRef:
|
||||
name: mongodb-config
|
||||
env:
|
||||
{{- include "hub.mongodb.appUriEnv" (dict "root" $ "app" "hub-pipeline-event") | nindent 8 }}
|
||||
- name: LOG_LEVEL
|
||||
value: "{{ .Values.kerberospipeline.event.logLevel }}"
|
||||
- name: READ_ONLY
|
||||
@@ -110,7 +105,6 @@ spec:
|
||||
value: "{{ .Values.opentelemetry.routingEnabled }}"
|
||||
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
||||
value: "{{ .Values.opentelemetry.collector.endpoint }}"
|
||||
{{- include "hub.opentelemetry.samplerEnv" $ | trim | nindent 8 }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: v1
|
||||
|
||||
@@ -18,7 +18,7 @@ spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configMaps/configmap-mongodb.yaml") . | sha256sum }}
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configmap-mongodb.yaml") . | sha256sum }}
|
||||
labels:
|
||||
app: pipe-export
|
||||
spec:
|
||||
@@ -64,7 +64,6 @@ spec:
|
||||
- configMapRef:
|
||||
name: mongodb-config
|
||||
env:
|
||||
{{- include "hub.mongodb.appUriEnv" (dict "root" $ "app" "hub-pipeline-export") | nindent 8 }}
|
||||
- name: LOG_LEVEL
|
||||
value: "{{ .Values.kerberospipeline.export.logLevel }}"
|
||||
{{- if .Values.kerberospipeline.export.playerAssetsPath }}
|
||||
@@ -142,7 +141,6 @@ spec:
|
||||
value: "{{ .Values.opentelemetry.routingEnabled }}"
|
||||
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
||||
value: "{{ .Values.opentelemetry.collector.endpoint }}"
|
||||
{{- include "hub.opentelemetry.samplerEnv" $ | trim | nindent 8 }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: v1
|
||||
|
||||
@@ -4,10 +4,6 @@ kind: Deployment
|
||||
metadata:
|
||||
name: pipe-monitor
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- with .Values.kerberospipeline.monitor.deploymentAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
replicas: {{ .Values.kerberospipeline.monitor.replicas }}
|
||||
selector:
|
||||
@@ -22,7 +18,7 @@ spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configMaps/configmap-mongodb.yaml") . | sha256sum }}
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configmap-mongodb.yaml") . | sha256sum }}
|
||||
labels:
|
||||
app: pipe-monitor
|
||||
spec:
|
||||
@@ -68,7 +64,6 @@ spec:
|
||||
- configMapRef:
|
||||
name: mongodb-config
|
||||
env:
|
||||
{{- include "hub.mongodb.appUriEnv" (dict "root" $ "app" "hub-pipeline-monitor") | nindent 8 }}
|
||||
# Application
|
||||
- name: LOG_LEVEL
|
||||
value: "{{ .Values.kerberospipeline.monitor.logLevel }}"
|
||||
@@ -131,7 +126,6 @@ spec:
|
||||
value: "{{ .Values.opentelemetry.routingEnabled }}"
|
||||
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
||||
value: "{{ .Values.opentelemetry.collector.endpoint }}"
|
||||
{{- include "hub.opentelemetry.samplerEnv" $ | trim | nindent 8 }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: v1
|
||||
|
||||
@@ -18,7 +18,7 @@ spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configMaps/configmap-mongodb.yaml") . | sha256sum }}
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configmap-mongodb.yaml") . | sha256sum }}
|
||||
labels:
|
||||
app: pipe-notify-test
|
||||
spec:
|
||||
@@ -64,7 +64,6 @@ spec:
|
||||
- configMapRef:
|
||||
name: mongodb-config
|
||||
env:
|
||||
{{- include "hub.mongodb.appUriEnv" (dict "root" $ "app" "hub-pipeline-notification-test") | nindent 8 }}
|
||||
- name: QUEUE_SYSTEM
|
||||
value: "{{ .Values.queueProvider }}"
|
||||
|
||||
@@ -130,7 +129,6 @@ spec:
|
||||
value: "{{ .Values.opentelemetry.routingEnabled }}"
|
||||
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
||||
value: "{{ .Values.opentelemetry.collector.endpoint }}"
|
||||
{{- include "hub.opentelemetry.samplerEnv" $ | trim | nindent 8 }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: v1
|
||||
|
||||
@@ -4,10 +4,6 @@ kind: Deployment
|
||||
metadata:
|
||||
name: pipe-notify
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- with .Values.kerberospipeline.notify.deploymentAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
replicas: {{ .Values.kerberospipeline.notify.replicas }}
|
||||
selector:
|
||||
@@ -22,14 +18,10 @@ spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configMaps/configmap-mongodb.yaml") . | sha256sum }}
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configmap-mongodb.yaml") . | sha256sum }}
|
||||
labels:
|
||||
app: pipe-notify
|
||||
spec:
|
||||
{{- with .Values.kerberospipeline.notify.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.kerberoshub.serviceAccount.create }}
|
||||
serviceAccountName: {{ default (printf "%s-%s-sa" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-") .Values.kerberoshub.serviceAccount.name }}
|
||||
{{- end }}
|
||||
@@ -55,10 +47,6 @@ spec:
|
||||
- name: pipe-notify
|
||||
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberospipeline.notify.repository }}:{{ .Values.kerberospipeline.notify.tag }}"
|
||||
imagePullPolicy: {{ .Values.kerberospipeline.notify.pullPolicy }}
|
||||
{{- with .Values.kerberospipeline.notify.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 10 }}
|
||||
{{- end }}
|
||||
{{- with .Values.kerberospipeline.notify.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
@@ -76,7 +64,6 @@ spec:
|
||||
- configMapRef:
|
||||
name: mongodb-config
|
||||
env:
|
||||
{{- include "hub.mongodb.appUriEnv" (dict "root" $ "app" "hub-pipeline-notification") | nindent 8 }}
|
||||
- name: LOG_LEVEL
|
||||
value: "{{ .Values.kerberospipeline.notify.logLevel }}"
|
||||
- name: NOTIFICATION_EXPIRY_MINUTES
|
||||
@@ -106,14 +93,6 @@ spec:
|
||||
value: "{{ .Values.rabbitmq.username }}"
|
||||
- name: RABBITMQ_PASSWORD
|
||||
value: "{{ .Values.rabbitmq.password }}"
|
||||
- name: AUDIT_ENABLED
|
||||
value: {{ and .Values.audit.enabled .Values.kerberospipeline.notify.audit.enabled | quote }}
|
||||
{{- if and .Values.audit.enabled .Values.kerberospipeline.notify.audit.enabled }}
|
||||
- name: AUDIT_QUEUE
|
||||
value: {{ .Values.audit.intake.queue | quote }}
|
||||
- name: AUDIT_DEADLETTER_QUEUE
|
||||
value: {{ .Values.audit.intake.deadletterQueue | quote }}
|
||||
{{- end }}
|
||||
|
||||
# Kerberos Vault
|
||||
- name: STORAGE_URI
|
||||
@@ -184,7 +163,6 @@ spec:
|
||||
value: "{{ .Values.opentelemetry.routingEnabled }}"
|
||||
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
||||
value: "{{ .Values.opentelemetry.collector.endpoint }}"
|
||||
{{- include "hub.opentelemetry.samplerEnv" $ | trim | nindent 8 }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: v1
|
||||
|
||||
@@ -4,10 +4,6 @@ kind: Deployment
|
||||
metadata:
|
||||
name: pipe-sequence
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- with .Values.kerberospipeline.sequence.deploymentAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
replicas: {{ .Values.kerberospipeline.sequence.replicas }}
|
||||
selector:
|
||||
@@ -22,7 +18,7 @@ spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configMaps/configmap-mongodb.yaml") . | sha256sum }}
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configmap-mongodb.yaml") . | sha256sum }}
|
||||
labels:
|
||||
app: pipe-sequence
|
||||
spec:
|
||||
@@ -68,9 +64,6 @@ spec:
|
||||
- configMapRef:
|
||||
name: mongodb-config
|
||||
env:
|
||||
{{- include "hub.mongodb.appUriEnv" (dict "root" $ "app" "hub-pipeline-sequence") | nindent 8 }}
|
||||
- name: LOG_LEVEL
|
||||
value: "{{ .Values.kerberospipeline.sequence.logLevel }}"
|
||||
- name: CLOUD_PROVIDER
|
||||
value: "{{ .Values.cloudProvider }}"
|
||||
- name: QUEUE_SYSTEM
|
||||
@@ -106,7 +99,6 @@ spec:
|
||||
value: "{{ .Values.opentelemetry.routingEnabled }}"
|
||||
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
||||
value: "{{ .Values.opentelemetry.collector.endpoint }}"
|
||||
{{- include "hub.opentelemetry.samplerEnv" $ | trim | nindent 8 }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: v1
|
||||
|
||||
@@ -4,10 +4,6 @@ kind: Deployment
|
||||
metadata:
|
||||
name: pipe-sprite
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- with .Values.kerberospipeline.sprite.deploymentAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
replicas: {{ .Values.kerberospipeline.sprite.replicas }}
|
||||
selector:
|
||||
@@ -87,10 +83,6 @@ spec:
|
||||
value: "{{ .Values.rabbitmq.username }}"
|
||||
- name: RABBITMQ_PASSWORD
|
||||
value: "{{ .Values.rabbitmq.password }}"
|
||||
{{- with .Values.kerberospipeline.sprite.prefetchCount }}
|
||||
- name: RABBITMQ_PREFETCH_COUNT
|
||||
value: {{ . | quote }}
|
||||
{{- end }}
|
||||
|
||||
# There is an option to store the sprites in a storage provider in Kerberos Vault.
|
||||
- name: VAULT_SPRITE_ENABLED
|
||||
@@ -116,7 +108,6 @@ spec:
|
||||
value: "{{ .Values.opentelemetry.routingEnabled }}"
|
||||
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
||||
value: "{{ .Values.opentelemetry.collector.endpoint }}"
|
||||
{{- include "hub.opentelemetry.samplerEnv" $ | trim | nindent 8 }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: v1
|
||||
|
||||
@@ -4,10 +4,6 @@ kind: Deployment
|
||||
metadata:
|
||||
name: pipe-throttler
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- with .Values.kerberospipeline.throttler.deploymentAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
replicas: {{ .Values.kerberospipeline.throttler.replicas }}
|
||||
selector:
|
||||
@@ -22,14 +18,10 @@ spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configMaps/configmap-mongodb.yaml") . | sha256sum }}
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configmap-mongodb.yaml") . | sha256sum }}
|
||||
labels:
|
||||
app: pipe-throttler
|
||||
spec:
|
||||
{{- with .Values.kerberospipeline.throttler.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.kerberoshub.serviceAccount.create }}
|
||||
serviceAccountName: {{ default (printf "%s-%s-sa" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-") .Values.kerberoshub.serviceAccount.name }}
|
||||
{{- end }}
|
||||
@@ -55,10 +47,6 @@ spec:
|
||||
- name: pipe-throttler
|
||||
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberospipeline.throttler.repository }}:{{ .Values.kerberospipeline.throttler.tag }}"
|
||||
imagePullPolicy: {{ .Values.kerberospipeline.throttler.pullPolicy }}
|
||||
{{- with .Values.kerberospipeline.throttler.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 10 }}
|
||||
{{- end }}
|
||||
{{- with .Values.kerberospipeline.throttler.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
@@ -76,7 +64,6 @@ spec:
|
||||
- configMapRef:
|
||||
name: mongodb-config
|
||||
env:
|
||||
{{- include "hub.mongodb.appUriEnv" (dict "root" $ "app" "hub-pipeline-throttler") | nindent 8 }}
|
||||
- name: LOG_LEVEL
|
||||
value: "{{ .Values.kerberospipeline.throttler.logLevel }}"
|
||||
- name: CLOUD_PROVIDER
|
||||
@@ -114,7 +101,6 @@ spec:
|
||||
value: "{{ .Values.opentelemetry.routingEnabled }}"
|
||||
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
||||
value: "{{ .Values.opentelemetry.collector.endpoint }}"
|
||||
{{- include "hub.opentelemetry.samplerEnv" $ | trim | nindent 8 }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: v1
|
||||
|
||||
@@ -4,10 +4,6 @@ kind: Deployment
|
||||
metadata:
|
||||
name: pipe-thumbnail
|
||||
namespace: {{ .Release.Namespace }}
|
||||
{{- with .Values.kerberospipeline.thumbnail.deploymentAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
replicas: {{ .Values.kerberospipeline.thumbnail.replicas }}
|
||||
selector:
|
||||
@@ -24,10 +20,6 @@ spec:
|
||||
labels:
|
||||
app: pipe-thumbnail
|
||||
spec:
|
||||
{{- with .Values.kerberospipeline.thumbnail.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.kerberoshub.serviceAccount.create }}
|
||||
serviceAccountName: {{ default (printf "%s-%s-sa" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-") .Values.kerberoshub.serviceAccount.name }}
|
||||
{{- end }}
|
||||
@@ -47,10 +39,6 @@ spec:
|
||||
- name: pipe-thumbnail
|
||||
image: "{{ .Values.global.imageRegistry }}{{ .Values.kerberospipeline.thumbnail.repository }}:{{ .Values.kerberospipeline.thumbnail.tag }}"
|
||||
imagePullPolicy: {{ .Values.kerberospipeline.thumbnail.pullPolicy }}
|
||||
{{- with .Values.kerberospipeline.thumbnail.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 10 }}
|
||||
{{- end }}
|
||||
{{- with .Values.kerberospipeline.thumbnail.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
@@ -95,10 +83,6 @@ spec:
|
||||
value: "{{ .Values.rabbitmq.username }}"
|
||||
- name: RABBITMQ_PASSWORD
|
||||
value: "{{ .Values.rabbitmq.password }}"
|
||||
{{- with .Values.kerberospipeline.thumbnail.prefetchCount }}
|
||||
- name: RABBITMQ_PREFETCH_COUNT
|
||||
value: {{ . | quote }}
|
||||
{{- end }}
|
||||
|
||||
# Thumbnail generation
|
||||
- name: THUMBNAIL_QUALITY
|
||||
@@ -107,8 +91,6 @@ spec:
|
||||
value: "{{ .Values.kerberospipeline.thumbnail.width }}"
|
||||
- name: THUMBNAIL_HEIGHT
|
||||
value: "{{ .Values.kerberospipeline.thumbnail.height }}"
|
||||
- name: THUMBNAIL_SKIP_NON_KEYFRAMES
|
||||
value: "{{ .Values.kerberospipeline.thumbnail.skipNonKeyframes }}"
|
||||
|
||||
# There is an option to store the thumbnails in a storage provider in Kerberos Vault.
|
||||
# This will lower storage in mongodb.
|
||||
@@ -129,7 +111,6 @@ spec:
|
||||
value: "{{ .Values.opentelemetry.routingEnabled }}"
|
||||
- name: OTEL_EXPORTER_OTLP_ENDPOINT
|
||||
value: "{{ .Values.opentelemetry.collector.endpoint }}"
|
||||
{{- include "hub.opentelemetry.samplerEnv" $ | trim | nindent 8 }}
|
||||
{{- end }}
|
||||
|
||||
---
|
||||
|
||||
@@ -19,7 +19,7 @@ spec:
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configMaps/configmap-mongodb.yaml") . | sha256sum }}
|
||||
checksum/config: {{ include (print $.Template.BasePath "/configmap-mongodb.yaml") . | sha256sum }}
|
||||
labels:
|
||||
app: vault-forwarder
|
||||
spec:
|
||||
@@ -62,7 +62,6 @@ spec:
|
||||
- configMapRef:
|
||||
name: mongodb-config
|
||||
env:
|
||||
{{- include "hub.mongodb.appUriEnv" (dict "root" $ "app" "vault-forwarder") | nindent 10 }}
|
||||
- name: ITERATION_SPEED
|
||||
value: "5"
|
||||
- name: BUFFER_TIME
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"classificationCatalogExistingConfigMap": {
|
||||
"type": "string",
|
||||
"maxLength": 253,
|
||||
"pattern": "^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$"
|
||||
},
|
||||
"classificationCatalog": {
|
||||
"type": ["array", "null"],
|
||||
"minItems": 1,
|
||||
"description": "Optional inline classification catalog in the Hub shared choice catalog format; replaces the bundled defaults in the chart's classification-catalog ConfigMap. Omit (or null) for the bundled defaults. Keys must be unique (checked when rendering).",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["key", "label"],
|
||||
"properties": {
|
||||
"key": {"type": "string", "minLength": 1},
|
||||
"label": {"type": "string", "minLength": 1},
|
||||
"icon": {"type": "string", "minLength": 1}
|
||||
}
|
||||
}
|
||||
},
|
||||
"deviceTags": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"system": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"description": "Owners may assign system tags and global workflows may select devices by them. Only for deployments whose owners are trusted (self-hosted)."
|
||||
},
|
||||
"catalog": {
|
||||
"type": "array",
|
||||
"description": "The system tags owners can assign, in the Hub shared choice catalog format (key, label) plus an optional description. Keys must be unique (checked when rendering).",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["key", "label"],
|
||||
"properties": {
|
||||
"key": {
|
||||
"type": "string",
|
||||
"maxLength": 64,
|
||||
"pattern": "^[a-z0-9]([a-z0-9._:-]*[a-z0-9])?$"
|
||||
},
|
||||
"label": {"type": "string", "minLength": 1},
|
||||
"description": {"type": "string"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"kerberoshub": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"api": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"passkeys": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"enabled": {"type": "boolean"},
|
||||
"rpId": {
|
||||
"type": "string",
|
||||
"description": "Stable WebAuthn relying-party hostname, without scheme, port or path. Must match each frontend origin's hostname or be its parent domain."
|
||||
},
|
||||
"origins": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^https://[^/?#]+$|^http://localhost(:[0-9]+)?$"
|
||||
},
|
||||
"description": "Exact trusted frontend origins, with no path or trailing slash. HTTPS is required except for HTTP localhost development."
|
||||
},
|
||||
"rpName": {"type": "string"}
|
||||
},
|
||||
"if": {
|
||||
"required": ["enabled"],
|
||||
"properties": {"enabled": {"const": true}}
|
||||
},
|
||||
"then": {
|
||||
"required": ["rpId", "origins"],
|
||||
"properties": {
|
||||
"rpId": {"minLength": 1},
|
||||
"origins": {"minItems": 1}
|
||||
}
|
||||
}
|
||||
},
|
||||
"corsAllowedOrigins": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^https?://[^/?#]+$"
|
||||
}
|
||||
},
|
||||
"auditExportMaxEventsPerFile": {
|
||||
"type": ["integer", "string"],
|
||||
"minimum": 1,
|
||||
"maximum": 1000000,
|
||||
"pattern": "^([1-9][0-9]{0,5}|1000000)?$",
|
||||
"description": "Most audit events one export file holds (AUDIT_EXPORT_MAX_EVENTS_PER_FILE). Empty uses the API default of 100000."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"workflowConfiguration": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["enabled"],
|
||||
"not": {
|
||||
"allOf": [
|
||||
{"anyOf": [{"required": ["includeDefaults"]}, {"required": ["configMaps"]}]},
|
||||
{"anyOf": [{"required": ["existingConfigMap"]}, {"required": ["items"]}, {"required": ["extraConfigMaps"]}]}
|
||||
]
|
||||
},
|
||||
"properties": {
|
||||
"enabled": {"type": "boolean"},
|
||||
"includeDefaults": {
|
||||
"type": "boolean",
|
||||
"description": "Include bundled files not explicitly replaced by configMaps items; true when omitted."
|
||||
},
|
||||
"configMaps": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/definitions/configMapSource"},
|
||||
"description": "Add files or explicitly replace a bundled file using replace: true on an item."
|
||||
},
|
||||
"extraConfigMaps": {
|
||||
"type": "array",
|
||||
"description": "Legacy additive sources; use configMaps for new deployments.",
|
||||
"items": {"$ref": "#/definitions/configMapSource"}
|
||||
},
|
||||
"existingConfigMap": {
|
||||
"type": "string",
|
||||
"maxLength": 253,
|
||||
"pattern": "^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$"
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/definitions/configMapItem"}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"configMapSource": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "items"],
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"maxLength": 253,
|
||||
"pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$"
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {"$ref": "#/definitions/configMapItem"}
|
||||
}
|
||||
}
|
||||
},
|
||||
"configMapItem": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["key", "path"],
|
||||
"properties": {
|
||||
"key": {"type": "string", "pattern": "^[A-Za-z0-9._-]+$"},
|
||||
"path": {"type": "string", "pattern": "^(workflow-contracts|catalogs)/[A-Za-z0-9_-][A-Za-z0-9._-]*\\.(yaml|json)$"},
|
||||
"replace": {
|
||||
"type": "boolean",
|
||||
"description": "Replace this whole bundled file, not individual fields/entries. Requires configMaps and includeDefaults: true; the path must identify a bundled file."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+89
-443
@@ -46,10 +46,7 @@ mongodb:
|
||||
# MongoDB URI (for example for a SaaS service like MongoDB Atlas)
|
||||
# If uri is set, the below properties are not used (host, adminDatabase, username, password)
|
||||
#uri: "mongodb+srv://xx:xx@kerberos-hub.xxx.mongodb.net/?retryWrites=true&w=majority&appName=xxx"
|
||||
# When uri is set, give each workload its own appName (hub-api,
|
||||
# hub-pipeline-monitor, ...) instead of the one in uri, so the Atlas query
|
||||
# profiler and currentOp show which service issued an operation.
|
||||
appNamePerService: true
|
||||
|
||||
# Self-hosted mongodb, you could also use the uri property above.
|
||||
host: mongodb.mongodb
|
||||
adminDatabase: admin
|
||||
@@ -72,63 +69,37 @@ mongodb:
|
||||
caFileName: ""
|
||||
mountPath: "/etc/mongodb/tls"
|
||||
insecureSkipVerify: false
|
||||
|
||||
# Classifications supported by Hub filters, alerts, markers, and other
|
||||
# downstream processing. Classifier output keys must match these values.
|
||||
# Disable this to use the legacy classification catalog stored in MongoDB.
|
||||
classificationCatalogEnabled: true
|
||||
# Optional existing ConfigMap in the release namespace with classifications.json.
|
||||
# When set, do not create a catalog ConfigMap; this replaces bundled defaults.
|
||||
classificationCatalogExistingConfigMap: ""
|
||||
# Defaults live in templates/configMaps/catalogs/classifications.yaml.
|
||||
# Optionally define the complete catalog inline instead; it replaces the
|
||||
# defaults in the chart's classification-catalog ConfigMap (and so the bundled
|
||||
# catalogs/classifications.json). Entries are {key, label, icon?} with unique
|
||||
# keys. Do not combine it with classificationCatalogExistingConfigMap or a
|
||||
# catalogs/classifications.json replacement in workflowConfiguration.
|
||||
# classificationCatalog:
|
||||
# - key: car
|
||||
# label: Car
|
||||
# icon: car
|
||||
|
||||
# Device tags, grouped by type. System tags are defined here and assigned by
|
||||
# owners (a multi-select on the device page); global workflows select devices
|
||||
# across organisations by system tag (`systemTags` in a trigger). Owners set
|
||||
# them, so any owner could enroll devices into such a workflow: enable only
|
||||
# where owners are the deployment's administrators (self-hosted), never on
|
||||
# multi-tenant SaaS. While disabled, hub-api gets an empty catalog (system tags
|
||||
# cannot be set) and rendering fails on workflows that select devices by
|
||||
# system tag.
|
||||
deviceTags:
|
||||
system:
|
||||
enabled: false # a boolean (true/false), not a string
|
||||
# The only system tags owners can assign, in the Hub shared choice catalog
|
||||
# format (as catalogs/classifications.json) plus an optional description.
|
||||
# Rendered into the device-system-tags ConfigMap for hub-api.
|
||||
# key stored tag: 1-64 characters of a-z, 0-9, ".", "_", ":" or
|
||||
# "-", starting and ending with a letter or digit; unique.
|
||||
# label display name.
|
||||
# description optional help text shown with the tag.
|
||||
# Workflow trigger systemTags must exist here; otherwise rendering fails.
|
||||
catalog: []
|
||||
# - key: anpr-pilot
|
||||
# label: ANPR pilot
|
||||
# description: Runs licence plate recognition on this camera
|
||||
# Configuration packaging only: consuming contracts requires a compatible
|
||||
# API/frontend. Enabling this mounts files but does not register stage workers.
|
||||
workflowConfiguration:
|
||||
enabled: false
|
||||
# Omitted includeDefaults means true; false requires a nonempty configMaps list.
|
||||
# includeDefaults: true
|
||||
# configMaps: []
|
||||
# Each ConfigMap supplies name and items [{key, path, replace?}] in the release namespace.
|
||||
# An item with replace: true replaces one bundled file while retaining other defaults.
|
||||
# Replacement requires includeDefaults: true and a known bundled destination path.
|
||||
# Unmarked collisions and repeated custom destinations are rejected.
|
||||
# Replacing catalogs/classifications.json also supplies the classification API:
|
||||
# do not combine it with classificationCatalogExistingConfigMap or classificationCatalog.
|
||||
# Legacy existingConfigMap/items/extraConfigMaps remain supported, but cannot
|
||||
# be mixed with includeDefaults/configMaps. Both interfaces are omitted here
|
||||
# so Helm's default merging does not introduce a false mixed configuration.
|
||||
classificationCatalog:
|
||||
- key: animal
|
||||
label: Animal
|
||||
icon: animal
|
||||
- key: pedestrian
|
||||
label: Pedestrian
|
||||
icon: pedestrian
|
||||
- key: cyclist
|
||||
label: Cyclist
|
||||
icon: cyclist
|
||||
- key: motorbike
|
||||
label: Motorbike
|
||||
icon: motorbike
|
||||
- key: lorry
|
||||
label: Lorry
|
||||
icon: lorry
|
||||
- key: car
|
||||
label: Car
|
||||
icon: car
|
||||
- key: handbag
|
||||
label: Handbag
|
||||
icon: handbag
|
||||
- key: suitcase
|
||||
label: Suitcase
|
||||
icon: suitcase
|
||||
- key: cell phone
|
||||
label: Cell phone
|
||||
icon: cell phone
|
||||
###################################################
|
||||
# MQTT configuration (bi-directional communication)
|
||||
###################################################
|
||||
@@ -162,16 +133,6 @@ rabbitmq:
|
||||
username: "yourusername"
|
||||
password: "yourpassword"
|
||||
exchange: ""
|
||||
# Shared audit transport. The master switch controls the consumer and all
|
||||
# producers; individual services may opt out through their own audit setting.
|
||||
audit:
|
||||
enabled: false
|
||||
intake:
|
||||
queue: "hub-audit-events"
|
||||
deadletterQueue: "hub-audit-dead-letter"
|
||||
maxRetries: 10
|
||||
prefetchCount: 20
|
||||
persistTimeout: "10s"
|
||||
# If you already have a Kafka cluster you might use this instead of RabbitMQ.
|
||||
kafka:
|
||||
broker: "kafka1.yourdomain.com:9094" # can be internal dns name or external
|
||||
@@ -206,27 +167,6 @@ opentelemetry:
|
||||
# the collector terminates TLS. A scheme-less value defaults to TLS and
|
||||
# fails against a plaintext collector ("server gave HTTP response to HTTPS client").
|
||||
endpoint: "http://otel-collector:4318"
|
||||
# Trace sampling is decided where a trace starts:
|
||||
# - Uploads start at the Vault, whose deployment sets their rate
|
||||
# (TRACE_SAMPLER_RATIO_UPLOAD / TRACE_SAMPLER_RATIO_OTHER).
|
||||
# - hub-api starts its own traces; apiSamplingRatios below sets its rates.
|
||||
# hub-workflows, the pipe-* services and the workflow stages only continue
|
||||
# traces started elsewhere: they follow that decision and never start traces
|
||||
# of their own (OTEL_TRACES_SAMPLER=parentbased_always_off, not configurable).
|
||||
#
|
||||
# apiSamplingRatios: hub-api's sampling ratio (0 to 1) per kind of work,
|
||||
# rendered as TRACE_SAMPLER_RATIO_<CLASS> (hub-api on trace library v1.2.6+).
|
||||
# Empty values are not rendered; with none set, hub-api records every trace.
|
||||
# operator: requests from users and API clients (UI, API tokens).
|
||||
# other: everything else hub-api starts: device heartbeats, agent uploads
|
||||
# and live segments, background work, queries outside a request.
|
||||
# Unset with operator set: records everything.
|
||||
# Example: ~10% of user requests, 1% of everything else:
|
||||
# operator: "0.1"
|
||||
# other: "0.01"
|
||||
apiSamplingRatios:
|
||||
operator: ""
|
||||
other: ""
|
||||
############################################
|
||||
# OpenAI configuration (semantic search)
|
||||
#
|
||||
@@ -265,11 +205,6 @@ kerberosvault:
|
||||
###########################################################################
|
||||
admin:
|
||||
repository: uugai/admin
|
||||
# Opt in to 8080 and a securityContext only with a compatible hardened image.
|
||||
containerPort: 80
|
||||
securityContext: {}
|
||||
readinessProbe: {}
|
||||
livenessProbe: {}
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v1.3.0"
|
||||
replicas: 2
|
||||
@@ -336,29 +271,8 @@ kerberoshub:
|
||||
api:
|
||||
repository: ghcr.io/uug-ai/hub-api
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v1.11.31"
|
||||
tag: "v1.9.51"
|
||||
replicas: 2
|
||||
podSecurityContext:
|
||||
fsGroup: 65532
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 65532
|
||||
runAsGroup: 65532
|
||||
readOnlyRootFilesystem: true
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
# Keep writable scratch space for attachment uploads and multipart parsing.
|
||||
volumes:
|
||||
- name: hub-api-tmp
|
||||
emptyDir: {}
|
||||
volumeMounts:
|
||||
- name: hub-api-tmp
|
||||
mountPath: /tmp
|
||||
audit:
|
||||
enabled: true
|
||||
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
|
||||
# Set to false to skip rendering the hub-api Service (e.g. when an
|
||||
# external/shared Service is managed elsewhere).
|
||||
@@ -367,22 +281,6 @@ kerberoshub:
|
||||
jwtSecret: "this-is-a-secret-please-change-to-random-string" # change to a random value, this is for generating JWT tokens.
|
||||
schema: "https"
|
||||
url: "api.yourdomain.com"
|
||||
# Additional complete HTTP(S) origins allowed to call the Hub API.
|
||||
# The configured frontend origins remain allowed automatically.
|
||||
corsAllowedOrigins: []
|
||||
# Temporarily stop serving the polled GET /devices list: hub-api answers 204
|
||||
# without querying MongoDB. The frontend still loads devices once through
|
||||
# /session/bootstrap, but device online/offline status stops refreshing.
|
||||
disableAgentHeartbeat: false
|
||||
# Media proxy: when enabled, hub-api wraps every media URL it receives from
|
||||
# Kerberos Vault into an opaque hub-api URL ({schema}://{url}/media/proxy?t=...)
|
||||
# and streams the object itself, so clients never see Vault or the
|
||||
# underlying storage endpoint. Increases hub-api egress/bandwidth.
|
||||
mediaProxy:
|
||||
enabled: false
|
||||
publicUrl: "" # Optional override of the public hub-api base URL; defaults to {schema}://{url}.
|
||||
key: "" # Optional encryption key (openssl rand -hex 32); defaults to a key derived from jwtSecret. Must be identical on all replicas.
|
||||
ttl: "" # Optional lifetime of proxied URLs (Go duration, e.g. "12h"); defaults to 24h, max 168h.
|
||||
resources:
|
||||
requests:
|
||||
memory: 100Mi
|
||||
@@ -391,7 +289,6 @@ kerberoshub:
|
||||
memory: 100Mi
|
||||
cpu: 250m
|
||||
# E-mail templates
|
||||
# Append custom mounts/volumes to the lists above; retain hub-api-tmp.
|
||||
#volumeMounts:
|
||||
# - name: custom-email-templates
|
||||
# mountPath: /mail
|
||||
@@ -414,13 +311,6 @@ kerberoshub:
|
||||
|
||||
# MFA issuer name
|
||||
mfaIssuer: "yourdomain.com"
|
||||
# WebAuthn passkeys for users and sub-users; requires compatible API/frontend images.
|
||||
# Use the public frontend hostname/origins, not the API URL. Keep rpId stable.
|
||||
passkeys:
|
||||
enabled: false
|
||||
rpId: "" # e.g. "hub.example.com", without scheme, port or path.
|
||||
origins: [] # e.g. ["https://hub.example.com"]; exact origins, no trailing slash.
|
||||
rpName: "Kerberos Hub"
|
||||
# Admin API's are made available for automation of Kerberos Hub.
|
||||
# To access those API's (e.g. creation of owner users), an API key needs to be provided.
|
||||
apiKey: "a-random-admin-api-key"
|
||||
@@ -436,11 +326,6 @@ kerberoshub:
|
||||
# beyond this cap, the value is clamped down to `now + casesMaxRetentionDays`.
|
||||
# Set to "0" (or a negative value) to disable the cap (no maximum).
|
||||
casesMaxRetentionDays: "0"
|
||||
# Most audit events one export file holds (AUDIT_EXPORT_MAX_EVENTS_PER_FILE).
|
||||
# It does not refuse larger exports: the Audit page downloads them as
|
||||
# several files of at most this size. An integer from 1 to 1000000; leave
|
||||
# empty to use the API default of 100000.
|
||||
auditExportMaxEventsPerFile: ""
|
||||
## Certificates
|
||||
tls:
|
||||
- hosts:
|
||||
@@ -499,28 +384,8 @@ kerberoshub:
|
||||
frontend:
|
||||
repository: ghcr.io/uug-ai/hub-frontend
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v1.19.28"
|
||||
tag: "v1.13.9"
|
||||
replicas: 2
|
||||
containerPort: 8080
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 65532
|
||||
runAsGroup: 65532
|
||||
readOnlyRootFilesystem: true
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
# Keep these entries when adding custom branding/translation mounts.
|
||||
volumes:
|
||||
- name: frontend-tmp
|
||||
emptyDir:
|
||||
medium: Memory
|
||||
sizeLimit: 16Mi
|
||||
volumeMounts:
|
||||
- name: frontend-tmp
|
||||
mountPath: /tmp
|
||||
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
|
||||
# Set to false to skip rendering the hub-frontend Service (e.g. when an
|
||||
# external/shared Service is managed elsewhere).
|
||||
@@ -646,16 +511,12 @@ kerberoshub:
|
||||
# Audit events are visible to owner/admin users when enabled.
|
||||
audit:
|
||||
enabled: "true" # Enable or disable the Audit events page and navigation link 'true' or 'false'
|
||||
# Day overview page and its navigation menu; role page permissions still apply.
|
||||
dayOverview:
|
||||
enabled: "true" # Enable or disable the Day overview page and navigation menu 'true' or 'false'
|
||||
# Organisation controls remain visible as a read-only current organisation when switching is disabled.
|
||||
organisations:
|
||||
enabled: "" # Enable or disable the organisation feature family, including projects; when empty, child groups apply independently
|
||||
switcherEnabled: "false" # Enable or disable organisation switching 'true' or 'false'
|
||||
creationEnabled: "false" # Enable or disable organisation creation; requires switcherEnabled 'true' or 'false'
|
||||
settingsEnabled: "false" # Enable or disable the organisation settings link 'true' or 'false'
|
||||
membersAccessPreviewEnabled: "false" # Show the mocked "Users (preview)" tab in organisation settings to owners 'true' or 'false'
|
||||
projects:
|
||||
enabled: "" # Fallback project group switch used only when organisations.enabled is empty
|
||||
switcherEnabled: "false" # Enable or disable the read-only project dropdown 'true' or 'false'
|
||||
@@ -678,8 +539,7 @@ kerberoshub:
|
||||
liveStreamMode: "webrtc" # Transport backing the LIVE (HD) mode: 'webrtc' (default), 'hls' or 'moq'
|
||||
hlsEnabled: "true" # Offer HLS as a selectable LIVE transport 'true' or 'false'. When 'false' the HLS option is removed from the front-end and streams use webrtc
|
||||
moqEnabled: "false" # Offer MoQ as a selectable LIVE transport 'true' or 'false'
|
||||
remoteRecordingEnabled: "true" # Show the manual REC control and recording-state overlay in live views
|
||||
deviceStatusEnabled: "true" # Show connectivity and recent-motion status in live view tile headers
|
||||
remoteRecordingEnabled: "true" # Show the manual REC control in live views
|
||||
moqRelayUrl: "https://relay.uug.ai/anon" # WebTransport URL of the MoQ relay
|
||||
moqBroadcastPrefix: "devices" # Prefix used to build devices/<deviceKey>/live.hang broadcast names
|
||||
paginationMode: "scroll" # Pagination mode in live view 'scroll', 'numbered' or 'maxStreams'
|
||||
@@ -689,19 +549,11 @@ kerberoshub:
|
||||
devices:
|
||||
hideAgent: "false" # Hide the 'add agent' button in the front-end
|
||||
analyticsEnabled: "true" # Enable or disable the Analytics tab on device detail pages 'true' or 'false'
|
||||
developerToolsEnabled: "true" # Enable or disable device logs and terminal access 'true' or 'false'
|
||||
# Dashboard page
|
||||
dashboard:
|
||||
sitesFullscreenEnabled: "true" # Enable or disable the fullscreen Sites map 'true' or 'false'
|
||||
# Media page
|
||||
media:
|
||||
# Bulk "Forward media" action in the Recordings Actions menu (on-demand forwarding of metadata-only recordings).
|
||||
forward:
|
||||
enabled: "true" # Enable or disable the Forward media action in media 'true' or 'false'
|
||||
# Analysis block (dominant colors, thumbnail, sprite, motion) on the media detail page.
|
||||
detail:
|
||||
analysis:
|
||||
enabled: "true" # Enable or disable the Analysis block on the media detail page 'true' or 'false'
|
||||
filter:
|
||||
date:
|
||||
enabled: "true" # Enable or disable date filter in media 'true' or 'false'
|
||||
@@ -715,8 +567,6 @@ kerberoshub:
|
||||
enabled: "true" # Enable or disable object detection filter in media 'true' or 'false'
|
||||
star:
|
||||
enabled: "true" # Enable or disable star filter in media 'true' or 'false'
|
||||
analysis:
|
||||
enabled: "true" # Enable or disable the "with analysis" filter (hides recordings without markers, description or detections) in media 'true' or 'false'
|
||||
region:
|
||||
enabled: "true" # Enable or disable region filter in media 'true' or 'false'
|
||||
sort:
|
||||
@@ -727,9 +577,6 @@ kerberoshub:
|
||||
enabled: "true" # Enable or disable markers filter in media 'true' or 'false'
|
||||
markerOptionsByDate:
|
||||
enabled: "false" # Limit marker-related filter options to the selected recording day
|
||||
markerCategoryPriority:
|
||||
enabled: "false" # Order marker options from configured categories before other markers
|
||||
categories: [] # Ordered marker category names, for example ["security", "traffic"]
|
||||
events:
|
||||
enabled: "true" # Enable or disable events filter in media 'true' or 'false'
|
||||
tags:
|
||||
@@ -781,7 +628,6 @@ kerberoshub:
|
||||
publicKey: ""
|
||||
googlemaps: # Google maps is used inside the application to visualise cameras and sites.
|
||||
apikey: "xxxx"
|
||||
placesApi: "legacy" # Places API for address autocomplete: "legacy" (default) or "new" for keys with Places API (New) enabled. Uses the same apikey.
|
||||
zendesk: # We can use different support tools, for now we use Zendesk but others can be integrated
|
||||
url: "yourdomain.zendesk.com"
|
||||
posthog: # Posthog is used for auditing and user interaction logging
|
||||
@@ -806,12 +652,9 @@ kerberoshub:
|
||||
organization: "github-organization"
|
||||
team: "github-team"
|
||||
cleanup:
|
||||
# Explicit opt-in for a selected hardened image; legacy defaults are unchanged.
|
||||
securityContext: {}
|
||||
podSecurityContext: {}
|
||||
repository: ghcr.io/uug-ai/hub-cleanup
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v1.4.26"
|
||||
tag: "v1.4.19"
|
||||
replicas: 1 # Number of pods for the service.
|
||||
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
|
||||
# Optional extra volumes / volumeMounts for this deployment (empty = none).
|
||||
@@ -851,27 +694,18 @@ kerberoshub:
|
||||
# recording plans, inactive-account cleanup, and the optional global pass.
|
||||
# Set to "0" or a negative value to retain audit events indefinitely.
|
||||
auditEventRetentionDays: "400"
|
||||
# Deployment-wide cap for workflow runs, anchored on run execution time.
|
||||
# Runs always expire with their media; this only shortens their lifetime.
|
||||
# Set to "0" or a negative value to let runs follow media retention only.
|
||||
workflowRunRetentionDays: "0"
|
||||
resources:
|
||||
requests:
|
||||
memory: 10Mi
|
||||
cpu: 10m
|
||||
# Dispatches canonical audit_events to one or more destinations: webhook
|
||||
# sinks, or S3 Object Lock buckets for immutable storage.
|
||||
# Destination credentials are set inline on each destination.
|
||||
# Dispatches canonical audit_events to one or more external webhook sinks.
|
||||
# Credentials are always read from existing Secrets and never stored here.
|
||||
audit:
|
||||
enabled: false
|
||||
repository: ghcr.io/uug-ai/hub-audit
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v1.0.10"
|
||||
tag: "v1.0.0"
|
||||
replicas: 1
|
||||
resources: {}
|
||||
# resources:
|
||||
# requests:
|
||||
# memory: 32Mi
|
||||
# cpu: 10m
|
||||
dispatchInterval: "5s"
|
||||
leaseDuration: "2m"
|
||||
terminationGracePeriodSeconds: 150 # Keep above leaseDuration so an in-flight cycle can finish.
|
||||
@@ -879,70 +713,37 @@ kerberoshub:
|
||||
volumes: []
|
||||
volumeMounts: []
|
||||
extraEnv: []
|
||||
# Optional ServiceAccount for hub-audit alone, for example to grant S3 access
|
||||
# through IRSA without giving it to every Hub workload. When create is false
|
||||
# and name is set, that existing ServiceAccount is used; otherwise the pod
|
||||
# falls back to kerberoshub.serviceAccount.
|
||||
serviceAccount:
|
||||
create: false
|
||||
name: ""
|
||||
annotations: {}
|
||||
# eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/hub-audit-archive
|
||||
labels: {}
|
||||
serviceMonitor:
|
||||
enabled: true
|
||||
interval: 15s
|
||||
# Each destination is optional; see the README for every field. Webhook:
|
||||
destinations: []
|
||||
# - id: primary-siem # required; ^[a-z][a-z0-9-]{0,62}$, keeps the checkpoint
|
||||
# type: webhook # optional: webhook (default) | s3
|
||||
# enabled: true # optional, default true; false pauses delivery
|
||||
# url: https://siem.example.com/api/audit # required, http or https
|
||||
# timeoutSeconds: 10 # optional, default 10, at most 60
|
||||
# batchSize: 100 # optional, default 100, at most 1000
|
||||
# maxAttempts: 10 # optional, default 10; then the batch is dead-lettered
|
||||
# startAt: latest # optional: latest (default) | beginning, for a new id only
|
||||
# headers: # optional; not Authorization or Idempotency-Key
|
||||
# - id: primary-siem
|
||||
# type: webhook
|
||||
# enabled: true
|
||||
# url: https://siem.example.com/api/audit
|
||||
# timeoutSeconds: 10
|
||||
# batchSize: 100
|
||||
# maxAttempts: 10
|
||||
# startAt: latest # latest | beginning, applied only on first creation
|
||||
# headers:
|
||||
# X-Source: kerberos-hub
|
||||
# # Optional inline credentials. The chart stores them in Secret
|
||||
# # hub-audit-<id>-credentials and mounts them.
|
||||
# bearerToken: "xxx" # sent as Authorization: Bearer <token>
|
||||
# bearerTokenSecret:
|
||||
# name: audit-primary-siem
|
||||
# key: token
|
||||
# secretHeaders:
|
||||
# X-Api-Key:
|
||||
# value: "xxx"
|
||||
# filters: # optional, default all events; an event must match every list set
|
||||
# secretName: audit-primary-siem
|
||||
# secretKey: api-key
|
||||
# filters:
|
||||
# organisationIds: []
|
||||
# projectIds: []
|
||||
# actions: []
|
||||
# categories: []
|
||||
# outcomes: [] # success | failure
|
||||
# S3 Object Lock destination. The bucket needs versioning, Object Lock and a
|
||||
# COMPLIANCE default retention; hub-audit refuses to deliver otherwise. Each
|
||||
# batch is written as <prefix>/<id>/yyyy/mm/dd/<firstId>-<lastId>.ndjson.gz.
|
||||
# Per-provider region/endpoint values (AWS, Wasabi, B2, Ceph RGW, MinIO, Garage):
|
||||
# https://learn.uug.ai/docs/hub/audit-events/#s3-providers
|
||||
# - id: immutable-archive # required
|
||||
# type: s3 # required for S3
|
||||
# enabled: true # optional, default true
|
||||
# timeoutSeconds: 30
|
||||
# batchSize: 500
|
||||
# maxAttempts: 100 # keep high: exhausted batches are dead-lettered, not archived
|
||||
# startAt: beginning
|
||||
# s3:
|
||||
# bucket: hub-audit-archive # required
|
||||
# region: eu-west-1 # required, also for S3-compatible stores
|
||||
# prefix: hub # optional, default none
|
||||
# endpoint: https://s3.example.com # optional; S3-compatible stores only, omit for AWS S3
|
||||
# forcePathStyle: true # optional, default false; usually true for Ceph RGW, MinIO and Garage
|
||||
# # Optional, default false. Only for stores without Object Lock (e.g. Garage):
|
||||
# # skips the lock check, so archived batches are deletable.
|
||||
# allowUnlockedBucket: false
|
||||
# # Optional inline credentials, both or neither. The chart stores them
|
||||
# # in Secret hub-audit-<id>-credentials and mounts them.
|
||||
# accessKey: "xxx"
|
||||
# secretKey: "xxx"
|
||||
# # Without them, hub-audit uses the pod's AWS identity (IRSA via
|
||||
# # kerberoshub.audit.serviceAccount, or an instance profile): AWS S3 only.
|
||||
# resources:
|
||||
# requests:
|
||||
# memory: 32Mi
|
||||
# cpu: 10m
|
||||
# hub-workflows is the standalone, queue-driven workflow engine. It consumes
|
||||
# pipeline events and dispatches the stages declared in its workflow
|
||||
# definitions, tracking each run in its own `workflow_runs` collection. It shares events
|
||||
@@ -972,22 +773,13 @@ kerberoshub:
|
||||
# more config workflows.
|
||||
#
|
||||
# Each definition:
|
||||
# id optional stable identity: quote a non-zero 24-hex Mongo ObjectID.
|
||||
# Recommended for new definitions; rendered lowercase. Omit for
|
||||
# legacy SHA256(exact map key)[:24] identity. Pin that old ID before
|
||||
# renaming an existing workflow; changing id creates a new identity.
|
||||
# Invalid IDs and duplicate effective IDs fail Helm rendering,
|
||||
# including disabled definitions. Name/version are not identity
|
||||
# when id is explicit. No per-project on/off overrides are provided.
|
||||
# enabled include this workflow (soft-delete toggle).
|
||||
# source always rendered as "config" (a Helm-defined, ops-managed,
|
||||
# deployment-global workflow — read-only in the API).
|
||||
# triggers how a run OPENS. Omit for a single bare automatic trigger
|
||||
# (opens for every recording); the per-stage `needs` then decide
|
||||
# which stages FIRE. Narrow with device/schedule triggers, e.g.
|
||||
# `- {type: automatic, devices: [{key: <device-key>}]}`, or by
|
||||
# device label across organisations, e.g.
|
||||
# `- {type: automatic, systemTags: [anpr-pilot]}` (see deviceTags).
|
||||
# `- {type: automatic, devices: [{key: <device-key>}]}`.
|
||||
# stages the executable stages, each {operation, dispatch?, needs?,
|
||||
# needsMode?}. dispatch is "always" (default) or "conditional";
|
||||
# a conditional stage's `needs` are upstream dependencies, each
|
||||
@@ -1005,42 +797,19 @@ kerberoshub:
|
||||
# kerberoshub.services.<operation> (deploy the objecttracking / loitering
|
||||
# workers below).
|
||||
definitions: {}
|
||||
# Replace {} above with the indented examples below to enable them. Their IDs
|
||||
# are pinned to the existing name-derived identities; do not regenerate them.
|
||||
# tracking-workflow:
|
||||
# id: "ec7e72bc4cedd32018e1f92a"
|
||||
# enabled: true
|
||||
# triggers:
|
||||
# - type: automatic
|
||||
# stages:
|
||||
# - operation: objecttracking
|
||||
# dispatch: always
|
||||
# - operation: loitering
|
||||
# dispatch: conditional
|
||||
# # Fire loitering once objecttracking has resolved — a readiness join
|
||||
# # (no condition ⇒ gate on the upstream's presence, not a value).
|
||||
# needs:
|
||||
# - operation: objecttracking
|
||||
# vlm-workflow:
|
||||
# id: "9e3666c56854b2befdb1f645"
|
||||
# # Describe every recording with the hub-vlm worker (services.vlm below).
|
||||
# enabled: true
|
||||
# triggers:
|
||||
# - type: automatic
|
||||
# stages:
|
||||
# - operation: vlm
|
||||
# dispatch: always
|
||||
# Optional editor metadata (name, description, params) per operation. With
|
||||
# workflowConfiguration contracts, a stage is offered when it has a valid
|
||||
# contract and a deployed worker (services.<operation>.queue); contract
|
||||
# presentation and params take precedence over these entries.
|
||||
editorStages: []
|
||||
# - operation: vlm
|
||||
# name: Describe recording
|
||||
# description: Summarise the recording with a vision language model.
|
||||
# - operation: forwarder
|
||||
# name: Forward to my system
|
||||
# description: Send the result to the external destination configured for the forwarder.
|
||||
#tracking-workflow:
|
||||
# enabled: true
|
||||
# triggers:
|
||||
# - type: automatic
|
||||
# stages:
|
||||
# - operation: objecttracking
|
||||
# dispatch: always
|
||||
# - operation: loitering
|
||||
# dispatch: conditional
|
||||
# # Fire loitering once objecttracking has resolved — a readiness join
|
||||
# # (no condition ⇒ gate on the upstream's presence, not a value).
|
||||
# needs:
|
||||
# - operation: objecttracking
|
||||
# Workflow deployments. Every workflows-subsystem Deployment's image/tag/
|
||||
# replicas/resources/queue lives here in a single, uniform shape:
|
||||
# - `workflows` is the engine itself (the orchestrator). It is deployed
|
||||
@@ -1051,8 +820,6 @@ kerberoshub:
|
||||
# Deploying a worker (services.<name>.enabled) is independent from routing
|
||||
# to it (a workflows.definitions stage of the same operation) — it runs only
|
||||
# when its own `enabled` is true AND the workflows engine is enabled.
|
||||
# Worker-specific settings go in `env` (plain key/values) and `secretEnv`
|
||||
# (ENV_NAME: {name, key} of an existing Secret, for credentials).
|
||||
services:
|
||||
# hub-workflows — the workflows engine (orchestrator). Consumes the engine
|
||||
# queue, evaluates the boot-loaded config workflows (WORKFLOW_DEFINITIONS)
|
||||
@@ -1061,8 +828,6 @@ kerberoshub:
|
||||
# `enabled` here.
|
||||
workflows:
|
||||
repository: ghcr.io/uug-ai/hub-workflows
|
||||
securityContext: {}
|
||||
podSecurityContext: {}
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v1.0.0"
|
||||
replicas: 1 # Number of pods for the service.
|
||||
@@ -1133,57 +898,13 @@ kerberoshub:
|
||||
# requests:
|
||||
# memory: 10Mi
|
||||
# cpu: 10m
|
||||
# ---------------------------------------------------------------------
|
||||
# EXAMPLE stage worker (commented out) — hub-vlm (operation "vlm").
|
||||
#
|
||||
# Samples frames from the recording, describes them with an Azure OpenAI
|
||||
# vision model and emits timeline markers plus a recording description.
|
||||
# Route to it with a workflows.definitions stage `- operation: vlm`.
|
||||
# See https://github.com/uug-ai/hub-vlm for every VLM_* / AZURE_OPENAI_*
|
||||
# setting. Create the API key Secret first, e.g.
|
||||
# kubectl create secret generic hub-vlm-azure-openai \
|
||||
# --from-literal=apiKey=<azure-openai-api-key>
|
||||
#vlm:
|
||||
# # Deploy the hub-vlm worker.
|
||||
# enabled: true
|
||||
# repository: ghcr.io/uug-ai/hub-vlm
|
||||
# pullPolicy: IfNotPresent
|
||||
# tag: "v1.0.0"
|
||||
# replicas: 1 # Number of pods for the worker.
|
||||
# topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
|
||||
# logLevel: "info" # possible values: trace, debug, info, warn, error
|
||||
# # Queue this worker consumes dispatched messages from (VLM_QUEUE).
|
||||
# queue: "hub-vlm-queue"
|
||||
# env:
|
||||
# AZURE_OPENAI_ENDPOINT: "https://<resource>.openai.azure.com"
|
||||
# AZURE_OPENAI_DEPLOYMENT: "gpt-5.4-mini" # Name of the model deployment.
|
||||
# AZURE_OPENAI_REASONING_EFFORT: "low" # none | minimal | low | medium | high
|
||||
# VLM_MAX_FRAMES: "8" # Frames sampled per recording (max 32).
|
||||
# VLM_FRAME_WIDTH: "1024" # Maximum frame width in pixels.
|
||||
# VLM_SET_DESCRIPTION: "true" # Write the summary to the recording description.
|
||||
# # The API key is read from an existing Secret, never from values.
|
||||
# secretEnv:
|
||||
# AZURE_OPENAI_API_KEY:
|
||||
# name: hub-vlm-azure-openai
|
||||
# key: apiKey
|
||||
# resources:
|
||||
# requests:
|
||||
# memory: 128Mi
|
||||
# cpu: 100m
|
||||
monitordevice:
|
||||
securityContext: {}
|
||||
podSecurityContext: {}
|
||||
repository: ghcr.io/uug-ai/hub-monitor-device
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v1.4.10"
|
||||
tag: "v1.4.2"
|
||||
replicas: 1 # Number of pods for the service.
|
||||
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
|
||||
logLevel: "info" # possible values: trace, debug, info, warn, error
|
||||
# Seconds between device status scans. Sets how quickly a camera going
|
||||
# offline/online is detected. With "Group notifications" on, hub-monitor-device
|
||||
# v1.4.10+ holds a change 20 seconds (DEVICE_GROUPING_HOLD_SECONDS) to collect
|
||||
# other cameras that change at the same time; older releases hold one scan.
|
||||
scanIntervalSeconds: 60
|
||||
resources:
|
||||
requests:
|
||||
memory: 10Mi
|
||||
@@ -1257,13 +978,9 @@ kerberoshub:
|
||||
# The pipeline is a critical component of Kerberos Hub, and should be monitored closely.
|
||||
kerberospipeline:
|
||||
event:
|
||||
deploymentAnnotations: {}
|
||||
# Example (opt-in Argo CD ordering):
|
||||
# deploymentAnnotations:
|
||||
# argocd.argoproj.io/sync-wave: "10"
|
||||
repository: ghcr.io/uug-ai/hub-pipeline-event
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v1.3.9"
|
||||
tag: "v1.3.1"
|
||||
replicas: 1 # Number of pods for the service.
|
||||
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
|
||||
# Optional extra volumes / volumeMounts for this deployment (empty = none).
|
||||
@@ -1280,11 +997,8 @@ kerberospipeline:
|
||||
cpu: 10m
|
||||
monitor:
|
||||
repository: ghcr.io/uug-ai/hub-pipeline-monitor
|
||||
deploymentAnnotations: {}
|
||||
# deploymentAnnotations:
|
||||
# argocd.argoproj.io/sync-wave: "70"
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v1.3.29"
|
||||
tag: "v1.3.13"
|
||||
replicas: 1 # Number of pods for the service.
|
||||
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
|
||||
# Optional extra volumes / volumeMounts for this deployment (empty = none).
|
||||
@@ -1301,11 +1015,8 @@ kerberospipeline:
|
||||
cpu: 10m
|
||||
sequence:
|
||||
repository: ghcr.io/uug-ai/hub-pipeline-sequence
|
||||
deploymentAnnotations: {}
|
||||
# deploymentAnnotations:
|
||||
# argocd.argoproj.io/sync-wave: "60"
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v1.7.14"
|
||||
tag: "v1.6.26"
|
||||
replicas: 1 # Number of pods for the service.
|
||||
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
|
||||
# Optional extra volumes / volumeMounts for this deployment (empty = none).
|
||||
@@ -1315,20 +1026,14 @@ kerberospipeline:
|
||||
#volumeMounts:
|
||||
# - name: extra
|
||||
# mountPath: /data
|
||||
logLevel: "info" # possible values: debug, info, warn, error
|
||||
resources:
|
||||
requests:
|
||||
memory: 10Mi
|
||||
cpu: 10m
|
||||
throttler:
|
||||
securityContext: {}
|
||||
podSecurityContext: {}
|
||||
repository: ghcr.io/uug-ai/hub-pipeline-throttler
|
||||
deploymentAnnotations: {}
|
||||
# deploymentAnnotations:
|
||||
# argocd.argoproj.io/sync-wave: "30"
|
||||
repository: uugai/hub-pipeline-throttler
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v1.2.10"
|
||||
tag: "v1.2.1"
|
||||
replicas: 1 # Number of pods for the service.
|
||||
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
|
||||
# Optional extra volumes / volumeMounts for this deployment (empty = none).
|
||||
@@ -1344,17 +1049,10 @@ kerberospipeline:
|
||||
memory: 10Mi
|
||||
cpu: 10m
|
||||
notify:
|
||||
securityContext: {}
|
||||
podSecurityContext: {}
|
||||
repository: ghcr.io/uug-ai/hub-pipeline-notification
|
||||
deploymentAnnotations: {}
|
||||
# deploymentAnnotations:
|
||||
# argocd.argoproj.io/sync-wave: "20"
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v1.3.46"
|
||||
tag: "v1.3.18"
|
||||
replicas: 1 # Number of pods for the service.
|
||||
audit:
|
||||
enabled: true
|
||||
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
|
||||
logLevel: "info" # possible values: trace, debug, info, warn, error
|
||||
notificationExpiryMinutes: "15" # Maximum recording age before notification delivery; 0 disables expiry.
|
||||
@@ -1371,9 +1069,9 @@ kerberospipeline:
|
||||
# persistentVolumeClaim:
|
||||
# claimName: custom-layout-claim
|
||||
notifyTest:
|
||||
repository: ghcr.io/uug-ai/hub-pipeline-notification-test
|
||||
repository: uugai/hub-pipeline-notification-test
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v1.2.6"
|
||||
tag: "v1.2.2"
|
||||
replicas: 1 # Number of pods for the service.
|
||||
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
|
||||
resources:
|
||||
@@ -1390,11 +1088,8 @@ kerberospipeline:
|
||||
# claimName: custom-layout-claim
|
||||
analysis:
|
||||
repository: ghcr.io/uug-ai/hub-pipeline-analysis
|
||||
deploymentAnnotations: {}
|
||||
# deploymentAnnotations:
|
||||
# argocd.argoproj.io/sync-wave: "40"
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v1.8.29"
|
||||
tag: "v1.8.5"
|
||||
replicas: 1 # Number of pods for the service.
|
||||
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
|
||||
# Optional extra volumes / volumeMounts for this deployment (empty = none).
|
||||
@@ -1405,34 +1100,14 @@ kerberospipeline:
|
||||
# - name: extra
|
||||
# mountPath: /data
|
||||
logLevel: "info" # possible values: trace, debug, info, warn, error
|
||||
# Opt-in: after classification, re-enqueue the recording on the thumbnail
|
||||
# queue to regenerate its thumbnail from the first frame of a tracked moving
|
||||
# object (THUMBNAIL_REGENERATION_ENABLED). Adds one thumbnail job per
|
||||
# classified recording; size pipe-thumbnail accordingly before enabling.
|
||||
thumbnailRegeneration:
|
||||
enabled: false
|
||||
# In-memory cache for hot-path MongoDB lookups (site vault per device).
|
||||
# Site changes apply after at most one TTL; each replica caches separately.
|
||||
dbCache:
|
||||
enabled: false
|
||||
ttl: "5m" # Go duration, e.g. "30s", "5m"
|
||||
resources:
|
||||
requests:
|
||||
memory: 10Mi
|
||||
cpu: 10m
|
||||
dominantColor:
|
||||
securityContext: {}
|
||||
podSecurityContext: {}
|
||||
# Opt-in: deploys pipe-dominantcolor and tells the analysis stage to schedule
|
||||
# it (DOMINANTCOLOR_ENABLED). Requires an analysis image that reads the flag;
|
||||
# older images always publish to the dominantcolor queue.
|
||||
enabled: false # Enable or disable dominant color extraction 'true' or 'false'
|
||||
repository: ghcr.io/uug-ai/hub-pipeline-dominantcolors
|
||||
deploymentAnnotations: {}
|
||||
# deploymentAnnotations:
|
||||
# argocd.argoproj.io/sync-wave: "50"
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v2.0.9"
|
||||
tag: "v2.0.3"
|
||||
replicas: 3 # Number of pods for the service.
|
||||
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
|
||||
# Optional extra volumes / volumeMounts for this deployment (empty = none).
|
||||
@@ -1451,16 +1126,10 @@ kerberospipeline:
|
||||
memory: 2Gi
|
||||
cpu: 1000m
|
||||
thumbnail:
|
||||
securityContext: {}
|
||||
podSecurityContext: {}
|
||||
repository: ghcr.io/uug-ai/hub-pipeline-thumbnail
|
||||
deploymentAnnotations: {}
|
||||
# deploymentAnnotations:
|
||||
# argocd.argoproj.io/sync-wave: "50"
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v1.3.22"
|
||||
tag: "v1.3.10"
|
||||
replicas: 2 # Number of pods for the service.
|
||||
prefetchCount: 0 # Max unacknowledged RabbitMQ messages per pod; 0 = service default (5).
|
||||
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
|
||||
# Optional extra volumes / volumeMounts for this deployment (empty = none).
|
||||
#volumes:
|
||||
@@ -1473,7 +1142,6 @@ kerberospipeline:
|
||||
quality: "1" # 1 (best) - 31 (worst)
|
||||
width: "600"
|
||||
height: "-1"
|
||||
skipNonKeyframes: true # Decode keyframes only for faster extraction; disable when exact-frame output is required.
|
||||
kerberosvault:
|
||||
enabled: true # If you want to use Kerberos Vault to store the thumbnails
|
||||
resources:
|
||||
@@ -1484,14 +1152,9 @@ kerberospipeline:
|
||||
memory: 2Gi
|
||||
cpu: 1000m
|
||||
counting:
|
||||
securityContext: {}
|
||||
podSecurityContext: {}
|
||||
repository: ghcr.io/uug-ai/hub-pipeline-counting
|
||||
deploymentAnnotations: {}
|
||||
# deploymentAnnotations:
|
||||
# argocd.argoproj.io/sync-wave: "50"
|
||||
repository: uugai/hub-pipeline-counting
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v2.0.6"
|
||||
tag: "v2.0.0"
|
||||
replicas: 1 # Number of pods for the service.
|
||||
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
|
||||
# Optional extra volumes / volumeMounts for this deployment (empty = none).
|
||||
@@ -1508,14 +1171,10 @@ kerberospipeline:
|
||||
cpu: 10m
|
||||
sprite:
|
||||
enabled: false # Enable or disable the sprite generation 'true' or 'false
|
||||
deploymentAnnotations: {}
|
||||
# deploymentAnnotations:
|
||||
# argocd.argoproj.io/sync-wave: "50"
|
||||
repository: ghcr.io/uug-ai/hub-pipeline-sprite
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v1.1.27"
|
||||
tag: "v1.1.16"
|
||||
replicas: 5 # Number of pods for the service.
|
||||
prefetchCount: 0 # Max unacknowledged RabbitMQ messages per pod; 0 = service default (5).
|
||||
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
|
||||
# Optional extra volumes / volumeMounts for this deployment (empty = none).
|
||||
#volumes:
|
||||
@@ -1526,10 +1185,8 @@ kerberospipeline:
|
||||
# mountPath: /data
|
||||
logLevel: "info" # possible values: trace, debug, info, warn,
|
||||
interval: "1" # Number of secondes between each thumbnail in the sprite
|
||||
# Tile size in pixels. Preserve width:height = 16:9 (e.g. 240x135, 480x270, 640x360).
|
||||
# Deploy dimension-aware sprite/analysis workers, Hub API and frontend before increasing.
|
||||
width: "240"
|
||||
height: "135"
|
||||
width: "240" # Should not be changed for the moment (hard coded in UI)
|
||||
height: "135" # Should not be changed for the moment (hard coded in UI)
|
||||
resources:
|
||||
requests:
|
||||
memory: 512Mi
|
||||
@@ -1540,7 +1197,7 @@ kerberospipeline:
|
||||
export:
|
||||
repository: ghcr.io/uug-ai/hub-pipeline-export
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v1.2.20"
|
||||
tag: "v1.2.10"
|
||||
replicas: 2 # Number of pods for the service.
|
||||
topologySpreadConstraints: [] # Optional pod topology spread constraints (empty = none).
|
||||
logLevel: "info" # possible values: trace, debug, info, warn, error
|
||||
@@ -1593,16 +1250,5 @@ email:
|
||||
disabled: "disable"
|
||||
highupload: "highupload"
|
||||
device: "device"
|
||||
# Sent instead of "device" when several cameras of an account change status
|
||||
# in the same scan and the account groups device notifications.
|
||||
deviceGroup: "device_group"
|
||||
alertTitle: "[Alert] Kerberos Hub detected something an event"
|
||||
deviceTitle: "[Device] A Kerberos Agent's status has been changed"
|
||||
deviceGroupTitle: "[Device] Several cameras changed status"
|
||||
# Recording check alerts (Alerts → Devices → Recording check). Empty uses
|
||||
# the "recording" template with SMTP. For SendGrid or Mailgun, set the
|
||||
# template id; empty uses the device template there.
|
||||
recording: ""
|
||||
# Empty uses "[Device] A camera stopped recording", "[Device] A camera is
|
||||
# recording again" and their forms for several cameras.
|
||||
recordingTitle: ""
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
CHART_DIR="${1:-charts/hub}"
|
||||
|
||||
resource() {
|
||||
local source="$1"
|
||||
awk -v source="# Source: hub/templates/admin/$source.yaml" '
|
||||
/^---/ { selected=0 }
|
||||
/^# Source:/ { selected=($0 == source); next }
|
||||
selected { print }
|
||||
'
|
||||
}
|
||||
|
||||
assert_contains() {
|
||||
local rendered="$1" pattern="$2"
|
||||
if ! grep -Eq -- "$pattern" <<< "$rendered"; then
|
||||
printf 'Missing expected Admin setting: %s\n' "$pattern" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
assert_absent() {
|
||||
local rendered="$1" pattern="$2"
|
||||
if grep -Eq -- "$pattern" <<< "$rendered"; then
|
||||
printf 'Unexpected Admin setting: %s\n' "$pattern" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
HARDENED_FLAGS=(
|
||||
--set admin.containerPort=8080
|
||||
--set-json 'admin.securityContext={"runAsNonRoot":true,"runAsUser":65532,"runAsGroup":65532,"readOnlyRootFilesystem":true,"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]},"seccompProfile":{"type":"RuntimeDefault"}}'
|
||||
--set-json 'admin.volumes=[{"name":"admin-tmp","emptyDir":{"medium":"Memory","sizeLimit":"16Mi"}}]'
|
||||
--set-json 'admin.volumeMounts=[{"name":"admin-tmp","mountPath":"/tmp"}]'
|
||||
--set-json 'admin.readinessProbe={"httpGet":{"path":"/healthz","port":"http"},"periodSeconds":10}'
|
||||
--set-json 'admin.livenessProbe={"httpGet":{"path":"/healthz","port":"http"},"initialDelaySeconds":10,"periodSeconds":10}'
|
||||
)
|
||||
|
||||
for mode in all ui; do
|
||||
default_render="$(helm template hub "$CHART_DIR" --set "mode=$mode" --set admin.oauth2Proxy.enabled=true)"
|
||||
default_deployment="$(resource deployment <<< "$default_render")"
|
||||
default_service="$(resource service <<< "$default_render")"
|
||||
default_ingress="$(resource ingress <<< "$default_render")"
|
||||
default_oauth="$(resource oauth2-proxy <<< "$default_render")"
|
||||
assert_contains "$default_deployment" 'containerPort: 80$'
|
||||
assert_absent "$default_deployment" 'securityContext:|readinessProbe:|livenessProbe:'
|
||||
assert_contains "$default_service" 'port: 80$'
|
||||
assert_contains "$default_service" 'targetPort: http$'
|
||||
|
||||
hardened_render="$(helm template hub "$CHART_DIR" --set "mode=$mode" --set admin.oauth2Proxy.enabled=true "${HARDENED_FLAGS[@]}")"
|
||||
hardened_deployment="$(resource deployment <<< "$hardened_render")"
|
||||
assert_contains "$hardened_deployment" 'containerPort: 8080$'
|
||||
assert_absent "$hardened_deployment" 'containerPort: 80$'
|
||||
for setting in 'runAsNonRoot: true' 'runAsUser: 65532' 'runAsGroup: 65532' \
|
||||
'readOnlyRootFilesystem: true' 'allowPrivilegeEscalation: false' \
|
||||
'type: RuntimeDefault' 'drop:' '- ALL' 'medium: Memory' \
|
||||
'sizeLimit: 16Mi' 'mountPath: /tmp' 'name: admin-tmp'; do
|
||||
assert_contains "$hardened_deployment" "^[[:space:]]*(- )?$setting$"
|
||||
done
|
||||
for probe in readinessProbe livenessProbe; do
|
||||
probe_render="$(awk -v probe="$probe:" '
|
||||
/^ [a-zA-Z]/ { selected=($1 == probe) }
|
||||
selected { print }
|
||||
' <<< "$hardened_deployment")"
|
||||
assert_contains "$probe_render" 'path: /healthz$'
|
||||
assert_contains "$probe_render" 'port: http$'
|
||||
done
|
||||
|
||||
[[ "$(resource service <<< "$hardened_render")" == "$default_service" ]]
|
||||
[[ "$(resource ingress <<< "$hardened_render")" == "$default_ingress" ]]
|
||||
[[ "$(resource oauth2-proxy <<< "$hardened_render")" == "$default_oauth" ]]
|
||||
assert_contains "$default_ingress" 'nginx.ingress.kubernetes.io/auth-url:'
|
||||
assert_contains "$default_ingress" 'number: 80$'
|
||||
|
||||
echo "OK ($mode): legacy defaults; hardened runtime/probes; stable Service, ingress and OAuth2"
|
||||
done
|
||||
|
||||
pipeline_render="$(helm template hub "$CHART_DIR" --set mode=pipeline "${HARDENED_FLAGS[@]}")"
|
||||
assert_absent "$pipeline_render" '^# Source: hub/templates/admin/'
|
||||
|
||||
echo "All Admin runtime checks passed."
|
||||
@@ -1,102 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
CHART_DIR="${1:-charts/hub}"
|
||||
values=()
|
||||
if [[ $# -ge 2 ]]; then values=(--values "$2"); fi
|
||||
|
||||
resource() {
|
||||
local kind="$1" name="$2"
|
||||
awk -v kind="$kind" -v name="$name" '
|
||||
function emit() { if (matchedKind && matchedName) printf "%s", block }
|
||||
/^---$/ { emit(); block=""; matchedKind=0; matchedName=0 }
|
||||
{ block=block $0 "\n" }
|
||||
$0 == "kind: " kind { matchedKind=1 }
|
||||
$0 == " name: " name { matchedName=1 }
|
||||
END { emit() }
|
||||
'
|
||||
}
|
||||
|
||||
assert_contains() {
|
||||
if ! grep -Eq -- "$2" <<< "$1"; then
|
||||
printf 'Missing expected frontend setting: %s\n' "$2" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
for mode in all ui; do
|
||||
rendered="$(helm template hub "$CHART_DIR" "${values[@]}" --set "mode=$mode" \
|
||||
--set kerberoshub.frontend.demoEnabled=true)"
|
||||
for name in hub-frontend hub-frontend-demo; do
|
||||
deployment="$(resource Deployment "$name" <<< "$rendered")"
|
||||
assert_contains "$deployment" 'image: "ghcr.io/uug-ai/hub-frontend:v1.19.28"$'
|
||||
assert_contains "$deployment" 'containerPort: 8080$'
|
||||
[[ "$(grep -c 'containerPort:' <<< "$deployment")" == 1 ]]
|
||||
for setting in 'name: http' 'runAsNonRoot: true' 'runAsUser: 65532' \
|
||||
'runAsGroup: 65532' 'readOnlyRootFilesystem: true' \
|
||||
'allowPrivilegeEscalation: false' 'type: RuntimeDefault' 'drop:' '- ALL' \
|
||||
'medium: Memory' 'sizeLimit: 16Mi' 'mountPath: /tmp' 'name: frontend-tmp'; do
|
||||
assert_contains "$deployment" "^[[:space:]]*(- )?$setting$"
|
||||
done
|
||||
service="$(resource Service "$name-svc" <<< "$rendered")"
|
||||
assert_contains "$service" 'port: 80$'
|
||||
assert_contains "$service" 'targetPort: http$'
|
||||
echo "OK ($mode): $name hardened image, security, scratch and named routing"
|
||||
done
|
||||
admin="$(resource Deployment admin <<< "$rendered")"
|
||||
assert_contains "$admin" 'containerPort: 80$'
|
||||
|
||||
legacy="$(helm template hub "$CHART_DIR" "${values[@]}" --set "mode=$mode" \
|
||||
--set kerberoshub.frontend.demoEnabled=true \
|
||||
--set kerberoshub.frontend.tag=v1.13.9 \
|
||||
--set kerberoshub.frontend.containerPort=80 \
|
||||
--set kerberoshub.frontend.securityContext=null \
|
||||
--set-json 'kerberoshub.frontend.volumes=[]' \
|
||||
--set-json 'kerberoshub.frontend.volumeMounts=[]')"
|
||||
for name in hub-frontend hub-frontend-demo; do
|
||||
deployment="$(resource Deployment "$name" <<< "$legacy")"
|
||||
assert_contains "$deployment" 'containerPort: 80$'
|
||||
assert_contains "$deployment" 'image: "ghcr.io/uug-ai/hub-frontend:v1.13.9"$'
|
||||
if grep -Eq 'securityContext:|mountPath: /tmp' <<< "$deployment"; then
|
||||
echo "Legacy frontend retains incompatible hardened runtime settings" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
if [[ "${CLUSTER_SMOKE:-false}" == true ]]; then
|
||||
namespace=frontend-runtime-test
|
||||
kubectl create namespace "$namespace"
|
||||
helm template runtime "$CHART_DIR" "${values[@]}" --namespace "$namespace" \
|
||||
--set mode=ui --set ingress="" --set kerberoshub.frontend.demoEnabled=true \
|
||||
--show-only templates/kerberos-hub/hub-frontend.yaml \
|
||||
--show-only templates/kerberos-hub/hub-frontend-demo.yaml | kubectl apply -f -
|
||||
pid=""
|
||||
cleanup() {
|
||||
if [[ -n "$pid" ]]; then kill "$pid"; fi
|
||||
if [[ "$1" != 0 ]]; then
|
||||
kubectl -n "$namespace" get pods
|
||||
kubectl -n "$namespace" describe pods
|
||||
kubectl -n "$namespace" logs -l app=hub-frontend --all-containers=true
|
||||
kubectl -n "$namespace" logs -l app=hub-frontend-demo --all-containers=true
|
||||
fi
|
||||
}
|
||||
trap 'cleanup "$?"' EXIT
|
||||
for name in hub-frontend hub-frontend-demo; do
|
||||
kubectl -n "$namespace" rollout status "deployment/$name" --timeout=180s
|
||||
kubectl -n "$namespace" port-forward "service/$name-svc" 18080:80 &
|
||||
pid=$!
|
||||
for attempt in {1..30}; do
|
||||
if curl --fail --silent http://127.0.0.1:18080/healthz > /dev/null; then break; fi
|
||||
sleep 1
|
||||
done
|
||||
[[ "$(curl --fail --silent http://127.0.0.1:18080/healthz)" == ok ]]
|
||||
curl --fail --silent http://127.0.0.1:18080/assets/env.js | grep 'window' > /dev/null
|
||||
kill "$pid"
|
||||
wait "$pid" || [[ "$?" == 143 ]]
|
||||
pid=""
|
||||
echo "OK: $name serves health and generated runtime configuration through Service port 80"
|
||||
done
|
||||
fi
|
||||
|
||||
echo "Frontend runtime checks passed."
|
||||
@@ -1,194 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
CHART_DIR="${1:-charts/hub}"
|
||||
components=(
|
||||
kerberospipeline.counting kerberospipeline.throttler
|
||||
kerberospipeline.notify
|
||||
kerberospipeline.thumbnail kerberospipeline.dominantColor
|
||||
kerberoshub.cleanup kerberoshub.monitordevice
|
||||
kerberoshub.services.workflows kerberoshub.services.loitering
|
||||
)
|
||||
fixtures=(
|
||||
--set kerberospipeline.dominantColor.enabled=true
|
||||
--set kerberoshub.workflows.enabled=true
|
||||
--set kerberoshub.services.loitering.enabled=true
|
||||
--set kerberoshub.services.loitering.repository=example/loitering
|
||||
--set kerberoshub.services.loitering.tag=test
|
||||
--set kerberoshub.services.loitering.queue=loitering
|
||||
--set mongodb.tls.enabled=true
|
||||
--set mongodb.tls.existingSecret=mongodb-ca
|
||||
--set mongodb.tls.caFileName=ca.crt
|
||||
)
|
||||
security=()
|
||||
for component in "${components[@]}"; do
|
||||
security+=(
|
||||
--set "$component.securityContext.runAsNonRoot=true"
|
||||
--set "$component.securityContext.runAsUser=65532"
|
||||
--set "$component.securityContext.runAsGroup=65532"
|
||||
--set "$component.securityContext.readOnlyRootFilesystem=true"
|
||||
--set "$component.securityContext.allowPrivilegeEscalation=false"
|
||||
--set "$component.securityContext.capabilities.drop[0]=ALL"
|
||||
--set "$component.securityContext.seccompProfile.type=RuntimeDefault"
|
||||
--set "$component.podSecurityContext.fsGroup=65532"
|
||||
--set "$component.volumes[0].name=scratch"
|
||||
--set "$component.volumes[0].emptyDir.sizeLimit=4Gi"
|
||||
--set "$component.volumeMounts[0].name=scratch"
|
||||
--set "$component.volumeMounts[0].mountPath=/tmp"
|
||||
--set "$component.resources.requests.ephemeral-storage=4Gi"
|
||||
--set "$component.resources.limits.ephemeral-storage=6Gi"
|
||||
)
|
||||
done
|
||||
|
||||
resource() {
|
||||
local kind="$1" name="$2"
|
||||
awk -v kind="$kind" -v name="$name" '
|
||||
function emit() { if (matchedKind && matchedName) printf "%s", block }
|
||||
/^---$/ { emit(); block=""; matchedKind=0; matchedName=0 }
|
||||
{ block=block $0 "\n" }
|
||||
$0 == "kind: " kind { matchedKind=1 }
|
||||
$0 == " name: " name { matchedName=1 }
|
||||
END { emit() }
|
||||
'
|
||||
}
|
||||
|
||||
assert_line() {
|
||||
local block="$1" pattern="$2"
|
||||
if ! grep -Eq "$pattern" <<< "$block"; then
|
||||
printf 'Missing expected pattern: %s\n%s\n' "$pattern" "$block" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
without_frontends() {
|
||||
awk '
|
||||
/^---$/ { skip=0 }
|
||||
/^# Source: hub\/templates\/kerberos-hub\/(hub-frontend(-demo)?|hub-api)\.yaml$/ { skip=1 }
|
||||
!skip { print }
|
||||
'
|
||||
}
|
||||
|
||||
for mode in all pipeline ui; do
|
||||
defaults="$(helm template hub "$CHART_DIR" --set "mode=$mode")"
|
||||
worker_defaults="$(without_frontends <<< "$defaults")"
|
||||
if grep -q 'securityContext:' <<< "$worker_defaults"; then
|
||||
echo "Default $mode mode unexpectedly enables worker security settings" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
legacy="$(helm template hub "$CHART_DIR" --set "mode=$mode" "${fixtures[@]}")"
|
||||
hardened="$(helm template hub "$CHART_DIR" --set "mode=$mode" "${fixtures[@]}" "${security[@]}")"
|
||||
helm lint "$CHART_DIR" --quiet --set "mode=$mode" "${fixtures[@]}" "${security[@]}"
|
||||
names=()
|
||||
if [[ "$mode" != ui ]]; then
|
||||
names+=(pipe-counting pipe-throttler pipe-notify pipe-thumbnail pipe-dominantcolor hub-workflows hub-loitering)
|
||||
fi
|
||||
if [[ "$mode" != pipeline ]]; then
|
||||
names+=(hub-cleanup hub-monitor-device)
|
||||
fi
|
||||
for name in "${names[@]}"; do
|
||||
block="$(resource Deployment "$name" <<< "$hardened")"
|
||||
for pattern in \
|
||||
'^[ ]{6}securityContext:$' '^[ ]{8}fsGroup: 65532$' \
|
||||
'^[ ]+(runAsUser|runAsGroup): 65532$' \
|
||||
'^[ ]+runAsNonRoot: true$' '^[ ]+readOnlyRootFilesystem: true$' \
|
||||
'^[ ]+allowPrivilegeEscalation: false$' '^[ ]+capabilities:$' \
|
||||
'^[ ]+drop:$' '^[ ]+- ALL$' '^[ ]+seccompProfile:$' \
|
||||
'^[ ]+type: RuntimeDefault$' '^[ ]+(- )?mountPath: /tmp$' \
|
||||
'^[ ]+sizeLimit: 4Gi$' '^[ ]+ephemeral-storage: 6Gi$' \
|
||||
'^[ ]+ephemeral-storage: 4Gi$'; do
|
||||
assert_line "$block" "$pattern"
|
||||
done
|
||||
# Verify both identity axes independently.
|
||||
assert_line "$block" '^[ ]+runAsUser: 65532$'
|
||||
assert_line "$block" '^[ ]+runAsGroup: 65532$'
|
||||
assert_line "$block" '^[ ]{8,10}securityContext:$'
|
||||
if [[ "$name" == hub-cleanup || "$name" == hub-monitor-device || "$name" == hub-workflows || "$name" == pipe-notify ]]; then
|
||||
assert_line "$block" '^[ ]+secretName: mongodb-ca$'
|
||||
assert_line "$block" '^[ ]+mountPath: /etc/mongodb/tls$'
|
||||
assert_line "$block" '^[ ]+readOnly: true$'
|
||||
fi
|
||||
if [[ "$name" == pipe-notify ]]; then
|
||||
# Provider, queue, FFmpeg and all other worker settings are unchanged.
|
||||
old_block="$(resource Deployment pipe-notify <<< "$legacy")"
|
||||
[[ "$(sed -n '/^ env:/,$p' <<< "$old_block")" == "$(sed -n '/^ env:/,$p' <<< "$block")" ]]
|
||||
fi
|
||||
if [[ "$name" != hub-monitor-device ]]; then
|
||||
service="$(resource Service "$name" <<< "$hardened")"
|
||||
assert_line "$service" '^[ ]+port: 8080$'
|
||||
assert_line "$service" '^[ ]+targetPort: 8080$'
|
||||
fi
|
||||
done
|
||||
expected="${#names[@]}"
|
||||
[[ "$(without_frontends <<< "$hardened" | grep -c 'runAsUser: 65532')" == "$expected" ]]
|
||||
[[ "$(without_frontends <<< "$hardened" | grep -c 'fsGroup: 65532')" == "$expected" ]]
|
||||
|
||||
# Image selection, Services, env and TLS mounts must not change.
|
||||
for pattern in '^[ ]+image:' '^[ ]+(port|targetPort|containerPort):' 'mongodb-tls'; do
|
||||
[[ "$(grep -E "$pattern" <<< "$legacy")" == "$(grep -E "$pattern" <<< "$hardened")" ]]
|
||||
done
|
||||
echo "OK ($mode): defaults unchanged; $expected independent hardened workers preserve images, ports and MongoDB TLS"
|
||||
done
|
||||
|
||||
# Hardening one worker must not opt in another, even on the same pod mode.
|
||||
isolated="$(helm template hub "$CHART_DIR" --set mode=pipeline \
|
||||
--set kerberospipeline.counting.securityContext.runAsUser=65532)"
|
||||
[[ "$(grep -c 'runAsUser: 65532' <<< "$isolated")" == 1 ]]
|
||||
assert_line "$(resource Deployment pipe-counting <<< "$isolated")" '^[ ]+runAsUser: 65532$'
|
||||
echo "All hardened worker checks passed."
|
||||
|
||||
admin_flags=(
|
||||
--set admin.containerPort=8080
|
||||
--set admin.securityContext.runAsNonRoot=true
|
||||
--set admin.securityContext.runAsUser=65532
|
||||
--set admin.securityContext.runAsGroup=65532
|
||||
--set admin.securityContext.readOnlyRootFilesystem=true
|
||||
--set admin.securityContext.allowPrivilegeEscalation=false
|
||||
--set admin.securityContext.capabilities.drop[0]=ALL
|
||||
--set admin.securityContext.seccompProfile.type=RuntimeDefault
|
||||
--set admin.volumes[0].name=admin-tmp
|
||||
--set admin.volumes[0].emptyDir.medium=Memory
|
||||
--set admin.volumes[0].emptyDir.sizeLimit=16Mi
|
||||
--set admin.volumeMounts[0].name=admin-tmp
|
||||
--set admin.volumeMounts[0].mountPath=/tmp
|
||||
--set admin.oauth2Proxy.enabled=true
|
||||
)
|
||||
for mode in all ui pipeline; do
|
||||
legacy="$(helm template hub "$CHART_DIR" --set "mode=$mode" --set admin.oauth2Proxy.enabled=true)"
|
||||
configured="$(helm template hub "$CHART_DIR" --set "mode=$mode" "${admin_flags[@]}")"
|
||||
helm lint "$CHART_DIR" --quiet --set "mode=$mode" "${admin_flags[@]}"
|
||||
if [[ "$mode" == pipeline ]]; then
|
||||
[[ -z "$(resource Deployment admin <<< "$configured")" ]]
|
||||
[[ -z "$(resource Service admin-svc <<< "$configured")" ]]
|
||||
[[ "$legacy" == "$configured" ]]
|
||||
echo "OK (pipeline): Admin settings render no Admin resources or unrelated changes"
|
||||
continue
|
||||
fi
|
||||
old="$(resource Deployment admin <<< "$legacy")"
|
||||
block="$(resource Deployment admin <<< "$configured")"
|
||||
assert_line "$old" '^[ ]+containerPort: 80$'
|
||||
[[ "$old" != *securityContext:* && "$old" != *admin-tmp* ]]
|
||||
for pattern in \
|
||||
'^[ ]+containerPort: 8080$' '^[ ]{10}securityContext:$' \
|
||||
'^[ ]+runAsUser: 65532$' '^[ ]+runAsGroup: 65532$' \
|
||||
'^[ ]+runAsNonRoot: true$' '^[ ]+readOnlyRootFilesystem: true$' \
|
||||
'^[ ]+allowPrivilegeEscalation: false$' '^[ ]+capabilities:$' \
|
||||
'^[ ]+drop:$' '^[ ]+- ALL$' '^[ ]+seccompProfile:$' \
|
||||
'^[ ]+type: RuntimeDefault$' '^[ ]+medium: Memory$' \
|
||||
'^[ ]+sizeLimit: 16Mi$' '^[ ]+(- )?mountPath: /tmp$'; do
|
||||
assert_line "$block" "$pattern"
|
||||
done
|
||||
old_service="$(resource Service admin-svc <<< "$legacy")"
|
||||
service="$(resource Service admin-svc <<< "$configured")"
|
||||
assert_line "$old_service" '^[ ]+targetPort: http$'
|
||||
assert_line "$service" '^[ ]+port: 80$'
|
||||
assert_line "$service" '^[ ]+targetPort: http$'
|
||||
[[ "$(resource Ingress admin-ingress <<< "$legacy")" == "$(resource Ingress admin-ingress <<< "$configured")" ]]
|
||||
[[ "$(resource Deployment oauth2-proxy-admin <<< "$legacy")" == "$(resource Deployment oauth2-proxy-admin <<< "$configured")" ]]
|
||||
[[ "$(resource Service oauth2-proxy-admin <<< "$legacy")" == "$(resource Service oauth2-proxy-admin <<< "$configured")" ]]
|
||||
[[ "$(grep 'image:' <<< "$legacy")" == "$(grep 'image:' <<< "$configured")" ]]
|
||||
# No other workload inherits Admin's container port or security settings.
|
||||
[[ "$(without_frontends <<< "$configured" | grep -c 'runAsUser: 65532')" == 1 ]]
|
||||
echo "OK ($mode): Admin opt-in port/security/tmpfs; legacy image, Service port, ingress and OAuth2-proxy preserved"
|
||||
done
|
||||
echo "All hardened Admin checks passed."
|
||||
@@ -1,100 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
REAL_KUBECTL="$(command -v kubectl)"
|
||||
export REAL_KUBECTL
|
||||
|
||||
# Substitute only transport: exercise the migration against Kubernetes' real patch engine.
|
||||
kubectl() {
|
||||
if [[ "$*" == *" get deployment "* ]]; then
|
||||
printf '%s\n' "$LIVE"
|
||||
else
|
||||
local patch="" previous="" argument
|
||||
for argument in "$@"; do
|
||||
if [[ "$previous" == -p ]]; then patch="$argument"; fi
|
||||
previous="$argument"
|
||||
done
|
||||
if [[ "${EXPECT_APPLY:-false}" == true ]]; then
|
||||
[[ "$*" != *"--dry-run=server"* ]] || return 1
|
||||
else
|
||||
[[ "$*" == *"--dry-run=server"* ]] || return 1
|
||||
fi
|
||||
local current="$LIVE"
|
||||
if [[ "${SIMULATE_RACE:-false}" == true ]]; then
|
||||
current="$(jq '.metadata.resourceVersion = "2"' <<< "$LIVE")"
|
||||
fi
|
||||
"$REAL_KUBECTL" patch --local -f <(printf '%s\n' "$current") \
|
||||
--type=json -p "$patch" -o json
|
||||
fi
|
||||
}
|
||||
export -f kubectl
|
||||
|
||||
for deployment in admin hub-frontend hub-frontend-demo; do
|
||||
for old in 80 8080; do
|
||||
target=8080
|
||||
if [[ "$old" == 8080 ]]; then target=80; fi
|
||||
LIVE="$(jq -nc --arg name "$deployment" --argjson port "$old" '{
|
||||
apiVersion: "apps/v1", kind: "Deployment",
|
||||
metadata: {name: $name, resourceVersion: "1"},
|
||||
spec: {selector: {matchLabels: {app: $name}}, template: {
|
||||
metadata: {labels: {app: $name}}, spec: {containers: [
|
||||
{name: "sidecar", image: "example/sidecar:unchanged"},
|
||||
{name: $name, image: "example/frontend:unchanged", ports: [
|
||||
{name: "http", containerPort: $port},
|
||||
{name: "metrics", containerPort: 2112}
|
||||
]}
|
||||
]}
|
||||
}}
|
||||
}')"
|
||||
export LIVE
|
||||
merge="$(jq -nc --arg name "$deployment" --argjson port "$target" \
|
||||
'{spec:{template:{spec:{containers:[{name:$name,ports:[{name:"http",containerPort:$port}]}]}}}}')"
|
||||
merged="$("$REAL_KUBECTL" patch --local -f <(printf '%s\n' "$LIVE") \
|
||||
--type=strategic -p "$merge" -o json)"
|
||||
jq -e '[.spec.template.spec.containers[] | .ports[]? |
|
||||
select(.name == "http")] | length == 2' <<< "$merged" > /dev/null
|
||||
|
||||
fixed="$(bash "$ROOT/scripts/migrate-http-port.sh" test "$deployment" "$target")"
|
||||
jq -e --argjson port "$target" '
|
||||
[.spec.template.spec.containers[] | .ports[]? | select(.name == "http")] |
|
||||
length == 1 and .[0].containerPort == $port
|
||||
' <<< "$fixed" > /dev/null
|
||||
[[ "$(jq -Sc '.spec.template.spec.containers[0]' <<< "$fixed")" == \
|
||||
"$(jq -Sc '.spec.template.spec.containers[0]' <<< "$LIVE")" ]]
|
||||
[[ "$(jq -Sc '.spec.template.spec.containers[1] | del(.ports)' <<< "$fixed")" == \
|
||||
"$(jq -Sc '.spec.template.spec.containers[1] | del(.ports)' <<< "$LIVE")" ]]
|
||||
jq -e '.spec.template.spec.containers[1].ports |
|
||||
any(.name == "metrics" and .containerPort == 2112)' <<< "$fixed" > /dev/null
|
||||
|
||||
LIVE="$fixed"
|
||||
again="$(EXPECT_APPLY=true bash "$ROOT/scripts/migrate-http-port.sh" test "$deployment" "$target" --apply)"
|
||||
[[ "$(jq -Sc . <<< "$again")" == "$(jq -Sc . <<< "$fixed")" ]]
|
||||
LIVE="$merged"
|
||||
repaired="$(EXPECT_APPLY=true bash "$ROOT/scripts/migrate-http-port.sh" test "$deployment" "$target" --apply)"
|
||||
jq -e '[.spec.template.spec.containers[] | .ports[]? |
|
||||
select(.name == "http")] | length == 1' <<< "$repaired" > /dev/null
|
||||
|
||||
if [[ "${SERVER_VALIDATION:-false}" == true ]]; then
|
||||
if "$REAL_KUBECTL" create --dry-run=server \
|
||||
-f <(jq 'del(.metadata.resourceVersion)' <<< "$merged") > /dev/null 2>&1; then
|
||||
echo "Expected API rejection of duplicate http ports" >&2
|
||||
exit 1
|
||||
fi
|
||||
"$REAL_KUBECTL" create --dry-run=server \
|
||||
-f <(jq 'del(.metadata.resourceVersion)' <<< "$repaired") > /dev/null
|
||||
fi
|
||||
echo "OK: $deployment $old -> $target; duplicate reproduced, replacement and rollback safe"
|
||||
done
|
||||
done
|
||||
|
||||
if SIMULATE_RACE=true bash "$ROOT/scripts/migrate-http-port.sh" test hub-frontend-demo 8080 > /dev/null 2>&1; then
|
||||
echo "Expected rejection of concurrent Deployment changes" >&2
|
||||
exit 1
|
||||
fi
|
||||
LIVE="$(jq '.spec.template.spec.containers[1].ports = [{name:"http",containerPort:9000}]' <<< "$LIVE")"
|
||||
if bash "$ROOT/scripts/migrate-http-port.sh" test hub-frontend-demo 8080 > /dev/null 2>&1; then
|
||||
echo "Expected rejection of an unknown HTTP port" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "HTTP port migration checks passed."
|
||||
@@ -1,86 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
CHART_DIR="${1:-charts/hub}"
|
||||
|
||||
resource() {
|
||||
local kind="$1" name="$2"
|
||||
awk -v kind="$kind" -v name="$name" '
|
||||
function emit() { if (matchedKind && matchedName) printf "%s", block }
|
||||
/^---$/ { emit(); block=""; matchedKind=0; matchedName=0 }
|
||||
{ block=block $0 "\n" }
|
||||
$0 == "kind: " kind { matchedKind=1 }
|
||||
$0 == " name: " name { matchedName=1 }
|
||||
END { emit() }
|
||||
'
|
||||
}
|
||||
|
||||
assert_line() {
|
||||
if ! grep -Eq -- "$2" <<< "$1"; then
|
||||
printf 'Missing expected Hub API setting: %s\n' "$2" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
fixtures=(
|
||||
--set workflowConfiguration.enabled=true
|
||||
--set mongodb.tls.enabled=true
|
||||
--set mongodb.tls.existingSecret=mongodb-ca
|
||||
--set kerberoshub.api.serverTLS.enabled=true
|
||||
--set kerberoshub.api.serverTLS.secretName=api-tls
|
||||
)
|
||||
|
||||
for mode in all ui pipeline; do
|
||||
defaults="$(helm template hub "$CHART_DIR" --set "mode=$mode" "${fixtures[@]}")"
|
||||
legacy="$(helm template hub "$CHART_DIR" --set "mode=$mode" "${fixtures[@]}" \
|
||||
--set kerberoshub.api.securityContext=null \
|
||||
--set kerberoshub.api.podSecurityContext=null)"
|
||||
configured="$(helm template hub "$CHART_DIR" --set "mode=$mode" "${fixtures[@]}" \
|
||||
--set kerberoshub.api.securityContext.runAsUser=1000 \
|
||||
--set kerberoshub.api.securityContext.runAsGroup=1000 \
|
||||
--set kerberoshub.api.podSecurityContext.fsGroup=1000)"
|
||||
helm lint "$CHART_DIR" --quiet --set "mode=$mode" "${fixtures[@]}"
|
||||
|
||||
if [[ "$mode" == pipeline ]]; then
|
||||
[[ -z "$(resource Deployment hub-api <<< "$defaults")" ]]
|
||||
[[ "$defaults" == "$legacy" && "$defaults" == "$configured" ]]
|
||||
echo "OK (pipeline): no Hub API resources or unrelated changes"
|
||||
continue
|
||||
fi
|
||||
|
||||
api="$(resource Deployment hub-api <<< "$defaults")"
|
||||
for pattern in \
|
||||
'^[ ]{6}securityContext:$' '^[ ]{8}fsGroup: 65532$' \
|
||||
'^[ ]{10}securityContext:$' '^[ ]{12}runAsUser: 65532$' \
|
||||
'^[ ]{12}runAsGroup: 65532$' '^[ ]{12}runAsNonRoot: true$' \
|
||||
'^[ ]{12}readOnlyRootFilesystem: true$' \
|
||||
'^[ ]{12}allowPrivilegeEscalation: false$' '^[ ]{12}capabilities:$' \
|
||||
'^[ ]{14}drop:$' '^[ ]{14}- ALL$' \
|
||||
'^[ ]{12}seccompProfile:$' '^[ ]{14}type: RuntimeDefault$' \
|
||||
'^[ ]+(- )?name: hub-api-tmp$' '^[ ]+(- )?emptyDir: \{\}$' \
|
||||
'^[ ]+(- )?mountPath: /tmp$' '^[ ]+secretName: mongodb-ca$' \
|
||||
'^[ ]+secretName: api-tls$' '^[ ]+mountPath: /etc/mongodb/tls$' \
|
||||
'^[ ]+mountPath: /etc/hub-api/tls$' \
|
||||
'^[ ]+mountPath: /etc/kerberos/configuration$' \
|
||||
'^[ ]+mountPath: /etc/kerberos/classifications$'; do
|
||||
assert_line "$api" "$pattern"
|
||||
done
|
||||
[[ "$(grep -c 'securityContext:' <<< "$api")" == 2 ]]
|
||||
old_api="$(resource Deployment hub-api <<< "$legacy")"
|
||||
if grep -q 'securityContext:' <<< "$old_api"; then
|
||||
echo "Legacy Hub API opt-out still renders a security context" >&2
|
||||
exit 1
|
||||
fi
|
||||
custom_api="$(resource Deployment hub-api <<< "$configured")"
|
||||
for field in runAsUser runAsGroup fsGroup; do
|
||||
assert_line "$custom_api" "^[ ]+$field: 1000$"
|
||||
done
|
||||
|
||||
# Only security settings change; preserve env, mounts, images and routing.
|
||||
for pattern in '^[ ]+image:' '^[ ]+(port|targetPort|containerPort):' \
|
||||
'^[ ]+(- )?(name|mountPath|secretName|value):'; do
|
||||
[[ "$(grep -E "$pattern" <<< "$defaults")" == "$(grep -E "$pattern" <<< "$legacy")" ]]
|
||||
[[ "$(grep -E "$pattern" <<< "$defaults")" == "$(grep -E "$pattern" <<< "$configured")" ]]
|
||||
done
|
||||
echo "OK ($mode): API security defaults, overrides, opt-out, scratch and TLS/config mounts"
|
||||
done
|
||||
@@ -1,130 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
CHART_DIR="${1:-charts/hub}"
|
||||
|
||||
extract_passkeys() {
|
||||
awk '
|
||||
/^---/ { deployment=0; name=""; variable="" }
|
||||
/^kind: Deployment$/ { deployment=1 }
|
||||
deployment && /^ name:/ { name=$2 }
|
||||
deployment && /- name: PASSKEY_/ { variable=$3; next }
|
||||
variable != "" && /value:/ {
|
||||
value=$0
|
||||
sub(/^[[:space:]]*value: /, "", value)
|
||||
print name, variable, value
|
||||
variable=""
|
||||
}
|
||||
' | sort
|
||||
}
|
||||
|
||||
assert_passkeys() {
|
||||
local rendered="$1" expected="$2" actual count expected_count
|
||||
actual="$(printf '%s\n' "$rendered" | extract_passkeys)"
|
||||
if [[ "$actual" != "$expected" ]]; then
|
||||
printf 'Unexpected passkey environment:\n%s\nExpected:\n%s\n' "$actual" "$expected" >&2
|
||||
return 1
|
||||
fi
|
||||
count="$(printf '%s\n' "$rendered" | awk '/- name: PASSKEY_/ { count++ } END { print count+0 }')"
|
||||
expected_count="$(printf '%s\n' "$expected" | awk 'NF { count++ } END { print count+0 }')"
|
||||
if [[ "$count" != "$expected_count" ]]; then
|
||||
echo "Passkey variables leaked outside the expected Deployment or were duplicated" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
extract_feature() {
|
||||
awk '
|
||||
/^---/ { deployment=0; name=""; flag=0 }
|
||||
/^kind: Deployment$/ { deployment=1 }
|
||||
deployment && /^ name:/ { name=$2 }
|
||||
deployment && /- name: FEATURE_PASSKEYS_ENABLED$/ { flag=1; next }
|
||||
flag && /value:/ {
|
||||
value=$0
|
||||
sub(/^[[:space:]]*value: /, "", value)
|
||||
print name, value
|
||||
flag=0
|
||||
}
|
||||
' | sort
|
||||
}
|
||||
|
||||
assert_feature() {
|
||||
local rendered="$1" expected="$2" actual
|
||||
actual="$(printf '%s\n' "$rendered" | extract_feature)"
|
||||
if [[ "$actual" != "$expected" ]]; then
|
||||
printf 'Unexpected FEATURE_PASSKEYS_ENABLED:\n%s\nExpected:\n%s\n' "$actual" "$expected" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
CONFIGURED_FLAGS=(
|
||||
--set-json kerberoshub.api.passkeys.enabled=true
|
||||
--set-json 'kerberoshub.api.passkeys.rpId="example.com"'
|
||||
--set-json 'kerberoshub.api.passkeys.origins=["https://hub.example.com","https://legacy.example.com:8443"]'
|
||||
--set-json 'kerberoshub.api.passkeys.rpName="Example Hub"'
|
||||
)
|
||||
expected="$(printf '%s\n' \
|
||||
'hub-api PASSKEY_ORIGINS "https://hub.example.com,https://legacy.example.com:8443"' \
|
||||
'hub-api PASSKEY_RP_ID "example.com"' \
|
||||
'hub-api PASSKEY_RP_NAME "Example Hub"' | sort)"
|
||||
|
||||
for mode in all ui pipeline; do
|
||||
default_render="$(helm template hub "$CHART_DIR" --set "mode=$mode")"
|
||||
assert_passkeys "$default_render" ""
|
||||
configured_render="$(helm template hub "$CHART_DIR" --set "mode=$mode" "${CONFIGURED_FLAGS[@]}")"
|
||||
if [[ "$mode" == pipeline ]]; then
|
||||
assert_passkeys "$configured_render" ""
|
||||
assert_feature "$default_render" ""
|
||||
assert_feature "$configured_render" ""
|
||||
else
|
||||
assert_passkeys "$configured_render" "$expected"
|
||||
assert_feature "$default_render" 'hub-frontend "false"'
|
||||
assert_feature "$configured_render" "$(printf '%s\n' 'hub-api "true"' 'hub-frontend "true"')"
|
||||
fi
|
||||
echo "OK ($mode): defaults opt out; enabled settings configure Hub API and flag the frontend"
|
||||
done
|
||||
|
||||
disabled_render="$(helm template hub "$CHART_DIR" "${CONFIGURED_FLAGS[@]}" --set kerberoshub.api.passkeys.enabled=false)"
|
||||
assert_passkeys "$disabled_render" ""
|
||||
assert_feature "$disabled_render" 'hub-frontend "false"'
|
||||
|
||||
demo_render="$(helm template hub "$CHART_DIR" "${CONFIGURED_FLAGS[@]}" --set kerberoshub.frontend.demoEnabled=true)"
|
||||
assert_feature "$demo_render" "$(printf '%s\n' 'hub-api "true"' 'hub-frontend "true"' 'hub-frontend-demo "true"')"
|
||||
|
||||
localhost_render="$(helm template hub "$CHART_DIR" --set mode=ui \
|
||||
--set kerberoshub.api.passkeys.enabled=true \
|
||||
--set-string kerberoshub.api.passkeys.rpId=localhost \
|
||||
--set-json 'kerberoshub.api.passkeys.origins=["http://localhost:4200"]')"
|
||||
assert_passkeys "$localhost_render" "$(printf '%s\n' \
|
||||
'hub-api PASSKEY_ORIGINS "http://localhost:4200"' \
|
||||
'hub-api PASSKEY_RP_ID "localhost"' \
|
||||
'hub-api PASSKEY_RP_NAME "Kerberos Hub"' | sort)"
|
||||
|
||||
legacy_render="$(helm template hub "$CHART_DIR" --set mode=ui \
|
||||
--set-json 'kerberoshub.extraEnv=[{"name":"PASSKEY_RP_ID","value":"example.com"}]')"
|
||||
printf '%s\n' "$legacy_render" | grep -q -- '- name: PASSKEY_RP_ID'
|
||||
|
||||
expect_invalid() {
|
||||
local output
|
||||
if output="$(helm template hub "$CHART_DIR" "${CONFIGURED_FLAGS[@]}" "$@" 2>&1)"; then
|
||||
echo "Invalid passkey settings unexpectedly rendered: $*" >&2
|
||||
return 1
|
||||
fi
|
||||
if [[ "$output" != *"passkeys"* ]]; then
|
||||
printf 'Rendering failed for an unexpected reason:\n%s\n' "$output" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
expect_invalid --set-string kerberoshub.api.passkeys.rpId=
|
||||
expect_invalid --set-json 'kerberoshub.api.passkeys.origins=[]'
|
||||
expect_invalid --set-json 'kerberoshub.api.passkeys.origins=["https://hub.example.com/"]'
|
||||
expect_invalid --set-json 'kerberoshub.api.passkeys.origins=["http://hub.example.com"]'
|
||||
expect_invalid --set-json 'kerberoshub.api.passkeys.origins=["https://hub.example.com","https://hub.example.com"]'
|
||||
expect_invalid --set-json 'kerberoshub.api.passkeys.origins="https://hub.example.com"'
|
||||
expect_invalid --set-json 'kerberoshub.api.passkeys.enabled="true"'
|
||||
expect_invalid --set-json 'kerberoshub.api.passkeys.rpId=123'
|
||||
expect_invalid --set-json 'kerberoshub.api.passkeys.rpName=123'
|
||||
expect_invalid --set-string kerberoshub.api.passkeys.origin=https://hub.example.com
|
||||
|
||||
echo "All Hub passkey configuration checks passed."
|
||||
@@ -1,81 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
CHART_DIR="${1:-charts/hub}"
|
||||
|
||||
# Supply per-service test overrides without requiring a separate values file.
|
||||
WAVE_FLAGS=()
|
||||
for setting in event:10 notify:20 throttler:30 analysis:40 thumbnail:50 sprite:50 dominantColor:50 counting:50 sequence:60 monitor:70; do
|
||||
service="${setting%%:*}"
|
||||
wave="${setting#*:}"
|
||||
WAVE_FLAGS+=(--set-string "kerberospipeline.${service}.deploymentAnnotations.argocd\\.argoproj\\.io/sync-wave=${wave}")
|
||||
done
|
||||
|
||||
# Only read Deployment metadata, never pod-template or Service annotations.
|
||||
extract_waves() {
|
||||
awk '
|
||||
/^---/ { deployment=0; metadata=0; name="" }
|
||||
/^kind: Deployment$/ { deployment=1 }
|
||||
/^metadata:$/ { metadata=1; next }
|
||||
/^[^ #]/ { metadata=0 }
|
||||
deployment && metadata && /^ name:/ { name=$2 }
|
||||
deployment && metadata && /^ argocd.argoproj.io\/sync-wave:/ {
|
||||
value=$2
|
||||
gsub(/"/, "", value)
|
||||
print name, value
|
||||
}
|
||||
' | sort
|
||||
}
|
||||
|
||||
assert_waves() {
|
||||
local rendered="$1"
|
||||
local expected="$2"
|
||||
local actual
|
||||
actual="$(printf '%s\n' "$rendered" | extract_waves)"
|
||||
if [[ "$actual" != "$expected" ]]; then
|
||||
printf 'Unexpected Deployment waves:\n%s\nExpected:\n%s\n' "$actual" "$expected" >&2
|
||||
return 1
|
||||
fi
|
||||
local all_count expected_count
|
||||
all_count="$(printf '%s\n' "$rendered" | grep -c 'argocd.argoproj.io/sync-wave:' || true)"
|
||||
expected_count="$(printf '%s\n' "$expected" | awk 'NF { count++ } END { print count+0 }')"
|
||||
if [[ "$all_count" != "$expected_count" ]]; then
|
||||
echo "Sync-wave annotation leaked outside expected Deployment metadata" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
expected="$(printf '%s\n' \
|
||||
'pipe-event 10' 'pipe-notify 20' 'pipe-throttler 30' 'pipe-analysis 40' \
|
||||
'pipe-thumbnail 50' 'pipe-sprite 50' 'pipe-dominantcolor 50' \
|
||||
'pipe-counting 50' 'pipe-sequence 60' 'pipe-monitor 70' | sort)"
|
||||
|
||||
for mode in all pipeline; do
|
||||
default_render="$(helm template hub "$CHART_DIR" --set "mode=$mode" --set kerberospipeline.sprite.enabled=true --set kerberospipeline.dominantColor.enabled=true)"
|
||||
assert_waves "$default_render" ""
|
||||
if printf '%s\n' "$default_render" | grep -q '^ annotations: null'; then
|
||||
echo "Empty annotations must be omitted" >&2
|
||||
exit 1
|
||||
fi
|
||||
configured_render="$(helm template hub "$CHART_DIR" "${WAVE_FLAGS[@]}" --set "mode=$mode" --set kerberospipeline.sprite.enabled=true --set kerberospipeline.dominantColor.enabled=true)"
|
||||
assert_waves "$configured_render" "$expected"
|
||||
echo "OK ($mode): defaults opt out; per-service values assign all ten Deployment waves"
|
||||
done
|
||||
|
||||
disabled_optional="$(helm template hub "$CHART_DIR" "${WAVE_FLAGS[@]}" --set mode=pipeline)"
|
||||
assert_waves "$disabled_optional" "$(printf '%s\n' "$expected" | grep -v -e '^pipe-sprite ' -e '^pipe-dominantcolor ')"
|
||||
|
||||
ui_render="$(helm template hub "$CHART_DIR" "${WAVE_FLAGS[@]}" --set mode=ui --set kerberospipeline.sprite.enabled=true --set kerberospipeline.dominantColor.enabled=true)"
|
||||
assert_waves "$ui_render" ""
|
||||
|
||||
# Exercise zero/negative strings and an unrelated annotation on the same map.
|
||||
for wave in 0 -10; do
|
||||
override="$(helm template hub "$CHART_DIR" --set mode=pipeline \
|
||||
--set-string "kerberospipeline.event.deploymentAnnotations.argocd\\.argoproj\\.io/sync-wave=$wave" \
|
||||
--set-string 'kerberospipeline.event.deploymentAnnotations.example\.com/owner=queue-team')"
|
||||
assert_waves "$override" "pipe-event $wave"
|
||||
printf '%s\n' "$override" | grep -q "^ argocd.argoproj.io/sync-wave: \"$wave\"$"
|
||||
printf '%s\n' "$override" | grep -q '^ example.com/owner: queue-team$'
|
||||
done
|
||||
|
||||
echo "All pipeline Deployment annotation checks passed."
|
||||
Executable
+97
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Render the hub chart and assert that every deployment which carries the
|
||||
# workflows hand-off queue (the WORKFLOWS_QUEUE env var) resolves to the SAME,
|
||||
# non-empty value.
|
||||
#
|
||||
# Why: the analysis pipeline (pipe-analysis) publishes opened workflow runs to
|
||||
# WORKFLOWS_QUEUE, the workflows engine (hub-workflows) consumes it, and every
|
||||
# stage worker (hub-stage) routes its result back to it. All three templates
|
||||
# read the single key `kerberoshub.services.workflows.queue`. If a future edit
|
||||
# hardcodes a value, reads the wrong key, or drops the env on one of them, the
|
||||
# producer and consumer silently drift onto different queue names and messages
|
||||
# pile up with no consumer. This check fails the build before that can ship.
|
||||
#
|
||||
# Usage: scripts/check-workflows-queue-consistency.sh [chart-dir]
|
||||
# (chart-dir defaults to charts/hub, relative to the repo root)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CHART_DIR="${1:-charts/hub}"
|
||||
PROBE="drift-probe-queue-name"
|
||||
|
||||
# Flags that force all three deployment kinds (analysis, engine and one stage
|
||||
# worker) to render, so the check actually has something to compare. The chart
|
||||
# ships NO enabled stage worker by default (custom stages are values-only and
|
||||
# opt-in), so we synthesise a throwaway stage purely to exercise the generic
|
||||
# hub-stage path. The name is a neutral fixture ("queuecheck") on purpose: any
|
||||
# arbitrary stage key must render the same way, so the check must not depend on
|
||||
# a specific bundled worker.
|
||||
STAGE="queuecheck"
|
||||
RENDER_FLAGS=(
|
||||
--set mode=all
|
||||
--set kerberoshub.workflows.enabled=true
|
||||
--set "kerberoshub.workflows.stages.${STAGE}.enabled=true"
|
||||
--set "kerberoshub.services.${STAGE}.enabled=true"
|
||||
--set "kerberoshub.services.${STAGE}.repository=example.invalid/queuecheck"
|
||||
--set "kerberoshub.services.${STAGE}.tag=test"
|
||||
--set "kerberoshub.services.${STAGE}.queue=queuecheck-fixture-queue"
|
||||
)
|
||||
|
||||
# Read `helm template` output on stdin and print one WORKFLOWS_QUEUE value per
|
||||
# line. Matches the `- name: WORKFLOWS_QUEUE` env entry and captures the value
|
||||
# from the following `value:` line, skipping blank/comment lines in between.
|
||||
extract_workflows_queue() {
|
||||
awk '
|
||||
/^[[:space:]]*-[[:space:]]*name:[[:space:]]*WORKFLOWS_QUEUE[[:space:]]*$/ { want=1; next }
|
||||
want==1 {
|
||||
if ($0 ~ /^[[:space:]]*#/ || $0 ~ /^[[:space:]]*$/) next
|
||||
v=$0
|
||||
sub(/^[[:space:]]*value:[[:space:]]*/, "", v)
|
||||
sub(/^"/, "", v); sub(/"[[:space:]]*$/, "", v)
|
||||
sub(/[[:space:]]+$/, "", v)
|
||||
print v
|
||||
want=0
|
||||
}
|
||||
'
|
||||
}
|
||||
|
||||
assert_all_equal() {
|
||||
local expected="$1"; shift
|
||||
local label="$1"; shift
|
||||
local -a vals=("$@")
|
||||
|
||||
if [ "${#vals[@]}" -lt 2 ]; then
|
||||
echo "FAIL (${label}): expected at least 2 WORKFLOWS_QUEUE values (analysis + engine), found ${#vals[@]}" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
local v
|
||||
for v in "${vals[@]}"; do
|
||||
if [ -z "${v}" ]; then
|
||||
echo "FAIL (${label}): a deployment rendered an empty WORKFLOWS_QUEUE value" >&2
|
||||
return 1
|
||||
fi
|
||||
if [ "${v}" != "${expected}" ]; then
|
||||
echo "FAIL (${label}): WORKFLOWS_QUEUE drift detected — expected '${expected}' but a deployment rendered '${v}'" >&2
|
||||
printf ' rendered values: %s\n' "${vals[*]}" >&2
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
|
||||
echo "OK (${label}): ${#vals[@]} deployments all use WORKFLOWS_QUEUE='${expected}'"
|
||||
}
|
||||
|
||||
echo "== Rendering ${CHART_DIR} with the chart's default workflows queue =="
|
||||
default_out="$(helm template hub "${CHART_DIR}" "${RENDER_FLAGS[@]}")"
|
||||
mapfile -t default_vals < <(printf '%s\n' "${default_out}" | extract_workflows_queue)
|
||||
default_queue="${default_vals[0]:-}"
|
||||
assert_all_equal "${default_queue}" "default values" "${default_vals[@]}" || exit 1
|
||||
|
||||
echo "== Rendering ${CHART_DIR} with an overridden workflows queue (-> ${PROBE}) =="
|
||||
probe_out="$(helm template hub "${CHART_DIR}" "${RENDER_FLAGS[@]}" \
|
||||
--set kerberoshub.services.workflows.queue="${PROBE}")"
|
||||
mapfile -t probe_vals < <(printf '%s\n' "${probe_out}" | extract_workflows_queue)
|
||||
assert_all_equal "${PROBE}" "override probe" "${probe_vals[@]}" || exit 1
|
||||
|
||||
echo "All WORKFLOWS_QUEUE consistency checks passed."
|
||||
@@ -1,54 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -lt 3 || $# -gt 4 ]]; then
|
||||
echo "Usage: bash scripts/migrate-http-port.sh NAMESPACE DEPLOYMENT PORT [--apply]" >&2
|
||||
exit 1
|
||||
fi
|
||||
namespace="$1" deployment="$2" port="$3"
|
||||
case "$deployment" in
|
||||
admin|hub-frontend|hub-frontend-demo) ;;
|
||||
*) echo "Unsupported Deployment: $deployment" >&2; exit 1 ;;
|
||||
esac
|
||||
case "$port" in
|
||||
80|8080) ;;
|
||||
*) echo "Expected HTTP port 80 or 8080, got: $port" >&2; exit 1 ;;
|
||||
esac
|
||||
dry_run=(--dry-run=server)
|
||||
if [[ $# -eq 4 ]]; then
|
||||
if [[ "$4" != --apply ]]; then
|
||||
echo "Unknown option: $4" >&2
|
||||
exit 1
|
||||
fi
|
||||
dry_run=()
|
||||
fi
|
||||
|
||||
live="$(kubectl -n "$namespace" get deployment "$deployment" -o json)"
|
||||
patch="$(jq -ce --arg container "$deployment" --argjson port "$port" '
|
||||
. as $deployment |
|
||||
[.spec.template.spec.containers | to_entries[] |
|
||||
select(.value.name == $container)] |
|
||||
if length != 1 then error("Expected exactly one matching container") else .[0] end |
|
||||
.key as $index | .value.ports as $ports |
|
||||
if ($ports | type) != "array" then error("Container ports must be an array") else . end |
|
||||
[$ports[] | select(.name == "http")] as $http |
|
||||
if ($http | length) == 0 then error("No named http port to migrate") else . end |
|
||||
if any($http[]; (.containerPort != 80 and .containerPort != 8080) or
|
||||
((.protocol // "TCP") != "TCP") or has("hostPort") or has("hostIP"))
|
||||
then error("Unsupported http port configuration; migrate manually") else . end |
|
||||
if any($ports[]; .name != "http" and .containerPort == $port and
|
||||
((.protocol // "TCP") == "TCP"))
|
||||
then error("Target port is already used by another named port") else . end |
|
||||
([$ports[] | select(.name != "http")] +
|
||||
[($http[0] + {containerPort: $port})]) as $replacement |
|
||||
[
|
||||
{op: "test", path: "/metadata/resourceVersion", value: $deployment.metadata.resourceVersion},
|
||||
{op: "test", path: ("/spec/template/spec/containers/" + ($index | tostring) + "/name"),
|
||||
value: $container},
|
||||
{op: "replace", path: ("/spec/template/spec/containers/" + ($index | tostring) + "/ports"),
|
||||
value: $replacement}
|
||||
]
|
||||
' <<< "$live")"
|
||||
|
||||
kubectl -n "$namespace" patch deployment "$deployment" --type=json \
|
||||
-p "$patch" "${dry_run[@]}" -o yaml
|
||||
Reference in New Issue
Block a user