github.com/rootless-containers/rootlesskit/v2@v2.3.4/pkg/api/router/router.go (about) 1 package router 2 3 import ( 4 "context" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "net/http" 9 "strconv" 10 11 "github.com/gorilla/mux" 12 "github.com/rootless-containers/rootlesskit/v2/pkg/api" 13 "github.com/rootless-containers/rootlesskit/v2/pkg/httputil" 14 "github.com/rootless-containers/rootlesskit/v2/pkg/port" 15 "github.com/rootless-containers/rootlesskit/v2/pkg/version" 16 ) 17 18 // NetworkDriver is implemented by network.ParentDriver 19 type NetworkDriver interface { 20 Info(context.Context) (*api.NetworkDriverInfo, error) 21 } 22 23 // PortDriver is implemented by port.ParentDriver 24 type PortDriver interface { 25 Info(context.Context) (*api.PortDriverInfo, error) 26 port.Manager 27 } 28 29 type Backend struct { 30 StateDir string 31 ChildPID int 32 // NetworkDriver can be nil 33 NetworkDriver NetworkDriver 34 // PortDriver MUST be thread-safe. 35 // PortDriver can be nil 36 PortDriver PortDriver 37 } 38 39 func (b *Backend) onPortDriverNil(w http.ResponseWriter, r *http.Request) { 40 httputil.WriteError(w, r, errors.New("no PortDriver is available"), http.StatusBadRequest) 41 } 42 43 // GetPorts is handler for GET /v{N}/ports 44 func (b *Backend) GetPorts(w http.ResponseWriter, r *http.Request) { 45 if b.PortDriver == nil { 46 b.onPortDriverNil(w, r) 47 return 48 } 49 ports, err := b.PortDriver.ListPorts(context.TODO()) 50 if err != nil { 51 httputil.WriteError(w, r, err, http.StatusInternalServerError) 52 return 53 } 54 m, err := json.Marshal(ports) 55 if err != nil { 56 httputil.WriteError(w, r, err, http.StatusInternalServerError) 57 return 58 } 59 w.Header().Set("Content-Type", "application/json") 60 w.WriteHeader(http.StatusOK) 61 w.Write(m) 62 } 63 64 // PostPort is the handler for POST /v{N}/ports 65 func (b *Backend) PostPort(w http.ResponseWriter, r *http.Request) { 66 if b.PortDriver == nil { 67 b.onPortDriverNil(w, r) 68 return 69 } 70 decoder := json.NewDecoder(r.Body) 71 var portSpec port.Spec 72 if err := decoder.Decode(&portSpec); err != nil { 73 httputil.WriteError(w, r, err, http.StatusBadRequest) 74 return 75 } 76 portStatus, err := b.PortDriver.AddPort(context.TODO(), portSpec) 77 if err != nil { 78 httputil.WriteError(w, r, err, http.StatusBadRequest) 79 return 80 } 81 m, err := json.Marshal(portStatus) 82 if err != nil { 83 httputil.WriteError(w, r, err, http.StatusInternalServerError) 84 return 85 } 86 w.Header().Set("Content-Type", "application/json") 87 w.WriteHeader(http.StatusCreated) 88 w.Write(m) 89 } 90 91 // DeletePort is the handler for POST /v{N}/ports/{id} 92 func (b *Backend) DeletePort(w http.ResponseWriter, r *http.Request) { 93 if b.PortDriver == nil { 94 b.onPortDriverNil(w, r) 95 return 96 } 97 idStr, ok := mux.Vars(r)["id"] 98 if !ok { 99 httputil.WriteError(w, r, errors.New("id not specified"), http.StatusBadRequest) 100 return 101 } 102 id, err := strconv.Atoi(idStr) 103 if err != nil { 104 httputil.WriteError(w, r, fmt.Errorf("bad id %s: %w", idStr, err), http.StatusBadRequest) 105 return 106 } 107 if err := b.PortDriver.RemovePort(context.TODO(), id); err != nil { 108 httputil.WriteError(w, r, err, http.StatusBadRequest) 109 return 110 } 111 w.WriteHeader(http.StatusOK) 112 } 113 114 func (b *Backend) GetInfo(w http.ResponseWriter, r *http.Request) { 115 info := &api.Info{ 116 APIVersion: api.Version, 117 Version: version.Version, 118 StateDir: b.StateDir, 119 ChildPID: b.ChildPID, 120 } 121 if b.NetworkDriver != nil { 122 ndInfo, err := b.NetworkDriver.Info(context.Background()) 123 if err != nil { 124 httputil.WriteError(w, r, err, http.StatusInternalServerError) 125 return 126 } 127 info.NetworkDriver = ndInfo 128 } 129 if b.PortDriver != nil { 130 pdInfo, err := b.PortDriver.Info(context.Background()) 131 if err != nil { 132 httputil.WriteError(w, r, err, http.StatusInternalServerError) 133 return 134 } 135 info.PortDriver = pdInfo 136 } 137 m, err := json.Marshal(info) 138 if err != nil { 139 httputil.WriteError(w, r, err, http.StatusInternalServerError) 140 return 141 } 142 w.Header().Set("Content-Type", "application/json") 143 w.WriteHeader(http.StatusOK) 144 w.Write(m) 145 } 146 147 func AddRoutes(r *mux.Router, b *Backend) { 148 v1 := r.PathPrefix("/v1").Subrouter() 149 v1.Path("/ports").Methods("GET").HandlerFunc(b.GetPorts) 150 v1.Path("/ports").Methods("POST").HandlerFunc(b.PostPort) 151 v1.Path("/ports/{id}").Methods("DELETE").HandlerFunc(b.DeletePort) 152 v1.Path("/info").Methods("GET").HandlerFunc(b.GetInfo) 153 }