Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.git
.github
.cursor
.vscode
bin
dist
demo
testdata
*.md
*.log
*.tmp
*.out
coverage.out
deploy/nginx.conf
123 changes: 123 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,126 @@ jobs:
- name: Validate rules
run: ./bin/metrics-analyzer validate ./automated-rules

container-smoke:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4

- name: Read version
id: version
run: echo "version=$(cat VERSION)" >> "$GITHUB_OUTPUT"

- name: Set build time
id: build_time
run: echo "build_time=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT"

- name: Build image
run: |
docker build \
--build-arg VERSION="${{ steps.version.outputs.version }}" \
--build-arg BUILD_TIME="${{ steps.build_time.outputs.build_time }}" \
-t sma:ci .

- name: Start container
run: |
docker run -d --rm --name sma-ci -p 8080:8080 sma:ci

- name: Wait for readiness
run: |
for i in {1..10}; do
if curl -fsS http://localhost:8080/health >/dev/null; then
exit 0
fi
sleep 1
done
docker logs sma-ci
exit 1

- name: Check version endpoint
run: |
for i in {1..5}; do
if curl -fsS http://localhost:8080/version -o /tmp/version.json; then
if [ -s /tmp/version.json ]; then
break
fi
fi
sleep 1
done
if [ ! -s /tmp/version.json ]; then
echo "version response was empty"
cat /tmp/version.json || true
docker logs sma-ci || true
exit 1
fi
python3 - <<'PY'
import json
with open("/tmp/version.json") as f:
data = json.load(f)
assert "version" in data
assert "lastUpdate" in data
print("version ok")
PY

- name: Analyze sample metrics
run: |
curl -fsS -o /tmp/analysis.json -X POST \
-F "file=@testdata/fixtures/sample_metrics.txt" \
http://localhost:8080/api/analyze/both
python3 - <<'PY'
import json
with open("/tmp/analysis.json") as f:
data = json.load(f)
assert "console" in data
assert "markdown" in data
print("analysis ok")
PY

- name: Show container logs on failure
if: failure()
run: docker logs sma-ci

container-image:
runs-on: ubuntu-latest
needs: build
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')
env:
QUAY_USERNAME: ${{ secrets.QUAY_USERNAME }}
QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }}
steps:
- uses: actions/checkout@v4

- name: Read version
id: version
run: echo "version=$(cat VERSION)" >> "$GITHUB_OUTPUT"

- name: Set build time
id: build_time
run: echo "build_time=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT"

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Quay
id: login
if: ${{ env.QUAY_USERNAME != '' && env.QUAY_PASSWORD != '' }}
uses: docker/login-action@v3
with:
registry: quay.io
username: ${{ env.QUAY_USERNAME }}
password: ${{ env.QUAY_PASSWORD }}

- name: Build and push image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: ${{ steps.login.conclusion == 'success' }}
tags: |
quay.io/prygiels/sma:${{ steps.version.outputs.version }}
quay.io/prygiels/sma:${{ github.sha }}
quay.io/prygiels/sma:latest
build-args: |
VERSION=${{ steps.version.outputs.version }}
BUILD_TIME=${{ steps.build_time.outputs.build_time }}

40 changes: 40 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
FROM golang:1.25-alpine AS builder

WORKDIR /src

RUN apk add --no-cache git

COPY go.mod go.sum ./
RUN go mod download

COPY . .

ARG VERSION=dev
ARG BUILD_TIME=""

RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -ldflags "-X main.buildVersion=${VERSION} -X main.buildTime=${BUILD_TIME}" \
-o /out/web-server ./web/server

FROM alpine:3.19

WORKDIR /app

RUN apk add --no-cache ca-certificates nginx
RUN adduser -D -H -u 10001 appuser

COPY --from=builder /out/web-server /app/web-server
COPY web/static /app/web/static
COPY automated-rules /app/automated-rules
COPY templates/markdown.tmpl /app/templates/markdown.tmpl
COPY deploy/nginx.container.conf /etc/nginx/nginx.conf
COPY deploy/container-entrypoint.sh /app/entrypoint.sh

RUN chmod +x /app/entrypoint.sh \
&& chown -R 10001:10001 /app /etc/nginx

EXPOSE 8080

USER 10001

ENTRYPOINT ["/app/entrypoint.sh"]
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.0.3
0.0.4
6 changes: 6 additions & 0 deletions charts/sensor-metrics-analyzer/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
apiVersion: v2
name: sma
description: Sensor Metrics Analyzer web server
type: application
version: 0.1.0
appVersion: "0.0.3"
19 changes: 19 additions & 0 deletions charts/sensor-metrics-analyzer/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{{- define "sensor-metrics-analyzer.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{- define "sensor-metrics-analyzer.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s" (include "sensor-metrics-analyzer.name" .) | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}

{{- define "sensor-metrics-analyzer.labels" -}}
app.kubernetes.io/name: {{ include "sensor-metrics-analyzer.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | quote }}
{{- end -}}
69 changes: 69 additions & 0 deletions charts/sensor-metrics-analyzer/templates/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "sensor-metrics-analyzer.fullname" . }}
labels:
{{- include "sensor-metrics-analyzer.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "sensor-metrics-analyzer.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "sensor-metrics-analyzer.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
spec:
containers:
- name: app
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: LISTEN_ADDR
value: ":8081"
- name: RULES_DIR
value: {{ .Values.config.rulesDir | quote }}
- name: LOAD_LEVEL_DIR
value: {{ .Values.config.loadLevelDir | quote }}
- name: TEMPLATE_PATH
value: {{ .Values.config.templatePath | quote }}
- name: MAX_FILE_SIZE
value: {{ .Values.config.maxFileSize | quote }}
- name: REQUEST_TIMEOUT
value: {{ .Values.config.requestTimeout | quote }}
ports:
- name: http
containerPort: 8080
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
resources:
{{- toYaml .Values.resources | nindent 12 }}
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
seccompProfile:
type: RuntimeDefault
capabilities:
drop:
- ALL
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
33 changes: 33 additions & 0 deletions charts/sensor-metrics-analyzer/templates/ingress.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "sensor-metrics-analyzer.fullname" . }}
labels:
{{- include "sensor-metrics-analyzer.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
ingressClassName: {{ .Values.ingress.className }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service:
name: {{ include "sensor-metrics-analyzer.fullname" $ }}
port:
number: {{ $.Values.service.port }}
{{- end }}
{{- end }}
{{- with .Values.ingress.tls }}
tls:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
15 changes: 15 additions & 0 deletions charts/sensor-metrics-analyzer/templates/service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "sensor-metrics-analyzer.fullname" . }}
labels:
{{- include "sensor-metrics-analyzer.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
selector:
app.kubernetes.io/name: {{ include "sensor-metrics-analyzer.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
ports:
- name: http
port: {{ .Values.service.port }}
targetPort: 8080
36 changes: 36 additions & 0 deletions charts/sensor-metrics-analyzer/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
replicaCount: 2

image:
repository: quay.io/prygiels/sma
tag: "0.0.3"
pullPolicy: IfNotPresent

service:
type: ClusterIP
port: 80

ingress:
enabled: true
className: nginx
annotations: {}
hosts:
- host: sensor-metrics.rhacs.io
paths:
- path: /
pathType: Prefix
tls: []

config:
rulesDir: /app/automated-rules
loadLevelDir: /app/automated-rules/load-level
templatePath: /app/templates/markdown.tmpl
maxFileSize: "52428800"
requestTimeout: "60s"

resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 256Mi
27 changes: 27 additions & 0 deletions deploy/container-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/sh
set -e

if [ -d /dev/shm ]; then
export TMPDIR=/dev/shm
mkdir -p \
/dev/shm/nginx/client_body \
/dev/shm/nginx/proxy \
/dev/shm/nginx/fastcgi \
/dev/shm/nginx/uwsgi \
/dev/shm/nginx/scgi
else
mkdir -p \
/tmp/nginx/client_body \
/tmp/nginx/proxy \
/tmp/nginx/fastcgi \
/tmp/nginx/uwsgi \
/tmp/nginx/scgi
fi

if [ -z "${LISTEN_ADDR:-}" ]; then
export LISTEN_ADDR=":8081"
fi

/app/web-server &

exec nginx -e /dev/stderr -g 'daemon off;' -c /etc/nginx/nginx.conf
File renamed without changes.
Loading
Loading