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  }