Skip to content

Commit

Permalink
CORS-3254: Update google.golang.org/api/cloudresourcemanager library …
Browse files Browse the repository at this point in the history
…version
  • Loading branch information
bharath-b-rh committed Apr 3, 2024
1 parent e2dafac commit 4d09657
Show file tree
Hide file tree
Showing 11 changed files with 132 additions and 171 deletions.
3 changes: 1 addition & 2 deletions pkg/asset/cluster/tfvars/tfvars.go
Original file line number Diff line number Diff line change
Expand Up @@ -554,8 +554,7 @@ func (t *TerraformVariables) Generate(parents asset.Parents) error {
return fmt.Errorf("%s: No GCP build found", st.FormatPrefix(archName))
}

tags, err := gcpconfig.GetUserTags(ctx,
gcpconfig.NewTagManager(client),
tags, err := gcpconfig.NewTagManager(client).GetUserTags(ctx,
installConfig.Config.Platform.GCP.ProjectID,
installConfig.Config.Platform.GCP.UserTags)
if err != nil {
Expand Down
60 changes: 55 additions & 5 deletions pkg/asset/installconfig/gcp/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ import (

"github.com/pkg/errors"
googleoauth "golang.org/x/oauth2/google"
"google.golang.org/api/cloudresourcemanager/v1"
"google.golang.org/api/cloudresourcemanager/v3"
compute "google.golang.org/api/compute/v1"
dns "google.golang.org/api/dns/v1"
"google.golang.org/api/googleapi"
iam "google.golang.org/api/iam/v1"
"google.golang.org/api/option"
"google.golang.org/api/serviceusage/v1"
"k8s.io/apimachinery/pkg/util/sets"

gcpconsts "github.com/openshift/installer/pkg/constants/gcp"
)

//go:generate mockgen -source=./client.go -destination=./mock/gcpclient_generated.go -package=mock
Expand Down Expand Up @@ -48,6 +50,8 @@ type API interface {
GetProjectPermissions(ctx context.Context, project string, permissions []string) (sets.Set[string], error)
GetProjectByID(ctx context.Context, project string) (*cloudresourcemanager.Project, error)
ValidateServiceAccountHasPermissions(ctx context.Context, project string, permissions []string) (bool, error)
GetProjectTags(ctx context.Context, projectID string) (sets.Set[string], error)
GetNamespacedTagValue(ctx context.Context, tagNamespacedName string) (*cloudresourcemanager.TagValue, error)
}

// Client makes calls to the GCP API.
Expand Down Expand Up @@ -317,9 +321,9 @@ func (c *Client) GetProjects(ctx context.Context) (map[string]string, error) {
return nil, err
}

req := svc.Projects.List()
req := svc.Projects.Search()
projects := make(map[string]string)
if err := req.Pages(ctx, func(page *cloudresourcemanager.ListProjectsResponse) error {
if err := req.Pages(ctx, func(page *cloudresourcemanager.SearchProjectsResponse) error {
for _, project := range page.Projects {
projects[project.ProjectId] = project.Name
}
Expand All @@ -340,7 +344,7 @@ func (c *Client) GetProjectByID(ctx context.Context, project string) (*cloudreso
return nil, err
}

return svc.Projects.Get(project).Context(ctx).Do()
return svc.Projects.Get(fmt.Sprintf(gcpconsts.ProjectNameFmt, project)).Context(ctx).Do()
}

// GetRegions gets the regions that are valid for the project. An error is returned when unsuccessful
Expand Down Expand Up @@ -485,7 +489,7 @@ func (c *Client) getPermissions(ctx context.Context, project string, permissions

projectsService := cloudresourcemanager.NewProjectsService(service)
rb := &cloudresourcemanager.TestIamPermissionsRequest{Permissions: permissions}
response, err := projectsService.TestIamPermissions(project, rb).Context(ctx).Do()
response, err := projectsService.TestIamPermissions(fmt.Sprintf(gcpconsts.ProjectNameFmt, project), rb).Context(ctx).Do()
if err != nil {
return nil, errors.Wrapf(err, "failed to get Iam permissions")
}
Expand Down Expand Up @@ -513,3 +517,49 @@ func (c *Client) ValidateServiceAccountHasPermissions(ctx context.Context, proje
}
return validPermissions.Len() == len(permissions), nil
}

// GetProjectTags returns the list of effective tags attached to the provided project resource.
func (c *Client) GetProjectTags(ctx context.Context, projectID string) (sets.Set[string], error) {
service, err := c.getCloudResourceService(ctx)
if err != nil {
return nil, fmt.Errorf("failed to create cloud resource service: %w", err)
}

effectiveTags := sets.New[string]()
effectiveTagsService := cloudresourcemanager.NewEffectiveTagsService(service)
effectiveTagsRequest := effectiveTagsService.List().
Context(ctx).
Parent(fmt.Sprintf(gcpconsts.ProjectParentPathFmt, projectID))

if err := effectiveTagsRequest.Pages(ctx, func(page *cloudresourcemanager.ListEffectiveTagsResponse) error {
for _, effectiveTag := range page.EffectiveTags {
effectiveTags.Insert(effectiveTag.NamespacedTagValue)
}
return nil
}); err != nil {
return nil, fmt.Errorf("failed to fetch tags attached to %s project: %w", projectID, err)
}

return effectiveTags, nil
}

// GetNamespacedTagValue returns the Tag Value metadata fetched using the tag's NamespacedName.
func (c *Client) GetNamespacedTagValue(ctx context.Context, tagNamespacedName string) (*cloudresourcemanager.TagValue, error) {
service, err := c.getCloudResourceService(ctx)
if err != nil {
return nil, fmt.Errorf("failed to create cloud resource service: %w", err)
}

tagValuesService := cloudresourcemanager.NewTagValuesService(service)

tagValue, err := tagValuesService.GetNamespaced().
Context(ctx).
Name(tagNamespacedName).
Do()

if err != nil {
return nil, fmt.Errorf("failed to fetch %s tag value: %w", tagNamespacedName, err)
}

return tagValue, nil
}
32 changes: 31 additions & 1 deletion pkg/asset/installconfig/gcp/mock/gcpclient_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

67 changes: 0 additions & 67 deletions pkg/asset/installconfig/gcp/mock/usertags_mock.go

This file was deleted.

87 changes: 10 additions & 77 deletions pkg/asset/installconfig/gcp/usertags.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,11 @@ import (

"github.com/googleapis/gax-go/v2/apierror"
"github.com/sirupsen/logrus"
tags "google.golang.org/api/cloudresourcemanager/v3"
"google.golang.org/api/option"
"k8s.io/apimachinery/pkg/util/sets"

"github.com/openshift/installer/pkg/types/gcp"
)

//go:generate mockgen -source=./usertags.go -destination=./mock/usertags_mock.go -package=mock

const (
// maxUserTagLimit is the maximum userTags that can be configured as defined in openshift/api.
// https://github.com/openshift/api/commit/ae73a19d05c35068af16c9aeff375d0b7c936a8a#diff-07b264a49084976b670fb699badaca1795027d6ea732a99226a5388104f6174fR604-R613
Expand All @@ -39,17 +35,11 @@ type processedUserTags struct {
sync.Mutex
}

// tagManager handles resource tagging.
type tagManager struct {
// TagManager handles resource tagging.
type TagManager struct {
client API
}

// TagManager is the interface that wraps methods for resource tag operations.
type TagManager interface {
GetProjectTags(ctx context.Context, projectID string) (sets.Set[string], error)
GetNamespacedTagValue(ctx context.Context, tagNamespacedName string) (*tags.TagValue, error)
}

// newProcessedUserTags is for initializing an instance of processedUserTags.
func newProcessedUserTags() *processedUserTags {
return &processedUserTags{}
Expand Down Expand Up @@ -100,16 +90,16 @@ func (p *processedUserTags) copy() map[string]string {
return t
}

// NewTagManager creates a tagManager instance.
func NewTagManager(client API) TagManager {
return &tagManager{client: client}
// NewTagManager creates a TagManager instance.
func NewTagManager(client API) *TagManager {
return &TagManager{client: client}
}

// GetUserTags returns the processed list of user provided tags if already available,
// else validates, persists in-memory and returns the processed tags.
func GetUserTags(ctx context.Context, mgr TagManager, projectID string, userTags []gcp.UserTag) (map[string]string, error) {
func (t *TagManager) GetUserTags(ctx context.Context, projectID string, userTags []gcp.UserTag) (map[string]string, error) {
if !processedTags.isProcessed() {
if err := validateAndPersistUserTags(ctx, mgr, projectID, userTags); err != nil {
if err := t.validateAndPersistUserTags(ctx, projectID, userTags); err != nil {
return nil, err
}
}
Expand All @@ -123,7 +113,7 @@ func GetUserTags(ctx context.Context, mgr TagManager, projectID string, userTags
// with key of the form `tagKeys/{tag_key_id}` and value of the form
// `tagValues/{tag_value_id}`. Returns error when fetching a tag fails or when
// tag already exists on the project resource.
func validateAndPersistUserTags(ctx context.Context, mgr TagManager, project string, userTags []gcp.UserTag) error {
func (t *TagManager) validateAndPersistUserTags(ctx context.Context, project string, userTags []gcp.UserTag) error {
if len(userTags) == 0 {
return nil
}
Expand All @@ -135,7 +125,7 @@ func validateAndPersistUserTags(ctx context.Context, mgr TagManager, project str
return fmt.Errorf("more than %d user tags is not allowed, configured count: %d", maxUserTagLimit, len(userTags))
}

projectTags, err := mgr.GetProjectTags(ctx, project)
projectTags, err := t.client.GetProjectTags(ctx, project)
if err != nil {
return err
}
Expand All @@ -148,7 +138,7 @@ func validateAndPersistUserTags(ctx context.Context, mgr TagManager, project str
nonexistentTags := make([]string, 0)
for _, tag := range userTags {
name := fmt.Sprintf("%s/%s/%s", tag.ParentID, tag.Key, tag.Value)
tagValue, err := mgr.GetNamespacedTagValue(ctx, name)
tagValue, err := t.client.GetNamespacedTagValue(ctx, name)
if err != nil {
// check and return all non-existing tags at once
// for user to fix in one go.
Expand Down Expand Up @@ -187,60 +177,3 @@ func findDuplicateTags(userTags []gcp.UserTag, parentTags sets.Set[string]) []st
}
return dupTags
}

// getCloudResourceServiceForTags returns the client required for querying resource manager resources.
func (m *tagManager) getCloudResourceServiceForTags(ctx context.Context) (*tags.Service, error) {
svc, err := tags.NewService(ctx, option.WithCredentials(m.client.GetCredentials()))
if err != nil {
return nil, fmt.Errorf("failed to create cloud resource service: %w", err)
}
return svc, nil
}

// GetProjectTags returns the list of effective tags attached to the provided project resource.
func (m *tagManager) GetProjectTags(ctx context.Context, projectID string) (sets.Set[string], error) {
const (
// projectParentPathFmt is the format string for parent path of a project resource.
projectParentPathFmt = "//cloudresourcemanager.googleapis.com/projects/%s"
)

ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel()

service, err := m.getCloudResourceServiceForTags(ctx)
if err != nil {
return nil, fmt.Errorf("failed to create cloud resource service: %w", err)
}

effectiveTags := sets.New[string]()
effectiveTagsService := tags.NewEffectiveTagsService(service)
effectiveTagsRequest := effectiveTagsService.List().Context(ctx).Parent(fmt.Sprintf(projectParentPathFmt, projectID))
if err := effectiveTagsRequest.Pages(ctx, func(page *tags.ListEffectiveTagsResponse) error {
for _, effectiveTag := range page.EffectiveTags {
effectiveTags.Insert(effectiveTag.NamespacedTagValue)
}
return nil
}); err != nil {
return nil, err
}

return effectiveTags, nil
}

// GetNamespacedTagValue returns the Tag Value metadata fetched using the tag's NamespacedName.
func (m *tagManager) GetNamespacedTagValue(ctx context.Context, tagNamespacedName string) (*tags.TagValue, error) {
ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel()

service, err := m.getCloudResourceServiceForTags(ctx)
if err != nil {
return nil, fmt.Errorf("failed to create cloud resource service: %w", err)
}

tagValuesService := tags.NewTagValuesService(service)

return tagValuesService.GetNamespaced().
Context(ctx).
Name(tagNamespacedName).
Do()
}
Loading

0 comments on commit 4d09657

Please sign in to comment.