Skip to content

chore: color value_source for deployment values #9922

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

Merged
merged 21 commits into from
Sep 29, 2023

Conversation

Emyrk
Copy link
Member

@Emyrk Emyrk commented Sep 28, 2023

closes #9760

Implementation of Optionset Unmarshal

Before this change, all Value fields of Options were nil and so unmarshalling was ok. Now that I am passing a value, the UnmarshalJSON function gets complicated because we cannot infer types easily.

I got this to work, although we don't use these values anywhere in the Golang, so it didn't really matter on the golang side. On the Typescript side, we just use the any type and type case to the types we know they are. 🤷‍♂️

It's a little janky. To move to a more statically typed struct, we would need to make OptionSet a struct with fields, rather than a slice? Or extend the pflag.Value interface to include some Type() fields we can do some type interferences on. Or just make an any type that implements the interface??

For now, I think this solution is pretty good.

What this does

Indicates where the value is set. Should we add a tooltip or something? Or is this intuitive enough?

Screenshot from 2023-09-28 16-36-58

How it looks in green
![Screenshot from 2023-09-28 16-29-30](https://github.com/coder/coder/assets/5446298/bfe3ac8a-b2ff-470c-9939-df17307c6c64)

@Emyrk Emyrk changed the title chore: Color value_source for deployment values chore: color value_source for deployment values Sep 28, 2023
@Emyrk Emyrk removed the request for review from mafredri September 28, 2023 21:57
Comment on lines +75 to +79
// If an option has no values, we have no idea how to unmarshal it.
// So just discard the json data.
if o.Value == nil {
o.Value = &DiscardValue
}
Copy link
Member Author

Choose a reason for hiding this comment

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

Since the values are behind an interface, if we do not know how to implement the .String() method, then having the value in an unknown form is pointless.

We could add some logic to try and detect primitive options, but I do not see the value. We should always have the full set of Options when we unmarshal, so we should always get the correct types and values.

@Emyrk Emyrk requested a review from johnstcn September 29, 2023 03:55
UpdateCheckOptions *updatecheck.Options // Set non-nil to enable update checking.
// DeploymentOptions do contain the copy of DeploymentValues, but contain
// contextual information about how the values were set.
// Do not use DeploymentOptions to retrieve values, use DeploymentValues instead.
Copy link
Member

Choose a reason for hiding this comment

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

Why not just strip all values from DeploymentOptions then? Too many allocations?

Copy link
Member Author

Choose a reason for hiding this comment

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

I wanted to go this route, but the UI grabs the value from the option. Values are returned in the parsed form, and the option form:

codersdk.DeploymentConfig{
Values: values,
Options: api.DeploymentOptions,
},

There is no way to associate the values between the two, so the value needs to be present on the Option for the UI to know.

I feel like this could use a bit of a refactor, but it is working and not really that important.

Copy link
Collaborator

@BrunoQuaresma BrunoQuaresma left a comment

Choose a reason for hiding this comment

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

FE looks good.

Copy link
Member

@mafredri mafredri left a comment

Choose a reason for hiding this comment

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

Neat feature! While I understood the shortcoming, I didn't quite grasp why we had to work around it in UnmarhsalJSON like we did. But I'm fine with it if you think that's the best approach. It would be ideal if we could just change our structures so that there's no need for it, though.

//
// The value is discarded if it's type cannot be inferred. This behavior just
// feels "safer", although it should never happen if the correct option set
// is passed in.
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure under what circumstances this Unmarshal is run? Is there a chance of conflicts between server versions (API response) and CLI version (decode)?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yup exactly. The new version can have more deployment options, so the old version has nothing to match it against.

@Emyrk
Copy link
Member Author

Emyrk commented Sep 29, 2023

Neat feature! While I understood the shortcoming, I didn't quite grasp why we had to work around it in UnmarshalJSON like we did. But I'm fine with it if you think that's the best approach. It would be ideal if we could just change our structures so that there's no need for it, though.

Yea this was an annoying headache. Ideally we change the the data structure, but that would require us to change the slice into a struct, because you cannot have typed slices where each element is different.

That change ripples aggressively through a lot of code on the FE and BE, and would be a much bigger change.


I am taking a step back and trying to understand how main is working... Because it must be working to some extent with the values, but I kept running into this unmarshal error demonstrated here:

https://goplay.tools/snippet/F5I4fFRB3au

Essentially when you call UnmarshalJSON on an interface with methods, the unmarshal target needs to implement those methods. So if you do not have the underlying type ready, then the unmarshal will not work. We always do (&codersdk.DeploymentValues{}).Options() before we call Unmarshal, but that would be the slice is being unmarshaled in place? Like the elements are being unmarshaled into each index?

If main is already doing that, maybe there is something about json.Unmarshal I did not know about.

Comment on lines 1772 to 1778
case "string-array":
var empty clibase.StringArray
cpyOpt.Value = &empty
default:
var empty clibase.String
cpyOpt.Value = &empty
}
Copy link
Member Author

Choose a reason for hiding this comment

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

Oooh this is where I broke the json.Unmarshal(). @johnstcn @mafredri

So json.Unmarshal does unmarshal in place for arrays, so all the types were working correctly. When I was zeroing the secrets, I figured a string is fine, but it is not. I should have used a nil.

So this made a clibase.String try to unmarshal into whatever the secret was, which did not work. If I instead just use a nil, then it all works 🤦‍♂️.


So I can delete all the json unmarshal stuff I wrote, but my json unmarshal handles all the edge cases that would usually just fail (see https://goplay.tools/snippet/GPxfjtnfvys).

So I can delete my unmarshal code, and if your client and version have different option sets, the call will fail. 🤔

Copy link
Member Author

Choose a reason for hiding this comment

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

so if you have client version X, and we add an option in server version Y, then the unmarshal will fail when client tries to get DeploymentOptions?

Yes.

Copy link
Member

Choose a reason for hiding this comment

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

I'd rather have a custom UnmarshalJSON method than a potential regression between versions :-)

Copy link
Member Author

Choose a reason for hiding this comment

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

I think we should keep this complicated unmarshal logic. It is more complicated, but with testing, it is safe and only affects the codersdk.Client. If we do not have this code, then we can unintentionally just break the client, and there is nothing the client can do to resolve this since the JSON from the server is the issue at hand.

@Emyrk Emyrk merged commit 92308be into main Sep 29, 2023
@Emyrk Emyrk deleted the stevenmasley/deployment_opts_source branch September 29, 2023 17:04
@github-actions github-actions bot locked and limited conversation to collaborators Sep 29, 2023
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.

Show how deployment option was set in UI
4 participants