Skip to content

Commit

Permalink
Merge pull request GoogleCloudPlatform#1029 from justinsb/mockgcp_net…
Browse files Browse the repository at this point in the history
…work_resource

mockgcp: Implement Network resource
  • Loading branch information
google-oss-prow[bot] authored Jan 19, 2024
2 parents 9403004 + 5e88358 commit 59c7576
Show file tree
Hide file tree
Showing 7 changed files with 369 additions and 0 deletions.
3 changes: 3 additions & 0 deletions config/tests/samples/create/harness.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,9 @@ func MaybeSkip(t *testing.T, name string, resources []*unstructured.Unstructured

case schema.GroupKind{Group: "containerattached.cnrm.cloud.google.com", Kind: "ContainerAttachedCluster"}:

case schema.GroupKind{Group: "compute.cnrm.cloud.google.com", Kind: "ComputeNetwork"}:
// ok

case schema.GroupKind{Group: "iam.cnrm.cloud.google.com", Kind: "IAMPartialPolicy"}:
case schema.GroupKind{Group: "iam.cnrm.cloud.google.com", Kind: "IAMPolicy"}:
case schema.GroupKind{Group: "iam.cnrm.cloud.google.com", Kind: "IAMPolicyMember"}:
Expand Down
2 changes: 2 additions & 0 deletions mockgcp/mock_http_roundtrip.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/mockbilling"
"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/mockcertificatemanager"
"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/mockcloudfunctions"
"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/mockcompute"
"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/mockedgecontainer"
"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/mockedgenetwork"
"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/mockgkemulticloud"
Expand Down Expand Up @@ -88,6 +89,7 @@ func NewMockRoundTripper(t *testing.T, k8sClient client.Client, storage storage.
services = append(services, resourcemanagerService)
services = append(services, mockbilling.New(env, storage))
services = append(services, mockcertificatemanager.New(env, storage))
services = append(services, mockcompute.New(env, storage))
services = append(services, mockgkemulticloud.New(env, storage))
services = append(services, mockiam.New(env, storage))
services = append(services, mocksecretmanager.New(env, storage))
Expand Down
32 changes: 32 additions & 0 deletions mockgcp/mockcompute/ids.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2023 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 "time"

func (s *MockService) nowString() string {
now := time.Now()
format := "2006-01-02T15:04:05.999Z07:00"

return now.Format(format)
}

func (s *MockService) generateID() uint64 {
return uint64(time.Now().UnixNano())
}

func PtrTo[T any](t T) *T {
return &t
}
51 changes: 51 additions & 0 deletions mockgcp/mockcompute/lro.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2023 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"
"strings"

pb "github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/generated/mockgcp/cloud/compute/v1"
lropb "google.golang.org/genproto/googleapis/longrunning"

"google.golang.org/protobuf/proto"
)

func (s *MockService) newLRO(ctx context.Context, projectID string) (*pb.Operation, error) {
lro, err := s.operations.NewLRO(ctx)
if err != nil {
return nil, err
}
selfLinkPrefix := "https://compute.googleapis.com/compute/v1/projects/" + projectID + "/global/operations/"
return mapToComputeLRO(lro, selfLinkPrefix)
}

func mapToComputeLRO(lro *lropb.Operation, selfLinkPrefix string) (*pb.Operation, error) {
lroName := strings.TrimPrefix(lro.Name, "operations/")
computeLRO := &pb.Operation{
Name: &lroName,
}
var status pb.Operation_Status
if lro.Done {
status = pb.Operation_DONE
} else {
status = pb.Operation_RUNNING
}
computeLRO.Status = &status

computeLRO.SelfLink = proto.String(selfLinkPrefix + lroName)
return computeLRO, nil
}
54 changes: 54 additions & 0 deletions mockgcp/mockcompute/names.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// 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 (
"strings"

"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/common/projects"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

type networkName struct {
Project *projects.ProjectData
Name string
}

func (n *networkName) String() string {
return "projects/" + n.Project.ID + "/global" + "/networks/" + n.Name
}

// parseNetworkName parses a string into a networkName.
// The expected form is `projects/*/global/networks/*`.
func (s *MockService) parseNetworkName(name string) (*networkName, error) {
tokens := strings.Split(name, "/")

if len(tokens) == 5 && tokens[0] == "projects" && tokens[2] == "global" && tokens[3] == "networks" {
project, err := s.projects.GetProjectByID(tokens[1])
if err != nil {
return nil, err
}

name := &networkName{
Project: project,
Name: tokens[4],
}

return name, nil
} else {
return nil, status.Errorf(codes.InvalidArgument, "name %q is not valid", name)
}
}
129 changes: 129 additions & 0 deletions mockgcp/mockcompute/networksv1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// 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"
"fmt"

"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
apierrors "k8s.io/apimachinery/pkg/api/errors"

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

type NetworksV1 struct {
*MockService
pb.UnimplementedNetworksServer
}

func (s *NetworksV1) Get(ctx context.Context, req *pb.GetNetworkRequest) (*pb.Network, error) {
name, err := s.parseNetworkName("projects/" + req.GetProject() + "/global" + "/networks/" + req.GetNetwork())
if err != nil {
return nil, err
}

fqn := name.String()

obj := &pb.Network{}
if err := s.storage.Get(ctx, fqn, obj); err != nil {
if apierrors.IsNotFound(err) {
return nil, status.Errorf(codes.NotFound, "network %q not found", name)
} else {
return nil, status.Errorf(codes.Internal, "error reading network: %v", err)
}
}

return obj, nil
}

func (s *NetworksV1) Insert(ctx context.Context, req *pb.InsertNetworkRequest) (*pb.Operation, error) {
name, err := s.parseNetworkName("projects/" + req.GetProject() + "/global" + "/networks/" + req.GetNetworkResource().GetName())
if err != nil {
return nil, err
}

fqn := name.String()

id := s.generateID()

obj := proto.Clone(req.GetNetworkResource()).(*pb.Network)
obj.SelfLink = PtrTo("https://compute.googleapis.com/compute/v1/" + name.String())
obj.CreationTimestamp = PtrTo(s.nowString())
obj.Id = &id
obj.SelfLinkWithId = PtrTo(fmt.Sprintf("https://compute.googleapis.com/compute/v1/projects/%s/global/networks/%d", name.Project.ID, id))
obj.Kind = PtrTo("compute#network")

if err := s.storage.Create(ctx, fqn, obj); err != nil {
return nil, status.Errorf(codes.Internal, "error creating network: %v", err)
}

return s.newLRO(ctx, name.Project.ID)
}

// Patches the specified network with the data included in the request.
// Only the following fields can be modified: routingConfig.routingMode.
func (s *NetworksV1) Patch(ctx context.Context, req *pb.PatchNetworkRequest) (*pb.Operation, error) {
name, err := s.parseNetworkName("projects/" + req.GetProject() + "/global" + "/networks/" + req.GetNetwork())
if err != nil {
return nil, err
}

fqn := name.String()
obj := &pb.Network{}
if err := s.storage.Get(ctx, fqn, obj); err != nil {
if apierrors.IsNotFound(err) {
return nil, status.Errorf(codes.NotFound, "network %q not found", fqn)
}
return nil, status.Errorf(codes.Internal, "error reading network: %v", err)
}

if req.GetNetworkResource().RoutingConfig != nil {
if req.GetNetworkResource().GetRoutingConfig().RoutingMode != nil {
if obj.RoutingConfig == nil {
obj.RoutingConfig = &pb.NetworkRoutingConfig{}
}
obj.RoutingConfig.RoutingMode = req.GetNetworkResource().GetRoutingConfig().RoutingMode
}
}

if err := s.storage.Update(ctx, fqn, obj); err != nil {
return nil, status.Errorf(codes.Internal, "error updating network: %v", err)
}

return s.newLRO(ctx, name.Project.ID)
}

func (s *NetworksV1) Delete(ctx context.Context, req *pb.DeleteNetworkRequest) (*pb.Operation, error) {
name, err := s.parseNetworkName("projects/" + req.GetProject() + "/global" + "/networks/" + req.GetNetwork())
if err != nil {
return nil, err
}

fqn := name.String()

deleted := &pb.Network{}
if err := s.storage.Delete(ctx, fqn, deleted); err != nil {
if apierrors.IsNotFound(err) {
return nil, status.Errorf(codes.NotFound, "network %q not found", name)
} else {
return nil, status.Errorf(codes.Internal, "error deleting network: %v", err)
}
}

return s.newLRO(ctx, name.Project.ID)
}
98 changes: 98 additions & 0 deletions mockgcp/mockcompute/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// 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"
"net/http"
"strings"

"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/common"
"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/common/operations"
"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/common/projects"
"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/pkg/storage"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"google.golang.org/grpc"
"sigs.k8s.io/controller-runtime/pkg/client"

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

// MockService represents a mocked compute service.
type MockService struct {
kube client.Client
storage storage.Storage

projects projects.ProjectStore
operations *operations.Operations

networksv1 *NetworksV1
}

// New creates a MockService.
func New(env *common.MockEnvironment, storage storage.Storage) *MockService {
s := &MockService{
kube: env.GetKubeClient(),
storage: storage,
projects: env.GetProjects(),
operations: operations.NewOperationsService(storage),
}
s.networksv1 = &NetworksV1{MockService: s}
return s
}

func (s *MockService) ExpectedHost() string {
return "compute.googleapis.com"
}

func (s *MockService) Register(grpcServer *grpc.Server) {
pb.RegisterNetworksServer(grpcServer, s.networksv1)
}

func (s *MockService) NewHTTPMux(ctx context.Context, conn *grpc.ClientConn) (*runtime.ServeMux, error) {
mux := runtime.NewServeMux()

// Terraform uses the /beta/ endpoints, but we have protos only for v1.
// Also, we probably want to be implementing the newer versions
// as that makes it easier to move KCC to newer API versions.
// So far, it seems that all of beta is a direct mapping to v1 - though
// I'm sure eventually we'll find something that needs special handling.
rewriteBetaToV1 := func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
u := r.URL
u.Path = "/compute/v1/" + strings.TrimPrefix(u.Path, "/compute/beta/")
r.URL = u

mux.ServeHTTP(w, r)
}

if err := mux.HandlePath("GET", "/compute/beta/{path=**}", rewriteBetaToV1); err != nil {
return nil, err
}
if err := mux.HandlePath("POST", "/compute/beta/{path=**}", rewriteBetaToV1); err != nil {
return nil, err
}
if err := mux.HandlePath("DELETE", "/compute/beta/{path=**}", rewriteBetaToV1); err != nil {
return nil, err
}
if err := mux.HandlePath("PATCH", "/compute/beta/{path=**}", rewriteBetaToV1); err != nil {
return nil, err
}

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

return mux, nil
}

0 comments on commit 59c7576

Please sign in to comment.