Installer: deallocate ports upon app uninstall
Change-Id: I19298537fed02de03a9e74fa351cf23f733de699
diff --git a/core/port-allocator/main.go b/core/port-allocator/main.go
index 48ffd35..d99550b 100644
--- a/core/port-allocator/main.go
+++ b/core/port-allocator/main.go
@@ -71,8 +71,9 @@
}
func (s *server) Start() error {
- s.r.HandleFunc("/api/allocate", s.handleAllocate)
s.r.HandleFunc("/api/reserve", s.handleReserve)
+ s.r.HandleFunc("/api/allocate", s.handleAllocate)
+ s.r.HandleFunc("/api/remove", s.handleRemove)
if err := s.s.ListenAndServe(); err != nil && err != http.ErrServerClosed {
return err
}
@@ -91,6 +92,13 @@
Secret string `json:"secret"`
}
+type removeReq struct {
+ Protocol string `json:"protocol"`
+ SourcePort int `json:"sourcePort"`
+ TargetService string `json:"targetService"`
+ TargetPort int `json:"targetPort"`
+}
+
func extractAllocateReq(r io.Reader) (allocateReq, error) {
var req allocateReq
if err := json.NewDecoder(r).Decode(&req); err != nil {
@@ -103,6 +111,18 @@
return req, nil
}
+func extractRemoveReq(r io.Reader) (removeReq, error) {
+ var req removeReq
+ if err := json.NewDecoder(r).Decode(&req); err != nil {
+ return removeReq{}, err
+ }
+ req.Protocol = strings.ToLower(req.Protocol)
+ if req.Protocol != "tcp" && req.Protocol != "udp" {
+ return removeReq{}, fmt.Errorf("Unexpected protocol %s", req.Protocol)
+ }
+ return req, nil
+}
+
type reserveResp struct {
Port int `json:"port"`
Secret string `json:"secret"`
@@ -155,6 +175,15 @@
return nil
}
+func removePort(pm map[string]any, req removeReq) error {
+ sourcePortStr := strconv.Itoa(req.SourcePort)
+ if _, ok := pm[sourcePortStr]; !ok {
+ return fmt.Errorf("port %d is not open to remove", req.SourcePort)
+ }
+ delete(pm, sourcePortStr)
+ return nil
+}
+
const start = 49152
const end = 65535
@@ -266,6 +295,50 @@
}
}
+func (s *server) handleRemove(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ http.Error(w, "only post method is supported", http.StatusBadRequest)
+ return
+ }
+ req, err := extractRemoveReq(r.Body)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+ s.l.Lock()
+ defer s.l.Unlock()
+ ingressRel, err := s.client.ReadRelease()
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ tcp, udp, err := extractPorts(ingressRel)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ switch req.Protocol {
+ case "tcp":
+ if err := removePort(tcp, req); err != nil {
+ http.Error(w, err.Error(), http.StatusConflict)
+ return
+ }
+ case "udp":
+ if err := removePort(udp, req); err != nil {
+ http.Error(w, err.Error(), http.StatusConflict)
+ return
+ }
+ default:
+ panic("MUST NOT REACH")
+ }
+ commitMsg := fmt.Sprintf("ingress: remove port map %d %s", req.SourcePort, req.Protocol)
+ if err := s.client.WriteRelease(ingressRel, commitMsg); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ delete(s.reserve, req.SourcePort)
+}
+
// TODO(gio): deduplicate
func createRepoClient(addr string, keyPath string) (soft.RepoIO, error) {
sshKey, err := os.ReadFile(keyPath)