Skip to content

Commit 9836755

Browse files
committed
chore: create shared project number/id mapper
1 parent 9d667de commit 9836755

File tree

5 files changed

+116
-41
lines changed

5 files changed

+116
-41
lines changed

apis/common/projects/mapper.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright 2025 Google LLC
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 projects
16+
17+
import (
18+
"context"
19+
"fmt"
20+
"strconv"
21+
"strings"
22+
23+
resourcemanager "cloud.google.com/go/resourcemanager/apiv3"
24+
"cloud.google.com/go/resourcemanager/apiv3/resourcemanagerpb"
25+
)
26+
27+
// ProjectMapper maps between projects ids and project numbers.
28+
// It maintains a short-lived cache to avoid excessive API calls.
29+
type ProjectMapper struct {
30+
client *resourcemanager.ProjectsClient
31+
}
32+
33+
// NewProjectMapper creates a new ProjectMapper.
34+
func NewProjectMapper(client *resourcemanager.ProjectsClient) *ProjectMapper {
35+
return &ProjectMapper{
36+
client: client,
37+
}
38+
}
39+
40+
// ReplaceProjectNumberWithID replaces a project number with a project id.
41+
// If the projectID is already a project ID, it is returned unchanged.
42+
func (m *ProjectMapper) ReplaceProjectNumberWithID(ctx context.Context, projectID string) (string, error) {
43+
if _, err := strconv.ParseInt(projectID, 10, 64); err != nil {
44+
// Not a project number, no need to map
45+
return projectID, nil
46+
}
47+
48+
req := &resourcemanagerpb.GetProjectRequest{
49+
Name: "projects/" + projectID,
50+
}
51+
project, err := m.client.GetProject(ctx, req)
52+
if err != nil {
53+
return "", fmt.Errorf("error getting project %q: %w", req.Name, err)
54+
}
55+
return project.ProjectId, nil
56+
}
57+
58+
// LookupProjectNumber returns the project number for the given project id.
59+
// If the project id is already numeric, it is returned unchanged.
60+
func (m *ProjectMapper) LookupProjectNumber(ctx context.Context, projectID string) (int64, error) {
61+
// Check if the project number is already a valid integer
62+
// If not, we need to look it up
63+
projectNumber, err := strconv.ParseInt(projectID, 10, 64)
64+
if err != nil {
65+
req := &resourcemanagerpb.GetProjectRequest{
66+
Name: "projects/" + projectID,
67+
}
68+
project, err := m.client.GetProject(ctx, req)
69+
if err != nil {
70+
return 0, fmt.Errorf("error getting project %q: %w", req.Name, err)
71+
}
72+
n, err := strconv.ParseInt(strings.TrimPrefix(project.Name, "projects/"), 10, 64)
73+
if err != nil {
74+
return 0, fmt.Errorf("error parsing project number for %q: %w", project.Name, err)
75+
}
76+
projectNumber = n
77+
}
78+
return projectNumber, nil
79+
}

apis/refs/v1beta1/computerefs.go

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ import (
2020
"strconv"
2121
"strings"
2222

23-
resourcemanager "cloud.google.com/go/resourcemanager/apiv3"
24-
resourcemanagerpb "cloud.google.com/go/resourcemanager/apiv3/resourcemanagerpb"
23+
"github.com/GoogleCloudPlatform/k8s-config-connector/apis/common/projects"
2524
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s"
2625
apierrors "k8s.io/apimachinery/pkg/api/errors"
2726
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -74,7 +73,7 @@ func ParseComputeNetworkID(external string) (*ComputeNetworkID, error) {
7473
}
7574

7675
// ConvertToProjectNumber converts the external reference to use a project number.
77-
func (ref *ComputeNetworkRef) ConvertToProjectNumber(ctx context.Context, projectsClient *resourcemanager.ProjectsClient) error {
76+
func (ref *ComputeNetworkRef) ConvertToProjectNumber(ctx context.Context, projectMapper *projects.ProjectMapper) error {
7877
if ref == nil {
7978
return nil
8079
}
@@ -84,24 +83,12 @@ func (ref *ComputeNetworkRef) ConvertToProjectNumber(ctx context.Context, projec
8483
return err
8584
}
8685

87-
// Check if the project number is already a valid integer
88-
// If not, we need to look it up
89-
projectNumber, err := strconv.ParseInt(id.Project, 10, 64)
86+
projectNumber, err := projectMapper.LookupProjectNumber(ctx, id.Project)
9087
if err != nil {
91-
req := &resourcemanagerpb.GetProjectRequest{
92-
Name: "projects/" + id.Project,
93-
}
94-
project, err := projectsClient.GetProject(ctx, req)
95-
if err != nil {
96-
return fmt.Errorf("error getting project %q: %w", req.Name, err)
97-
}
98-
n, err := strconv.ParseInt(strings.TrimPrefix(project.Name, "projects/"), 10, 64)
99-
if err != nil {
100-
return fmt.Errorf("error parsing project number for %q: %w", project.Name, err)
101-
}
102-
projectNumber = n
88+
return fmt.Errorf("error looking up project number for project %q: %w", id.Project, err)
10389
}
10490
id.Project = strconv.FormatInt(projectNumber, 10)
91+
10592
ref.External = id.String()
10693
return nil
10794
}

pkg/config/controllerconfig.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@
1515
package config
1616

1717
import (
18+
"context"
19+
"fmt"
1820
"net/http"
1921

22+
cloudresourcemanager "cloud.google.com/go/resourcemanager/apiv3"
23+
"github.com/GoogleCloudPlatform/k8s-config-connector/apis/common/projects"
2024
"golang.org/x/oauth2"
2125
"google.golang.org/api/option"
2226
"google.golang.org/grpc"
@@ -45,6 +49,25 @@ type ControllerConfig struct {
4549
// GCPTokenSource mints OAuth2 tokens to be passed with GCP API calls,
4650
// allowing use of a non-default OAuth2 identity
4751
GCPTokenSource oauth2.TokenSource
52+
53+
// ProjectMapper maps between project ids and numbers
54+
ProjectMapper *projects.ProjectMapper
55+
}
56+
57+
func (c *ControllerConfig) Init(ctx context.Context) error {
58+
if c.ProjectMapper == nil {
59+
opts, err := c.RESTClientOptions()
60+
if err != nil {
61+
return err
62+
}
63+
64+
projectsClient, err := cloudresourcemanager.NewProjectsRESTClient(ctx, opts...)
65+
if err != nil {
66+
return fmt.Errorf("building cloudresourcemanager client: %w", err)
67+
}
68+
c.ProjectMapper = projects.NewProjectMapper(projectsClient)
69+
}
70+
return nil
4871
}
4972

5073
func (c *ControllerConfig) RESTClientOptions() ([]option.ClientOption, error) {

pkg/controller/direct/cloudbuild/workerpool_controller.go

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ import (
2323

2424
gcp "cloud.google.com/go/cloudbuild/apiv1/v2"
2525
cloudbuildpb "cloud.google.com/go/cloudbuild/apiv1/v2/cloudbuildpb"
26-
cloudresourcemanager "cloud.google.com/go/resourcemanager/apiv3"
2726
"google.golang.org/protobuf/types/known/fieldmaskpb"
2827

2928
krm "github.com/GoogleCloudPlatform/k8s-config-connector/apis/cloudbuild/v1beta1"
29+
"github.com/GoogleCloudPlatform/k8s-config-connector/apis/common/projects"
3030
refs "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1"
3131
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/config"
3232
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct"
@@ -69,19 +69,6 @@ func (m *model) client(ctx context.Context) (*gcp.Client, error) {
6969
return gcpClient, err
7070
}
7171

72-
func (m *model) projectsClient(ctx context.Context) (*cloudresourcemanager.ProjectsClient, error) {
73-
opts, err := m.config.RESTClientOptions()
74-
if err != nil {
75-
return nil, err
76-
}
77-
78-
crmClient, err := cloudresourcemanager.NewProjectsRESTClient(ctx, opts...)
79-
if err != nil {
80-
return nil, fmt.Errorf("building cloudresourcemanager client: %w", err)
81-
}
82-
return crmClient, err
83-
}
84-
8572
func (m *model) AdapterForObject(ctx context.Context, reader client.Reader, u *unstructured.Unstructured) (directbase.Adapter, error) {
8673
obj := &krm.CloudBuildWorkerPool{}
8774
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &obj); err != nil {
@@ -134,12 +121,6 @@ func (m *model) AdapterForObject(ctx context.Context, reader client.Reader, u *u
134121
}
135122
}
136123

137-
// Get Project GCP client
138-
projectClient, err := m.projectsClient(ctx)
139-
if err != nil {
140-
return nil, err
141-
}
142-
143124
// Get CloudBuild GCP client
144125
gcpClient, err := m.client(ctx)
145126
if err != nil {
@@ -148,7 +129,7 @@ func (m *model) AdapterForObject(ctx context.Context, reader client.Reader, u *u
148129

149130
return &Adapter{
150131
id: id,
151-
projectClient: projectClient,
132+
projectMapper: m.config.ProjectMapper,
152133
gcpClient: gcpClient,
153134
reader: reader,
154135
desired: obj,
@@ -170,6 +151,7 @@ func (m *model) AdapterForURL(ctx context.Context, url string) (directbase.Adapt
170151
}
171152

172153
return &Adapter{
154+
projectMapper: m.config.ProjectMapper,
173155
id: &CloudBuildWorkerPoolIdentity{
174156
project: tokens[1],
175157
location: tokens[3],
@@ -184,7 +166,7 @@ func (m *model) AdapterForURL(ctx context.Context, url string) (directbase.Adapt
184166

185167
type Adapter struct {
186168
id *CloudBuildWorkerPoolIdentity
187-
projectClient *cloudresourcemanager.ProjectsClient
169+
projectMapper *projects.ProjectMapper
188170
gcpClient *gcp.Client
189171
reader client.Reader
190172
desired *krm.CloudBuildWorkerPool
@@ -370,7 +352,7 @@ func (a *Adapter) resolveDependencies(ctx context.Context, reader client.Reader,
370352
return err
371353
}
372354

373-
if err := networkSpec.PeeredNetworkRef.ConvertToProjectNumber(ctx, a.projectClient); err != nil {
355+
if err := networkSpec.PeeredNetworkRef.ConvertToProjectNumber(ctx, a.projectMapper); err != nil {
374356
return err
375357
}
376358
}

pkg/controller/kccmanager/kccmanager.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,10 @@ func New(ctx context.Context, restConfig *rest.Config, cfg Config) (manager.Mana
164164
controllerConfig.GCPTokenSource = oauth2.StaticTokenSource(&oauth2.Token{AccessToken: cfg.GCPAccessToken})
165165
}
166166

167+
if err := controllerConfig.Init(ctx); err != nil {
168+
return nil, err
169+
}
170+
167171
// Initialize direct controllers
168172
if err := registry.Init(ctx, controllerConfig); err != nil {
169173
return nil, err

0 commit comments

Comments
 (0)