Skip to content

Commit

Permalink
adding keyhandle identity
Browse files Browse the repository at this point in the history
  • Loading branch information
nb-goog committed Jan 8, 2025
1 parent 974a6ea commit 03fe745
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 143 deletions.
133 changes: 133 additions & 0 deletions apis/kms/v1beta1/keyhandle_identity.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// 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 v1beta1

import (
"context"
"fmt"
"strings"

refsv1beta1 "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1"
"sigs.k8s.io/controller-runtime/pkg/client"
)

type KMSKeyHandleIdentity struct {
id string
parent *KMSKeyHandleParent
}

func (i *KMSKeyHandleIdentity) String() string {
return i.parent.String() + "/keyHandles/" + i.id
}

func (r *KMSKeyHandleIdentity) Parent() *KMSKeyHandleParent {
return r.parent
}

type KMSKeyHandleParent struct {
ProjectID string
Location string
}

func (p *KMSKeyHandleParent) String() string {
return "projects/" + p.ProjectID + "/locations/" + p.Location
}

func asKMSKeyHandleExternal(parent *KMSKeyHandleParent, resourceID string) (external string) {
return parent.String() + "/keyHandles/" + resourceID
}

func NewKMSKeyHandleIdentity(ctx context.Context, reader client.Reader, obj *KMSKeyHandle) (*KMSKeyHandleIdentity, error) {
id := &KMSKeyHandleIdentity{}

// Get Parent
projectRef, err := refsv1beta1.ResolveProject(ctx, reader, obj.GetNamespace(), obj.Spec.ProjectRef)
if err != nil {
return nil, err
}
projectID := projectRef.ProjectID
if projectID == "" {
return nil, fmt.Errorf("cannot resolve project")
}
location := valueOf(obj.Spec.Location)
id.parent = &KMSKeyHandleParent{ProjectID: projectID, Location: location}

// Get desired ID
desiredHandleID := valueOf(obj.Spec.ResourceID)
// set the desired keyhandle id in the identity
id.id = desiredHandleID

// At this point we are expecting desiredHandleID to be either empty or valid uuid
// 1. if desiredHandleID empty:
// id.external will be projects/{{pid}}/locations/{{loc}}/keyHandles/. i.e without resourceID.
// A call will be made to find() with invalid externalID which will return false.
// 2. if desiredHandleID is a valid UUID: id.external will be valid.

// Use approved External
externalRef := valueOf(obj.Status.ExternalRef)
if externalRef != "" {
actualParent, actualHandleID, err := ParseKMSKeyHandleExternal(externalRef)
if err != nil {
return nil, err
}
// Validate desired with actual
if actualParent.ProjectID != projectID {
return nil, fmt.Errorf("spec.projectRef changed, expect %s, got %s", actualParent.ProjectID, projectID)
}
if actualParent.Location != location {
return nil, fmt.Errorf("spec.location changed, expect %s, got %s", actualParent.Location, location)
}
if desiredHandleID != "" && (actualHandleID != desiredHandleID) {
return nil, fmt.Errorf("cannot reset `spec.resourceID` to %s, since it has already assigned to %s",
desiredHandleID, actualHandleID)
}
return id, nil
}
return id, nil
}

func ParseKMSKeyHandleExternal(external string) (parent *KMSKeyHandleParent, resourceID string, err error) {
external = strings.TrimPrefix(external, "/")
tokens := strings.Split(external, "/")
if len(tokens) != 6 || tokens[0] != "projects" || tokens[2] != "locations" || tokens[4] != "keyHandles" {
return nil, "", fmt.Errorf("format of KMSKeyHandle external=%q was not known (use projects/{{projectId}}/locations/{{location}}/keyHandles/{{keyhandleID}})", external)
}
parent = &KMSKeyHandleParent{
ProjectID: tokens[1],
Location: tokens[3],
}
resourceID = tokens[5]
return parent, resourceID, nil
}

func (r *KMSKeyHandleIdentity) KeyHandleID() (string, bool, error) {
if r.id != "" {
return r.id, true, nil
}
return "", false, fmt.Errorf("KMSKeyHandleIdentity not normalized to External form or not created from `New()`")
}

func asKMSKeyHandleExternal_FromSpec(spec *KMSKeyHandleSpec) (parent *KMSKeyHandleParent, resourceID string, err error) {
external := strings.TrimPrefix(spec.ProjectRef.External, "/")
tokens := strings.Split(external, "/")
if len(tokens) != 2 || tokens[0] != "projects" {
return nil, "", fmt.Errorf("invalid projectRef found in KMSKeyHandle=%q was not known (use projects/{{projectId}})", external)
}
parent = &KMSKeyHandleParent{
ProjectID: tokens[1],
Location: valueOf(spec.Location),
}
return parent, valueOf(spec.ResourceID), nil
}
119 changes: 0 additions & 119 deletions apis/kms/v1beta1/keyhandle_reference.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ package v1beta1
import (
"context"
"fmt"
"strings"

refsv1beta1 "github.com/GoogleCloudPlatform/k8s-config-connector/apis/refs/v1beta1"
"github.com/GoogleCloudPlatform/k8s-config-connector/pkg/k8s"
Expand All @@ -41,8 +40,6 @@ type KMSKeyHandleRef struct {

// The namespace of a KMSKeyHandle resource.
Namespace string `json:"namespace,omitempty"`

parent *KMSKeyHandleParent
}

// NormalizedExternal provision the "External" value for other resource that depends on KMSKeyHandle.
Expand Down Expand Up @@ -84,119 +81,3 @@ func (r *KMSKeyHandleRef) NormalizedExternal(ctx context.Context, reader client.
r.External = actualExternalRef
return r.External, nil
}

// New builds a KMSKeyHandleRef from the Config Connector KMSKeyHandle object.
func NewKMSKeyHandleRef(ctx context.Context, reader client.Reader, obj *KMSKeyHandle) (*KMSKeyHandleRef, error) {
id := &KMSKeyHandleRef{}

// Get Parent
projectRef, err := refsv1beta1.ResolveProject(ctx, reader, obj.GetNamespace(), obj.Spec.ProjectRef)
if err != nil {
return nil, err
}
projectID := projectRef.ProjectID
if projectID == "" {
return nil, fmt.Errorf("cannot resolve project")
}
location := valueOf(obj.Spec.Location)
id.parent = &KMSKeyHandleParent{ProjectID: projectID, Location: location}

// Get desired ID
desiredHandleID := valueOf(obj.Spec.ResourceID)

// At this point we are expecting desiredHandleID to be either empty or valid uuid
// 1. if desiredHandleID empty:
// id.external will be projects/{{pid}}/locations/{{loc}}/keyHandles/. i.e without resourceID.
// A call will be made to find() with invalid externalID which will return false.
// 2. if desiredHandleID is a valid UUID: id.external will be valid.

// Use approved External
externalRef := valueOf(obj.Status.ExternalRef)
if externalRef != "" {
actualParent, actualHandleID, err := ParseKMSKeyHandleExternal(externalRef)
if err != nil {
return nil, err
}
// Validate desired with actual
if actualParent.ProjectID != projectID {
return nil, fmt.Errorf("spec.projectRef changed, expect %s, got %s", actualParent.ProjectID, projectID)
}
if actualParent.Location != location {
return nil, fmt.Errorf("spec.location changed, expect %s, got %s", actualParent.Location, location)
}
if desiredHandleID != "" && (actualHandleID != desiredHandleID) {
return nil, fmt.Errorf("cannot reset `spec.resourceID` to %s, since it has already assigned to %s",
desiredHandleID, actualHandleID)
}
id.External = externalRef
return id, nil
}
id.parent = &KMSKeyHandleParent{ProjectID: projectID, Location: location}
id.External = id.parent.String() + "/keyHandles/" + desiredHandleID
return id, nil
}

func (r *KMSKeyHandleRef) KeyHandleID() (string, bool, error) {
if r.External != "" {
_, id, err := ParseKMSKeyHandleExternal(r.External)
if err != nil {
return "", false, err
}
return id, id != "", nil
}
return "", false, fmt.Errorf("KMSKeyHandleRef not normalized to External form or not created from `New()`")
}

func (r *KMSKeyHandleRef) Parent() (*KMSKeyHandleParent, error) {
if r.parent != nil {
return r.parent, nil
}
if r.External != "" {
parent, _, err := ParseKMSKeyHandleExternal(r.External)
if err != nil {
return nil, err
}
return parent, nil
}
return nil, fmt.Errorf("KMSKeyHandleRef not initialized from `NewKMSKeyHandleRef` or `NormalizedExternal`")
}

type KMSKeyHandleParent struct {
ProjectID string
Location string
}

func (p *KMSKeyHandleParent) String() string {
return "projects/" + p.ProjectID + "/locations/" + p.Location
}

func AsKMSKeyHandleExternal(parent *KMSKeyHandleParent, resourceID string) (external string) {
return parent.String() + "/keyHandles/" + resourceID
}

func ParseKMSKeyHandleExternal(external string) (parent *KMSKeyHandleParent, resourceID string, err error) {
external = strings.TrimPrefix(external, "/")
tokens := strings.Split(external, "/")
if len(tokens) != 6 || tokens[0] != "projects" || tokens[2] != "locations" || tokens[4] != "keyHandles" {
return nil, "", fmt.Errorf("format of KMSKeyHandle external=%q was not known (use projects/{{projectId}}/locations/{{location}}/keyHandles/{{keyhandleID}})", external)
}
parent = &KMSKeyHandleParent{
ProjectID: tokens[1],
Location: tokens[3],
}
resourceID = tokens[5]
return parent, resourceID, nil
}

func AsKMSKeyHandleExternal_FromSpec(spec *KMSKeyHandleSpec) (parent *KMSKeyHandleParent, resourceID string, err error) {
external := strings.TrimPrefix(spec.ProjectRef.External, "/")
tokens := strings.Split(external, "/")
if len(tokens) != 2 || tokens[0] != "projects" {
return nil, "", fmt.Errorf("invalid projectRef found in KMSKeyHandle=%q was not known (use projects/{{projectId}})", external)
}
parent = &KMSKeyHandleParent{
ProjectID: tokens[1],
Location: valueOf(spec.Location),
}
return parent, valueOf(spec.ResourceID), nil
}
5 changes: 0 additions & 5 deletions apis/kms/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 11 additions & 17 deletions pkg/controller/direct/kms/keyhandle/keyhandle_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (m *model) AdapterForObject(ctx context.Context, reader client.Reader, u *u
return nil, fmt.Errorf("error converting to %T: %w", obj, err)
}

id, err := krm.NewKMSKeyHandleRef(ctx, reader, obj)
id, err := krm.NewKMSKeyHandleIdentity(ctx, reader, obj)
if err != nil {
return nil, err
}
Expand All @@ -95,7 +95,7 @@ func (m *model) AdapterForURL(ctx context.Context, url string) (directbase.Adapt
}

type Adapter struct {
id *krm.KMSKeyHandleRef
id *krm.KMSKeyHandleIdentity
gcpClient *gcp.AutokeyClient
desired *krm.KMSKeyHandle
actual *kmspb.KeyHandle
Expand All @@ -105,21 +105,21 @@ var _ directbase.Adapter = &Adapter{}

func (a *Adapter) Find(ctx context.Context) (bool, error) {
log := klog.FromContext(ctx).WithName(ctrlName)
log.V(2).Info("getting KeyHandle", "name", a.id.External)
log.V(2).Info("getting KeyHandle", "name", a.id.String())
_, idIsSet, err := a.id.KeyHandleID()
if err != nil {
return false, err
}
if !idIsSet {
return false, nil
}
req := &kmspb.GetKeyHandleRequest{Name: a.id.External}
req := &kmspb.GetKeyHandleRequest{Name: a.id.String()}
keyhandlepb, err := a.gcpClient.GetKeyHandle(ctx, req)
if err != nil {
if direct.IsNotFound(err) {
return false, nil
}
return false, fmt.Errorf("getting KeyHandle %q: %w", a.id.External, err)
return false, fmt.Errorf("getting KeyHandle %q: %w", a.id.String(), err)
}

a.actual = keyhandlepb
Expand All @@ -137,10 +137,7 @@ func (a *Adapter) Create(ctx context.Context, createOp *directbase.CreateOperati
return mapCtx.Err()
}

parent, err := a.id.Parent()
if err != nil {
return err
}
parent := a.id.Parent()
id, idIsSet, err := a.id.KeyHandleID()
if err != nil {
return err
Expand All @@ -155,13 +152,13 @@ func (a *Adapter) Create(ctx context.Context, createOp *directbase.CreateOperati
}
op, err := a.gcpClient.CreateKeyHandle(ctx, req)
if err != nil {
return fmt.Errorf("creating KeyHandle %s: %w", a.id.External, err)
return fmt.Errorf("creating KeyHandle %s: %w", a.id.String(), err)
}
created, err := op.Wait(ctx)
if err != nil {
return fmt.Errorf("KeyHandle %s waiting creation: %w", a.id.External, err)
return fmt.Errorf("KeyHandle %s waiting creation: %w", a.id.String(), err)
}
log.V(2).Info("successfully created KeyHandle", "name", a.id.External)
log.V(2).Info("successfully created KeyHandle", "name", a.id.String())

status := &krm.KMSKeyHandleStatus{}
status.ObservedState = KMSKeyHandleStatusObservedState_FromProto(mapCtx, created)
Expand Down Expand Up @@ -190,10 +187,7 @@ func (a *Adapter) Export(ctx context.Context) (*unstructured.Unstructured, error
if mapCtx.Err() != nil {
return nil, mapCtx.Err()
}
parent, err := a.id.Parent()
if err != nil {
return nil, err
}
parent := a.id.Parent()
obj.Spec.ProjectRef = &refs.ProjectRef{Name: parent.ProjectID}
obj.Spec.Location = direct.LazyPtr(parent.Location)
uObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
Expand All @@ -208,6 +202,6 @@ func (a *Adapter) Export(ctx context.Context) (*unstructured.Unstructured, error
// Delete operation not supported for KeyHandle, so this operation is a no-op.
func (a *Adapter) Delete(ctx context.Context, deleteOp *directbase.DeleteOperation) (bool, error) {
log := klog.FromContext(ctx).WithName(ctrlName)
log.V(2).Error(fmt.Errorf("delete operation not supported on KeyHandle, name: %s", a.id.External), "delete operation on KeyHandle resource not supported,")
log.V(2).Error(fmt.Errorf("delete operation not supported on KeyHandle, name: %s", a.id.String()), "delete operation on KeyHandle resource not supported,")
return false, nil
}
4 changes: 2 additions & 2 deletions pkg/controller/direct/kms/keyhandle/mapper.generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 03fe745

Please sign in to comment.