Skip to content

Commit c2d508a

Browse files
committed
make it fully mcr compatible with kcp
1 parent 7cb1c30 commit c2d508a

269 files changed

Lines changed: 4908 additions & 15987 deletions

File tree

Some content is hidden

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

Makefile

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ KCP_VER := v0.28.0
8585
KCP_BIN := kcp
8686
KCP := $(TOOLS_GOBIN_DIR)/$(KCP_BIN)-$(KCP_VER)
8787

88+
KCP_APIGEN_VER := v0.28.0
89+
KCP_APIGEN_BIN := apigen
90+
KCP_APIGEN_GEN := $(TOOLS_GOBIN_DIR)/$(KCP_APIGEN_BIN)-$(KCP_APIGEN_VER)
91+
export KCP_APIGEN_GEN # so hack scripts can use it
92+
8893
DEX_VER := v2.41.1
8994
DEX_BIN := dex
9095
DEX := $(TOOLS_GOBIN_DIR)/$(DEX_BIN)-$(DEX_VER)
@@ -122,7 +127,7 @@ ldflags:
122127
require-%:
123128
@if ! command -v $* 1> /dev/null 2>&1; then echo "$* not found in \$$PATH"; exit 1; fi
124129

125-
build: WHAT ?= ./cmd/... ./cli/cmd/...
130+
build: WHAT ?= ./cmd/... ./cli/cmd/... ./kcp/cmd/kcp-init/...
126131
build: require-jq require-go require-git verify-go-versions ## Build the project
127132
mkdir -p $(GOBIN_DIR)
128133
set -x; for W in $(WHAT); do \
@@ -132,15 +137,14 @@ build: require-jq require-go require-git verify-go-versions ## Build the project
132137
done
133138
.PHONY: build
134139

135-
.PHONY: build-all
136-
build-all:
137-
GOOS=$(OS) GOARCH=$(ARCH) $(MAKE) build WHAT=./cmd/...
138-
139140
install: WHAT ?= ./cmd/... ./cli/cmd/...
140141
install: ## install binaries to GOBIN
141142
GOOS=$(OS) GOARCH=$(ARCH) go install -ldflags="$(LDFLAGS)" $(WHAT)
142143
.PHONY: install
143144

145+
$(KCP_APIGEN_GEN):
146+
GOBIN=$(TOOLS_GOBIN_DIR) $(GO_INSTALL) github.com/kcp-dev/kcp/sdk/cmd/apigen $(KCP_APIGEN_BIN) $(KCP_APIGEN_VER)
147+
144148
$(GOLANGCI_LINT):
145149
GOBIN=$(TOOLS_GOBIN_DIR) $(GO_INSTALL) github.com/golangci/golangci-lint/v2/cmd/golangci-lint $(GOLANGCI_LINT_BIN) $(GOLANGCI_LINT_VER)
146150

@@ -166,7 +170,7 @@ vendor: ## Vendor the dependencies
166170
go mod vendor
167171
.PHONY: vendor
168172

169-
tools: $(GOLANGCI_LINT) $(CONTROLLER_GEN) $(YAML_PATCH) $(GOTESTSUM) $(CODE_GENERATOR)
173+
tools: $(GOLANGCI_LINT) $(CONTROLLER_GEN) $(YAML_PATCH) $(GOTESTSUM) $(CODE_GENERATOR) $(KCP_APIGEN_GEN)
170174
.PHONY: tools
171175

172176
$(CONTROLLER_GEN):
@@ -188,8 +192,8 @@ $(KUBE_APPLYCONFIGURATION_GEN):
188192
GOBIN=$(GOBIN_DIR) $(GO_INSTALL) k8s.io/code-generator/cmd/$(KUBE_APPLYCONFIGURATION_GEN_BIN) $(KUBE_APPLYCONFIGURATION_GEN_BIN) $(KUBE_APPLYCONFIGURATION_GEN_VER)
189193

190194

191-
codegen: WHAT ?= ./sdk/kcp ./sdk/client
192-
codegen: $(CONTROLLER_GEN) $(YAML_PATCH) $(CODE_GENERATOR) $(KUBE_CLIENT_GEN) $(KUBE_LISTER_GEN) $(KUBE_INFORMER_GEN) $(KUBE_APPLYCONFIGURATION_GEN)
195+
codegen: WHAT ?= ./sdk/client
196+
codegen: $(CONTROLLER_GEN) $(YAML_PATCH) $(CODE_GENERATOR) $(KUBE_CLIENT_GEN) $(KUBE_LISTER_GEN) $(KUBE_INFORMER_GEN) $(KUBE_APPLYCONFIGURATION_GEN) $(KCP_APIGEN_GEN)
193197
go mod download
194198
./hack/update-codegen.sh
195199
$(MAKE) imports
@@ -264,7 +268,7 @@ $(KCP):
264268
mv $(TOOLS_DIR)/kcp $(KCP)
265269

266270
run-kcp: $(KCP)
267-
$(KCP) start
271+
$(KCP) start -v=8
268272

269273
.PHONY: test-e2e
270274
ifdef USE_GOTESTSUM
@@ -273,7 +277,7 @@ endif
273277
test-e2e: TEST_ARGS ?=
274278
test-e2e: WORK_DIR ?= .
275279
test-e2e: WHAT ?= ./test/e2e...
276-
test-e2e: $(KCP) $(DEX) build-all
280+
test-e2e: $(KCP) $(DEX) build
277281
mkdir .kcp
278282
$(DEX) serve hack/dex-config-dev.yaml 2>&1 & DEX_PID=$$!; \
279283
$(KCP) start &>.kcp/kcp.log & KCP_PID=$$!; \
@@ -288,7 +292,7 @@ endif
288292
test: WHAT ?= ./...
289293
# We will need to move into the sub package, of pkg/apis to run those tests.
290294
test: ## run unit tests
291-
$(GO_TEST) -race -count $(COUNT) -coverprofile=coverage.txt -covermode=atomic $(TEST_ARGS) $$(go list "$(WHAT)" | grep -v ./test/e2e/)
295+
$(GO_TEST) -race -count $(COUNT) -coverprofile=coverage.txt -covermode=atomic $(TEST_ARGS) $$(go list "$(WHAT)" | grep -v ./test/e2e/ | grep -v ./kcp)
292296
cd sdk/apis && $(GO_TEST) -race -count $(COUNT) -coverprofile=coverage.txt -covermode=atomic $(TEST_ARGS) $(WHAT)
293297

294298
.PHONY: verify-imports

TODO

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
* remove SDK generation in favor of ctrl runtime client
2+
* remove duplicate controller in manager

apiserviceexport.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
apiVersion: kube-bind.io/v1alpha2
2+
kind: APIServiceExportRequest
3+
metadata:
4+
name: mangodbs.mangodb.com
5+
spec:
6+
resources:
7+
- group: mangodb.com
8+
resource: mangodbs
9+
status: {}

backend/config.go

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*
2+
Copyright 2022 The Kube Bind Authors.
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 backend
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"net/url"
23+
"strings"
24+
25+
apisv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1"
26+
apisv1alpha2 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha2"
27+
"github.com/kcp-dev/multicluster-provider/apiexport"
28+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
29+
"k8s.io/apimachinery/pkg/runtime"
30+
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
31+
"k8s.io/client-go/rest"
32+
"k8s.io/client-go/tools/clientcmd"
33+
"k8s.io/utils/ptr"
34+
ctrl "sigs.k8s.io/controller-runtime"
35+
ctrlconfig "sigs.k8s.io/controller-runtime/pkg/config"
36+
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
37+
mcmanager "sigs.k8s.io/multicluster-runtime/pkg/manager"
38+
"sigs.k8s.io/multicluster-runtime/pkg/multicluster"
39+
40+
kuberesources "github.com/kube-bind/kube-bind/backend/kubernetes/resources"
41+
"github.com/kube-bind/kube-bind/backend/options"
42+
kubebindv1alpha1 "github.com/kube-bind/kube-bind/sdk/apis/kubebind/v1alpha1"
43+
kubebindv1alpha2 "github.com/kube-bind/kube-bind/sdk/apis/kubebind/v1alpha2"
44+
)
45+
46+
type Config struct {
47+
Options *options.CompletedOptions
48+
49+
Provider multicluster.Provider
50+
ExternalAddressGenerator kuberesources.ExternalAddreesGeneratorFunc
51+
Manager mcmanager.Manager
52+
Scheme *runtime.Scheme
53+
54+
ClientConfig *rest.Config
55+
}
56+
57+
func NewConfig(options *options.CompletedOptions) (*Config, error) {
58+
config := &Config{
59+
Options: options,
60+
}
61+
62+
// create clients
63+
rules := clientcmd.NewDefaultClientConfigLoadingRules()
64+
rules.ExplicitPath = options.KubeConfig
65+
var err error
66+
config.ClientConfig, err = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(rules, nil).ClientConfig()
67+
if err != nil {
68+
return nil, err
69+
}
70+
config.ClientConfig = rest.CopyConfig(config.ClientConfig)
71+
config.ClientConfig = rest.AddUserAgent(config.ClientConfig, "kube-bind-backend")
72+
73+
if options.ServerURL != "" {
74+
config.ClientConfig.Host = options.ServerURL
75+
}
76+
77+
// Set up controller-runtime manager
78+
scheme := runtime.NewScheme()
79+
if err := clientgoscheme.AddToScheme(scheme); err != nil {
80+
return nil, fmt.Errorf("error adding client-go scheme: %w", err)
81+
}
82+
if err := apiextensionsv1.AddToScheme(scheme); err != nil {
83+
return nil, fmt.Errorf("error adding apiextensions scheme: %w", err)
84+
}
85+
if err := kubebindv1alpha1.AddToScheme(scheme); err != nil {
86+
return nil, fmt.Errorf("error adding kubebind scheme: %w", err)
87+
}
88+
if err := kubebindv1alpha2.AddToScheme(scheme); err != nil {
89+
return nil, fmt.Errorf("error adding kubebind scheme: %w", err)
90+
}
91+
92+
config.Scheme = scheme
93+
94+
switch options.Provider {
95+
case "kcp":
96+
if err := apisv1alpha1.AddToScheme(scheme); err != nil {
97+
return nil, fmt.Errorf("error adding apis scheme: %w", err)
98+
}
99+
if err := apisv1alpha2.AddToScheme(scheme); err != nil {
100+
return nil, fmt.Errorf("error adding apis scheme: %w", err)
101+
}
102+
provider, err := apiexport.New(config.ClientConfig, apiexport.Options{
103+
Scheme: scheme,
104+
})
105+
if err != nil {
106+
return nil, fmt.Errorf("error setting up kcp provider: %w", err)
107+
}
108+
109+
config.ExternalAddressGenerator = func(ctx context.Context, clusterConfig *rest.Config) (string, error) {
110+
// In kcp case, we are talking via apiexport so clientconfig will be pointing to
111+
// https://192.168.2.166:6443/services/apiexport/2sssrgg8ivlpw0cy/kube-bind.io/clusters/2p0rtkf7b697s6mj
112+
// We need to extract host and /clusters/... part
113+
u, err := url.Parse(clusterConfig.Host)
114+
if err != nil {
115+
return "", err
116+
}
117+
118+
// Extract cluster ID from the path
119+
// Path format: /services/apiexport/{export-id}/kube-bind.io/clusters/{cluster-id}
120+
pathParts := strings.Split(strings.Trim(u.Path, "/"), "/")
121+
if len(pathParts) < 5 || pathParts[4] != "clusters" {
122+
return "", fmt.Errorf("invalid apiexport URL format")
123+
}
124+
125+
clusterID := pathParts[5]
126+
127+
// Construct new URL with cluster path
128+
u.Path = "/clusters/" + clusterID
129+
130+
return u.String(), nil
131+
}
132+
config.Provider = provider
133+
default:
134+
config.ExternalAddressGenerator = kuberesources.DefaultExternalAddreesGenerator
135+
config.Provider = nil
136+
}
137+
138+
opts := ctrl.Options{
139+
Controller: ctrlconfig.Controller{
140+
SkipNameValidation: ptr.To(config.Options.ExtraOptions.TestingSkipNameValidation),
141+
},
142+
Metrics: metricsserver.Options{
143+
BindAddress: "0",
144+
},
145+
Scheme: scheme,
146+
}
147+
148+
manager, err := mcmanager.New(config.ClientConfig, config.Provider, opts)
149+
if err != nil {
150+
return nil, fmt.Errorf("error setting up controller manager: %w", err)
151+
}
152+
153+
config.Manager = manager
154+
155+
return config, nil
156+
}

contrib/example-backend/controllers/clusterbinding/clusterbinding_controller.go renamed to backend/controllers/clusterbinding/clusterbinding_controller.go

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ import (
2626
"k8s.io/apimachinery/pkg/api/errors"
2727
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2828
"k8s.io/apimachinery/pkg/types"
29-
"k8s.io/client-go/rest"
3029
ctrl "sigs.k8s.io/controller-runtime"
3130
"sigs.k8s.io/controller-runtime/pkg/cache"
3231
"sigs.k8s.io/controller-runtime/pkg/client"
3332
"sigs.k8s.io/controller-runtime/pkg/cluster"
33+
"sigs.k8s.io/controller-runtime/pkg/controller"
3434
"sigs.k8s.io/controller-runtime/pkg/handler"
3535
"sigs.k8s.io/controller-runtime/pkg/log"
3636
"sigs.k8s.io/controller-runtime/pkg/reconcile"
@@ -39,40 +39,31 @@ import (
3939
mcreconcile "sigs.k8s.io/multicluster-runtime/pkg/reconcile"
4040

4141
kubebindv1alpha2 "github.com/kube-bind/kube-bind/sdk/apis/kubebind/v1alpha2"
42-
bindclient "github.com/kube-bind/kube-bind/sdk/client/clientset/versioned"
4342
)
4443

4544
const (
46-
controllerName = "kube-bind-example-backend-clusterbinding"
45+
controllerName = "kube-bind-backend-clusterbinding"
4746
)
4847

4948
// ClusterBindingReconciler reconciles a ClusterBinding object.
5049
type ClusterBindingReconciler struct {
51-
manager mcmanager.Manager
52-
50+
manager mcmanager.Manager
51+
opts controller.TypedOptions[mcreconcile.Request]
5352
scope kubebindv1alpha2.InformerScope
54-
bindClient bindclient.Interface
5553
reconciler reconciler
5654
}
5755

5856
// NewClusterBindingReconciler returns a new ClusterBindingReconciler to reconcile ClusterBindings.
5957
func NewClusterBindingReconciler(
58+
_ context.Context,
6059
mgr mcmanager.Manager,
61-
config *rest.Config,
60+
opts controller.TypedOptions[mcreconcile.Request],
6261
scope kubebindv1alpha2.InformerScope,
6362
) (*ClusterBindingReconciler, error) {
64-
config = rest.CopyConfig(config)
65-
config = rest.AddUserAgent(config, controllerName)
66-
67-
bindClient, err := bindclient.NewForConfig(config)
68-
if err != nil {
69-
return nil, err
70-
}
71-
7263
r := &ClusterBindingReconciler{
73-
manager: mgr,
74-
scope: scope,
75-
bindClient: bindClient,
64+
manager: mgr,
65+
opts: opts,
66+
scope: scope,
7667
reconciler: reconciler{
7768
scope: scope,
7869
listServiceExports: func(ctx context.Context, cache cache.Cache, ns string) ([]*kubebindv1alpha2.APIServiceExport, error) {
@@ -88,7 +79,7 @@ func NewClusterBindingReconciler(
8879
},
8980
getAPIResourceSchema: func(ctx context.Context, cache cache.Cache, name string) (*kubebindv1alpha2.APIResourceSchema, error) {
9081
result := &kubebindv1alpha2.APIResourceSchema{}
91-
err = cache.Get(ctx, types.NamespacedName{Name: name}, result)
82+
err := cache.Get(ctx, types.NamespacedName{Name: name}, result)
9283
if err != nil {
9384
return nil, fmt.Errorf("failed to get APIResourceSchema %q: %w", name, err)
9485
}
@@ -104,13 +95,13 @@ func NewClusterBindingReconciler(
10495
},
10596
createClusterRole: func(ctx context.Context, client client.Client, binding *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error) {
10697
if err := client.Create(ctx, binding); err != nil {
107-
return nil, err
98+
return nil, fmt.Errorf("failed to create ClusterRole %q: %w", binding.Name, err)
10899
}
109100
return binding, nil
110101
},
111102
updateClusterRole: func(ctx context.Context, client client.Client, binding *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error) {
112103
if err := client.Update(ctx, binding); err != nil {
113-
return nil, err
104+
return nil, fmt.Errorf("failed to update ClusterRole %q: %w", binding.Name, err)
114105
}
115106
return binding, nil
116107
},
@@ -124,13 +115,13 @@ func NewClusterBindingReconciler(
124115
},
125116
createClusterRoleBinding: func(ctx context.Context, client client.Client, binding *rbacv1.ClusterRoleBinding) (*rbacv1.ClusterRoleBinding, error) {
126117
if err := client.Create(ctx, binding); err != nil {
127-
return nil, err
118+
return nil, fmt.Errorf("failed to create ClusterRoleBinding %q: %w", binding.Name, err)
128119
}
129120
return binding, nil
130121
},
131122
updateClusterRoleBinding: func(ctx context.Context, client client.Client, binding *rbacv1.ClusterRoleBinding) (*rbacv1.ClusterRoleBinding, error) {
132123
if err := client.Update(ctx, binding); err != nil {
133-
return nil, err
124+
return nil, fmt.Errorf("failed to update ClusterRoleBinding %q: %w", binding.Name, err)
134125
}
135126
return binding, nil
136127
},
@@ -148,17 +139,15 @@ func NewClusterBindingReconciler(
148139
}
149140
return &ns, nil
150141
},
151-
createRoleBinding: func(ctx context.Context, client client.Client, ns string, binding *rbacv1.RoleBinding) (*rbacv1.RoleBinding, error) {
152-
binding.Namespace = ns
142+
createRoleBinding: func(ctx context.Context, client client.Client, binding *rbacv1.RoleBinding) (*rbacv1.RoleBinding, error) {
153143
if err := client.Create(ctx, binding); err != nil {
154-
return nil, err
144+
return nil, fmt.Errorf("failed to create RoleBinding %q in namespace %q: %w", binding.Name, binding.Namespace, err)
155145
}
156146
return binding, nil
157147
},
158-
updateRoleBinding: func(ctx context.Context, client client.Client, ns string, binding *rbacv1.RoleBinding) (*rbacv1.RoleBinding, error) {
159-
binding.Namespace = ns
148+
updateRoleBinding: func(ctx context.Context, client client.Client, binding *rbacv1.RoleBinding) (*rbacv1.RoleBinding, error) {
160149
if err := client.Update(ctx, binding); err != nil {
161-
return nil, err
150+
return nil, fmt.Errorf("failed to update RoleBinding %q in namespace %q: %w", binding.Name, binding.Namespace, err)
162151
}
163152
return binding, nil
164153
},
@@ -244,6 +233,7 @@ func (r *ClusterBindingReconciler) SetupWithManager(mgr mcmanager.Manager) error
244233
&kubebindv1alpha2.APIServiceExport{},
245234
mapCRD,
246235
).
236+
WithOptions(r.opts).
247237
Named(controllerName).
248238
Complete(r)
249239
}

0 commit comments

Comments
 (0)