/
garden_clientmap.go
158 lines (131 loc) · 5.75 KB
/
garden_clientmap.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// Copyright 2023 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package internal
import (
"context"
"fmt"
"github.com/go-logr/logr"
corev1 "k8s.io/api/core/v1"
componentbaseconfig "k8s.io/component-base/config"
"k8s.io/utils/clock"
"sigs.k8s.io/controller-runtime/pkg/client"
v1beta1constants "github.com/gardener/gardener/pkg/apis/core/v1beta1/constants"
"github.com/gardener/gardener/pkg/client/kubernetes"
"github.com/gardener/gardener/pkg/client/kubernetes/clientmap"
operatorclient "github.com/gardener/gardener/pkg/operator/client"
"github.com/gardener/gardener/pkg/utils"
"github.com/gardener/gardener/pkg/utils/gardener/tokenrequest"
)
// gardenClientMap is a ClientMap for requesting and storing clients for virtual gardens.
type gardenClientMap struct {
clientmap.ClientMap
}
// NewGardenClientMap creates a new gardenClientMap with the given factory.
func NewGardenClientMap(log logr.Logger, factory *GardenClientSetFactory) clientmap.ClientMap {
logger := log.WithValues("clientmap", "GardenClientMap")
factory.log = logger
return &gardenClientMap{
ClientMap: NewGenericClientMap(factory, logger, clock.RealClock{}),
}
}
// GardenClientSetFactory is a ClientSetFactory that can produce new ClientSets to virtual gardens.
type GardenClientSetFactory struct {
// RuntimeClient is the runtime cluster client.
RuntimeClient client.Client
// ClientConnectionConfiguration is the configuration that will be used by created ClientSets.
ClientConnectionConfig componentbaseconfig.ClientConnectionConfiguration
// GardenNamespace is the namespace the virtual gardens run in. Defaults to `garden` if not set.
GardenNamespace string
// log is a logger for logging entries related to creating Garden ClientSets.
log logr.Logger
}
// CalculateClientSetHash calculates a SHA256 hash of the kubeconfig in the 'gardener' secret in the Garden's Garden namespace.
func (f *GardenClientSetFactory) CalculateClientSetHash(ctx context.Context, k clientmap.ClientSetKey) (string, error) {
_, hash, err := f.getSecretAndComputeHash(ctx, k)
if err != nil {
return "", err
}
return hash, nil
}
// NewClientSet creates a new ClientSet for a Garden cluster.
func (f *GardenClientSetFactory) NewClientSet(ctx context.Context, k clientmap.ClientSetKey) (kubernetes.Interface, string, error) {
kubeconfigSecret, hash, err := f.getSecretAndComputeHash(ctx, k)
if err != nil {
return nil, "", err
}
// Kubeconfig secrets are created with empty authinfo and it's expected that gardener-resource-manager eventually
// populates a token, so let's check whether the read secret already contains authinfo
tokenPopulated, err := tokenrequest.IsTokenPopulated(kubeconfigSecret)
if err != nil {
return nil, "", err
}
if !tokenPopulated {
return nil, "", fmt.Errorf("token for virtual garden kubeconfig was not populated yet")
}
clientSet, err := NewClientFromSecretObject(kubeconfigSecret,
kubernetes.WithClientConnectionOptions(f.ClientConnectionConfig),
kubernetes.WithClientOptions(client.Options{Scheme: operatorclient.VirtualScheme}),
kubernetes.WithDisabledCachedClient(),
)
if err != nil {
return nil, "", err
}
return clientSet, hash, nil
}
func (f *GardenClientSetFactory) getSecretAndComputeHash(ctx context.Context, k clientmap.ClientSetKey) (*corev1.Secret, string, error) {
_, ok := k.(GardenClientSetKey)
if !ok {
return nil, "", fmt.Errorf("unsupported ClientSetKey: expected %T got %T", GardenClientSetKey{}, k)
}
gardenNamespace := f.getGardenNamespace()
kubeconfigSecret := &corev1.Secret{}
if err := f.RuntimeClient.Get(ctx, client.ObjectKey{Namespace: gardenNamespace, Name: f.secretName(gardenNamespace)}, kubeconfigSecret); err != nil {
return nil, "", err
}
return kubeconfigSecret, utils.ComputeSHA256Hex(kubeconfigSecret.Data[kubernetes.KubeConfig]), nil
}
func (f *GardenClientSetFactory) secretName(gardenNamespace string) string {
secretName := v1beta1constants.SecretNameGardener
// Prefer the internal host if available
addr, err := LookupHost(fmt.Sprintf("virtual-garden-%s.%s.svc.cluster.local", v1beta1constants.DeploymentNameKubeAPIServer, gardenNamespace))
if err != nil {
f.log.Info("Service DNS name lookup of virtual-garden-kube-apiserver failed, falling back to external kubeconfig", "error", err)
} else if len(addr) > 0 {
secretName = v1beta1constants.SecretNameGardenerInternal
}
return secretName
}
var _ clientmap.Invalidate = &GardenClientSetFactory{}
// InvalidateClient invalidates information cached for the given ClientSetKey in the factory.
func (f *GardenClientSetFactory) InvalidateClient(k clientmap.ClientSetKey) error {
_, ok := k.(GardenClientSetKey)
if !ok {
return fmt.Errorf("unsupported ClientSetKey: expected %T got %T", GardenClientSetKey{}, k)
}
return nil
}
func (f *GardenClientSetFactory) getGardenNamespace() string {
if f.GardenNamespace == "" {
return v1beta1constants.GardenNamespace
}
return f.GardenNamespace
}
// GardenClientSetKey is a ClientSetKey for a Garden cluster.
type GardenClientSetKey struct {
Name string
}
// Key returns the string representation of the ClientSetKey.
func (k GardenClientSetKey) Key() string {
return k.Name
}