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

DNM: fix: Clients now correctly quote/unquote ref parts #2145

Closed
wants to merge 34 commits into from

Conversation

tssweeney
Copy link
Collaborator

@tssweeney tssweeney commented Aug 15, 2024

Summary: This PR hopefully 🤞 solves >90% of our remaining Ref/Naming issues - meaning users should be able to freely create entities/projects (as long as Gorilla accepts them) and name their objects/ops anything they want (baring a % symbol). Under the hood, I close a few remaining constraints / parsing issues on the server side and mostly modify the UI and Python clients to correctly read/write quoted refs.


High Level Improvements:

  1. Python Client will always produce valid refs (ie. it correctly quotes when needed)
    • Benefit to user: no more crashed python scripts when bad chars are in the name
    • Note: the one case of % in the name is still illegal. We sanitize on the python client for this specific case.
  2. Python Client is able to consume quoted refs (ie. it correctly unquotes when needed)
    • Benefit to user: able to correctly walk ref data edges with bad chars in the name
  3. UI Client will always produce valid refs (ie. it correctly encodes when needed)
    • Benefit to user: able to link safely when dealing with bad characters
  4. UI Client is able to consume encoded refs (ie. it correctly decodes when needed)
    • Benefit to user: able to correctly display links with bad characters

Smaller Details:

  • Adds correct quoting of URLs to printing URLs
  • Adds server-side entity/project de-quoting

Companion PR: https://github.com/wandb/core/pull/23333

  • Correctly routes when parts are encoded
  • Correctly de-quoting encoded refs on project lookup

This PR finishes up the Ref work to ensure we support any ref the user throws at us.

Context:
In a recent PR: #2077, I implemented a more robust parser and checker to ensure Refs and Names conform to expectations. This stopped the "bleeding" where we were storing invalid Refs.

However, our two clients Python and UI had not been updated to reflect the new rules. In fact, the Python client was still creating bad refs, and the UI was overly restrictive with what it thought a ref could be. So double edge sword. As a result, users still hit crashes (either python write time or ui read time).

Moreover, our Ref parsing logic is a mess (especially in the UI) as it is spread across many locations & riddled with legacy junk.

A little terminology note: in PY, URL escaping is called quoting and in TS it is encoding. When used in this PR, they follow the language-specific convention, but logically mean the same concept.

Understanding Refs:
See https://www.notion.so/wandbai/WIP-Robust-Refs-550bf06f4a244a1ab4fdd26133952e2c for more details.

The important part to understand is the ref format:

Calls:
    weave:///<entity>/<project>/call/<callid>[extra]
Ops:
    weave:///<entity>/<project>/op/<name>:<digest>[extra]
Objects:
    weave:///<entity>/<project>/object/<name>:<digest>[extra]
Tables:
    weave:///<entity>/<project>/table/<digest>[extra]
Where:
[extra] is an optional set of slash-seperated tuples of edges. for example:
    /key/key_name_1/index/0
They always follow the pattern of [edgeName/edgeValue], where 
`edgeName` is a shortlist of 4 specific edges names.

Furthermore, entity, project, name and the edgeValues are user-controlled.

The main constraints we need to parse these refs are:

  1. entity cannot contain slashes /
  2. project cannot contain slashes /
  3. name cannot contain slashes / or colons :
  4. edgeValue cannot contain slashes /

Therefore, we need to basically URL encode/decode each of these parts in all the places that they are used

  • This PR basically updates the parsing and construction logic to properly encode/decode entity, project, nameandedgeValue`.
  • This adds server-side decoding of entity/project per this more rigorous spec

Deployment
The ideal order would be:

  1. The companion PR (land and deploy): https://github.com/wandb/core/pull/23333
  2. Secondly, split out the trace server changes from this PR (land and deploy)
  3. Then we can land & deploy/release UI and python changes in any order
  4. Make sure prod is good for few days
  5. Rollout to dedicated

@circle-job-mirror
Copy link

circle-job-mirror bot commented Aug 15, 2024

@@ -29,7 +29,7 @@ class InvalidInternalRef(ValueError):
pass


def quote_select(s: str, quote_chars: tuple[str, ...] = ("/", ":", "%")) -> str:
def quote_select(s: str, quote_chars: tuple[str, ...] = ("%", "/", ":")) -> str:
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is a legit bug fix!

@@ -7,7 +7,7 @@ export const flattenObjectPreservingWeaveTypes = (obj: {
[key: string]: any;
}) => {
return flattenObject(obj, '', {}, (key, value) => {
return typeof value !== 'object' || value == null || value._type == null;
return typeof value !== 'object' || value == null || value._type !== 'CustomWeaveType';
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is an unrelated fix I stumbled upon while testing

@@ -7,7 +7,11 @@ export const flattenObjectPreservingWeaveTypes = (obj: {
[key: string]: any;
}) => {
return flattenObject(obj, '', {}, (key, value) => {
return typeof value !== 'object' || value == null || value._type == null;
return (
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is a little followup fix from the Image PR i just put in - it is unrelated to refs

@@ -48,8 +49,13 @@ export const refUriToOpVersionKey = (refUri: RefUri): OpVersionKey => {
};

export const opVersionKeyToRefUri = (key: OpVersionKey): RefUri => {
return `${WEAVE_REF_PREFIX}${key.entity}/${key.project}/op/${key.opId}:${key.versionHash}`;
// return `${WANDB_ARTIFACT_REF_PREFIX}${key.entity}/${key.project}/${key.opId}:${key.versionHash}/obj`;
return makeRefObject(
Copy link
Collaborator Author

@tssweeney tssweeney Aug 16, 2024

Choose a reason for hiding this comment

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

We have too much ref construction / parsing logic floating around, centralizing to one location.

@@ -0,0 +1,56 @@
import {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

much of this file already existed - moved from weave-js/util/refs.ts to here as this is scoped to "New Weave" (aka Browse3)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Moved to weave-js/src/components/PagePanelComponents/Home/Browse3/refs.ts

wandb._wandb_module = "weave"
weave_messages = wandb.sdk.internal.update.check_available(weave.__version__)
wandb._wandb_module = orig_module
weave_messages = None
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is unrelated to Refs... for some reason mypy was complaining about this.

@@ -1,4 +1,5 @@
import dataclasses
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Major changes in this file are to ensure that we quote the user-controlled fields, similar to refs_internal

@tssweeney tssweeney marked this pull request as draft August 16, 2024 03:35
@@ -0,0 +1,19 @@
import {ErrorPanel} from '@wandb/weave/components/ErrorPanel';
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I noticed while testing that it is pretty lame when a not found error crashes the whole page. This is a common component that can be used to render not found data

@@ -92,8 +92,13 @@ export const browse2Context = {
entityUrl: (entityName: string) => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Pretty much this whole file is all about encoding the url parts

</div>
</div>
);
return <NotFoundPanel title="Call not found" />;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Moved to a common component

@@ -669,6 +670,13 @@ const useOpVersion = (
};
}

if (opVersionRes.obj == null) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This block (and the one below that is similar basically handles the case when the target data is not found. before, this would create a JS error due to incorrect nulls

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

moved to new PR

* artifacts or local artifacts. It is not exported currently, but is used for
* the new Weave branch in parseRef
*/
const parseWeaveTraceRef = (ref: string): WeaveObjectRef => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This was extracted from parseRef and is easier to review with whitespace hidden

@tssweeney tssweeney marked this pull request as ready for review August 16, 2024 08:58
entityName: string,
projectName: string,
noEncode?: boolean
) => string;
typeUIUrl: (
entityName: string,
projectName: string,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can't we assume entityName and projectName are always safe?

@@ -7,7 +7,11 @@ export const flattenObjectPreservingWeaveTypes = (obj: {
[key: string]: any;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Moved to New PR

@@ -35,7 +36,7 @@ export const OpVersionPage: React.FC<{
if (opVersion.loading) {
return <CenteredAnimatedLoader />;
} else if (opVersion.result == null) {
return <div>Op version not found</div>;
return <NotFoundPanel title="Op not found" />;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

moved to new PR

@@ -59,7 +60,7 @@ export const ObjectVersionPage: React.FC<{
if (objectVersion.loading) {
return <CenteredAnimatedLoader />;
} else if (objectVersion.result == null) {
return <div>Object not found</div>;
return <NotFoundPanel title="Object not found" />;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

moved to new PR

</div>
</div>
);
return <NotFoundPanel title="Call not found" />;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

moved to new PR

@tssweeney tssweeney changed the title fix: Clients now correctly quote/unquote ref parts DNM: fix: Clients now correctly quote/unquote ref parts Aug 16, 2024
@tssweeney tssweeney closed this Aug 29, 2024
@github-actions github-actions bot locked and limited conversation to collaborators Aug 29, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants