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

Remove all port proxies upon shutdown #4503

Merged
merged 6 commits into from Apr 25, 2023
Merged

Remove all port proxies upon shutdown #4503

merged 6 commits into from Apr 25, 2023

Conversation

Nino-K
Copy link
Member

@Nino-K Nino-K commented Apr 19, 2023

Currently, privileged services leave behind all the port proxies that it creates, this change allows to clean up during shutdown. Privileged service manages this by keeping track of all the added/deleted port proxies in a map using the hash of the port mapping struct as a map key, this is so that we can avoid the recursive lookups.

Fixes: #3567

Signed-off-by: Nino Kodabande <nkodabande@suse.com>
@Nino-K Nino-K requested a review from mook-as April 19, 2023 17:21
@Nino-K Nino-K marked this pull request as draft April 19, 2023 17:34
Signed-off-by: Nino Kodabande <nkodabande@suse.com>
@mook-as mook-as removed their request for review April 19, 2023 19:38
Signed-off-by: Nino Kodabande <nkodabande@suse.com>
@Nino-K Nino-K requested a review from mook-as April 19, 2023 21:36
@Nino-K Nino-K marked this pull request as ready for review April 19, 2023 21:36
Signed-off-by: Nino Kodabande <nkodabande@suse.com>
Copy link
Contributor

@mook-as mook-as left a comment

Choose a reason for hiding this comment

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

There should be tests for this. I'm in particular worried about getHash.

github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-connections v0.4.0
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be good to clean up this file. (Merge all of the require() blocks into one, then run go mod tidy.)

}

func (p *proxy) exec(portMapping types.PortMapping) error {
portProxy := portProxy{
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we call this port or newPort instead (so that we don't have portProxy referring to either the instance or the type depending on context)?

p.mutex.Lock()
defer p.mutex.Unlock()
for _, proxy := range p.portMappings {
_ = p.delete(proxy)
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we at least log the error?

@@ -67,6 +100,7 @@ func deleteProxy(portMapping types.PortMapping) error {
}
}
}
delete(p.portMappings, getHash(portProxy))
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, shouldn't this be done under the mutex?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, thanks for catching it, I totally missed it.

@@ -150,3 +184,8 @@ func portProxyAddArgs(listenPort, listenAddr, connectAddr string) ([]string, err
fmt.Sprintf("connectaddress=%s", connectAddr),
}, nil
}

func getHash(portProxy portProxy) string {
data := fmt.Sprintf("%v", portProxy)
Copy link
Contributor

Choose a reason for hiding this comment

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

Is %v going to be consistent in the right way? (Would this include any pointers? Because if it does, then the hash would not match when we want it to.)

Copy link
Member Author

@Nino-K Nino-K Apr 21, 2023

Choose a reason for hiding this comment

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

The %v is consistent since the protProxy and its sub-structs do not contain any pointers, however, this is not really future-proof, but maybe that's ok since we are retiring the privileged service soon.

An alternative approach is to use gob or JSON, I will investigate an alternative.

Comment on lines 84 to 86
p.mutex.Lock()
p.portMappings[getHash(portProxy)] = portProxy
p.mutex.Unlock()
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a risk of a race here? Assume the same port definition below.

  1. add gets called, the port is added, but blocks on acquiring the mutex.
  2. delete gets called, runs through completion. (The port is removed from netsh.)
  3. add continues, and inserts into p.portMappings even though it's no longer in netsh.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, this could potentially happen but remember the purpose of portMappings map is to remove all the port mappings upon shutdown and it ignores any potential errors from netsh interface portproxy delete.... So, if there is an attempt to delete an entry that doesn't have a corresponding netsh it's ok because the error is ignored. The most important thing is to make sure the netsh is up to date and that's why that operation takes precedence.

Signed-off-by: Nino Kodabande <nkodabande@suse.com>
@Nino-K Nino-K requested a review from mook-as April 24, 2023 17:48
}

func addProxy(portMapping types.PortMapping) error {
for _, v := range portMapping.Ports {
func (p *proxy) add(portProxy portProxy) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider calling the variable something like so we don't repeat the type name.

- Also, changes the byte creation for struct to json marshal
- Manually sorts all the sub-slices to prevent hash mismatch

Signed-off-by: Nino Kodabande <nkodabande@suse.com>
actual portProxy
expect portProxy
shouldMatch bool
expectedErr error
Copy link
Contributor

Choose a reason for hiding this comment

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

This can't be anything non-nil, because we expect both hashes to lead to the same error. (This means the coverage is not great.)

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, there is a separate test case that needs to be added for when json.Marshal returns an error, which requires making json.Marshal fail. But, when I thought about it in detail, I realized we would just be testing the go's built-in packages which are already tested by the library. Also, when json.Marshal fails getHash always returns an empty string, so it wouldn't have been a great test, to begin with.

Copy link
Contributor

Choose a reason for hiding this comment

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

Right, the comment was more "why do we have this field" than anything :)

@Nino-K Nino-K merged commit f0af250 into main Apr 25, 2023
9 checks passed
@Nino-K Nino-K deleted the clean-up-port-mappings branch April 25, 2023 22:47
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.

rdctl factory-reset: also needs to kill Privileged Service
3 participants