Skip to content

Commit

Permalink
add mockGCP for service attachment and collect logs
Browse files Browse the repository at this point in the history
  • Loading branch information
gemmahou committed Aug 29, 2024
1 parent 316f97f commit 226915b
Show file tree
Hide file tree
Showing 13 changed files with 5,749 additions and 16 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ TEST_TARGET ?= mock

.PHONY: e2e-sample-tests
e2e-sample-tests:
RUN_E2E=1 E2E_KUBE_TARGET=envtest E2E_GCP_TARGET=${TEST_TARGET} KCC_USE_DIRECT_RECONCILERS="SQLInstance" go test -test.count=1 -timeout 3600s -v ./tests/e2e -run ${SAMPLE_TESTCASE}
RUN_E2E=1 E2E_KUBE_TARGET=envtest E2E_GCP_TARGET=${TEST_TARGET} KCC_USE_DIRECT_RECONCILERS="SQLInstance,ComputeForwardingRule" \ go test -test.count=1 -timeout 3600s -v ./tests/e2e -run ${SAMPLE_TESTCASE}

# orgnization ID for google.com
ORG_ID ?= 433637338589
Expand Down
1 change: 1 addition & 0 deletions config/tests/samples/create/harness.go
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,7 @@ func MaybeSkip(t *testing.T, name string, resources []*unstructured.Unstructured
case schema.GroupKind{Group: "compute.cnrm.cloud.google.com", Kind: "ComputeNodeGroup"}:
case schema.GroupKind{Group: "compute.cnrm.cloud.google.com", Kind: "ComputeNodeTemplate"}:
case schema.GroupKind{Group: "compute.cnrm.cloud.google.com", Kind: "ComputeManagedSSLCertificate"}:
case schema.GroupKind{Group: "compute.cnrm.cloud.google.com", Kind: "ComputeServiceAttachment"}:
case schema.GroupKind{Group: "compute.cnrm.cloud.google.com", Kind: "ComputeSubnetwork"}:
case schema.GroupKind{Group: "compute.cnrm.cloud.google.com", Kind: "ComputeTargetVPNGateway"}:
case schema.GroupKind{Group: "compute.cnrm.cloud.google.com", Kind: "ComputeVPNGateway"}:
Expand Down
2 changes: 1 addition & 1 deletion hack/record-gcp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ E2E_GCP_TARGET=real \
GOLDEN_REQUEST_CHECKS=1 \
GOLDEN_OBJECT_CHECKS=1 \
WRITE_GOLDEN_OUTPUT=1 \
KCC_USE_DIRECT_RECONCILERS="SQLInstance" \
RUN_E2E=1 \
KCC_USE_DIRECT_RECONCILERS="SQLInstance,ComputeForwardingRule" \
go test ./tests/e2e -timeout 7200s -v -run $RUN_TESTS
220 changes: 220 additions & 0 deletions mockgcp/mockcompute/regionalserviceattachmentv1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
// Copyright 2022 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 mockcompute

import (
"context"
"errors"
"fmt"
"strings"

"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"k8s.io/klog/v2"

"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/common/projects"
pb "github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/generated/mockgcp/cloud/compute/v1"
)

type RegionalServiceAttachmentV1 struct {
*MockService
pb.UnimplementedServiceAttachmentsServer
}

func (s *RegionalServiceAttachmentV1) Get(ctx context.Context, req *pb.GetServiceAttachmentRequest) (*pb.ServiceAttachment, error) {
reqName := "projects/" + req.GetProject() + "/regions/" + req.GetRegion() + "/serviceAttachments/" + req.GetServiceAttachment()
name, err := s.parseRegionalServiceAttachmentName(reqName)
if err != nil {
return nil, err
}

fqn := name.String()

obj := &pb.ServiceAttachment{}
if err := s.storage.Get(ctx, fqn, obj); err != nil {
return nil, err
}

return obj, nil
}

func (s *RegionalServiceAttachmentV1) Insert(ctx context.Context, req *pb.InsertServiceAttachmentRequest) (*pb.Operation, error) {
reqName := "projects/" + req.GetProject() + "/regions/" + req.GetRegion() + "/serviceAttachments/" + req.GetServiceAttachmentResource().GetName()
name, err := s.parseRegionalServiceAttachmentName(reqName)
if err != nil {
return nil, err
}

fqn := name.String()

id := s.generateID()

obj := proto.Clone(req.GetServiceAttachmentResource()).(*pb.ServiceAttachment)
obj.SelfLink = PtrTo("https://www.googleapis.com/compute/v1/" + name.String())
obj.CreationTimestamp = PtrTo(s.nowString())
obj.Id = &id
obj.Kind = PtrTo("compute#serviceAttachment")
if obj.Fingerprint == nil {
obj.Fingerprint = PtrTo(computeFingerprint(obj))
}

if err := s.storage.Create(ctx, fqn, obj); err != nil {
return nil, err
}

op := &pb.Operation{
TargetId: obj.Id,
TargetLink: obj.SelfLink,
OperationType: PtrTo("insert"),
User: PtrTo("[email protected]"),
}
return s.startRegionalLRO(ctx, name.Project.ID, name.Region, op, func() (proto.Message, error) {
return obj, nil
})
}

// Updates a ServiceAttachment resource in the specified project using the data included in the request.
// This method supports PATCH semantics and uses the JSON merge patch format and processing rules.
func (s *RegionalServiceAttachmentV1) Patch(ctx context.Context, req *pb.PatchServiceAttachmentRequest) (*pb.Operation, error) {
reqName := "projects/" + req.GetProject() + "/regions/" + req.GetRegion() + "/serviceAttachments/" + req.GetServiceAttachment()
name, err := s.parseRegionalServiceAttachmentName(reqName)
if err != nil {
return nil, err
}

fqn := name.String()
obj := &pb.ServiceAttachment{}
if err := s.storage.Get(ctx, fqn, obj); err != nil {
return nil, err
}

if err := mergeProtos(obj.ProtoReflect(), req.GetServiceAttachmentResource().ProtoReflect()); err != nil {
return nil, err
}
obj.Fingerprint = nil
obj.Fingerprint = PtrTo(computeFingerprint(obj))

if err := s.storage.Update(ctx, fqn, obj); err != nil {
return nil, err
}

op := &pb.Operation{
TargetId: obj.Id,
TargetLink: obj.SelfLink,
OperationType: PtrTo("patch"),
User: PtrTo("[email protected]"),
}

return s.startRegionalLRO(ctx, name.Project.ID, name.Region, op, func() (proto.Message, error) {
return obj, nil
})
}

func (s *RegionalServiceAttachmentV1) Delete(ctx context.Context, req *pb.DeleteServiceAttachmentRequest) (*pb.Operation, error) {
reqName := "projects/" + req.GetProject() + "/regions/" + req.GetRegion() + "/serviceAttachments/" + req.GetServiceAttachment()
name, err := s.parseRegionalServiceAttachmentName(reqName)
if err != nil {
return nil, err
}

fqn := name.String()

deleted := &pb.ServiceAttachment{}
if err := s.storage.Delete(ctx, fqn, deleted); err != nil {
return nil, err
}

op := &pb.Operation{
TargetId: deleted.Id,
TargetLink: deleted.SelfLink,
OperationType: PtrTo("delete"),
User: PtrTo("[email protected]"),
}

return s.startRegionalLRO(ctx, name.Project.ID, name.Region, op, func() (proto.Message, error) {
return deleted, nil
})
}

type regionalServiceAttachmentName struct {
Project *projects.ProjectData
Region string
Name string
}

func (n *regionalServiceAttachmentName) String() string {
return "projects/" + n.Project.ID + "/regions/" + n.Region + "/serviceAttachments/" + n.Name
}

// parseRegionalServiceAttachmentName parses a string into a serviceattachmentName.
// The expected form is `projects/*/regions/*/serviceattachment/*`.
func (s *MockService) parseRegionalServiceAttachmentName(name string) (*regionalServiceAttachmentName, error) {
tokens := strings.Split(name, "/")

if len(tokens) == 6 && tokens[0] == "projects" && tokens[2] == "regions" && tokens[4] == "serviceAttachments" {
project, err := s.Projects.GetProjectByID(tokens[1])
if err != nil {
return nil, err
}

name := &regionalServiceAttachmentName{
Project: project,
Region: tokens[3],
Name: tokens[5],
}

return name, nil
} else {
return nil, status.Errorf(codes.InvalidArgument, "name %q is not valid", name)
}
}

// mergeProtos implements the patch/update semantics of GCP updates.
func mergeProtos(dst protoreflect.Message, src protoreflect.Message) error {
var errs []error
src.Range(func(fd protoreflect.FieldDescriptor, srcVal protoreflect.Value) bool {
if fd.IsList() {
// errs = append(errs, fmt.Errorf("unhandled list for field %v: %v", fd, fd.Kind()))
switch fd.Kind() {
case protoreflect.StringKind:
// Replace []string
dstVal := dst.Get(fd)
klog.Warningf("replacing string list src=%v, dst=%v", srcVal, dstVal)
dst.Set(fd, srcVal)
case protoreflect.MessageKind:
dstVal := dst.Get(fd)
klog.Warningf("replacing message list src=%v, dst=%v", srcVal, dstVal)
dst.Set(fd, srcVal)

default:
errs = append(errs, fmt.Errorf("unhandled kind for list field %v: %v", fd, fd.Kind()))
}
} else if fd.IsMap() {
errs = append(errs, fmt.Errorf("unhandled list for field %v: %v", fd, fd.Kind()))
} else {
switch fd.Kind() {
case protoreflect.StringKind:
dst.Set(fd, srcVal)
default:
errs = append(errs, fmt.Errorf("unhandled kind for field %v: %v", fd, fd.Kind()))
}
}

return true
})
return errors.Join(errs...)
}
9 changes: 8 additions & 1 deletion mockgcp/mockcompute/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ func New(env *common.MockEnvironment, storage storage.Storage) *MockService {
}

func (s *MockService) ExpectedHosts() []string {
return []string{"compute.googleapis.com"}
// service attachment has host "www.googleapis.com"
return []string{"compute.googleapis.com", "www.googleapis.com"}
}

func (s *MockService) Register(grpcServer *grpc.Server) {
Expand Down Expand Up @@ -80,6 +81,8 @@ func (s *MockService) Register(grpcServer *grpc.Server) {
pb.RegisterGlobalAddressesServer(grpcServer, &GlobalAddressesV1{MockService: s})
pb.RegisterSslCertificatesServer(grpcServer, &GlobalSSLCertificatesV1{MockService: s})

pb.RegisterServiceAttachmentsServer(grpcServer, &RegionalServiceAttachmentV1{MockService: s})

pb.RegisterGlobalForwardingRulesServer(grpcServer, &GlobalForwardingRulesV1{MockService: s})
pb.RegisterForwardingRulesServer(grpcServer, &RegionalForwardingRulesV1{MockService: s})

Expand Down Expand Up @@ -179,6 +182,10 @@ func (s *MockService) NewHTTPMux(ctx context.Context, conn *grpc.ClientConn) (ht
return nil, err
}

if err := pb.RegisterServiceAttachmentsHandler(ctx, mux.ServeMux, conn); err != nil {
return nil, err
}

if err := pb.RegisterImagesHandler(ctx, mux.ServeMux, conn); err != nil {
return nil, err
}
Expand Down
57 changes: 45 additions & 12 deletions pkg/controller/direct/compute/forwardingrule_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,34 +90,67 @@ func (m *forwardingRuleModel) AdapterForObject(ctx context.Context, reader clien
obj.Spec.NetworkRef.External = networkRef.External
}

// Get compute address
if obj.Spec.IpAddress.AddressRef != nil {
computeAddressRef, err := ResolveComputeAddress(ctx, reader, obj, obj.Spec.IpAddress.AddressRef)
// Get subnetwork
if obj.Spec.SubnetworkRef != nil {
subnetworkRef, err := ResolveComputeSubnetwork(ctx, reader, obj, obj.Spec.SubnetworkRef)
if err != nil {
return nil, err

}
obj.Spec.IpAddress.AddressRef.External = computeAddressRef.External
obj.Spec.SubnetworkRef.External = subnetworkRef.External
}

// Get target ComputeTargetHTTPProxy
if obj.Spec.Target.TargetHTTPProxyRef != nil {
targetHTTPProxyRef, err := ResolveComputeTargetHTTPProxy(ctx, reader, obj, obj.Spec.Target.TargetHTTPProxyRef)
// Get backend service
if obj.Spec.BackendServiceRef != nil {
backendServiceRef, err := ResolveComputeBackendService(ctx, reader, obj, obj.Spec.BackendServiceRef)
if err != nil {
return nil, err

}
obj.Spec.Target.TargetHTTPProxyRef.External = targetHTTPProxyRef.External
obj.Spec.BackendServiceRef.External = backendServiceRef.External
}

// Get target TargetVPNGateway
if obj.Spec.Target.TargetVPNGatewayRef != nil {
targetVPNGatewayRef, err := ResolveComputeTargetVPNGateway(ctx, reader, obj, obj.Spec.Target.TargetVPNGatewayRef)
// Get ip address, ip address is optional
if obj.Spec.IpAddress != nil && obj.Spec.IpAddress.AddressRef != nil {
computeAddressRef, err := ResolveComputeAddress(ctx, reader, obj, obj.Spec.IpAddress.AddressRef)
if err != nil {
return nil, err

}
obj.Spec.Target.TargetVPNGatewayRef.External = targetVPNGatewayRef.External
obj.Spec.IpAddress.AddressRef.External = computeAddressRef.External
}

// Get target, target is optional
if obj.Spec.Target != nil {
// Get target ServiceAttachment
if obj.Spec.Target.ServiceAttachmentRef != nil {
serviceAttachmentRef, err := ResolveComputeServiceAttachment(ctx, reader, obj, obj.Spec.Target.ServiceAttachmentRef)
if err != nil {
return nil, err

}
obj.Spec.Target.ServiceAttachmentRef.External = serviceAttachmentRef.External
}

// Get target ComputeTargetHTTPProxy
if obj.Spec.Target.TargetHTTPProxyRef != nil {
targetHTTPProxyRef, err := ResolveComputeTargetHTTPProxy(ctx, reader, obj, obj.Spec.Target.TargetHTTPProxyRef)
if err != nil {
return nil, err

}
obj.Spec.Target.TargetHTTPProxyRef.External = targetHTTPProxyRef.External
}

// Get target TargetVPNGateway
if obj.Spec.Target.TargetVPNGatewayRef != nil {
targetVPNGatewayRef, err := ResolveComputeTargetVPNGateway(ctx, reader, obj, obj.Spec.Target.TargetVPNGatewayRef)
if err != nil {
return nil, err

}
obj.Spec.Target.TargetVPNGatewayRef.External = targetVPNGatewayRef.External
}
}

// Get location
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
apiVersion: compute.cnrm.cloud.google.com/v1beta1
kind: ComputeForwardingRule
metadata:
annotations:
cnrm.cloud.google.com/management-conflict-prevention-policy: none
cnrm.cloud.google.com/project-id: ${projectId}
finalizers:
- cnrm.cloud.google.com/finalizer
- cnrm.cloud.google.com/deletion-defender
generation: 1
labels:
cnrm-test: "true"
label-one: value-two
name: computeforwardingrule-${uniqueId}
namespace: ${uniqueId}
spec:
allowPscGlobalAccess: true
description: A VPC private service connect forwarding rule
ipAddress:
addressRef:
external: https://www.googleapis.com/compute/v1/projects/${projectId}/regions/us-central1/addresses/computeaddress-${uniqueId}
loadBalancingScheme: ""
location: us-central1
networkRef:
name: computenetwork-1-${uniqueId}
target:
serviceAttachmentRef:
name: computeserviceattachment-${uniqueId}
status:
conditions:
- lastTransitionTime: "1970-01-01T00:00:00Z"
message: The resource is up to date
reason: UpToDate
status: "True"
type: Ready
creationTimestamp: "1970-01-01T00:00:00Z"
labelFingerprint: abcdef0123A=
observedGeneration: 1
selfLink: https://www.googleapis.com/compute/v1/projects/${projectId}/regions/us-central1/forwardingRules/computeforwardingrule-${uniqueId}
Loading

0 comments on commit 226915b

Please sign in to comment.