Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 63 additions & 29 deletions docs/developer-manual/first-commiter.md
Original file line number Diff line number Diff line change
@@ -1,84 +1,118 @@
---
title: First Contribution
---

# 第一次贡献
If this is your first time contributing on GitHub, the steps below will get you started. For the
conventions this project expects — branch names, commit messages, what to run before opening a pull
request — see [Collaboration Guide](./collaboration.md) and
[Development Guideline](./develop-guideline.md).

如果你是第一次在 Github 上贡献代码,请参考如下步骤快速开始:
## Fork the project

## Fork 项目

- 首先需要fork这个项目, 进入项目页面, 点击右上角的Fork按钮
- 你的 github 帐号中会出现相应名称的项目, 例如: `<your_name>/docs`
- 在本地电脑(Linux)上使用以下命令克隆项目到本地
- Open the project page and click **Fork** in the top right
- A copy appears under your account, for example `<your_name>/docs`
- Clone it locally:

```bash
git clone https://github.com/<your_name>/docs
```

## 获取最新源代码
## Track the upstream repository

将本地个人仓库和上游仓库关联
Link your local clone to the upstream repository:

```bash
git remote add upstream https://github.com/kubedoop.dev/docs
git remote add upstream https://github.com/zncdatadev/docs
```

同步最新源代码
Then sync:

```bash
git pull upstream main
```

现在我们在 fork 来的 `main` 分支上, 这个 `main` 留作跟踪 `upstream` 的远程代码
You are now on the `main` branch of your fork, which is kept for tracking `upstream`. Do not commit
to it directly.

## 创建分支
## Create a branch

现在开始在本地开发,并准备贡献代码。
Do your work on a branch rather than on `main`.

按照国际惯例,我们一般不在 `main` 分支上开发,而是创建一个新的分支,然后在新的分支上开发,开发完成后再合并到 `main` 分支。
Name the branch for the kind of change you are making:

首先明确我们要不贡献的代码是一个新的功能特性还是修复一个 bug 。如果是新增一个功能特性,需要创建一个基于 `feature/` 开头的
分支,如果是修复一个 bug ,在创建一个基于 `fix/` 开头的分支。
| Type | Format | Example |
|------|--------|---------|
| New feature | `feature/<scope>-<desc>` | `feature/kafka-rebalance` |
| Bug fix | `fix/<scope>-<desc>` | `fix/hdfs-memory-leak` |
| Documentation | `docs/<desc>` | `docs/add-trino-operator` |
| Refactor | `refactor/<scope>-<desc>` | `refactor/operator-go-api` |
| Chore, deps, CI | `chore/<desc>` | `chore/upgrade-k8s-0.36` |

```bash
git checkout -b fix/foo-error
git switch -c fix/foo-error
```

然后在这个分支上进行代码开发,并在开发完成后提交代码。
Make your changes, then commit. Commit messages follow
[Conventional Commits](https://www.conventionalcommits.org/):

```bash
git commit -a -m "fix: foo error"
```

## 合并修改
## Rebase before you push

一个常见的问题是远程的 upstream (swoole/swoole-src) 有了新的更新, 从而会导致我们提交的 Pull Request 时会导致冲突, 因此我们可以在提交前先把远程其他开发者的commit和我们的commit合并.
While you were working, `upstream` probably moved. Rebasing onto the latest `main` first means your
pull request applies cleanly instead of arriving with conflicts.

首先我们需要切换到 `main` 分支, 然后同步最新的代码
Update `main`:

```bash
git checkout main
git switch main
git pull upstream main
```

然后切换回我们的开发分支, 并合并 `main` 分支
Then rebase your branch onto it:

```bash
git checkout fix/foo-error
git switch fix/foo-error
git rebase main
```

如果有冲突, 请解决冲突, 然后继续合并
If there are conflicts, resolve them and continue:

```bash
git add .
git rebase --continue
```

最后提交代码
Then push to your fork:

```bash
git push origin fix/foo-error
```

## 提交 Pull Request
If you had already pushed the branch before rebasing, the push will be rejected because the history
changed. Force-push your own branch:

```bash
git push --force-with-lease origin fix/foo-error
```

## Open a pull request

Go to the project on GitHub, switch to the branch you just pushed, click **Pull Request**, and fill
in the description.

Before you do, run the checks locally so CI does not fail on something you could have caught:

```bash
npm run verify
```

All CI checks must pass, and one reviewer approval is required, before a pull request can be merged.

## Related

在 Github 的项目中,切换到刚刚推送的分支,点击 `Pull Request` 按钮,填写相应的信息,然后提交 Pull Request。
- [Collaboration Guide](./collaboration.md)
- [Development Guideline](./develop-guideline.md)
- [Document Writing Guidelines](./document-guideline.md)
130 changes: 129 additions & 1 deletion docs/reference/kubebuilder.md
Original file line number Diff line number Diff line change
@@ -1 +1,129 @@
# Kuberbuilder
---
title: Kubebuilder
---

Every Kubedoop Operator is a [Kubebuilder](https://book.kubebuilder.io/) project. This page is a
reference for the parts of Kubebuilder that Kubedoop actually relies on — the project metadata, the
marker vocabulary used in the API types, and the code generation pipeline. It is aimed at people
working on the Operators; see [Development Guideline](../developer-manual/develop-guideline.md) for
the wider workflow.

## The PROJECT file

Every Operator repository has a `PROJECT` file at its root recording how the project was scaffolded
and which resources it owns. It is generated — do not hand-edit it.

```yaml
cliVersion: 4.10.1
domain: kubedoop.dev
layout:
- go.kubebuilder.io/v4
projectName: zookeeper-operator
repo: github.com/zncdatadev/zookeeper-operator
resources:
- api:
crdVersion: v1
namespaced: true
controller: true
domain: kubedoop.dev
group: zookeeper
kind: ZookeeperCluster
path: github.com/zncdatadev/zookeeper-operator/api/v1alpha1
version: v1alpha1
version: "3"
```

Two things are consistent across the whole project:

- **`domain: kubedoop.dev`** — every API group is `<group>.kubedoop.dev`, which is why resources
appear as `zookeeper.kubedoop.dev/v1alpha1`, `s3.kubedoop.dev/v1alpha1` and so on
- **`layout: go.kubebuilder.io/v4`** — all Operators use the v4 layout, so their directory
structures match

## Code generation

Two tools do the generation, both pinned in each Operator's Makefile and downloaded into `bin/`:

| Tool | Version | Produces |
|------|---------|----------|
| `controller-gen` | v0.19.0 | CRD YAML under `config/crd/bases/`, and `zz_generated.deepcopy.go` |
| `kustomize` | v5.7.1 | The assembled install manifests |

```bash
make manifests # CRD YAML from the Go types
make generate # DeepCopy implementations
```

Both outputs are **committed to the repository**. Editing an API type without regenerating leaves
the CRD and the Go types disagreeing, and shows up as a diff in CI. The Helm chart keeps yet another
copy — see [Development Guideline](../developer-manual/develop-guideline.md) for `helm-crd-sync`.

## Markers

Markers are `+kubebuilder:` comments above a type or field that tell `controller-gen` what to emit.
These are the ones Kubedoop uses.

### Object markers

| Marker | Effect |
|--------|--------|
| `+kubebuilder:object:root=true` | This type is a top-level API object, so generate a CRD for it |
| `+kubebuilder:object:generate=true` | Generate DeepCopy for every type in the package |
| `+kubebuilder:subresource:status` | Give the resource a `/status` subresource |
| `+kubebuilder:resource:path=...,scope=Cluster,shortName=...` | Plural path, scope, and `kubectl` short name |
| `+kubebuilder:printcolumn:name=...` | Extra column in `kubectl get` output |

Most Kubedoop resources are namespaced. The cluster-scoped ones are the classes that describe
shared infrastructure — `AuthenticationClass`, `ListenerClass` — because they are referenced by name
from any namespace.

### Validation markers

| Marker | Effect |
|--------|--------|
| `+kubebuilder:validation:Required` / `Optional` | Whether the field must be present |
| `+kubebuilder:default=<value>` | Default applied by the API server when the field is absent |
| `+kubebuilder:validation:Enum=a;b;c` | Restrict to a fixed set |
| `+kubebuilder:validation:Minimum` / `Maximum` | Numeric bounds |
| `+kubebuilder:validation:Pattern` | Regular expression |
| `+kubebuilder:validation:items:MinLength` / `items:Pattern` | Constraints on array elements |
| `+kubebuilder:validation:XValidation:rule="..."` | CEL expression for anything the above cannot express |

## Two ways to get this wrong

Both of these have already bitten this project.

### Markers are case-sensitive

`controller-gen` matches marker names exactly. `+kubebuilder:validation:optional` — lowercase `o` —
is not a marker; it is silently ignored, and the field falls back to the default rule that a field
without `omitempty` in its JSON tag is **required**.

Nothing warns you. The build succeeds, the CRD generates, and the field quietly lands in the CRD's
`required` list. The only way to notice is to read the generated YAML:

```bash
grep -A3 'required:' config/crd/bases/<group>_<plural>.yaml
```

Always write `Optional` and `Required` capitalised, and give optional fields `omitempty` in the JSON
tag as well.

### Defaults on fields inside `config`

`+kubebuilder:default` looks harmless but must not be used on fields inside the `config` block,
which is folded from role to role group.

Structural defaulting fills a leaf as soon as its **enclosing object** exists. So a default on a
field inside `config` is applied to any role group that wrote a `config` block for any reason at
all — which makes "the group did not set this" indistinguishable from "the group explicitly set the
default", and stops the role-level value from ever winning the merge.

Several fields in `operator-go` carry comments explaining exactly this and deliberately omit a
default. Defaults for folded fields belong at consumption time, in the code that reads them.

## Related

- [Kubebuilder Book](https://book.kubebuilder.io/)
- [CRD generation reference](https://book.kubebuilder.io/reference/generating-crd)
- [Development Guideline](../developer-manual/develop-guideline.md)
Loading
Loading