tokens.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. // Copyright 2019 Yunion
  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. package tokens
  15. import (
  16. "context"
  17. "encoding/base64"
  18. "strings"
  19. "time"
  20. v1 "k8s.io/api/core/v1"
  21. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  22. "k8s.io/client-go/kubernetes"
  23. corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
  24. "k8s.io/client-go/rest"
  25. bootstrapapi "k8s.io/cluster-bootstrap/token/api"
  26. bootstraputil "k8s.io/cluster-bootstrap/token/util"
  27. "yunion.io/x/jsonutils"
  28. "yunion.io/x/log"
  29. "yunion.io/x/pkg/errors"
  30. "yunion.io/x/pkg/util/wait"
  31. "yunion.io/x/pkg/utils"
  32. "yunion.io/x/onecloud/pkg/util/k8s/kubeadm"
  33. )
  34. func GetClusterConfig() (*rest.Config, error) {
  35. // Load kubernetes config inside cluster
  36. cfg, err := rest.InClusterConfig()
  37. if err != nil {
  38. return nil, errors.Wrap(err, "get kubernetes config inside cluster")
  39. }
  40. return cfg, nil
  41. }
  42. func GetClient() (kubernetes.Interface, error) {
  43. cfg, err := GetClusterConfig()
  44. if err != nil {
  45. return nil, err
  46. }
  47. return kubernetes.NewForConfig(cfg)
  48. }
  49. func GetCoreClient() (corev1.CoreV1Interface, error) {
  50. cli, err := GetClient()
  51. if err != nil {
  52. return nil, err
  53. }
  54. return cli.CoreV1(), nil
  55. }
  56. func IsInsideKubernetesCluster() (bool, error) {
  57. _, err := GetCoreClient()
  58. if err != nil {
  59. return false, err
  60. }
  61. return true, nil
  62. }
  63. func GetControlPlaneEndpoint() (string, error) {
  64. coreCli, err := GetCoreClient()
  65. if err != nil {
  66. return "", errors.Wrap(err, "get cluster control plane endpoint")
  67. }
  68. configMap, err := coreCli.ConfigMaps(metav1.NamespaceSystem).Get(context.Background(), kubeadm.KubeadmConfigConfigMap, metav1.GetOptions{})
  69. if err != nil {
  70. return "", errors.Wrap(err, "get kubeadm cluster config")
  71. }
  72. clusterConfig, err := kubeadm.GetClusterConfigurationFromConfigMap(configMap)
  73. if err != nil {
  74. return "", errors.Wrap(err, "get kubeadm cluster configuration")
  75. }
  76. return clusterConfig.ControlPlaneEndpoint, nil
  77. }
  78. func GetNodeJoinToken() (string, error) {
  79. coreCli, err := GetCoreClient()
  80. if err != nil {
  81. return "", errors.Wrap(err, "get node join token")
  82. }
  83. bootstrapToken, err := NewBootstrap(coreCli, 24*time.Hour)
  84. if err != nil {
  85. return "", errors.Wrap(err, "failed to create new bootstrap token")
  86. }
  87. return bootstrapToken, nil
  88. }
  89. func GetImageRegistries() ([]string, error) {
  90. cli, err := GetClient()
  91. if err != nil {
  92. return nil, errors.Wrap(err, "get k8s client")
  93. }
  94. nodes, err := cli.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{})
  95. if err != nil {
  96. return nil, errors.Wrap(err, "get k8s nodes")
  97. }
  98. masterNodes := make([]*v1.Node, 0)
  99. for _, n := range nodes.Items {
  100. if _, ok := n.Labels["node-role.kubernetes.io/master"]; ok {
  101. masterNodes = append(masterNodes, &n)
  102. }
  103. }
  104. if len(masterNodes) == 0 {
  105. return nil, errors.Wrap(err, "not found master nodes")
  106. }
  107. regs := make([]string, 0)
  108. getImg := func(img v1.ContainerImage) {
  109. for _, name := range img.Names {
  110. parts := strings.Split(name, "/")
  111. if len(parts) == 0 {
  112. continue
  113. }
  114. imgRepo := parts[0]
  115. if !strings.Contains(imgRepo, ".") {
  116. // filter image like: grafana, nginx
  117. continue
  118. }
  119. if utils.IsInStringArray(imgRepo, regs) {
  120. continue
  121. }
  122. regs = append(regs, imgRepo)
  123. }
  124. }
  125. for _, n := range masterNodes {
  126. for _, img := range n.Status.Images {
  127. getImg(img)
  128. }
  129. }
  130. return regs, nil
  131. }
  132. // TODO: move to other packages
  133. type DockerDaemonConfig struct {
  134. Bridge string `json:"bridge"`
  135. Iptables bool `json:"iptables"`
  136. ExecOpts []string `json:"exec-opts"`
  137. DataRoot string `json:"data-root"`
  138. LogDriver string `json:"log-driver"`
  139. LogOpts map[string]string `json:"log-opts"`
  140. RegistryMirrors []string `json:"registry-mirrors"`
  141. InsecureRegistries []string `json:"insecure-registries"`
  142. LiveRestore bool `json:"live-restore"`
  143. }
  144. func GetDockerDaemonConfig() (*DockerDaemonConfig, error) {
  145. regs, err := GetImageRegistries()
  146. if err != nil {
  147. return nil, err
  148. }
  149. return &DockerDaemonConfig{
  150. Bridge: "none",
  151. Iptables: false,
  152. ExecOpts: []string{"native.cgroupdriver=systemd"},
  153. DataRoot: "/opt/docker",
  154. LogDriver: "json-file",
  155. LogOpts: map[string]string{
  156. "max-size": "100m",
  157. },
  158. InsecureRegistries: regs,
  159. LiveRestore: true,
  160. }, nil
  161. }
  162. func GetDockerDaemonContent() (string, error) {
  163. cfg, err := GetDockerDaemonConfig()
  164. if err != nil {
  165. return "", nil
  166. }
  167. content := jsonutils.Marshal(cfg).PrettyString()
  168. return base64.StdEncoding.EncodeToString([]byte(content)), nil
  169. }
  170. var (
  171. MaximumRetries = 5
  172. )
  173. // NewBootstrap attempts to create a token with the given ID.
  174. func NewBootstrap(client corev1.SecretsGetter, ttl time.Duration) (string, error) {
  175. token, err := bootstraputil.GenerateBootstrapToken()
  176. if err != nil {
  177. return "", errors.Wrap(err, "unable to generate bootstrap token")
  178. }
  179. substrs := bootstraputil.BootstrapTokenRegexp.FindStringSubmatch(token)
  180. if len(substrs) != 3 {
  181. return "", errors.Wrapf(err, "the bootstrap token %q was not of the form %q", token, bootstrapapi.BootstrapTokenPattern)
  182. }
  183. tokenID := substrs[1]
  184. tokenSecret := substrs[2]
  185. secretName := bootstraputil.BootstrapTokenSecretName(tokenID)
  186. secretToken := &v1.Secret{
  187. ObjectMeta: metav1.ObjectMeta{
  188. Name: secretName,
  189. Namespace: metav1.NamespaceSystem,
  190. },
  191. Type: bootstrapapi.SecretTypeBootstrapToken,
  192. Data: map[string][]byte{
  193. bootstrapapi.BootstrapTokenIDKey: []byte(tokenID),
  194. bootstrapapi.BootstrapTokenSecretKey: []byte(tokenSecret),
  195. bootstrapapi.BootstrapTokenExpirationKey: []byte(time.Now().UTC().Add(ttl).Format(time.RFC3339)),
  196. bootstrapapi.BootstrapTokenUsageSigningKey: []byte("true"),
  197. bootstrapapi.BootstrapTokenUsageAuthentication: []byte("true"),
  198. bootstrapapi.BootstrapTokenExtraGroupsKey: []byte("system:bootstrappers:kubeadm:default-node-token"),
  199. bootstrapapi.BootstrapTokenDescriptionKey: []byte("Node join token generate by 'onecloud region server'"),
  200. },
  201. }
  202. err = TryRunCommand(func() error {
  203. _, err := client.Secrets(secretToken.ObjectMeta.Namespace).Create(context.Background(), secretToken, metav1.CreateOptions{})
  204. log.Errorf("create secrets %s/%s error: %v", secretToken.GetNamespace(), secretToken.GetName(), err)
  205. return err
  206. }, MaximumRetries)
  207. if err != nil {
  208. return "", errors.Wrap(err, "unable to create secret")
  209. }
  210. return token, nil
  211. }
  212. // TryRunCommand runs a function a maximum of failureThreshold times, and retries on error. If failureThreshold is hit; the last error is returned
  213. func TryRunCommand(f func() error, failureThreshold int) error {
  214. backoff := wait.Backoff{
  215. Duration: 5 * time.Second,
  216. Factor: 2, // double the timeout for every failure
  217. Steps: failureThreshold,
  218. }
  219. return wait.ExponentialBackoff(backoff, func() (bool, error) {
  220. err := f()
  221. if err != nil {
  222. // Retry until the timeout
  223. return false, nil
  224. }
  225. // The last f() call was a success, return cleanly
  226. return true, nil
  227. })
  228. }