Skip to content

1719725: rhsm - Write config file atomically#2120

Merged
jirihnidek merged 1 commit intocandlepin:masterfrom
mvollmer:rhsm-write-config-atomically
Jun 26, 2019
Merged

1719725: rhsm - Write config file atomically#2120
jirihnidek merged 1 commit intocandlepin:masterfrom
mvollmer:rhsm-write-config-atomically

Conversation

@mvollmer
Copy link
Copy Markdown
Contributor

The config file is read asynchronously by at least the file watches of
dbus/server.py, and if it is just overwritten in place the file watch
handler might read it when it has only been partially written.

@candlepin-bot
Copy link
Copy Markdown

Can one of the admins verify this patch?

@jirihnidek
Copy link
Copy Markdown
Contributor

@candlepin-bot ok to test

1 similar comment
@jirihnidek
Copy link
Copy Markdown
Contributor

@candlepin-bot ok to test

Copy link
Copy Markdown
Contributor

@jirihnidek jirihnidek left a comment

Choose a reason for hiding this comment

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

Hi, thanks for PR. I have small proposal how to improve it.

"""Writes config file to storage."""
fo = open(self.config_file, "w")
self.write(fo)
with open(self.config_file + ".tmp", "w") as fo:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is better than previous solution, but I would like to improve it a little bit more. Why? Let's consider following situation. Two processes try to write to the /etc/rhsm/rhsm.conf.tmp in the same time. The configuration file can be corrupted, because self.write(fo) can be interrupted. I propose something like this:

import tempfile
import os

dir_name = os.path.dirname(self.config_file)
temp_filename = tempfile.mktemp(prefix='rhsm.', suffix='.tmp', dir=dir_name)
with open(temp_filename, "w") as fo:
    self.write(fo)
os.rename(self.config_file + ".tmp", self.config_file)

As you can see each process creates unique temporary file and renaming should be atomic.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, I was thinking the same.

mktemp is deprecated, though. I'll use NamedTemporaryFile.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

mktemp is deprecated, though. I'll use NamedTemporaryFile.

Sure, the code was just suggestion how to solve the issue. :-)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

@jirihnidek jirihnidek self-assigned this Jun 17, 2019
@mvollmer mvollmer force-pushed the rhsm-write-config-atomically branch from 880da3a to f74c120 Compare June 18, 2019 08:25
Copy link
Copy Markdown
Contributor

@jirihnidek jirihnidek left a comment

Choose a reason for hiding this comment

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

Hi, thanks for updating your PR. 👍 I have we two more requests. 😃

def save(self, config_file=None):
"""Writes config file to storage."""
fo = open(self.config_file, "w")
fo = tempfile.NamedTemporaryFile(mode="w", dir="/etc/rhsm", delete=False)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

  1. Please use dir=os.path.dirname(self.config_file). Your implementation breaks several unit tests and the configuration directory can be different than '/etc/rhsm' (e.g. containers).
  2. Please use try: except: here, because rhsm is module that can be used by other application. When the application is not executed by root, then it will raise OSError exception.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Please use dir=os.path.dirname(self.config_file).

Ouch, but of course!

Copy link
Copy Markdown
Contributor Author

@mvollmer mvollmer Jun 24, 2019

Choose a reason for hiding this comment

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

Please use try: except: here, because rhsm is module that can be used by other application. When the application is not executed by root, then it will raise OSError exception.

What should be done in the except: branch? The current code on master does not use try/except, so maybe we fix that in a separate PR?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Please use dir=os.path.dirname(self.config_file).

Ouch, but of course!

Done.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please use try: except: here, because rhsm is module that can be used by other application. When the application is not executed by root, then it will raise OSError exception.

What should be done in the except: branch? The current code on master does not use try/except, so maybe we fix that in a separate PR?

Good point. rhsm is module and it is not possible to e.g. log anything to rhsm.log. Let's do not use try-except and application using this module will have to handle exceptions.

fo = open(self.config_file, "w")
fo = tempfile.NamedTemporaryFile(mode="w", dir="/etc/rhsm", delete=False)
self.write(fo)
fo.close()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I prefer to use with statement. When some exception happens inside with statement, then the file is closed automatically.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done.

@mvollmer mvollmer force-pushed the rhsm-write-config-atomically branch from f74c120 to 601fa44 Compare June 24, 2019 12:04
The config file is read asynchronously by at least the file watches of
dbus/server.py, and if it is just overwritten in place the file watch
handler might read it when it has only been partially written.
@mvollmer mvollmer force-pushed the rhsm-write-config-atomically branch from 601fa44 to 195c154 Compare June 24, 2019 15:21
Copy link
Copy Markdown
Contributor

@jirihnidek jirihnidek left a comment

Choose a reason for hiding this comment

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

ACK

@jirihnidek jirihnidek merged commit c0f3f16 into candlepin:master Jun 26, 2019
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.

3 participants