> For the complete documentation index, see [llms.txt](https://renhongcai.gitbook.io/kubernetes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://renhongcai.gitbook.io/kubernetes/di-shi-liu-zhang-api-she-ji-yue-ding/1.2-api_convention_condition.md).

# 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`）。
