# 16.2 condition设计约定

## 导读

绝大部分`Kubernetes`资源对象都包含`status.conditions`字段，用来表示资源状态，比如`deployment`资源中的`status.conditions`如下所示：

```
  conditions:
  - lastTransitionTime: "2020-12-15T01:52:53Z"
    lastUpdateTime: "2020-12-15T01:52:53Z"
    message: Deployment has minimum availability.
    reason: MinimumReplicasAvailable
    status: "True"
    type: Available
  - lastTransitionTime: "2020-12-15T01:52:39Z"
    lastUpdateTime: "2020-12-15T01:52:53Z"
    message: ReplicaSet "nginx-deployment-85b45874d9" has successfully progressed.
    reason: NewReplicaSetAvailable
    status: "True"
    type: Progressing
```

以上`conditions`的信息是很容易读懂的（后面还会详细解释），然而有个问题曾经令笔者非常困惑，那就是：

* `conditions`和`status`到底有什么区别？
* `conditions`的设计原则是什么？在设计API扩展时，该如何定义`conditions`？

本节会先从`conditions`的设计原则讲起，再介绍一些设计`conditions`时的一些经验总结。

## conditions设计原则

`conditions`寄居于资源对象的`status`字段中，与`status`其他字段值一样都用来表示资源的状态。不过`conditions`的设计初衷是提供一种通用的数据结构表示资源的状态，它通常能够提供比`status`其他字段更详细的信息（比如状态切换时间、更新时间），`conditions`实际上是一种扩展机制，外部监控程序可以根据`conditions`无差别地监控各种资源的状态，而不必过分关注资源对象`status`中的其他信息。

通常`status.conditions`字段类型为切片，切片中的每个元素表示资源的某个状态，该状态由特定的控制器更新，比如`Deployment`控制器会更新`deployment`对象的`status.conditions`信息。`conditions`作为扩展机制，它还支持第三方控制器增加新的状态。通常`status.conditions`中的信息由控制器根据资源的`status`其他字段计算出来。

## condition字段内容

通常一个`condition`必须包含`type`（状态类型）和`status`（状态值）两个信息。在`Kubernetes` v1.19版之前，关于`condition`并没有统一的标准，导致众多API都自行定义了`condition`。比如：

* Deployment使用的Condition为`type DeploymentCondition struct`;&#x20;
* Pod使用的Condition为`type PodCondition struct`;&#x20;

庆幸的是，在`Kubernetes`v1.19版本社区提供了一个标准的`condition`类型定义，由于兼容性考虑，`Kubernetes`既有的API不一定能采用这一标准类型，但对于后续新增的API将会使用这一标准类型，并且笔者建议用户设计的CRD扩展也应使用这一标准类型。

标准的`condition`类型定义如下所示：

```go
type ConditionStatus string

const (
    ConditionTrue    ConditionStatus = "True"
    ConditionFalse   ConditionStatus = "False"
    ConditionUnknown ConditionStatus = "Unknown"
)

type Condition struct {
    // 类型（使用驼峰风格），如”Available“。
    Type string
    // 状态（枚举值：”True“、”False“和”Unknown“）。
    Status ConditionStatus
    // 观察到的generation。
    // 如果ObservedGeneration 比metada.generation小，说明不是最新状态。
    // +optional
    ObservedGeneration int64
    // 上次变化时间
    LastTransitionTime Time
    // 状态变化原因（使用驼峰风格），如”NewReplicaSetAvailable“
    Reason string
    // 描述信息，如”Deployment has minimum availability“
    Message string `json:"message" protobuf:"bytes,6,opt,name=message"`
}
```

标准的`condition`类型定义对`condition`的各个字段做了进一步约定。除了`ObservedGeneration`是可选的以外，其他字段都是必填的。

## condition设计约定

为了让`condition`起到最大的作用，需要遵守一定的设计约定：

### 约定一：condition定义要有明确的信息

每个`condition`需要传递一个用户关心的明确信息，用户读取到该信息不需要再根据资源的其他状态来揣测和计算。

### 约定二：condition需要保持兼容

`condition`一旦被定义，它就成了API中的一部分，需要跟API一样提供稳定性保证。如果需要新的`condition`仍然可以增加（没有破坏兼容性），但既有的`condition`是否能够改变就需要根据API成熟度来决定，比如`stable`的API不可以改变，`alpha`的API可以改变。

## 约定三：condition需要控制器第一次处理资源时更新

控制器需要尽快地更新`condition`状态值（`condition.status`），即便该状态值为`Unknown`，这么做的好处是可以让其他组件了解到控制器正在`调谐`这个资源。

然而，并不是所有的控制器都能遵守这个约定，即控制器并不会报告特定的`condition`（此时该`condition`状态值可能为`Unknown`），可能该`condition`还无法确定，需要在下一次`调谐`时决定。此种情况下，外部组件无法读取到特定的`condition`，可以假设该`condition`为`Unknown`。

## 约定四：condition类型名需要确保可读性

`condition`类型名要使用人类可读的名称，而且尽量避免出现双重否定的语境。例如，类型名`Ready`比使用`Failed`要好，因为`condition`状态为`False`时，`Failed = False`将出现双重否定，不如`Ready = False`容易理解。

## 约定五：condition不要定义成状态机

`condition`需要描述当前资源的确定状态，而不是当前资源状态机中的状态。通俗地讲，`condition`类型需要使用形容词（如`Ready`）或过去动词（`Succeeded`），而不是使用当前运行时（如`Deploying`）。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://renhongcai.gitbook.io/kubernetes/di-shi-liu-zhang-api-she-ji-yue-ding/1.2-api_convention_condition.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
