跳到主要内容

46.hook

PR #1156

问题描述:

在配置容器生命周期时候,我们有 LifeCycle 和 postStart 字段,但是我们一直没有用,这个pr实现了 kubelet 创建容器后能够做些 hook 操作。

解决方法:

  1. 修改了4个文件

  1. 修改 kubelet.go 文件在runContainer函数中判断是否设置了 hook ,如果设置了,执行该hook

  1. 增加 handler.go 实现2个 handler 接口,分别是 exec 和 http hander,这里我们来看下 http handler

在 上图代码中,我们会调用 ResolvePort 来匹配端口,这里来复习下这段代码:

// ResolvePort attempts to turn a IntOrString port reference into a concrete port number.
// If portReference has an int value, it is treated as a literal, and simply returns that value.
// If portReference is a string, an attempt is first made to parse it as an integer. If that fails,
// an attempt is made to find a port with the same name in the container spec.
// If a port with the same name is found, it's ContainerPort value is returned. If no matching
// port is found, an error is returned.
func ResolvePort(portReference util.IntOrString, container *api.Container) (int, error) {
if portReference.Kind == util.IntstrInt {
return portReference.IntVal, nil
} else {
portName := portReference.StrVal
port, err := strconv.Atoi(portName)
if err == nil {
return port, nil
}
for _, portSpec := range container.Ports {
if portSpec.Name == portName {
return portSpec.ContainerPort, nil
}
}

}
return -1, fmt.Errorf("couldn't find port: %v in %v", portReference, container)
}

这段代码是Go语言编写的,它是一个名为ResolvePort的函数,用于将一个整数或字符串形式的端口引用解析为一个具体的端口号。函数接受两个参数:portReference 和 container,并返回一个整数和一个错误对象。

  • portReference 是一个 IntOrString 类型的变量,它可以是整数或字符串。这种类型通常用于 Kubernetes 的 API 中,允许用户以整数或字符串的形式提供值。

  • container 是一个指向 api.Container 类型的指针,它包含容器的规范,包括容器暴露的端口。

函数的工作流程如下:

a. 首先检查 portReference 的类型。如果它是整数 (portReference.Kind == util.IntstrInt),那么直接返回这个整数值。在这种情况下,错误对象为 nil。

b. 如果 portReference 是一个字符串,函数首先尝试将它解析为一个整数(通过 strconv.Atoi(portName))。如果解析成功,返回解析出的整数值和 nil 错误对象。

c. 如果字符串不能被解析为整数,函数会遍历容器规范中定义的所有端口。对于每一个端口,它检查端口的名称是否与 portReference 的字符串值相匹配。如果找到匹配的端口,返回该端口的 ContainerPort 值和 nil 错误对象。

d. 如果 portReference 既不是一个有效的整数,也没有在容器规范中找到匹配的端口名称,函数返回 -1 作为端口号,并返回一个包含错误信息的错误对象。

总的来说,ResolvePort 函数是用来解析可能是整数或字符串的端口引用,并尝试将其转换为一个具体的端口号。如果无法转换,它会返回一个错误。

PR #1202

Please expedite: the rarely attempted interface{} -> runtime.Object rename

问题描述:

问题一:

目前代码中有些地方还是用 interface{} 来接收对象,替换成 runtime.Object 。runtime.Object 是一个接口,此接口包含一个名为 IsAnAPIObject 的方法,但是请注意,这个方法实际上没有任何参数或返回值,并且代码中的注释提到它“仅用于强制成员资格,从不被调用”。这个接口的主要目的是作为 Kubernetes API 对象的一个标记接口。在 Go 语言中,接口通常用于定义一个类型应具备的行为。然而,在某些情况下,接口可以不包含任何实际的行为,而仅用作类型的标记,这就是所谓的“标记接口”。在这个特定的情况下,Object 接口的作用是确保所有 Kubernetes API 类型都实现这个接口。通过这样做,可以在编译时利用 Go 编译器来检查一个对象是否符合预期的 API 对象标准。如果一个类型没有实现 IsAnAPIObject 方法,那么它将不能通过编译时的类型检查,从而表示它不是一个有效的 Kubernetes API 对象。总的来说,Object 接口是一个约定,它强制 Kubernetes API 对象类型遵循一个共同的标准,尽管这个标准在实际行为上没有任何意义。这有助于保持代码的清晰度和一致性,并且可以在不需要运行时开销的情况下进行静态类型检查。

问题二:

注册转换函数的时候,把 Scope 传入,这里我们来复习下 Convert 以及 Scope 在转换时候的作用。

// Scope is passed to conversion funcs to allow them to continue an ongoing conversion.
// If multiple converters exist in the system, Scope will allow you to use the correct one
// from a conversion function--that is, the one your conversion function was called by.
type Scope interface {
// Call Convert to convert sub-objects. Note that if you call it with your own exact
// parameters, you'll run out of stack space before anything useful happens.
Convert(src, dest interface{}, flags FieldMatchingFlags) error
}

  1. Register方法接收一个参数,名为conversionFunc,它应该是一个函数。

  2. 使用reflect包来检查conversionFunc的类型。首先,它检查conversionFunc是否是一个函数。如果不是,它将返回一个错误。

  3. 然后,它检查这个函数是否有三个输入参数和一个输出参数。如果不符合这个条件,它会返回一个错误。

  4. 接下来,它检查前两个输入参数是否是指针类型。如果不是,它返回一个错误。

  5. 然后,它检查第三个输入参数是否是conversion.Scope类型。

  6. 它还检查输出参数是否是错误类型。如果不是,它返回一个错误。

  7. 如果所有检查都通过,那么它将conversionFunc函数存储在c.funcs映射中,以输入类型作为键。

我们可以看到Scope应该在递归转换调用需要的时候使用。这意味着,如果转换函数在执行转换时需要再次调用其他转换函数(例如,如果你正在转换一个结构体,该结构体的字段也需要转换),那么你应该使用Scope来进行这些递归调用。这通常是为了保持上下文或状态,在递归调用链中共享信息。

当我们处理嵌套的数据结构时,我们可能需要将一个对象及其子对象从一种类型转换为另一种类型。在这种情况下,Scope 是有用的,因为它允许我们在转换过程中保持上下文,并确保我们使用正确的转换逻辑。

让我们通过一个简单的例子来理解这一点:

假设我们有两个版本的 API - v1 和 v2。我们有一个Person结构体,它在v1中定义为:

package v1

type Person struct {
Name string
Age int
// Address is a sub-object
Address Address
}

type Address struct {
Street string
City string
}

在v2中,我们对Person结构体做了一些更改:

package v2

type Person struct {
FullName string
Age int
// Address is a sub-object
Address Address
}

type Address struct {
Street string
City string
PostCode string
}

我们可能需要一个转换函数来将v1.Person转换为v2.Person。注意,Address也发生了变化,因此我们还需要一个转换函数来处理Address。

func Convert_v1_Person_to_v2_Person(in *v1.Person, out *v2.Person, s Scope) error {
// Convert the Name
out.FullName = in.Name

// Convert the Age
out.Age = in.Age

// Convert the Address using the scope
return s.Convert(&in.Address, &out.Address, 0)
}

func Convert_v1_Address_to_v2_Address(in *v1.Address, out *v2.Address, s Scope) error {
// Convert the Street and City
out.Street = in.Street
out.City = in.City

// PostCode is new in v2, leave it empty
out.PostCode = ""

return nil
}

在上面的示例中,当我们转换Person对象时,我们也需要转换其子对象Address。我们使用Scope的Convert方法来执行这个嵌套的转换。这使我们能够重用已经存在的转换逻辑,并确保我们在转换过程中使用正确的转换器。

问题3

将 codec 和 resourceVersion 添加 Default 前缀,方便开发者调用时候知道这是个包级别默认变量。

PR #1160

Support configurations for cloudproviders

问题:

每个云厂商应该都有自己的配置文件,我们的k8s 云厂商工厂方法应该提供 ioStreaming,使得云厂商能够读取配置来加载自身。

// Factory is a function that returns a cloudprovider.Interface.
// The config parameter provides an io.Reader handler to the factory in
// order to load specific configurations. If no configuration is provided
// the parameter is nil.
type Factory func(config io.Reader) (Interface, error)

PR #12217

Rename all XStorage types to REST for clarity

问题:

Using the name "Storage" is very misleading as these objects don't store anything, they pass through to the registry layer.

解决办法:

将 NewRegistry 构造函数改名为 NewRest: