Skip to content

Commit

Permalink
mockgcp: Implement TagsTagValue
Browse files Browse the repository at this point in the history
  • Loading branch information
justinsb committed Feb 7, 2024
1 parent cafb6d8 commit eba170a
Show file tree
Hide file tree
Showing 7 changed files with 690 additions and 0 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 @@ -444,6 +444,7 @@ func MaybeSkip(t *testing.T, name string, resources []*unstructured.Unstructured
case schema.GroupKind{Group: "serviceusage.cnrm.cloud.google.com", Kind: "ServiceIdentity"}:

case schema.GroupKind{Group: "tags.cnrm.cloud.google.com", Kind: "TagsTagKey"}:
case schema.GroupKind{Group: "tags.cnrm.cloud.google.com", Kind: "TagsTagValue"}:

default:
t.Skipf("gk %v not suppported by mock gcp; skipping", gvk.GroupKind())
Expand Down
2 changes: 2 additions & 0 deletions mockgcp/mockresourcemanager/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,15 @@ func (s *MockService) Register(grpcServer *grpc.Server) {
pb_v1.RegisterProjectsServer(grpcServer, s.projectsV1)
pb_v3.RegisterProjectsServer(grpcServer, s.projectsV3)
pb_v3.RegisterTagKeysServer(grpcServer, &TagKeys{MockService: s})
pb_v3.RegisterTagValuesServer(grpcServer, &TagValues{MockService: s})
}

func (s *MockService) NewHTTPMux(ctx context.Context, conn *grpc.ClientConn) (*runtime.ServeMux, error) {
mux, err := httpmux.NewServeMux(ctx, conn,
pb_v1.RegisterProjectsHandler,
pb_v3.RegisterProjectsHandler,
pb_v3.RegisterTagKeysHandler,
pb_v3.RegisterTagValuesHandler,
s.operations.RegisterOperationsHandler("v3"))
if err != nil {
return nil, err
Expand Down
222 changes: 222 additions & 0 deletions mockgcp/mockresourcemanager/tagvalues.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
// 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 mockresourcemanager

import (
"context"
"encoding/base64"
"fmt"
"strconv"
"strings"
"time"

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

"github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/common/projects"
pb "github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/generated/mockgcp/cloud/resourcemanager/v3"
longrunningpb "google.golang.org/genproto/googleapis/longrunning"
)

type TagValues struct {
*MockService
pb.UnimplementedTagValuesServer
}

func (s *TagValues) GetTagValue(ctx context.Context, req *pb.GetTagValueRequest) (*pb.TagValue, error) {
name, err := s.parseTagValueName(req.Name)
if err != nil {
return nil, err
}

fqn := name.String()

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

// We should verify that this is part of on of our projects, but ... it's a mock

return obj, nil
}

func (s *TagValues) CreateTagValue(ctx context.Context, req *pb.CreateTagValueRequest) (*longrunningpb.Operation, error) {
parentName, err := s.parseTagKeyName(req.GetTagValue().GetParent())
if err != nil {
return nil, err
}

parentTagKey := &pb.TagKey{}
if err := s.storage.Get(ctx, parentName.String(), parentTagKey); err != nil {
if apierrors.IsNotFound(err) {
return nil, status.Errorf(codes.NotFound, "tagKey %q not found", req.GetTagValue().GetParent())
} else {
return nil, status.Errorf(codes.Internal, "error reading tagKey: %v", err)
}
}

namespacedName := ""

// We should verify that this is part of on of our projects, but ... it's a mock
tagKeyParent := parentTagKey.GetParent()
if strings.HasPrefix(tagKeyParent, "projects/") {
projectName, err := projects.ParseProjectName(tagKeyParent)
if err != nil {
return nil, err
}
project, err := s.projectsInternal.GetProject(projectName)
if err != nil {
return nil, err
}
namespacedName = project.ID + "/" + parentTagKey.GetShortName() + "/" + req.GetTagValue().GetShortName()
} else {
return nil, status.Errorf(codes.InvalidArgument, "parent %q is not valid", tagKeyParent)
}

if req.ValidateOnly {
return nil, fmt.Errorf("ValidateOnly not yet implemented")
}

name := &tagValueName{
ID: time.Now().UnixNano(),
}

fqn := name.String()
now := timestamppb.Now()

obj := proto.Clone(req.TagValue).(*pb.TagValue)

obj.CreateTime = now
obj.UpdateTime = now
obj.NamespacedName = namespacedName
obj.Etag = base64.StdEncoding.EncodeToString(computeEtag(obj))
obj.Name = fqn
obj.Parent = parentTagKey.Name

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

metadata := &pb.CreateTagValueMetadata{}
return s.operations.StartLRO(ctx, metadata, func() (proto.Message, error) {
return obj, nil
})
}

func (s *TagValues) UpdateTagValue(ctx context.Context, req *pb.UpdateTagValueRequest) (*longrunningpb.Operation, error) {
reqName := req.GetTagValue().GetName()
name, err := s.parseTagValueName(reqName)
if err != nil {
return nil, err
}

if req.ValidateOnly {
return nil, fmt.Errorf("ValidateOnly not yet implemented")
}

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

// We should verify that this is part of on of our projects, but ... it's a mock

paths := req.GetUpdateMask().GetPaths()
for _, path := range paths {
switch path {
case "description":
obj.Description = req.GetTagValue().GetDescription()
default:
return nil, status.Errorf(codes.InvalidArgument, "update_mask path %q not valid", path)
}
}
if len(paths) == 0 {
obj.Description = req.GetTagValue().GetDescription()
}

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

metadata := &pb.UpdateTagValueMetadata{}
return s.operations.StartLRO(ctx, metadata, func() (proto.Message, error) {
return obj, nil
})
}

func (s *TagValues) DeleteTagValue(ctx context.Context, req *pb.DeleteTagValueRequest) (*longrunningpb.Operation, error) {
name, err := s.parseTagValueName(req.Name)
if err != nil {
return nil, err
}

fqn := name.String()

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

// We should verify that this is part of on of our projects, but ... it's a mock

metadata := &pb.DeleteTagValueMetadata{}
return s.operations.StartLRO(ctx, metadata, func() (proto.Message, error) {
return deleted, nil
})
}

type tagValueName struct {
ID int64
}

func (n *tagValueName) String() string {
return fmt.Sprintf("tagValues/%d", n.ID)
}

// parseTagValueName parses a string into a tagValueName.
// The expected form is tagValues/<tagvalueName>
func (s *MockService) parseTagValueName(name string) (*tagValueName, error) {
tokens := strings.Split(name, "/")

if len(tokens) == 2 && tokens[0] == "tagValues" {
n, err := strconv.ParseInt(tokens[1], 10, 64)
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, "name %q is not valid (bad id)", name)
}
name := &tagValueName{
ID: n,
}

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

0 comments on commit eba170a

Please sign in to comment.