diff --git a/config/tests/samples/create/harness.go b/config/tests/samples/create/harness.go index 231d0ea79a..afd4133853 100644 --- a/config/tests/samples/create/harness.go +++ b/config/tests/samples/create/harness.go @@ -438,9 +438,10 @@ func MaybeSkip(t *testing.T, name string, resources []*unstructured.Unstructured } switch gvk.GroupKind() { + case schema.GroupKind{Group: "accesscontextmanager.cnrm.cloud.google.com", Kind: "AccessContextManagerAccessPolicy"}: case schema.GroupKind{Group: "accesscontextmanager.cnrm.cloud.google.com", Kind: "AccessContextManagerAccessLevel"}: case schema.GroupKind{Group: "accesscontextmanager.cnrm.cloud.google.com", Kind: "AccessContextManagerServicePerimeter"}: - //case schema.GroupKind{Group: "accesscontextmanager.cnrm.cloud.google.com", Kind: "AccessContextManagerServicePerimeterResource"}: + case schema.GroupKind{Group: "accesscontextmanager.cnrm.cloud.google.com", Kind: "AccessContextManagerServicePerimeterResource"}: case schema.GroupKind{Group: "apikeys.cnrm.cloud.google.com", Kind: "APIKeysKey"}: diff --git a/mockgcp/mockaccesscontextmanager/accesslevel.go b/mockgcp/mockaccesscontextmanager/accesslevel.go index e322195ff6..2cf6973975 100644 --- a/mockgcp/mockaccesscontextmanager/accesslevel.go +++ b/mockgcp/mockaccesscontextmanager/accesslevel.go @@ -11,6 +11,7 @@ // 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 mockaccesscontextmanager import ( diff --git a/mockgcp/mockaccesscontextmanager/accesspolicy.go b/mockgcp/mockaccesscontextmanager/accesspolicy.go new file mode 100644 index 0000000000..de594bf81a --- /dev/null +++ b/mockgcp/mockaccesscontextmanager/accesspolicy.go @@ -0,0 +1,113 @@ +// 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 mockaccesscontextmanager + +import ( + "context" + "crypto/md5" + "fmt" + "time" + + pb "github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/generated/mockgcp/identity/accesscontextmanager/v1" + "google.golang.org/genproto/googleapis/longrunning" + "google.golang.org/protobuf/proto" + "k8s.io/klog/v2" +) + +func (s *AccessContextManagerV1) GetAccessPolicy(ctx context.Context, req *pb.GetAccessPolicyRequest) (*pb.AccessPolicy, error) { + name, err := s.parseAccessPolicyName(req.Name) + if err != nil { + return nil, err + } + + fqn := name.String() + + obj := &pb.AccessPolicy{} + if err := s.storage.Get(ctx, fqn, obj); err != nil { + return nil, err + } + + return obj, nil +} + +func (s *AccessContextManagerV1) CreateAccessPolicy(ctx context.Context, req *pb.AccessPolicy) (*longrunning.Operation, error) { + accessPolicy := req.Name + if accessPolicy == "" { + accessPolicy = fmt.Sprintf("%d", time.Now().UnixNano()) + } + reqName := "accessPolicies/" + accessPolicy + name, err := s.parseAccessPolicyName(reqName) + if err != nil { + return nil, err + } + + fqn := name.String() + + obj := proto.Clone(req).(*pb.AccessPolicy) + obj.Name = fqn + + if err := s.storage.Create(ctx, fqn, obj); err != nil { + return nil, err + } + + return s.operations.NewLRO(ctx) +} + +func (s *AccessContextManagerV1) UpdateAccessPolicy(ctx context.Context, req *pb.UpdateAccessPolicyRequest) (*longrunning.Operation, error) { + reqName := req.GetPolicy().GetName() + + name, err := s.parseAccessPolicyName(reqName) + if err != nil { + return nil, err + } + + fqn := name.String() + obj := &pb.AccessPolicy{} + if err := s.storage.Get(ctx, fqn, obj); err != nil { + return nil, err + } + + if err := s.storage.Update(ctx, fqn, obj); err != nil { + return nil, err + } + + return s.operations.NewLRO(ctx) +} + +func (s *AccessContextManagerV1) DeleteAccessPolicy(ctx context.Context, req *pb.DeleteAccessPolicyRequest) (*longrunning.Operation, error) { + name, err := s.parseAccessPolicyName(req.Name) + if err != nil { + return nil, err + } + + fqn := name.String() + + oldObj := &pb.AccessPolicy{} + if err := s.storage.Delete(ctx, fqn, oldObj); err != nil { + return nil, err + } + + return s.operations.NewLRO(ctx) +} + +func computeEtag(obj proto.Message) []byte { + // TODO: Do we risk exposing internal fields? Doesn't matter on a mock, I guess + b, err := proto.Marshal(obj) + if err != nil { + klog.Fatalf("failed to marshal proto object: %v", err) + } + hash := md5.Sum(b) + return hash[:] +} diff --git a/mockgcp/mockaccesscontextmanager/names.go b/mockgcp/mockaccesscontextmanager/names.go index 105a16295e..52a996abae 100644 --- a/mockgcp/mockaccesscontextmanager/names.go +++ b/mockgcp/mockaccesscontextmanager/names.go @@ -17,23 +17,28 @@ package mockaccesscontextmanager import ( "strings" - "github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp/common/projects" - "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) +type accessPolicyName struct { + AccessPolicyName string +} + type accessLevelName struct { AccessPolicyName string AccessLevelName string } type servicePerimeterName struct { - Project *projects.ProjectData AccessPolicyName string ServicePerimeterName string } +func (n *accessPolicyName) String() string { + return "accessPolicies/" + n.AccessPolicyName +} + func (n *accessLevelName) String() string { return "accessPolicies/" + n.AccessPolicyName + "/accessLevels/" + n.AccessLevelName } @@ -42,6 +47,21 @@ func (n *servicePerimeterName) String() string { return "accessPolicies/" + n.AccessPolicyName + "/servicePerimeters/" + n.ServicePerimeterName } +// parseAccessLevelName parses a string into a accessLevelName. +// The expected form is accessPolicies//accessLevels/ +func (s *MockService) parseAccessPolicyName(name string) (*accessPolicyName, error) { + tokens := strings.Split(name, "/") + + if len(tokens) == 2 && tokens[0] == "accessPolicies" { + name := &accessPolicyName{ + AccessPolicyName: tokens[1], + } + return name, nil + } else { + return nil, status.Errorf(codes.InvalidArgument, "name %q is not valid", name) + } +} + // parseAccessLevelName parses a string into a accessLevelName. // The expected form is accessPolicies//accessLevels/ func (s *MockService) parseAccessLevelName(name string) (*accessLevelName, error) { @@ -58,8 +78,8 @@ func (s *MockService) parseAccessLevelName(name string) (*accessLevelName, error } } -// parseAccessLevelName parses a string into a accessLevelName. -// The expected form is accessPolicies//accessLevels/ +// parseServicePerimeterName parses a string into a ServicePerimeterName. +// The expected form is accessPolicies//servicePerimeters/ func (s *MockService) parseServicePerimeterName(name string) (*servicePerimeterName, error) { tokens := strings.Split(name, "/") diff --git a/mockgcp/mockaccesscontextmanager/service.go b/mockgcp/mockaccesscontextmanager/service.go index c0a8b98ed2..0202fd434d 100644 --- a/mockgcp/mockaccesscontextmanager/service.go +++ b/mockgcp/mockaccesscontextmanager/service.go @@ -11,6 +11,7 @@ // 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 mockaccesscontextmanager import ( diff --git a/mockgcp/mockaccesscontextmanager/serviceperimeter.go b/mockgcp/mockaccesscontextmanager/serviceperimeter.go index c6830404f3..7429247ae7 100644 --- a/mockgcp/mockaccesscontextmanager/serviceperimeter.go +++ b/mockgcp/mockaccesscontextmanager/serviceperimeter.go @@ -11,6 +11,7 @@ // 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 mockaccesscontextmanager import ( @@ -39,7 +40,7 @@ func (s *AccessContextManagerV1) GetServicePerimeter(ctx context.Context, req *p } func (s *AccessContextManagerV1) CreateServicePerimeter(ctx context.Context, req *pb.CreateServicePerimeterRequest) (*longrunning.Operation, error) { - reqName := req.GetServicePerimeter().Name + reqName := req.GetServicePerimeter().GetName() name, err := s.parseServicePerimeterName(reqName) if err != nil { return nil, err diff --git a/pkg/test/resourcefixture/testdata/basic/accesscontextmanager/v1beta1/accesscontextmanagerserviceperimeterresource/create.yaml b/pkg/test/resourcefixture/testdata/basic/accesscontextmanager/v1beta1/accesscontextmanagerserviceperimeterresource/create.yaml index 1c247be379..7b75694fad 100644 --- a/pkg/test/resourcefixture/testdata/basic/accesscontextmanager/v1beta1/accesscontextmanagerserviceperimeterresource/create.yaml +++ b/pkg/test/resourcefixture/testdata/basic/accesscontextmanager/v1beta1/accesscontextmanagerserviceperimeterresource/create.yaml @@ -21,7 +21,4 @@ spec: #TODO: support recursive reference https://github.com/GoogleCloudPlatform/k8s-config-connector/issues/1309 external: "accessPolicies/578359180191/servicePerimeters/restrict_all_test" resourceRef: - # Referencing a resource name leads to recursive reference and KCC does not support the feature for now. - # Please use external reference instead. - external: "accessPolicies/578359180191/servicePerimeters/restrict_all_test" name: project-${uniqueId}