Merge lp:~jml/juju-jitsu/deploy-one into lp:juju-jitsu

Proposed by Jonathan Lange
Status: Needs review
Proposed branch: lp:~jml/juju-jitsu/deploy-one
Merge into: lp:juju-jitsu
Diff against target: 236 lines (+202/-1)
3 files modified
sub-commands/Makefile.am (+2/-0)
sub-commands/deploy-one (+200/-0)
sub-commands/deploy-to (+0/-1)
To merge this branch: bzr merge lp:~jml/juju-jitsu/deploy-one
Reviewer Review Type Date Requested Status
Clint Byrum (community) Needs Fixing
Review via email: mp+128061@code.launchpad.net

Commit message

Add deploy-one subcommand. Deploys one instance of a service.

Description of the change

Hello!

A thing I do a lot when developing charms is this:

  juju deploy $CHARM_NAME
  jitsu watch $SERVICE_NAME --x-state pending --num-units 1
  juju status

And then in another terminal:

  juju debug-log

Because I was raised by humans, I've acquired their trait of being easily
bored by doing the same things over and over.

This branch adds a command that does pretty much all of that:

  jitsu deploy-one --debug $CHARM_NAME

Combined with tools like 'alert' or <http://mumak.net/undistract-me>, this
takes much of the pain out of iterating on charms.

Known issues:

 - Figuring out the default service name from the charm name is
   somewhat kludgy.

 - Always exits with 0, even if the charm did not deploy

cheers,
jml

To post a comment you must log in.
Revision history for this message
Clint Byrum (clint-fewbar) wrote :

Actually there is one issue, please fix. Add deploy-one to sub-commands/Makefile.am

review: Needs Fixing
lp:~jml/juju-jitsu/deploy-one updated
84. By Jonathan Lange

Add deploy-one to sub-commands.

85. By Jonathan Lange

Dicking about with documentation.

Revision history for this message
Jonathan Lange (jml) wrote :

Have fixed sub-commands/Makefile.am. I tried to make the --help command work better, but I couldn't actually get _any_ --help to work when run for my branch.

cheers,
jml

Revision history for this message
Clint Byrum (clint-fewbar) wrote :

  File "sub-commands/deploy-one", line 40
    description="deploy a single unit of a service"))
                                                    ^
SyntaxError: invalid syntax

and when that is addressed

Traceback (most recent call last):
  File "sub-commands/deploy-one", line 201, in <module>
    main()
  File "sub-commands/deploy-one", line 179, in main
    parser = get_parser()
  File "sub-commands/deploy-one", line 38, in get_parser
    parser = ArgumentParser(
NameError: global name 'ArgumentParser' is not defined

And when *that* is addressed..

Traceback (most recent call last):
  File "sub-commands/deploy-one", line 201, in <module>
    main()
  File "sub-commands/deploy-one", line 179, in main
    parser = get_parser()
  File "sub-commands/deploy-one", line 40, in get_parser
    description="deploy a single unit of a service")
TypeError: __init__() got an unexpected keyword argument 'help'

review: Needs Fixing
lp:~jml/juju-jitsu/deploy-one updated
86. By Jonathan Lange

Fix a bunch of trivial syntax errors

Revision history for this message
Jonathan Lange (jml) wrote :

On 9 October 2012 18:53, Clint Byrum <email address hidden> wrote:
> Review: Needs Fixing
>
> File "sub-commands/deploy-one", line 40
> description="deploy a single unit of a service"))
> ^
> SyntaxError: invalid syntax
>
> and when that is addressed
>
> Traceback (most recent call last):
> File "sub-commands/deploy-one", line 201, in <module>
> main()
> File "sub-commands/deploy-one", line 179, in main
> parser = get_parser()
> File "sub-commands/deploy-one", line 38, in get_parser
> parser = ArgumentParser(
> NameError: global name 'ArgumentParser' is not defined
>
> And when *that* is addressed..
>
> Traceback (most recent call last):
> File "sub-commands/deploy-one", line 201, in <module>
> main()
> File "sub-commands/deploy-one", line 179, in main
> parser = get_parser()
> File "sub-commands/deploy-one", line 40, in get_parser
> description="deploy a single unit of a service")
> TypeError: __init__() got an unexpected keyword argument 'help'
>
> --
> https://code.launchpad.net/~jml/juju-jitsu/deploy-one/+merge/128061
> You are the owner of lp:~jml/juju-jitsu/deploy-one.

All three fixed. Sorry about the cock-up, I think it was due to a
poorly-reverted attempt to integrate better with --help.

jml

Unmerged revisions

86. By Jonathan Lange

Fix a bunch of trivial syntax errors

85. By Jonathan Lange

Dicking about with documentation.

84. By Jonathan Lange

Add deploy-one to sub-commands.

83. By Jonathan Lange

More docs & sanity checking.

82. By Jonathan Lange

Dump status at the end.

81. By Jonathan Lange

Tidy up a little. Don't show debug log by default

80. By Jonathan Lange

First cut of deploy-one.

79. By Jonathan Lange

Flakes

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'sub-commands/Makefile.am'
--- sub-commands/Makefile.am 2012-08-09 16:40:53 +0000
+++ sub-commands/Makefile.am 2012-10-19 11:07:24 +0000
@@ -3,6 +3,7 @@
3EXTRA_DIST = aiki \3EXTRA_DIST = aiki \
4 capfile \4 capfile \
5 deploy \5 deploy \
6 deploy-one \
6 deploy-to \7 deploy-to \
7 get-service-info \8 get-service-info \
8 get-unit-info \9 get-unit-info \
@@ -20,6 +21,7 @@
2021
21subcommandsexec_SCRIPTS = capfile \22subcommandsexec_SCRIPTS = capfile \
22 deploy \23 deploy \
24 deploy-one \
23 deploy-to \25 deploy-to \
24 get-service-info \26 get-service-info \
25 get-unit-info \27 get-unit-info \
2628
=== added file 'sub-commands/deploy-one'
--- sub-commands/deploy-one 1970-01-01 00:00:00 +0000
+++ sub-commands/deploy-one 2012-10-19 11:07:24 +0000
@@ -0,0 +1,200 @@
1#!/usr/bin/python
2#
3# deploy-one - deploy one instance of a service
4# author - Jonathan M. Lange <jml@mumak.net>
5
6# Copyright 2012 Canonical Ltd. All Rights Reserved
7#
8# This program is free software: you can redistribute it and/or modify
9# it under the terms of the GNU Affero General Public License as published by
10# the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU Affero General Public License for more details.
17#
18# You should have received a copy of the GNU Affero General Public License
19# along with this program. If not, see <http://www.gnu.org/licenses/>.
20
21"""deploy-one - deploy one instance of a service
22
23Primarily intended for use while developing charms. This command will open a
24tunnel, deploy a service, (optionally) show the debug log, and then terminate
25when the service has either deployed or failed to deploy.
26"""
27
28import argparse
29from contextlib import contextmanager
30import os
31import subprocess
32import sys
33
34from juju.control.utils import expand_path
35
36
37def get_parser():
38 parser = argparse.ArgumentParser(
39 description="deploy a single unit of a service")
40
41 parser.add_argument(
42 "--environment", "-e",
43 default=os.environ.get("JUJU_ENV"),
44 help="Environment to deploy the charm in.")
45
46 parser.add_argument(
47 "--repository",
48 help="Directory for charm lookup and retrieval",
49 default=os.environ.get("JUJU_REPOSITORY"),
50 type=expand_path)
51
52 parser.add_argument(
53 "--config",
54 help="YAML file containing service options")
55
56 parser.add_argument(
57 "--debug", action="store_true",
58 help="Whether or not to show the debug log")
59
60 parser.add_argument(
61 "charm", nargs=None,
62 help="Charm name")
63
64 parser.add_argument(
65 "service_name", nargs="?",
66 help="Service name of deployed charm")
67
68 return parser
69
70
71class NullProcess(object):
72
73 def terminate(self):
74 pass
75
76
77def _sanity_check_subprocess(p):
78 # Check that 'p' has printed a line to stdout and is still running.
79 # Otherwise, raise an exception with its stdout.
80 line = p.stderr.readline()
81 if not line:
82 raise RuntimeError(p.stdout.read())
83 if p.poll() is not None:
84 raise RuntimeError("Process terminated:\n%s" % (p.stderr.read(),))
85
86
87def open_tunnel(environment):
88 """Run 'juju open-tunnel'.
89
90 Use this to accelerate interaction between the client and the Juju
91 environment.
92 """
93 cmd = ['juju', 'open-tunnel']
94 if environment:
95 cmd.extend(['--environment', environment])
96 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
97 _sanity_check_subprocess(p)
98 return p
99
100
101def debug_log(environment, debug=True):
102 """Run 'juju debug-log', forwarding output to our stdout."""
103 if not debug:
104 return NullProcess()
105 cmd = ['juju', 'debug-log']
106 if environment:
107 cmd.extend(['--environment', environment])
108 # stdout & stderr options here aren't necessary, but jml likes to be
109 # explicit.
110 return subprocess.Popen(cmd, stdout=sys.stdout, stderr=sys.stderr)
111
112
113def deploy(environment, repository, config, charm, service_name):
114 # juju deploy --environment options.environment \
115 # --repository options.repository \
116 # --config options.config
117 # charm [service_name]
118 cmd = ['juju', 'deploy']
119 if environment:
120 cmd.extend(['--environment', environment])
121 if repository:
122 cmd.extend(['--repository', repository])
123 if config:
124 cmd.extend(['--config', config])
125 cmd.extend([charm, service_name])
126 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
127 out, err = p.communicate()
128 if p.returncode:
129 raise RuntimeError(err)
130
131
132def watch_until_not_pending(environment, service_name):
133 cmd = ['jitsu', 'watch']
134 if environment:
135 cmd.extend(['-e', environment])
136 # Order matters here. service_name **must** be first.
137 cmd.extend([
138 service_name,
139 '--x-state', 'pending',
140 '--num-units', '1',
141 ])
142 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
143 p.communicate()
144
145
146def dump_status(environment, scope=None, stream=None):
147 """Dump status for an environment to 'stream'."""
148 if stream is None:
149 stream = sys.stdout
150 cmd = ['juju', 'status']
151 if environment:
152 cmd.extend(['--environment', environment])
153 if scope:
154 cmd.append(scope)
155 p = subprocess.Popen(cmd, stdout=stream, stderr=subprocess.STDOUT)
156 p.communicate()
157
158
159def get_service_name(charm, service_name=None):
160 # XXX: Do I seriously have to do this? --- jml, 2012-10-04
161 if service_name:
162 return service_name
163 if ':' in charm:
164 return charm.split(':', 1)[1]
165 return charm
166
167
168@contextmanager
169def terminating(process):
170 try:
171 yield
172 finally:
173 process.terminate()
174
175
176def main():
177 """Deploy a single unit of a service."""
178 parser = get_parser()
179 options = parser.parse_args()
180
181 environment = options.environment
182 service_name = get_service_name(options.charm, options.service_name)
183
184 # Open a tunnel, deploy, start the debug log and then terminate once
185 # deployment has finished (either successfully or not).
186 with terminating(open_tunnel(environment)):
187 deploy(
188 environment, options.repository, options.config, options.charm,
189 service_name)
190 with terminating(debug_log(environment, options.debug)):
191 watch_until_not_pending(environment, service_name)
192
193 dump_status(environment)
194 # XXX: Always exit with 0, even if it failed. Should really return
195 # non-zero when deploy failed.
196 sys.exit(0)
197
198
199if __name__ == '__main__':
200 main()
0201
=== modified file 'sub-commands/deploy-to'
--- sub-commands/deploy-to 2012-06-28 05:18:42 +0000
+++ sub-commands/deploy-to 2012-10-19 11:07:24 +0000
@@ -31,7 +31,6 @@
31from juju.control.utils import (31from juju.control.utils import (
32 expand_path, sync_environment_state)32 expand_path, sync_environment_state)
3333
34from juju.charm.errors import ServiceConfigValueError
35from juju.charm.publisher import CharmPublisher34from juju.charm.publisher import CharmPublisher
36from juju.charm.repository import resolve35from juju.charm.repository import resolve
37from juju.environment.config import EnvironmentsConfig36from juju.environment.config import EnvironmentsConfig

Subscribers

People subscribed via source and target branches