Merge pull request #943 from mrunalp/info_client

client: Add crio client package
This commit is contained in:
Antonio Murdaca 2017-09-25 23:04:04 +02:00 committed by GitHub
commit a8ee86b1cc
12 changed files with 282 additions and 84 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

@ -384,7 +384,7 @@ func (c *ContainerServer) LoadSandbox(id string) error {
return err return err
} }
scontainer, err := oci.NewContainer(m.Annotations[annotations.ContainerID], cname, sandboxPath, m.Annotations[annotations.LogPath], sb.NetNs(), labels, kubeAnnotations, "", "", "", nil, id, false, false, false, privileged, trusted, sandboxDir, created, m.Annotations["org.opencontainers.image.stopSignal"]) scontainer, err := oci.NewContainer(m.Annotations[annotations.ContainerID], cname, sandboxPath, m.Annotations[annotations.LogPath], sb.NetNs(), labels, m.Annotations, kubeAnnotations, "", "", "", nil, id, false, false, false, privileged, trusted, sandboxDir, created, m.Annotations["org.opencontainers.image.stopSignal"])
if err != nil { if err != nil {
return err return err
} }
@ -507,7 +507,7 @@ func (c *ContainerServer) LoadContainer(id string) error {
return err return err
} }
ctr, err := oci.NewContainer(id, name, containerPath, m.Annotations[annotations.LogPath], sb.NetNs(), labels, kubeAnnotations, img, imgName, imgRef, &metadata, sb.ID(), tty, stdin, stdinOnce, sb.Privileged(), sb.Trusted(), containerDir, created, m.Annotations["org.opencontainers.image.stopSignal"]) ctr, err := oci.NewContainer(id, name, containerPath, m.Annotations[annotations.LogPath], sb.NetNs(), labels, m.Annotations, kubeAnnotations, img, imgName, imgRef, &metadata, sb.ID(), tty, stdin, stdinOnce, sb.Privileged(), sb.Trusted(), containerDir, created, m.Annotations["org.opencontainers.image.stopSignal"])
if err != nil { if err != nil {
return err return err
} }

View file

@ -22,22 +22,23 @@ const (
// Container represents a runtime container. // Container represents a runtime container.
type Container struct { type Container struct {
id string id string
name string name string
logPath string logPath string
labels fields.Set labels fields.Set
annotations fields.Set annotations fields.Set
image string crioAnnotations fields.Set
sandbox string image string
netns ns.NetNS sandbox string
terminal bool netns ns.NetNS
stdin bool terminal bool
stdinOnce bool stdin bool
privileged bool stdinOnce bool
trusted bool privileged bool
state *ContainerState trusted bool
metadata *pb.ContainerMetadata state *ContainerState
opLock sync.Locker metadata *pb.ContainerMetadata
opLock sync.Locker
// this is the /var/run/storage/... directory, erased on reboot // this is the /var/run/storage/... directory, erased on reboot
bundlePath string bundlePath string
// this is the /var/lib/storage/... directory // this is the /var/lib/storage/... directory
@ -68,31 +69,32 @@ type ContainerState struct {
} }
// NewContainer creates a container object. // NewContainer creates a container object.
func NewContainer(id string, name string, bundlePath string, logPath string, netns ns.NetNS, labels map[string]string, annotations map[string]string, image string, imageName string, imageRef string, metadata *pb.ContainerMetadata, sandbox string, terminal bool, stdin bool, stdinOnce bool, privileged bool, trusted bool, dir string, created time.Time, stopSignal string) (*Container, error) { func NewContainer(id string, name string, bundlePath string, logPath string, netns ns.NetNS, labels map[string]string, crioAnnotations map[string]string, annotations map[string]string, image string, imageName string, imageRef string, metadata *pb.ContainerMetadata, sandbox string, terminal bool, stdin bool, stdinOnce bool, privileged bool, trusted bool, dir string, created time.Time, stopSignal string) (*Container, error) {
state := &ContainerState{} state := &ContainerState{}
state.Created = created state.Created = created
c := &Container{ c := &Container{
id: id, id: id,
name: name, name: name,
bundlePath: bundlePath, bundlePath: bundlePath,
logPath: logPath, logPath: logPath,
labels: labels, labels: labels,
sandbox: sandbox, sandbox: sandbox,
netns: netns, netns: netns,
terminal: terminal, terminal: terminal,
stdin: stdin, stdin: stdin,
stdinOnce: stdinOnce, stdinOnce: stdinOnce,
privileged: privileged, privileged: privileged,
trusted: trusted, trusted: trusted,
metadata: metadata, metadata: metadata,
annotations: annotations, annotations: annotations,
image: image, crioAnnotations: crioAnnotations,
imageName: imageName, image: image,
imageRef: imageRef, imageName: imageName,
dir: dir, imageRef: imageRef,
state: state, dir: dir,
stopSignal: stopSignal, state: state,
opLock: new(sync.Mutex), stopSignal: stopSignal,
opLock: new(sync.Mutex),
} }
return c, nil return c, nil
} }
@ -163,6 +165,11 @@ func (c *Container) Annotations() map[string]string {
return c.annotations return c.annotations
} }
// CrioAnnotations returns the crio annotations of the container.
func (c *Container) CrioAnnotations() map[string]string {
return c.crioAnnotations
}
// Image returns the image of the container. // Image returns the image of the container.
func (c *Container) Image() string { func (c *Container) Image() string {
return c.image return c.image

View file

@ -862,6 +862,7 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
specgen.AddAnnotation(annotations.Stdin, fmt.Sprintf("%v", containerConfig.Stdin)) specgen.AddAnnotation(annotations.Stdin, fmt.Sprintf("%v", containerConfig.Stdin))
specgen.AddAnnotation(annotations.StdinOnce, fmt.Sprintf("%v", containerConfig.StdinOnce)) specgen.AddAnnotation(annotations.StdinOnce, fmt.Sprintf("%v", containerConfig.StdinOnce))
specgen.AddAnnotation(annotations.Image, image) specgen.AddAnnotation(annotations.Image, image)
specgen.AddAnnotation(annotations.ResolvPath, sb.InfraContainer().CrioAnnotations()[annotations.ResolvPath])
created := time.Now() created := time.Now()
specgen.AddAnnotation(annotations.Created, created.Format(time.RFC3339Nano)) specgen.AddAnnotation(annotations.Created, created.Format(time.RFC3339Nano))
@ -1000,7 +1001,9 @@ func (s *Server) createSandboxContainer(ctx context.Context, containerID string,
return nil, err return nil, err
} }
container, err := oci.NewContainer(containerID, containerName, containerInfo.RunDir, logPath, sb.NetNs(), labels, kubeAnnotations, image, imageName, imageRef, metadata, sb.ID(), containerConfig.Tty, containerConfig.Stdin, containerConfig.StdinOnce, sb.Privileged(), sb.Trusted(), containerInfo.Dir, created, containerImageConfig.Config.StopSignal) crioAnnotations := specgen.Spec().Annotations
container, err := oci.NewContainer(containerID, containerName, containerInfo.RunDir, logPath, sb.NetNs(), labels, crioAnnotations, kubeAnnotations, image, imageName, imageRef, metadata, sb.ID(), containerConfig.Tty, containerConfig.Stdin, containerConfig.StdinOnce, sb.Privileged(), sb.Trusted(), containerInfo.Dir, created, containerImageConfig.Config.StopSignal)
if err != nil { if err != nil {
return nil, err return nil, err
} }

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,35 +27,36 @@ 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(),
CreatedTime: ctrState.Created.UnixNano(), CreatedTime: ctrState.Created.UnixNano(),
Labels: ctr.Labels(), Labels: ctr.Labels(),
Annotations: ctr.Annotations(), Annotations: ctr.Annotations(),
Root: ctr.MountPoint(), CrioAnnotations: ctr.CrioAnnotations(),
LogPath: ctr.LogPath(), Root: ctr.MountPoint(),
Sandbox: ctr.Sandbox(), LogPath: ctr.LogPath(),
IP: sb.IP(), Sandbox: ctr.Sandbox(),
IP: sb.IP(),
}, nil }, nil
} }

View file

@ -67,7 +67,7 @@ func TestGetContainerInfo(t *testing.T) {
"io.kubernetes.test1": "value1", "io.kubernetes.test1": "value1",
} }
getContainerFunc := func(id string) *oci.Container { getContainerFunc := func(id string) *oci.Container {
container, err := oci.NewContainer("testid", "testname", "", "/container/logs", mockNetNS{}, labels, annotations, "imageName", "imageName", "imageRef", &runtime.ContainerMetadata{}, "testsandboxid", false, false, false, false, false, "/root/for/container", created, "SIGKILL") container, err := oci.NewContainer("testid", "testname", "", "/container/logs", mockNetNS{}, labels, annotations, annotations, "imageName", "imageName", "imageRef", &runtime.ContainerMetadata{}, "testsandboxid", false, false, false, false, false, "/root/for/container", created, "SIGKILL")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -181,7 +181,7 @@ func TestGetContainerInfoCtrStateNil(t *testing.T) {
labels := map[string]string{} labels := map[string]string{}
annotations := map[string]string{} annotations := map[string]string{}
getContainerFunc := func(id string) *oci.Container { getContainerFunc := func(id string) *oci.Container {
container, err := oci.NewContainer("testid", "testname", "", "/container/logs", mockNetNS{}, labels, annotations, "imageName", "imageName", "imageRef", &runtime.ContainerMetadata{}, "testsandboxid", false, false, false, false, false, "/root/for/container", created, "SIGKILL") container, err := oci.NewContainer("testid", "testname", "", "/container/logs", mockNetNS{}, labels, annotations, annotations, "imageName", "imageName", "imageRef", &runtime.ContainerMetadata{}, "testsandboxid", false, false, false, false, false, "/root/for/container", created, "SIGKILL")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -212,7 +212,7 @@ func TestGetContainerInfoSandboxNotFound(t *testing.T) {
labels := map[string]string{} labels := map[string]string{}
annotations := map[string]string{} annotations := map[string]string{}
getContainerFunc := func(id string) *oci.Container { getContainerFunc := func(id string) *oci.Container {
container, err := oci.NewContainer("testid", "testname", "", "/container/logs", mockNetNS{}, labels, annotations, "imageName", "imageName", "imageRef", &runtime.ContainerMetadata{}, "testsandboxid", false, false, false, false, false, "/root/for/container", created, "SIGKILL") container, err := oci.NewContainer("testid", "testname", "", "/container/logs", mockNetNS{}, labels, annotations, annotations, "imageName", "imageName", "imageRef", &runtime.ContainerMetadata{}, "testsandboxid", false, false, false, false, false, "/root/for/container", created, "SIGKILL")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -472,7 +472,7 @@ func (s *Server) RunPodSandbox(ctx context.Context, req *pb.RunPodSandboxRequest
g.AddAnnotation(annotations.HostnamePath, hostnamePath) g.AddAnnotation(annotations.HostnamePath, hostnamePath)
sb.AddHostnamePath(hostnamePath) sb.AddHostnamePath(hostnamePath)
container, err := oci.NewContainer(id, containerName, podContainer.RunDir, logPath, sb.NetNs(), labels, kubeAnnotations, "", "", "", nil, id, false, false, false, sb.Privileged(), sb.Trusted(), podContainer.Dir, created, podContainer.Config.Config.StopSignal) container, err := oci.NewContainer(id, containerName, podContainer.RunDir, logPath, sb.NetNs(), labels, g.Spec().Annotations, kubeAnnotations, "", "", "", nil, id, false, false, false, sb.Privileged(), sb.Trusted(), podContainer.Dir, created, podContainer.Config.Config.StopSignal)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -12,6 +12,11 @@ function teardown() {
echo "$out" echo "$out"
[[ "$out" =~ "\"cgroup_driver\":\"$CGROUP_MANAGER\"" ]] [[ "$out" =~ "\"cgroup_driver\":\"$CGROUP_MANAGER\"" ]]
[[ "$out" =~ "\"storage_root\":\"$TESTDIR/crio\"" ]] [[ "$out" =~ "\"storage_root\":\"$TESTDIR/crio\"" ]]
run crioctl info
echo "$output"
[ "$status" -eq 0 ]
[[ "$output" =~ "\"cgroup_driver\": \"$CGROUP_MANAGER\"" ]]
[[ "$output" =~ "\"storage_root\": \"$TESTDIR/crio\"" ]]
stop_crio stop_crio
} }
@ -32,13 +37,20 @@ function teardown() {
[[ "$out" =~ "\"sandbox\":\"$pod_id\"" ]] [[ "$out" =~ "\"sandbox\":\"$pod_id\"" ]]
[[ "$out" =~ "\"image\":\"redis:alpine\"" ]] [[ "$out" =~ "\"image\":\"redis:alpine\"" ]]
run crioctl ctr inspect --id $ctr_id
echo "$output"
[ "$status" -eq 0 ]
[[ "$output" =~ "\"sandbox\": \"$pod_id\"" ]]
[[ "$output" =~ "\"image\": \"redis:alpine\"" ]]
inet=`crioctl ctr execsync --id $ctr_id ip addr show dev eth0 scope global 2>&1 | grep inet` inet=`crioctl ctr execsync --id $ctr_id ip addr show dev eth0 scope global 2>&1 | grep inet`
IFS=" " IFS=" "
ip=`parse_pod_ip $inet` ip=`parse_pod_ip $inet`
[[ "$out" =~ "\"ip_address\":\"$ip\"" ]] [[ "$out" =~ "\"ip_address\":\"$ip\"" ]]
[[ "$out" =~ "\"name\":\"k8s_container1_podsandbox1_redhat.test.crio_redhat-test-crio_1\"" ]] [[ "$out" =~ "\"name\":\"k8s_container1_podsandbox1_redhat.test.crio_redhat-test-crio_1\"" ]]
[[ "$output" =~ "\"ip_address\": \"$ip\"" ]]
[[ "$output" =~ "\"name\": \"k8s_container1_podsandbox1_redhat.test.crio_redhat-test-crio_1\"" ]]
# TODO: add some other check based on the json below: # TODO: add some other check based on the json below:

23
types/types.go Normal file
View file

@ -0,0 +1,23 @@
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"`
CrioAnnotations map[string]string `json:"crio_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"`
}