From d69b507fb5171780ae4e5a9b780f3e91bbc1432c Mon Sep 17 00:00:00 2001 From: Gemma Hou Date: Fri, 26 Jul 2024 22:32:18 +0000 Subject: [PATCH] move resolver functions back into api/refs --- apis/refs/v1beta1/computerefs.go | 164 ++++++++++++++++++ apis/refs/v1beta1/helper.go | 44 +++++ apis/refs/v1beta1/projectref.go | 131 ++++++++++++++ .../cloudbuild/workerpool_controller.go | 6 +- .../direct/dataform/repository_controller.go | 4 +- .../gkehub/featuremembership_controller.go | 4 +- pkg/controller/direct/logging/logbucketref.go | 4 +- .../direct/logging/logmetric_controller.go | 4 +- .../monitoringdashboard_controller.go | 4 +- pkg/controller/direct/monitoring/refs.go | 14 +- .../privateca/privatecapool_controller.go | 4 +- .../direct/resolverefs/computenetworkref.go | 96 ---------- .../resolverefs/computetargethttpproxyref.go | 114 ------------ pkg/controller/direct/resolverefs/helper.go | 88 ---------- .../direct/resolverefs/projectref.go | 109 ------------ 15 files changed, 358 insertions(+), 432 deletions(-) create mode 100644 apis/refs/v1beta1/helper.go delete mode 100644 pkg/controller/direct/resolverefs/computenetworkref.go delete mode 100644 pkg/controller/direct/resolverefs/computetargethttpproxyref.go delete mode 100644 pkg/controller/direct/resolverefs/helper.go delete mode 100644 pkg/controller/direct/resolverefs/projectref.go diff --git a/apis/refs/v1beta1/computerefs.go b/apis/refs/v1beta1/computerefs.go index d0a0671d2f6..d3802e3438b 100644 --- a/apis/refs/v1beta1/computerefs.go +++ b/apis/refs/v1beta1/computerefs.go @@ -14,6 +14,17 @@ package v1beta1 +import ( + "context" + "fmt" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "strings" +) + type ComputeNetworkRef struct { /* The compute network selflink of form "projects//global/networks/", when not managed by KCC. */ External string `json:"external,omitempty"` @@ -23,6 +34,74 @@ type ComputeNetworkRef struct { Namespace string `json:"namespace,omitempty"` } +type ComputeNetwork struct { + Project string + ComputeNetworkID string +} + +func (c *ComputeNetwork) String() string { + return fmt.Sprintf("projects/%s/global/networks/%s", c.Project, c.ComputeNetworkID) +} + +func ResolveComputeNetworkRef(ctx context.Context, reader client.Reader, src client.Object, ref *ComputeNetworkRef) (*ComputeNetwork, error) { + if ref == nil { + return nil, nil + } + + if ref.External != "" { + if ref.Name != "" { + return nil, fmt.Errorf("cannot specify both name and external on computenetwork reference") + } + + tokens := strings.Split(ref.External, "/") + if len(tokens) == 5 && tokens[0] == "projects" && tokens[2] == "global" && tokens[3] == "networks" { + return &ComputeNetwork{ + Project: tokens[1], + ComputeNetworkID: tokens[4]}, nil + } + return nil, fmt.Errorf("format of computenetwork external=%q was not known (use projects//global/networks/)", ref.External) + } + + if ref.Name == "" { + return nil, fmt.Errorf("must specify either name or external on computenetwork reference") + } + + key := types.NamespacedName{ + Namespace: ref.Namespace, + Name: ref.Name, + } + if key.Namespace == "" { + key.Namespace = src.GetNamespace() + } + + computenetwork := &unstructured.Unstructured{} + computenetwork.SetGroupVersionKind(schema.GroupVersionKind{ + Group: "compute.cnrm.cloud.google.com", + Version: "v1beta1", + Kind: "ComputeNetwork", + }) + if err := reader.Get(ctx, key, computenetwork); err != nil { + if apierrors.IsNotFound(err) { + return nil, fmt.Errorf("referenced ComputeNetwork %v not found", key) + } + return nil, fmt.Errorf("error reading referenced ComputeNetwork %v: %w", key, err) + } + + computenetworkID, err := GetResourceID(computenetwork) + if err != nil { + return nil, err + } + + computeNetworkProjectID, err := ResolveProjectID(ctx, reader, computenetwork) + if err != nil { + return nil, err + } + return &ComputeNetwork{ + Project: computeNetworkProjectID, + ComputeNetworkID: computenetworkID, + }, nil +} + type ComputeSubnetworkRef struct { /* The ComputeSubnetwork selflink of form "projects/{{project}}/regions/{{region}}/subnetworks/{{name}}", when not managed by KCC. */ External string `json:"external,omitempty"` @@ -77,6 +156,91 @@ type ComputeTargetHTTPProxyRef struct { Namespace string `json:"namespace,omitempty"` } +type ComputeTargetHTTPProxy struct { + Project string + Location string + ComputeTargetHTTPProxyID string +} + +func (c *ComputeTargetHTTPProxy) String() string { + if c.Location == "global" { + return fmt.Sprintf("projects/%s/global/targetHttpProxies/%s", c.Project, c.ComputeTargetHTTPProxyID) + } + return fmt.Sprintf("projects/%s/location/%s/targetHttpProxies/%s", c.Project, c.Location, c.ComputeTargetHTTPProxyID) +} + +func ResolveTargetHTTPProxyRef(ctx context.Context, reader client.Reader, src client.Object, ref *ComputeTargetHTTPProxyRef) (*ComputeTargetHTTPProxy, error) { + if ref == nil { + return nil, nil + } + + if ref.External != "" { + if ref.Name != "" { + return nil, fmt.Errorf("cannot specify both name and external on ComputeNetwork reference") + } + + tokens := strings.Split(ref.External, "/") + if len(tokens) == 5 && tokens[0] == "projects" && tokens[2] == "global" && tokens[3] == "targetHttpProxies" { + return &ComputeTargetHTTPProxy{ + Project: tokens[1], + Location: "global", + ComputeTargetHTTPProxyID: tokens[4]}, nil + } else if len(tokens) == 6 && tokens[0] == "projects" && tokens[2] == "location" && tokens[4] == "targetHttpProxies" { + return &ComputeTargetHTTPProxy{ + Project: tokens[1], + Location: tokens[3], + ComputeTargetHTTPProxyID: tokens[5]}, nil + } + return nil, fmt.Errorf("format of ComputeTargetHTTPProxy external=%q was not known (use projects//global/targetHttpProxies/ or projects//location//targetHttpProxies/)", ref.External) + } + + if ref.Name == "" { + return nil, fmt.Errorf("must specify either name or external on ComputeTargetHTTPProxy reference") + } + + key := types.NamespacedName{ + Namespace: ref.Namespace, + Name: ref.Name, + } + if key.Namespace == "" { + key.Namespace = src.GetNamespace() + } + + computeTargetHTTPProxy := &unstructured.Unstructured{} + computeTargetHTTPProxy.SetGroupVersionKind(schema.GroupVersionKind{ + Group: "compute.cnrm.cloud.google.com", + Version: "v1beta1", + Kind: "ComputeTargetHTTPProxy", + }) + if err := reader.Get(ctx, key, computeTargetHTTPProxy); err != nil { + if apierrors.IsNotFound(err) { + return nil, fmt.Errorf("referenced ComputeTargetHTTPProxy %v not found", key) + } + return nil, fmt.Errorf("error reading referenced ComputeTargetHTTPProxy %v: %w", key, err) + } + + computeTargetHTTPProxyID, err := GetResourceID(computeTargetHTTPProxy) + if err != nil { + return nil, err + } + + computeTargetHTTPProxyProjectID, err := ResolveProjectID(ctx, reader, computeTargetHTTPProxy) + if err != nil { + return nil, err + } + + computeTargetHTTPProxyLocation, err := GetLocation(computeTargetHTTPProxy) + if err != nil { + return nil, err + } + + return &ComputeTargetHTTPProxy{ + Project: computeTargetHTTPProxyProjectID, + Location: computeTargetHTTPProxyLocation, + ComputeTargetHTTPProxyID: computeTargetHTTPProxyID, + }, nil +} + type ComputeTargetHTTPSProxyRef struct { /* The ComputeTargetHTTPSProxy selflink in the form "projects/{{project}}/global/targetHttpProxies/{{name}}" or "projects/{{project}}/regions/{{region}}/targetHttpProxies/{{name}}" when not managed by KCC. */ External string `json:"external,omitempty"` diff --git a/apis/refs/v1beta1/helper.go b/apis/refs/v1beta1/helper.go new file mode 100644 index 00000000000..a8da9fee059 --- /dev/null +++ b/apis/refs/v1beta1/helper.go @@ -0,0 +1,44 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v1beta1 + +import ( + "fmt" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +func GetResourceID(u *unstructured.Unstructured) (string, error) { + resourceID, _, err := unstructured.NestedString(u.Object, "spec", "resourceID") + if err != nil { + return "", fmt.Errorf("reading spec.resourceID from %v %v/%v: %w", u.GroupVersionKind().Kind, u.GetNamespace(), u.GetName(), err) + } + if resourceID == "" { + resourceID = u.GetName() + } + return resourceID, nil +} + +// TODO(yuhou): Location can be optional. Use provider default location when it's unset. +func GetLocation(obj *unstructured.Unstructured) (string, error) { + // TODO(yuhou): field can be "location" or "region". + location, _, err := unstructured.NestedString(obj.Object, "spec", "location") + if err != nil { + return "", fmt.Errorf("cannot get location for referenced %s %v: %w", obj.GetKind(), obj.GetNamespace(), err) + } + if location == "" { + return "", fmt.Errorf("cannot get location for referenced %s %v (spec.location not set)", obj.GetKind(), obj.GetNamespace()) + } + return location, nil +} diff --git a/apis/refs/v1beta1/projectref.go b/apis/refs/v1beta1/projectref.go index f733d505d33..21c2f9ea342 100644 --- a/apis/refs/v1beta1/projectref.go +++ b/apis/refs/v1beta1/projectref.go @@ -14,6 +14,18 @@ package v1beta1 +import ( + "context" + "fmt" + "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/k8s/v1alpha1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "strings" +) + // The Project that this resource belongs to. type ProjectRef struct { /* The `projectID` field of a project, when not managed by KCC. */ @@ -26,3 +38,122 @@ type ProjectRef struct { // +optional Kind string `json:"kind,omitempty"` } + +// AsProjectRef converts a generic ResourceRef into a ProjectRef +func AsProjectRef(in *v1alpha1.ResourceRef) *ProjectRef { + if in == nil { + return nil + } + return &ProjectRef{ + Namespace: in.Namespace, + Name: in.Name, + External: in.External, + Kind: in.Kind, + } +} + +type Project struct { + ProjectID string +} + +// ResolveProject will resolve a ProjectRef to a Project, with the ProjectID. +func ResolveProject(ctx context.Context, reader client.Reader, src client.Object, ref *ProjectRef) (*Project, error) { + if ref == nil { + return nil, nil + } + + if ref.Kind != "" { + if ref.Kind != "Project" { + return nil, fmt.Errorf("kind is optional on project reference, but must be \"Project\" if provided") + } + } + + if ref.External != "" { + if ref.Name != "" { + return nil, fmt.Errorf("cannot specify both name and external on project reference") + } + + tokens := strings.Split(ref.External, "/") + if len(tokens) == 1 { + return &Project{ProjectID: tokens[0]}, nil + } + if len(tokens) == 2 && tokens[0] == "projects" { + return &Project{ProjectID: tokens[1]}, nil + } + return nil, fmt.Errorf("format of project external=%q was not known (use projects/ or )", ref.External) + } + + if ref.Name == "" { + return nil, fmt.Errorf("must specify either name or external on project reference") + } + + key := types.NamespacedName{ + Namespace: ref.Namespace, + Name: ref.Name, + } + if key.Namespace == "" { + key.Namespace = src.GetNamespace() + } + + project := &unstructured.Unstructured{} + project.SetGroupVersionKind(schema.GroupVersionKind{ + Group: "resourcemanager.cnrm.cloud.google.com", + Version: "v1beta1", + Kind: "Project", + }) + if err := reader.Get(ctx, key, project); err != nil { + if apierrors.IsNotFound(err) { + return nil, fmt.Errorf("referenced Project %v not found", key) + } + return nil, fmt.Errorf("error reading referenced Project %v: %w", key, err) + } + + projectID, err := GetResourceID(project) + if err != nil { + return nil, err + } + + return &Project{ + ProjectID: projectID, + }, nil +} + +func ResolveProjectID(ctx context.Context, reader client.Reader, obj *unstructured.Unstructured) (string, error) { + projectRefExternal, _, _ := unstructured.NestedString(obj.Object, "spec", "projectRef", "external") + if projectRefExternal != "" { + projectRef := ProjectRef{ + External: projectRefExternal, + } + + project, err := ResolveProject(ctx, reader, obj, &projectRef) + if err != nil { + return "", fmt.Errorf("cannot parse projectRef.external %q in %v %v/%v: %w", projectRefExternal, obj.GetKind(), obj.GetNamespace(), obj.GetName(), err) + } + return project.ProjectID, nil + } + + projectRefName, _, _ := unstructured.NestedString(obj.Object, "spec", "projectRef", "name") + if projectRefName != "" { + projectRefNamespace, _, _ := unstructured.NestedString(obj.Object, "spec", "projectRef", "namespace") + + projectRef := ProjectRef{ + Name: projectRefName, + Namespace: projectRefNamespace, + } + if projectRef.Namespace == "" { + projectRef.Namespace = obj.GetNamespace() + } + + project, err := ResolveProject(ctx, reader, obj, &projectRef) + if err != nil { + return "", fmt.Errorf("cannot parse projectRef in %v %v/%v: %w", obj.GetKind(), obj.GetNamespace(), obj.GetName(), err) + } + return project.ProjectID, nil + } + + if projectID := obj.GetAnnotations()["cnrm.cloud.google.com/project-id"]; projectID != "" { + return projectID, nil + } + + return "", fmt.Errorf("cannot find project id for %v %v/%v", obj.GetKind(), obj.GetNamespace(), obj.GetName()) +} diff --git a/pkg/controller/direct/cloudbuild/workerpool_controller.go b/pkg/controller/direct/cloudbuild/workerpool_controller.go index e67bc3a4963..e559509212c 100644 --- a/pkg/controller/direct/cloudbuild/workerpool_controller.go +++ b/pkg/controller/direct/cloudbuild/workerpool_controller.go @@ -22,7 +22,7 @@ import ( "reflect" "strings" - "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/resolverefs" + refs "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1" gcp "cloud.google.com/go/cloudbuild/apiv1/v2" cloudbuildpb "cloud.google.com/go/cloudbuild/apiv1/v2/cloudbuildpb" @@ -91,7 +91,7 @@ func (m *model) AdapterForObject(ctx context.Context, reader client.Reader, u *u } // Get GCP Project - projectRef, err := resolverefs.ResolveProjectRef(ctx, reader, obj, obj.Spec.ProjectRef) + projectRef, err := refs.ResolveProject(ctx, reader, obj, obj.Spec.ProjectRef) if err != nil { return nil, err } @@ -105,7 +105,7 @@ func (m *model) AdapterForObject(ctx context.Context, reader client.Reader, u *u // Get computeNetwork if obj.Spec.PrivatePoolConfig.NetworkConfig != nil { - networkRef, err := resolverefs.ResolveComputeNetworkRef(ctx, reader, obj, &obj.Spec.PrivatePoolConfig.NetworkConfig.PeeredNetworkRef) + networkRef, err := refs.ResolveComputeNetworkRef(ctx, reader, obj, &obj.Spec.PrivatePoolConfig.NetworkConfig.PeeredNetworkRef) if err != nil { return nil, err diff --git a/pkg/controller/direct/dataform/repository_controller.go b/pkg/controller/direct/dataform/repository_controller.go index 6288d55a5ef..6ce6b7c7614 100644 --- a/pkg/controller/direct/dataform/repository_controller.go +++ b/pkg/controller/direct/dataform/repository_controller.go @@ -19,7 +19,7 @@ import ( "fmt" "reflect" - "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/resolverefs" + refs "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1" krm "github.com/GoogleCloudPlatform/k8s-config-connector/apis/dataform/v1alpha1" "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/config" @@ -88,7 +88,7 @@ func (m *model) AdapterForObject(ctx context.Context, reader client.Reader, u *u return nil, fmt.Errorf("cannot resolve resource ID") } - projectRef, err := resolverefs.ResolveProjectRef(ctx, reader, obj, obj.Spec.ProjectRef) + projectRef, err := refs.ResolveProject(ctx, reader, obj, obj.Spec.ProjectRef) if err != nil { return nil, err } diff --git a/pkg/controller/direct/gkehub/featuremembership_controller.go b/pkg/controller/direct/gkehub/featuremembership_controller.go index 8b6a409d2cc..2801e003ea5 100644 --- a/pkg/controller/direct/gkehub/featuremembership_controller.go +++ b/pkg/controller/direct/gkehub/featuremembership_controller.go @@ -19,8 +19,6 @@ import ( "fmt" "reflect" - "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/resolverefs" - featureapi "google.golang.org/api/gkehub/v1beta" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" @@ -86,7 +84,7 @@ func (m *gkeHubModel) AdapterForObject(ctx context.Context, reader client.Reader Namespace: obj.Spec.ProjectRef.Namespace, External: obj.Spec.ProjectRef.External, } - project, err := resolverefs.ResolveProjectRef(ctx, reader, obj, projectRef) + project, err := refs.ResolveProject(ctx, reader, obj, projectRef) if err != nil { return nil, err } diff --git a/pkg/controller/direct/logging/logbucketref.go b/pkg/controller/direct/logging/logbucketref.go index 4ff5a2d8257..0c887268c15 100644 --- a/pkg/controller/direct/logging/logbucketref.go +++ b/pkg/controller/direct/logging/logbucketref.go @@ -19,8 +19,6 @@ import ( "fmt" "strings" - "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/resolverefs" - refs "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1" "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/k8s/v1alpha1" "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/logging/v1beta1" @@ -101,7 +99,7 @@ func LogBucketRef_ConvertToExternal(ctx context.Context, reader client.Reader, s Namespace: obj.Spec.ProjectRef.Namespace, External: obj.Spec.ProjectRef.External, } - project, err := resolverefs.ResolveProjectRef(ctx, reader, loggingLogBucket, projectRef) + project, err := refs.ResolveProject(ctx, reader, loggingLogBucket, projectRef) if err != nil { return fmt.Errorf("cannot get project for referenced LoggingLogBucket %v: %w", key, err) } diff --git a/pkg/controller/direct/logging/logmetric_controller.go b/pkg/controller/direct/logging/logmetric_controller.go index 93a29925fc0..06613e8cb35 100644 --- a/pkg/controller/direct/logging/logmetric_controller.go +++ b/pkg/controller/direct/logging/logmetric_controller.go @@ -20,7 +20,7 @@ import ( "reflect" "strings" - "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/resolverefs" + refs "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1" api "google.golang.org/api/logging/v2" corev1 "k8s.io/api/core/v1" @@ -90,7 +90,7 @@ func (m *logMetricModel) AdapterForObject(ctx context.Context, reader client.Rea return nil, fmt.Errorf("cannot resolve resource ID") } - projectRef, err := resolverefs.ResolveProjectRef(ctx, reader, obj, &obj.Spec.ProjectRef) + projectRef, err := refs.ResolveProject(ctx, reader, obj, &obj.Spec.ProjectRef) if err != nil { return nil, err } diff --git a/pkg/controller/direct/monitoring/monitoringdashboard_controller.go b/pkg/controller/direct/monitoring/monitoringdashboard_controller.go index aa640f25a00..610ad699ad1 100644 --- a/pkg/controller/direct/monitoring/monitoringdashboard_controller.go +++ b/pkg/controller/direct/monitoring/monitoringdashboard_controller.go @@ -19,7 +19,7 @@ import ( "fmt" "strings" - "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/resolverefs" + refs "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1" api "cloud.google.com/go/monitoring/dashboard/apiv1" pb "cloud.google.com/go/monitoring/dashboard/apiv1/dashboardpb" @@ -88,7 +88,7 @@ func (m *dashboardModel) AdapterForObject(ctx context.Context, kube client.Reade return nil, fmt.Errorf("cannot resolve resource ID") } - projectRef, err := resolverefs.ResolveProjectRef(ctx, kube, obj, &obj.Spec.ProjectRef) + projectRef, err := refs.ResolveProject(ctx, kube, obj, &obj.Spec.ProjectRef) if err != nil { return nil, err } diff --git a/pkg/controller/direct/monitoring/refs.go b/pkg/controller/direct/monitoring/refs.go index d89c71a226a..04138e6f7d0 100644 --- a/pkg/controller/direct/monitoring/refs.go +++ b/pkg/controller/direct/monitoring/refs.go @@ -19,8 +19,6 @@ import ( "fmt" "strings" - "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/resolverefs" - krm "github.com/GoogleCloudPlatform/k8s-config-connector/apis/monitoring/v1beta1" refs "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1" "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/k8s/v1alpha1" @@ -56,7 +54,7 @@ func normalizeResourceName(ctx context.Context, reader client.Reader, src client switch ref.Kind { case "Project": - project, err := resolverefs.ResolveProjectRef(ctx, reader, src, &refs.ProjectRef{ + project, err := refs.ResolveProject(ctx, reader, src, &refs.ProjectRef{ Name: ref.Name, Namespace: ref.Namespace, External: ref.External, @@ -90,7 +88,7 @@ func normalizeResourceName(ctx context.Context, reader client.Reader, src client return ref, nil } -func normalizeMonitoringAlertPolicyRef(ctx context.Context, reader client.Reader, src client.Object, project resolverefs.Project, ref *refs.MonitoringAlertPolicyRef) (*refs.MonitoringAlertPolicyRef, error) { +func normalizeMonitoringAlertPolicyRef(ctx context.Context, reader client.Reader, src client.Object, project refs.Project, ref *refs.MonitoringAlertPolicyRef) (*refs.MonitoringAlertPolicyRef, error) { if ref == nil { return nil, nil } @@ -140,12 +138,12 @@ func normalizeMonitoringAlertPolicyRef(ctx context.Context, reader client.Reader return nil, fmt.Errorf("error reading referenced MonitoringAlertPolicy %v: %w", key, err) } - alertPolicyResourceID, err := resolverefs.GetResourceID(alertPolicy) + alertPolicyResourceID, err := refs.GetResourceID(alertPolicy) if err != nil { return nil, err } - alertPolicyProjectID, err := resolverefs.GetProjectID(ctx, reader, alertPolicy) + alertPolicyProjectID, err := refs.ResolveProjectID(ctx, reader, alertPolicy) if err != nil { return nil, err } @@ -162,7 +160,7 @@ func normalizeProjectRef(ctx context.Context, reader client.Reader, src client.O return nil, nil } - project, err := resolverefs.ResolveProjectRef(ctx, reader, src, ref) + project, err := refs.ResolveProject(ctx, reader, src, ref) if err != nil { return nil, err } @@ -176,7 +174,7 @@ type refNormalizer struct { ctx context.Context kube client.Reader src client.Object - project resolverefs.Project + project refs.Project } func (r *refNormalizer) VisitField(path string, v any) error { diff --git a/pkg/controller/direct/privateca/privatecapool_controller.go b/pkg/controller/direct/privateca/privatecapool_controller.go index 65302a65f4d..078f2b0c943 100644 --- a/pkg/controller/direct/privateca/privatecapool_controller.go +++ b/pkg/controller/direct/privateca/privatecapool_controller.go @@ -19,7 +19,7 @@ import ( "fmt" "strings" - "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/controller/direct/resolverefs" + refs "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1" iampb "cloud.google.com/go/iam/apiv1/iampb" api "cloud.google.com/go/security/privateca/apiv1" @@ -91,7 +91,7 @@ func (m *caPoolModel) AdapterForObject(ctx context.Context, reader client.Reader return nil, fmt.Errorf("cannot resolve location") } - projectRef, err := resolverefs.ResolveProjectRef(ctx, reader, obj, resolverefs.AsProjectRef(&obj.Spec.ProjectRef)) + projectRef, err := refs.ResolveProject(ctx, reader, obj, refs.AsProjectRef(&obj.Spec.ProjectRef)) if err != nil { return nil, err } diff --git a/pkg/controller/direct/resolverefs/computenetworkref.go b/pkg/controller/direct/resolverefs/computenetworkref.go deleted file mode 100644 index 3e4d483eda0..00000000000 --- a/pkg/controller/direct/resolverefs/computenetworkref.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resolverefs - -import ( - "context" - "fmt" - "strings" - - refs "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -type ComputeNetwork struct { - Project string - ComputeNetworkID string -} - -func (c *ComputeNetwork) String() string { - return fmt.Sprintf("projects/%s/global/networks/%s", c.Project, c.ComputeNetworkID) -} - -func ResolveComputeNetworkRef(ctx context.Context, reader client.Reader, src client.Object, ref *refs.ComputeNetworkRef) (*ComputeNetwork, error) { - if ref == nil { - return nil, nil - } - - if ref.External != "" { - if ref.Name != "" { - return nil, fmt.Errorf("cannot specify both name and external on computenetwork reference") - } - - tokens := strings.Split(ref.External, "/") - if len(tokens) == 5 && tokens[0] == "projects" && tokens[2] == "global" && tokens[3] == "networks" { - return &ComputeNetwork{ - Project: tokens[1], - ComputeNetworkID: tokens[4]}, nil - } - return nil, fmt.Errorf("format of computenetwork external=%q was not known (use projects//global/networks/)", ref.External) - } - - if ref.Name == "" { - return nil, fmt.Errorf("must specify either name or external on computenetwork reference") - } - - key := types.NamespacedName{ - Namespace: ref.Namespace, - Name: ref.Name, - } - if key.Namespace == "" { - key.Namespace = src.GetNamespace() - } - - computenetwork := &unstructured.Unstructured{} - computenetwork.SetGroupVersionKind(schema.GroupVersionKind{ - Group: "compute.cnrm.cloud.google.com", - Version: "v1beta1", - Kind: "ComputeNetwork", - }) - if err := reader.Get(ctx, key, computenetwork); err != nil { - if apierrors.IsNotFound(err) { - return nil, fmt.Errorf("referenced ComputeNetwork %v not found", key) - } - return nil, fmt.Errorf("error reading referenced ComputeNetwork %v: %w", key, err) - } - - computenetworkID, err := GetResourceID(computenetwork) - if err != nil { - return nil, err - } - - computeNetworkProjectID, err := GetProjectID(ctx, reader, computenetwork) - if err != nil { - return nil, err - } - return &ComputeNetwork{ - Project: computeNetworkProjectID, - ComputeNetworkID: computenetworkID, - }, nil -} diff --git a/pkg/controller/direct/resolverefs/computetargethttpproxyref.go b/pkg/controller/direct/resolverefs/computetargethttpproxyref.go deleted file mode 100644 index bb58699bd35..00000000000 --- a/pkg/controller/direct/resolverefs/computetargethttpproxyref.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resolverefs - -import ( - "context" - "fmt" - "strings" - - refs "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - - "sigs.k8s.io/controller-runtime/pkg/client" -) - -type ComputeTargetHTTPProxy struct { - Project string - Location string - ComputeTargetHTTPProxyID string -} - -func (c *ComputeTargetHTTPProxy) String() string { - if c.Location == "global" { - return fmt.Sprintf("projects/%s/global/targetHttpProxies/%s", c.Project, c.ComputeTargetHTTPProxyID) - } - return fmt.Sprintf("projects/%s/location/%s/targetHttpProxies/%s", c.Project, c.Location, c.ComputeTargetHTTPProxyID) -} - -func ResolveTargetHTTPProxyRef(ctx context.Context, reader client.Reader, src client.Object, ref *refs.ComputeTargetHTTPProxyRef) (*ComputeTargetHTTPProxy, error) { - if ref == nil { - return nil, nil - } - - if ref.External != "" { - if ref.Name != "" { - return nil, fmt.Errorf("cannot specify both name and external on ComputeNetwork reference") - } - - tokens := strings.Split(ref.External, "/") - if len(tokens) == 5 && tokens[0] == "projects" && tokens[2] == "global" && tokens[3] == "targetHttpProxies" { - return &ComputeTargetHTTPProxy{ - Project: tokens[1], - ComputeTargetHTTPProxyID: tokens[4]}, nil - } else if len(tokens) == 6 && tokens[0] == "projects" && tokens[2] == "location" && tokens[4] == "targetHttpProxies" { - return &ComputeTargetHTTPProxy{ - Project: tokens[1], - Location: tokens[3], - ComputeTargetHTTPProxyID: tokens[5]}, nil - } - return nil, fmt.Errorf("format of ComputeTargetHTTPProxy external=%q was not known (use projects//global/targetHttpProxies/ or projects//location//targetHttpProxies/)", ref.External) - } - - if ref.Name == "" { - return nil, fmt.Errorf("must specify either name or external on ComputeTargetHTTPProxy reference") - } - - key := types.NamespacedName{ - Namespace: ref.Namespace, - Name: ref.Name, - } - if key.Namespace == "" { - key.Namespace = src.GetNamespace() - } - - computeTargetHTTPProxy := &unstructured.Unstructured{} - computeTargetHTTPProxy.SetGroupVersionKind(schema.GroupVersionKind{ - Group: "compute.cnrm.cloud.google.com", - Version: "v1beta1", - Kind: "ComputeTargetHTTPProxy", - }) - if err := reader.Get(ctx, key, computeTargetHTTPProxy); err != nil { - if apierrors.IsNotFound(err) { - return nil, fmt.Errorf("referenced ComputeTargetHTTPProxy %v not found", key) - } - return nil, fmt.Errorf("error reading referenced ComputeTargetHTTPProxy %v: %w", key, err) - } - - computeTargetHTTPProxyID, err := GetResourceID(computeTargetHTTPProxy) - if err != nil { - return nil, err - } - - computeTargetHTTPProxyProjectID, err := GetProjectID(ctx, reader, computeTargetHTTPProxy) - if err != nil { - return nil, err - } - - computeTargetHTTPProxyLocation, err := GetLocation(computeTargetHTTPProxy) - if err != nil { - return nil, err - } - - return &ComputeTargetHTTPProxy{ - Project: computeTargetHTTPProxyProjectID, - Location: computeTargetHTTPProxyLocation, - ComputeTargetHTTPProxyID: computeTargetHTTPProxyID, - }, nil - -} diff --git a/pkg/controller/direct/resolverefs/helper.go b/pkg/controller/direct/resolverefs/helper.go deleted file mode 100644 index 79a43629e4b..00000000000 --- a/pkg/controller/direct/resolverefs/helper.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resolverefs - -import ( - "context" - "fmt" - - "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -func GetResourceID(u *unstructured.Unstructured) (string, error) { - resourceID, _, err := unstructured.NestedString(u.Object, "spec", "resourceID") - if err != nil { - return "", fmt.Errorf("reading spec.resourceID from %v %v/%v: %w", u.GroupVersionKind().Kind, u.GetNamespace(), u.GetName(), err) - } - if resourceID == "" { - resourceID = u.GetName() - } - return resourceID, nil -} - -// TODO(yuhou): Location can be optional. Use provider default location when it's unset. -func GetLocation(obj *unstructured.Unstructured) (string, error) { - // TODO(yuhou): field can be "location" or "region". - location, _, err := unstructured.NestedString(obj.Object, "spec", "location") - if err != nil { - return "", fmt.Errorf("cannot get location for referenced %s %v: %w", obj.GetKind(), obj.GetNamespace(), err) - } - if location == "" { - return "", fmt.Errorf("cannot get location for referenced %s %v (spec.location not set)", obj.GetKind(), obj.GetNamespace()) - } - return location, nil -} - -func GetProjectID(ctx context.Context, reader client.Reader, obj *unstructured.Unstructured) (string, error) { - projectRefExternal, _, _ := unstructured.NestedString(obj.Object, "spec", "projectRef", "external") - if projectRefExternal != "" { - projectRef := v1beta1.ProjectRef{ - External: projectRefExternal, - } - - project, err := ResolveProjectRef(ctx, reader, obj, &projectRef) - if err != nil { - return "", fmt.Errorf("cannot parse projectRef.external %q in %v %v/%v: %w", projectRefExternal, obj.GetKind(), obj.GetNamespace(), obj.GetName(), err) - } - return project.ProjectID, nil - } - - projectRefName, _, _ := unstructured.NestedString(obj.Object, "spec", "projectRef", "name") - if projectRefName != "" { - projectRefNamespace, _, _ := unstructured.NestedString(obj.Object, "spec", "projectRef", "namespace") - - projectRef := v1beta1.ProjectRef{ - Name: projectRefName, - Namespace: projectRefNamespace, - } - if projectRef.Namespace == "" { - projectRef.Namespace = obj.GetNamespace() - } - - project, err := ResolveProjectRef(ctx, reader, obj, &projectRef) - if err != nil { - return "", fmt.Errorf("cannot parse projectRef in %v %v/%v: %w", obj.GetKind(), obj.GetNamespace(), obj.GetName(), err) - } - return project.ProjectID, nil - } - - if projectID := obj.GetAnnotations()["cnrm.cloud.google.com/project-id"]; projectID != "" { - return projectID, nil - } - - return "", fmt.Errorf("cannot find project id for %v %v/%v", obj.GetKind(), obj.GetNamespace(), obj.GetName()) -} diff --git a/pkg/controller/direct/resolverefs/projectref.go b/pkg/controller/direct/resolverefs/projectref.go deleted file mode 100644 index 4b019d8115a..00000000000 --- a/pkg/controller/direct/resolverefs/projectref.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package resolverefs - -import ( - "context" - "fmt" - "strings" - - "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1" - - "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/k8s/v1alpha1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// AsProjectRef converts a generic ResourceRef into a ProjectRef -func AsProjectRef(in *v1alpha1.ResourceRef) *v1beta1.ProjectRef { - if in == nil { - return nil - } - return &v1beta1.ProjectRef{ - Namespace: in.Namespace, - Name: in.Name, - External: in.External, - Kind: in.Kind, - } -} - -type Project struct { - ProjectID string -} - -// ResolveProjectRef will resolve a ProjectRef to a Project, with the ProjectID. -func ResolveProjectRef(ctx context.Context, reader client.Reader, src client.Object, ref *v1beta1.ProjectRef) (*Project, error) { - if ref == nil { - return nil, nil - } - - if ref.Kind != "" { - if ref.Kind != "Project" { - return nil, fmt.Errorf("kind is optional on project reference, but must be \"Project\" if provided") - } - } - - if ref.External != "" { - if ref.Name != "" { - return nil, fmt.Errorf("cannot specify both name and external on project reference") - } - - tokens := strings.Split(ref.External, "/") - if len(tokens) == 1 { - return &Project{ProjectID: tokens[0]}, nil - } - if len(tokens) == 2 && tokens[0] == "projects" { - return &Project{ProjectID: tokens[1]}, nil - } - return nil, fmt.Errorf("format of project external=%q was not known (use projects/ or )", ref.External) - } - - if ref.Name == "" { - return nil, fmt.Errorf("must specify either name or external on project reference") - } - - key := types.NamespacedName{ - Namespace: ref.Namespace, - Name: ref.Name, - } - if key.Namespace == "" { - key.Namespace = src.GetNamespace() - } - - project := &unstructured.Unstructured{} - project.SetGroupVersionKind(schema.GroupVersionKind{ - Group: "resourcemanager.cnrm.cloud.google.com", - Version: "v1beta1", - Kind: "Project", - }) - if err := reader.Get(ctx, key, project); err != nil { - if apierrors.IsNotFound(err) { - return nil, fmt.Errorf("referenced Project %v not found", key) - } - return nil, fmt.Errorf("error reading referenced Project %v: %w", key, err) - } - - projectID, err := GetResourceID(project) - if err != nil { - return nil, err - } - - return &Project{ - ProjectID: projectID, - }, nil -}