ScuttleBot
fix: nil pointer panic in topology handlers when topology not configured Add nil guards for s.topoMgr in all three topology API handlers. The topology manager is nil when no topology config is present, which caused a panic on GET /v1/topology (hit by the channels tab).
Commit
319a0fe1b5aed88db74f271e6cf42d0e93e8a32653b008c1cd8299d47ff6a45f
Parent
6b4392764b30638…
1 file changed
+12
| --- internal/api/channels_topology.go | ||
| +++ internal/api/channels_topology.go | ||
| @@ -41,10 +41,14 @@ | ||
| 41 | 41 | } |
| 42 | 42 | if err := topology.ValidateName(req.Name); err != nil { |
| 43 | 43 | writeError(w, http.StatusBadRequest, err.Error()) |
| 44 | 44 | return |
| 45 | 45 | } |
| 46 | + if s.topoMgr == nil { | |
| 47 | + writeError(w, http.StatusServiceUnavailable, "topology not configured") | |
| 48 | + return | |
| 49 | + } | |
| 46 | 50 | |
| 47 | 51 | policy := s.topoMgr.Policy() |
| 48 | 52 | |
| 49 | 53 | // Merge autojoin from policy if the caller didn't specify any. |
| 50 | 54 | autojoin := req.Autojoin |
| @@ -95,18 +99,26 @@ | ||
| 95 | 99 | func (s *Server) handleDropChannel(w http.ResponseWriter, r *http.Request) { |
| 96 | 100 | channel := "#" + r.PathValue("channel") |
| 97 | 101 | if err := topology.ValidateName(channel); err != nil { |
| 98 | 102 | writeError(w, http.StatusBadRequest, err.Error()) |
| 99 | 103 | return |
| 104 | + } | |
| 105 | + if s.topoMgr == nil { | |
| 106 | + writeError(w, http.StatusServiceUnavailable, "topology not configured") | |
| 107 | + return | |
| 100 | 108 | } |
| 101 | 109 | s.topoMgr.DropChannel(channel) |
| 102 | 110 | w.WriteHeader(http.StatusNoContent) |
| 103 | 111 | } |
| 104 | 112 | |
| 105 | 113 | // handleGetTopology handles GET /v1/topology. |
| 106 | 114 | // Returns the channel type rules and static channel names declared in config. |
| 107 | 115 | func (s *Server) handleGetTopology(w http.ResponseWriter, r *http.Request) { |
| 116 | + if s.topoMgr == nil { | |
| 117 | + writeJSON(w, http.StatusOK, topologyResponse{}) | |
| 118 | + return | |
| 119 | + } | |
| 108 | 120 | policy := s.topoMgr.Policy() |
| 109 | 121 | if policy == nil { |
| 110 | 122 | writeJSON(w, http.StatusOK, topologyResponse{}) |
| 111 | 123 | return |
| 112 | 124 | } |
| 113 | 125 |
| --- internal/api/channels_topology.go | |
| +++ internal/api/channels_topology.go | |
| @@ -41,10 +41,14 @@ | |
| 41 | } |
| 42 | if err := topology.ValidateName(req.Name); err != nil { |
| 43 | writeError(w, http.StatusBadRequest, err.Error()) |
| 44 | return |
| 45 | } |
| 46 | |
| 47 | policy := s.topoMgr.Policy() |
| 48 | |
| 49 | // Merge autojoin from policy if the caller didn't specify any. |
| 50 | autojoin := req.Autojoin |
| @@ -95,18 +99,26 @@ | |
| 95 | func (s *Server) handleDropChannel(w http.ResponseWriter, r *http.Request) { |
| 96 | channel := "#" + r.PathValue("channel") |
| 97 | if err := topology.ValidateName(channel); err != nil { |
| 98 | writeError(w, http.StatusBadRequest, err.Error()) |
| 99 | return |
| 100 | } |
| 101 | s.topoMgr.DropChannel(channel) |
| 102 | w.WriteHeader(http.StatusNoContent) |
| 103 | } |
| 104 | |
| 105 | // handleGetTopology handles GET /v1/topology. |
| 106 | // Returns the channel type rules and static channel names declared in config. |
| 107 | func (s *Server) handleGetTopology(w http.ResponseWriter, r *http.Request) { |
| 108 | policy := s.topoMgr.Policy() |
| 109 | if policy == nil { |
| 110 | writeJSON(w, http.StatusOK, topologyResponse{}) |
| 111 | return |
| 112 | } |
| 113 |
| --- internal/api/channels_topology.go | |
| +++ internal/api/channels_topology.go | |
| @@ -41,10 +41,14 @@ | |
| 41 | } |
| 42 | if err := topology.ValidateName(req.Name); err != nil { |
| 43 | writeError(w, http.StatusBadRequest, err.Error()) |
| 44 | return |
| 45 | } |
| 46 | if s.topoMgr == nil { |
| 47 | writeError(w, http.StatusServiceUnavailable, "topology not configured") |
| 48 | return |
| 49 | } |
| 50 | |
| 51 | policy := s.topoMgr.Policy() |
| 52 | |
| 53 | // Merge autojoin from policy if the caller didn't specify any. |
| 54 | autojoin := req.Autojoin |
| @@ -95,18 +99,26 @@ | |
| 99 | func (s *Server) handleDropChannel(w http.ResponseWriter, r *http.Request) { |
| 100 | channel := "#" + r.PathValue("channel") |
| 101 | if err := topology.ValidateName(channel); err != nil { |
| 102 | writeError(w, http.StatusBadRequest, err.Error()) |
| 103 | return |
| 104 | } |
| 105 | if s.topoMgr == nil { |
| 106 | writeError(w, http.StatusServiceUnavailable, "topology not configured") |
| 107 | return |
| 108 | } |
| 109 | s.topoMgr.DropChannel(channel) |
| 110 | w.WriteHeader(http.StatusNoContent) |
| 111 | } |
| 112 | |
| 113 | // handleGetTopology handles GET /v1/topology. |
| 114 | // Returns the channel type rules and static channel names declared in config. |
| 115 | func (s *Server) handleGetTopology(w http.ResponseWriter, r *http.Request) { |
| 116 | if s.topoMgr == nil { |
| 117 | writeJSON(w, http.StatusOK, topologyResponse{}) |
| 118 | return |
| 119 | } |
| 120 | policy := s.topoMgr.Policy() |
| 121 | if policy == nil { |
| 122 | writeJSON(w, http.StatusOK, topologyResponse{}) |
| 123 | return |
| 124 | } |
| 125 |