Skip to content

Commit 600c366

Browse files
committed
implement controller and reconciler
On-behalf-of: @SAP [email protected]
1 parent 534b16b commit 600c366

File tree

7 files changed

+620
-8
lines changed

7 files changed

+620
-8
lines changed

cmd/main.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import (
3939
"github.com/kcp-dev/kcp-operator/internal/controller/cacheserver"
4040
"github.com/kcp-dev/kcp-operator/internal/controller/frontproxy"
4141
"github.com/kcp-dev/kcp-operator/internal/controller/kubeconfig"
42+
kubeconfigrbac "github.com/kcp-dev/kcp-operator/internal/controller/kubeconfig-rbac"
4243
"github.com/kcp-dev/kcp-operator/internal/controller/rootshard"
4344
"github.com/kcp-dev/kcp-operator/internal/controller/shard"
4445
"github.com/kcp-dev/kcp-operator/internal/reconciling"
@@ -188,6 +189,13 @@ func main() {
188189
setupLog.Error(err, "unable to create controller", "controller", "Kubeconfig")
189190
os.Exit(1)
190191
}
192+
if err = (&kubeconfigrbac.KubeconfigRBACReconciler{
193+
Client: mgr.GetClient(),
194+
Scheme: mgr.GetScheme(),
195+
}).SetupWithManager(mgr); err != nil {
196+
setupLog.Error(err, "unable to create controller", "controller", "KubeconfigRBAC")
197+
os.Exit(1)
198+
}
191199
// +kubebuilder:scaffold:builder
192200

193201
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {

internal/client/clients.go

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
Copyright 2024 The KCP 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 client
18+
19+
import (
20+
"context"
21+
"fmt"
22+
23+
"github.com/kcp-dev/logicalcluster/v3"
24+
25+
corev1 "k8s.io/api/core/v1"
26+
"k8s.io/apimachinery/pkg/runtime"
27+
"k8s.io/apimachinery/pkg/types"
28+
"k8s.io/client-go/rest"
29+
ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client"
30+
31+
"github.com/kcp-dev/kcp-operator/internal/resources"
32+
operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1"
33+
)
34+
35+
func NewRootShardClient(ctx context.Context, c ctrlruntimeclient.Client, rootShard *operatorv1alpha1.RootShard, cluster logicalcluster.Name, scheme *runtime.Scheme) (ctrlruntimeclient.Client, error) {
36+
baseUrl := fmt.Sprintf("https://%s.%s.svc.cluster.local:6443", resources.GetRootShardServiceName(rootShard), rootShard.Namespace)
37+
38+
if !cluster.Empty() {
39+
baseUrl = fmt.Sprintf("%s/clusters/%s", baseUrl, cluster.String())
40+
}
41+
42+
return newClient(ctx, c, baseUrl, scheme, rootShard, nil, nil)
43+
}
44+
45+
func NewRootShardProxyClient(ctx context.Context, c ctrlruntimeclient.Client, rootShard *operatorv1alpha1.RootShard, cluster logicalcluster.Name, scheme *runtime.Scheme) (ctrlruntimeclient.Client, error) {
46+
baseUrl := fmt.Sprintf("https://%s.%s.svc.cluster.local:6443", resources.GetRootShardProxyServiceName(rootShard), rootShard.Namespace)
47+
48+
if !cluster.Empty() {
49+
baseUrl = fmt.Sprintf("%s/clusters/%s", baseUrl, cluster.String())
50+
}
51+
52+
return newClient(ctx, c, baseUrl, scheme, rootShard, nil, nil)
53+
}
54+
55+
func NewShardClient(ctx context.Context, c ctrlruntimeclient.Client, shard *operatorv1alpha1.Shard, cluster logicalcluster.Name, scheme *runtime.Scheme) (ctrlruntimeclient.Client, error) {
56+
baseUrl := fmt.Sprintf("https://%s.%s.svc.cluster.local:6443", resources.GetShardServiceName(shard), shard.Namespace)
57+
58+
if !cluster.Empty() {
59+
baseUrl = fmt.Sprintf("%s/clusters/%s", baseUrl, cluster.String())
60+
}
61+
62+
return newClient(ctx, c, baseUrl, scheme, nil, shard, nil)
63+
}
64+
65+
func newClient(
66+
ctx context.Context,
67+
c ctrlruntimeclient.Client,
68+
url string,
69+
scheme *runtime.Scheme,
70+
// only one of these three should be provided, the others nil
71+
rootShard *operatorv1alpha1.RootShard,
72+
shard *operatorv1alpha1.Shard,
73+
frontProxy *operatorv1alpha1.FrontProxy,
74+
) (ctrlruntimeclient.Client, error) {
75+
tlsConfig, err := getTLSConfig(ctx, c, rootShard, shard, frontProxy)
76+
if err != nil {
77+
return nil, fmt.Errorf("failed to determine TLS settings: %w", err)
78+
}
79+
80+
cfg := &rest.Config{
81+
Host: url,
82+
TLSClientConfig: tlsConfig,
83+
}
84+
85+
return ctrlruntimeclient.New(cfg, ctrlruntimeclient.Options{Scheme: scheme})
86+
}
87+
88+
func getTLSConfig(ctx context.Context, c ctrlruntimeclient.Client, rootShard *operatorv1alpha1.RootShard, shard *operatorv1alpha1.Shard, frontProxy *operatorv1alpha1.FrontProxy) (rest.TLSClientConfig, error) {
89+
rootShard, err := getRootShard(ctx, c, rootShard, shard, frontProxy)
90+
if err != nil {
91+
return rest.TLSClientConfig{}, fmt.Errorf("failed to determine effective RootShard: %w", err)
92+
}
93+
94+
// get the secret for the kcp-operator client cert
95+
key := types.NamespacedName{
96+
Namespace: rootShard.Namespace,
97+
Name: resources.GetRootShardCertificateName(rootShard, operatorv1alpha1.OperatorCertificate),
98+
}
99+
100+
certSecret := &corev1.Secret{}
101+
if err := c.Get(ctx, key, certSecret); err != nil {
102+
return rest.TLSClientConfig{}, fmt.Errorf("failed to get root shard proxy Secret: %w", err)
103+
}
104+
105+
return rest.TLSClientConfig{
106+
CAData: certSecret.Data["ca.crt"],
107+
CertData: certSecret.Data["tls.crt"],
108+
KeyData: certSecret.Data["tls.key"],
109+
}, nil
110+
}
111+
112+
func getRootShard(ctx context.Context, c ctrlruntimeclient.Client, rootShard *operatorv1alpha1.RootShard, shard *operatorv1alpha1.Shard, frontProxy *operatorv1alpha1.FrontProxy) (*operatorv1alpha1.RootShard, error) {
113+
if rootShard != nil {
114+
return rootShard, nil
115+
}
116+
117+
var ref *corev1.LocalObjectReference
118+
119+
switch {
120+
case shard != nil:
121+
ref = shard.Spec.RootShard.Reference
122+
123+
case frontProxy != nil:
124+
ref = frontProxy.Spec.RootShard.Reference
125+
126+
default:
127+
panic("Must be called with either RootShard, Shard or FrontProxy.")
128+
}
129+
130+
rootShard = &operatorv1alpha1.RootShard{}
131+
if err := c.Get(ctx, types.NamespacedName{Namespace: rootShard.Namespace, Name: ref.Name}, rootShard); err != nil {
132+
return nil, fmt.Errorf("failed to get RootShard: %w", err)
133+
}
134+
135+
return rootShard, nil
136+
}

internal/client/frontproxy.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
Copyright 2024 The KCP 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 client
18+
19+
import (
20+
"context"
21+
"errors"
22+
"fmt"
23+
24+
"github.com/kcp-dev/logicalcluster/v3"
25+
26+
"k8s.io/apimachinery/pkg/runtime"
27+
"k8s.io/apimachinery/pkg/types"
28+
ctrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client"
29+
30+
operatorv1alpha1 "github.com/kcp-dev/kcp-operator/sdk/apis/operator/v1alpha1"
31+
)
32+
33+
func NewInternalKubeconfigClient(ctx context.Context, c ctrlruntimeclient.Client, kubeconfig *operatorv1alpha1.Kubeconfig, cluster logicalcluster.Name, scheme *runtime.Scheme) (ctrlruntimeclient.Client, error) {
34+
target := kubeconfig.Spec.Target
35+
36+
switch {
37+
case target.RootShardRef != nil:
38+
rootShard := &operatorv1alpha1.RootShard{}
39+
if err := c.Get(ctx, types.NamespacedName{Name: target.RootShardRef.Name, Namespace: kubeconfig.Namespace}, rootShard); err != nil {
40+
return nil, fmt.Errorf("failed to get RootShard: %w", err)
41+
}
42+
43+
return NewRootShardClient(ctx, c, rootShard, cluster, scheme)
44+
45+
case target.ShardRef != nil:
46+
shard := &operatorv1alpha1.Shard{}
47+
if err := c.Get(ctx, types.NamespacedName{Name: target.ShardRef.Name, Namespace: kubeconfig.Namespace}, shard); err != nil {
48+
return nil, fmt.Errorf("failed to get Shard: %w", err)
49+
}
50+
51+
return NewShardClient(ctx, c, shard, cluster, scheme)
52+
53+
case target.FrontProxyRef != nil:
54+
frontProxy := &operatorv1alpha1.FrontProxy{}
55+
if err := c.Get(ctx, types.NamespacedName{Name: target.FrontProxyRef.Name, Namespace: kubeconfig.Namespace}, frontProxy); err != nil {
56+
return nil, fmt.Errorf("failed to get FrontProxy: %w", err)
57+
}
58+
59+
rootShard := &operatorv1alpha1.RootShard{}
60+
if err := c.Get(ctx, types.NamespacedName{Name: frontProxy.Spec.RootShard.Reference.Name, Namespace: kubeconfig.Namespace}, rootShard); err != nil {
61+
return nil, fmt.Errorf("failed to get RootShard: %w", err)
62+
}
63+
64+
return NewRootShardProxyClient(ctx, c, rootShard, cluster, scheme)
65+
66+
default:
67+
return nil, errors.New("no valid target configured in Kubeconfig: neither rootShard, shard nor frontProxy ref set")
68+
}
69+
}

0 commit comments

Comments
 (0)