跳到主要内容

第二十二课

PR #183

pkg/client: refactor tests

这个pr主要是对于冗余的测试代码进行重构,可以看到下面的核心代码,抽象了一个 TestClient 负责生成 fakeClient,然后通过validate来比较结果。

type Request struct {
Method string
Path string
Header string
Query url.Values
Body interface{}
RawBody *string
}

type Response struct {
StatusCode int
Body interface{}
RawBody *string
}

type TestClient struct {
*Client
Request Request
Response Response
Error bool
server *httptest.Server
handler *util.FakeHandler
Target interface{}
}

func (c *TestClient) Setup() *TestClient {
c.handler = &util.FakeHandler{
StatusCode: c.Response.StatusCode,
}
if responseBody := body(c.Response.Body, c.Response.RawBody); responseBody != nil {
c.handler.ResponseBody = *responseBody
}
c.server = httptest.NewTLSServer(c.handler)
if c.Client == nil {
c.Client = &Client{}
}
c.Client.Host = c.server.URL
return c
}

func (c *TestClient) Validate(t *testing.T, received interface{}, err error) {
defer c.server.Close()

if c.Error {
if err == nil {
t.Errorf("error expeced for %#v, got none", c.Request)
}
return
}
if err != nil {
t.Errorf("no error expected for %#v, got: %v", c.Request, err)
}

requestBody := body(c.Request.Body, c.Request.RawBody)
c.handler.ValidateRequest(t, makeUrl(c.Request.Path), c.Request.Method, requestBody)
if expected, received := c.Request.Query.Encode(), c.handler.RequestReceived.URL.Query().Encode(); expected != received {
t.Errorf("bad query for request %#v: expected %s, got %s", c.Request, expected, received)
}
if c.Request.Header != "" {
if c.handler.RequestReceived.Header.Get(c.Request.Header) == "" {
t.Errorf("header %q not found in request %#v", c.Request.Header, c.handler.RequestReceived)
}
}

if expected, received := requestBody, c.handler.RequestBody; expected != nil && *expected != received {
t.Errorf("bad body for request %#v: expected %s, got %s", c.Request, expected, received)
}

if c.Response.Body != nil && !reflect.DeepEqual(c.Response.Body, received) {
t.Errorf("bad response for request %#v: expeced %s, got %s", c.Request, c.Response.Body, received)
}
}

PR #174

Letting kubelet retrieve container stats from cAdvisor

kubelet 通过 cAvisor 来获取 容器的状态 , 在 kubeletServer 上添加一个api ,当用户请求 /containerStats?containerID=22 的时候,返回cpu,内存占用百分比。

下面的是 核心代码:

func (kl *Kubelet) GetContainerStats(name string) (*api.ContainerStats, error) {
if kl.CadvisorClient == nil {
return nil, nil
}
id, found, err := kl.GetContainerID(name)
if err != nil || !found {
return nil, err
}

info, err := kl.CadvisorClient.ContainerInfo(fmt.Sprintf("/docker/%v", id))

if err != nil {
return nil, err
}
// When the stats data for the container is not available yet.
if info.StatsPercentiles == nil {
return nil, nil
}

ret := new(api.ContainerStats)
ret.MaxMemoryUsage = info.StatsPercentiles.MaxMemoryUsage
if len(info.StatsPercentiles.CpuUsagePercentiles) > 0 {
percentiles := make([]api.Percentile, len(info.StatsPercentiles.CpuUsagePercentiles))
for i, p := range info.StatsPercentiles.CpuUsagePercentiles {
percentiles[i].Percentage = p.Percentage
percentiles[i].Value = p.Value
}
ret.CpuUsagePercentiles = percentiles
}
if len(info.StatsPercentiles.MemoryUsagePercentiles) > 0 {
percentiles := make([]api.Percentile, len(info.StatsPercentiles.MemoryUsagePercentiles))
for i, p := range info.StatsPercentiles.MemoryUsagePercentiles {
percentiles[i].Percentage = p.Percentage
percentiles[i].Value = p.Value
}
ret.MemoryUsagePercentiles = percentiles
}
return ret, nil
}

PR #182

Update IP assignment to be per-pod, not per-container 【每个 pod 分配一个 ip】

根据 Kubernetes 设计,IP 应该在 pod 内的容器之间共享。但是,要实现此目的,必须指示 Docker 在启动容器引用时使用 container 网络。 Kubulet 代码中似乎缺少此配置: 因此,看起来 IP 将根据指定给 Docker 守护进程参数的外部网桥分配给每个容器。

因此我们在同步pod时候,先创建一个 network container ,所有容器共享一个网络 ip。

我们可以看到图中的代码逻辑,如果没有 共享的 network container ,k8s 会创建一个 名叫 “k8snet” 的基础网络容器 ,代码如下:

func (kl *Kubelet) createNetworkContainer(manifest *api.ContainerManifest) (string, error) {
var ports []api.Port
// Docker only exports ports from the network container. Let's
// collect all of the relevant ports and export them.
for _, container := range manifest.Containers {
ports = append(ports, container.Ports...)
}
container := &api.Container{
Name: networkContainerName,
Image: "busybox",
Command: []string{"sh", "-c", "rm -f nap && mkfifo nap && exec cat nap"},
Ports: ports,
}
kl.pullImage("busybox")
return kl.RunContainer(manifest, container, "")
}

PR # 173

Add config dir support to kubelet [kubelet 支持监听文件夹]