diff --git a/apis/refs/v1beta1/computenetworkref.go b/apis/refs/v1beta1/computenetworkref.go deleted file mode 100644 index 6a789a97b8..0000000000 --- a/apis/refs/v1beta1/computenetworkref.go +++ /dev/null @@ -1,138 +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 v1beta1 - -import ( - "context" - "fmt" - "strings" - - 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 ResolveComputeNetwork(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 := unstructured.NestedString(computenetwork.Object, "spec", "resourceID") - if err != nil { - return nil, fmt.Errorf("reading spec.resourceID from ComputeNetwork %v: %w", key, err) - } - if computenetworkID == "" { - computenetworkID = computenetwork.GetName() - } - - computeNetworkProjectID, err := ResolveProjectIDForObject(ctx, reader, computenetwork) - if err != nil { - return nil, err - } - return &ComputeNetwork{ - Project: computeNetworkProjectID, - ComputeNetworkID: computenetworkID, - }, nil -} - -func ResolveProjectIDForObject(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/apis/refs/v1beta1/computerefs.go b/apis/refs/v1beta1/computerefs.go index d0a0671d2f..ce813db935 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 ResolveComputeNetwork(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 ResolveTargetHTTPProxy(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"` @@ -112,3 +276,16 @@ type ComputeTargetVPNGatewayRef struct { /* The `namespace` field of a `ComputeTargetVPNGateway` resource. */ Namespace string `json:"namespace,omitempty"` } + +// 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/resourceid.go b/apis/refs/v1beta1/helper.go similarity index 99% rename from apis/refs/v1beta1/resourceid.go rename to apis/refs/v1beta1/helper.go index 2513a1353f..fc3ac23f14 100644 --- a/apis/refs/v1beta1/resourceid.go +++ b/apis/refs/v1beta1/helper.go @@ -16,7 +16,6 @@ package v1beta1 import ( "fmt" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) diff --git a/apis/refs/v1beta1/projectref.go b/apis/refs/v1beta1/projectref.go index ae41316515..21c2f9ea34 100644 --- a/apis/refs/v1beta1/projectref.go +++ b/apis/refs/v1beta1/projectref.go @@ -17,14 +17,13 @@ package v1beta1 import ( "context" "fmt" - "strings" - "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. @@ -118,3 +117,43 @@ func ResolveProject(ctx context.Context, reader client.Reader, src client.Object 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/monitoring/refs.go b/pkg/controller/direct/monitoring/refs.go index a39ca07c6c..04138e6f7d 100644 --- a/pkg/controller/direct/monitoring/refs.go +++ b/pkg/controller/direct/monitoring/refs.go @@ -143,7 +143,7 @@ func normalizeMonitoringAlertPolicyRef(ctx context.Context, reader client.Reader return nil, err } - alertPolicyProjectID, err := refs.ResolveProjectIDForObject(ctx, reader, alertPolicy) + alertPolicyProjectID, err := refs.ResolveProjectID(ctx, reader, alertPolicy) if err != nil { return nil, err }