Merge lp:~adam-collard/landscape-client/config-file-sysinfo into lp:~landscape/landscape-client/trunk

Proposed by Adam Collard
Status: Merged
Approved by: Adam Collard
Approved revision: 763
Merged at revision: 759
Proposed branch: lp:~adam-collard/landscape-client/config-file-sysinfo
Merge into: lp:~landscape/landscape-client/trunk
Diff against target: 199 lines (+33/-40)
5 files modified
landscape/__init__.py (+1/-1)
landscape/broker/config.py (+2/-3)
landscape/configuration.py (+1/-1)
landscape/deployment.py (+18/-21)
landscape/tests/test_deployment.py (+11/-14)
To merge this branch: bzr merge lp:~adam-collard/landscape-client/config-file-sysinfo
Reviewer Review Type Date Requested Status
Björn Tillenius (community) Approve
Geoff Teale (community) Approve
Review via email: mp+212052@code.launchpad.net

Commit message

Only complain about missing default configuration files if they are present but unreadable.

Description of the change

Only complain about missing default configuration files if they are present but unreadable.

To test:

rm /etc/landscape/client.conf (or move it out of the way if it's special)

landscape-sysinfo should not error out.

To post a comment you must log in.
Revision history for this message
Björn Tillenius (bjornt) wrote :

[1]

This code:

51 + if os.path.isfile(self.config) and os.access(self.config, os.R_OK):
52 + self.load_configuration_file(self.config)
53 + elif not accept_nonexistent_config:
54 + sys.exit("error: config file %s can't be read" % self.config)

looks a lot like this code:

77 + if os.path.isfile(config_filename):
78 + if not os.access(config_filename, os.R_OK):
79 + errors.append("error: config file %s can't be read" %
80 + config_filename)
81 + else:
82 + self.load_configuration_file(config_filename)
83 + break

I had trouble seeing the difference at first. Only after re-reading the code several
time I spotted what was different.

I'd suggest being a bit more explicit and write the whole think like this:

        if (self.config and not os.path.isfile(self.config)
                and not accept_nonexistent_config):
            sys.exit("error: config file %s doesn't exist" % self.config)

        if self.config:
            config_filenames = [self.config]
        else:
            config_filenames = self.default_config_filenames

        errors = []
        for config_filename in config_filenames:
            if os.path.isfile(config_filename):
                if not os.access(config_filename, os.R_OK):
                    errors.append("error: config file %s can't be read" %
                                  config_filename)
                else:
                    self.load_configuration_file(config_filename)
                    break
        else:
            if errors and not accept_nonexistent_config:
                sys.exit("\n".join(errors))

I.e., first check whether self.config exists, and then do the general checking
that was already in place. What do you think?

review: Needs Information
Revision history for this message
Geoff Teale (tealeg) wrote :

+1 Approve

With regards to Bjorn's suggestion above - it seem quite good, but I'm slightly concerned that we wouldn't append an error if os.path.isfile returns false for a filename in self.default_config_filenames. That should be done if you choose to adopt Bjorn's code.

review: Approve
Revision history for this message
Adam Collard (adam-collard) wrote :

> +1 Approve
>
> With regards to Bjorn's suggestion above - it seem quite good, but I'm
> slightly concerned that we wouldn't append an error if os.path.isfile returns
> false for a filename in self.default_config_filenames. That should be done if
> you choose to adopt Bjorn's code.

Hehe, that would take me back to where I started.

We don't want errors if /etc/landscape/client.conf is missing, only if it's present, but unreadable

Revision history for this message
Adam Collard (adam-collard) wrote :

> [1]
>
> This code:
>
> 51 + if os.path.isfile(self.config) and os.access(self.config,
> os.R_OK):
> 52 + self.load_configuration_file(self.config)
> 53 + elif not accept_nonexistent_config:
> 54 + sys.exit("error: config file %s can't be read" %
> self.config)
>
>
> looks a lot like this code:
>
> 77 + if os.path.isfile(config_filename):
> 78 + if not os.access(config_filename, os.R_OK):
> 79 + errors.append("error: config file %s can't be
> read" %
> 80 + config_filename)
> 81 + else:
> 82 + self.load_configuration_file(config_filename)
> 83 + break
>
> I had trouble seeing the difference at first. Only after re-reading the code
> several
> time I spotted what was different.

Yeah sorry about that. I'll add some comments to ease readability too.

>
> I'd suggest being a bit more explicit and write the whole think like this:
>
> if (self.config and not os.path.isfile(self.config)
> and not accept_nonexistent_config):
> sys.exit("error: config file %s doesn't exist" % self.config)
>
> if self.config:
> config_filenames = [self.config]
> else:
> config_filenames = self.default_config_filenames
>
> errors = []
> for config_filename in config_filenames:
> if os.path.isfile(config_filename):
> if not os.access(config_filename, os.R_OK):
> errors.append("error: config file %s can't be read" %
> config_filename)
> else:
> self.load_configuration_file(config_filename)
> break
> else:
> if errors and not accept_nonexistent_config:
> sys.exit("\n".join(errors))
>
>
> I.e., first check whether self.config exists, and then do the general checking
> that was already in place. What do you think?

I'm -0.5 on the suggestion - I don't like checking for it being a readable file twice. Once we've got past the first if we've already checked that self.config is a readable file.

I think how I have it now is more explicit, even if more verbose. There are two different behaviours for a specified configuration file vs. default configuration files, and those behaviours are in two separate blocks.

An alternative would be to try...except here and have different ways of handling the exception

Revision history for this message
Björn Tillenius (bjornt) wrote :

I still prefer my version, since it doesn't repeat the code so much.

But if you still want to go with your version, please add comments
for the place where you check whether the file exists and the
permission, to make it clear that when you specify the config
file on the command line, the file has to be there, and when
using the default files, only the permission is interesting.

[2]

Sorry for not spotting, this before, but this branch actually
changes the behavior, so that the name accept_nonexistent_config
isn't accurate anymore. If no default config files are found,
it doesn't matter what value accept_nonexisting_config has,
no error will be raised.

Do you know what this variable is used for?

review: Needs Information
Revision history for this message
Björn Tillenius (bjornt) wrote :

Thanks, removing the accept_nonexisting_config makes it look a lot
cleaner, +1!

review: Approve
Revision history for this message
🤖 Landscape Builder (landscape-builder) wrote :
Download full text (1.2 MiB)

The attempt to merge lp:~adam-collard/landscape-client/config-file-sysinfo into lp:landscape-client failed. Below is the output from the failed tests.

python setup.py build_ext -i
running build_ext
landscape.broker.tests.test_amp
  RemoteBrokerTest
    test_call_if_accepted ... [ERROR]
    test_call_if_accepted_with_not_accepted ... [ERROR]
    test_call_on_events ... [ERROR]
    test_exit ... [ERROR]
    test_fire_event ... [ERROR]
    test_get_accepted_message_types ... [ERROR]
    test_get_server_uuid ... [ERROR]
    test_is_message_pending ... [ERROR]
    test_listen_events ... [ERROR]
    test_method_call_error ... [ERROR]
    test_ping ... [ERROR]
    test_register ... [ERROR]
    test_register_client ... [ERROR]
    test_register_client_accepted_message_type ... [ERROR]
    test_reload_configuration ... [ERROR]
    test_send_message ... [ERROR]
    test_send_message_with_urgent ... [ERROR]
    test_stop_clients ... [ERROR]
  RemoteClientTest
    test_exit ... [ERROR]
    test_fire_event ... [ERROR]
    test_message ... [ERROR]
    test_method_call_error ... [ERROR]
    test_ping ... [ERROR]
landscape.broker.tests.test_client
  BrokerClientTest
    test_add ... [ERROR]
    test_dispatch_message ... [ERROR]
    test_dispatch_message_with_exception ... [ERROR]
    test_dispatch_message_with_no_handler ... [ERROR]
    test_exchange ... [ERROR]
    test_exchange_logs_errors_and_continues ... [ERROR]
    test_exchange_on_plugin_without_exchange_method ... [ERROR]
    test_exit ... [ERROR]
    test_fire_event ... [ERROR]
    test_fire_event_with_acceptance_changed ... [ERROR]
    test_fire_event_with_arguments ... [ERROR]
    test_fire_event_with_mixed_results ... [ERROR]
    test_get_named_plugin ... [ERRO...

763. By Adam Collard

Remove unused variable (oops)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'landscape/__init__.py'
2--- landscape/__init__.py 2013-12-17 09:44:42 +0000
3+++ landscape/__init__.py 2014-03-25 09:06:18 +0000
4@@ -1,5 +1,5 @@
5 DEBIAN_REVISION = ""
6-UPSTREAM_VERSION = "13.10"
7+UPSTREAM_VERSION = "14.01"
8 VERSION = "%s%s" % (UPSTREAM_VERSION, DEBIAN_REVISION)
9
10 # The "server-api" field of outgoing messages will be set to this value, and
11
12=== modified file 'landscape/broker/config.py'
13--- landscape/broker/config.py 2013-11-25 09:55:59 +0000
14+++ landscape/broker/config.py 2014-03-25 09:06:18 +0000
15@@ -79,7 +79,7 @@
16 """Get the path to the message store."""
17 return os.path.join(self.data_path, "messages")
18
19- def load(self, args, accept_nonexistent_config=False):
20+ def load(self, args):
21 """
22 Load options from command line arguments and a config file.
23
24@@ -87,8 +87,7 @@
25 C{http_proxy} and C{https_proxy} environment variables based on
26 that config data.
27 """
28- super(BrokerConfiguration, self).load(
29- args, accept_nonexistent_config=accept_nonexistent_config)
30+ super(BrokerConfiguration, self).load(args)
31 if self.http_proxy:
32 os.environ["http_proxy"] = self.http_proxy
33 elif self._original_http_proxy:
34
35=== modified file 'landscape/configuration.py'
36--- landscape/configuration.py 2014-02-25 18:06:48 +0000
37+++ landscape/configuration.py 2014-03-25 09:06:18 +0000
38@@ -411,7 +411,7 @@
39 self.prompt("tags", "Tags", False)
40 if self._get_invalid_tags(self.config.tags):
41 self.show_help("Tag names may only contain alphanumeric "
42- "characters.")
43+ "characters.")
44 self.config.tags = None # Reset for the next prompt
45 else:
46 break
47
48=== modified file 'landscape/deployment.py'
49--- landscape/deployment.py 2013-10-22 21:34:31 +0000
50+++ landscape/deployment.py 2014-03-25 09:06:18 +0000
51@@ -63,7 +63,7 @@
52 unsaved_options = ()
53 default_config_filenames = ["/etc/landscape/client.conf"]
54 if (os.path.dirname(os.path.abspath(sys.argv[0]))
55- == os.path.abspath("scripts")):
56+ == os.path.abspath("scripts")):
57 default_config_filenames.insert(0, "landscape-client.conf")
58 default_config_filenames = tuple(default_config_filenames)
59 config_section = "client"
60@@ -148,7 +148,7 @@
61 """
62 self.load(self._command_line_args)
63
64- def load(self, args, accept_nonexistent_config=False):
65+ def load(self, args):
66 """
67 Load configuration data from command line arguments and a config file.
68
69@@ -156,28 +156,26 @@
70 """
71 self.load_command_line(args)
72
73+ if self.config and not os.path.isfile(self.config):
74+ sys.exit("error: config file %s doesn't exist" % self.config)
75+
76 if self.config:
77 config_filenames = [self.config]
78 else:
79 config_filenames = self.default_config_filenames
80- # Parse configuration file, if found.
81+
82+ errors = []
83 for config_filename in config_filenames:
84- if (os.path.isfile(config_filename)
85- and os.access(config_filename, os.R_OK)):
86-
87- self.load_configuration_file(config_filename)
88- break
89-
90+ if os.path.isfile(config_filename):
91+ if not os.access(config_filename, os.R_OK):
92+ errors.append("error: config file %s can't be read" %
93+ config_filename)
94+ else:
95+ self.load_configuration_file(config_filename)
96+ break
97 else:
98- if not accept_nonexistent_config:
99- if len(config_filenames) == 1:
100- message = (
101- "error: config file %s can't be read" %
102- config_filenames[0])
103- else:
104- message = "error: no config file could be read"
105- sys.exit(message)
106-
107+ if errors:
108+ sys.exit("\n".join(errors))
109 self._load_external_options()
110
111 # Check that all needed options were given.
112@@ -402,12 +400,11 @@
113
114 return parser
115
116- def load(self, args, accept_nonexistent_config=False):
117+ def load(self, args):
118 """
119 Load configuration data from command line arguments and a config file.
120 """
121- super(Configuration, self).load(
122- args, accept_nonexistent_config=accept_nonexistent_config)
123+ super(Configuration, self).load(args)
124
125 if not isinstance(self.server_autodiscover, bool):
126 autodiscover = str(self.server_autodiscover).lower()
127
128=== modified file 'landscape/tests/test_deployment.py'
129--- landscape/tests/test_deployment.py 2013-10-24 01:55:54 +0000
130+++ landscape/tests/test_deployment.py 2014-03-25 09:06:18 +0000
131@@ -196,8 +196,7 @@
132 self.config.log_level = "warning"
133 self.config.write()
134 data = open(self.config_filename).read()
135- self.assertConfigEqual(data,
136- "[client]\nlog_level = warning\n")
137+ self.assertConfigEqual(data, "[client]\nlog_level = warning\n")
138
139 def test_write_empty_list_values_instead_of_double_quotes(self):
140 """
141@@ -303,10 +302,8 @@
142 self.assertConfigEqual(data, "[client]\nlog_level = error\n")
143
144 def test_write_to_given_config_file(self):
145- filename = self.makeFile()
146- self.config.load(
147- ["--log-level", "warning", "--config", filename],
148- accept_nonexistent_config=True)
149+ filename = self.makeFile(content="")
150+ self.config.load(["--log-level", "warning", "--config", filename])
151 self.config.log_level = "error"
152 self.config.write()
153 data = open(filename).read()
154@@ -383,7 +380,8 @@
155 """
156 filename = self.makeFile("[client]\nhello = world1\n")
157 self.config.load(["--config", filename])
158- open(filename, "w").write("[client]\nhello = world2\n")
159+ with open(filename, "w") as fh:
160+ fh.write("[client]\nhello = world2\n")
161 self.config.reload()
162 self.assertEqual(self.config.hello, "world2")
163
164@@ -408,7 +406,7 @@
165 error = self.assertRaises(
166 SystemExit, self.config.load, ["--config", filename])
167 self.assertEqual(
168- "error: config file %s can't be read" % filename, str(error))
169+ "error: config file %s doesn't exist" % filename, str(error))
170
171 def test_load_cannot_read_default(self):
172 """
173@@ -424,13 +422,11 @@
174
175 def test_load_not_found_default(self):
176 """
177- C{config.load} exits the process if the default config file is not
178- found.
179+ C{config.load} doesn't exit the process if the default config file
180+ is not found.
181 """
182 [default] = self.config.default_config_filenames[:] = ["/not/here"]
183- error = self.assertRaises(SystemExit, self.config.load, [])
184- self.assertEqual(
185- "error: config file %s can't be read" % default, str(error))
186+ self.config.load([])
187
188 def test_load_cannot_read_many_defaults(self):
189 """
190@@ -444,7 +440,8 @@
191 self.config.default_config_filenames[:] = [default1, default2]
192
193 error = self.assertRaises(SystemExit, self.config.load, [])
194- self.assertEqual("error: no config file could be read", str(error))
195+ self.assertEqual(
196+ "error: config file %s can't be read" % default1, str(error))
197
198 def test_data_directory_option(self):
199 """Ensure options.data_path option can be read by parse_args."""

Subscribers

People subscribed via source and target branches

to all changes: