client: Add crio client package

Signed-off-by: Mrunal Patel <mrunalp@gmail.com>
This commit is contained in:
Mrunal Patel 2017-09-22 14:56:10 -07:00
parent 4fadbea75d
commit 2e3d5240c2
6 changed files with 202 additions and 28 deletions

103
client/client.go Normal file
View file

@ -0,0 +1,103 @@
package client
import (
"encoding/json"
"fmt"
"net"
"net/http"
"syscall"
"time"
"github.com/kubernetes-incubator/cri-o/types"
)
const (
maxUnixSocketPathSize = len(syscall.RawSockaddrUnix{}.Path)
)
// CrioClient is an interface to get information from crio daemon endpoint.
type CrioClient interface {
DaemonInfo() (types.CrioInfo, error)
ContainerInfo(string) (*types.ContainerInfo, error)
}
type crioClientImpl struct {
client *http.Client
crioSocketPath string
}
func configureUnixTransport(tr *http.Transport, proto, addr string) error {
if len(addr) > maxUnixSocketPathSize {
return fmt.Errorf("Unix socket path %q is too long", addr)
}
// No need for compression in local communications.
tr.DisableCompression = true
tr.Dial = func(_, _ string) (net.Conn, error) {
return net.DialTimeout(proto, addr, 32*time.Second)
}
return nil
}
// New returns a crio client
func New(crioSocketPath string) (CrioClient, error) {
tr := new(http.Transport)
configureUnixTransport(tr, "unix", crioSocketPath)
c := &http.Client{
Transport: tr,
}
return &crioClientImpl{
client: c,
crioSocketPath: crioSocketPath,
}, nil
}
func (c *crioClientImpl) getRequest(path string) (*http.Request, error) {
req, err := http.NewRequest("GET", path, nil)
if err != nil {
return nil, err
}
// For local communications over a unix socket, it doesn't matter what
// the host is. We just need a valid and meaningful host name.
req.Host = "crio"
req.URL.Host = c.crioSocketPath
req.URL.Scheme = "http"
return req, nil
}
// DaemonInfo return cri-o daemon info from the cri-o
// info endpoint.
func (c *crioClientImpl) DaemonInfo() (types.CrioInfo, error) {
info := types.CrioInfo{}
req, err := c.getRequest("/info")
if err != nil {
return info, err
}
resp, err := c.client.Do(req)
if err != nil {
return info, err
}
defer resp.Body.Close()
if err := json.NewDecoder(resp.Body).Decode(&info); err != nil {
return info, err
}
return info, nil
}
// ContainerInfo returns container info by querying
// the cri-o container endpoint.
func (c *crioClientImpl) ContainerInfo(id string) (*types.ContainerInfo, error) {
req, err := c.getRequest("/containers/" + id)
if err != nil {
return nil, err
}
resp, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
cInfo := types.ContainerInfo{}
if err := json.NewDecoder(resp.Body).Decode(&cInfo); err != nil {
return nil, err
}
return &cInfo, nil
}

View file

@ -1,6 +1,7 @@
package main package main
import ( import (
"encoding/json"
"fmt" "fmt"
"log" "log"
"net/url" "net/url"
@ -8,6 +9,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/kubernetes-incubator/cri-o/client"
"github.com/urfave/cli" "github.com/urfave/cli"
"golang.org/x/net/context" "golang.org/x/net/context"
remocommandconsts "k8s.io/apimachinery/pkg/util/remotecommand" remocommandconsts "k8s.io/apimachinery/pkg/util/remotecommand"
@ -21,6 +23,7 @@ var containerCommand = cli.Command{
Aliases: []string{"ctr"}, Aliases: []string{"ctr"},
Subcommands: []cli.Command{ Subcommands: []cli.Command{
createContainerCommand, createContainerCommand,
inspectContainerCommand,
startContainerCommand, startContainerCommand,
stopContainerCommand, stopContainerCommand,
removeContainerCommand, removeContainerCommand,
@ -617,3 +620,37 @@ func ListContainers(client pb.RuntimeServiceClient, opts listOptions) error {
} }
return nil return nil
} }
var inspectContainerCommand = cli.Command{
Name: "inspect",
Usage: "get container info from crio daemon",
Flags: []cli.Flag{
cli.StringFlag{
Name: "id",
Value: "",
Usage: "id of the container",
},
},
Action: func(context *cli.Context) error {
ID := context.String("id")
if ID == "" {
return fmt.Errorf("ID cannot be empty")
}
c, err := client.New(context.GlobalString("connect"))
if err != nil {
return err
}
cInfo, err := c.ContainerInfo(ID)
if err != nil {
return err
}
jsonBytes, err := json.MarshalIndent(cInfo, "", " ")
if err != nil {
return err
}
fmt.Println(string(jsonBytes))
return nil
},
}

31
cmd/crioctl/info.go Normal file
View file

@ -0,0 +1,31 @@
package main
import (
"encoding/json"
"fmt"
"github.com/kubernetes-incubator/cri-o/client"
"github.com/urfave/cli"
)
var infoCommand = cli.Command{
Name: "info",
Usage: "get crio daemon info",
Action: func(context *cli.Context) error {
c, err := client.New(context.GlobalString("connect"))
if err != nil {
return err
}
di, err := c.DaemonInfo()
if err != nil {
return err
}
jsonBytes, err := json.MarshalIndent(di, "", " ")
if err != nil {
return err
}
fmt.Println(string(jsonBytes))
return nil
},
}

View file

@ -91,6 +91,7 @@ func main() {
containerCommand, containerCommand,
runtimeVersionCommand, runtimeVersionCommand,
imageCommand, imageCommand,
infoCommand,
} }
app.Flags = []cli.Flag{ app.Flags = []cli.Flag{

View file

@ -9,32 +9,12 @@ import (
"github.com/go-zoo/bone" "github.com/go-zoo/bone"
"github.com/kubernetes-incubator/cri-o/libkpod/sandbox" "github.com/kubernetes-incubator/cri-o/libkpod/sandbox"
"github.com/kubernetes-incubator/cri-o/oci" "github.com/kubernetes-incubator/cri-o/oci"
"github.com/kubernetes-incubator/cri-o/types"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
// ContainerInfo stores information about containers func (s *Server) getInfo() types.CrioInfo {
type ContainerInfo struct { return types.CrioInfo{
Name string `json:"name"`
Pid int `json:"pid"`
Image string `json:"image"`
CreatedTime int64 `json:"created_time"`
Labels map[string]string `json:"labels"`
Annotations map[string]string `json:"annotations"`
LogPath string `json:"log_path"`
Root string `json:"root"`
Sandbox string `json:"sandbox"`
IP string `json:"ip_address"`
}
// CrioInfo stores information about the crio daemon
type CrioInfo struct {
StorageDriver string `json:"storage_driver"`
StorageRoot string `json:"storage_root"`
CgroupDriver string `json:"cgroup_driver"`
}
func (s *Server) getInfo() CrioInfo {
return CrioInfo{
StorageDriver: s.config.Config.Storage, StorageDriver: s.config.Config.Storage,
StorageRoot: s.config.Config.Root, StorageRoot: s.config.Config.Root,
CgroupDriver: s.config.Config.CgroupManager, CgroupDriver: s.config.Config.CgroupManager,
@ -47,25 +27,25 @@ var (
errSandboxNotFound = errors.New("sandbox for container not found") errSandboxNotFound = errors.New("sandbox for container not found")
) )
func (s *Server) getContainerInfo(id string, getContainerFunc func(id string) *oci.Container, getInfraContainerFunc func(id string) *oci.Container, getSandboxFunc func(id string) *sandbox.Sandbox) (ContainerInfo, error) { func (s *Server) getContainerInfo(id string, getContainerFunc func(id string) *oci.Container, getInfraContainerFunc func(id string) *oci.Container, getSandboxFunc func(id string) *sandbox.Sandbox) (types.ContainerInfo, error) {
ctr := getContainerFunc(id) ctr := getContainerFunc(id)
if ctr == nil { if ctr == nil {
ctr = getInfraContainerFunc(id) ctr = getInfraContainerFunc(id)
if ctr == nil { if ctr == nil {
return ContainerInfo{}, errCtrNotFound return types.ContainerInfo{}, errCtrNotFound
} }
} }
// TODO(mrunalp): should we call UpdateStatus()? // TODO(mrunalp): should we call UpdateStatus()?
ctrState := ctr.State() ctrState := ctr.State()
if ctrState == nil { if ctrState == nil {
return ContainerInfo{}, errCtrStateNil return types.ContainerInfo{}, errCtrStateNil
} }
sb := getSandboxFunc(ctr.Sandbox()) sb := getSandboxFunc(ctr.Sandbox())
if sb == nil { if sb == nil {
logrus.Debugf("can't find sandbox %s for container %s", ctr.Sandbox(), id) logrus.Debugf("can't find sandbox %s for container %s", ctr.Sandbox(), id)
return ContainerInfo{}, errSandboxNotFound return types.ContainerInfo{}, errSandboxNotFound
} }
return ContainerInfo{ return types.ContainerInfo{
Name: ctr.Name(), Name: ctr.Name(),
Pid: ctrState.Pid, Pid: ctrState.Pid,
Image: ctr.Image(), Image: ctr.Image(),

22
types/types.go Normal file
View file

@ -0,0 +1,22 @@
package types
// ContainerInfo stores information about containers
type ContainerInfo struct {
Name string `json:"name"`
Pid int `json:"pid"`
Image string `json:"image"`
CreatedTime int64 `json:"created_time"`
Labels map[string]string `json:"labels"`
Annotations map[string]string `json:"annotations"`
LogPath string `json:"log_path"`
Root string `json:"root"`
Sandbox string `json:"sandbox"`
IP string `json:"ip_address"`
}
// CrioInfo stores information about the crio daemon
type CrioInfo struct {
StorageDriver string `json:"storage_driver"`
StorageRoot string `json:"storage_root"`
CgroupDriver string `json:"cgroup_driver"`
}