Skip to content

RSDK-6839 - Add machine part id to resource names - #3757

Merged
Cheuk (cheukt) merged 19 commits into
viamrobotics:mainfrom
cheukt:add-part-id
Apr 2, 2024
Merged

Cheuk (cheukt) merged 19 commits into
viamrobotics:mainfrom
cheukt:add-part-id

Conversation

@cheukt

@cheukt Cheuk (cheukt) commented Mar 29, 2024 •

Copy link
Copy Markdown
Member

API change: viamrobotics/api#467

tried to limit scope of changes, in particular I didn't want to break the resource name's String() and FromString() method. Ultimately this means that the part id doesn't get serialized when converting to string/printed in logs, but don't think that's the worst. The alternative would mean we have to figure out a way to display the part id as part of the resource name in a string sanely, and I'm not sure that's possible.

So part IDs will be returned in clients, but all comparisons will be done without part IDs. I thought about adding a check to at least make sure that part ID == client's part ID, but didn't think it necessary since part ID is the same for all resources from a robot. Circumventing that doesn't seem to give any benefit but making the check strict may break code that works on multiple robots (a fleet that uses the same fragment, grab resource names from one robot and then trying to act on the whole fleet with those names). Let me know if you think the check would be valuable

@viambot viambot added the safe to test This pull request is marked safe to test from a trusted zone label Mar 29, 2024
Comment thread go.mod Outdated
golang.org/x/exp v0.0.0-20230725012225-302865e7556b
)

replace go.viam.com/api => ../api-cheuk

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

will update once API changes are approved

Comment thread testutils/inject/robot.go
defer r.Mu.RUnlock()
if r.CloudMetadataFunc == nil {
return r.CloudMetadata(ctx)
return r.LocalRobot.CloudMetadata(ctx)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

flyby from an earlier version of changes that used inject.CloudMetadata a lot more

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

thanks!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I made a comment in the API pr to ask why we're not also including the machine ID, but your reasoning about comparisons/stringifications sans part ID seem fine to me.

Comment thread protoutils/messages.go
return resource.NewNameWithPartID(
resource.APINamespace(name.Namespace).WithType(name.Type).WithSubtype(name.Subtype),
name.Name,
name.Name, name.GetMachinePartId(),

@maximpertsov Maxim Pertsov (maximpertsov) Apr 1, 2024 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[q] will this be a *string since this field is optional?

i guess i'll have my answer once we merge the API change...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ah wait, you have the API changes locally - interesting, so unset optional string fields are still just blank strings?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

it's a string

func (x *ResourceName) GetMachinePartId() string {
	if x != nil && x.MachinePartId != nil {
		return *x.MachinePartId
	}
	return ""
}

Comment thread resource/name.go Outdated
Comment on lines +131 to +142
// AddPartID returns a new name with part ID added. This will replace an existing part ID.
func (n Name) AddPartID(partID string) Name {
tempName := NewName(n.API, n.ShortName())
tempName.MachinePartID = partID
return tempName
}

// RemovePartID returns a new name with part ID removed.
func (n Name) RemovePartID() Name {
return NewName(n.API, n.ShortName())
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[q] do we have places were we might want to lookup or compare using either a name with OR without the part id?

If not, I think it would be helpful to have two different structs - one with the machine id and one without - e.g. Name, which includes the machine id, and NameWithoutMachineID, which does not (having the reverse is fine too - can probably be private/internal). This will prevent use from accidentally use a With/Without comparison when we meant to use the other one - it's also pretty easy to convert to the version we need.


[minor] Somewhat unrelated from my above comment - I slightly prefer we name these functions With/Without instead of Add/Remove, just to clarify that nothing is being mutated. We do document that behavior so no big deal if you prefer to leave as is.

Suggested change
// AddPartID returns a new name with part ID added. This will replace an existing part ID.
func (n Name) AddPartID(partID string) Name {
tempName := NewName(n.API, n.ShortName())
tempName.MachinePartID = partID
return tempName
}
// RemovePartID returns a new name with part ID removed.
func (n Name) RemovePartID() Name {
return NewName(n.API, n.ShortName())
}
// WithPartID returns a new name with part ID added. This will replace an existing part ID.
func (n Name) WithPartID(partID string) Name {
tempName := NewName(n.API, n.ShortName())
tempName.MachinePartID = partID
return tempName
}
// WithoutPartID returns a new name with part ID removed.
func (n Name) WithoutPartID() Name {
return NewName(n.API, n.ShortName())
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I'll play around with an internal struct without machine id for usage around comparisons

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

playing around with it, I don't think we should make a separate struct.

A user should never use the struct without machine id, so this would be purely internal usage for internal structures that are tracking resources. If so, I would want to make it private, but that creates an import cycle if I want to embed NameWithoutMachineID back in Name. We could have resource.Name not embed NameWithoutMachineID, but that makes everything harder to maintain. Ultimately we just have to be vigilant around adding tests.

@maximpertsov Maxim Pertsov (maximpertsov) Apr 2, 2024 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

what about adding a "public" struct in the resource package but putting it in an internal folder so it can't be imported by external clients of rdk?

e.g.

// internal/resource/resource.go

package resource

type NameWithoutMachineID struct {
// ...
}

With this setup, we can import resource.NameWithoutMachineID wherever we need to, but external users won't have access to the this struct.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I guess this still has the downside of requiring a separate import e.g. go.viam.com/rdk/internal/resource. Anyway, I'm not gonna block on this - my general view is that we should use the static type system to make our lives easier whenever possible - so long as it's reasonable to achieve.

Thanks for exploring this.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

my general view is that we should use the static type system to make our lives easier whenever possible - so long as it's reasonable to achieve.

ya, I agree with that, I do feel like this one is a bit difficult to achieve and ends up complicating the types a bit too much

Co-authored-by: Maxim Pertsov <maxim.pertsov@gmail.com>
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Apr 1, 2024
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Apr 1, 2024
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Apr 1, 2024
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Apr 2, 2024
@cheukt
Cheuk (cheukt) merged commit e549123 into viamrobotics:main Apr 2, 2024
@cheukt
Cheuk (cheukt) deleted the add-part-id branch April 2, 2024 19:37
Cheuk (cheukt) added a commit to cheukt/rdk that referenced this pull request Apr 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

safe to test This pull request is marked safe to test from a trusted zone

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants