Stage 5: installer chart + release infra, kind e2e pass through the chart
Release / test (push) Successful in 2m48s
CI / chart (push) Successful in 1s
CI / security (push) Successful in 1m3s
CI / test (push) Successful in 2m5s
Release / chart (push) Successful in 4s
Release / image (push) Successful in 7m6s
Release / scan-image (push) Failing after 33s
Release / test (push) Successful in 2m48s
CI / chart (push) Successful in 1s
CI / security (push) Successful in 1m3s
CI / test (push) Successful in 2m5s
Release / chart (push) Successful in 4s
Release / image (push) Successful in 7m6s
Release / scan-image (push) Failing after 33s
Chart (charts/terdut-operator) generated via kubebuilder's own helm/v2-alpha plugin from config/'s kustomize output -- CRDs + manager Deployment/RBAC come from the same markers every other stage already generates, one source of truth. Hand-added on top: the optional terdutServer values block (DESIGN.md §10's "helm install and get a server" path, off by default) and the release-skill plumbing -- .release.conf, release-vars/helm-lint/push/ helm-package/helm-push/release Makefile targets, .gitea/workflows/release.yaml (test -> image/chart -> scan-image) -- mirroring terdut-server's own shape (registry/namespace convention, multi-arch buildx push, trivy/govulncheck/ gitleaks scans). ci.yaml gains security and chart jobs to match. Two real issues caught while wiring this, fixed before either shipped: - Dockerfile's builder stage didn't pin --platform=$BUILDPLATFORM, which would have made a multi-arch release build fail outright on this org's runners (no binfmt registration) -- same fix terdut-server's own Dockerfile already needed for the same reason. - govulncheck found one real, reachable finding: google.golang.org/grpc v1.82.1 (transitive via controller-runtime's otel exporter), fixed by bumping to v1.83.1. Full golden-path kind e2e pass, this time through `helm install` rather than raw kustomize: TerdutServer (real terdut-server v0.33.0 image) -> TerdutTeam -> one of each child kind, each confirmed Ready and then independently confirmed against terdut-server's own API from inside the cluster (not just the operator's own status). Deleted every CR in reverse order and confirmed server-side cleanup the same independent way for all three child kinds, the team, and the server. No new bugs found -- Stage 1's own kind pass already caught what a real cluster catches that envtest can't. Also dropped the kubebuilder helm plugin's default .github/workflows/ scaffold, same as Stage 0 already did for the main scaffold: this org runs on Gitea, not GitHub. Not done here, deliberately: an actual tagged release. release-preflight found no terdut-operator/ entry under Ryuvia/charts yet to bump -- that one-time wrapper bootstrap is a decision about deploying this operator for real, not a side effect of finishing this stage. make fmt lint test helm-lint build all clean.
This commit is contained in:
@@ -0,0 +1,457 @@
|
||||
{{- if .Values.crd.enabled }}
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
{{- if .Values.crd.keep }}
|
||||
"helm.sh/resource-policy": keep
|
||||
{{- end }}
|
||||
controller-gen.kubebuilder.io/version: v0.22.0
|
||||
name: terdutservers.terdut.ryuvia.com
|
||||
spec:
|
||||
group: terdut.ryuvia.com
|
||||
names:
|
||||
kind: TerdutServer
|
||||
listKind: TerdutServerList
|
||||
plural: terdutservers
|
||||
singular: terdutserver
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- additionalPrinterColumns:
|
||||
- jsonPath: .spec.replicas
|
||||
name: Replicas
|
||||
type: integer
|
||||
- jsonPath: .status.conditions[?(@.type=="Ready")].status
|
||||
name: Ready
|
||||
type: string
|
||||
- jsonPath: .status.conditions[?(@.type=="Ready")].reason
|
||||
name: Reason
|
||||
type: string
|
||||
name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: TerdutServer is the Schema for the terdutservers API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: spec defines the desired state of TerdutServer
|
||||
properties:
|
||||
allowedTeams:
|
||||
description: |-
|
||||
allowedTeams gates cross-namespace TerdutTeams (DESIGN.md §4.6).
|
||||
Unused until TerdutTeam exists (ROADMAP.md Stage 2); present now so
|
||||
this CRD's schema doesn't need a breaking change to grow it later.
|
||||
properties:
|
||||
namespaces:
|
||||
description: |-
|
||||
AllowedTeamsNamespaces gates which namespaces a TerdutTeam may resolve a
|
||||
cross-namespace serverRef into this TerdutServer from (DESIGN.md §4.6).
|
||||
Same-namespace TerdutTeams are always allowed, regardless of this field.
|
||||
Modeled on Gateway API's Gateway.spec.allowedListeners.namespaces.
|
||||
properties:
|
||||
from:
|
||||
default: None
|
||||
description: |-
|
||||
from selects which namespaces may attach. Same is equivalent to None in
|
||||
effect (same-namespace is unrestricted either way) but kept for parity
|
||||
with the upstream enum this mirrors, and to make the policy
|
||||
self-documenting in a diff.
|
||||
enum:
|
||||
- None
|
||||
- Same
|
||||
- All
|
||||
- Selector
|
||||
type: string
|
||||
selector:
|
||||
description: |-
|
||||
selector is required, and only meaningful, when from is Selector: a
|
||||
standard label selector over Namespace objects.
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector
|
||||
requirements. The requirements are ANDed.
|
||||
items:
|
||||
description: |-
|
||||
A label selector requirement is a selector that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector
|
||||
applies to.
|
||||
type: string
|
||||
operator:
|
||||
description: |-
|
||||
operator represents a key's relationship to a set of values.
|
||||
Valid operators are In, NotIn, Exists and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: |-
|
||||
values is an array of string values. If the operator is In or NotIn,
|
||||
the values array must be non-empty. If the operator is Exists or DoesNotExist,
|
||||
the values array must be empty. This array is replaced during a strategic
|
||||
merge patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
x-kubernetes-list-type: atomic
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
|
||||
map is equivalent to an element of matchExpressions, whose key field is "key", the
|
||||
operator is "In", and the values array contains only "value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
type: object
|
||||
type: object
|
||||
database:
|
||||
description: |-
|
||||
DatabaseSpec is the Postgres connection this TerdutServer uses. Exactly
|
||||
one of dsn or postgresClusterRef must be set (DESIGN.md §8) — this
|
||||
operator provisions no database either way, only wires up one that
|
||||
exists.
|
||||
properties:
|
||||
dsn:
|
||||
description: |-
|
||||
dsn is a DSN with no password in it, e.g.
|
||||
"postgres://terdut@terdut-postgres:5432/terdut?sslmode=require" --
|
||||
mutually exclusive with postgresClusterRef.
|
||||
type: string
|
||||
passwordSecretRef:
|
||||
description: |-
|
||||
passwordSecretRef is where PGPASSWORD comes from for the dsn path.
|
||||
pgx falls back to libpq's environment variables for anything the DSN
|
||||
omits, so the password never appears in the DSN string itself. Unused
|
||||
on the postgresClusterRef path -- the Zalando-generated Secret is
|
||||
wired in directly instead.
|
||||
properties:
|
||||
key:
|
||||
description: key is the data key inside the Secret holding
|
||||
the raw value.
|
||||
minLength: 1
|
||||
type: string
|
||||
name:
|
||||
description: name is the Secret's name.
|
||||
minLength: 1
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- name
|
||||
type: object
|
||||
postgresClusterRef:
|
||||
description: |-
|
||||
postgresClusterRef names a Zalando postgres-operator CR instead of a
|
||||
plain DSN -- mutually exclusive with dsn.
|
||||
properties:
|
||||
name:
|
||||
minLength: 1
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-validations:
|
||||
- message: exactly one of dsn or postgresClusterRef must be set
|
||||
rule: '(has(self.dsn) ? 1 : 0) + (has(self.postgresClusterRef) ?
|
||||
1 : 0) == 1'
|
||||
deadman:
|
||||
description: |-
|
||||
DeadmanSpec controls dead man's switch alerts. Matchers/Timeout/Severity
|
||||
map straight to TERDUT_DEADMAN_MATCHERS/TERDUT_DEADMAN_TIMEOUT/
|
||||
TERDUT_DEADMAN_SEVERITY.
|
||||
properties:
|
||||
matchers:
|
||||
type: string
|
||||
severity:
|
||||
type: string
|
||||
timeout:
|
||||
type: string
|
||||
type: object
|
||||
image:
|
||||
description: ImageSpec is the terdut-server image to run.
|
||||
properties:
|
||||
repository:
|
||||
minLength: 1
|
||||
type: string
|
||||
tag:
|
||||
minLength: 1
|
||||
type: string
|
||||
required:
|
||||
- repository
|
||||
- tag
|
||||
type: object
|
||||
networking:
|
||||
description: |-
|
||||
NetworkingSpec is how this TerdutServer is reached from outside the
|
||||
cluster.
|
||||
|
||||
hostname/gatewayListener describe the intended Gateway API HTTPRoute
|
||||
(matching charts/terdut-server's own templates/httpproxy.yaml, despite its
|
||||
name — that chart carries a Gateway API HTTPRoute, not a Contour
|
||||
HTTPProxy), but creating that HTTPRoute isn't implemented yet: it needs
|
||||
the Gateway API types as a new dependency, and nothing about proving a
|
||||
TerdutServer boots and bootstraps a real server depends on external
|
||||
ingress existing. Tracked as a near-term follow-up, not deferred to a
|
||||
later ROADMAP.md stage the way Deployment/database/bootstrap once were.
|
||||
properties:
|
||||
gatewayListener:
|
||||
description: |-
|
||||
gatewayListener is the HTTPRoute's sectionName once it exists. Empty
|
||||
attaches to every matching listener, including plaintext HTTP.
|
||||
type: string
|
||||
hostname:
|
||||
description: hostname the HTTPRoute will carry once it exists.
|
||||
type: string
|
||||
servicePort:
|
||||
default: 8080
|
||||
description: |-
|
||||
servicePort is both the Service's port and the HTTPRoute's backend
|
||||
port once it exists. Defaults to 8080, matching the chart's own
|
||||
service.port default.
|
||||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
notify:
|
||||
description: |-
|
||||
NotifySpec controls push notifications via ntfy. Empty ntfyURL disables
|
||||
notifications entirely (matches the chart's own default).
|
||||
properties:
|
||||
fallbackTopic:
|
||||
type: string
|
||||
ntfyURL:
|
||||
type: string
|
||||
repeatEvery:
|
||||
type: string
|
||||
tokenSecretRef:
|
||||
description: |-
|
||||
tokenSecretRef is an optional bearer token for an access-controlled
|
||||
ntfy. Leave unset for an open ntfy.
|
||||
properties:
|
||||
key:
|
||||
description: key is the data key inside the Secret holding
|
||||
the raw value.
|
||||
minLength: 1
|
||||
type: string
|
||||
name:
|
||||
description: name is the Secret's name.
|
||||
minLength: 1
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- name
|
||||
type: object
|
||||
type: object
|
||||
oidc:
|
||||
description: |-
|
||||
OIDCSpec controls single sign-on. Fields the chart also exposes but
|
||||
DESIGN.md's spec doesn't (usernameClaim, emailClaim, groupsClaim,
|
||||
trustEmail) use terdut-server's own defaults
|
||||
(preferred_username/email/groups/false) rather than being added here
|
||||
speculatively.
|
||||
properties:
|
||||
adminGroup:
|
||||
type: string
|
||||
allowedGroups:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
clientID:
|
||||
type: string
|
||||
clientSecretRef:
|
||||
description: |-
|
||||
SecretKeyRef names one data key inside a Secret. Every use of this type in
|
||||
TerdutServerSpec resolves in the TerdutServer's own namespace (it's wired
|
||||
straight into the Deployment's pod spec as a secretKeyRef env source,
|
||||
which Kubernetes itself only allows same-namespace) -- unlike the
|
||||
generated credentials Secret (DESIGN.md §6), which always lives in the
|
||||
operator's own namespace and is never referenced through this type.
|
||||
properties:
|
||||
key:
|
||||
description: key is the data key inside the Secret holding
|
||||
the raw value.
|
||||
minLength: 1
|
||||
type: string
|
||||
name:
|
||||
description: name is the Secret's name.
|
||||
minLength: 1
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- name
|
||||
type: object
|
||||
enabled:
|
||||
type: boolean
|
||||
issuer:
|
||||
type: string
|
||||
name:
|
||||
default: SSO
|
||||
type: string
|
||||
scopes:
|
||||
default: openid profile email
|
||||
type: string
|
||||
sessionMaxAge:
|
||||
default: 12h
|
||||
type: string
|
||||
type: object
|
||||
passwordLogin:
|
||||
default: true
|
||||
description: |-
|
||||
passwordLogin: whether a user may sign in, or sign up, with a
|
||||
password.
|
||||
type: boolean
|
||||
replicas:
|
||||
default: 1
|
||||
description: |-
|
||||
replicas. terdut-server is not horizontally-scale-tested; keep this
|
||||
at its default of 1 unless you've verified otherwise -- the sweeper
|
||||
and the notifier are unsynchronised singletons.
|
||||
format: int32
|
||||
type: integer
|
||||
sweeper:
|
||||
description: |-
|
||||
SweeperSpec controls incident auto-resolve/archive timing. Values are
|
||||
Go duration strings (e.g. "6h"), passed straight through to the
|
||||
TERDUT_STALE_AFTER/TERDUT_ARCHIVE_AFTER env vars exactly as written --
|
||||
not a structured metav1.Duration, since terdut-server parses them itself
|
||||
and a round-trip through a different type would buy nothing.
|
||||
properties:
|
||||
archiveAfter:
|
||||
type: string
|
||||
staleAfter:
|
||||
type: string
|
||||
type: object
|
||||
required:
|
||||
- database
|
||||
- image
|
||||
- networking
|
||||
type: object
|
||||
status:
|
||||
description: status defines the observed state of TerdutServer
|
||||
properties:
|
||||
conditions:
|
||||
description: conditions represent the current state of the TerdutServer
|
||||
resource.
|
||||
items:
|
||||
description: Condition contains details for one aspect of the current
|
||||
state of this API Resource.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: |-
|
||||
lastTransitionTime is the last time the condition transitioned from one status to another.
|
||||
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: |-
|
||||
message is a human readable message indicating details about the transition.
|
||||
This may be an empty string.
|
||||
maxLength: 32768
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: |-
|
||||
observedGeneration represents the .metadata.generation that the condition was set based upon.
|
||||
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
|
||||
with respect to the current state of the instance.
|
||||
format: int64
|
||||
minimum: 0
|
||||
type: integer
|
||||
reason:
|
||||
description: |-
|
||||
reason contains a programmatic identifier indicating the reason for the condition's last transition.
|
||||
Producers of specific condition types may define expected values and meanings for this field,
|
||||
and whether the values are considered a guaranteed API.
|
||||
The value should be a CamelCase string.
|
||||
This field may not be empty.
|
||||
maxLength: 1024
|
||||
minLength: 1
|
||||
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
|
||||
type: string
|
||||
status:
|
||||
description: status of the condition, one of True, False, Unknown.
|
||||
enum:
|
||||
- "True"
|
||||
- "False"
|
||||
- Unknown
|
||||
type: string
|
||||
type:
|
||||
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
maxLength: 316
|
||||
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||
type: string
|
||||
required:
|
||||
- lastTransitionTime
|
||||
- message
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
x-kubernetes-list-map-keys:
|
||||
- type
|
||||
x-kubernetes-list-type: map
|
||||
credentialsSecretRef:
|
||||
description: |-
|
||||
credentialsSecretRef is the generated instance-scoped credential
|
||||
(DESIGN.md §6) -- pure output, always in the operator's own
|
||||
namespace, under a fixed data key ("token"). Set only once
|
||||
Bootstrapped is True.
|
||||
properties:
|
||||
key:
|
||||
description: key is the data key inside the Secret holding the
|
||||
raw value.
|
||||
minLength: 1
|
||||
type: string
|
||||
name:
|
||||
description: name is the Secret's name.
|
||||
minLength: 1
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
- name
|
||||
type: object
|
||||
observedGeneration:
|
||||
description: |-
|
||||
observedGeneration is the .metadata.generation this status was last
|
||||
computed against — the standard way a client (or `kubectl wait`)
|
||||
tells "applied" from "seen" (DESIGN.md §7).
|
||||
format: int64
|
||||
type: integer
|
||||
serviceName:
|
||||
description: |-
|
||||
serviceName is the Service this controller created for the
|
||||
Deployment, so other objects can reference it without recomputing the
|
||||
naming convention.
|
||||
type: string
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
{{- end }}
|
||||
Reference in New Issue
Block a user