2
0

agent.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. package gcp
  2. import (
  3. "context"
  4. "fmt"
  5. "net/url"
  6. admin "cloud.google.com/go/iam/admin/apiv1"
  7. adminpb "google.golang.org/genproto/googleapis/iam/admin/v1"
  8. crm "google.golang.org/api/cloudresourcemanager/v1"
  9. gke "google.golang.org/api/container/v1"
  10. )
  11. type Agent struct {
  12. Ctx context.Context
  13. ProjectID string
  14. IAMClient *admin.IamClient
  15. CloudResourceManagerService *crm.Service
  16. GKEService *gke.Service
  17. }
  18. func (a *Agent) CreateServiceAccount(name string) (*adminpb.ServiceAccount, error) {
  19. req := &adminpb.CreateServiceAccountRequest{
  20. Name: "projects/" + a.ProjectID,
  21. AccountId: name,
  22. ServiceAccount: &adminpb.ServiceAccount{
  23. DisplayName: name,
  24. },
  25. }
  26. return a.IAMClient.CreateServiceAccount(a.Ctx, req)
  27. }
  28. func (a *Agent) SetServiceAccountIAMPolicy(sa *adminpb.ServiceAccount) error {
  29. projectSvc := a.CloudResourceManagerService.Projects
  30. policy, err := projectSvc.GetIamPolicy(
  31. a.ProjectID,
  32. &crm.GetIamPolicyRequest{},
  33. ).Do()
  34. if err != nil {
  35. return err
  36. }
  37. doesExist := false
  38. // find a container.developer binding if it exists
  39. for _, binding := range policy.Bindings {
  40. if binding.Role == "roles/container.developer" {
  41. doesExist = true
  42. binding.Members = append(binding.Members, "serviceAccount:"+sa.Email)
  43. break
  44. }
  45. }
  46. if !doesExist {
  47. policy.Bindings = append(policy.Bindings, &crm.Binding{
  48. Members: []string{"serviceAccount:" + sa.Email},
  49. Role: "roles/container.developer",
  50. })
  51. }
  52. policy, err = projectSvc.SetIamPolicy(
  53. a.ProjectID,
  54. &crm.SetIamPolicyRequest{
  55. Policy: policy,
  56. },
  57. ).Do()
  58. if err != nil {
  59. return err
  60. }
  61. return nil
  62. }
  63. type ServiceAccountKey struct {
  64. // set to service_account
  65. Type string `json:"type"`
  66. ProjectID string `json:"project_id"`
  67. PrivateKeyID string `json:"private_key_id"`
  68. // the private key, not base64 encoded
  69. PrivateKey string `json:"private_key"`
  70. ClientEmail string `json:"client_email"`
  71. ClientID string `json:"client_id"`
  72. AuthURI string `json:"auth_uri"`
  73. TokenURI string `json:"token_uri"`
  74. AuthProviderX509CertURL string `json:"auth_provider_x509_cert_url"`
  75. ClientX509CertURL string `json:"client_x509_cert_url"`
  76. }
  77. // CreateServiceAccountKey will create a new key for the specified service account
  78. func (a *Agent) CreateServiceAccountKey(sa *adminpb.ServiceAccount) ([]byte, error) {
  79. req := &adminpb.CreateServiceAccountKeyRequest{
  80. Name: "projects/" + a.ProjectID + "/serviceAccounts/" + sa.Email,
  81. }
  82. resp, err := a.IAMClient.CreateServiceAccountKey(a.Ctx, req)
  83. if err != nil {
  84. return nil, err
  85. }
  86. return resp.GetPrivateKeyData(), nil
  87. }
  88. // GetProjectIDForGKECluster automatically determines the project ID for a cluster
  89. // that a user has access to
  90. func (a *Agent) GetProjectIDForGKECluster(endpoint string) (string, error) {
  91. // get a list of project IDs
  92. projectSvc := a.CloudResourceManagerService.Projects
  93. resp, err := projectSvc.List().Do()
  94. if err != nil {
  95. return "", err
  96. }
  97. projectIDs := make([]string, 0)
  98. for _, project := range resp.Projects {
  99. projectIDs = append(projectIDs, project.ProjectId)
  100. }
  101. // parse endpoint for ip address
  102. u, err := url.Parse(endpoint)
  103. if err != nil {
  104. return "", err
  105. }
  106. ipAddr := u.Hostname()
  107. // iterate through the projects, and get the GKE endpoints for each project
  108. // if there's a match, return that project id
  109. for _, projectID := range projectIDs {
  110. projectsLocsService := a.GKEService.Projects.Locations
  111. // this should be all zones
  112. resp, err := projectsLocsService.Clusters.List("projects/" + projectID + "/locations/-").Do()
  113. // we'll just continue -- if nothing is found, we'll return an error
  114. if err != nil {
  115. continue
  116. }
  117. for _, cluster := range resp.Clusters {
  118. if cluster.Endpoint == ipAddr {
  119. return projectID, nil
  120. }
  121. }
  122. }
  123. return "", fmt.Errorf("cluster not found")
  124. }