/srv/irclogs.ubuntu.com/2012/03/07/#juju-dev.txt

=== andrewsmedina_ is now known as andrewsmedina
rogfwereade: mornin'08:11
fwereaderog, heyhey08:11
fwereaderog, about the pipeline requests, feel free to ignore anything with a prereq08:11
rogfwereade: ok.08:11
fwereaderog, or I guess I could try to remember to always -prep -req08:12
rogfwereade: yeah, that's what i try to do08:12
rogfwereade: (but you have to remember to manually move the branch state out of "work in progress" when you finally do propose)08:12
TheMuerog, fwereade: morning08:13
fwereaderog, oh! really? "-unprep" is not implicit?08:13
fwereadeheya TheMue08:13
rogfwereade: no. it's a bug, and i've reported it...08:13
rogTheMue: hiya08:13
fwereaderog, ok, cool, I'll try to remember :)08:14
rogfwereade: i've been thinking about your command context stuff08:15
rogfwereade: i'm not sure about it08:15
fwereaderog, hm, ok, go on08:15
rogfwereade: the motivation is so that the hook commands can run the commands inside the agent, right?08:16
fwereaderog, maybe, sort of08:16
fwereaderog, let's say yes for now08:16
rogfwereade: if it's not, then my concern is orthogonal08:16
rogfwereade: anyway08:16
rogfwereade: i think we should provide a "hook" API so that Go programs can use relation-get etc without invoking the command line commands08:17
rogfwereade: then jujuc can layer onto that08:17
rogfwereade: 5 second sketch: http://paste.ubuntu.com/872668/08:18
fwereaderog, I think I need a bit more context for why you think that's necessary or desirable08:19
fwereaderog, IMO invoking a command-line command is not a big deal08:19
rogfwereade: it would be useful (IMO) to be able to write hooks in Go08:19
fwereaderog, it would be cool but it's pretty niche really08:20
rogfwereade: i think it makes sense from a modularity perspective, just like the juju command08:20
rogfwereade: (it's certainly niche now, but not necessarily in the future)08:20
rogsorry, just like the juju package08:20
rogwe can think of the Hook type as the primary API. the command line is just an interface to it.08:21
fwereaderog, what will be the increased costs of implementing it at the time it turns out to be needed?08:21
rogfwereade: what are the increased costs of implementing it now?08:22
rogfwereade: i think it makes things simpler if anything08:22
fwereaderog, I haven't implemented a single hook command yet08:22
fwereaderog, we know they need to be invoked via the command line08:23
fwereaderog, we have this idea that it might be nice to write hooks in Go08:24
fwereaderog, but os/exec doesn't feel like such a lame horse that we should devote dev effort to privileging go hooks over other languages by giving them their own API08:25
rogfwereade: it would enable other languages to write their own API if they want.08:25
rogfwereade: Go is privileged here because it's the implementation language.08:25
fwereaderog, IMO the *lack* of privilege is an important feature08:27
rogfwereade: if we provide exactly the same capabilities, there's no real privilege, just convenience (and type safety)08:27
rogfwereade: IMO it's a better separation of concerns08:28
rogfwereade: and you don't need the command context :-)08:28
fwereaderog, but it's additional convenience for a language that is unlikely to be a normal user's first choice for hook implementation, and doing so carries a subtle implication that you "should" write hooks in go08:28
rogfwereade: i really don't think so. it's just that we can easily provide a nice API and layer onto that with the command line stuff08:29
fwereaderog, the context is a replacement for far too many layers of plumbing in python08:29
rogfwereade: i think it's only needed because the innards of the code is printing to stdout and stderr.08:30
rogfwereade: if there's an API, there's no need for that.08:30
fwereaderog, it's not necessarily even the innards08:30
fwereaderog, really it shouldn't/won't be the innards08:31
fwereaderog, ok, I think there's something I'm missing: the intent of the Context is entirely to simplify what strikes me as an unnecessarily convoluted architecture in python08:33
fwereaderog, if "native" hook command execution were a priority, we would have exposed it in python08:34
rogfwereade: i'm not sure we need it though, if we're not doing remote execution of arbitrary commands.08:34
rogfwereade: it's not a priority, but i think it would work well as a way of structuring the system.08:35
rogsending command line commands across the pipe seems like it's making things more abstract than they need to be08:36
fwereaderog, can we take a step back because I'm not sure of the extent of your proposal08:37
rogfwereade: ok.08:37
fwereaderog, are you suggesting that we should basically have an RPC server implementing N methods, and N executables each calling one of those methods?08:38
rognot quite08:39
hazmatg'morning08:39
rogfwereade: i'm proposing that we have an RPC server implementing N methods, yes, but that the jujuc command calls one of those methods depending on the command line args08:39
fwereadehazmat, heyhey08:40
roghazmat: yo!08:40
hazmatgreetings08:40
roghazmat: i guess it's not morning for you...08:40
hazmatfwereade, did you have a a chance to look at the review on service constraints?08:40
hazmatrog, the birds are chirping ;-)08:40
fwereadehazmat, yeah, I rote you an essay08:40
roghazmat: tell 'em to shut up and go back to sleep :-)08:41
fwereadehazmat, it may be filled with crack but I'd be interested to hear your views08:41
hazmatfwereade, sounds good.. waiting for the intertubes to catch up..08:41
rogfwereade: the point is that all the awkward command-line stuff is inside jujuc, and the unit agent only deals with nice fluffy typed abstractions.08:42
fwereaderog, IMO that is itself a significant increase in complexity -- N request types, N response types, N methods -- just to avoid one layer of abstraction that doesn't affect any other code08:42
fwereaderog, what makes you think the unit agent won't have nice fluffy typed abstractions to work with?08:44
fwereaderog, the command line needs to be dealt with *somewhere*08:44
rogfwereade: sure. i'd just prefer to deal with it as close to the metal as possible.08:44
hazmatfwereade, ah.. re (4) it was about the storage (dict) but about the choice of names (layer_data) which implied it was a layer in some lookup that's not germane to the entity, to the given entity that data are its serializable constraints.08:46
fwereadehazmat, sorry, having trouble parsing that08:46
hazmater.. it wasn't about the storage, just the naming08:46
fwereadehazmat, ha, ok; sorry I completely missed that08:47
hazmatfwereade, no worries, thanks for keeping up with it08:48
fwereaderog, ok, but a cmd.Context is all-the-complexity-we-need, right here, right now, and done; and it lets us add accessible commands to a hook context in one place only (the hook context itself)08:49
fwereaderog, your proposal implies we need to change jujuc, and the server, every time we implement a new hook command08:49
rogfwereade: one other consideration is that doing it this way constrains our future implementation options. all hook commands must run as RPCs to the unit agent, which isn't necessarily always going to be the case.08:50
rogfwereade: yeah, that's true. jujuc would need to know what commands it's implementing08:51
fwereaderog, if we do end up with hook commands that don't depend on their execution context, we can just expose those as standalone commands08:51
fwereaderog, jujuc is solving the very specific problem of hook commands that need to know an unhealthy amount of stuff controlled by the unit agent08:52
hazmatfwereade, if a service is deployed with no constraints, the service state will end up storing a copy of the environment constraints as the actualized constraints for the service?08:52
fwereadehazmat, hmm, tbh I forget exactly when things are collapsed08:53
rogfwereade: agreed08:53
fwereadehazmat, but I'm pretty sure my intent was to defer collapse until we have an actual unit that needs them08:54
rogfwereade: but that stuff might vary according to the command.08:54
fwereaderog, indeed it could; but I don;t see where you're going with this08:56
rogfwereade: i just have a strong feeling that a hook API would be a Good Thing. perhaps i'm on crack.08:57
rogapart from anything, it would provide a nice documentation of the hook capabilities.08:57
fwereaderog, I'm not saying it would automatically be a *bad* thing, but I think that it's (1) premature and (2) perfectly easy to add at a later stage assuming I implement the commands themselves with some sanity08:58
rogfwereade: FWIW i think the code would be almost as simple with an API. there's no need to define N request types and N response types - existing types will do the job.09:01
fwereaderog, that is to say that the true meat of the code will still be "call some hook context method"09:01
fwereaderog, the Commands we expose will be responsible purely for parg parsing, and transforming the result for stdout09:02
rogfwereade: yeah. the question is: does that code happen inside jujuc or inside the unit agent09:02
rog?09:02
rogthe difference is: (commandline -> RPC -> demux -> call) vs (commandline -> demux -> RPC -> call)09:04
rogi'm contending that the latter is nicer, and gives us a Go hook API for free.09:05
fwereaderog, I'm contending that the former is, because it allows us to keep all the knowledge about what commands we want to expose in one place09:06
fwereaderog, and that a go hook API will still be cheap if and when we need it09:06
rogfwereade: we've still got to make the symlinks from jujuc to the various commands, so that information's not *quite* in one place :-)09:08
fwereaderog, but that's utterly trivial09:08
fwereaderog, and we can do that for each execution context09:08
fwereaderog, it's a simple transformation of a list of Commands; the very same list of Commands that we use in the server09:09
roghmm. would we prefer "command not found" or "command not appropriate for hook context"?09:09
fwereaderog, IMO command not found, if for no other reason that it implies less wanton shitting in my command space09:10
fwereaderog, as a user, I don;t really appreciate having relation-get on my path ;)09:10
fwereaderog, alternate conception: relation-get is not a right, it's a privilege ;)09:11
rogfwereade: the question is not whether you've got relation-get in your path, but whether relation-get is in the path for hook executions where it's not allowed09:12
fwereaderog, it feels to me like fundamentally the same question; what's the point of exposing commands whose only purpose is to error out?09:13
rogfwereade: i *think* i prefer it to be in the path - that way there's the possibility of getting it to log a debug message saying "inappropriate hook call" when a charm gets it wrong09:13
rogfwereade: as above - because the error can be more informative that way.09:13
rogfwereade: also we wouldn't have to muck around having a directory with a different set of symlinks for each possible context.09:14
fwereaderog, "command not found" seems pretty informative to me09:14
fwereaderog, intent is that the symlinks dir lives only as long as the hook does09:15
rogfwereade: so we create the symlinks dir on every hook execution?09:15
fwereaderog, the dir is trivial to create -- list of commands09:15
rogfwereade: seems a bit unnecessary, but i suppose all this is very slow anyway09:15
fwereaderog, it seems fine to me to spend a couple of ms per hook execution creating a directory that gives us exactly the interface exposed by the hook context -- again letting us keep all the interface info on the hook context itself, and growing seamlessly as we implement the commands one by one09:17
rogfwereade: i think that updating jujuc at the same time as the juju agent would be just as simple as updating just the agent.09:19
fwereaderog, isn't "if you change X, you have to change Y" rather the hallmark of inappropriate factoring?09:21
rogfwereade: you'd have to change two files anyway. whether those two files end up in two separate commands seems fairly by the by.09:22
fwereaderog, ok, we have to implement a Command in some way regardless, assume that's a sunk cost09:22
rogagreed09:23
fwereaderog, in my case, once we have a command, it's a matter of adding a line reading `&FooCommand{Context: ctx},` to the appropriate contexts09:23
rogfwereade: and, presumable, the actual call that FooCommand makes to get or set the information?09:25
rogpresumably09:25
fwereaderog, yes, FooCommand will indeed be talking to ctx09:26
fwereaderog, I think that's a sunk cost as well...?09:26
fwereaderog,  the ctx needs to know what to do in either case09:27
rogfwereade: the question is simply whether the FooCommand does a local or a remote call, i think.09:28
rogfwereade: and, yes, if it's a remote call, there would be one additional layer of indirection, to hide the remote method invocation.09:29
fwereaderog, quite so; and it's an indirection layer that we need to keep updating as the exposed commands change09:29
rogfwereade: true. but i see that as a good thing. the API would mirror the capabilities of the hook.09:30
fwereaderog, as it is the exposed environment already does that09:31
rogfwereade: BTW, just out of interest, does this API reasonably represent the capabilities of the current hook execution environment? http://paste.ubuntu.com/872742/09:31
fwereaderog, and it does it dynamically rather than forcing us to manually mirror the same information in multiple places09:31
rogfwereade: the exposed environment gives the names of the commands, but not the interface to those commands09:32
fwereaderog, there's juju-log as well and possibly one or two others09:32
fwereaderog, 8 in total09:34
fwereaderog: relation-get, relation-set, relation-list, juju-log, config-get, unit-get, open-port, close-port09:36
rogfwereade: like this, then: http://paste.ubuntu.com/872750/09:37
fwereaderog, yes, we'll end up with hook contexts implementing something very much like that regardless09:37
fwereaderog, we can expose them in another way if it ever turns out to be necessary09:37
fwereaderog, and in the meantime we don't need a layer of redundant code for dealing with remote calls with all the different types and results09:38
fwereaderog, eg, what happens when we need to add a flag to something? we can: fix the Command and the HookContext method; or, fix the Command, fix the HookContext method, fix the RPC server and fix the RPC client09:42
fwereaderog, not forgetting the Request type09:43
rogfwereade: the Request type?09:43
fwereaderog, assuming we use net/rpc, that is09:44
rogfwereade: i was assuming that. but what do you mean by the request type?09:44
fwereaderog, call(req, &resp)09:45
rogfwereade: ah, yeah if you add options, you might need a new request type.09:45
fwereaderog, if we add another flag that affects what we want to send... we need to send it09:45
rogfwereade: i agree it's a little more work. but i still think it would be worth it.09:46
fwereaderog, tbh I'm still not seeing the benefits09:47
fwereaderog, apart from this speculative hook API that would still be easy to implement if we ever needed it09:47
fwereaderog, (and I have my doubts about the hook API's utility *anyway* -- I really think that any hint of a "preferred" implementation for hooks is a pretty fundamentally bad thing)09:50
fwereaderog, and I think that a golang hook API *would* be seen that way09:50
rogfwereade: isn't there a python API to the hook context?09:51
rogfwereade: (layered onto the command line)09:51
fwereaderog, well, technically yes; but I don't think we've ever suggested that anyone use it09:52
rogfwereade: i thought some people write hooks in python09:52
fwereaderog, maybe they do, but I don;t think they have any business whatsoever importing code from juju itself and interacting directly with the unit agent09:53
rogi  wasn't suggesting that they did09:53
fwereaderog, if they were they're be Doing It Wrong IMO09:53
fwereaderog, ok... I missed your point then09:53
fwereaderog, people can write hooks in python or go right now09:54
rogfwereade: if i was writing a hook in python, i'd probably write an API so i could do relation_get() rather than calling exec and getting its piped output.09:54
rog(obviously relation_get would do exec under the covers)09:55
fwereaderog, ok, and that'd be a good thing for charm-tools or whatever09:55
fwereaderog, but to me the exec-under-the-covers is the important thing09:56
rogfwereade: so, i see the Go hook API in the same way as that, except because we're implementing it in Go, it can be a little more efficient, and synced more directly with the core09:57
fwereaderog, because it means you're *actually* using the same interface everyone else is, as you should be09:57
rogfwereade: the inefficiency is important?09:57
fwereaderog, why would it be a bad thing to use the python API but not the go one?09:57
rogfwereade: it wouldn't. just a little slower.09:58
fwereaderog, hell yes it would be a bad thing to use the python API09:59
fwereaderog, eithet your charm doesn't work in the go implementation, or we're stuck reimplementing the backend of the python api in go09:59
rogfwereade: that's what we're doing... relation-get will be implemented in Go10:00
fwereaderog, we have a perfectly good way of calling hook commands10:01
fwereaderog, what benefit do we get from introducing another?10:01
fwereaderog, especially when it makes development more inconvenient10:01
fwereaderog, and the only people who'll feel the benefits will be those writing charms in go10:02
rogthat is indeed true10:02
rogfwereade: i had this idea that all the user-scriptable functionality in juju would be nicely exposed in a Go package (the juju package, in fact)10:03
rogmaybe it's a silly idea10:03
fwereaderog, I think the distinction is between user-scriptable and charmer-scriptable10:03
rogi see charmers as users10:04
rog(just a different category from someone that just types "juju deploy")10:04
fwereaderog, fair enough, but we're still talking about two classes of users with very distinct needs10:04
rogfwereade: they both benefit from a nice API, AFAICS10:05
rogfwereade: if we *do* provide a Go hook API, the code in it would look remarkably similar to the code we'd write if we did a hook API to start with... except more inefficient. perhaps that's where my reluctance comes from. maybe it's the usual premature optimisation "but... i can see how it can be more efficient!"10:08
fwereaderog, well, to be generally useful to either class we need an API that can be consumed from more languages than just go, surely?10:08
rogfwereade: if we used json as the rpc transport, that would be quite straightforward actually.10:09
fwereaderog, and that's all well and good, but it's still an additional api, and I don't think we can currently  justify taking on the cost and hassle of maintaining two when we have a perfectly serviceable one already10:11
fwereaderog, "exec" is kinda lowest-common-denominator but that's the point10:11
* rog 's premature-optimisation head is screaming "but we can do better!"10:12
rogbut i'm ok to go your route10:12
fwereaderog, by introducing another we're implicitly saying "you shouldn't be writing bash scripts, it's better to do it in go" ;)10:13
fwereaderog, I hope I've at least half-convinced you rather than just talked you to death ;p10:13
rogfwereade: i still have a strong intuition that keeping the command-line parsing in jujuc is somehow more "right". but i'm working hard to try to suppress it.10:15
fwereaderog, my intuition that it's "wrong" largely comes from the feeling that the python code takes on a disturbing amount of complexity (apparently) to accommodate that very feeling10:17
rogfwereade: which complexity are you talking about there, BTW?10:18
fwereaderog, juju/hooks/protocol.py (and all the associated stuff)10:19
fwereade    @defer.inlineCallbacks10:19
fwereade    def open_port(self, client_id, port, proto):10:19
fwereade        """Open `port` for `proto` for this unit identified by `client_id`."""10:19
fwereade        yield self.callRemote(10:19
fwereade            OpenPortCommand, client_id=client_id, port=port, proto=proto)10:19
fwereade        defer.returnValue(None)10:19
fwereadeetc etc10:19
rogi don't mind that file too much. it's fairly naive (in a good way), and it documents the capabilities quite nicely. the Go source would be quite a bit shorter too, i think.10:23
rogfwereade: i do see where you're coming from now though. i'm suggesting exactly what's in the current python implementation!10:23
fwereaderog, yeah :)10:24
rogfwereade: so, for the record, i still think that's the right approach. but i appreciate your arguments in favour of a "command line across the pipe" too.10:25
fwereaderog, and it's not just that file -- if's relation-set which calls relation_set which constructs a RelationSetCli which calls the client which calls the server whcih calls the context10:25
rogthe "client" being the code in protocol.py, yes?10:26
fwereaderog, the client/server both being in protocol.py, yes10:26
fwereaderog, at some point the ravioli code got to me and I rejected the whole aproach ;)10:27
fwereaderog, (lots of tiny little things with hardly any meat in them)10:27
rogfwereade: yeah, i appreciate that feeling very well10:29
rogfwereade: so in go, it would be command RelationSetCommand which invokes Hook.SetRelation which does rpc.Call("Hook.SetRelation") which invokes unitAgent.SetRelation.10:30
rogthe ravioli would be in Hook.SetRelation which wouldn't do much more than marshal its arguments and make the call.10:32
fwereaderog, all the marshalling is busywork that forces us to touch at least extra two places in the code any time we make a change10:33
rogfwereade: yes, i agree. but it's only three lines per call. and we've only got 8 calls, and no prospect of significant numbers of incomers.10:34
fwereaderog, but it's still taking on repetitive extra code in exchange for speculated future benefits10:35
rogfwereade: i think that having the API laid out in one place (the Hook type) is a benefit in itself.10:36
fwereaderog, won't the HookContext interface have all that, without the marshalling busywork?10:37
fwereaderog, can't we just add marshalling that talks directly to that when we turn out to need it?10:37
fwereaderog, which I again consider to be a somewhat remote prospect10:37
fwereaderog, IMO even if you want to write hooks in go you can damn well wrap os/exec like everybody else does :p10:38
fwereaderog, well, I guess it'll be Context, RelationContext, DepartedContext interfaces, but details details10:40
rogfwereade: hmm, not quite sure how that would work10:41
fwereaderog, hm, no need for a separate Departed *interface*, just another implementation of it10:41
fwereaderog, sorry brb10:41
fwereaderog, b10:44
rogfwereade: just thinking about how the unit agent's hook execution would work10:44
fwereaderog, take a look at juju.state.hook10:45
rogfwereade: from a Go perspective, that is.10:45
fwereaderog, understood, but I think there'd be some similarities10:46
rogfwereade: here's a sketch: http://paste.ubuntu.com/872817/10:46
rogso startHookServer interprets the command line and invokes the appropriate HookContext method (which might return an error if a function was called in an inappropriate context)10:47
rog(if the wrong symlink was made, for example)10:47
fwereaderog, I think I want to step further away from that idea actually10:47
rogso, yeah, the hook context capabilities would be nicely exposed there10:47
rogfwereade: which idea?10:48
fwereaderog, hook contexts shouldn't be implementing the *commands* themselves; they should just be exposing capabilities made use of by the commands10:48
fwereaderog, eg in python, context.get_value(unit_name, key)10:49
rogfwereade: so you'd just pass a bitmask of available capabilities?10:49
fwereaderog, no, I'd define an interface of available capabilities10:49
rogfwereade: isn't that what the HookContext is?10:50
fwereaderog, two in particular; one which knows about relations and one which doesn't10:50
rogso you'd do a dynamic type check to see if the given interface is implemented?10:51
fwereaderog, no...?10:51
fwereaderog, take juju-log10:51
fwereaderog, the juju-log Command would just have a `ctx PlainContext`; where the relation-set one would have a `ctx RelationContext`10:53
fwereaderog, the departed/non-departed implementations of RelationContext would act diferently but there's no reason for the commands to see a difference10:53
fwereaderog, where the RelationContext interface includes the PlainContext one10:54
rogfwereade: but where does the ctx argument to juju-log come from?10:54
rogfwereade: the rpc server can't know in advance that the juju-log command is going to be run10:54
fwereaderog, JUJU_CONTEXT_ID10:54
roghuh?10:55
rogthat's not my question.10:55
rogi'm talking about within the unit agent10:55
rogfwereade: referring back to my code, how would you write RunHook?10:56
fwereaderog, pretty much like this: https://codereview.appspot.com/5753057/10:57
fwereaderog, so the ultra-minimal Context interface there is all that's necessary to exec a hook in a context10:58
fwereaderog, you'd need actual context struct types to implement more fleshed-out interfaces to be useful to their commands10:59
rog[10:37] <fwereade> rog, won't the HookContext interface have all that, without the marshalling busywork?11:00
rogit seems not11:00
fwereaderog, yep, that was horseshit11:00
fwereadesorry :)11:00
fwereaderog, on that point I fall back to the "we shouldn't be implementing an alternative interface until we know we need it" argument11:00
fwereaderog, that said there's nothing stopping us from implementing those methods on HookContext11:02
fwereaderog, I'm just not sure it'd be very neat in practice11:03
rogfwereade: i think it would be quite neat actually. i think the command-line stuff is clunky, and it's nice to isolate it.11:06
rogfwereade: here's what i'd do: http://paste.ubuntu.com/872832/11:06
rogthen each context just passes a value that exactly implements its needs, and all the command line stuff is confined to startHookServer11:07
fwereaderog, tbh it doesn't seem strange to me that an execution context should provide Commands11:09
rogit's also potentially easier to test, because you can test the context independently of the command parsing11:09
fwereaderog, that's already perfectly possible isn't it?11:10
rogfwereade: to my mind the callbacks within the hook execution are fundamentally operations, not commands.11:12
rogfwereade: they happen to implemented with commands (currently) but conceptually i think an interface containing the operations to be made available makes better sense to me11:12
fwereaderog, in which case IMO they should still be `func RelationSet(ctx RelationContext, blah blah)`11:13
rogfwereade: why not an interface containing the set of possible operations, as in my example?11:13
fwereaderog, because it includes impossible operations11:14
rogfwereade: that's fine. impossible operations return an error.11:14
fwereaderog, and because DepartedContext and RelationContext will be disgustingly similar ;)11:14
rogfwereade: really? what's the difference between them?11:15
fwereaderog, this may come down to the "should we expose meaningless commands" question, and I still think "no"11:15
rogfwereade: i think that operationally there's no difference11:15
fwereaderog, RelationContext and DepartedContext make different information available11:16
rogfwereade: we can always use embedding to remove duplication.11:17
rogfwereade: different information available through which operation, BTW?11:17
fwereaderog,  departed relations only make the local unit's settings accessible11:18
fwereaderog, and should IMO not even expose relation-set etc11:18
rogfwereade: i don't have a problem if relation-set runs but returns an error. i don't see that as an issue.11:18
fwereaderog, it's not exactly an advantage, though11:19
rogfwereade: plus i think there *is* an advantage to avoiding the need to create and populate a directory of symlinks on every hook execution11:20
rogfwereade: it means there's no need to write that code...11:21
fwereaderog, that 30 lines?11:21
rogyeah! 30 lines gone, hurray!11:22
rogand one less moving part, also good.11:22
fwereaderog, you did just dismiss 24 lines of marshalling, plus all the types and so forth, as insignificant :p11:22
fwereaderog, and those aren't exactly non-moving parts either11:23
rogfwereade: nah, worth it for the benefits i saw...11:23
rogfwereade: but i don't see any particular benefit to creating the command set dynamically.11:23
fwereaderog, one moving part in one place vs 8+ distributed here and there?11:24
fwereaderog, the benefit is that it allows us to do the rest of our work without worrying about the plumbing11:25
rogfwereade: ?11:25
rogfwereade: i'm not sure what you mean by "worrying about the plumbing"11:26
rogBTW of course i think that some moving parts are worth it... just not this one :-)11:27
fwereaderog, I mean that the symlinks need to come from somewhere (or even the individual executables); we can do it manually for each command -- adding types, changing the server, changing jujuc, worrying about how we expose them at deploy time11:27
fwereaderog, or we can have the execution context know how to create itself11:28
fwereaderog, by just implementing another Command11:28
fwereaderog, for, y'know, command-line parsing11:28
fwereaderog, which can then interact with the actual context however appears to make sense (I think this in particular is orthogonal)11:29
rogi'd thought that the symlinks would just be part of the package. the hook execution code would just add the directory to $PATH and that's it11:29
rogthe apt-get package, that is11:29
fwereaderog, that's still extra packaging work11:29
rogfwereade: can't the symlinks simply be in the VCS?11:30
rogfwereade: i.e. we create them once and then commit them11:30
rog(i admit i know nothing at all about the workings of apt-get)11:31
fwereaderog, that's *still* an extra step along with the stuff I was just complaining about11:31
rogfwereade: a step that one person has to make once, with no code involved. i don't see that as an issue.11:32
rogsomeone has to install the jujuc command too...11:32
fwereaderog, once per command, along with all the other once-per-command steps...11:33
fwereaderog, and still the only benefit to all the extra work is that it *might* become easier to implement a speculative feature at some point in the future...11:34
rogfwereade: no, not at all11:34
rogfwereade: i've moved on from there11:34
rogfwereade: i've accepted that the RPC interface is command-line-centric11:34
fwereaderog, ok, sorry -- we're purely talking complexity distribution now then?11:34
fwereaderog, consider the argument for making agents implement Command11:35
rogfwereade: i'm arguing for the use of a Go interface to provide the callback interface rather than a set of Commands11:35
fwereaderog, and I'm arguing that we need the Commands for parsing command lines, because we're exposing them to the execution context as command-line tools11:36
rogfwereade: i.e. hide the fact that the hook callbacks are implemented with commands, because it's cleaner to do so11:36
fwereaderog, and that whether or not Context has that interface, or a different one, is not material11:37
fwereaderog, the Commands themselves can talk back to the context over whatever interface makes sense11:37
fwereaderog, it may be yours, it may look more like the python ones11:38
rogfwereade: i think the Commands can be hidden inside Exec.11:39
fwereaderog, then what does all the command-specific command-line parsing?11:39
rogfwereade: i think things would be nice if the agent code itself never had to know about Commands11:39
fwereaderog, you're saying *Exec* should now know about all the different possible commands and how they should be handled?11:39
rogfwereade: that's inside startHookServer (or whatever you might call it) which maps from the command line stuff to calls on the hook context.11:40
rogfwereade: yeah, i'm saying that, yes.11:40
rogfwereade: it would know about the HookContext interface (which encapsulates all possible hook operations)11:41
fwereaderog, the knowledge of the commands is entirely within the context itself; I don't see why the agent should care11:42
fwereaderog, seriously: we have a hook execution context that needs to provide env vars and executables; what is the possible benefit of pretending, at this level, that the executables are not really executables?11:44
fwereaderog, the actual implementation of RelationSetCommand.Run will either be `c.ctx.RelationSet(c.foo, c.bar)` or `RelationSet(c.ctx, c.foo, c,bar)`11:46
fwereaderog,  plus some transformation of the result for stdout, where necessary11:46
rogthe env vars are for the hook context, not necessarily for the callback operations.11:48
fwereaderog, ie it's doing exactly what's needed at this level and not forcing anything on how we implement the operations themselves11:48
fwereaderog, yes, exactly so11:48
fwereaderog, so are the executables for the hook context11:49
fwereaderog, and they're responsible for parsing command lines, running the operations (which can be independent) and returning the results11:49
rogi see that your hook package is very general. i think i'm just thinking that something with a little less abstraction would be easier to understand.11:49
rogso a Context would be an interface containing the actual operations, rather than a set of arbitrary commands.11:50
rogand the environment would be an argument to Exec rather than something returned from the context.11:51
fwereaderog, a given Context will expose [1] .Commands() and .Env() for use by Exec and [2] RelationSet, or GetValue, or whatever, for use by the Commands themselves11:52
rog[2]11:52
fwereaderog, passing the env to exec doesn't alter the fact that it still comes from the context in the first place...11:53
fwereaderog, I'm less and less sure that RelationSet should be on the hook context11:53
fwereaderog, I think the capabilities exposed by the python hook contexts are at the right level11:54
fwereaderog, a RelationSet operation that takes a RelationContext and uses its SetValue method seems pretty reasonable to me11:54
fwereaderog, when seen in the context of the total number of ways we need to interact with a context anyway11:55
fwereaderog, ie what's implemented in juju.state.hooks11:55
rogfwereade: i see a context containing all the operations as a nice reflection of the way that the charm author will see things11:57
rogfwereade: i don't see a need to have one type for each operation11:57
fwereaderog, in practice a charm author will see the operations-that-don't-always-return-errors as the real interface she has available11:58
fwereaderog, RelationSet isn't a type, it's a function11:58
rogfwereade: sure. that's what they get in practice11:58
fwereaderog, ...so if you're arguing from conceptual integrity, exposing only the operations an author cares about seems sensible to me11:59
rogfwereade: Go works better when there's a known set of operations, even if some return errors.12:00
rogfwereade: i like the idea of mapping the set of command line callback operations onto a single go interface that encapsulates all those calls.12:01
fwereade_rog, what was the last thing you saw me say?12:03
fwereade_rog, that I saw from you was:12:03
fwereade_<rog> fwereade: sure. that's what they get in practice12:03
rogfwereade_: in practice, a charm won't tell the difference between a command that returns an error and a command that doesn't exist12:04
rogfwereade_: hence my comment12:04
rogfwereade_: even though we can actually implement all the calls all the time12:05
rogfwereade_: BTW does the unit agent write to disk for any other reason?12:06
fwereade_rog, it does12:06
rogah, the socket12:06
fwereade_rog, also stuff like remembering state in case of unexpected process death etc12:07
rogfwereade_: the zk reconnection id?12:07
fwereade_rog, nah -- what hooks have been queued and not executed, what relations we're currently part of, etc12:07
roginteresting12:08
fwereade_rog, stuff like that is not appropriate for ZK -- nothing else needs to know about it12:08
fwereade_rog, basically just local state that needs to be persisted for one reason or another12:10
fwereade_rog, also stuff like upgrading the charm itself :p12:10
rogfwereade_: is there an operation log or something? (i'm wondering how it deals with atomicity of writes)12:10
fwereade_rog, I'm not sure exactly what context we're talking about12:11
rogfwereade_: so, if you've just queued a hook, you need to write to disk that you've queued it, right, in case you die there and then?12:12
fwereade_rog, that's the general idea yes12:12
rogfwereade_: so i was wondering how it managed that data such that it doesn't grow without bound but it's ok whenever the process dies unexpectedly.12:13
fwereade_rog, whenever we've executed something we rewrite the queue without the executed thing12:14
rogfwereade_: so if it dies half way through rewriting the queue?12:14
fwereade_rog, mv is atomic, right?12:14
rogfwereade_: ah, so we create a new file each time we queue an operation. makes sense.12:15
fwereade_rog, if it dies after executing but before rewriting, no big deal, we execute that hook again12:15
fwereade_rog, the danger is executing a hook 0 times; sometimes executing one twice is not a major issue12:15
fwereade_rog, anyway, I suspect the does-the-agent-write-to-disk question fed back into the original discussion in some way12:16
rogfwereade_: yeah, but only if it didn't write to disk :-)12:17
fwereade_rog, haha :)12:17
rogfwereade_: in the end perhaps it comes down to this: i really like the look of this type (http://paste.ubuntu.com/872911/) and i like the directness of passing that as an argument rather than passing a context that can return a list of Commands that when Run can excecute the relevant operations.12:28
rogfwereade_: but i understand your approach, i think, and it will work fine too12:33
rogfwereade_: thanks for going round the garden path with me :-)12:34
fwereade_rog, a pleasure :)12:34
TheMueHOLY SH...12:43
TheMuei'm trying to find out why my watcher isn't firing. and what did i missed? starting the goroutine which listens to the watch.12:44
TheMueso stupid, aaargh12:44
rogTheMue: i did exactly that when writing the example code!12:45
rogTheMue: (did you see it, BTW?)12:46
TheMuerog: opened several pastes regarding this topic. which one you're referencing?12:48
TheMueHA! great, now it fires. you only have to do it right. *smile*12:49
rogTheMue: this one: http://paste.ubuntu.com/871544/12:49
TheMuerog: oh, no, will study it. tom tom.Tomb is a funny declaration. ;)12:50
rogtomb tomb Tomb TOMB!12:51
TheMuerog: and i have to update my Tomb, i have still Fata instead of Kill12:51
TheMueFatal12:52
rogTheMue: there was one thing wrong in that example: FooWatcher.Err should return w.tomb.Err not w.tomb.Wait12:52
TheMuerog: yep, sounds reasonable12:53
rogother than that, i think it represents pretty well the structure we were talking about.12:54
rogTheMue: oh yes, that example won't work without my tiny local/testzk package12:55
rogTheMue: ... the implementation of which is here: http://paste.ubuntu.com/872945/12:56
=== Leseb_ is now known as Leseb
TheMuerog: mine is now working and needs only some last adjustments and more tests12:57
rogTheMue: cool12:57
rogTheMue: i wonder how similar it is...12:57
TheMuerog: so far very similar. only that my watcher is only observing the creation and deletion of a node12:59
TheMuerog: and i retrieve the agent key out of the path13:00
rogTheMue: yeah. i chose the children example because it can be used to demonstrate delta events, but it could've been anything13:00
TheMuerog: it's a good example, more complex than mine13:01
rogTheMue: i hope you find it useful :-)13:02
TheMuerog: for sure, gives more security13:05
=== marrusl_ is now known as marrusl
niemeyerGreeetings14:19
hazmatgreetings14:23
hazmatniemeyer, its alive!14:23
* hazmat points at the soon to be deployed store14:23
niemeyerhazmat: Almost there! :-)14:23
niemeyerfwereade_, rog: https://codereview.appspot.com/575806114:24
niemeyerShould be a trivial one given previous reviews14:24
niemeyerHmm.. how do specify cross-PPA dependencies again..14:27
rogniemeyer: review delivered14:34
niemeyerrog: Cheers!14:34
andrewsmedinaniemeyer: I sent two reviews, one for initial lxc port to Go14:37
andrewsmedinaniemeyer: and another to goetveld14:37
niemeyerandrewsmedina: Thanks a lot.. I'll finish sorting out the store details and will get back on track in review land14:38
andrewsmedinaniemeyer: ty14:38
fwereade_hmm, I guess I should eat something, bbiab14:52
TheMueniemeyer: what can cause zk to close a connection after a Delete() of a node?14:52
TheMueniemeyer: i create my agent node => watch fires correctly; now i delete it => next event is "zk connection closed"14:53
niemeyerTheMue: a connection closed event is unrelated to deletions15:02
niemeyerTheMue: You just happen to be seeing that event because that's when you asked about it, but that's not what the event is being caused by15:02
niemeyerHas it ever happen to anyone that some kind of white rectangle shows at the top of the screen, on the left-hand side?15:04
niemeyerIt's a bug in some app and I can't spot which one it is because xwininfo shows the underlying window rather than the float15:05
TheMueniemeyer: i know that it isn't related and hadn't it ever before. so i'm wondering this time.15:06
niemeyerTheMue: It's something else.. the delete is a red-herring15:06
TheMueniemeyer: i know - but still have no clue15:08
TheMueniemeyer: will try something to gather more infos15:08
rog"That's what Russ said, but besides the point above, this is definitely not ok to ignore. There's both a read and a write side."15:36
rogniemeyer: not quite sure what you mean by that15:36
rogniemeyer: this is just the write side, yes?15:36
niemeyerrog: Copy reads and writes15:36
rogniemeyer: ah, good point. that's fine then.15:37
rogniemeyer: (i was thinking about the http client reading)15:37
niemeyerrog: Cool15:40
* rog wishes it was possible to easily distinguish read and write errors when returned from io.Copy15:40
niemeyer"not entirely convinced, but fair enough."15:45
niemeyerrog: People log 200 results from web sites on a regular basis15:45
niemeyerrog: Logging errors sounds like an extremely natural thing to me, FWIW15:46
rogniemeyer: ok. i guess i was just extrapolating from russ's remark.15:46
niemeyerrog: Russ pointed out that there's no reason to *return* an error15:46
rogniemeyer: it sounds like they're not really errors, because they'll happen whenever the client is aborted15:46
niemeyerrog: And I agree with him on that15:46
niemeyerKnowing about errors in an app we care about is a different story, though15:47
rogniemeyer: huh? it's ok to ignore write errors but only sometimes?15:47
niemeyerrog: Not sure how you got to that conclusion15:47
rog[15:46] <niemeyer> rog: Russ pointed out that there's no reason to *return* an error15:48
rogthat's ignoring the error AFAICS15:48
niemeyerrog: Are we ignoring the error in that handler, even though we're not returning it!?15:48
rogniemeyer: yes15:48
niemeyerrog: No, we're not ignoring it.. we're logging the error, and we're reporting to the client that errors happened15:49
rogsorry15:49
rogyes, but if the write error happens in Flush or WriteHeader then we have to ignore it15:49
rogbecause it's not returned for us to log15:49
niemeyerrog: Ok, nevermind.. are you happy with the current branch content or do you want something else there?15:50
rogobviously the handler can't return the error - it has no return value15:50
rogniemeyer: LGTM. you might want to consider StripPrefix to remove the /charm repetition and the need for a panic check, as i just mentioned in my reply.15:51
rogniemeyer: but i don't mind either way.15:51
niemeyerrog: I still want a panic if it doesn't match..15:51
rogniemeyer: you won't see /charm in the URL if you use StripPrefix15:51
rogniemeyer: so there's nothing to check.15:51
rogniemeyer: (and nothing to strip off either)15:52
niemeyerrog: Exactly.. I want to check if the prefix being stripped is wrong, and panic so that when someone changes /charm to whatever else and forget to update that code, it blows up rather than failing silently15:52
rogthere's only one occurrence of /charm needed, AFAICS15:53
rogso there'd be nothing to update15:53
niemeyerrog: Which prefix is being stripped?15:53
rogniemeyer: the /charm prefix.15:54
rogniemeyer: i'd write a tiny helper function that added the mux handler and put a strip prefix handler on it15:54
niemeyerrog: Sounds good.. I'll move on with what's there though, as it seems to be safe and work well as far as I can see.15:55
rogniemeyer: yeah, that's fine. when you have more handlers you might want to move in that direction though.15:55
rogniemeyer: if there are ever more handlers :-)15:56
niemeyerThere will be.. by then I worry about it15:56
niemeyerLunch time.. biab to sort out deployment details..16:00
TheMuerobbiew: i:m ready when are16:01
robbiewTheMue: ack...need 5min16:01
TheMuerobbiew: ok16:02
TheMuerog: followed your concept of using new watches in each loop run. now it works fine.16:03
robbiewTheMue: 5 more minutes...waiting on an update to complete :/16:06
TheMuerobbiew: :D16:07
robbiewTheMue: https://talkgadget.google.com/hangouts/extras/canonical.com/the-hulk-hideout16:13
rogTheMue: not sure what you mean by that, but i'm glad you've got it working...16:23
TheMuerog: in my first approach i tried to use only one watch. now i'm creaing a new one like you inside the loop each time. you do it with a GetW, i with an existsW16:30
rogTheMue: ah - you hadn't appreciated that a watch fires only once...16:30
rogTheMue: i was confused by that initially too16:31
TheMuerog:exactly16:34
hazmatjimbaker, could you have a final look over the maas provider branch, i think its good to go, i'm planning on landing it today, but wanted to make sure you had a chance to look over it first.16:38
TheMueback again16:43
jimbakerhazmat, sounds good. i looked at last night and i think it's fine, but i'll do one more pass16:48
jimbakerhazmat, also i have a new version of the spec on the relation support changes ready for review; it's about half the size of the original16:49
hazmatjimbaker, awesome16:54
* niemeyer waves16:56
hazmatbcsaller, any updates on subordinates, i saw the subordinate-relation-types is in for review, i'm curious what's next/left, is it just the status support branch?17:00
bcsallerhazmat: no no, working on the changes to unit lifecycle now17:01
bcsalleractually triggering the deploy and all that17:01
bcsallerhazmat: its starting to come together I think17:01
hazmatbcsaller, ah.. right jim's deploy refactor wired into the unit rel watcher17:01
hazmatbcsaller, cool17:01
bcsallerhazmat: yeah in unit/lifecycle, I had to merge it into this branch by hand as it wasn't on trunk yet17:02
hazmatbcsaller, luckily its your review day, so you can help move it along  ;-)17:03
bcsallerhazmat: looks likes its already approved, but point taken17:03
hazmatyeah.. i'm going to be spending most of the day merging extant approved branches17:03
hazmati've been refactoring the scheduler to have some better behavior (at least once wrt to events, instead of at most once)17:05
hazmatniemeyer, i believe we had some discussions on the last meeting wrt to this. I believe the conclusion was on failure for hook execution on an event we should keep the event around, and if resolved --retry rexecute the hook, or if not pop it off the queue.17:07
hazmats/on/in17:07
niemeyerhazmat: Yeah, unless there's some bad idea, doing the same we do for other cases sounds sane at least17:08
hazmatniemeyer, indeed it does17:08
jimbakerbcsaller, hazmat, sounds good about getting refactor-machine-agent into trunk17:09
niemeyerhazmat: s/bad/better/, sorry17:09
niemeyerhazmat: But I guess you go tit17:09
hazmatlol17:09
niemeyer:-)17:09
hazmatmust be a golang thing ;-)17:09
niemeyerhazmat: If it was go tit(), it might be.. but that's just a very funny typo.. :D17:10
jimbakerthere's also a minor branch depending on that, robust-test-removed-service-unit17:10
hazmatjimbaker, cool, it looks like it still needs a review though.17:13
jimbakerhazmat, btw, when do you go out to pycon? i arrive pretty late tomorrow night17:13
jimbakerhazmat, re refactor-machine-agent, yes, just comments so far17:13
hazmatjimbaker, i arrive to sfo tomorrow at 6pm, i should be at the hotel by 8pm17:13
jimbakerhazmat, i should be at the hotel by 11p, so i guess i'll see you fri17:14
jimbakerunless i happen to catch you in the bar ;)17:14
hazmatjimbaker, sounds plausible ;-)17:14
hazmatsigh.. i'm starting to think it might just be simpler to rewrite the scheduler18:33
hazmatif starting to feel like there's too many edge cases with the current data structures18:34
hazmatif/its18:34
* rog is off for the evening. until tomorrow.18:40
hazmatalthough even with a simplified list we have versions and memberships to track, but i hope we can fold those into the list18:40
* hazmat lunches18:40
hazmatniemeyer, SpamapS so agent-state for machines sound good, for consistency that also means turning the unit's 'state' key to an agent-state key18:52
niemeyerhazmat: +118:53
niemeyerhazmat: Even though that's a little bit more subtle18:53
niemeyerhazmat: Since the "unit" is actually represented by the agent itself18:53
hazmatniemeyer, yeah. its quite a bit richer wrt to semantics18:53
niemeyerhazmat: While the "machine" has an independent life18:54
niemeyerhazmat: I'm happy with the change, though..won't hurt to be clear18:54
hazmatniemeyer, i'm happy either way re unit 'state' to 'agent-state'.. i can banish the hobgoblin of consistency.. even though there both agents, as you say the state reported there is the unit's not really the agents.18:55
niemeyerhazmat: What are the values we have for the unit's state?18:56
niemeyerhazmat: not-started and .>?18:56
niemeyerstarted? anything else?18:56
niemeyerhazmat: Also, I wonder about what happens if the agent dies.. is that state updated? :-)18:57
niemeyerState is hard..18:57
hazmatniemeyer, started, start_error, install_error, stop_error, stopped18:57
niemeyerhazmat: What about for the machine agent?18:58
hazmatniemeyer, just running or pending18:58
hazmatthere is no state machine for  the machine agent18:58
niemeyerhazmat: Hmm.. that's instance-state.. I mean the current "state" that we're talking about chnaging18:58
niemeyerThe go tool+conventions and launchpad's daily builds were made for one another19:17
niemeyerhazmat: Just check that out: https://code.launchpad.net/~niemeyer/+recipe/charmstore19:18
niemeyerhazmat: You've hacked around with PPAs before, right?19:18
hazmatniemeyer, very nice19:18
niemeyerhazmat: The whole build is "go install launchpad.net/go/store"19:19
hazmatniemeyer, so re the unit's state.... the machine agent state is really just running, pending, or down. the machine instance state has pending or running.19:21
niemeyerhazmat: I got not-started, so this must be at least not reflecting the actual names19:22
hazmatdown is basically the same as pending, ie the agent isn't connected, we just infer that it must have been running if it has an assigned unit with a state.19:22
hazmatniemeyer, yeah.. pending == 'not-started'19:22
niemeyerhazmat: Ok19:22
hazmatactually that's not true19:23
niemeyerhazmat: Does running => down happen automatically based on ephemerals?19:23
hazmatnot-started means no machine id.. so provisioning agent hasn't acted, pending means its got an id, but no address19:23
hazmatniemeyer, yes19:23
niemeyerhazmat: Ok, that's nice.. what about the unit's state.. does it shift automatically to down based on ephemerals as well?19:24
hazmathmm.. interesting.. we actually stick 'pending' into the instance-id field of the machine instead of the state.. hrm.19:24
hazmatniemeyer, it does19:25
hazmatfor status display, a unit is 'down' if its agent is not connected19:25
hazmatand the unit workflow state is shown if its connected19:25
niemeyerhazmat: The pending in the instance-id sounds reasonable I guess?19:26
niemeyerhazmat: Hmm.. or maybe not..19:26
niemeyerhazmat: We're supposed to have the id from the moment we request.. so I'm not sure about why we'd show pending19:27
niemeyerhazmat: Or do you mean instance-state rather than instance-id?19:27
hazmatniemeyer, we might not have processed the zk state into a provider request and instance-id19:28
hazmatie. the provisioning agent hasn't caught up yet19:28
niemeyerhazmat: Ah, bingo19:28
niemeyermake: dh_clean: Command not found19:30
niemeyerWTF?19:30
niemeyerhazmat: So, yeah.. I'm happy with changing both to agent-state if you'd like that19:30
niemeyerhazmat: Btw, is it start_error or start-error that we show?19:31
hazmatniemeyer, underscore19:32
hazmatniemeyer, i'm starting to feel like it should remain 'state' for unit.. because its not really the agent's state, its the charm and unit's state that's being represented.. the unit agent by itself is either running or its not, if its running we report the unit state, if its not we report the unit is down.19:35
niemeyerhazmat: bummer re. the underscore :(19:36
hazmatniemeyer, we could change it to display.replace("_", "-")19:37
niemeyerhazmat: It's actually the agent.. the software in the unit might be running even if the agent is down19:37
niemeyerhazmat: For consistency, I'd find that quite adequate.. I think we've managed to stick to dashes in pretty much every other option19:37
hazmatright.. but its not really the agent's state.. its the unit aka charm instance's state. the underling service is of course independent19:38
niemeyerhazmat: It's the agent really.. start-error means the *agent* is in a start error state19:38
niemeyerhazmat: Because the hook failed, true.. but it's the agent that is locked in that state19:38
hazmatokay.. its good to have the consistency of key and to distinguish service vs agent.19:39
hazmatagent-state for both19:39
hazmatniemeyer, sorry that got long winded.19:39
* hazmat looks for some caffeine19:40
niemeyerhazmat: No worries, thanks for explaining.. I had vague memories about it19:43
niemeyerFound the dh_clean error.. for some reason it was setup to upload to the wrong ppa19:55
niemeyerWhich didn't have the proper dep19:56
niemeyerAwwwwww19:59
niemeyer3h estimated build time for the charmstore19:59
niemeyerSuddenly the Go build time was severely downgraded by Launchpad :-(20:00
niemeyerFolks, I'll have a shorter day today as we have a family gathering tonight..20:16
niemeyerSee you all tomorrow20:16
hazmatwtf tests seem to have stalled out a few weeks ago23:11

Generated by irclog2html.py 2.7 by Marius Gedminas - find it at mg.pov.lt!