Lore revision control server is deploying.

{{- $svc := include "lore.fullname" . }}
{{- $ns := .Release.Namespace }}
{{- /*
  Only advertise lores:// when grpcTls is really on. tls.mode alone puts a cert on
  QUIC, not gRPC. Use the shared helper; do not repeat the check here.
*/}}
{{- $scheme := ternary "lores" "lore" (eq (include "lore.grpcTlsActive" .) "true") }}

1. Connect to the server
{{- if eq .Values.service.type "NodePort" }}

   Exposed as a NodePort service. QUIC is UDP on a node port.
{{- if eq .Values.service.externalTrafficPolicy "Local" }}

   service.externalTrafficPolicy is Local (the default). Only a node running a
   serving pod accepts traffic on this port. Any other node drops it. Find that
   node from the Service's EndpointSlice. Wait for the pod first, or there is no
   endpoint to find.

     kubectl rollout status statefulset/{{ $svc }} -n {{ $ns }} --timeout=5m

     NODE_NAME=$(kubectl get endpointslice -n {{ $ns }} -l kubernetes.io/service-name={{ $svc }} -o jsonpath='{.items[0].endpoints[0].nodeName}')
     if [ -z "$NODE_NAME" ]; then
       echo "No ready endpoint yet. Wait for the pod to become Ready, then re-run."
     else
       NODE_IP=$(kubectl get node "$NODE_NAME" -o jsonpath='{.status.addresses[?(@.type=="ExternalIP")].address}')
       [ -z "$NODE_IP" ] && NODE_IP=$(kubectl get node "$NODE_NAME" -o jsonpath='{.status.addresses[?(@.type=="InternalIP")].address}')
       QUIC_PORT=$(kubectl get svc {{ $svc }} -n {{ $ns }} -o jsonpath='{.spec.ports[?(@.name=="quic")].nodePort}')
       echo "{{ $scheme }}://$NODE_IP:$QUIC_PORT"
     fi

   The pod can move to another node and change this address. Re-run the lookup if
   connections start failing.

   Set service.externalTrafficPolicy=Cluster to accept traffic on any node. That
   costs an extra hop and the client's real source IP.
{{- else }}

   Get the assigned port and any node's address, then build the URL:

     NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="ExternalIP")].address}')
     [ -z "$NODE_IP" ] && NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
     QUIC_PORT=$(kubectl get svc {{ $svc }} -n {{ $ns }} -o jsonpath='{.spec.ports[?(@.name=="quic")].nodePort}')
     echo "{{ $scheme }}://$NODE_IP:$QUIC_PORT"
{{- end }}
{{- if .Values.service.nodePorts.quic }}

   This release pins the QUIC node port to {{ .Values.service.nodePorts.quic }}:
     {{ $scheme }}://<NODE-IP>:{{ .Values.service.nodePorts.quic }}
{{- end }}

   The node's firewall/security group must allow inbound UDP on that node port.
{{- else if eq .Values.service.type "LoadBalancer" }}

   Exposed as a LoadBalancer. Get its external address:

     kubectl get svc {{ $svc }} -n {{ $ns }} -o jsonpath='{.status.loadBalancer.ingress[0].hostname}{.status.loadBalancer.ingress[0].ip}'

   Watch until it is assigned:
     kubectl get svc {{ $svc }} -n {{ $ns }} -w

   Connect with: {{ $scheme }}://<EXTERNAL-ADDRESS>:{{ .Values.service.quicPort }}
{{- else }}

   service.type is {{ .Values.service.type }} (in-cluster only). Reach it from
   inside the cluster at:
     {{ $scheme }}://{{ $svc }}.{{ $ns }}.svc.cluster.local:{{ .Values.service.quicPort }}
{{- end }}

2. Install the lore client

   Download from: https://github.com/EpicGames/lore/releases
   Verify install: lore --version

3. TLS
{{- if or (eq .Values.tls.mode "ephemeral") (eq .Values.tls.mode "selfSigned") }}

   tls.mode={{ .Values.tls.mode }} — the cert is self-signed and untrusted.
{{- if eq .Values.tls.mode "ephemeral" }}
   It is generated by the server and regenerated on every restart. For local/dev
   use only; use selfSigned or certManager for anything shared.
{{- end }}
{{- if not .Values.tls.grpcTls }}
   Use the lore:// scheme (skips certificate verification).
{{- end }}
{{- end }}
{{- if and (ne .Values.tls.mode "ephemeral") (not .Values.tls.grpcTls) }}

   tls.grpcTls=false, so the gRPC listener is plaintext and the lores:// scheme will
   NOT connect (transport error). Clients must use lore://. QUIC traffic is still
   encrypted with the configured cert; only certificate verification is skipped.
   To enable lores://, set tls.grpcTls=true — see the chart README.
{{- else if .Values.tls.grpcTls }}

   tls.grpcTls=true — gRPC is served over TLS, so clients MUST use lores:// and must
   trust the certificate. Plaintext lore:// clients can no longer connect.
   Add the address clients dial to tls.extraSANs (and helm upgrade) so the cert matches.
{{- if or (eq .Values.tls.mode "ephemeral") (eq .Values.tls.mode "selfSigned") }}

   WARNING: tls.mode={{ .Values.tls.mode }} produces an UNTRUSTED certificate, so
   lores:// will fail for clients that have not installed it as a trusted root.
   Use tls.mode=certManager with a real CA Issuer, or tls.mode=secret.
{{- end }}
{{- end }}

4. Quick start (use the URL from step 1 as <SERVER>)

   lore repository create {{ $scheme }}://<SERVER>/myrepo
   lore clone           {{ $scheme }}://<SERVER>/myrepo
{{- if .Values.auth.enabled }}

   Auth is enabled — log in first:
   lore login {{ $scheme }}://<SERVER>
{{- end }}

{{- if eq .Values.service.type "ClusterIP" }}

WARNING: service.type is ClusterIP. QUIC clone/push (UDP) is reachable only inside
the cluster. Use NodePort (default) or LoadBalancer for external clients.
{{- end }}

{{- /*
  Ask lore.storageType, not data.persistence.enabled: type=emptyDir leaves that
  legacy flag true, so testing the flag stays silent on the config it warns about.
*/}}
{{- $dataStorage := include "lore.storageType" .Values.data.persistence }}
{{- if eq $dataStorage "emptyDir" }}

WARNING: the data tier is on emptyDir. All repositories and data under /data are
LOST when the data pod restarts. Set data.persistence.type=pvc for any
non-ephemeral use, unless your store is a remote backend like S3 + DynamoDB.
{{- else if eq $dataStorage "hostPath" }}

WARNING: the data tier is on hostPath. /data survives a pod restart but does NOT
follow the pod to another node, so a reschedule leaves the authoritative store
behind. Set data.persistence.type=pvc unless the pod is pinned to one node and you
accept that risk.
{{- end }}

{{- /*
  Warn on the combination that turns "full" into total loss: a kubelet-enforced
  ceiling with nothing aging content out below it. A PVC hitting ENOSPC is bad
  too, but it does not delete the volume.
*/}}
{{- $dataStore := .Values.data.store | default dict }}
{{- $sizeLimit := (.Values.data.persistence.emptyDir | default dict).sizeLimit }}
{{- if and (eq $dataStorage "emptyDir") $sizeLimit (not (or $dataStore.maxSize $dataStore.maxCapacity)) }}

WARNING: the data store has no cap (data.store.maxSize and .maxCapacity are both
unset), but its emptyDir has a sizeLimit of {{ $sizeLimit }}. Nothing reclaims
space, so the store grows until it reaches that limit and the kubelet evicts the
pod — which deletes the volume and the entire store in one step. Set
data.store.maxSize, together with data.store.compactionDelaySeconds (the server's
own default is once a day), to age the coldest content out gradually instead.
{{- end }}

{{- if eq .Values.topology.mode "tiered" }}

Topology: tiered. One DATA pod ({{ $svc }}-0) is the authoritative durable store.
{{ .Values.handler.replicas }} HANDLER pods ({{ include "lore.handlerFullname" . }}-0 ...)
are caching replicas backed by the data pod.

{{- if .Values.handler.upstreamURL }}
The handlers front the external upstream {{ .Values.handler.upstreamURL }}, so the
public Service routes ONLY to the handlers; the local data pod is unused. Run
repository/branch `list` against the upstream directly — a handler cannot serve
`list` from a remote store.
{{- else }}
By default the public Service routes ONLY to the data pod, because it is the only
pod that can serve repository/branch `list` correctly. Handlers are an in-cluster
cache tier reached via {{ include "lore.handlerFullname" . }}-headless. Set
handler.servePublic=true to add handlers to the public rotation (read fan-out;
only safe once the server can serve `list` from a remote store).
{{- end }}

Check pod health:
  kubectl get pods -n {{ $ns }} -l app.kubernetes.io/instance={{ .Release.Name }}
{{- end }}
