Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: A new parent interface #3234

Merged
merged 2 commits into from
Nov 27, 2024

Conversation

yuwenma
Copy link
Collaborator

@yuwenma yuwenma commented Nov 21, 2024

Change description

Fixes #

Tests you have done

  • Run make ready-pr to ensure this PR is ready for review.
  • Perform necessary E2E testing for changed resources.


var _ Parent = &ProjectAndLocationParent{}

type ProjectAndLocationParent struct {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can merge ProjectAndLocationParent and ProjectAndLocationRef, it makes this file shorter. But it adds additional complexity to the controller on error handling, and the order of the function calls to set the external.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want Foo, FooRef and FooIdentity. I think this is FooIdentity, and ProjectAndLocationRef is FooRef

}
if location != actualLocation {
return nil, fmt.Errorf("spec.location changed, expect %s, got %s", location, actualLocation)
if err := desiredParent.MatchActual(actualParent); err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest just comparing String() of the ID

Copy link
Collaborator Author

@yuwenma yuwenma Nov 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This MatchActual is more for KCC users.
My main concern about comparing String is that it cannot give users enough information to help them fix their bad config.
For example, the parent is build from spec.ProjectRef.External and spec.Location (for some other resources it may also have spec. FooRef.External spec.BarRef.External), it is unclear for users to know which field is misconfigured by just seeing something likeexpected format projects/<projectID>/location/<lcoation>, got ....

One improvements we can do is spec.parent.projectRef (I think we discussed this as well, but we have to make the API backward compatible)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well I think I would make the message "Can't change object identity; existing identity is %q, new identity is %q" so it would be pretty easy to figure out what has changed, but ... I can send a PR on top of this - let's get this in!

@@ -88,36 +87,13 @@ type BigQueryConnectionConnectionRef struct {
Namespace string `json:"namespace,omitempty"`
}

func parseExternal(external string) (string, string, string, error) {
func ParseConnectionExternal(external string) (*parent.ProjectAndLocationParent, string, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest this should return (*ConnectionIdentity,error) (and not the parent split out)

Copy link
Collaborator Author

@yuwenma yuwenma Nov 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! (It used to be handy to split them because we don't have the identity. 😄 )

// BigQueryConnectionConnectionSpec defines the desired state to connect BigQuery to external resources
// +kcc:proto=google.cloud.bigquery.connection.v1.Connection
type BigQueryConnectionConnectionSpec struct {
Parent `json:",inline"`
*parent.ProjectAndLocationRef `json:",inline"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought you wanted to embed ConnectionIdentity here instead

// ParentBuilder builds a Parent object from a ParentRef.
// - ParentRef is the Config Connector API reference for identifying a resource's logical parent.
// - The Parent object provides helper functions for parent-related logic in direct reconciliation.
type ParentBuilder interface {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the krm object should implement ResolveIdentity(ctx context.Context, reader client.Reader) (Identity, error)

If we want Identity to be typed, it would have to be something like this:

type ObjectWithIdentity[T] interface {
  ResolveIdentity(ctx contetx.Context, reader client.Reader) (*T, error)
}

// Builds a the ProjectParent from ProjectRef.
// If `projectRef.external` is given, parse projectID from External, otherwise find the ConfigConnector project
// according to `projectRef.name` and `projectRef.namespace`.
func (p *ProjectRef) Build(ctx context.Context, reader client.Reader, othernamespace string, parent Parent) error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect we don't need this function. We need a function that resolves a ProjectRef to a ProjectIdentity, and ProjectIdentity should have a function that can return the ProjectID. It might be rename this function to map from ProjectRef -> ProjectIdentity.

We would have 3 concepts: Foo, FooRef and FooIdentity

FooIdentity is essentially the fully-qualified name, I don't know if we should name it FooLink or something like that instead, but I do think that just using these 3 concepts will simplify our code a lot.

@@ -293,10 +289,6 @@ spec:
description: The `projectID` field of a project, when not managed
by Config Connector.
type: string
kind:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sadly we can't do this, this is a breaking API change. We can't break resources that specify kind, even though we (I) introduced it by mistake.

@@ -76,7 +75,7 @@ func (m *model) AdapterForObject(ctx context.Context, reader client.Reader, u *u
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &obj); err != nil {
return nil, fmt.Errorf("error converting to %T: %w", obj, err)
}
connectionRef, err := krm.NewBigQueryConnectionConnectionRef(ctx, reader, obj)
connectionIdentity, err := krm.NewConnectionIdentity(ctx, reader, obj)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the one where maybe we could call obj.ResolveIdentity ... maybe :-)

req := &pb.CreateConnectionRequest{
Parent: parent,
Parent: a.id.Parent.String(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this approach, but one thing we will have to be careful of is that we have a "partial ID" here, ResourceID is empty, the parent fields are set. There are going to be bugs because of this.

It might be easier to resolve the parent directly here, perhaps with a method on the embedded ProjectAndLocationID in BigQueryConnectionID

@@ -243,14 +223,14 @@ func (a *Adapter) Create(ctx context.Context, createOp *directbase.CreateOperati
return mapCtx.Err()
}

tokens := strings.Split(created.Name, "/")
parent, err = a.id.Parent()
_, name, err := krm.ParseConnectionExternal(created.Name)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at this, I think it would read well if it was a method on BigQueryConnectionID, like this:

name := &BigQueryConnectionID{}
if err := name.ParseSelfLink(created.Name); err != nil {
....

}
obj.Spec.ResourceID = &id
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we embed ID into Spec, I think these three lines will just be obj.Spec.ID = id

}
if location != actualLocation {
return nil, fmt.Errorf("spec.location changed, expect %s, got %s", location, actualLocation)
if err := desiredParent.MatchActual(actualParent); err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well I think I would make the message "Can't change object identity; existing identity is %q, new identity is %q" so it would be pretty easy to figure out what has changed, but ... I can send a PR on top of this - let's get this in!

return nil, fmt.Errorf("cannot resolve project")
}
// Get location
location := obj.Spec.Location

// Get desired connection ID from spec
desiredID := direct.ValueOf(obj.Spec.ResourceID)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to fallback to name here?

@justinsb
Copy link
Collaborator

So I think two blockers, and everything else we can tackle in follow-up PRs:

  • I think we need a fallback to name?
  • I don't think you can remove the kind field

@yuwenma
Copy link
Collaborator Author

yuwenma commented Nov 25, 2024

So I think two blockers, and everything else we can tackle in follow-up PRs:

  • I think we need a fallback to name?
  • I don't think you can remove the kind field

I reverted the changes to BQCC. So this PR only contains the interface and the implemented ProjectParent and ProjectAndLocationParent

@google-oss-prow google-oss-prow bot added the lgtm label Nov 26, 2024
@justinsb
Copy link
Collaborator

/approve
/lgtm

@google-oss-prow google-oss-prow bot added the lgtm label Nov 27, 2024
@google-oss-prow google-oss-prow bot added the lgtm label Nov 27, 2024
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: justinsb

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@google-oss-prow google-oss-prow bot merged commit d192546 into GoogleCloudPlatform:master Nov 27, 2024
18 checks passed
@yuwenma yuwenma added this to the 1.126 milestone Dec 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants