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

roachprod, roachtest: use same cluster name sanitization #123961

Merged
merged 1 commit into from
May 16, 2024

Conversation

DarrylWong
Copy link
Contributor

@DarrylWong DarrylWong commented May 10, 2024

Previously, roachtest had it's own function to sanitize cluster names, while roachprod had it's own function to verify (but not sanitize) cluster names. This change removes both and opts instead to use vm.DNSSafeName.

This change also introduces MalformedClusterNameError which gives a hint on what is wrong with the name and
tells roachtest not to retry cluster creation.

Fixes: #122633
Epic: none
Release note: none

@cockroach-teamcity
Copy link
Member

This change is Reviewable

@DarrylWong DarrylWong force-pushed the invalid-username-fix branch 4 times, most recently from 0016299 to 69cc304 Compare May 13, 2024 16:11
@DarrylWong DarrylWong marked this pull request as ready for review May 13, 2024 18:56
@DarrylWong DarrylWong requested a review from a team as a code owner May 13, 2024 18:56
@DarrylWong DarrylWong requested review from nameisbhaskar and vidit-bhat and removed request for a team May 13, 2024 18:56
@srosenberg srosenberg self-requested a review May 13, 2024 22:28
// Unsafe characters are dropped. No length check is performed.
func DNSSafeAccount(account string) string {
func DNSSafeName(name string) string {
safe := func(r rune) rune {
switch {
case r >= 'a' && r <= 'z':
return r
case r >= 'A' && r <= 'Z':
return unicode.ToLower(r)
Copy link
Member

Choose a reason for hiding this comment

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

Nice! This is indeed more efficient than strings.ToLower when it's known that the char sequence is ascii. It's essentially inlined to this r += 'a' - 'A'.

@@ -973,6 +966,9 @@ func (f *clusterFactory) newCluster(
// or a destroy from the previous iteration failed.
return nil, nil, err
}
if errors.HasType(err, (*roachprod.MalformedClusterNameError)(nil)) {
Copy link
Member

Choose a reason for hiding this comment

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

Let's add a unit test to cover this case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I gave it a shot, but I wasn't sure how to feasibly do this without mocking a ton of things. There already is a way to mock cluster creation via TestSpec + r.MakeClusterSpec(0), but that skips roachprod.Create and this entire retry loop where we validate the cluster name.

I could mock roachprod.Create to just return a MalformedclusterNameError, but I don't think that test would be very useful.

Copy link
Member

Choose a reason for hiding this comment

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

I could mock roachprod.Create to just return a MalformedclusterNameError, but I don't think that test would be very useful.

That would have been my suggestion as well. I think it would be useful to unit test roachprod.Create especially in the context of [1]; i.e., ClusterAlreadyExistsError, MalformedClusterNameError and potentially future type of errors are non-retryable, so we should be able to cover the fact via a unit test. For now, the mock could randomly return the two non-retryable errors, and we assert that the mock is invoked exactly once, i.e., no retries. Wdyt?

[1] #114523

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. I was trying to add a case where it didn't error out and that was quite tricky to do without mocking a bunch of things. Mocking the two non-retryable errors cases was simple though.

Copy link
Member

@srosenberg srosenberg left a comment

Choose a reason for hiding this comment

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

Could use a couple of new unit tests (for verifyClusterName and newCluster), otherwise looks great!

@DarrylWong DarrylWong force-pushed the invalid-username-fix branch 2 times, most recently from 2d3446b to 927338d Compare May 14, 2024 21:06

// _findActiveAccounts is a test only variable, used for mocking a provider finding
// active accounts in a unit test, where we don't want to actually access a provider.
var _findActiveAccounts func(l *logger.Logger) (map[string]string, error)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is it okay to do this for a package variable? I couldn't think of an easier way to mock vm.FindActiveAccounts. AFAICT, the tests in a package are run sequentially, so no concurrency in accessing this.

Copy link
Member

Choose a reason for hiding this comment

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

Yep, this is a totally legit. way of mocking which is also used sometimes for dependency injection. E.g., InitTestConnectorFactory [1] is used to inject (or mock) a test-specific implementation of the KV connector.

However, we could initialize it with the actual implementation, i.e.,

var _findActiveAccounts = vm.FindActiveAccounts

and get rid of the conditional logic below.

[1]

Factory = testConnectorFactory{}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done! Thanks for tip

pkg/roachprod/roachprod.go Show resolved Hide resolved
@@ -973,6 +966,9 @@ func (f *clusterFactory) newCluster(
// or a destroy from the previous iteration failed.
return nil, nil, err
}
if errors.HasType(err, (*roachprod.MalformedClusterNameError)(nil)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I gave it a shot, but I wasn't sure how to feasibly do this without mocking a ton of things. There already is a way to mock cluster creation via TestSpec + r.MakeClusterSpec(0), but that skips roachprod.Create and this entire retry loop where we validate the cluster name.

I could mock roachprod.Create to just return a MalformedclusterNameError, but I don't think that test would be very useful.

active, err := vm.FindActiveAccounts(l)
var active map[string]string
var err error
if _findActiveAccounts != nil {
Copy link
Member

Choose a reason for hiding this comment

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

Can elide this check and just use active, err = _findActiveAccounts(l) as per above comment.

@srosenberg
Copy link
Member

I am guessing check_generated_code failed because you forgot to run,

./dev generate bazel

which creates the missing roachprod_test package,

--- a/pkg/roachprod/BUILD.bazel
+++ b/pkg/roachprod/BUILD.bazel
@@ -1,4 +1,4 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
+load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

 go_library(
     name = "roachprod",
@@ -38,3 +38,13 @@ go_library(
         "@org_golang_x_sys//unix",
     ],
 )
+
+go_test(
+    name = "roachprod_test",
+    srcs = ["roachprod_test.go"],
+    embed = [":roachprod"],
+    deps = [
+        "//pkg/roachprod/logger",
+        "@com_github_stretchr_testify//assert",
+    ],
+)

Previously, roachtest had it's own function to sanitize
cluster names, while roachprod had it's own function to
verify cluster names. This change removes both and opts
instead to use vm.DNSSafeName.

This change also introduces MalformedClusterNameError
which gives a hint on what is wrong with the name and
tells roachtest not to retry cluster creation.
Copy link
Member

@srosenberg srosenberg left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks for adding the unit tests!

@DarrylWong DarrylWong added backport-23.1.x Flags PRs that need to be backported to 23.1 backport-23.2.x Flags PRs that need to be backported to 23.2. backport-24.1.x Flags PRs that need to be backported to 24.1. labels May 16, 2024
@DarrylWong
Copy link
Contributor Author

TFTR!

bors r=srosenberg

@craig
Copy link
Contributor

craig bot commented May 16, 2024

Build failed:

@DarrylWong
Copy link
Contributor Author

🥺

bors retry

@craig craig bot merged commit 6fa15a6 into cockroachdb:master May 16, 2024
22 checks passed
Copy link

blathers-crl bot commented May 16, 2024

Encountered an error creating backports. Some common things that can go wrong:

  1. The backport branch might have already existed.
  2. There was a merge conflict.
  3. The backport branch contained merge commits.

You might need to create your backport manually using the backport tool.


error creating merge commit from 278679f to blathers/backport-release-24.1-123961: POST https://api.github.com/repos/cockroachdb/cockroach/merges: 409 Merge conflict []

you may need to manually resolve merge conflicts with the backport tool.

Backport to branch 24.1.x failed. See errors above.


🦉 Hoot! I am a Blathers, a bot for CockroachDB. My owner is dev-inf.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backport-23.1.x Flags PRs that need to be backported to 23.1 backport-23.2.x Flags PRs that need to be backported to 23.2. backport-24.1.x Flags PRs that need to be backported to 24.1.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

roachprod,roachtest: bug when choosing a username with invalid characters
3 participants