Skip to content

Commit

Permalink
Merge pull request #154 from mlycore/master
Browse files Browse the repository at this point in the history
fix esterwang's conflicts
  • Loading branch information
mlycore authored Jul 2, 2019
2 parents 5c058d2 + 3509b57 commit 222b4ee
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 109 deletions.
25 changes: 25 additions & 0 deletions posts/2019-04-01-caasone-daily-news.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
title: "技术日报(2019-04-01)"
date: 2019-04-01T00:00:00+08:00
categories: [ "daily"]
draft: false
---
### [容器时代]技术日报(20180922)

1. PostgreSQL on Kubernetes: How to run a stateful legacy app on a stateless microservice http://www.bmc.com/blogs/kubernetes-postgresql/
2. Securing a dockerized plumber API with SSL and Basic Authentication https://www.tuicool.com/articles/IrqQRbi
3. MicroK8s in the Wild https://blog.ubuntu.com/2019/03/28/microk8s-in-the-wild
4. Docker-06-持久化存储和数据共享 https://www.cnblogs.com/liuguangjiji/p/10630801.html
5. GoLang with Rails https://shwetakale.wordpress.com/2019/03/28/golang-with-rails/

编辑:@esterwang

地址:<http://k8s.today/>

[容器时代志愿编辑招募] <https://mp.weixin.qq.com/s/BVBzWx_u1xfwSZGTO-6qlg>

### 其他

1、欢迎将自己的博文转载到公众号进一步推广

2、想翻译文章的同学现在就可开始
234 changes: 125 additions & 109 deletions translation/Unit Testing with the Kubernetes Client Library.md
Original file line number Diff line number Diff line change
@@ -1,157 +1,173 @@
# 如何进行单元测试代码来调用Kubernetes API
使用Kubernetes客户端库进行单元测试
=============

使用kubernetes客户机库可以帮助您模拟出一个集群来测试您的代码。
如何对被 Kubernetes API 调用的代码进行单元测试?

在构建[kubernetes/minikube](https://github.com/kubernetes/minikube)时,作为[kubernetes/client-go ](https://github.com/kubernetes/client-go)库的第一个消费者之一,services、pods和deployments构建了详细的模拟,以单元测为例,现在,有一种更简单的方法可以用更少的代码行来做同样的事情。
使用 Kubernetes 客户端库可以通过模拟集群来测试代码。

我将演示如何测试一个简单的函数,该函数列出了集群中运行的所有容器镜像。你需要一个Kubernetes集群,我建议用GKE或桌面版的Docker
作为在构建 kubernetes / minikube 时,第一批使用kubernetes / client-go 库的用户之一,我曾经详尽的设计了服务、调度单元和部署来对我的代码进行单元测试。 现在,我发现有一种更简单的方法可以用更少的代码行来做同样的事情

## Setup
克隆示例存储库[https://github.com/r2d4/k8s-unit-test-example ](https://github.com/r2d4/k8s-unit-test-example ),如果要运行命令并以交互方式继续操作。
我将演示如何测试一个**列出了集群中运行的所有容器映像**的简单的函数。我建议你用 GKE 或 Docker 作为桌面,并且你还需要一个 Kubernetes 集群。

###main.go
### 安装程序

如果你想要运行命令并以交互方式继续操作,请克隆示例仓库 https://github.com/r2d4/k8s-unit-test-example

### main.go

```go
import (
"github.com/pkg/errors"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/typed/core/v1"
)
//ListImages返回在提供的命名空间中运行的容器映像的列表
func ListImages(client v1.CoreV1Interface, namespace string) ([]string, error) {
pl, err := client.Pods(namespace).List(meta_v1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "getting pods")
}
var images []string
for _, p := range pl.Items {
for _, c := range p.Spec.Containers {
images = append(images, c.Image)
}
package main

import (
"github.com/pkg/errors"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/typed/core/v1"
)

// ListImages returns a list of container images running in the provided namespace
func ListImages(client v1.CoreV1Interface, namespace string) ([]string, error) {
pl, err := client.Pods(namespace).List(meta_v1.ListOptions{})
if err != nil {
return nil, errors.Wrap(err, "getting pods")
}

var images []string
for _, p := range pl.Items {
for _, c := range p.Spec.Containers {
images = append(images, c.Image)
}
return images, nil
}
}

return images, nil
}
```


## Writing the Tests
从测试用例的定义开始,以及一些运行测试的框架代码。



### 编写测试

我们可以从测试用例的定义开始,运行一些测试的框架代码。

```go
func TestListImages(t *testing.T) {
var tests = []struct {
description string
namespace string
expected []string
objs []runtime.Object
var tests = []struct {
description string
namespace string
expected []string
objs []runtime.Object
}{
{"no pods", "", nil, nil},
}
{
{"no pods", "", nil, nil},
}
// 在这写需要测试的代码
}
```

## What's Happening

// Actual testing code goes here...
}

这种编写测试的风格称为“表驱动测试”,在Go中,这是首选的样式。实际的测试代码迭代表条目并执行必要的测试。测试代码只写一次,用于每种情况。需要注意一些事情:
```

* 用于保存测试用例定义的匿名结构。它们允许我们简洁地定义测试用例。
#### 发生了什么

这种编写测试的风格称为**“表驱动测试”**,是Go语言中首选的样式,实际测试代码会迭代表条目并执行必要的测试。测试代码只需要写一次,却可以适用于每种情况。我门可以注意到一些有趣的事情:

* 运行时对象切片objs将保存所有希望我们的模拟API服务器保存的运行时对象。将用一些pods填充,但是您可以在这里使用任何kubernetes对象
* 琐碎的测试用例,服务器上没有pods不应返回任何image
- 测试用例的定义是用匿名结构保存的,因此我们可以简洁的定义测试用例
- 对象切片运行时,`objs`将保存我们的模拟 API 服务器保存的所有正在运行的对象,此处我们选择用一些调度单元来填充它,但其实可以在这里使用任何 Kubernetes 对象。
- 如果测试用例非常琐碎,服务器上将没有调度单元,并且不返回任何图像

### 测试回路

## Test Loop
填写将为每个测试用例运行的实际测试代码。
让我们来为每个测试用例都写一段实际测试代码。

```go
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
client := fake.NewSimpleClientset(test.objs...)
actual, err := ListImages(client.CoreV1(), test.namespace)
if err != nil {
t.Errorf("Unexpected error: %s", err)
return
}
if diff := cmp.Diff(actual, test.expected); diff != "" {
t.Errorf("%T differ (-got, +want): %s", test.expected, diff)
return
}
})
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
client := fake.NewSimpleClientset(test.objs...)
actual, err := ListImages(client.CoreV1(), test.namespace)
if err != nil {
t.Errorf("Unexpected error: %s", err)
return
}
if diff := cmp.Diff(actual, test.expected); diff != "" {
t.Errorf("%T differ (-got, +want): %s", test.expected, diff)
return
}
})
}
```
需要注意的一些有趣的事情,
t.run运行执行子测试。为什么使用子测试?

* 可以使用-run flg to go test
在这里我们也可以注意到一些有趣的事情:

* 可以做设置和拆卸子测试是并行运行测试用例的入口(这里不做)
- 文件`t.Run` 的功能是执行子测试,那么为什么要使用子测试?

* 实际结果和预期结果与cmp.diff不同。diff返回两个值之间差异的人类可读报告。如果相同的输入值和选项的equal返回true,则返回空字符串
- - 你可以使用`-run` 命令运行特定的测试用例进行测试

fake.newsImpleClientSet返回将使用提供的对象响应的客户端集。 它由一个非常简单的对象跟踪器提供支持,该跟踪器按原样处理增加、更新和删除操作,不应用任何验证和/或默认值。
- 你可以设置和清理测试代码
- 子测试是同时运行测试用例的入口(这篇文章将不涉及)

- 实际结果和预期结果都与 `cmp.diff` 不同。Diff 返回的两个值之间差异是可读的,当且仅当输入值和选项相等且为真时,Diff返回空字符串。

## Test Cases
`fake.NewSimpleClientset` 返回一个使用提供的对象进行响应的客户端集。它由一个非常简单的对象跟踪器支持,该跟踪器按原样处理创建、更新和删除操作,而不应用任何验证和默认值。

  创建一个pod函数,它将帮助提供一些pod供我们测试。既然我们关心namespace和image,它基于这些参数创建新的pod.

```
func pod(namespace, image string) *v1.Pod {
return &v1.Pod{ObjectMeta: meta_v1.ObjectMeta{Namespace: namespace}, Spec: v1.PodSpec{Containers: []v1.Container{{Image: image}}}}
### 测试用例

我们来创建一个调度单元助手函数,它将帮助我们提供一些测试的调度单元。由于我们关心的是命名空间和镜像,所以我们可以创建一个可以基于这些参数创建新的调度单元的助手。

```go
func pod(namespace, image string) *v1.Pod {
return &v1.Pod{ObjectMeta: meta_v1.ObjectMeta{Namespace: namespace}, Spec: v1.PodSpec{Containers: []v1.Container{{Image: image}}}}
}
```

  写三个单元测试。如果使用特殊的namespace值列出所有namespace中的pods,第一个方法将确保获取所有image
  
```
我们来写三个单元测试。第一个方法能确保我门获取所有镜像,这时我们需要使用特殊的命名空间值 `“”` 来列出命名空间中所有的调度单元

```go
{"all namespaces", "", []string{"a", "b"}, []runtime.Object{pod("correct-namespace", "a"), pod("wrong-namespace", "b")}}
```

  第二种情况将确保按namespace正确筛选,忽略错误namespace中的pod。
```
第二种方法能确保我们按名称空间正确筛选,忽略调度单元中的 `wrong-namespace`

```go
{"filter namespace", "correct-namespace", []string{"a"}, []runtime.Object{pod("correct-namespace", "a"), pod("wrong-namespace", "b")}}
```

  第三种情况将确保如果所需的namespace中没有pods,则不会返回任何内容。
```
第三种方法能确保如果所需的命名空间中没有调度单元,则不会返回任何内容。

```go
{"wrong namespace", "correct-namespace", nil, []runtime.Object{pod("wrong-namespace", "b")}}
```
#### Putting it all together

把上面这些代码放在一起。

```go
func TestListImages(t *testing.T) {
var tests = []struct {
description string
namespace string
expected []string
objs []runtime.Object
func TestListImages(t *testing.T) {
var tests = []struct {
description string
namespace string
expected []string
objs []runtime.Object
}{
{"no pods", "", nil, nil},
{"all namespaces", "", []string{"a", "b"}, []runtime.Object{pod("correct-namespace", "a"), pod("wrong-namespace", "b")}},
{"filter namespace", "correct-namespace", []string{"a"}, []runtime.Object{pod("correct-namespace", "a"), pod("wrong-namespace", "b")}},
{"wrong namespace", "correct-namespace", nil, []runtime.Object{pod("wrong-namespace", "b")}},
}
{
{"no pods", "", nil, nil},
{"all namespaces", "", []string{"a", "b"},[]runtime.Object{pod("correct-namespace", "a"), pod("wrong-namespace", "b")}},
{"filter namespace", "correct-namespace", []string{"a"},[]runtime.Object{pod("correct-namespace", "a"), pod("wrong-namespace", "b")}},
{"wrong namespace", "correct-namespace", nil,[]runtime.Object{pod("wrong-namespace", "b")}},
}
for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
client := fake.NewSimpleClientset(test.objs...)
actual, err := ListImages(client.CoreV1(), test.namespace)
if err != nil {

for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
client := fake.NewSimpleClientset(test.objs...)
actual, err := ListImages(client.CoreV1(), test.namespace)
if err != nil {
t.Errorf("Unexpected error: %s", err)
return
}
if diff := cmp.Diff(actual, test.expected); diff != "" {
return
}
if diff := cmp.Diff(actual, test.expected); diff != "" {
t.Errorf("%T differ (-got, +want): %s", test.expected, diff)
return
return
}
})
}
}
})
}
}

```
Matt Rickard

[https://matt-rickard.com/kubernetes-unit-testing/](https://matt-rickard.com/kubernetes-unit-testing/)
### 原文链接
[原文作者:@mattrickard]
[原文链接:https://matt-rickard.com/kubernetes-unit-testing/](https://matt-rickard.com/kubernetes-unit-testing/)

0 comments on commit 222b4ee

Please sign in to comment.