legionio 1.4.79 → 1.4.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.dockerignore +11 -0
- data/.github/workflow-templates/eval-gate.yml +118 -0
- data/.github/workflows/ci-cd.yml +40 -0
- data/.github/workflows/ci.yml +25 -0
- data/CHANGELOG.md +52 -0
- data/Dockerfile +26 -8
- data/deploy/helm/legion/Chart.yaml +6 -0
- data/deploy/helm/legion/templates/_helpers.tpl +16 -0
- data/deploy/helm/legion/templates/deployment-api.yaml +55 -0
- data/deploy/helm/legion/templates/deployment-worker.yaml +46 -0
- data/deploy/helm/legion/templates/hpa-worker.yaml +22 -0
- data/deploy/helm/legion/templates/pdb.yaml +13 -0
- data/deploy/helm/legion/templates/service-api.yaml +15 -0
- data/deploy/helm/legion/templates/serviceaccount.yaml +8 -0
- data/deploy/helm/legion/values.yaml +69 -0
- data/lib/legion/api/governance.rb +80 -0
- data/lib/legion/api/org_chart.rb +41 -0
- data/lib/legion/api/workflow.rb +46 -0
- data/lib/legion/api.rb +8 -0
- data/lib/legion/cli/acp_command.rb +46 -0
- data/lib/legion/cli/chat_command.rb +85 -0
- data/lib/legion/cli/check/privacy_check.rb +86 -0
- data/lib/legion/cli/check_command.rb +46 -0
- data/lib/legion/cli/dashboard/renderer.rb +20 -0
- data/lib/legion/cli/eval_command.rb +217 -0
- data/lib/legion/cli/init/config_generator.rb +22 -0
- data/lib/legion/cli/lex_cli_manifest.rb +67 -0
- data/lib/legion/cli/lex_command.rb +101 -0
- data/lib/legion/cli.rb +14 -1
- data/lib/legion/extensions.rb +66 -0
- data/lib/legion/helpers/context.rb +62 -0
- data/lib/legion/service.rb +23 -0
- data/lib/legion/version.rb +1 -1
- data/public/governance/index.html +284 -0
- data/public/workflow/index.html +216 -0
- metadata +23 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 512139af5d950a28790627a762f8dc5f52e37cd093ed2829a9a42aa833ededec
|
|
4
|
+
data.tar.gz: f80a9bfa077caa791bf68058c91eb1061b9cf3851c1766ef5aedd49e9ecda95e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ecd74de131841cb6a00d1fe66ccfa51fe0a0eafe649e0cdf26e90c8602127f0d29d7f55b4cfeda0f13908b0eebe9828d61dc380a7518268e29bda49bd780855d
|
|
7
|
+
data.tar.gz: 9d27ac10782d1e6319e96b517c6da5a819e7b599f15a2e9ae27a5d1f3e33f13d70838bd64390218931ded9be7ec45c3cc28e1e65dc2cedebcbd5faf17fa1ba6a
|
data/.dockerignore
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# .github/workflow-templates/eval-gate.yml
|
|
2
|
+
#
|
|
3
|
+
# Eval gate workflow template for LegionIO CI/CD pipelines.
|
|
4
|
+
# Copy this file to .github/workflows/eval-gate.yml in your repo and adjust the
|
|
5
|
+
# env vars to match your dataset and threshold requirements.
|
|
6
|
+
#
|
|
7
|
+
# Required secrets:
|
|
8
|
+
# LEGIONIO_BOOTSTRAP_CONFIG (base64-encoded bootstrap JSON, or omit for defaults)
|
|
9
|
+
#
|
|
10
|
+
# Usage:
|
|
11
|
+
# - Trigger manually (workflow_dispatch) or on push/PR targeting main
|
|
12
|
+
# - Job exits 0 if avg_score >= threshold, exits 1 and fails the pipeline if below
|
|
13
|
+
|
|
14
|
+
name: Eval Gate
|
|
15
|
+
|
|
16
|
+
on:
|
|
17
|
+
push:
|
|
18
|
+
branches: [main]
|
|
19
|
+
pull_request:
|
|
20
|
+
branches: [main]
|
|
21
|
+
workflow_dispatch:
|
|
22
|
+
inputs:
|
|
23
|
+
dataset:
|
|
24
|
+
description: 'Dataset name to evaluate'
|
|
25
|
+
required: true
|
|
26
|
+
default: 'default'
|
|
27
|
+
threshold:
|
|
28
|
+
description: 'Pass/fail threshold (0.0 - 1.0)'
|
|
29
|
+
required: false
|
|
30
|
+
default: '0.8'
|
|
31
|
+
evaluator:
|
|
32
|
+
description: 'Evaluator name (leave blank for first builtin template)'
|
|
33
|
+
required: false
|
|
34
|
+
default: ''
|
|
35
|
+
|
|
36
|
+
env:
|
|
37
|
+
DATASET: ${{ github.event.inputs.dataset || 'default' }}
|
|
38
|
+
THRESHOLD: ${{ github.event.inputs.threshold || '0.8' }}
|
|
39
|
+
EVALUATOR: ${{ github.event.inputs.evaluator || '' }}
|
|
40
|
+
|
|
41
|
+
jobs:
|
|
42
|
+
eval-gate:
|
|
43
|
+
name: Eval Gate (${{ env.DATASET }} @ ${{ env.THRESHOLD }})
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
|
|
46
|
+
steps:
|
|
47
|
+
- name: Checkout
|
|
48
|
+
uses: actions/checkout@v4
|
|
49
|
+
|
|
50
|
+
- name: Set up Ruby
|
|
51
|
+
uses: ruby/setup-ruby@v1
|
|
52
|
+
with:
|
|
53
|
+
ruby-version: '3.4'
|
|
54
|
+
bundler-cache: true
|
|
55
|
+
|
|
56
|
+
- name: Install Legion
|
|
57
|
+
run: gem install legionio --no-document
|
|
58
|
+
|
|
59
|
+
- name: Bootstrap config (optional)
|
|
60
|
+
if: ${{ secrets.LEGIONIO_BOOTSTRAP_CONFIG != '' }}
|
|
61
|
+
env:
|
|
62
|
+
LEGIONIO_BOOTSTRAP_CONFIG: ${{ secrets.LEGIONIO_BOOTSTRAP_CONFIG }}
|
|
63
|
+
run: echo "Bootstrap config present"
|
|
64
|
+
|
|
65
|
+
- name: Run eval gate
|
|
66
|
+
id: eval
|
|
67
|
+
env:
|
|
68
|
+
LEGIONIO_BOOTSTRAP_CONFIG: ${{ secrets.LEGIONIO_BOOTSTRAP_CONFIG }}
|
|
69
|
+
run: |
|
|
70
|
+
EVAL_ARGS="--dataset $DATASET --threshold $THRESHOLD --exit-code --json"
|
|
71
|
+
if [ -n "$EVALUATOR" ]; then
|
|
72
|
+
EVAL_ARGS="$EVAL_ARGS --evaluator $EVALUATOR"
|
|
73
|
+
fi
|
|
74
|
+
legion eval run $EVAL_ARGS | tee eval-report.json
|
|
75
|
+
|
|
76
|
+
- name: Upload eval report
|
|
77
|
+
if: always()
|
|
78
|
+
uses: actions/upload-artifact@v4
|
|
79
|
+
with:
|
|
80
|
+
name: eval-report-${{ github.run_number }}
|
|
81
|
+
path: eval-report.json
|
|
82
|
+
retention-days: 30
|
|
83
|
+
|
|
84
|
+
- name: Annotate PR with eval results
|
|
85
|
+
if: github.event_name == 'pull_request' && always()
|
|
86
|
+
uses: actions/github-script@v7
|
|
87
|
+
with:
|
|
88
|
+
script: |
|
|
89
|
+
const fs = require('fs');
|
|
90
|
+
let report;
|
|
91
|
+
try {
|
|
92
|
+
report = JSON.parse(fs.readFileSync('eval-report.json', 'utf8'));
|
|
93
|
+
} catch (e) {
|
|
94
|
+
console.log('Could not parse eval report:', e.message);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const gate = report.passed ? 'PASSED' : 'FAILED';
|
|
98
|
+
const score = (report.avg_score || 0).toFixed(3);
|
|
99
|
+
const thresh = report.threshold || 0;
|
|
100
|
+
const body = [
|
|
101
|
+
`## Eval Gate: ${gate}`,
|
|
102
|
+
'',
|
|
103
|
+
`| Metric | Value |`,
|
|
104
|
+
`|--------|-------|`,
|
|
105
|
+
`| Dataset | \`${report.dataset}\` |`,
|
|
106
|
+
`| Evaluator | \`${report.evaluator}\` |`,
|
|
107
|
+
`| Avg Score | ${score} |`,
|
|
108
|
+
`| Threshold | ${thresh} |`,
|
|
109
|
+
`| Total Rows | ${report.summary?.total ?? 'N/A'} |`,
|
|
110
|
+
`| Passed | ${report.summary?.passed ?? 'N/A'} |`,
|
|
111
|
+
`| Failed | ${report.summary?.failed ?? 'N/A'} |`,
|
|
112
|
+
].join('\n');
|
|
113
|
+
github.rest.issues.createComment({
|
|
114
|
+
issue_number: context.issue.number,
|
|
115
|
+
owner: context.repo.owner,
|
|
116
|
+
repo: context.repo.repo,
|
|
117
|
+
body: body,
|
|
118
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
name: CI/CD
|
|
2
|
+
on:
|
|
3
|
+
pull_request:
|
|
4
|
+
branches: [main]
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
test:
|
|
8
|
+
name: Test
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
services:
|
|
11
|
+
rabbitmq:
|
|
12
|
+
image: rabbitmq:3.13-management
|
|
13
|
+
ports: ['5672:5672']
|
|
14
|
+
postgres:
|
|
15
|
+
image: postgres:16
|
|
16
|
+
env:
|
|
17
|
+
POSTGRES_PASSWORD: test
|
|
18
|
+
POSTGRES_DB: legion_test
|
|
19
|
+
ports: ['5432:5432']
|
|
20
|
+
options: >-
|
|
21
|
+
--health-cmd pg_isready
|
|
22
|
+
--health-interval 10s
|
|
23
|
+
--health-timeout 5s
|
|
24
|
+
--health-retries 5
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
- uses: ruby/setup-ruby@v1
|
|
28
|
+
with:
|
|
29
|
+
ruby-version: '3.4'
|
|
30
|
+
bundler-cache: true
|
|
31
|
+
- run: bundle install && bundle exec rspec
|
|
32
|
+
- run: bundle exec rubocop
|
|
33
|
+
|
|
34
|
+
helm-lint:
|
|
35
|
+
name: Helm Lint
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/checkout@v4
|
|
39
|
+
- uses: azure/setup-helm@v3
|
|
40
|
+
- run: helm lint deploy/helm/legion
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -27,3 +27,28 @@ jobs:
|
|
|
27
27
|
gh api repos/LegionIO/homebrew-tap/dispatches \
|
|
28
28
|
-f event_type=build-daemon \
|
|
29
29
|
-f "client_payload[legionio_version]=${{ needs.release.outputs.version }}"
|
|
30
|
+
|
|
31
|
+
docker-build:
|
|
32
|
+
name: Build Docker Image
|
|
33
|
+
needs: release
|
|
34
|
+
if: needs.release.outputs.changed == 'true'
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
permissions:
|
|
37
|
+
packages: write
|
|
38
|
+
steps:
|
|
39
|
+
- uses: actions/checkout@v4
|
|
40
|
+
- uses: docker/setup-buildx-action@v3
|
|
41
|
+
- uses: docker/login-action@v3
|
|
42
|
+
with:
|
|
43
|
+
registry: ghcr.io
|
|
44
|
+
username: ${{ github.actor }}
|
|
45
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
46
|
+
- uses: docker/build-push-action@v5
|
|
47
|
+
with:
|
|
48
|
+
context: .
|
|
49
|
+
push: true
|
|
50
|
+
tags: |
|
|
51
|
+
ghcr.io/legionio/legion:${{ needs.release.outputs.version }}
|
|
52
|
+
ghcr.io/legionio/legion:latest
|
|
53
|
+
cache-from: type=gha
|
|
54
|
+
cache-to: type=gha,mode=max
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,57 @@
|
|
|
1
1
|
# Legion Changelog
|
|
2
2
|
|
|
3
|
+
## [1.4.85] - 2026-03-20
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `legion lex fixes` CLI command to list pending auto-fix patches (filterable by status)
|
|
7
|
+
- `legion lex approve-fix FIX_ID` CLI command to approve LLM-generated fixes
|
|
8
|
+
- `legion lex reject-fix FIX_ID` CLI command to reject LLM-generated fixes
|
|
9
|
+
- `with_data` helper to `legion lex` subcommand class for data-required operations
|
|
10
|
+
|
|
11
|
+
## [1.4.84] - 2026-03-20
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
- `Legion::Extensions.load_yaml_agents` — loads YAML/JSON agent definitions from `~/.legionio/agents/` or configured directory
|
|
15
|
+
- `generate_yaml_runner` — dynamically generates a runner Module for each agent with `llm`, `script`, and `http` function types
|
|
16
|
+
- YAML agent loading integrated into `hook_extensions` boot sequence
|
|
17
|
+
- Governance API routes under `/api/governance/approvals` (list, show, submit, approve, reject)
|
|
18
|
+
- HTML governance dashboard at `/governance/` with approve/reject buttons, 30s auto-poll, and reviewer dialog
|
|
19
|
+
- Static file serving enabled for `public/` directory in Sinatra
|
|
20
|
+
|
|
21
|
+
## [1.4.83] - 2026-03-20
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
- `Helpers::Context` for filesystem-based inter-agent context sharing
|
|
25
|
+
- Org chart API endpoint (`GET /api/org-chart`) with dashboard panel
|
|
26
|
+
- Workflow relationship graph API (`GET /api/relationships/graph`)
|
|
27
|
+
- Workflow visualizer web page (`public/workflow/`) with Cytoscape.js
|
|
28
|
+
- `--worktree` flag for `legion chat` with auto-checkpointing
|
|
29
|
+
- `.legion-context/` and `.legion-worktrees/` in generated `.gitignore`
|
|
30
|
+
|
|
31
|
+
## [1.4.82] - 2026-03-20
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
- `legion check --privacy` command: verifies enterprise privacy mode (flag set, no cloud API keys, external endpoints unreachable)
|
|
35
|
+
- `PrivacyCheck` class with three probes: flag_set, no_cloud_keys, no_external_endpoints
|
|
36
|
+
- `Legion::Service.log_privacy_mode_status` logs enterprise privacy state at startup
|
|
37
|
+
|
|
38
|
+
## [1.4.81] - 2026-03-20
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
- `legion eval experiments` subcommand: list all experiment runs with status and summary
|
|
42
|
+
- `legion eval promote --experiment NAME --tag TAG` subcommand: tag a prompt version for production via lex-prompt
|
|
43
|
+
- `legion eval compare --run1 NAME --run2 NAME` subcommand: side-by-side diff of two experiment runs
|
|
44
|
+
- `require_prompt!` guard for lex-prompt extension availability
|
|
45
|
+
|
|
46
|
+
## [1.4.80] - 2026-03-20
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
- `legion eval run` CLI subcommand for CI/CD threshold-based eval gating
|
|
50
|
+
- `--dataset`, `--threshold`, `--evaluator`, `--exit-code` options on `eval run`
|
|
51
|
+
- JSON report output to stdout with per-row scores, summary, and timestamp
|
|
52
|
+
- `.github/workflow-templates/eval-gate.yml` reusable GitHub Actions workflow template
|
|
53
|
+
- PR annotation step in workflow template for inline eval result comments
|
|
54
|
+
|
|
3
55
|
## [1.4.79] - 2026-03-20
|
|
4
56
|
|
|
5
57
|
### Added
|
data/Dockerfile
CHANGED
|
@@ -1,9 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
# Build stage
|
|
2
|
+
FROM ruby:3.4-slim AS builder
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
RUN apt-get update && \
|
|
5
|
+
apt-get install -y --no-install-recommends build-essential libpq-dev git && \
|
|
6
|
+
rm -rf /var/lib/apt/lists/*
|
|
7
|
+
COPY Gemfile legionio.gemspec ./
|
|
8
|
+
COPY lib/legion/version.rb lib/legion/
|
|
9
|
+
RUN bundle lock && \
|
|
10
|
+
bundle config set --local without 'development test' && \
|
|
11
|
+
bundle install --jobs 4 --retry 3
|
|
12
|
+
COPY . .
|
|
3
13
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
14
|
+
# Runtime stage
|
|
15
|
+
FROM ruby:3.4-slim AS runtime
|
|
16
|
+
RUN apt-get update && \
|
|
17
|
+
apt-get install -y --no-install-recommends libpq5 curl && \
|
|
18
|
+
rm -rf /var/lib/apt/lists/* && \
|
|
19
|
+
groupadd -r legion && useradd -r -g legion -d /app -s /sbin/nologin legion
|
|
20
|
+
WORKDIR /app
|
|
21
|
+
COPY --from=builder --chown=legion:legion /app /app
|
|
22
|
+
USER legion
|
|
23
|
+
EXPOSE 4567
|
|
24
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
25
|
+
CMD curl -sf http://localhost:4567/api/health || exit 1
|
|
26
|
+
ENTRYPOINT ["bundle", "exec"]
|
|
27
|
+
CMD ["legion", "start"]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{{- define "legion.fullname" -}}
|
|
2
|
+
{{- .Release.Name }}-legion
|
|
3
|
+
{{- end }}
|
|
4
|
+
|
|
5
|
+
{{- define "legion.labels" -}}
|
|
6
|
+
app.kubernetes.io/name: legion
|
|
7
|
+
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
8
|
+
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
|
9
|
+
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
|
10
|
+
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
|
11
|
+
{{- end }}
|
|
12
|
+
|
|
13
|
+
{{- define "legion.selectorLabels" -}}
|
|
14
|
+
app.kubernetes.io/name: legion
|
|
15
|
+
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
16
|
+
{{- end }}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
apiVersion: apps/v1
|
|
2
|
+
kind: Deployment
|
|
3
|
+
metadata:
|
|
4
|
+
name: {{ include "legion.fullname" . }}-api
|
|
5
|
+
labels:
|
|
6
|
+
{{- include "legion.labels" . | nindent 4 }}
|
|
7
|
+
app.kubernetes.io/component: api
|
|
8
|
+
spec:
|
|
9
|
+
replicas: {{ .Values.api.replicas }}
|
|
10
|
+
selector:
|
|
11
|
+
matchLabels:
|
|
12
|
+
{{- include "legion.selectorLabels" . | nindent 6 }}
|
|
13
|
+
app.kubernetes.io/component: api
|
|
14
|
+
template:
|
|
15
|
+
metadata:
|
|
16
|
+
labels:
|
|
17
|
+
{{- include "legion.selectorLabels" . | nindent 8 }}
|
|
18
|
+
app.kubernetes.io/component: api
|
|
19
|
+
spec:
|
|
20
|
+
serviceAccountName: {{ .Values.serviceAccount.name }}
|
|
21
|
+
containers:
|
|
22
|
+
- name: api
|
|
23
|
+
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
|
24
|
+
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
|
25
|
+
command: ["bundle", "exec", "legion", "api"]
|
|
26
|
+
ports:
|
|
27
|
+
- containerPort: {{ .Values.api.port }}
|
|
28
|
+
protocol: TCP
|
|
29
|
+
livenessProbe:
|
|
30
|
+
httpGet:
|
|
31
|
+
path: /api/health
|
|
32
|
+
port: {{ .Values.api.port }}
|
|
33
|
+
initialDelaySeconds: 10
|
|
34
|
+
periodSeconds: 30
|
|
35
|
+
readinessProbe:
|
|
36
|
+
httpGet:
|
|
37
|
+
path: /api/health
|
|
38
|
+
port: {{ .Values.api.port }}
|
|
39
|
+
initialDelaySeconds: 5
|
|
40
|
+
periodSeconds: 10
|
|
41
|
+
resources:
|
|
42
|
+
{{- toYaml .Values.api.resources | nindent 12 }}
|
|
43
|
+
env:
|
|
44
|
+
- name: LEGION_TRANSPORT_HOST
|
|
45
|
+
value: {{ .Values.rabbitmq.host | quote }}
|
|
46
|
+
- name: LEGION_DATA_URL
|
|
47
|
+
value: "postgres://$(DB_USER):$(DB_PASS)@{{ .Values.postgresql.host }}:{{ .Values.postgresql.port }}/{{ .Values.postgresql.database }}"
|
|
48
|
+
{{- with .Values.api.env }}
|
|
49
|
+
{{- toYaml . | nindent 12 }}
|
|
50
|
+
{{- end }}
|
|
51
|
+
envFrom:
|
|
52
|
+
- secretRef:
|
|
53
|
+
name: {{ .Values.rabbitmq.existingSecret }}
|
|
54
|
+
- secretRef:
|
|
55
|
+
name: {{ .Values.postgresql.existingSecret }}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
apiVersion: apps/v1
|
|
2
|
+
kind: Deployment
|
|
3
|
+
metadata:
|
|
4
|
+
name: {{ include "legion.fullname" . }}-worker
|
|
5
|
+
labels:
|
|
6
|
+
{{- include "legion.labels" . | nindent 4 }}
|
|
7
|
+
app.kubernetes.io/component: worker
|
|
8
|
+
spec:
|
|
9
|
+
replicas: {{ .Values.worker.replicas }}
|
|
10
|
+
selector:
|
|
11
|
+
matchLabels:
|
|
12
|
+
{{- include "legion.selectorLabels" . | nindent 6 }}
|
|
13
|
+
app.kubernetes.io/component: worker
|
|
14
|
+
template:
|
|
15
|
+
metadata:
|
|
16
|
+
labels:
|
|
17
|
+
{{- include "legion.selectorLabels" . | nindent 8 }}
|
|
18
|
+
app.kubernetes.io/component: worker
|
|
19
|
+
spec:
|
|
20
|
+
serviceAccountName: {{ .Values.serviceAccount.name }}
|
|
21
|
+
containers:
|
|
22
|
+
- name: worker
|
|
23
|
+
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
|
24
|
+
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
|
25
|
+
command: ["bundle", "exec", "legion", "start"]
|
|
26
|
+
resources:
|
|
27
|
+
{{- toYaml .Values.worker.resources | nindent 12 }}
|
|
28
|
+
env:
|
|
29
|
+
- name: LEGION_TRANSPORT_HOST
|
|
30
|
+
value: {{ .Values.rabbitmq.host | quote }}
|
|
31
|
+
- name: LEGION_TRANSPORT_PORT
|
|
32
|
+
value: {{ .Values.rabbitmq.port | quote }}
|
|
33
|
+
- name: LEGION_DATA_URL
|
|
34
|
+
value: "postgres://$(DB_USER):$(DB_PASS)@{{ .Values.postgresql.host }}:{{ .Values.postgresql.port }}/{{ .Values.postgresql.database }}"
|
|
35
|
+
{{- with .Values.worker.env }}
|
|
36
|
+
{{- toYaml . | nindent 12 }}
|
|
37
|
+
{{- end }}
|
|
38
|
+
envFrom:
|
|
39
|
+
- secretRef:
|
|
40
|
+
name: {{ .Values.rabbitmq.existingSecret }}
|
|
41
|
+
- secretRef:
|
|
42
|
+
name: {{ .Values.postgresql.existingSecret }}
|
|
43
|
+
{{- with .Values.nodeSelector }}
|
|
44
|
+
nodeSelector:
|
|
45
|
+
{{- toYaml . | nindent 8 }}
|
|
46
|
+
{{- end }}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{{- if .Values.worker.hpa.enabled }}
|
|
2
|
+
apiVersion: autoscaling/v2
|
|
3
|
+
kind: HorizontalPodAutoscaler
|
|
4
|
+
metadata:
|
|
5
|
+
name: {{ include "legion.fullname" . }}-worker
|
|
6
|
+
labels:
|
|
7
|
+
{{- include "legion.labels" . | nindent 4 }}
|
|
8
|
+
spec:
|
|
9
|
+
scaleTargetRef:
|
|
10
|
+
apiVersion: apps/v1
|
|
11
|
+
kind: Deployment
|
|
12
|
+
name: {{ include "legion.fullname" . }}-worker
|
|
13
|
+
minReplicas: {{ .Values.worker.hpa.minReplicas }}
|
|
14
|
+
maxReplicas: {{ .Values.worker.hpa.maxReplicas }}
|
|
15
|
+
metrics:
|
|
16
|
+
- type: Resource
|
|
17
|
+
resource:
|
|
18
|
+
name: cpu
|
|
19
|
+
target:
|
|
20
|
+
type: Utilization
|
|
21
|
+
averageUtilization: {{ .Values.worker.hpa.targetCPUUtilization }}
|
|
22
|
+
{{- end }}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{{- if .Values.podDisruptionBudget.enabled }}
|
|
2
|
+
apiVersion: policy/v1
|
|
3
|
+
kind: PodDisruptionBudget
|
|
4
|
+
metadata:
|
|
5
|
+
name: {{ include "legion.fullname" . }}
|
|
6
|
+
labels:
|
|
7
|
+
{{- include "legion.labels" . | nindent 4 }}
|
|
8
|
+
spec:
|
|
9
|
+
minAvailable: {{ .Values.podDisruptionBudget.minAvailable }}
|
|
10
|
+
selector:
|
|
11
|
+
matchLabels:
|
|
12
|
+
{{- include "legion.selectorLabels" . | nindent 6 }}
|
|
13
|
+
{{- end }}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
apiVersion: v1
|
|
2
|
+
kind: Service
|
|
3
|
+
metadata:
|
|
4
|
+
name: {{ include "legion.fullname" . }}-api
|
|
5
|
+
labels:
|
|
6
|
+
{{- include "legion.labels" . | nindent 4 }}
|
|
7
|
+
spec:
|
|
8
|
+
type: {{ .Values.api.service.type }}
|
|
9
|
+
ports:
|
|
10
|
+
- port: {{ .Values.api.port }}
|
|
11
|
+
targetPort: {{ .Values.api.port }}
|
|
12
|
+
protocol: TCP
|
|
13
|
+
selector:
|
|
14
|
+
{{- include "legion.selectorLabels" . | nindent 4 }}
|
|
15
|
+
app.kubernetes.io/component: api
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
image:
|
|
2
|
+
repository: ghcr.io/legionio/legion
|
|
3
|
+
tag: latest
|
|
4
|
+
pullPolicy: IfNotPresent
|
|
5
|
+
|
|
6
|
+
worker:
|
|
7
|
+
replicas: 2
|
|
8
|
+
resources:
|
|
9
|
+
requests:
|
|
10
|
+
cpu: 250m
|
|
11
|
+
memory: 256Mi
|
|
12
|
+
limits:
|
|
13
|
+
cpu: 1000m
|
|
14
|
+
memory: 512Mi
|
|
15
|
+
hpa:
|
|
16
|
+
enabled: false
|
|
17
|
+
minReplicas: 2
|
|
18
|
+
maxReplicas: 20
|
|
19
|
+
targetCPUUtilization: 70
|
|
20
|
+
env: []
|
|
21
|
+
|
|
22
|
+
api:
|
|
23
|
+
replicas: 2
|
|
24
|
+
port: 4567
|
|
25
|
+
resources:
|
|
26
|
+
requests:
|
|
27
|
+
cpu: 100m
|
|
28
|
+
memory: 128Mi
|
|
29
|
+
limits:
|
|
30
|
+
cpu: 500m
|
|
31
|
+
memory: 256Mi
|
|
32
|
+
service:
|
|
33
|
+
type: ClusterIP
|
|
34
|
+
ingress:
|
|
35
|
+
enabled: false
|
|
36
|
+
env: []
|
|
37
|
+
|
|
38
|
+
rabbitmq:
|
|
39
|
+
host: rabbitmq
|
|
40
|
+
port: 5672
|
|
41
|
+
vhost: /
|
|
42
|
+
existingSecret: legion-rabbitmq
|
|
43
|
+
|
|
44
|
+
postgresql:
|
|
45
|
+
host: postgresql
|
|
46
|
+
port: 5432
|
|
47
|
+
database: legion
|
|
48
|
+
existingSecret: legion-postgresql
|
|
49
|
+
|
|
50
|
+
redis:
|
|
51
|
+
host: redis
|
|
52
|
+
port: 6379
|
|
53
|
+
|
|
54
|
+
vault:
|
|
55
|
+
enabled: false
|
|
56
|
+
address: ""
|
|
57
|
+
role: legion
|
|
58
|
+
|
|
59
|
+
serviceAccount:
|
|
60
|
+
create: true
|
|
61
|
+
name: legion
|
|
62
|
+
|
|
63
|
+
podDisruptionBudget:
|
|
64
|
+
enabled: true
|
|
65
|
+
minAvailable: 1
|
|
66
|
+
|
|
67
|
+
nodeSelector: {}
|
|
68
|
+
tolerations: []
|
|
69
|
+
affinity: {}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
class API < Sinatra::Base
|
|
5
|
+
module Routes
|
|
6
|
+
module Governance
|
|
7
|
+
def self.registered(app)
|
|
8
|
+
app.helpers GovernanceHelpers
|
|
9
|
+
register_approvals(app)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
module GovernanceHelpers
|
|
13
|
+
def run_governance_runner(method, **)
|
|
14
|
+
require 'legion/extensions/audit/runners/approval_queue'
|
|
15
|
+
runner = Object.new.extend(Legion::Extensions::Audit::Runners::ApprovalQueue)
|
|
16
|
+
runner.send(method, **)
|
|
17
|
+
rescue LoadError
|
|
18
|
+
halt 503, json_error('service_unavailable', 'lex-audit not available', status_code: 503)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.register_approvals(app)
|
|
23
|
+
app.get '/api/governance/approvals' do
|
|
24
|
+
require_data!
|
|
25
|
+
result = run_governance_runner(:list_pending,
|
|
26
|
+
tenant_id: params[:tenant_id],
|
|
27
|
+
limit: (params[:limit] || 50).to_i)
|
|
28
|
+
json_response(result)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
app.get '/api/governance/approvals/:id' do
|
|
32
|
+
require_data!
|
|
33
|
+
result = run_governance_runner(:show_approval, id: params[:id].to_i)
|
|
34
|
+
if result[:success]
|
|
35
|
+
json_response(result)
|
|
36
|
+
else
|
|
37
|
+
halt 404, json_error('not_found', 'Approval not found', status_code: 404)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
app.post '/api/governance/approvals' do
|
|
42
|
+
require_data!
|
|
43
|
+
body = parse_request_body
|
|
44
|
+
halt 422, json_error('missing_field', 'approval_type is required', status_code: 422) unless body[:approval_type]
|
|
45
|
+
halt 422, json_error('missing_field', 'requester_id is required', status_code: 422) unless body[:requester_id]
|
|
46
|
+
|
|
47
|
+
result = run_governance_runner(:submit,
|
|
48
|
+
approval_type: body[:approval_type],
|
|
49
|
+
payload: body[:payload] || {},
|
|
50
|
+
requester_id: body[:requester_id],
|
|
51
|
+
tenant_id: body[:tenant_id])
|
|
52
|
+
json_response(result, status_code: 201)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
app.put '/api/governance/approvals/:id/approve' do
|
|
56
|
+
require_data!
|
|
57
|
+
body = parse_request_body
|
|
58
|
+
halt 422, json_error('missing_field', 'reviewer_id is required', status_code: 422) unless body[:reviewer_id]
|
|
59
|
+
|
|
60
|
+
result = run_governance_runner(:approve,
|
|
61
|
+
id: params[:id].to_i,
|
|
62
|
+
reviewer_id: body[:reviewer_id])
|
|
63
|
+
json_response(result)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
app.put '/api/governance/approvals/:id/reject' do
|
|
67
|
+
require_data!
|
|
68
|
+
body = parse_request_body
|
|
69
|
+
halt 422, json_error('missing_field', 'reviewer_id is required', status_code: 422) unless body[:reviewer_id]
|
|
70
|
+
|
|
71
|
+
result = run_governance_runner(:reject,
|
|
72
|
+
id: params[:id].to_i,
|
|
73
|
+
reviewer_id: body[:reviewer_id])
|
|
74
|
+
json_response(result)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|