How can I get a Kubernetes clientset in GO using a JSON service account key?

huangapple go评论120阅读模式
英文:

How can I get a Kubernetes clientset in GO using a JSON service account key?

问题

我需要使用从JSON服务帐户密钥文件中提取的令牌来创建一个Kubernetes clientset。

我在配置中明确提供了这个令牌,但它仍然在寻找Google Application-Default凭据,并且因为找不到而崩溃。

以下是我的代码:

  1. package main
  2. import (
  3. "context"
  4. "encoding/base64"
  5. "fmt"
  6. "io/ioutil"
  7. "golang.org/x/oauth2"
  8. "golang.org/x/oauth2/google"
  9. gke "google.golang.org/api/container/v1"
  10. "google.golang.org/api/option"
  11. "k8s.io/client-go/kubernetes"
  12. _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
  13. "k8s.io/client-go/tools/clientcmd"
  14. "k8s.io/client-go/tools/clientcmd/api"
  15. )
  16. const (
  17. projectID = "my_project_id"
  18. clusterName = "my_cluster_name"
  19. scope = "https://www.googleapis.com/auth/cloud-platform"
  20. )
  21. func main() {
  22. ctx := context.Background()
  23. // 读取JSON密钥并提取令牌
  24. data, err := ioutil.ReadFile("sa_key.json")
  25. if err != nil {
  26. panic(err)
  27. }
  28. creds, err := google.CredentialsFromJSON(ctx, data, scope)
  29. if err != nil {
  30. panic(err)
  31. }
  32. token, err := creds.TokenSource.Token()
  33. if err != nil {
  34. panic(err)
  35. }
  36. fmt.Println("token", token.AccessToken)
  37. // 创建GKE客户端
  38. tokenSource := oauth2.StaticTokenSource(token)
  39. gkeClient, err := gke.NewService(ctx, option.WithTokenSource(tokenSource))
  40. if err != nil {
  41. panic(err)
  42. }
  43. // 创建动态kube配置
  44. inMemKubeConfig, err := createInMemKubeConfig(ctx, gkeClient, token, projectID)
  45. if err != nil {
  46. panic(err)
  47. }
  48. // 使用它创建rest.Config
  49. config, err := clientcmd.NewNonInteractiveClientConfig(*inMemKubeConfig, clusterName, &clientcmd.ConfigOverrides{CurrentContext: clusterName}, nil).ClientConfig()
  50. if err != nil {
  51. panic(err)
  52. }
  53. // 创建clientset
  54. clientset, err := kubernetes.NewForConfig(config)
  55. if err != nil {
  56. panic(err) // 这是代码崩溃的地方,因为它找不到Google ADCs
  57. }
  58. fmt.Printf("clientset %+v\n", clientset)
  59. }
  60. func createInMemKubeConfig(ctx context.Context, client *gke.Service, token *oauth2.Token, projectID string) (*api.Config, error) {
  61. k8sConf := api.Config{
  62. APIVersion: "v1",
  63. Kind: "Config",
  64. Clusters: map[string]*api.Cluster{},
  65. AuthInfos: map[string]*api.AuthInfo{},
  66. Contexts: map[string]*api.Context{},
  67. }
  68. // 在所有区域(“-”)中列出具有项目ID projectID的所有集群
  69. resp, err := client.Projects.Zones.Clusters.List(projectID, "-").Context(ctx).Do()
  70. if err != nil {
  71. return nil, err
  72. }
  73. for _, f := range resp.Clusters {
  74. name := fmt.Sprintf("gke_%s_%s_%s", projectID, f.Zone, f.Name) // 我的自定义命名约定
  75. cert, err := base64.StdEncoding.DecodeString(f.MasterAuth.ClusterCaCertificate)
  76. if err != nil {
  77. return nil, err
  78. }
  79. k8sConf.Clusters[name] = &api.Cluster{
  80. CertificateAuthorityData: cert,
  81. Server: "https://" + f.Endpoint,
  82. }
  83. k8sConf.Contexts[name] = &api.Context{
  84. Cluster: name,
  85. AuthInfo: name,
  86. }
  87. k8sConf.AuthInfos[name] = &api.AuthInfo{
  88. Token: token.AccessToken,
  89. AuthProvider: &api.AuthProviderConfig{
  90. Name: "gcp",
  91. Config: map[string]string{
  92. "scopes": scope,
  93. },
  94. },
  95. }
  96. }
  97. return &k8sConf, nil
  98. }

以下是错误消息:

  1. panic: cannot construct google default token source: google: could not find default credentials. See https://developers.google.com/accounts/docs/application-default-credentials for more information.
英文:

I need to create a Kubernetes clientset using a token extracted from JSON service account key file.

I explicitly provide this token inside the config, however it still looks for Google Application-Default credentials, and crashes because it cannot find them.

Below is my code:

  1. package main
  2. import (
  3. "context"
  4. "encoding/base64"
  5. "fmt"
  6. "io/ioutil"
  7. "golang.org/x/oauth2"
  8. "golang.org/x/oauth2/google"
  9. gke "google.golang.org/api/container/v1"
  10. "google.golang.org/api/option"
  11. "k8s.io/client-go/kubernetes"
  12. _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
  13. "k8s.io/client-go/tools/clientcmd"
  14. "k8s.io/client-go/tools/clientcmd/api"
  15. )
  16. const (
  17. projectID = "my_project_id"
  18. clusterName = "my_cluster_name"
  19. scope = "https://www.googleapis.com/auth/cloud-platform"
  20. )
  21. func main() {
  22. ctx := context.Background()
  23. // Read JSON key and extract the token
  24. data, err := ioutil.ReadFile("sa_key.json")
  25. if err != nil {
  26. panic(err)
  27. }
  28. creds, err := google.CredentialsFromJSON(ctx, data, scope)
  29. if err != nil {
  30. panic(err)
  31. }
  32. token, err := creds.TokenSource.Token()
  33. if err != nil {
  34. panic(err)
  35. }
  36. fmt.Println("token", token.AccessToken)
  37. // Create GKE client
  38. tokenSource := oauth2.StaticTokenSource(token)
  39. gkeClient, err := gke.NewService(ctx, option.WithTokenSource(tokenSource))
  40. if err != nil {
  41. panic(err)
  42. }
  43. // Create a dynamic kube config
  44. inMemKubeConfig, err := createInMemKubeConfig(ctx, gkeClient, token, projectID)
  45. if err != nil {
  46. panic(err)
  47. }
  48. // Use it to create a rest.Config
  49. config, err := clientcmd.NewNonInteractiveClientConfig(*inMemKubeConfig, clusterName, &clientcmd.ConfigOverrides{CurrentContext: clusterName}, nil).ClientConfig()
  50. if err != nil {
  51. panic(err)
  52. }
  53. // Create the clientset
  54. clientset, err := kubernetes.NewForConfig(config)
  55. if err != nil {
  56. panic(err) // this where the code crashes because it can't find the Google ADCs
  57. }
  58. fmt.Printf("clientset %+v\n", clientset)
  59. }
  60. func createInMemKubeConfig(ctx context.Context, client *gke.Service, token *oauth2.Token, projectID string) (*api.Config, error) {
  61. k8sConf := api.Config{
  62. APIVersion: "v1",
  63. Kind: "Config",
  64. Clusters: map[string]*api.Cluster{},
  65. AuthInfos: map[string]*api.AuthInfo{},
  66. Contexts: map[string]*api.Context{},
  67. }
  68. // List all clusters in project with id projectID across all zones ("-")
  69. resp, err := client.Projects.Zones.Clusters.List(projectID, "-").Context(ctx).Do()
  70. if err != nil {
  71. return nil, err
  72. }
  73. for _, f := range resp.Clusters {
  74. name := fmt.Sprintf("gke_%s_%s_%s", projectID, f.Zone, f.Name) // My custom naming convention
  75. cert, err := base64.StdEncoding.DecodeString(f.MasterAuth.ClusterCaCertificate)
  76. if err != nil {
  77. return nil, err
  78. }
  79. k8sConf.Clusters[name] = &api.Cluster{
  80. CertificateAuthorityData: cert,
  81. Server: "https://" + f.Endpoint,
  82. }
  83. k8sConf.Contexts[name] = &api.Context{
  84. Cluster: name,
  85. AuthInfo: name,
  86. }
  87. k8sConf.AuthInfos[name] = &api.AuthInfo{
  88. Token: token.AccessToken,
  89. AuthProvider: &api.AuthProviderConfig{
  90. Name: "gcp",
  91. Config: map[string]string{
  92. "scopes": scope,
  93. },
  94. },
  95. }
  96. }
  97. return &k8sConf, nil
  98. }

and here is the error message:

  1. panic: cannot construct google default token source: google: could not find default credentials. See https://developers.google.com/accounts/docs/application-default-credentials for more information.

答案1

得分: 2

这是对我有效的解决方案。

它基于这个gist,正是我一直在寻找的。它使用了一个oauth2.TokenSource对象,可以提供各种类型的令牌,因此非常灵活。

我花了很长时间才找到这个解决方案,希望能对某人有所帮助!

  1. package main
  2. import (
  3. "context"
  4. "encoding/base64"
  5. "fmt"
  6. "io/ioutil"
  7. "log"
  8. "net/http"
  9. gke "google.golang.org/api/container/v1"
  10. "google.golang.org/api/option"
  11. "golang.org/x/oauth2"
  12. "golang.org/x/oauth2/google"
  13. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  14. "k8s.io/client-go/kubernetes"
  15. "k8s.io/client-go/rest"
  16. clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
  17. )
  18. const (
  19. googleAuthPlugin = "gcp"
  20. projectID = "my_project"
  21. clusterName = "my_cluster"
  22. zone = "my_cluster_zone"
  23. scope = "https://www.googleapis.com/auth/cloud-platform"
  24. )
  25. type googleAuthProvider struct {
  26. tokenSource oauth2.TokenSource
  27. }
  28. // 即使我们不使用它们,也需要这些函数
  29. // 以便googleAuthProvider是一个rest.AuthProvider接口
  30. func (g *googleAuthProvider) WrapTransport(rt http.RoundTripper) http.RoundTripper {
  31. return &oauth2.Transport{
  32. Base: rt,
  33. Source: g.tokenSource,
  34. }
  35. }
  36. func (g *googleAuthProvider) Login() error { return nil }
  37. func main() {
  38. ctx := context.Background()
  39. // 从JSON SA密钥中提取令牌
  40. data, err := ioutil.ReadFile("sa_key.json")
  41. if err != nil {
  42. panic(err)
  43. }
  44. creds, err := google.CredentialsFromJSON(ctx, data, scope)
  45. if err != nil {
  46. panic(err)
  47. }
  48. token, err := creds.TokenSource.Token()
  49. if err != nil {
  50. panic(err)
  51. }
  52. tokenSource := oauth2.StaticTokenSource(token)
  53. // 使用令牌进行身份验证
  54. // 如果它是nil,则使用Google ADC
  55. if err := rest.RegisterAuthProviderPlugin(googleAuthPlugin,
  56. func(clusterAddress string, config map[string]string, persister rest.AuthProviderConfigPersister) (rest.AuthProvider, error) {
  57. var err error
  58. if tokenSource == nil {
  59. tokenSource, err = google.DefaultTokenSource(ctx, scope)
  60. if err != nil {
  61. return nil, fmt.Errorf("failed to create google token source: %+v", err)
  62. }
  63. }
  64. return &googleAuthProvider{tokenSource: tokenSource}, nil
  65. }); err != nil {
  66. log.Fatalf("Failed to register %s auth plugin: %v", googleAuthPlugin, err)
  67. }
  68. gkeClient, err := gke.NewService(ctx, option.WithTokenSource(tokenSource))
  69. if err != nil {
  70. panic(err)
  71. }
  72. clientset, err := getClientSet(ctx, gkeClient, projectID, org, env)
  73. if err != nil {
  74. panic(err)
  75. }
  76. // 演示确保它工作正常
  77. pods, err := clientset.CoreV1().Pods("").List(ctx, metav1.ListOptions{})
  78. if err != nil {
  79. panic(err)
  80. }
  81. log.Printf("集群中有%d个Pod", len(pods.Items))
  82. for _, pod := range pods.Items {
  83. fmt.Println(pod.Name)
  84. }
  85. }
  86. func getClientSet(ctx context.Context, client *gke.Service, projectID, name string) (*kubernetes.Clientset, error) {
  87. // 获取集群信息
  88. cluster, err := client.Projects.Zones.Clusters.Get(projectID, zone, name).Context(ctx).Do()
  89. if err != nil {
  90. panic(err)
  91. }
  92. // 解码集群CA证书
  93. cert, err := base64.StdEncoding.DecodeString(cluster.MasterAuth.ClusterCaCertificate)
  94. if err != nil {
  95. return nil, err
  96. }
  97. // 使用集群信息构建配置
  98. config := &rest.Config{
  99. TLSClientConfig: rest.TLSClientConfig{
  100. CAData: cert,
  101. },
  102. Host: "https://" + cluster.Endpoint,
  103. AuthProvider: &clientcmdapi.AuthProviderConfig{Name: googleAuthPlugin},
  104. }
  105. return kubernetes.NewForConfig(config)
  106. }
英文:

Here's what worked for me.

It is based on this gist
and it's exactly what I was looking for. It uses an oauth2.TokenSource object which can be fed with a variety of token types so it's quite flexible.

It took me a long time to find this solution so I hope this helps somebody!

  1. package main
  2. import (
  3. "context"
  4. "encoding/base64"
  5. "fmt"
  6. "io/ioutil"
  7. "log"
  8. "net/http"
  9. gke "google.golang.org/api/container/v1"
  10. "google.golang.org/api/option"
  11. "golang.org/x/oauth2"
  12. "golang.org/x/oauth2/google"
  13. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  14. "k8s.io/client-go/kubernetes"
  15. "k8s.io/client-go/rest"
  16. clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
  17. )
  18. const (
  19. googleAuthPlugin = "gcp"
  20. projectID = "my_project"
  21. clusterName = "my_cluster"
  22. zone = "my_cluster_zone"
  23. scope = "https://www.googleapis.com/auth/cloud-platform"
  24. )
  25. type googleAuthProvider struct {
  26. tokenSource oauth2.TokenSource
  27. }
  28. // These funcitons are needed even if we don't utilize them
  29. // So that googleAuthProvider is an rest.AuthProvider interface
  30. func (g *googleAuthProvider) WrapTransport(rt http.RoundTripper) http.RoundTripper {
  31. return &oauth2.Transport{
  32. Base: rt,
  33. Source: g.tokenSource,
  34. }
  35. }
  36. func (g *googleAuthProvider) Login() error { return nil }
  37. func main() {
  38. ctx := context.Background()
  39. // Extract a token from the JSON SA key
  40. data, err := ioutil.ReadFile("sa_key.json")
  41. if err != nil {
  42. panic(err)
  43. }
  44. creds, err := google.CredentialsFromJSON(ctx, data, scope)
  45. if err != nil {
  46. panic(err)
  47. }
  48. token, err := creds.TokenSource.Token()
  49. if err != nil {
  50. panic(err)
  51. }
  52. tokenSource := oauth2.StaticTokenSource(token)
  53. // Authenticate with the token
  54. // If it's nil use Google ADC
  55. if err := rest.RegisterAuthProviderPlugin(googleAuthPlugin,
  56. func(clusterAddress string, config map[string]string, persister rest.AuthProviderConfigPersister) (rest.AuthProvider, error) {
  57. var err error
  58. if tokenSource == nil {
  59. tokenSource, err = google.DefaultTokenSource(ctx, scope)
  60. if err != nil {
  61. return nil, fmt.Errorf("failed to create google token source: %+v", err)
  62. }
  63. }
  64. return &googleAuthProvider{tokenSource: tokenSource}, nil
  65. }); err != nil {
  66. log.Fatalf("Failed to register %s auth plugin: %v", googleAuthPlugin, err)
  67. }
  68. gkeClient, err := gke.NewService(ctx, option.WithTokenSource(tokenSource))
  69. if err != nil {
  70. panic(err)
  71. }
  72. clientset, err := getClientSet(ctx, gkeClient, projectID, org, env)
  73. if err != nil {
  74. panic(err)
  75. }
  76. // Demo to make sure it works
  77. pods, err := clientset.CoreV1().Pods("").List(ctx, metav1.ListOptions{})
  78. if err != nil {
  79. panic(err)
  80. }
  81. log.Printf("There are %d pods in the cluster", len(pods.Items))
  82. for _, pod := range pods.Items {
  83. fmt.Println(pod.Name)
  84. }
  85. }
  86. func getClientSet(ctx context.Context, client *gke.Service, projectID, name string) (*kubernetes.Clientset, error) {
  87. // Get cluster info
  88. cluster, err := client.Projects.Zones.Clusters.Get(projectID, zone, name).Context(ctx).Do()
  89. if err != nil {
  90. panic(err)
  91. }
  92. // Decode cluster CA certificate
  93. cert, err := base64.StdEncoding.DecodeString(cluster.MasterAuth.ClusterCaCertificate)
  94. if err != nil {
  95. return nil, err
  96. }
  97. // Build a config using the cluster info
  98. config := &rest.Config{
  99. TLSClientConfig: rest.TLSClientConfig{
  100. CAData: cert,
  101. },
  102. Host: "https://" + cluster.Endpoint,
  103. AuthProvider: &clientcmdapi.AuthProviderConfig{Name: googleAuthPlugin},
  104. }
  105. return kubernetes.NewForConfig(config)
  106. }

huangapple
  • 本文由 发表于 2022年8月17日 18:00:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/73386475.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定