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

Consider DC-level config when validating numToken updates in webhook (fixes #1222) #1358

Merged
merged 2 commits into from
Jun 27, 2024

Conversation

olim7t
Copy link
Contributor

@olim7t olim7t commented Jun 18, 2024

What this PR does:
Instead of only comparing the top-level config (which can fail if fields are missing), compute and compare the numToken of each individual DC, using the inheritance and defaulting rules.
This also covers cases that were not handled before, like config being nil on one side but not the other.

Which issue(s) this PR fixes:
Fixes #1222

Checklist

  • Changes manually tested
  • Automated Tests added/updated
  • Documentation added/updated
  • CHANGELOG.md updated (not required for documentation PRs)
  • CLA Signed: DataStax CLA

@olim7t olim7t requested a review from a team as a code owner June 18, 2024 00:22
for dcName, newNumTokens := range newNumTokensPerDc {
oldNumTokens, oldExists := oldNumTokensPerDc[dcName]
if oldExists && oldNumTokens != newNumTokens {
return ErrNumTokens
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We could add more details and keep iterating to get an exhaustive list, but I'm not sure if it's worth it.

return nil
}

func numTokensPerDc(cassandra *CassandraClusterTemplate) (map[string]interface{}, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

question: Why interface{} and not map[string]float64. Or actually just map[string]uint / int, since we don't use floats for anything in reality (it's a YAML/JSON problem) and we should throw an error if we can't map to uint value.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because it's good enough for the task at hand, which is only about comparing values.

We could indeed check that the values are integers, but then why stop at num_tokens, and not have the webhook validate every other config value? It's not the approach we have taken so far, so I assume we want to keep things as light as possible here and do more advanced checks later (in this case computeDcNumTokens()).

As to float64 for the defaults, it's because that's what unmarshaling into Unstructured produces for any numeric value:

	u := new(Unstructured)
	u.UnmarshalJSON([]byte(`{"num_token": 16}`))
	numToken, _ := u.Get("num_token")
	i16 := 16
	f16 := float64(i16)
	fmt.Println("numToken==i16", numToken == i16) // false
	fmt.Println("numToken==f16", numToken == f16) // true

Copy link
Contributor

Choose a reason for hiding this comment

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

As to float64 for the defaults, it's because that's what unmarshaling into Unstructured produces for any numeric value

It is, because JSON & YAML do not accept any other numeric value. Problem itself is more interesting if you compare:

"num_tokens": "16" and "num_tokens": 16. Both are the same value, but they could be unmarshalled differently. We do not force users to use : 16 or : "16" and most users wouldn't even able to tell what the difference is. From your example, we end up with a difference however:

func TestUnmarshalNumbers(t *testing.T) {
	dest := make(map[string]interface{}, 1)
	assert.NoError(t, json.Unmarshal([]byte(`{"num_token": "16"}`), &dest))
	numToken := dest["num_token"]
	i16 := 16
	f16 := float64(i16)

	assert.NotEqual(t, numToken, i16)
	assert.NotEqual(t, numToken, f16)
}

Neither compare will work. One needs to parse it first as number (using strconv) and only then compare the resulting int.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

True, this PR assumes the user will provide a YAML number (as does the current implementation on the main branch btw).
If supporting YAML strings is also a requirement, then it would make sense to parse.

@adejanovski
Copy link
Contributor

@olim7t @burmanm, what's the status on this PR?

Copy link
Contributor

@burmanm burmanm left a comment

Choose a reason for hiding this comment

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

suggestion: I'm going to approve since current main branch behavior is "wrong" also, but we really shouldn't expect users to have their YAML as numbers always, since we also have situations where we require user to input the number as string instead of number (to avoid string/uint64 -> float64 issue, like we had the bug with weird commit number begin parsed as a number).

@olim7t olim7t force-pushed the fix-webhook-numtokens branch from 74bfa69 to 53231f2 Compare June 27, 2024 17:51
@olim7t olim7t merged commit 63ff0fe into k8ssandra:main Jun 27, 2024
60 checks passed
@olim7t olim7t deleted the fix-webhook-numtokens branch June 27, 2024 21:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Validating webhook rejects updates if the server version is declared at the DC level
3 participants