Skip to content

Commit fd7cd5b

Browse files
authored
Merge pull request #60 from tnthornton/tnthornton-kcp-0.19-take-2
✨ Rebase to 0.19
2 parents 22d9eab + 0f609a8 commit fd7cd5b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+3742
-500
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,5 @@ tools/setup-envtest/out
2828

2929
junit-report.xml
3030
/artifacts
31+
32+
examples/kcp/.gitignore

.golangci.yml

-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ linters:
1313
- exhaustive
1414
- exportloopref
1515
- ginkgolinter
16-
- goconst
1716
- gocritic
1817
- gocyclo
1918
- gofmt

.prow.yaml

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
presubmits:
2+
- name: pull-controller-runtime-everything
3+
always_run: true
4+
decorate: true
5+
clone_uri: "ssh://[email protected]/kcp-dev/controller-runtime.git"
6+
labels:
7+
preset-goproxy: "true"
8+
spec:
9+
containers:
10+
- image: ghcr.io/kcp-dev/infra/build:1.22.2-1
11+
command:
12+
- make
13+
- test
14+
15+
- name: pull-controller-runtime-example-e2e
16+
decorate: true
17+
# only run e2e tests if code changed.
18+
run_if_changed: "(pkg|examples|go.mod|go.sum|Makefile|.prow.yaml)"
19+
clone_uri: "https://github.com/kcp-dev/controller-runtime"
20+
labels:
21+
preset-goproxy: "true"
22+
spec:
23+
containers:
24+
- image: ghcr.io/kcp-dev/infra/build:1.22.2-1
25+
env:
26+
- name: KUBECONFIG
27+
value: /home/prow/go/src/github.com/kcp-dev/controller-runtime/examples/kcp/.test/kcp.kubeconfig
28+
command:
29+
- make
30+
- test-kcp-e2e
31+
resources:
32+
requests:
33+
memory: 6Gi
34+
cpu: 4

DOWNSTREAM_OWNERS

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
approvers:
2+
- sttts
3+
- xrstf
4+
- mjudeikis
5+
- embik

DOWNSTREAM_OWNERS_ALIASES

Whitespace-only changes.

Makefile

+3
Original file line numberDiff line numberDiff line change
@@ -216,3 +216,6 @@ verify-apidiff: $(GO_APIDIFF) ## Check for API differences
216216

217217
go-version: ## Print the go version we use to compile our binaries and images
218218
@echo $(GO_VERSION)
219+
.PHONY: test-kcp-e2e
220+
test-kcp-e2e:
221+
cd examples/kcp && make kcp-server kcp-controller test

examples/builtins/main.go

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ func main() {
4242

4343
// Setup a Manager
4444
entryLog.Info("setting up manager")
45+
4546
mgr, err := manager.New(config.GetConfigOrDie(), manager.Options{})
4647
if err != nil {
4748
entryLog.Error(err, "unable to set up overall controller manager")

examples/kcp/Makefile

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
SHELL := /bin/bash
2+
3+
.PHONY: help
4+
help: ## Display this help.
5+
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
6+
7+
GO_INSTALL = ./hack/go-install.sh
8+
9+
LOCALBIN ?= $(shell pwd)/bin
10+
TOOLS_DIR=hack/tools
11+
TOOLS_BIN_DIR := $(abspath $(TOOLS_DIR)/bin)
12+
ARTIFACT_DIR ?= .test
13+
14+
KCP ?= $(LOCALBIN)/kcp
15+
KUBECTL_KCP ?= $(LOCALBIN)/kubectl-kcp
16+
17+
KCP_VERSION ?= 0.23.0
18+
CONTROLLER_GEN := $(TOOLS_BIN_DIR)/controller-gen
19+
export CONTROLLER_GEN # so hack scripts can use it
20+
21+
KCP_APIGEN_VER := v0.21.0
22+
KCP_APIGEN_BIN := apigen
23+
KCP_APIGEN_GEN := $(TOOLS_BIN_DIR)/$(KCP_APIGEN_BIN)-$(KCP_APIGEN_VER)
24+
export KCP_APIGEN_GEN # so hack scripts can use it
25+
26+
OS ?= $(shell go env GOOS )
27+
ARCH ?= $(shell go env GOARCH )
28+
29+
$(KCP): ## Download kcp locally if necessary.
30+
mkdir -p $(LOCALBIN)
31+
curl -L -s -o - https://github.com/kcp-dev/kcp/releases/download/v$(KCP_VERSION)/kcp_$(KCP_VERSION)_$(OS)_$(ARCH).tar.gz | tar --directory $(LOCALBIN)/../ -xvzf - bin/kcp
32+
touch $(KCP) # we download an "old" file, so make will re-download to refresh it unless we make it newer than the owning dir
33+
34+
$(KUBECTL_KCP): ## Download kcp kubectl plugins locally if necessary.
35+
curl -L -s -o - https://github.com/kcp-dev/kcp/releases/download/v$(KCP_VERSION)/kubectl-kcp-plugin_$(KCP_VERSION)_$(OS)_$(ARCH).tar.gz | tar --directory $(LOCALBIN)/../ -xvzf - bin
36+
touch $(KUBECTL_KCP) # we download an "old" file, so make will re-download to refresh it unless we make it newer than the owning dir
37+
38+
$(KCP_APIGEN_GEN):
39+
GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) github.com/kcp-dev/kcp/sdk/cmd/apigen $(KCP_APIGEN_BIN) $(KCP_APIGEN_VER)
40+
41+
$(CONTROLLER_GEN): $(TOOLS_DIR)/go.mod # Build controller-gen from tools folder.
42+
cd $(TOOLS_DIR) && go build -tags=tools -o bin/controller-gen sigs.k8s.io/controller-tools/cmd/controller-gen
43+
44+
build: $(KCP) $(KUBECTL_KCP) build-controller
45+
46+
ifeq (,$(shell go env GOBIN))
47+
GOBIN=$(shell go env GOPATH)/bin
48+
else
49+
GOBIN=$(shell go env GOBIN)
50+
endif
51+
52+
build-controller: ## Build the controller binary.
53+
go build -o $(LOCALBIN)/kcp-controller ./main.go
54+
55+
.PHONY: kcp-server
56+
kcp-server: $(KCP) $(ARTIFACT_DIR)/kcp ## Run the kcp server.
57+
@if [[ ! -s $(ARTIFACT_DIR)/kcp.log ]]; then ( $(KCP) start -v 5 --root-directory $(ARTIFACT_DIR)/kcp --kubeconfig-path $(ARTIFACT_DIR)/kcp.kubeconfig --audit-log-maxsize 1024 --audit-log-mode=batch --audit-log-batch-max-wait=1s --audit-log-batch-max-size=1000 --audit-log-batch-buffer-size=10000 --audit-log-batch-throttle-burst=15 --audit-log-batch-throttle-enable=true --audit-log-batch-throttle-qps=10 --audit-policy-file ./test/e2e/audit-policy.yaml --audit-log-path $(ARTIFACT_DIR)/audit.log >$(ARTIFACT_DIR)/kcp.log 2>&1 & ); fi
58+
@echo "Waiting for kcp server to generate kubeconfig..."
59+
@while true; do if [[ ! -s $(ARTIFACT_DIR)/kcp.kubeconfig ]]; then sleep 0.2; else break; fi; done
60+
@echo "Waiting for kcp server to be ready..."
61+
@while true; do if ! kubectl --kubeconfig $(ARTIFACT_DIR)/kcp.kubeconfig get --raw /readyz >$(ARTIFACT_DIR)/kcp.probe.log 2>&1; then sleep 0.2; else break; fi; done
62+
@echo "kcp server is ready and running in the background. To stop run 'make test-cleanup'"
63+
64+
.PHONY: kcp-bootstrap
65+
kcp-bootstrap: ## Bootstrap the kcp server.
66+
@go run ./config/main.go
67+
68+
.PHONY: kcp-controller
69+
kcp-controller: build kcp-bootstrap ## Run the kcp-controller.
70+
@echo "Starting kcp-controller in the background. To stop run 'make test-cleanup'"
71+
@if [[ ! -s $(ARTIFACT_DIR)/controller.log ]]; then ( ./bin/kcp-controller >$(ARTIFACT_DIR)/controller.log 2>&1 & ); fi
72+
73+
.PHONY: test-e2e-cleanup
74+
test-cleanup: ## Clean up processes and directories from an end-to-end test run.
75+
rm -rf $(ARTIFACT_DIR) || true
76+
pkill -sigterm kcp || true
77+
pkill -sigterm kubectl || true
78+
pkill -sigterm kcp-controller || true
79+
80+
$(ARTIFACT_DIR)/kcp: ## Create a directory for the kcp server data.
81+
mkdir -p $(ARTIFACT_DIR)/kcp
82+
83+
generate: build $(CONTROLLER_GEN) $(KCP_APIGEN_GEN) # Generate code
84+
./hack/update-codegen-crds.sh
85+
86+
run-local: build-controller kcp-bootstrap
87+
./bin/kcp-controller
88+
89+
.PHONY: test # Run tests
90+
test:
91+
go test ./... --workspace=root --kubeconfig=$(CURDIR)/$(ARTIFACT_DIR)/kcp.kubeconfig

examples/kcp/README.md

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# controller-runtime-example
2+
An example project that is multi-cluster aware and works with [kcp](https://github.com/kcp-dev/kcp)
3+
4+
## Description
5+
6+
In this example, we intentionally not using advanced kubebuilder patterns to keep the example simple and easy to understand.
7+
In the future, we will add more advanced examples. Example covers 3 parts:
8+
1. KCP bootstrapping - creating APIExport & consumer workspaces
9+
1. Creating WorkspaceType for particular exports
10+
2. Running controller with APIExport aware configuration and reconciling multiple consumer workspaces
11+
12+
13+
This example contains an example project that works with APIExports and multiple kcp workspaces. It demonstrates
14+
two reconcilers:
15+
16+
1. ConfigMap
17+
1. Get a ConfigMap for the key from the queue, from the correct logical cluster
18+
2. If the ConfigMap has labels["name"], set labels["response"] = "hello-$name" and save the changes
19+
3. List all ConfigMaps in the logical cluster and log each one's namespace and name
20+
4. If the ConfigMap from step 1 has data["namespace"] set, create a namespace whose name is the data value.
21+
5. If the ConfigMap from step 1 has data["secretData"] set, create a secret in the same namespace as the ConfigMap,
22+
with an owner reference to the ConfigMap, and data["dataFromCM"] set to the data value.
23+
24+
2. Widget
25+
1. Show how to list all Widget instances across all logical clusters
26+
2. Get a Widget for the key from the queue, from the correct logical cluster
27+
3. List all Widgets in the same logical cluster
28+
4. Count the number of Widgets (list length)
29+
5. Make sure `.status.total` matches the current count (via a `patch`)
30+
31+
## Getting Started
32+
33+
### Running on kcp
34+
35+
1. Run KCP with the following command:
36+
37+
```sh
38+
make kcp-server
39+
```
40+
41+
From this point onwards you can inspect kcp configuration using kubeconfig:
42+
43+
```sh
44+
export KUBECONFIG=.test/kcp.kubeconfig
45+
```
46+
47+
1. Bootstrap the KCP server with the following command:
48+
49+
```sh
50+
export KUBECONFIG=./.test/kcp.kubeconfig
51+
make kcp-bootstrap
52+
```
53+
54+
1. Run controller:
55+
56+
```sh
57+
export KUBECONFIG=./.test/kcp.kubeconfig
58+
make run-local
59+
```
60+
61+
1. In separate shell you can run tests to exercise the controller:
62+
63+
```sh
64+
export KUBECONFIG=./.test/kcp.kubeconfig
65+
make test
66+
```
67+
68+
### Uninstall resources
69+
To delete the resources from kcp:
70+
71+
```sh
72+
make test-clean
73+
```
74+
75+
76+
77+
### How it works
78+
This project aims to follow the Kubernetes [Operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/)
79+
80+
It uses [Controllers](https://kubernetes.io/docs/concepts/architecture/controller/)
81+
which provides a reconcile function responsible for synchronizing resources until the desired state is reached.
82+
83+
84+
85+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
Copyright 2024.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Package v1alpha1 contains API Schema definitions for the data v1alpha1 API group
18+
// +kubebuilder:object:generate=true
19+
// +groupName=data.my.domain
20+
package v1alpha1
21+
22+
import (
23+
"k8s.io/apimachinery/pkg/runtime/schema"
24+
"sigs.k8s.io/controller-runtime/pkg/scheme"
25+
)
26+
27+
var (
28+
// GroupVersion is group version used to register these objects
29+
GroupVersion = schema.GroupVersion{Group: "data.my.domain", Version: "v1alpha1"}
30+
31+
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
32+
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
33+
34+
// AddToScheme adds the types in this group-version to the given scheme.
35+
AddToScheme = SchemeBuilder.AddToScheme
36+
)
+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
Copyright 2024.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1alpha1
18+
19+
import (
20+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21+
)
22+
23+
// WidgetSpec defines the desired state of Widget
24+
type WidgetSpec struct {
25+
Foo string `json:"foo,omitempty"`
26+
}
27+
28+
// WidgetStatus defines the observed state of Widget
29+
type WidgetStatus struct {
30+
Total int `json:"total,omitempty"`
31+
}
32+
33+
// +kubebuilder:object:root=true
34+
// +kubebuilder:subresource:status
35+
36+
// Widget is the Schema for the widgets API
37+
type Widget struct {
38+
metav1.TypeMeta `json:",inline"`
39+
metav1.ObjectMeta `json:"metadata,omitempty"`
40+
41+
Spec WidgetSpec `json:"spec,omitempty"`
42+
Status WidgetStatus `json:"status,omitempty"`
43+
}
44+
45+
// +kubebuilder:object:root=true
46+
47+
// WidgetList contains a list of Widget
48+
type WidgetList struct {
49+
metav1.TypeMeta `json:",inline"`
50+
metav1.ListMeta `json:"metadata,omitempty"`
51+
Items []Widget `json:"items"`
52+
}
53+
54+
func init() {
55+
SchemeBuilder.Register(&Widget{}, &WidgetList{})
56+
}

0 commit comments

Comments
 (0)