3
This commit is contained in:
@@ -84,8 +84,10 @@ func (m *Module) RegisterRoutes(router chi.Router) {
|
||||
r.Post("/{clusterID}/updates/releases", m.createReleaseVersion)
|
||||
r.Put("/{clusterID}/nodes/{nodeID}/updates/policy", m.upsertNodeUpdatePolicy)
|
||||
r.Get("/{clusterID}/nodes/{nodeID}/updates/plan", m.getNodeUpdatePlan)
|
||||
r.Get("/{clusterID}/nodes/{nodeID}/updates/bridge-replay-plan", m.getNodeBridgeReplayPlan)
|
||||
r.Post("/{clusterID}/nodes/{nodeID}/updates/status", m.reportNodeUpdateStatus)
|
||||
r.Get("/{clusterID}/nodes/{nodeID}/updates/statuses", m.listNodeUpdateStatuses)
|
||||
r.Get("/{clusterID}/updates/stale-node-risk-report", m.getStaleNodeRiskReport)
|
||||
r.Get("/{clusterID}/nodes/{nodeID}/testing-flags", m.getEffectiveNodeTestingFlags)
|
||||
r.Get("/{clusterID}/nodes/{nodeID}/mesh/synthetic-config", m.getNodeSyntheticMeshConfig)
|
||||
r.Post("/{clusterID}/nodes/{nodeID}/telemetry", m.recordNodeTelemetry)
|
||||
@@ -843,6 +845,29 @@ func (m *Module) listNodeUpdateStatuses(w http.ResponseWriter, r *http.Request)
|
||||
httpx.WriteJSON(w, http.StatusOK, map[string]any{"node_update_statuses": items})
|
||||
}
|
||||
|
||||
func (m *Module) getStaleNodeRiskReport(w http.ResponseWriter, r *http.Request) {
|
||||
item, err := m.service.GetStaleNodeRiskReport(r.Context(), GetStaleNodeRiskReportInput{
|
||||
ActorUserID: r.URL.Query().Get("actor_user_id"),
|
||||
ClusterID: chi.URLParam(r, "clusterID"),
|
||||
})
|
||||
if writeServiceError(w, err) {
|
||||
return
|
||||
}
|
||||
httpx.WriteJSON(w, http.StatusOK, map[string]any{"stale_node_risk_report": item})
|
||||
}
|
||||
|
||||
func (m *Module) getNodeBridgeReplayPlan(w http.ResponseWriter, r *http.Request) {
|
||||
item, err := m.service.GetNodeBridgeReplayPlan(r.Context(), GetNodeBridgeReplayPlanInput{
|
||||
ActorUserID: r.URL.Query().Get("actor_user_id"),
|
||||
ClusterID: chi.URLParam(r, "clusterID"),
|
||||
NodeID: chi.URLParam(r, "nodeID"),
|
||||
})
|
||||
if writeServiceError(w, err) {
|
||||
return
|
||||
}
|
||||
httpx.WriteJSON(w, http.StatusOK, map[string]any{"node_bridge_replay_plan": item})
|
||||
}
|
||||
|
||||
func (m *Module) getEffectiveNodeTestingFlags(w http.ResponseWriter, r *http.Request) {
|
||||
item, err := m.service.GetEffectiveNodeTestingFlags(r.Context(), chi.URLParam(r, "clusterID"), chi.URLParam(r, "nodeID"))
|
||||
if writeServiceError(w, err) {
|
||||
@@ -3386,6 +3411,7 @@ func writeServiceError(w http.ResponseWriter, err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
var legacyRemovalBlocked *LegacyRemovalBlockedError
|
||||
switch {
|
||||
case errors.Is(err, ErrAccessDenied):
|
||||
httpx.WriteError(w, http.StatusForbidden, err.Error())
|
||||
@@ -3393,6 +3419,12 @@ func writeServiceError(w http.ResponseWriter, err error) bool {
|
||||
httpx.WriteError(w, http.StatusForbidden, err.Error())
|
||||
case errors.Is(err, ErrClusterReadOnly):
|
||||
httpx.WriteError(w, http.StatusConflict, err.Error())
|
||||
case errors.As(err, &legacyRemovalBlocked):
|
||||
httpx.WriteErrorMessage(w, http.StatusConflict, httpx.ErrorResponse{
|
||||
Error: httpx.NewErrorMessage(http.StatusConflict, err.Error(), legacyRemovalBlockedErrorDetails(*legacyRemovalBlocked), ""),
|
||||
})
|
||||
case errors.Is(err, ErrLegacyRemovalBlocked):
|
||||
httpx.WriteError(w, http.StatusConflict, err.Error())
|
||||
case errors.Is(err, ErrVPNLeaseAlreadyActive):
|
||||
httpx.WriteError(w, http.StatusConflict, err.Error())
|
||||
case errors.Is(err, ErrInvalidPayload), errors.Is(err, ErrInvalidJoinToken), errors.Is(err, ErrInvalidNodeRole):
|
||||
@@ -3404,3 +3436,37 @@ func writeServiceError(w http.ResponseWriter, err error) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func legacyRemovalBlockedErrorDetails(err LegacyRemovalBlockedError) map[string]any {
|
||||
details := map[string]any{
|
||||
"blocked_operation": err.BlockedOperation,
|
||||
"legacy_removal_allowed": err.Report.LegacyRemovalAllowed,
|
||||
"bridge_hold_required": err.Report.BridgeHoldRequired,
|
||||
"bridge_hold_reasons": err.Report.BridgeHoldReasons,
|
||||
"blocked_operations": err.Report.BlockedOperations,
|
||||
"heartbeat_stale_after_seconds": err.Report.HeartbeatStaleAfterSeconds,
|
||||
"stale_nodes": err.Report.Summary.StaleNodes,
|
||||
"blocked_nodes": err.Report.Summary.BlockedNodes,
|
||||
"artifact_gap_nodes": err.Report.Summary.ArtifactGapNodes,
|
||||
"unknown_profile_nodes": err.Report.Summary.UnknownProfileNodes,
|
||||
"waiting_update_status_nodes": err.Report.Summary.WaitingUpdateStatusNodes,
|
||||
"unknown_version_nodes": err.Report.Summary.UnknownVersionNodes,
|
||||
"legacy_recovery_contract_nodes": err.Report.Summary.LegacyRecoveryContractNodes,
|
||||
"recovery_bridge_required_nodes": err.Report.Summary.RecoveryBridgeRequiredNodes,
|
||||
"recovery_bridge_replay_ready_nodes": err.Report.Summary.RecoveryBridgeReplayReadyNodes,
|
||||
"waiting_recovery_heartbeat_nodes": err.Report.Summary.WaitingRecoveryHeartbeatNodes,
|
||||
}
|
||||
blockedNodeIDs := make([]string, 0, len(err.Report.Nodes))
|
||||
for _, node := range err.Report.Nodes {
|
||||
if node.Blocked {
|
||||
blockedNodeIDs = append(blockedNodeIDs, node.NodeID)
|
||||
}
|
||||
}
|
||||
if len(blockedNodeIDs) > 0 {
|
||||
details["blocked_node_ids"] = blockedNodeIDs
|
||||
}
|
||||
if len(err.Report.BridgeHoldNodeIDs) > 0 {
|
||||
details["bridge_hold_node_ids"] = err.Report.BridgeHoldNodeIDs
|
||||
}
|
||||
return details
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user