Files
fx-serverless/container_runtimes/docker/http/api.go

196 lines
4.2 KiB
Go

package api
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strconv"
"strings"
"time"
dockerTypes "github.com/docker/docker/api/types"
"github.com/google/go-querystring/query"
"github.com/metrue/fx/types"
"github.com/metrue/fx/utils"
)
// API interact with dockerd http api
type API struct {
endpoint string
version string
}
// Create a API
func Create(host string, port string) (*API, error) {
version, err := utils.DockerVersion(host, port)
if err != nil {
return nil, err
}
endpoint := fmt.Sprintf("http://%s:%s/v%s", host, port, version)
return &API{
endpoint: endpoint,
version: version,
}, nil
}
// MustCreate a api object, panic if not
func MustCreate(host string, port string) *API {
version, err := utils.DockerVersion(host, port)
if err != nil {
panic(err)
}
endpoint := fmt.Sprintf("http://%s:%s/v%s", host, port, version)
return &API{
endpoint: endpoint,
version: version,
}
}
func (api *API) get(path string, qs string, v interface{}) error {
url := fmt.Sprintf("%s%s", api.endpoint, path)
if !strings.HasPrefix(url, "http") {
url = "http://" + url
}
if qs != "" {
url += "?" + qs
}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return err
}
client := &http.Client{Timeout: 20 * time.Second}
resp, err := client.Do(req)
if err != nil {
return err
}
if resp.StatusCode != 200 {
return fmt.Errorf("request %s failed: %d - %s", url, resp.StatusCode, resp.Status)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
err = json.Unmarshal(body, &v)
if err != nil {
return err
}
return nil
}
func (api *API) post(path string, body []byte, expectStatus int, v interface{}) error {
url := fmt.Sprintf("%s%s", api.endpoint, path)
req, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")
client := &http.Client{Timeout: 20 * time.Second}
resp, err := client.Do(req)
if err != nil {
return err
}
if resp.StatusCode != expectStatus {
return fmt.Errorf("request %s (%s) failed: %d - %s", url, string(body), resp.StatusCode, resp.Status)
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
err = json.Unmarshal(b, &v)
if err != nil {
return err
}
return nil
}
// List list service
func (api *API) list(name string) ([]types.Service, error) {
if name != "" {
info, err := api.inspect(name)
if err != nil {
return []types.Service{}, err
}
port, err := strconv.Atoi(info.HostConfig.PortBindings["3000/tcp"][0].HostPort)
if err != nil {
return []types.Service{}, err
}
return []types.Service{
types.Service{
Name: name,
Image: info.Image,
State: info.State.Status,
ID: info.ID,
Host: info.HostConfig.PortBindings["3000/tcp"][0].HostIP,
Port: port,
},
}, nil
}
type filterItem struct {
Status []string `json:"url,omitempty"`
Label []string `json:"label,omitempty"`
Name []string `json:"name,omitempty"`
}
type Filters struct {
Items string `url:"filters"`
}
filter := filterItem{
// Status: []string{"running"},
Label: []string{"belong-to=fx"},
}
q, err := json.Marshal(filter)
if err != nil {
return []types.Service{}, err
}
filters := Filters{Items: string(q)}
qs, err := query.Values(filters)
if err != nil {
return []types.Service{}, err
}
var containers []dockerTypes.Container
if err := api.get("/containers/json", qs.Encode(), &containers); err != nil {
return []types.Service{}, err
}
svs := make(map[string]types.Service)
for _, container := range containers {
// container name have extra forward slash
// https://github.com/moby/moby/issues/6705
if strings.HasPrefix(container.Names[0], fmt.Sprintf("/%s", name)) {
svs[container.Image] = types.Service{
Name: container.Names[0],
Image: container.Image,
ID: container.ID,
Host: container.Ports[0].IP,
Port: int(container.Ports[0].PublicPort),
State: container.State,
}
}
}
services := []types.Service{}
for _, s := range svs {
services = append(services, s)
}
return services, nil
}