@@ -21,6 +21,7 @@ import (
21
21
"os/user"
22
22
"path/filepath"
23
23
24
+ "k8s.io/apiserver/pkg/server/egressselector"
24
25
"k8s.io/client-go/rest"
25
26
"k8s.io/client-go/tools/clientcmd"
26
27
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
@@ -32,13 +33,44 @@ var (
32
33
log = ctrl .Log .WithName ("configutils" )
33
34
)
34
35
36
+ // EgressSelectionName is the name of the egress configuration to use.
37
+ type EgressSelectionName string
38
+
39
+ const (
40
+ // EgressSelectionNameControlPlane instructs to use the controlplane egress selection.
41
+ EgressSelectionNameControlPlane EgressSelectionName = "controlplane"
42
+ // EgressSelectionNameEtcd instructs to use the etcd egress selection.
43
+ EgressSelectionNameEtcd EgressSelectionName = "etcd"
44
+ // EgressSelectionNameCluster instructs to use the cluster egress selection.
45
+ EgressSelectionNameCluster EgressSelectionName = "cluster"
46
+ )
47
+
48
+ // NetworkContext returns the corresponding network context of the egress selection.
49
+ func (n EgressSelectionName ) NetworkContext () (egressselector.NetworkContext , error ) {
50
+ switch n {
51
+ case EgressSelectionNameControlPlane :
52
+ return egressselector .ControlPlane .AsNetworkContext (), nil
53
+ case EgressSelectionNameEtcd :
54
+ return egressselector .Etcd .AsNetworkContext (), nil
55
+ case EgressSelectionNameCluster :
56
+ return egressselector .Cluster .AsNetworkContext (), nil
57
+ default :
58
+ return egressselector.NetworkContext {}, fmt .Errorf ("unknown egress selection name %q" , n )
59
+ }
60
+ }
61
+
35
62
// GetConfigOptions are options to supply for a GetConfig call.
36
63
type GetConfigOptions struct {
37
64
// Context is the kubeconfig context to load.
38
65
Context string
39
66
// Kubeconfig is the path to a kubeconfig to load.
40
67
// If unset, the '--kubeconfig' flag is used.
41
68
Kubeconfig * string
69
+ // EgressSelectorConfig is the path to an egress selector config to load.
70
+ EgressSelectorConfig string
71
+ // EgressSelectionName is the name of the egress configuration to use.
72
+ // Defaults to EgressSelectionNameControlPlane.
73
+ EgressSelectionName EgressSelectionName
42
74
}
43
75
44
76
// ApplyToGetConfig implements GetConfigOption.
@@ -49,6 +81,12 @@ func (o *GetConfigOptions) ApplyToGetConfig(o2 *GetConfigOptions) {
49
81
if o .Kubeconfig != nil {
50
82
o2 .Kubeconfig = pointer .String (* o .Kubeconfig )
51
83
}
84
+ if o .EgressSelectorConfig != "" {
85
+ o2 .EgressSelectorConfig = o .EgressSelectorConfig
86
+ }
87
+ if o .EgressSelectionName != "" {
88
+ o2 .EgressSelectionName = o .EgressSelectionName
89
+ }
52
90
}
53
91
54
92
// ApplyOptions applies all GetConfigOption tro this GetConfigOptions.
@@ -74,6 +112,20 @@ func (c Context) ApplyToGetConfig(o *GetConfigOptions) {
74
112
o .Context = string (c )
75
113
}
76
114
115
+ // EgressSelectorConfig allows specifying the path to an egress selector config to use.
116
+ type EgressSelectorConfig string
117
+
118
+ func (c EgressSelectorConfig ) ApplyToGetConfig (o * GetConfigOptions ) {
119
+ o .EgressSelectorConfig = string (c )
120
+ }
121
+
122
+ type WithEgressSelectionName EgressSelectionName
123
+
124
+ // ApplyToGetConfig implements GetConfigOption.
125
+ func (w WithEgressSelectionName ) ApplyToGetConfig (o * GetConfigOptions ) {
126
+ o .EgressSelectionName = EgressSelectionName (w )
127
+ }
128
+
77
129
// GetConfigOption are options to a GetConfig call.
78
130
type GetConfigOption interface {
79
131
// ApplyToGetConfig modifies the underlying GetConfigOptions.
@@ -105,6 +157,12 @@ func getKubeconfigFlag() string {
105
157
return f .Value .String ()
106
158
}
107
159
160
+ func setGetConfigOptionsDefaults (o * GetConfigOptions ) {
161
+ if o .EgressSelectionName == "" {
162
+ o .EgressSelectionName = EgressSelectionNameControlPlane
163
+ }
164
+ }
165
+
108
166
// GetConfig creates a *rest.Config for talking to a Kubernetes API server.
109
167
// Kubeconfig / the '--kubeconfig' flag instruct to use the kubeconfig file at that location.
110
168
// Otherwise, will assume running in cluster and use the cluster provided kubeconfig.
@@ -124,6 +182,7 @@ func getKubeconfigFlag() string {
124
182
func GetConfig (opts ... GetConfigOption ) (* rest.Config , error ) {
125
183
o := & GetConfigOptions {}
126
184
o .ApplyOptions (opts )
185
+ setGetConfigOptionsDefaults (o )
127
186
128
187
var kubeconfig string
129
188
if o .Kubeconfig != nil {
@@ -132,9 +191,22 @@ func GetConfig(opts ...GetConfigOption) (*rest.Config, error) {
132
191
kubeconfig = getKubeconfigFlag ()
133
192
}
134
193
194
+ cfg , err := loadConfig (kubeconfig , o .Context )
195
+ if err != nil {
196
+ return nil , fmt .Errorf ("error loading config: %w" , err )
197
+ }
198
+
199
+ if err := applyEgressSelector (o .EgressSelectorConfig , o .EgressSelectionName , cfg ); err != nil {
200
+ return nil , fmt .Errorf ("error applying egress selector: %w" , err )
201
+ }
202
+
203
+ return cfg , nil
204
+ }
205
+
206
+ func loadConfig (kubeconfig , context string ) (* rest.Config , error ) {
135
207
// If a flag is specified with the config location, use that
136
208
if len (kubeconfig ) > 0 {
137
- return loadConfigWithContext ("" , & clientcmd.ClientConfigLoadingRules {ExplicitPath : kubeconfig }, o . Context )
209
+ return loadConfigWithContext ("" , & clientcmd.ClientConfigLoadingRules {ExplicitPath : kubeconfig }, context )
138
210
}
139
211
140
212
// If the recommended kubeconfig env variable is not specified,
@@ -155,7 +227,39 @@ func GetConfig(opts ...GetConfigOption) (*rest.Config, error) {
155
227
loadingRules .Precedence = append (loadingRules .Precedence , filepath .Join (u .HomeDir , clientcmd .RecommendedHomeDir , clientcmd .RecommendedFileName ))
156
228
}
157
229
158
- return loadConfigWithContext ("" , loadingRules , o .Context )
230
+ return loadConfigWithContext ("" , loadingRules , context )
231
+ }
232
+
233
+ func applyEgressSelector (egressSelectorConfig string , egressSelectionName EgressSelectionName , cfg * rest.Config ) error {
234
+ if egressSelectorConfig == "" {
235
+ return nil
236
+ }
237
+
238
+ networkContext , err := egressSelectionName .NetworkContext ()
239
+ if err != nil {
240
+ return fmt .Errorf ("error obtaining network context: %w" , err )
241
+ }
242
+
243
+ egressSelectorCfg , err := egressselector .ReadEgressSelectorConfiguration (egressSelectorConfig )
244
+ if err != nil {
245
+ return fmt .Errorf ("error reading egress selector configuration: %w" , err )
246
+ }
247
+
248
+ egressSelector , err := egressselector .NewEgressSelector (egressSelectorCfg )
249
+ if err != nil {
250
+ return fmt .Errorf ("error creating egress selector: %w" , err )
251
+ }
252
+
253
+ dial , err := egressSelector .Lookup (networkContext )
254
+ if err != nil {
255
+ return fmt .Errorf ("error looking up network context %s: %w" , networkContext .EgressSelectionName .String (), err )
256
+ }
257
+ if dial == nil {
258
+ return fmt .Errorf ("no dialer for network context %s" , networkContext .EgressSelectionName .String ())
259
+ }
260
+
261
+ cfg .Dial = dial
262
+ return nil
159
263
}
160
264
161
265
// GetConfigOrDie creates a *rest.Config for talking to a Kubernetes apiserver.
0 commit comments