Merge lp:~stub/charms/precise/postgresql/cleanups into lp:charms/postgresql
- Precise Pangolin (12.04)
- cleanups
- Merge into trunk
Proposed by Stuart Bishop
| Status: | Merged | ||||
|---|---|---|---|---|---|
| Approved by: | Mark Mims | ||||
| Approved revision: | 72 | ||||
| Merged at revision: | 62 | ||||
| Proposed branch: | lp:~stub/charms/precise/postgresql/cleanups | ||||
| Merge into: | lp:charms/postgresql | ||||
| Prerequisite: | lp:~stub/charms/precise/postgresql/bug-1205286 | ||||
| Diff against target: | 569 lines (+127/-183) 7 files modified README.md (+37/-40) charm-helpers.yaml (+1/-1) hooks/charmhelpers/core/host.py (+7/-5) hooks/hooks.py (+49/-126) metadata.yaml (+12/-8) templates/start_conf.tmpl (+13/-0) test.py (+8/-3) | ||||
| To merge this branch: | bzr merge lp:~stub/charms/precise/postgresql/cleanups | ||||
| Related bugs: |
|
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Mark Mims (community) | Approve | ||
| Review via email: | |||
Commit message
Description of the change
Update documentation and code cleanups.
To post a comment you must log in.
- 72. By Stuart Bishop
-
Add missing template
Revision history for this message
| Mark Mims (mark-mims) : | # |
review: Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
| 1 | === modified file 'README.md' |
| 2 | --- README.md 2013-08-07 13:50:02 +0000 |
| 3 | +++ README.md 2013-08-23 09:40:09 +0000 |
| 4 | @@ -41,15 +41,6 @@ |
| 5 | maintains replication for you, using standard PostgreSQL streaming |
| 6 | replication. |
| 7 | |
| 8 | - - Multiple services linked using 'master'/'slave' relationships. A |
| 9 | - single service can be the 'master', and multiple services connected |
| 10 | - to this master in a 'slave' role. Each service can contain multiple |
| 11 | - units; the 'master' service will contain a single 'master' unit and |
| 12 | - remaining units all 'hot standby'. The 'slave' services will only |
| 13 | - contain 'hot standby' units. 'Cascading replication is not |
| 14 | - supported', so do not attempt to relate an existing 'slave' service |
| 15 | - as a 'master' to another service. |
| 16 | - |
| 17 | |
| 18 | To setup a single 'standalone' service:: |
| 19 | |
| 20 | @@ -61,41 +52,34 @@ |
| 21 | |
| 22 | juju add-unit pg-a |
| 23 | |
| 24 | -To deploy a new service containing a 'master' and a 'hot standby':: |
| 25 | - |
| 26 | - juju deploy -n 2 postgresql pg-b |
| 27 | - |
| 28 | - |
| 29 | -To relate a PostgreSQL service as a 'slave' of another PostgreSQL service. |
| 30 | -**Caution** - this destroys the existing databases in the pg-b service:: |
| 31 | - |
| 32 | - juju add-relation pg-a:master pg-b:slave |
| 33 | - |
| 34 | - |
| 35 | -To setup a client using a PostgreSQL database, in this case OpenERP and |
| 36 | -its web front end. Note that OpenERP requires an administrative level |
| 37 | -connection:: |
| 38 | +To deploy a new service containing a 'master' and two 'hot standbys':: |
| 39 | + |
| 40 | + juju deploy -n 3 postgresql pg-b |
| 41 | + |
| 42 | +You can remove units as normal. If the master unit is removed, failover |
| 43 | +occurs and the most up to date 'hot standby' is promoted to 'master'. |
| 44 | +The 'db-relation-changed' and 'db-admin-relation-changed' hooks are |
| 45 | +fired, letting clients adjust:: |
| 46 | + |
| 47 | + juju remove-unit pg-b/0 |
| 48 | + |
| 49 | + |
| 50 | +To setup a client using a PostgreSQL database, in this case a vanilla |
| 51 | +Django installation listening on port 8080:: |
| 52 | |
| 53 | juju deploy postgresql |
| 54 | - juju deploy postgresql pg-standby |
| 55 | - juju deploy openerp-web |
| 56 | - juju deploy openerp-server |
| 57 | - |
| 58 | - juju add-relation postgresql:master pg-standby:slave |
| 59 | - juju add-relation openerp-server:db postgresql:db-admin |
| 60 | - juju add-relation openerp-web openerp-server |
| 61 | - |
| 62 | - juju expose openerp-web |
| 63 | - juju expose openerp-server |
| 64 | + juju deploy python-django |
| 65 | + juju deploy gunicorn |
| 66 | + juju add-relation python-django postgresql:db |
| 67 | + juju add-relation python-django gunicorn |
| 68 | + juju expose python-django |
| 69 | |
| 70 | |
| 71 | ## Restrictions |
| 72 | |
| 73 | - Do not attempt to relate client charms to a PostgreSQL service |
| 74 | containing multiple units unless you know the charm supports |
| 75 | - a replicated service. You can use a 'master'/'slave' relationship |
| 76 | - to create a redundant copy of your database until the client charms |
| 77 | - are updated. |
| 78 | + a replicated service. |
| 79 | |
| 80 | - You cannot host multiple units in a single juju container. This is |
| 81 | problematic as some PostgreSQL features, such as tablespaces, use |
| 82 | @@ -103,10 +87,23 @@ |
| 83 | |
| 84 | # Interacting with the Postgresql Service |
| 85 | |
| 86 | -Typically, you just need to join a the `db` relation, and a user and database |
| 87 | -will be created for you. For more advanced uses, you can join the `db-admin` |
| 88 | -relation, and a super user will be created. Using this account, you can |
| 89 | -manipulate all other aspects of the database. |
| 90 | +At a minimum, you just need to join a the `db` relation, and a user and |
| 91 | +database will be created for you. For more complex environments, |
| 92 | +you can provide the `database` name allowing multiple services to share |
| 93 | +the same database. A client may also wish to defer its setup until the |
| 94 | +unit name is listed in `allowed-units`, to avoid attempting to connect |
| 95 | +to a database before it has been authorized. |
| 96 | + |
| 97 | +The `db-admin` relation may be used similarly to the `db` relation. |
| 98 | +The automatically generated user for `db-admin` relations is a |
| 99 | +PostgreSQL superuser. |
| 100 | + |
| 101 | +## During db-relation-joined |
| 102 | + |
| 103 | +### the client service provides: |
| 104 | + |
| 105 | +- `database`: Optional. The name of the database to use. The postgresql |
| 106 | + service will create it if necessary. |
| 107 | |
| 108 | ## During db-relation-changed |
| 109 | |
| 110 | |
| 111 | === modified file 'charm-helpers.yaml' |
| 112 | --- charm-helpers.yaml 2013-08-23 09:40:09 +0000 |
| 113 | +++ charm-helpers.yaml 2013-08-23 09:40:09 +0000 |
| 114 | @@ -1,4 +1,4 @@ |
| 115 | destination: hooks/charmhelpers |
| 116 | -branch: lp:charm-helpers |
| 117 | +branch: lp:~stub/charm-helpers/bug-1214793-service-wrappers |
| 118 | include: |
| 119 | - core |
| 120 | |
| 121 | === modified file 'hooks/charmhelpers/core/host.py' |
| 122 | --- hooks/charmhelpers/core/host.py 2013-08-23 09:40:09 +0000 |
| 123 | +++ hooks/charmhelpers/core/host.py 2013-08-23 09:40:09 +0000 |
| 124 | @@ -20,20 +20,22 @@ |
| 125 | |
| 126 | |
| 127 | def service_start(service_name): |
| 128 | - service('start', service_name) |
| 129 | + return service('start', service_name) |
| 130 | |
| 131 | |
| 132 | def service_stop(service_name): |
| 133 | - service('stop', service_name) |
| 134 | + return service('stop', service_name) |
| 135 | |
| 136 | |
| 137 | def service_restart(service_name): |
| 138 | - service('restart', service_name) |
| 139 | + return service('restart', service_name) |
| 140 | |
| 141 | |
| 142 | def service_reload(service_name, restart_on_failure=False): |
| 143 | - if not service('reload', service_name) and restart_on_failure: |
| 144 | - service('restart', service_name) |
| 145 | + service_result = service('reload', service_name) |
| 146 | + if not service_result and restart_on_failure: |
| 147 | + service_result = service('restart', service_name) |
| 148 | + return service_result |
| 149 | |
| 150 | |
| 151 | def service(action, service_name): |
| 152 | |
| 153 | === modified file 'hooks/hooks.py' |
| 154 | --- hooks/hooks.py 2013-08-23 09:40:09 +0000 |
| 155 | +++ hooks/hooks.py 2013-08-23 09:40:09 +0000 |
| 156 | @@ -32,20 +32,13 @@ |
| 157 | return Template(*args, **kw) |
| 158 | |
| 159 | |
| 160 | -def write_file(path, contents, owner='root', group='root', perms=0o444): |
| 161 | - '''Temporary alternative to charm-helpers write_file(). |
| 162 | - |
| 163 | - charm-helpers' write_file() magic makes it useless for any file |
| 164 | - containing curly brackets, so work around for now until the feature |
| 165 | - can be discussed. |
| 166 | - ''' |
| 167 | - log("Writing file {} {}:{} {:o}".format(path, owner, group, perms)) |
| 168 | - uid = getpwnam(owner).pw_uid |
| 169 | - gid = getgrnam(group).gr_gid |
| 170 | - dest_fd = os.open(path, os.O_WRONLY | os.O_TRUNC | os.O_CREAT, perms) |
| 171 | - os.fchown(dest_fd, uid, gid) |
| 172 | - with os.fdopen(dest_fd, 'w') as destfile: |
| 173 | - destfile.write(str(contents)) |
| 174 | +def log(msg, lvl=INFO): |
| 175 | + # Per Bug #1208787, log messages sent via juju-log are being lost. |
| 176 | + # Spit messages out to a log file to work around the problem. |
| 177 | + myname = hookenv.local_unit().replace('/', '-') |
| 178 | + with open('/tmp/{}-debug.log'.format(myname), 'a') as f: |
| 179 | + f.write('{}: {}\n'.format(lvl, msg)) |
| 180 | + hookenv.log(msg, lvl) |
| 181 | |
| 182 | |
| 183 | class State(dict): |
| 184 | @@ -87,8 +80,6 @@ |
| 185 | |
| 186 | replication_state = dict(client_state) |
| 187 | |
| 188 | - add(replication_state, 'public_ssh_key') |
| 189 | - add(replication_state, 'ssh_host_key') |
| 190 | add(replication_state, 'replication_password') |
| 191 | add(replication_state, 'wal_received_offset') |
| 192 | add(replication_state, 'following') |
| 193 | @@ -189,30 +180,18 @@ |
| 194 | return output |
| 195 | |
| 196 | |
| 197 | -#------------------------------------------------------------------------------ |
| 198 | -# Enable/disable service start by manipulating policy-rc.d |
| 199 | -#------------------------------------------------------------------------------ |
| 200 | -def enable_service_start(service): |
| 201 | - ### NOTE: doesn't implement per-service, this can be an issue |
| 202 | - ### for colocated charms (subordinates) |
| 203 | - log("enabling {} start by policy-rc.d".format(service)) |
| 204 | - if os.path.exists('/usr/sbin/policy-rc.d'): |
| 205 | - os.unlink('/usr/sbin/policy-rc.d') |
| 206 | - return True |
| 207 | - return False |
| 208 | - |
| 209 | - |
| 210 | -def disable_service_start(service): |
| 211 | - log("disabling {} start by policy-rc.d".format(service)) |
| 212 | - policy_rc = '/usr/sbin/policy-rc.d' |
| 213 | - policy_rc_tmp = "{}.tmp".format(policy_rc) |
| 214 | - open(policy_rc_tmp, 'w').write("""#!/bin/bash |
| 215 | -[[ "$1"-"$2" == %s-start ]] && exit 101 |
| 216 | -exit 0 |
| 217 | -EOF |
| 218 | -""" % service) |
| 219 | - os.chmod(policy_rc_tmp, 0755) |
| 220 | - os.rename(policy_rc_tmp, policy_rc) |
| 221 | +def postgresql_autostart(enabled): |
| 222 | + if enabled: |
| 223 | + log("Enabling PostgreSQL startup in {}".format(startup_file)) |
| 224 | + mode = 'auto' |
| 225 | + else: |
| 226 | + log("Disabling PostgreSQL startup in {}".format(startup_file)) |
| 227 | + mode = 'manual' |
| 228 | + startup_file = os.path.join(postgresql_config_dir, 'start.conf') |
| 229 | + contents = Template(open("templates/start_conf.tmpl").read()).render( |
| 230 | + {'mode': mode}) |
| 231 | + host.write_file( |
| 232 | + startup_file, contents, 'postgres', 'postgres', perms=0o644) |
| 233 | |
| 234 | |
| 235 | def run(command, exit_on_error=True): |
| 236 | @@ -229,10 +208,6 @@ |
| 237 | raise |
| 238 | |
| 239 | |
| 240 | -#------------------------------------------------------------------------------ |
| 241 | -# postgresql_stop, postgresql_start, postgresql_is_running: |
| 242 | -# wrappers over invoke-rc.d, with extra check for postgresql_is_running() |
| 243 | -#------------------------------------------------------------------------------ |
| 244 | def postgresql_is_running(): |
| 245 | # init script always return true (9.1), add extra check to make it useful |
| 246 | status, output = commands.getstatusoutput("invoke-rc.d postgresql status") |
| 247 | @@ -244,17 +219,12 @@ |
| 248 | |
| 249 | |
| 250 | def postgresql_stop(): |
| 251 | - status, output = commands.getstatusoutput("invoke-rc.d postgresql stop") |
| 252 | - if status != 0: |
| 253 | - return False |
| 254 | + host.service_stop('postgresql') |
| 255 | return not postgresql_is_running() |
| 256 | |
| 257 | |
| 258 | def postgresql_start(): |
| 259 | - status, output = commands.getstatusoutput("invoke-rc.d postgresql start") |
| 260 | - if status != 0: |
| 261 | - log(output, CRITICAL) |
| 262 | - return False |
| 263 | + host.service_start('postgresql') |
| 264 | return postgresql_is_running() |
| 265 | |
| 266 | |
| 267 | @@ -275,12 +245,9 @@ |
| 268 | last_warning = time.time() |
| 269 | time.sleep(5) |
| 270 | |
| 271 | - status, output = \ |
| 272 | - commands.getstatusoutput("invoke-rc.d postgresql restart") |
| 273 | - if status != 0: |
| 274 | - return False |
| 275 | + return host.service_restart('postgresql') |
| 276 | else: |
| 277 | - postgresql_start() |
| 278 | + return host.service_start('postgresql') |
| 279 | |
| 280 | # Store a copy of our known live configuration so |
| 281 | # postgresql_reload_or_restart() can make good choices. |
| 282 | @@ -406,7 +373,7 @@ |
| 283 | # Return it as pg_config |
| 284 | pg_config = Template( |
| 285 | open("templates/postgresql.conf.tmpl").read()).render(config_data) |
| 286 | - write_file( |
| 287 | + host.write_file( |
| 288 | postgresql_config, pg_config, |
| 289 | owner="postgres", group="postgres", perms=0600) |
| 290 | |
| 291 | @@ -419,7 +386,7 @@ |
| 292 | ident_data = {} |
| 293 | pg_ident_template = Template( |
| 294 | open("templates/pg_ident.conf.tmpl").read()) |
| 295 | - write_file( |
| 296 | + host.write_file( |
| 297 | postgresql_ident, pg_ident_template.render(ident_data), |
| 298 | owner="postgres", group="postgres", perms=0600) |
| 299 | |
| 300 | @@ -520,7 +487,7 @@ |
| 301 | relation_data.append(local_replication) |
| 302 | |
| 303 | pg_hba_template = Template(open("templates/pg_hba.conf.tmpl").read()) |
| 304 | - write_file( |
| 305 | + host.write_file( |
| 306 | postgresql_hba, pg_hba_template.render(access_list=relation_data), |
| 307 | owner="postgres", group="postgres", perms=0600) |
| 308 | postgresql_reload() |
| 309 | @@ -542,7 +509,7 @@ |
| 310 | } |
| 311 | crontab_template = Template( |
| 312 | open("templates/postgres.cron.tmpl").read()).render(crontab_data) |
| 313 | - write_file('/etc/cron.d/postgres', crontab_template, perms=0600) |
| 314 | + host.write_file('/etc/cron.d/postgres', crontab_template, perms=0600) |
| 315 | |
| 316 | |
| 317 | def create_recovery_conf(master_host, restart_on_change=False): |
| 318 | @@ -557,7 +524,7 @@ |
| 319 | 'host': master_host, |
| 320 | 'password': local_state['replication_password']}) |
| 321 | log(recovery_conf, DEBUG) |
| 322 | - write_file( |
| 323 | + host.write_file( |
| 324 | os.path.join(postgresql_cluster_dir, 'recovery.conf'), |
| 325 | recovery_conf, owner="postgres", group="postgres", perms=0o600) |
| 326 | |
| 327 | @@ -782,7 +749,7 @@ |
| 328 | volid = volume_get_volume_id() |
| 329 | if not volid: |
| 330 | ## Invalid configuration (whether ephemeral, or permanent) |
| 331 | - disable_service_start("postgresql") |
| 332 | + postgresql_autostart(False) |
| 333 | postgresql_stop() |
| 334 | mounts = volume_get_all_mounted() |
| 335 | if mounts: |
| 336 | @@ -797,9 +764,9 @@ |
| 337 | ## config_changed_volume_apply will stop the service if it founds |
| 338 | ## it necessary, ie: new volume setup |
| 339 | if config_changed_volume_apply(): |
| 340 | - enable_service_start("postgresql") |
| 341 | + postgresql_autostart(True) |
| 342 | else: |
| 343 | - disable_service_start("postgresql") |
| 344 | + postgresql_autostart(False) |
| 345 | postgresql_stop() |
| 346 | mounts = volume_get_all_mounted() |
| 347 | if mounts: |
| 348 | @@ -863,10 +830,10 @@ |
| 349 | open("templates/dump-pg-db.tmpl").read()).render(paths) |
| 350 | backup_job = Template( |
| 351 | open("templates/pg_backup_job.tmpl").read()).render(paths) |
| 352 | - write_file( |
| 353 | + host.write_file( |
| 354 | '{}/dump-pg-db'.format(postgresql_scripts_dir), |
| 355 | dump_script, perms=0755) |
| 356 | - write_file( |
| 357 | + host.write_file( |
| 358 | '{}/pg_backup_job'.format(postgresql_scripts_dir), |
| 359 | backup_job, perms=0755) |
| 360 | install_postgresql_crontab(postgresql_crontab) |
| 361 | @@ -1256,56 +1223,6 @@ |
| 362 | local_state.save() |
| 363 | |
| 364 | |
| 365 | -def ensure_local_ssh(): |
| 366 | - """Generate SSH keys for postgres user. |
| 367 | - |
| 368 | - The public key is stored in public_ssh_key on the relation. |
| 369 | - |
| 370 | - Bidirectional SSH access is required by repmgr. |
| 371 | - """ |
| 372 | - comment = 'repmgr key for {}'.format(os.environ['JUJU_UNIT_NAME']) |
| 373 | - if not os.path.isdir(postgres_ssh_dir): |
| 374 | - host.mkdir(postgres_ssh_dir, "postgres", "postgres", 0o700) |
| 375 | - if not os.path.exists(postgres_ssh_private_key): |
| 376 | - run("sudo -u postgres -H ssh-keygen -q -t rsa -C '{}' -N '' " |
| 377 | - "-f '{}'".format(comment, postgres_ssh_private_key)) |
| 378 | - public_key = open(postgres_ssh_public_key, 'r').read().strip() |
| 379 | - host_key = open('/etc/ssh/ssh_host_ecdsa_key.pub').read().strip() |
| 380 | - local_state['public_ssh_key'] = public_key |
| 381 | - local_state['ssh_host_key'] = host_key |
| 382 | - local_state.publish() |
| 383 | - |
| 384 | - |
| 385 | -def authorize_remote_ssh(): |
| 386 | - """Generate the SSH authorized_keys file.""" |
| 387 | - authorized_units = set() |
| 388 | - authorized_keys = set() |
| 389 | - known_hosts = set() |
| 390 | - for relid in hookenv.relation_ids('replication'): |
| 391 | - for unit in hookenv.related_units(relid): |
| 392 | - relation = hookenv.relation_get(unit=unit, rid=relid) |
| 393 | - public_key = relation.get('public_ssh_key', None) |
| 394 | - if public_key: |
| 395 | - authorized_units.add(unit) |
| 396 | - authorized_keys.add(public_key) |
| 397 | - known_hosts.add('{} {}'.format( |
| 398 | - relation['private-address'], relation['ssh_host_key'])) |
| 399 | - |
| 400 | - # Generate known_hosts |
| 401 | - write_file( |
| 402 | - postgres_ssh_known_hosts, '\n'.join(known_hosts), |
| 403 | - owner="postgres", group="postgres", perms=0o644) |
| 404 | - |
| 405 | - # Generate authorized_keys |
| 406 | - write_file( |
| 407 | - postgres_ssh_authorized_keys, '\n'.join(authorized_keys), |
| 408 | - owner="postgres", group="postgres", perms=0o400) |
| 409 | - |
| 410 | - # Publish details, so relation knows they have been granted access. |
| 411 | - local_state['authorized'] = authorized_units |
| 412 | - local_state.publish() |
| 413 | - |
| 414 | - |
| 415 | @contextmanager |
| 416 | def pgpass(): |
| 417 | passwords = {} |
| 418 | @@ -1665,6 +1582,16 @@ |
| 419 | config_changed() |
| 420 | |
| 421 | |
| 422 | +@contextmanager |
| 423 | +def switch_cwd(new_working_directory): |
| 424 | + org_dir = os.getcwd() |
| 425 | + os.chdir(new_working_directory) |
| 426 | + try: |
| 427 | + yield new_working_directory |
| 428 | + finally: |
| 429 | + os.chdir(org_dir) |
| 430 | + |
| 431 | + |
| 432 | def clone_database(master_unit, master_host): |
| 433 | with pgpass(): |
| 434 | postgresql_stop() |
| 435 | @@ -1680,7 +1607,10 @@ |
| 436 | shutil.rmtree(postgresql_cluster_dir) |
| 437 | |
| 438 | try: |
| 439 | - output = subprocess.check_output(cmd) |
| 440 | + # Change directory the postgres user can read. |
| 441 | + with switch_cwd('/tmp'): |
| 442 | + # Run the sudo command. |
| 443 | + output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) |
| 444 | log(output, DEBUG) |
| 445 | # Debian by default expects SSL certificates in the datadir. |
| 446 | os.symlink( |
| 447 | @@ -1690,7 +1620,7 @@ |
| 448 | '/etc/ssl/private/ssl-cert-snakeoil.key', |
| 449 | os.path.join(postgresql_cluster_dir, 'server.key')) |
| 450 | create_recovery_conf(master_host) |
| 451 | - except subprocess.CalledProcessError, x: |
| 452 | + except subprocess.CalledProcessError as x: |
| 453 | # We failed, and this cluster is broken. Rebuild a |
| 454 | # working cluster so start/stop etc. works and we |
| 455 | # can retry hooks again. Even assuming the charm is |
| 456 | @@ -1817,7 +1747,7 @@ |
| 457 | check_file_age -w {} -c {} -f {}".format(warn_age, crit_age, backup_log)) |
| 458 | |
| 459 | if os.path.isfile('/etc/init.d/nagios-nrpe-server'): |
| 460 | - subprocess.call(['service', 'nagios-nrpe-server', 'reload']) |
| 461 | + host.service_reload('nagios-nrpe-server') |
| 462 | |
| 463 | |
| 464 | ############################################################################### |
| 465 | @@ -1841,12 +1771,6 @@ |
| 466 | config_data['backup_dir'].strip() or |
| 467 | os.path.join(postgresql_data_dir, 'backups')) |
| 468 | postgresql_logs_dir = os.path.join(postgresql_data_dir, 'logs') |
| 469 | -postgres_ssh_dir = os.path.expanduser('~postgres/.ssh') |
| 470 | -postgres_ssh_public_key = os.path.join(postgres_ssh_dir, 'id_rsa.pub') |
| 471 | -postgres_ssh_private_key = os.path.join(postgres_ssh_dir, 'id_rsa') |
| 472 | -postgres_ssh_authorized_keys = os.path.join(postgres_ssh_dir, |
| 473 | - 'authorized_keys') |
| 474 | -postgres_ssh_known_hosts = os.path.join(postgres_ssh_dir, 'known_hosts') |
| 475 | hook_name = os.path.basename(sys.argv[0]) |
| 476 | replication_relation_types = ['master', 'slave', 'replication'] |
| 477 | local_state = State('local_state.pickle') |
| 478 | @@ -1859,5 +1783,4 @@ |
| 479 | if hookenv.relation_id(): |
| 480 | log("Relation {} with {}".format( |
| 481 | hookenv.relation_id(), hookenv.remote_unit())) |
| 482 | - |
| 483 | hooks.execute(sys.argv) |
| 484 | |
| 485 | === modified file 'metadata.yaml' |
| 486 | --- metadata.yaml 2013-06-25 11:29:13 +0000 |
| 487 | +++ metadata.yaml 2013-08-23 09:40:09 +0000 |
| 488 | @@ -1,13 +1,17 @@ |
| 489 | name: postgresql |
| 490 | -summary: "object-relational SQL database (supported version)" |
| 491 | +summary: "PostgreSQL object-relational SQL database (supported version)" |
| 492 | description: | |
| 493 | - PostgreSQL is a fully featured object-relational database management |
| 494 | - system. It supports a large part of the SQL standard and is designed |
| 495 | - to be extensible by users in many aspects. Some of the features are: |
| 496 | - ACID transactions, foreign keys, views, sequences, subqueries, |
| 497 | - triggers, user-defined types and functions, outer joins, multiversion |
| 498 | - concurrency control. Graphical user interfaces and bindings for many |
| 499 | - programming languages are available as well. |
| 500 | + PostgreSQL is a powerful, open source object-relational database system. |
| 501 | + It has more than 15 years of active development and a proven |
| 502 | + architecture that has earned it a strong reputation for reliability, |
| 503 | + data integrity, and correctness. It is fully ACID compliant, has full |
| 504 | + support for foreign keys, joins, views, triggers, and stored procedures |
| 505 | + (in multiple languages). It includes most SQL:2008 data types, including |
| 506 | + INTEGER, NUMERIC, BOOLEAN, CHAR, VARCHAR, DATE, INTERVAL, and TIMESTAMP. |
| 507 | + It also supports storage of binary large objects, including pictures, |
| 508 | + sounds, or video. It has native programming interfaces for C/C++, Java, |
| 509 | + .Net, Perl, Python, Ruby, Tcl, ODBC, among others, and exceptional |
| 510 | + documentation (http://www.postgresql.org/docs/manuals/). |
| 511 | maintainer: Stuart Bishop <stuart.bishop@canonical.com> |
| 512 | categories: |
| 513 | - databases |
| 514 | |
| 515 | === added file 'templates/start_conf.tmpl' |
| 516 | --- templates/start_conf.tmpl 1970-01-01 00:00:00 +0000 |
| 517 | +++ templates/start_conf.tmpl 2013-08-23 09:40:09 +0000 |
| 518 | @@ -0,0 +1,13 @@ |
| 519 | +# |
| 520 | +# This file is managed by Juju. |
| 521 | +# |
| 522 | +# Automatic startup configuration |
| 523 | +# auto: automatically start/stop the cluster in the init script |
| 524 | +# manual: do not start/stop in init scripts, but allow manual startup with |
| 525 | +# pg_ctlcluster |
| 526 | +# disabled: do not allow manual startup with pg_ctlcluster (this can be easily |
| 527 | +# circumvented and is only meant to be a small protection for |
| 528 | +# accidents). |
| 529 | + |
| 530 | +{{mode}} |
| 531 | + |
| 532 | |
| 533 | === modified file 'test.py' |
| 534 | --- test.py 2013-08-23 09:40:09 +0000 |
| 535 | +++ test.py 2013-08-23 09:40:09 +0000 |
| 536 | @@ -128,13 +128,19 @@ |
| 537 | # enough that our system is probably stable. This means we have |
| 538 | # extremely slow and flaky tests, but that is possibly better |
| 539 | # than no tests. |
| 540 | - time.sleep(30) |
| 541 | + time.sleep(45) |
| 542 | |
| 543 | def setUp(self): |
| 544 | DEBUG("JujuFixture.setUp()") |
| 545 | super(JujuFixture, self).setUp() |
| 546 | self.reset() |
| 547 | - self.addCleanup(self.reset) |
| 548 | + # Optionally, don't teardown services and machines after running |
| 549 | + # a test. If a subsequent test is run, they will be torn down at |
| 550 | + # that point. This option is only useful when running a single |
| 551 | + # test, or when the test harness is set to abort after the first |
| 552 | + # failed test. |
| 553 | + if not os.environ.get('TEST_DONT_TEARDOWN_JUJU', False): |
| 554 | + self.addCleanup(self.reset) |
| 555 | |
| 556 | def reset(self): |
| 557 | DEBUG("JujuFixture.reset()") |
| 558 | @@ -286,7 +292,6 @@ |
| 559 | result = self.sql('SELECT TRUE', dbname='postgres') |
| 560 | self.assertEqual(result, [['t']]) |
| 561 | |
| 562 | - |
| 563 | def is_master(self, postgres_unit, dbname=None): |
| 564 | is_master = self.sql( |
| 565 | 'SELECT NOT pg_is_in_recovery()', |
| 566 | |
| 567 | === added directory 'tests' |
| 568 | === added symlink 'tests/01_pg_testsuite.test' |
| 569 | === target is u'../test.py' |