Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Migrate compute target TCP proxy(global) to direct controller #3047

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 26 additions & 7 deletions apis/compute/v1beta1/targettcpproxy_reference.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ func NewComputeTargetTCPProxyRef(ctx context.Context, reader client.Reader, obj
if projectID == "" {
return nil, fmt.Errorf("cannot resolve project")
}
region := valueOf(obj.Spec.Location)
id.parent = &ComputeTargetTCPProxyParent{ProjectID: projectID, Region: region}

id.parent = &ComputeTargetTCPProxyParent{ProjectID: projectID}

// Get desired ID
resourceID := valueOf(obj.Spec.ResourceID)
Expand All @@ -121,15 +121,13 @@ func NewComputeTargetTCPProxyRef(ctx context.Context, reader client.Reader, obj
if actualParent.ProjectID != projectID {
return nil, fmt.Errorf("spec.projectRef changed, expect %s, got %s", actualParent.ProjectID, projectID)
}
if actualParent.Region != region {
return nil, fmt.Errorf("spec.location changed, expect %s, got %s", actualParent.Region, region)
}

if actualResourceID != resourceID {
return nil, fmt.Errorf("cannot reset `metadata.name` or `spec.resourceID` to %s, since it has already assigned to %s",
resourceID, actualResourceID)
}
id.External = externalRef
id.parent = &ComputeTargetTCPProxyParent{ProjectID: projectID, Region: region}
id.parent = &ComputeTargetTCPProxyParent{ProjectID: projectID}
return id, nil
}

Expand All @@ -149,10 +147,18 @@ func (r *ComputeTargetTCPProxyRef) Parent() (*ComputeTargetTCPProxyParent, error

type ComputeTargetTCPProxyParent struct {
ProjectID string
}

type RegionalComputeTargetTCPProxyParent struct {
ProjectID string
Region string
}

func (p *ComputeTargetTCPProxyParent) String() string {
return "projects/" + p.ProjectID + "/global"
}

func (p *RegionalComputeTargetTCPProxyParent) String() string {
return "projects/" + p.ProjectID + "/regions/" + p.Region
}

Expand All @@ -161,12 +167,25 @@ func asComputeTargetTCPProxyExternal(parent *ComputeTargetTCPProxyParent, resour
}

func parseComputeTargetTCPProxyExternal(external string) (parent *ComputeTargetTCPProxyParent, resourceID string, err error) {
external = strings.TrimPrefix(external, "/")
tokens := strings.Split(external, "/")
if len(tokens) != 5 || tokens[0] != "projects" || tokens[3] != "targetTcpProxies" {
return nil, "", fmt.Errorf("format of ComputeTargetTCPProxy external=%q was not known (use projects/<projectId>/global/targetTcpProxies/<targettcpproxyID>)", external)
}
parent = &ComputeTargetTCPProxyParent{
ProjectID: tokens[1],
}
resourceID = tokens[4]
return parent, resourceID, nil
}

func parseRegionalComputeTargetTCPProxyExternal(external string) (parent *RegionalComputeTargetTCPProxyParent, resourceID string, err error) {
external = strings.TrimPrefix(external, "/")
tokens := strings.Split(external, "/")
if len(tokens) != 6 || tokens[0] != "projects" || tokens[2] != "regions" || tokens[4] != "targetTcpProxies" {
return nil, "", fmt.Errorf("format of ComputeTargetTCPProxy external=%q was not known (use projects/<projectId>/regions/<region>/targetTcpProxies/<targettcpproxyID>)", external)
}
parent = &ComputeTargetTCPProxyParent{
parent = &RegionalComputeTargetTCPProxyParent{
ProjectID: tokens[1],
Region: tokens[3],
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ type ComputeTargetTCPProxySpec struct {

// The geographical location of the ComputeTargetTCPProxy.
// Reference: GCP definition of regions/zones (https://cloud.google.com/compute/docs/regions-zones/)
// +optional
Location *string `json:"location"`
Location *string `json:"location,omitempty"`

// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="ProxyBind is immutable"
// Immutable. This field only applies when the forwarding rule that references
Expand Down
15 changes: 15 additions & 0 deletions apis/compute/v1beta1/zz_generated.deepcopy.go

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

7 changes: 5 additions & 2 deletions mockgcp/mockcompute/globaltargettcpproxyv1.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func (s *GlobalTargetTcpProxyV1) Get(ctx context.Context, req *pb.GetTargetTcpPr

obj := &pb.TargetTcpProxy{}
if err := s.storage.Get(ctx, fqn, obj); err != nil {
return nil, err
return nil, status.Errorf(codes.NotFound, "The resource '%s' was not found", fqn)
}

return obj, nil
Expand All @@ -63,6 +63,9 @@ func (s *GlobalTargetTcpProxyV1) Insert(ctx context.Context, req *pb.InsertTarge
obj.CreationTimestamp = PtrTo(s.nowString())
obj.Id = &id
obj.Kind = PtrTo("compute#targetTcpProxy")
if obj.ProxyHeader == nil {
obj.ProxyHeader = PtrTo("NONE")
}

if err := s.storage.Create(ctx, fqn, obj); err != nil {
return nil, err
Expand Down Expand Up @@ -126,7 +129,7 @@ func (s *GlobalTargetTcpProxyV1) SetProxyHeader(ctx context.Context, req *pb.Set
op := &pb.Operation{
TargetId: obj.Id,
TargetLink: obj.SelfLink,
OperationType: PtrTo("setProxyHeader"),
OperationType: PtrTo("TargetTcpProxySetProxyHeader"),
User: PtrTo("[email protected]"),
}
return s.startGlobalLRO(ctx, name.Project.ID, op, func() (proto.Message, error) {
Expand Down
100 changes: 100 additions & 0 deletions pkg/controller/direct/compute/targettcpproxy/refs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// 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 targettcpproxy

import (
"context"
"fmt"

"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s"
apierrors "k8s.io/apimachinery/pkg/api/errors"

krm "github.com/GoogleCloudPlatform/k8s-config-connector/apis/compute/v1beta1"

refs "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1"
"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"
)

func ResolveComputeBackendService(ctx context.Context, reader client.Reader, src client.Object, ref *refs.ComputeBackendServiceRef) (*refs.ComputeBackendServiceRef, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggest moving this to API so it can be shared by other services.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will address this in b/379722657

if ref == nil {
return nil, nil
}

if ref.External != "" {
if ref.Name != "" {
return nil, fmt.Errorf("cannot specify both name and external on reference")
}
return ref, nil
}

if ref.Name == "" {
return nil, fmt.Errorf("must specify either name or external on reference")
}

key := types.NamespacedName{
Namespace: ref.Namespace,
Name: ref.Name,
}
if key.Namespace == "" {
key.Namespace = src.GetNamespace()
}

computeBackendService, err := resolveResourceName(ctx, reader, key, schema.GroupVersionKind{
Group: "compute.cnrm.cloud.google.com",
Version: "v1beta1",
Kind: "ComputeBackendService",
})
if err != nil {
return nil, err
}

// targetField: self_link
// See compute servicemappings for details
selfLink, _, err := unstructured.NestedString(computeBackendService.Object, "status", "selfLink")
if err != nil || selfLink == "" {
return nil, fmt.Errorf("cannot get selfLink for referenced %s %v (status.selfLink is empty)", computeBackendService.GetKind(), computeBackendService.GetNamespace())
}
return &refs.ComputeBackendServiceRef{
External: selfLink}, nil
}

func resolveResourceName(ctx context.Context, reader client.Reader, key client.ObjectKey, gvk schema.GroupVersionKind) (*unstructured.Unstructured, error) {
resource := &unstructured.Unstructured{}
resource.SetGroupVersionKind(gvk)
if err := reader.Get(ctx, key, resource); err != nil {
if apierrors.IsNotFound(err) {
return nil, k8s.NewReferenceNotFoundError(resource.GroupVersionKind(), key)
}
return nil, fmt.Errorf("error reading referenced %v %v: %w", gvk.Kind, key, err)
}

return resource, nil
}

func resolveDependencies(ctx context.Context, reader client.Reader, obj *krm.ComputeTargetTCPProxy) error {
// Get backend service
if obj.Spec.BackendServiceRef != nil {
backendServiceRef, err := ResolveComputeBackendService(ctx, reader, obj, obj.Spec.BackendServiceRef)
if err != nil {
return err

}
obj.Spec.BackendServiceRef.External = backendServiceRef.External
}
return nil
}
Loading
Loading