github.com/erda-project/erda-infra@v1.0.10-0.20240327085753-f3a249292aeb/providers/kubernetes/kubernetes.go (about) 1 // Copyright (c) 2021 Terminus, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package kubernetes 16 17 import ( 18 "context" 19 "fmt" 20 "io/ioutil" 21 "os" 22 "path/filepath" 23 "reflect" 24 "time" 25 26 "k8s.io/client-go/kubernetes" 27 "k8s.io/client-go/rest" 28 "k8s.io/client-go/tools/clientcmd" 29 certutil "k8s.io/client-go/util/cert" 30 31 "github.com/erda-project/erda-infra/base/servicehub" 32 ) 33 34 // Interface . 35 type Interface interface { 36 Client() *kubernetes.Clientset 37 } 38 39 var clientType = reflect.TypeOf((*kubernetes.Clientset)(nil)) 40 41 type config struct { 42 MasterURL string `file:"master_url"` 43 ConfigPath string `file:"config_path"` 44 RootCAFile string `file:"root_ca_file"` 45 TokenFile string `file:"token_file"` 46 InsecureSkipVerify bool `file:"insecure_skip_verify"` 47 ConnectionCheck bool `file:"connection_check" default:"true"` 48 } 49 50 // provider . 51 type provider struct { 52 Cfg *config 53 client *kubernetes.Clientset 54 } 55 56 // Init . 57 func (p *provider) Init(ctx servicehub.Context) error { 58 59 config, err := p.createRestConfig() 60 if err != nil { 61 return fmt.Errorf("create rest config err: %w", err) 62 } 63 64 // create the clientset 65 clientset, err := kubernetes.NewForConfig(config) 66 if err != nil { 67 return fmt.Errorf("create k8s client err: %w", err) 68 } 69 70 if p.Cfg.ConnectionCheck { 71 if err := HealthCheck(clientset, 30*time.Second); err != nil { 72 return fmt.Errorf("check connection err: %w", err) 73 } 74 } 75 76 p.client = clientset 77 return nil 78 } 79 80 func (p *provider) Client() *kubernetes.Clientset { return p.client } 81 82 // Provide . 83 func (p *provider) Provide(ctx servicehub.DependencyContext, args ...interface{}) interface{} { 84 if ctx.Type() == clientType || ctx.Service() == "kube-client" || ctx.Service() == "kubernetes-client" { 85 return p.client 86 } 87 return p 88 } 89 90 func (p *provider) createRestConfig() (*rest.Config, error) { 91 var config *rest.Config 92 if p.Cfg.MasterURL != "" { 93 if p.Cfg.RootCAFile != "" && p.Cfg.TokenFile != "" { 94 tlscfg := rest.TLSClientConfig{ 95 Insecure: p.Cfg.InsecureSkipVerify, 96 } 97 if _, err := certutil.NewPool(p.Cfg.RootCAFile); err != nil { 98 return nil, fmt.Errorf("expected to load root CA config from %s, but got err: %v", p.Cfg.RootCAFile, err) 99 } 100 tlscfg.CAFile = p.Cfg.RootCAFile 101 token, err := ioutil.ReadFile(p.Cfg.TokenFile) 102 if err != nil { 103 return nil, err 104 } 105 106 config = &rest.Config{ 107 TLSClientConfig: tlscfg, 108 Host: p.Cfg.MasterURL, 109 BearerTokenFile: p.Cfg.TokenFile, 110 BearerToken: string(token), 111 } 112 } else { 113 if p.Cfg.ConfigPath == "" { 114 if home := homeDir(); home != "" { 115 p.Cfg.ConfigPath = filepath.Join(home, ".kube", "config") 116 } 117 } 118 if _, err := os.Stat(p.Cfg.ConfigPath); err == nil { 119 // use the current context in kubeconfig 120 cfg, err := clientcmd.BuildConfigFromFlags(p.Cfg.MasterURL, p.Cfg.ConfigPath) 121 if err != nil { 122 return nil, fmt.Errorf("fail to build kube config: %s", err) 123 } 124 config = cfg 125 } 126 } 127 } else { 128 cfg, err := rest.InClusterConfig() 129 if err != nil { 130 return nil, fmt.Errorf("build from inCluster err: %w", err) 131 } 132 config = cfg 133 } 134 return config, nil 135 } 136 137 // HealthCheck check apiserver connection 138 func HealthCheck(client *kubernetes.Clientset, to time.Duration) error { 139 ctx, _ := context.WithTimeout(context.TODO(), to) 140 _, err := client.Discovery().RESTClient().Get().AbsPath("/healthz").DoRaw(ctx) 141 return err 142 } 143 144 func homeDir() string { 145 if h := os.Getenv("HOME"); h != "" { 146 return h 147 } 148 return os.Getenv("USERPROFILE") // windows 149 } 150 151 func init() { 152 servicehub.Register("kubernetes", &servicehub.Spec{ 153 Services: []string{"kubernetes", "kubernetes-client", "kube-client"}, 154 Types: []reflect.Type{ 155 reflect.TypeOf((*Interface)(nil)).Elem(), 156 clientType, 157 }, 158 Description: "kubernetes", 159 ConfigFunc: func() interface{} { return &config{} }, 160 Creator: func() servicehub.Provider { 161 return &provider{} 162 }, 163 }) 164 }