@@ -21,6 +21,7 @@ import (
2121 "os/user"
2222 "path/filepath"
2323
24+ "k8s.io/apiserver/pkg/server/egressselector"
2425 "k8s.io/client-go/rest"
2526 "k8s.io/client-go/tools/clientcmd"
2627 clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
@@ -32,13 +33,44 @@ var (
3233 log = ctrl .Log .WithName ("configutils" )
3334)
3435
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+
3562// GetConfigOptions are options to supply for a GetConfig call.
3663type GetConfigOptions struct {
3764 // Context is the kubeconfig context to load.
3865 Context string
3966 // Kubeconfig is the path to a kubeconfig to load.
4067 // If unset, the '--kubeconfig' flag is used.
4168 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
4274}
4375
4476// ApplyToGetConfig implements GetConfigOption.
@@ -49,6 +81,12 @@ func (o *GetConfigOptions) ApplyToGetConfig(o2 *GetConfigOptions) {
4981 if o .Kubeconfig != nil {
5082 o2 .Kubeconfig = pointer .String (* o .Kubeconfig )
5183 }
84+ if o .EgressSelectorConfig != "" {
85+ o2 .EgressSelectorConfig = o .EgressSelectorConfig
86+ }
87+ if o .EgressSelectionName != "" {
88+ o2 .EgressSelectionName = o .EgressSelectionName
89+ }
5290}
5391
5492// ApplyOptions applies all GetConfigOption tro this GetConfigOptions.
@@ -74,6 +112,20 @@ func (c Context) ApplyToGetConfig(o *GetConfigOptions) {
74112 o .Context = string (c )
75113}
76114
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+
77129// GetConfigOption are options to a GetConfig call.
78130type GetConfigOption interface {
79131 // ApplyToGetConfig modifies the underlying GetConfigOptions.
@@ -105,6 +157,12 @@ func getKubeconfigFlag() string {
105157 return f .Value .String ()
106158}
107159
160+ func setGetConfigOptionsDefaults (o * GetConfigOptions ) {
161+ if o .EgressSelectionName == "" {
162+ o .EgressSelectionName = EgressSelectionNameControlPlane
163+ }
164+ }
165+
108166// GetConfig creates a *rest.Config for talking to a Kubernetes API server.
109167// Kubeconfig / the '--kubeconfig' flag instruct to use the kubeconfig file at that location.
110168// Otherwise, will assume running in cluster and use the cluster provided kubeconfig.
@@ -124,6 +182,7 @@ func getKubeconfigFlag() string {
124182func GetConfig (opts ... GetConfigOption ) (* rest.Config , error ) {
125183 o := & GetConfigOptions {}
126184 o .ApplyOptions (opts )
185+ setGetConfigOptionsDefaults (o )
127186
128187 var kubeconfig string
129188 if o .Kubeconfig != nil {
@@ -132,9 +191,22 @@ func GetConfig(opts ...GetConfigOption) (*rest.Config, error) {
132191 kubeconfig = getKubeconfigFlag ()
133192 }
134193
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 ) {
135207 // If a flag is specified with the config location, use that
136208 if len (kubeconfig ) > 0 {
137- return loadConfigWithContext ("" , & clientcmd.ClientConfigLoadingRules {ExplicitPath : kubeconfig }, o . Context )
209+ return loadConfigWithContext ("" , & clientcmd.ClientConfigLoadingRules {ExplicitPath : kubeconfig }, context )
138210 }
139211
140212 // If the recommended kubeconfig env variable is not specified,
@@ -155,7 +227,39 @@ func GetConfig(opts ...GetConfigOption) (*rest.Config, error) {
155227 loadingRules .Precedence = append (loadingRules .Precedence , filepath .Join (u .HomeDir , clientcmd .RecommendedHomeDir , clientcmd .RecommendedFileName ))
156228 }
157229
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
159263}
160264
161265// GetConfigOrDie creates a *rest.Config for talking to a Kubernetes apiserver.
0 commit comments