Skip to content

Commit

Permalink
feat: Add mockgcp tests for ApigeeInstance
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonvigil committed Feb 4, 2025
1 parent cab0d4e commit 44aceab
Show file tree
Hide file tree
Showing 10 changed files with 584 additions and 73 deletions.
1 change: 1 addition & 0 deletions config/tests/samples/create/harness.go
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,7 @@ func MaybeSkip(t *testing.T, name string, resources []*unstructured.Unstructured

case schema.GroupKind{Group: "apigee.cnrm.cloud.google.com", Kind: "ApigeeEnvgroup"}:
case schema.GroupKind{Group: "apigee.cnrm.cloud.google.com", Kind: "ApigeeEnvironment"}:
case schema.GroupKind{Group: "apigee.cnrm.cloud.google.com", Kind: "ApigeeInstance"}:
case schema.GroupKind{Group: "apigee.cnrm.cloud.google.com", Kind: "ApigeeOrganization"}:

case schema.GroupKind{Group: "apikeys.cnrm.cloud.google.com", Kind: "APIKeysKey"}:
Expand Down
2 changes: 1 addition & 1 deletion mockgcp/mockapigee/envgroup.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ func (s *EnvgroupV1) PatchOrganizationsEnvgroup(ctx context.Context, req *pb.Pat

// Required. A list of fields to be updated in this request.
paths := req.GetUpdateMask()
fieldMask, err := field_mask.New(obj, paths)
fieldMask, err := field_mask.New(obj, strings.Split(paths, ",")...)
if err != nil {
return nil, err
}
Expand Down
207 changes: 207 additions & 0 deletions mockgcp/mockapigee/instance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
// Copyright 2025 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 mockapigee

import (
"context"
"fmt"
"strings"

"cloud.google.com/go/longrunning/autogen/longrunningpb"
pb "github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/generated/mockgcp/cloud/apigee/v1"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/fieldmaskpb"
)

type instanceName struct {
Organization string
Instance string
}

func (n *instanceName) Parent() string {
return fmt.Sprintf("organizations/%v", n.Organization)
}

func (n *instanceName) String() string {
return fmt.Sprintf("organizations/%v/instances/%v", n.Organization, n.Instance)
}

// parseInstanceName parses a string into a instanceName.
// The expected form is organizations/{organization}/instances/{instance}.
func (s *instancesServer) parseInstanceName(name string) (*instanceName, error) {
expectedFormat := "organizations/{organization}/instances/{instance}"
parts := strings.Split(name, "/")
if len(parts) != 4 || parts[0] != "organizations" || parts[2] != "instances" {
return nil, fmt.Errorf("name '%s' is not of the form %s", name, expectedFormat)
}
return &instanceName{
Organization: parts[1],
Instance: parts[3],
}, nil
}

type instancesServer struct {
*MockService
pb.UnimplementedOrganizationsInstancesServerServer
}

func (s *instancesServer) GetOrganizationsInstance(ctx context.Context, req *pb.GetOrganizationsInstanceRequest) (*pb.GoogleCloudApigeeV1Instance, error) {
name, err := s.parseInstanceName(req.Name)
if err != nil {
return nil, err
}

fqn := name.String()

obj := &pb.GoogleCloudApigeeV1Instance{}
if err := s.storage.Get(ctx, fqn, obj); err != nil {
if status.Code(err) == codes.NotFound {
return nil, status.Errorf(codes.NotFound, "generic::not_found: resource %s not found", fqn)
}
return nil, err
}
return obj, nil
}

func (s *instancesServer) CreateOrganizationsInstance(ctx context.Context, req *pb.CreateOrganizationsInstanceRequest) (*longrunningpb.Operation, error) {
reqName := req.Parent + "/instances/" + req.OrganizationsInstance.Name
name, err := s.parseInstanceName(reqName)
if err != nil {
return nil, err
}

fqn := name.String()

obj := proto.Clone(req.OrganizationsInstance).(*pb.GoogleCloudApigeeV1Instance)
obj.Name = req.OrganizationsInstance.Name
populateDefaultsForOrganizationsInstance(obj)
if err := s.storage.Create(ctx, fqn, obj); err != nil {
return nil, err
}

metadata := &pb.GoogleCloudApigeeV1OperationMetadata{
OperationType: "INSERT",
State: "IN_PROGRESS",
TargetResourceName: fqn,
}
op, err := s.operations.StartLRO(ctx, req.GetParent(), metadata, func() (proto.Message, error) {
metadata.Progress = &pb.GoogleCloudApigeeV1OperationMetadataProgress{
Description: "Succeeded",
PercentDone: 100,
}
metadata.State = "FINISHED"
result := proto.Clone(obj).(*pb.GoogleCloudApigeeV1Instance)
populateOutputsForOrganizationsInstance(result)
s.storage.Update(ctx, fqn, result)
return result, nil
})
return op, err
}

func (s *instancesServer) PatchOrganizationsInstance(ctx context.Context, req *pb.PatchOrganizationsInstanceRequest) (*longrunningpb.Operation, error) {
name, err := s.parseInstanceName(req.Name)
if err != nil {
return nil, err
}

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

// Required. A list of fields to be updated in this request.
paths := req.GetUpdateMask()
fieldMask, err := fieldmaskpb.New(obj, strings.Split(paths, ",")...)
if err != nil {
return nil, err
}

for _, path := range fieldMask.GetPaths() {
switch path {
case "access_logging_config":
obj.AccessLoggingConfig = req.OrganizationsInstance.AccessLoggingConfig
case "consumer_accept_list":
obj.ConsumerAcceptList = req.OrganizationsInstance.ConsumerAcceptList
default:
return nil, status.Errorf(codes.InvalidArgument, "update mask path %q not supported by mockgcp", path)
}
}

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

metadata := &pb.GoogleCloudApigeeV1OperationMetadata{
OperationType: "PATCH",
State: "IN_PROGRESS",
TargetResourceName: fqn,
}
op, err := s.operations.StartLRO(ctx, name.Parent(), metadata, func() (proto.Message, error) {
metadata.State = "FINISHED"
result := proto.Clone(obj).(*pb.GoogleCloudApigeeV1Instance)
return result, nil
})
return op, err
}

func (s *instancesServer) DeleteOrganizationsInstance(ctx context.Context, req *pb.DeleteOrganizationsInstanceRequest) (*longrunningpb.Operation, error) {
name, err := s.parseInstanceName(req.Name)
if err != nil {
return nil, err
}

fqn := name.String()

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

metadata := &pb.GoogleCloudApigeeV1OperationMetadata{
OperationType: "DELETE",
State: "IN_PROGRESS",
TargetResourceName: fqn,
}
op, err := s.operations.StartLRO(ctx, name.Parent(), metadata, func() (proto.Message, error) {
metadata.State = "FINISHED"
return &pb.GoogleCloudApigeeV1Instance{}, nil
})
return op, err
}

func populateDefaultsForOrganizationsInstance(obj *pb.GoogleCloudApigeeV1Instance) {
if len(obj.ConsumerAcceptList) == 0 {
obj.ConsumerAcceptList = []string{"${projectId}"}
}
if obj.IpRange == "" {
obj.IpRange = "10.39.56.0/22,10.14.0.64/28"
}
if obj.PeeringCidrRange == "" {
obj.PeeringCidrRange = "SLASH_22"
}
}

func populateOutputsForOrganizationsInstance(obj *pb.GoogleCloudApigeeV1Instance) {
obj.CreatedAt = 123456789
obj.Host = "10.39.56.2"
obj.LastModifiedAt = 123456789
obj.Port = "443"
obj.RuntimeVersion = "1-14-0-apigee-4"
obj.ServiceAttachment = "projects/${projectId}/regions/us-central1/serviceAttachments/apigee-us-central1-abcd"
obj.State = "ACTIVE"
}
2 changes: 2 additions & 0 deletions mockgcp/mockapigee/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,15 @@ func (s *MockService) ExpectedHosts() []string {
func (s *MockService) Register(grpcServer *grpc.Server) {
pb.RegisterOrganizationsEnvironmentsServerServer(grpcServer, &environmentsServer{MockService: s})
pb.RegisterOrganizationsEnvgroupsServerServer(grpcServer, &EnvgroupV1{MockService: s})
pb.RegisterOrganizationsInstancesServerServer(grpcServer, &instancesServer{MockService: s})
pb.RegisterOrganizationsServerServer(grpcServer, &organizationsServer{MockService: s})
}

func (s *MockService) NewHTTPMux(ctx context.Context, conn *grpc.ClientConn) (http.Handler, error) {
mux, err := httpmux.NewServeMux(ctx, conn, httpmux.Options{},
pb.RegisterOrganizationsEnvironmentsServerHandler,
pb.RegisterOrganizationsEnvgroupsServerHandler,
pb.RegisterOrganizationsInstancesServerHandler,
pb.RegisterOrganizationsServerHandler,
s.operations.RegisterOperationsPath("/v1/{prefix=**}/operations/{name}"))
if err != nil {
Expand Down
3 changes: 3 additions & 0 deletions mockgcp/mockserviceusage/serviceusagev1beta1.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ func (s *ServiceUsageV1Beta1) GenerateServiceIdentity(ctx context.Context, req *
case "bigquery.googleapis.com":
identity.Email = "bq-" + strconv.FormatInt(name.Project.Number, 10) + "@bigquery-encryption.iam.gserviceaccount.com"
identity.UniqueId = "123456789007"
case "apigee.googleapis.com":
identity.Email = "service-" + strconv.FormatInt(name.Project.Number, 10) + "@gcp-sa-apigee.iam.gserviceaccount.com"
identity.UniqueId = "123456789008"
default:
return nil, fmt.Errorf("generating serviceIdentity for service %q not implemented in mock", name.ServiceName)
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/controller/direct/apigee/instance_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,11 @@ func (a *ApigeeInstanceAdapter) Update(ctx context.Context, updateOp *directbase

if resource.AccessLoggingConfig != nil && !reflect.DeepEqual(resource.AccessLoggingConfig, a.actual.AccessLoggingConfig) {
log.V(2).Info("change detected: accessLoggingConfig")
updateMask.Paths = append(updateMask.Paths, "accessLoggingConfig")
updateMask.Paths = append(updateMask.Paths, "access_logging_config")
}
if resource.ConsumerAcceptList != nil && !reflect.DeepEqual(asSortedCopy(resource.ConsumerAcceptList), asSortedCopy(a.actual.ConsumerAcceptList)) {
log.V(2).Info("change detected: consumerAcceptList")
updateMask.Paths = append(updateMask.Paths, "consumerAcceptList")
updateMask.Paths = append(updateMask.Paths, "consumer_accept_list")
}

if len(updateMask.Paths) == 0 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@ status:
lastModifiedAt: 1711974896
port: "443"
runtimeVersion: 1-14-0-apigee-4
serviceAttachment: projects/eef779b38d9fb4b50p-tp/regions/us-central1/serviceAttachments/apigee-us-central1-8dhb
serviceAttachment: projects/${projectId}/regions/us-central1/serviceAttachments/apigee-us-central1-abcd
state: ACTIVE
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ GET https://apigee.googleapis.com/v1/organizations/${projectId}/instances/apigee
User-Agent: kcc/${kccVersion} (+https://github.com/GoogleCloudPlatform/k8s-config-connector) kcc/controller-manager/${kccVersion}

404 Not Found
Cache-Control: private
Content-Type: application/json; charset=UTF-8
Server: ESF
Vary: Origin
Expand Down Expand Up @@ -31,6 +32,7 @@ User-Agent: kcc/${kccVersion} (+https://github.com/GoogleCloudPlatform/k8s-confi
}

200 OK
Cache-Control: private
Content-Type: application/json; charset=UTF-8
Server: ESF
Vary: Origin
Expand All @@ -42,7 +44,7 @@ X-Xss-Protection: 0

{
"metadata": {
"@type": "type.googleapis.com/google.cloud.apigee.v1.OperationMetadata",
"@type": "type.googleapis.com/google.cloud.apigee.v1.GoogleCloudApigeeV1OperationMetadata",
"operationType": "INSERT",
"state": "IN_PROGRESS",
"targetResourceName": "organizations/${projectId}/instances/apigeeinstance-${uniqueId}"
Expand All @@ -56,6 +58,7 @@ GET https://apigee.googleapis.com/v1/organizations/${projectId}/operations/${ope
User-Agent: kcc/${kccVersion} (+https://github.com/GoogleCloudPlatform/k8s-config-connector) kcc/controller-manager/${kccVersion}

200 OK
Cache-Control: private
Content-Type: application/json; charset=UTF-8
Server: ESF
Vary: Origin
Expand All @@ -68,7 +71,7 @@ X-Xss-Protection: 0
{
"done": true,
"metadata": {
"@type": "type.googleapis.com/google.cloud.apigee.v1.OperationMetadata",
"@type": "type.googleapis.com/google.cloud.apigee.v1.GoogleCloudApigeeV1OperationMetadata",
"operationType": "INSERT",
"progress": {
"description": "Succeeded",
Expand All @@ -79,7 +82,7 @@ X-Xss-Protection: 0
},
"name": "organizations/${projectId}/operations/${operationID}",
"response": {
"@type": "type.googleapis.com/google.cloud.apigee.v1.Instance",
"@type": "type.googleapis.com/google.cloud.apigee.v1.GoogleCloudApigeeV1Instance",
"consumerAcceptList": [
"${projectId}"
],
Expand All @@ -92,7 +95,7 @@ X-Xss-Protection: 0
"peeringCidrRange": "SLASH_22",
"port": "443",
"runtimeVersion": "1-14-0-apigee-4",
"serviceAttachment": "projects/eef779b38d9fb4b50p-tp/regions/us-central1/serviceAttachments/apigee-us-central1-8dhb",
"serviceAttachment": "projects/${projectId}/regions/us-central1/serviceAttachments/apigee-us-central1-abcd",
"state": "ACTIVE"
}
}
Expand All @@ -103,6 +106,7 @@ GET https://apigee.googleapis.com/v1/organizations/${projectId}/instances/apigee
User-Agent: kcc/${kccVersion} (+https://github.com/GoogleCloudPlatform/k8s-config-connector) kcc/controller-manager/${kccVersion}

200 OK
Cache-Control: private
Content-Type: application/json; charset=UTF-8
Server: ESF
Vary: Origin
Expand All @@ -125,7 +129,7 @@ X-Xss-Protection: 0
"peeringCidrRange": "SLASH_22",
"port": "443",
"runtimeVersion": "1-14-0-apigee-4",
"serviceAttachment": "projects/eef779b38d9fb4b50p-tp/regions/us-central1/serviceAttachments/apigee-us-central1-8dhb",
"serviceAttachment": "projects/${projectId}/regions/us-central1/serviceAttachments/apigee-us-central1-abcd",
"state": "ACTIVE"
}

Expand All @@ -135,6 +139,7 @@ DELETE https://apigee.googleapis.com/v1/organizations/${projectId}/instances/api
User-Agent: kcc/${kccVersion} (+https://github.com/GoogleCloudPlatform/k8s-config-connector) kcc/controller-manager/${kccVersion}

200 OK
Cache-Control: private
Content-Type: application/json; charset=UTF-8
Server: ESF
Vary: Origin
Expand All @@ -146,7 +151,7 @@ X-Xss-Protection: 0

{
"metadata": {
"@type": "type.googleapis.com/google.cloud.apigee.v1.OperationMetadata",
"@type": "type.googleapis.com/google.cloud.apigee.v1.GoogleCloudApigeeV1OperationMetadata",
"operationType": "DELETE",
"state": "IN_PROGRESS",
"targetResourceName": "organizations/${projectId}/instances/apigeeinstance-${uniqueId}"
Expand All @@ -160,6 +165,7 @@ GET https://apigee.googleapis.com/v1/organizations/${projectId}/operations/${ope
User-Agent: kcc/${kccVersion} (+https://github.com/GoogleCloudPlatform/k8s-config-connector) kcc/controller-manager/${kccVersion}

200 OK
Cache-Control: private
Content-Type: application/json; charset=UTF-8
Server: ESF
Vary: Origin
Expand All @@ -172,13 +178,13 @@ X-Xss-Protection: 0
{
"done": true,
"metadata": {
"@type": "type.googleapis.com/google.cloud.apigee.v1.OperationMetadata",
"@type": "type.googleapis.com/google.cloud.apigee.v1.GoogleCloudApigeeV1OperationMetadata",
"operationType": "DELETE",
"state": "FINISHED",
"targetResourceName": "organizations/${projectId}/instances/apigeeinstance-${uniqueId}"
},
"name": "organizations/${projectId}/operations/${operationID}",
"response": {
"@type": "type.googleapis.com/google.protobuf.Empty"
"@type": "type.googleapis.com/google.cloud.apigee.v1.GoogleCloudApigeeV1Instance"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,5 @@ status:
lastModifiedAt: 1711974896
port: "443"
runtimeVersion: 1-14-0-apigee-4
serviceAttachment: projects/eef779b38d9fb4b50p-tp/regions/us-central1/serviceAttachments/apigee-us-central1-i9mj
serviceAttachment: projects/${projectId}/regions/us-central1/serviceAttachments/apigee-us-central1-abcd
state: ACTIVE
Loading

0 comments on commit 44aceab

Please sign in to comment.