Authenticate with a seeded operator key; fold escalation and switches into TerdutTeam
Credentials: the TerdutServer controller generates <name>-operator-key in the server's own namespace (owned by it) and hands it to the pods as TERDUT_OPERATOR_KEY; the server creates its instance-scoped account from it at every start. A replaced Secret rolls the pods. The bootstrap handshake, the checkpoint Secret, per-team service accounts and credentials Secrets, BootstrapStateLost and credentials.deletionPolicy are gone. CRDs: TerdutServer, TerdutTeam and TerdutAlertSource. TerdutEscalationRule and TerdutDeadmanSwitch become spec.escalation and spec.deadmanSwitches[] on the team (matched by name, extras removed); team invites are removed. A team is created under the identity <namespace>/<name> (external_id), so a retry, a lost status or a deleted team heal by repeating the same call, and a display name owned by another team is TeamNameTaken instead of an adoption. The server resolves escalation usernames (UnknownUser condition). OIDC claim names and trustEmail are spec fields. Fixes: query values are URL-escaped; every delete treats 404 as success; deleting a team no longer depends on allowedTeams consent; a switch or integration deleted on the server is recreated; unnamed switches take the CR's name. Cleanup: scaffold e2e test, AGENTS.md, devcontainer, unused config/ pieces and Client.Version() removed; DESIGN.md, README, ROADMAP and the demo (run-demo.sh, manifests) rewritten for the new design. Secret RBAC stays cluster-wide, now stated in DESIGN.md section 9. Claude-Session: https://claude.ai/code/session_016mBLURvJoMuUEr9cB2RpUN
This commit is contained in:
@@ -2,7 +2,9 @@ package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@@ -39,9 +41,8 @@ type TerdutAlertSourceReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
|
||||
OperatorNamespace string
|
||||
Recorder recorder.EventRecorder
|
||||
NewClient func(endpoint string) *tdclient.Client
|
||||
Recorder recorder.EventRecorder
|
||||
NewClient func(endpoint string) *tdclient.Client
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=terdut.ryuvia.com,resources=terdutalertsources,verbs=get;list;watch;create;update;patch;delete
|
||||
@@ -79,7 +80,7 @@ func (r *TerdutAlertSourceReconciler) Reconcile(ctx context.Context, req ctrl.Re
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
team, tc, resolveErr := resolveTeamAndClient(ctx, r.Client, r.OperatorNamespace, as.Namespace, as.Spec.TeamRef, newClient)
|
||||
team, tc, resolveErr := resolveTeamAndClient(ctx, r.Client, as.Namespace, as.Spec.TeamRef, newClient)
|
||||
if resolveErr != nil {
|
||||
return r.setNotReady(ctx, &as, resolveErr.reason, resolveErr.message, waitInterval)
|
||||
}
|
||||
@@ -111,7 +112,27 @@ func (r *TerdutAlertSourceReconciler) Reconcile(ctx context.Context, req ctrl.Re
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
} else if err := tc.RenameIntegration(ctx, teamID, as.Status.IntegrationID, as.Spec.Name); err != nil {
|
||||
return ctrl.Result{}, fmt.Errorf("PATCH /api/teams/%d/integrations/%d: %w", teamID, as.Status.IntegrationID, err)
|
||||
se, ok := errors.AsType[*tdclient.StatusError](err)
|
||||
if !ok || se.Code != http.StatusNotFound {
|
||||
return ctrl.Result{}, fmt.Errorf("PATCH /api/teams/%d/integrations/%d: %w", teamID, as.Status.IntegrationID, err)
|
||||
}
|
||||
// Deleted on the server behind our back, so the webhook URL
|
||||
// already stopped working: recreate it (a new URL, in the same
|
||||
// Secret) rather than fail forever. reconcileCreate trusts the
|
||||
// Secret's existence as "already created", so drop it first.
|
||||
lostID := as.Status.IntegrationID
|
||||
if err := r.Delete(ctx, &secret); err != nil && !apierrors.IsNotFound(err) {
|
||||
return ctrl.Result{}, fmt.Errorf("deleting stale webhook Secret %s/%s: %w", as.Namespace, secretName, err)
|
||||
}
|
||||
as.Status.IntegrationID = 0
|
||||
if err := r.reconcileCreate(ctx, &as, tc, teamID); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
if r.Recorder != nil {
|
||||
r.Recorder.Eventf(&as, nil, corev1.EventTypeWarning, "IntegrationRecreated", "IntegrationRecreated",
|
||||
"integration %d no longer exists on the server; created %d with a new webhook URL (Secret %s)",
|
||||
lostID, as.Status.IntegrationID, secretName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,7 +300,7 @@ func (r *TerdutAlertSourceReconciler) reconcileDelete(
|
||||
|
||||
if as.Status.IntegrationID != 0 {
|
||||
if team, tc, resolveErr := resolveTeamAndClient(
|
||||
ctx, r.Client, r.OperatorNamespace, as.Namespace, as.Spec.TeamRef, newClient,
|
||||
ctx, r.Client, as.Namespace, as.Spec.TeamRef, newClient,
|
||||
); resolveErr == nil {
|
||||
if err := tc.DeleteIntegration(ctx, team.Status.TeamID, as.Status.IntegrationID); err != nil {
|
||||
if r.Recorder != nil {
|
||||
|
||||
Reference in New Issue
Block a user