Skip to content

Commit

Permalink
Add automatic cleanup up conflicted config files
Browse files Browse the repository at this point in the history
  • Loading branch information
jacebrowning committed Jul 3, 2015
1 parent f85f03f commit 270628d
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 115 deletions.
113 changes: 6 additions & 107 deletions mine/cli.py
Expand Up @@ -114,7 +114,7 @@ def run(path=None, cleanup=True, delay=None,
log.info("current computer: %s", computer)

if cleanup:
clean(config, status)
data.clean(config, status)
if delete:
return services.delete_conflicts(root, force=force)

Expand All @@ -123,11 +123,13 @@ def run(path=None, cleanup=True, delay=None,
elif switch:
switch = config.computers.match(switch)
if switch:
queue(config, status, switch)
data.queue(config, status, switch)

while True:
launch(config, status, computer, manager)
update(config, status, computer, manager)
services.delete_conflicts(root, config_only=True, force=True)

data.launch(config, status, computer, manager)
data.update(config, status, computer, manager)

if delay is None:
break
Expand All @@ -137,108 +139,5 @@ def run(path=None, cleanup=True, delay=None,
return True


# TODO: consider moving this logic to `data`
def clean(config, status):
"""Remove undefined applications and computers."""
log.info("cleaning up applications and computers...")
for appstatus in status.applications.copy():
if not config.applications.find(appstatus.application):
status.applications.remove(appstatus)
log.info("removed application: %s", appstatus)
else:
for computerstate in appstatus.computers.copy():
if not config.computers.find(computerstate.computer):
appstatus.computers.remove(computerstate)
log.info("removed computer: %s", computerstate)


# TODO: consider moving this logic to `data`
def queue(config, status, computer):
"""Queue applications for launch."""
log.info("queuing applications for launch...")
for application in config.applications:
if application.queued:
log.debug("queuing %s on %s...", application, computer)
status.queue(application, computer)
else:
log.debug("skipping %s (not queued)...", application)


# TODO: consider moving this logic to `data`
def launch(config, status, computer, manager):
"""Launch applications that have been queued."""
log.info("launching queued applications...")
for app_status in status.applications:
if app_status.next:
application = config.applications.get(app_status.application)
show_queued(application, app_status.next)
if app_status.next == computer:
latest = status.get_latest(application)
if latest in (computer, None):
if not manager.is_running(application):
manager.start(application)
app_status.next = None
else:
show_waiting(application, latest)
elif manager.is_running(application):
manager.stop(application)


# TODO: consider moving this logic to `data`
def update(config, status, computer, manager):
"""Update each application's status."""
log.info("recording application status...")
for application in config.applications:
if manager.is_running(application):
latest = status.get_latest(application)
if computer != latest:
if status.is_running(application, computer):
# case 1: application just launched remotely
manager.stop(application)
status.stop(application, computer)
show_running(application, latest)
show_stopped(application, computer)
else:
# case 2: application just launched locally
status.start(application, computer)
show_running(application, computer)
else:
# case 3: application already running locally
pass
else:
if status.is_running(application, computer):
# case 4: application just closed locally
status.stop(application, computer)
show_stopped(application, computer)
else:
# case 5: application already closed locally
pass


def show_queued(application, computer):
"""Display the state of a queued application."""
print("{} is queued for {}".format(application, computer))


def show_waiting(application, computer):
"""Display the old state of a running application."""
print("{} is still running on {}".format(application, computer))


def show_running(application, computer):
"""Display the new state of a running application."""
print("{} is now running on {}".format(application, computer))


def show_started(application, computer):
"""Display the new state of a started application."""
print("{} is now started on {}".format(application, computer))


def show_stopped(application, computer):
"""Display the new state of a stopped application."""
print("{} is now stopped on {}".format(application, computer))


if __name__ == '__main__': # pragma: no cover (manual test)
main()
99 changes: 99 additions & 0 deletions mine/data.py
Expand Up @@ -22,3 +22,102 @@ def __init__(self):

def __repr__(self):
return "settings"

@staticmethod
def clean(config, status):
"""Remove undefined applications and computers."""
log.info("cleaning up applications and computers...")
for appstatus in status.applications.copy():
if not config.applications.find(appstatus.application):
status.applications.remove(appstatus)
log.info("removed application: %s", appstatus)
else:
for computerstate in appstatus.computers.copy():
if not config.computers.find(computerstate.computer):
appstatus.computers.remove(computerstate)
log.info("removed computer: %s", computerstate)

@staticmethod
def queue(config, status, computer):
"""Queue applications for launch."""
log.info("queuing applications for launch...")
for application in config.applications:
if application.queued:
log.debug("queuing %s on %s...", application, computer)
status.queue(application, computer)
else:
log.debug("skipping %s (not queued)...", application)

@staticmethod
def launch(config, status, computer, manager):
"""Launch applications that have been queued."""
log.info("launching queued applications...")
for app_status in status.applications:
if app_status.next:
application = config.applications.get(app_status.application)
show_queued(application, app_status.next)
if app_status.next == computer:
latest = status.get_latest(application)
if latest in (computer, None):
if not manager.is_running(application):
manager.start(application)
app_status.next = None
else:
show_waiting(application, latest)
elif manager.is_running(application):
manager.stop(application)

@staticmethod
def update(config, status, computer, manager):
"""Update each application's status."""
log.info("recording application status...")
for application in config.applications:
if manager.is_running(application):
latest = status.get_latest(application)
if computer != latest:
if status.is_running(application, computer):
# case 1: application just launched remotely
manager.stop(application)
status.stop(application, computer)
show_running(application, latest)
show_stopped(application, computer)
else:
# case 2: application just launched locally
status.start(application, computer)
show_running(application, computer)
else:
# case 3: application already running locally
pass
else:
if status.is_running(application, computer):
# case 4: application just closed locally
status.stop(application, computer)
show_stopped(application, computer)
else:
# case 5: application already closed locally
pass


def show_queued(application, computer):
"""Display the state of a queued application."""
print("{} is queued for {}".format(application, computer))


def show_waiting(application, computer):
"""Display the old state of a running application."""
print("{} is still running on {}".format(application, computer))


def show_running(application, computer):
"""Display the new state of a running application."""
print("{} is now running on {}".format(application, computer))


def show_started(application, computer):
"""Display the new state of a started application."""
print("{} is now started on {}".format(application, computer))


def show_stopped(application, computer):
"""Display the new state of a stopped application."""
print("{} is now stopped on {}".format(application, computer))
19 changes: 11 additions & 8 deletions mine/services.py
Expand Up @@ -15,8 +15,10 @@
'Dropbox',
'Dropbox (Personal)',
)
CONFLICT = r".+\(.+'s conflicted copy \d+-\d+-\d+.*\).*"
FILENAME = 'mine.yml'
CONFIG = 'mine.yml'
CONFLICT_BASE = r"{} \(.+'s conflicted copy \d+-\d+-\d+.*\).*"
CONFLICT_ANY = CONFLICT_BASE.format(".+")
CONFLICT_CONFIG = CONFLICT_BASE.format("mine")
DEPTH = 3 # number of levels to search for the settings file

log = common.logger(__name__)
Expand Down Expand Up @@ -47,19 +49,19 @@ def find_config_path(top=None, root=None):
top = top or _default_top()
root = root or find_root(top=top)

log.debug("looking for '%s' in '%s'...", FILENAME, root)
log.debug("looking for '%s' in '%s'...", CONFIG, root)
for dirpath, dirnames, _, in os.walk(root):
depth = dirpath.count(os.path.sep) - root.count(os.path.sep)
if depth >= DEPTH:
del dirnames[:]
continue
path = os.path.join(dirpath, FILENAME)
path = os.path.join(dirpath, CONFIG)
if os.path.isfile(path) and \
not os.path.isfile(os.path.join(path, 'setup.py')):
log.info("found settings file: %s", path)
return path

raise EnvironmentError("no '{}' file found".format(FILENAME))
raise EnvironmentError("no '{}' file found".format(CONFIG))


def _default_top():
Expand All @@ -75,13 +77,14 @@ def _default_top():
raise EnvironmentError("no home directory found for '{}'".format(username))


def delete_conflicts(root=None, force=False):
def delete_conflicts(root=None, config_only=False, force=False):
"""Delete all files with conflicted filenames."""
root = root or find_root()

log.info("%s conflicted files...", 'deleting' if force else 'displaying')
log.debug("pattern: %r", CONFLICT)
regex = re.compile(CONFLICT)
pattern = CONFLICT_CONFIG if config_only else CONFLICT_ANY
log.debug("pattern: %r", pattern)
regex = re.compile(pattern)
count = 0
for dirname, _, filenames in os.walk(root):
for filename in filenames:
Expand Down

0 comments on commit 270628d

Please sign in to comment.