Skip to content

Commit cce3a0c

Browse files
committed
Move backend out of examples.
Make backend work with kcp/mcr Simplify code Signed-off-by: Mangirdas Judeikis <mangirdas@judeikis.lt> On-behalf-of: @SAP mangirdas.judeikis@sap.com
1 parent b351917 commit cce3a0c

105 files changed

Lines changed: 4949 additions & 551 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: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ ldflags:
122122
require-%:
123123
@if ! command -v $* 1> /dev/null 2>&1; then echo "$* not found in \$$PATH"; exit 1; fi
124124

125-
build: WHAT ?= ./cmd/... ./cli/cmd/...
125+
build: WHAT ?= ./cmd/... ./cli/cmd/... ./kcp/cmd/kcp-init/...
126126
build: require-jq require-go require-git verify-go-versions ## Build the project
127127
mkdir -p $(GOBIN_DIR)
128128
set -x; for W in $(WHAT); do \
@@ -132,15 +132,12 @@ build: require-jq require-go require-git verify-go-versions ## Build the project
132132
done
133133
.PHONY: build
134134

135-
.PHONY: build-all
136-
build-all:
137-
GOOS=$(OS) GOARCH=$(ARCH) $(MAKE) build WHAT=./cmd/...
138-
139135
install: WHAT ?= ./cmd/... ./cli/cmd/...
140136
install: ## install binaries to GOBIN
141137
GOOS=$(OS) GOARCH=$(ARCH) go install -ldflags="$(LDFLAGS)" $(WHAT)
142138
.PHONY: install
143139

140+
144141
$(GOLANGCI_LINT):
145142
GOBIN=$(TOOLS_GOBIN_DIR) $(GO_INSTALL) github.com/golangci/golangci-lint/v2/cmd/golangci-lint $(GOLANGCI_LINT_BIN) $(GOLANGCI_LINT_VER)
146143

@@ -166,7 +163,7 @@ vendor: ## Vendor the dependencies
166163
go mod vendor
167164
.PHONY: vendor
168165

169-
tools: $(GOLANGCI_LINT) $(CONTROLLER_GEN) $(YAML_PATCH) $(GOTESTSUM) $(CODE_GENERATOR)
166+
tools: $(GOLANGCI_LINT) $(CONTROLLER_GEN) $(YAML_PATCH) $(GOTESTSUM) $(CODE_GENERATOR)
170167
.PHONY: tools
171168

172169
$(CONTROLLER_GEN):
@@ -188,7 +185,7 @@ $(KUBE_APPLYCONFIGURATION_GEN):
188185
GOBIN=$(GOBIN_DIR) $(GO_INSTALL) k8s.io/code-generator/cmd/$(KUBE_APPLYCONFIGURATION_GEN_BIN) $(KUBE_APPLYCONFIGURATION_GEN_BIN) $(KUBE_APPLYCONFIGURATION_GEN_VER)
189186

190187

191-
codegen: WHAT ?= ./sdk/kcp ./sdk/client
188+
codegen: WHAT ?= ./sdk/client
192189
codegen: $(CONTROLLER_GEN) $(YAML_PATCH) $(CODE_GENERATOR) $(KUBE_CLIENT_GEN) $(KUBE_LISTER_GEN) $(KUBE_INFORMER_GEN) $(KUBE_APPLYCONFIGURATION_GEN)
193190
go mod download
194191
./hack/update-codegen.sh
@@ -273,7 +270,7 @@ endif
273270
test-e2e: TEST_ARGS ?=
274271
test-e2e: WORK_DIR ?= .
275272
test-e2e: WHAT ?= ./test/e2e...
276-
test-e2e: $(KCP) $(DEX) build-all
273+
test-e2e: $(KCP) $(DEX) build
277274
mkdir .kcp
278275
$(DEX) serve hack/dex-config-dev.yaml 2>&1 & DEX_PID=$$!; \
279276
$(KCP) start &>.kcp/kcp.log & KCP_PID=$$!; \
@@ -288,7 +285,7 @@ endif
288285
test: WHAT ?= ./...
289286
# We will need to move into the sub package, of pkg/apis to run those tests.
290287
test: ## run unit tests
291-
$(GO_TEST) -race -count $(COUNT) -coverprofile=coverage.txt -covermode=atomic $(TEST_ARGS) $$(go list "$(WHAT)" | grep -v ./test/e2e/)
288+
$(GO_TEST) -race -count $(COUNT) -coverprofile=coverage.txt -covermode=atomic $(TEST_ARGS) $$(go list "$(WHAT)" | grep -v ./test/e2e/ | grep -v ./kcp)
292289
cd sdk/apis && $(GO_TEST) -race -count $(COUNT) -coverprofile=coverage.txt -covermode=atomic $(TEST_ARGS) $(WHAT)
293290

294291
.PHONY: verify-imports

backend/config.go

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
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 = parseKCPServerURL
110+
config.Provider = provider
111+
default:
112+
config.ExternalAddressGenerator = kuberesources.DefaultExternalAddreesGenerator
113+
config.Provider = nil
114+
}
115+
116+
opts := ctrl.Options{
117+
Controller: ctrlconfig.Controller{
118+
SkipNameValidation: ptr.To(config.Options.ExtraOptions.TestingSkipNameValidation),
119+
},
120+
Metrics: metricsserver.Options{
121+
BindAddress: "0",
122+
},
123+
Scheme: scheme,
124+
}
125+
126+
manager, err := mcmanager.New(config.ClientConfig, config.Provider, opts)
127+
if err != nil {
128+
return nil, fmt.Errorf("error setting up controller manager: %w", err)
129+
}
130+
131+
config.Manager = manager
132+
133+
return config, nil
134+
}
135+
136+
func parseKCPServerURL(ctx context.Context, clusterConfig *rest.Config) (string, error) {
137+
// In kcp case, we are talking via apiexport so clientconfig will be pointing to
138+
// https://192.168.2.166:6443/services/apiexport/2sssrgg8ivlpw0cy/kube-bind.io/clusters/2p0rtkf7b697s6mj
139+
// We need to extract host and /clusters/... part
140+
u, err := url.Parse(clusterConfig.Host)
141+
if err != nil {
142+
return "", err
143+
}
144+
145+
// Extract cluster ID from the path
146+
// Path format: /services/apiexport/{export-id}/kube-bind.io/clusters/{cluster-id}
147+
pathParts := strings.Split(strings.Trim(u.Path, "/"), "/")
148+
if len(pathParts) < 5 || pathParts[4] != "clusters" {
149+
return "", fmt.Errorf("invalid apiexport URL format")
150+
}
151+
152+
clusterID := pathParts[5]
153+
154+
// Construct new URL with cluster path
155+
u.Path = "/clusters/" + clusterID
156+
157+
return u.String(), nil
158+
}

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

Lines changed: 25 additions & 53 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
}
@@ -102,17 +93,11 @@ func NewClusterBindingReconciler(
10293
}
10394
return &role, nil
10495
},
105-
createClusterRole: func(ctx context.Context, client client.Client, binding *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error) {
106-
if err := client.Create(ctx, binding); err != nil {
107-
return nil, err
108-
}
109-
return binding, nil
96+
createClusterRole: func(ctx context.Context, client client.Client, binding *rbacv1.ClusterRole) error {
97+
return client.Create(ctx, binding)
11098
},
111-
updateClusterRole: func(ctx context.Context, client client.Client, binding *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error) {
112-
if err := client.Update(ctx, binding); err != nil {
113-
return nil, err
114-
}
115-
return binding, nil
99+
updateClusterRole: func(ctx context.Context, client client.Client, binding *rbacv1.ClusterRole) error {
100+
return client.Update(ctx, binding)
116101
},
117102
getClusterRoleBinding: func(ctx context.Context, cache cache.Cache, name string) (*rbacv1.ClusterRoleBinding, error) {
118103
var binding rbacv1.ClusterRoleBinding
@@ -122,17 +107,11 @@ func NewClusterBindingReconciler(
122107
}
123108
return &binding, nil
124109
},
125-
createClusterRoleBinding: func(ctx context.Context, client client.Client, binding *rbacv1.ClusterRoleBinding) (*rbacv1.ClusterRoleBinding, error) {
126-
if err := client.Create(ctx, binding); err != nil {
127-
return nil, err
128-
}
129-
return binding, nil
110+
createClusterRoleBinding: func(ctx context.Context, client client.Client, binding *rbacv1.ClusterRoleBinding) error {
111+
return client.Create(ctx, binding)
130112
},
131-
updateClusterRoleBinding: func(ctx context.Context, client client.Client, binding *rbacv1.ClusterRoleBinding) (*rbacv1.ClusterRoleBinding, error) {
132-
if err := client.Update(ctx, binding); err != nil {
133-
return nil, err
134-
}
135-
return binding, nil
113+
updateClusterRoleBinding: func(ctx context.Context, client client.Client, binding *rbacv1.ClusterRoleBinding) error {
114+
return client.Update(ctx, binding)
136115
},
137116
deleteClusterRoleBinding: func(ctx context.Context, client client.Client, name string) error {
138117
binding := &rbacv1.ClusterRoleBinding{
@@ -144,29 +123,21 @@ func NewClusterBindingReconciler(
144123
var ns v1.Namespace
145124
key := types.NamespacedName{Name: name}
146125
if err := cache.Get(ctx, key, &ns); err != nil {
147-
return nil, fmt.Errorf("failed to get Namespace %q: %w", name, err)
126+
return nil, err
148127
}
149128
return &ns, nil
150129
},
151-
createRoleBinding: func(ctx context.Context, client client.Client, ns string, binding *rbacv1.RoleBinding) (*rbacv1.RoleBinding, error) {
152-
binding.Namespace = ns
153-
if err := client.Create(ctx, binding); err != nil {
154-
return nil, err
155-
}
156-
return binding, nil
130+
createRoleBinding: func(ctx context.Context, client client.Client, binding *rbacv1.RoleBinding) error {
131+
return client.Create(ctx, binding)
157132
},
158-
updateRoleBinding: func(ctx context.Context, client client.Client, ns string, binding *rbacv1.RoleBinding) (*rbacv1.RoleBinding, error) {
159-
binding.Namespace = ns
160-
if err := client.Update(ctx, binding); err != nil {
161-
return nil, err
162-
}
163-
return binding, nil
133+
updateRoleBinding: func(ctx context.Context, client client.Client, binding *rbacv1.RoleBinding) error {
134+
return client.Update(ctx, binding)
164135
},
165136
getRoleBinding: func(ctx context.Context, cache cache.Cache, ns, name string) (*rbacv1.RoleBinding, error) {
166137
var binding rbacv1.RoleBinding
167138
key := types.NamespacedName{Namespace: ns, Name: name}
168139
if err := cache.Get(ctx, key, &binding); err != nil {
169-
return nil, fmt.Errorf("failed to get RoleBinding %q in namespace %q: %w", name, ns, err)
140+
return nil, err
170141
}
171142
return &binding, nil
172143
},
@@ -244,6 +215,7 @@ func (r *ClusterBindingReconciler) SetupWithManager(mgr mcmanager.Manager) error
244215
&kubebindv1alpha2.APIServiceExport{},
245216
mapAPIResourceSchema,
246217
).
218+
WithOptions(r.opts).
247219
Named(controllerName).
248220
Complete(r)
249221
}

0 commit comments

Comments
 (0)