=== _mup__ is now known as _mup_ [00:31] davecheney: g'day. i want a mutex with a timeout. any pointers? i couldn't see native support in the go libs? is there anything we use already? [00:32] wallyworld___: hmm, might have to use something with a channel [00:32] let me ponder for a few mins [00:32] ok [00:33] hard to believe a modern language doesn't have such a construct built in [00:33] or at least in the libs i mean [00:33] i never ran across such a thing in java [00:33] can you give me an example from another language [00:34] probably can do it with a waitgroup and a goroutine [00:34] but a mutex that unlocks itself sounds dangerous [00:34] java has had it for quite a while [00:35] not unlocks itself, just a wait to have the acquire not wait forever [00:35] so you can say, try and grab this thing, but if not successful after x seconds, return with an error [00:35] useful to avoid deadlocks [00:35] and also when making network calls [00:36] oh, right [00:36] i understand now [00:36] you don't want to block on the lock forever [00:36] understood [00:36] the java stuff is in java.util.concurrent [00:36] yes [00:37] probably still going to look like a channel [00:37] ie, you try to send a command to something via a channel [00:37] * davecheney searches archives [00:38] ok. i may extend the built in mutex to provide a Lock(timeoutms int) method, using a channel or whatever [00:39] i can't be the only one who needs this [00:43] * davecheney agrees [00:43] * davecheney continues to search archive [00:43] thank you [00:44] i'll look as well [00:44] if you feel brave you can as on #go-nuts [00:49] i'll see what i can find first, my flame retardant suit in at the cleaners [00:54] smart move, the annoying pedant quotient is quite high in that channel atm [00:57] :-) [01:22] wallyworld___: chop off your tail dude... === wallyworld___ is now known as wallyworld [01:22] davecheney: I'm wondering if I raised the annoying pedant quotient [01:22] :) === wallyworld is now known as Guest86404 [01:23] thumper: not possible [01:23] i got a bitchslap by @tqbf two days ago [01:24] * thumper doesn't know who that is [01:24] wallyworld__: you are broken [01:24] yep [01:24] it's only freenode too [01:25] no issue with canonical channels [01:25] wallyworld__: how are you connecting to freenode? [01:25] using auto identity in quassel [01:26] I seem to have lost my menus for quassel [01:26] dumb unity [01:27] davecheney: i couldn't find anything, so i did this - seems to work http://pastebin.ubuntu.com/1654692/ [01:28] thumper: you had your chance to solve all of Unity's problems before you let them [01:28] left [01:28] you failed :-P [01:29] wallyworld__: unfortunately I think that code is flawed [01:29] :-( [01:29] wallyworld__: lets say you Attemt(100) and it fails [01:30] wallyworld__: looking at your sample [01:30] but 50ms later, the lock succeeds [01:30] i need to think through it some more [01:30] wallyworld__: the channel then has a true in it [01:30] next time you attempt [01:30] you pull off the true, but have another attempted lock pending [01:30] davecheney: am I right in that reading? [01:31] ah, i want to make the channel locally [01:31] not as an attribute of the mutex [01:31] then i also can ditch the New() [01:32] thumper: yeah, it's even a little more complicated because the channel is not buffered [01:32] right, so the previous go routine is still alive [01:32] so chan <- true does not succeed unless something else comes along and does <- chan [01:33] I think there is a possiblity the lock could leak in the locked state [01:33] and locks are not re-enterent [01:33] ouch [01:33] so broken by design [01:33] sorry wally [01:33] you suck :-) [01:33] that sounds like my cue to go to lunch [01:33] http://pastebin.ubuntu.com/1654719/ [01:33] * thumper is laughing to himself [01:34] * thumper feels lucky to know wallyworld__ personally and knowing he can handle the ribbing [01:34] * thumper clicks on rev2 [01:34] * wallyworld__ contemplates ending it all, can't take it anymore [01:35] wallyworld__: still have the issue of the lock attempting to lock after the method has returned [01:35] wallyworld__: perhaps this crazy thing... [01:35] oh oh... [01:35] * thumper writes some go [01:35] ah yes, you are right [01:35] i'll see if i can fix that [01:36] i just wanted to first ditch the channel from the mutex [01:36] * wallyworld__ still doesn't understand why this is so hard in Go :-( [01:38] wallyworld__: http://pastebin.ubuntu.com/1654742/ [01:38] might have some syntax wrong [01:39] or http://pastebin.ubuntu.com/1654745/ [01:39] nah [01:39] that won't work [01:39] as the defer is run when the method ends [01:39] which may be before the Lock succeeds and the goroutine finishes [01:40] I am assuming that the lifetime of the goroutine can outlive the surrounding function [01:40] i wonder what happens which the function ends - are the channels cleaned up straight away? [01:40] they are created on the heap (effectively) and killed when no longer reffed [01:40] so the channels will no doubt still be around [01:40] what happends to the go routines that are running - i guess they keep going [01:40] * thumper hinks [01:40] I think so [01:41] but I'm really just guessing [01:41] i had assumed everything would be cleaned up, hence my original solution [01:41] but if that doesn't hold true, then yours looks better for sure [01:44] except that with yours the test spews out all osrts of go routine type errors [01:44] ah deadlock [01:45] in the failure test [01:45] * wallyworld__ debugs [01:48] heh [01:48] I did say it might not be right [01:48] my first go in anger [01:49] others so far have been little test scripts [01:49] * thumper pokes the help command [01:49] i wasn't criticising :-) [01:57] school run, bbs [03:30] * thumper sobs quietly [03:31] but in a happy way as I understand it, just... ugh [03:33] * thumper now understands all the command stuff [03:33] now to just write a useful help command [03:34] (FSVO all) [04:30] davecheney: we don't do much with internationalization do we? [04:30] davecheney: is there any locale support in go? [04:34] virtually none [04:34] ^ as in, we do [04:36] locale support ... nothing that looks like the c tools (iconv isn't it ?) [04:36] not sure [04:37] I'm just going to pretend the entire world speaks english [04:37] :) [04:37] that's the spirit [04:53] davecheney: thumper: this seems to work ok now, http://pastebin.ubuntu.com/1655490/ [04:53] davecheney: what does %q do in fmt.Errorf ? [04:53] i could put this into goose but it doesn't belong there. what's our policy for utilis like this? [04:53] thumper: "quotes" things [04:54] but will also print them 'safely' for some value of safely i can't remember at the top of my head [04:55] ta [04:57] hmm... [04:57] I wonder why we write help to stderr instead of stdout [05:03] davecheney: ok, once I've been editing away on some go files [05:03] davecheney: how to I (re)build the juju command?? [05:08] davecheney: nm [05:09] davecheney: nah, had that wrong [05:10] how do I rebuild? [05:10] wallyworld__: you know? [05:10] I just want to get this bit working and I can finish and have a drink [05:11] * thumper taps his fingers [05:11] no pressure guys [05:21] hmm... [05:21] found install [05:21] seems to be mostly going now [05:21] enough for me to call time and get a drink anyway [05:21] caio [07:17] davecheney, thanks for reverting the store [07:17] fwereade: no worries mate [07:18] i think i'm missing email at the moment [07:18] or was that just poorly communicated [07:18] davecheney, I think it was screwed-up communications [07:19] oh well, it's fixed now [07:20] davecheney, yeah -- and it's really nice to have the thing I was planning to do first thing already done when I come in :) [07:21] davecheney, incidentally, re external dependencies... I am becoming entirely dissatisfied by the state of go in this regard, and starting to think that the only way to get anything resembling a consistent build is to copy every damn thing into thirdparty/ [07:22] davecheney, which sucks, obviously, but seems to suck strictly less than every other option [07:23] fwereade: i understand [07:24] this is a problem that has occupied my thoughts for some time [07:25] i do not have anything to add at this point [07:25] davecheney, even goamz etc -- even if it's stable API-wise, new things are being added and it's only a matter of time before we start getting weird bugs that are eventually tracked down to "oh, you didn't update XXX? silly you" [07:25] davecheney, (not even new things -- just little fixes, improvements, etc) [07:27] davecheney, another thought -- the 4th option is for us to publish, ourselves, to a public bucket we control (on *any* openstack, so long as that bucket in configured for unauthenticated access) and make sure the public-bucket-url defaults to that [07:27] fwereade: yes, part of the problem is API breakage, major version changes [07:28] the other, much larger half, is behavioural changes, X.1, and X.Y.1 changes [07:28] davecheney, yeah, exactly [07:28] fwereade: yes, on the 4th optoin, i need to get a clear indication from the buisiness what these 'network hostile' customers look like [07:29] davecheney, (btw, we should *also* I think be checking hashes of the tools we use, and I don't think we do that... it's crazy that we have it for charms but not for the actual code that we run) [07:30] fwereade: i agree, if we have to stay with the tarball approach, then hashs' or maybe even sigs would be the order of the day [07:32] fwereade: on a scale of annoying to fucking us daily, where would you place the pkg version problem ? [07:35] davecheney, persistently annoying... the actual *detected* fuckery is relatively rare but I suspect that (1) probably someone is inconvenienced by it every single day and (2) critically, none of us can *tell* when we might be being fucked by it [07:35] davecheney, it's the uncertainty more than anything else [07:35] davecheney, fwereade, mgz: morning! [07:36] fwereade: understood, seconded [07:36] davecheney, eg bac produced a very helpful paste yesterday, with juju-core's bzr version and the go version at the bottom [07:36] i don't like that double checking your own local environment has to be the first step when debugging biuld breakage [07:36] davecheney, but that isn't actually enough, and I couldn't esily give you a complete list of external packages we depend on [07:37] davecheney, trashing src/ every time you have a problem is absurd, but it's starting to seem reasonable in my mind [07:37] rogpeppe, heyhey [07:38] fwereade: for you and me and rog, it is acceptable [07:39] for blue, who have branches in goose and juju-reo, it is totally unacceptable [07:39] davecheney, I cannot ask anyone else to do that without permanently staining my honour [07:39] davecheney, exactly [07:51] wallyworld_: ping [07:51] hi [07:52] wallyworld_: http://play.golang.org/p/YXAHy5kBfD [07:52] * wallyworld_ looks [07:53] wallyworld_: that's the classic method [07:53] ok. the good thing about my home grown approach is you don't need a New() [07:54] in any case, why isn't this part of the std libs i wonder? why is everyone forced to roll their own? [07:55] wallyworld_: it's not that common a requirement (people usually need to wait for channels to be available or functions to return, not mutexes to be taken), and it's trivial to implement, as you can see. [07:56] wallyworld_: we could make the above example not need New too [07:56] it's very common in any environment i've ever worked in. trivial yes, but still code that has to be written and live somewhere. goose? juju-core? yet another 3rd party lib we create? [07:57] i guess i'm saying it should have been included out of the box [07:57] wallyworld_: i've written go for quite a while now, and although i know that idiom, i've never required it [07:58] wallyworld_: i think it's probably because mutexes aren't usually used to guard long-lived things [07:58] really? ok. forget Go per se. when using mutexes with network or other resources, you need to timeout to prevent deadlocks etc [07:59] i guess compared to java.util.concurrent, i'm finding Go's concurrent support to be a bit primitive [07:59] so, thanks for the code btw, it does look nice. but where should we put it? [08:00] not Goose, cause then other stuff can't easily use it [08:00] wallyworld_: honestly, it's trivial - if you need it, just copy and paste it. [08:00] or maybe just stick it there for now [08:00] aargggh noooo. not copy and paste [08:01] Morning [08:01] TheMue, wallyworld_, heyhey [08:01] hi [08:01] Somewhat flaky connection this morning. :/ [08:01] fwereade, wallyworld_ : Hiya [08:01] wallyworld_: seriously, it's about as trivial as a for loop [08:01] fwereade: I yesterday pushed the last proposal again. [08:02] TheMue, cool, thanks, I'll take a look as soon as I can [08:02] rogpeppe: that's not the point - it's still business logic that can and should be re-used via being packaged [08:03] Go is verbose enough already without yet more cut and paste - they all start to add up [08:04] wallyworld_: out of interest, what are you using the mutex for? [08:05] rogpeppe: to guard the Authenticate call in a Goose client (which we are now sharing between all nova, swift etc instances). we can't allow 2 users to attempt to Auth at the same time [08:05] but Authenticate() is a network call [08:05] so we don't want to block indefinitely [08:05] wallyworld_: presumably the network call itself will time out eventually? [08:05] perhaps. who knows. we can't asume that [08:06] even if it does, the timeout might be tool ong [08:06] wallyworld_: it's easy to make a network call time out if you want [08:06] wallyworld_: i think i'd go in that direction (having one thing timing out on the operation in question) rather than have lots of things all timing out on the mutex [08:06] rogpeppe, http://thecodelesscode.com/case/71 [08:07] we are using http.client as the transport - request.Do() and all that - you saying we can make that time out? [08:11] fwereade: nice. i think i've missed the point though. [08:11] wallyworld_: yes, in two ways, one much easier than the other [08:11] rogpeppe: out of curiousity, how would i get rid of the New() in the implementation using a channel, if the channel needs to be make()ed ? [08:11] wallyworld_: use a mutex :-) [08:11] rogpeppe, I was alluding to the "it will eventually time out" assertion, probably not in a very constructive way [08:13] ah, yes, ok [08:14] wallyworld_: http://play.golang.org/p/vooGdZw5aa [08:14] yes, that seems sensible, thanks [08:15] bbiab [08:15] wallyworld_: that's actually a useful general technique for making any zero value usable without a New function. [08:15] rogpeppe: i'll look at the http request timeout, but it will need a little thought - we only really want to do it for Auth calls, since they are the only ones that need to be serialised. we don't really need to do anything for other send request calls [08:16] so having a mutex with a timeout works nicely [08:16] wallyworld_: if the auth times out, does it matter if the actual authenticate call is still going on the background? [08:17] not really - i don't think so, but need to work through that scenario [08:17] wallyworld_: that is, is the authenticate call idempotent? if it's failed once, do you want everything that wants to use it to fail? [08:17] without it succeeding, nothing can really work from that point [08:17] since nothing will have a token [08:17] nor the endpoint urls [08:19] rogpeppe: thanks for the input, i have to head off to soccer. i'll revisit what's been done with the new info next SOD [08:20] wallyworld_: ok [08:20] wallyworld_: i have a little example for you when/if you come back this evening [08:21] i have a few more minutes before i have to go [08:23] wallyworld_: http://play.golang.org/p/sd1Cx2Ey-c [08:24] wallyworld_: where the authenticate function is standing in for your Auth call. [08:28] rogpeppe: excellent thanks. it will take me a little time to digest. i'll read it in detail after soccer [08:29] wallyworld_: look ma, no mutex timeouts! :-) [08:30] rogpeppe: yes indeed. i wonder how reusable it is though. eg with a mutex.TryLock() type operation, it can be used trivially as needed [08:31] this latest solution *seems* to mix a fair bit of boilerplate with the business logic [08:31] but i need to read it fully [08:31] whereas with the mutex.TryLock(), there is no boilerplate in the business logic [08:32] anyway, gotta run, hopefully back later. thanks again for the code, i'll learn something for sure just reading it [08:33] wallyworld_: see ya! [08:33] wallyworld_: have a good footy session [08:33] i will hopefully, thanks :-) [08:37] wallyworld_: for the record, here's some of the boilerplate factored out: http://play.golang.org/p/XHJ_PLO6RR [08:59] fwereade: ping [08:59] dimitern, heyhey [08:59] fwereade: hey :) [08:59] fwereade: about that CL from yesterday [08:59] fwereade: https://codereview.appspot.com/7317045/ [09:00] dimitern, rogpeppe: IMO it is worth having a package just for those constants -- is there some cost to a package that I'm not aware of? [09:00] fwereade: how do you feel about moving hook inside charm/, rather than have it in charm/hook/ [09:00] dimitern, rogpeppe: I'm probably +1 on chook rather than uhook [09:01] fwereade: i see the hooks as very closely related to charms, so can't really see that it needs a new package. but i don't mind much. no there's no particular cost to a package. [09:01] dimitern, rogpeppe, I don't have a strong argument tbh, it's just that charm.HookUpgradeCharm feels irritatingly verbose compared to chook.UpgradeCharm [09:01] fwereade: if the constants were in charm, there would be no need for the awkward charm/hook uniter/hook name clash [09:02] fwereade: i'd go for charm.HUpgradeCharm, i think [09:02] rogpeppe, eww ;p [09:02] rogpeppe: but we might have to rename a few things to make them unambiguous [09:02] :) [09:02] dimitern, rogpeppe: ha: charm/hooks vs worker/uniter/hook would resolve it [09:03] +1 [09:03] dimitern, rogpeppe: and read better just about everywhere, I think [09:03] fwereade: so rename it to charm/hooks [09:04] dimitern, I'd like to see what rogpeppe thinks [09:04] rogpeppe: sounds good? [09:04] * rogpeppe sucks his teeth [09:05] fwereade: the singular/plural distinction seems... arbitrary [09:05] rogpeppe, hooks.UpgradeCharm vs hook.Info seems to read pretty well to me [09:05] fwereade, rogpeppe: uniter/hookinfo and use hi.* ? [09:06] fwereade: haven't you got that the wrong way around? [09:07] rogpeppe, I don't think so... hooks enumerates the available hooks, while hook is the uniter-specific package dealing with info attached to single hook executions [09:07] fwereade: oh yeah, i'd forgotten that UpgradeCharm is a hook :-) [09:09] rogpeppe, yeah, the actual upgrade code is mostly in uniter/charm, which itself collides with charm :( [09:11] fwereade: ha ha! http://godoc.org/launchpad.net/juju-core/worker/uniter?view=import-graph [09:11] fwereade: that's a bit better: http://godoc.org/launchpad.net/juju-core/worker/uniter?view=import-graph&hide=1 [09:11] rogpeppe, my eyes, my eyes; the goggles, they do nothing [09:12] rogpeppe, yeah, slightly less awful [09:12] fwereade: why do we need a uniter/hook package again? [09:14] rogpeppe, primarily to avoid the everything-friends-with-everything situation in (eg) state that makes it so hard to tell what uses what in arbitrarily inappropriate ways [09:14] cool graph, i didn't know you can generate these from godoc [09:14] fwereade: does anything outside of uniter itself use hook.Info? [09:14] rogpeppe, uniter/relation [09:16] rogpeppe, for the relation hook queues [09:16] fwereade: ah, of course, i thought it was something like that. [09:17] fwereade: presumably, it *could* be relation.HookInfo ? [09:17] rogpeppe, fwereade: do do we reach a consensus? [09:18] rogpeppe, nope, it's not just for relation hooks [09:18] rogpeppe, and I'm really not keen on just jamming everything into uniter [09:18] fwereade: i mean theoretically, but yeah [09:18] fwereade: i don't think you *can* jam it into uniter [09:18] fwereade: because then there would be a cycle [09:19] rogpeppe, we could if we jammed relation in as well -- I seem to recall yu arguing for that once, but I was not then and am not not convinced that's a good idea [09:19] s/not not/not now/ [09:20] rogpeppe, can we agree on charm/hooks and uniter/hook on the basis that it leads to clarity and readability at the "cost" of having more smaller packages that do one thing only? ;p [09:21] fwereade: i think i'd go for uniter/hookinfo, as that's really what it is [09:22] rogpeppe, dimitern: the other possible direction *may* be uniter/state (not that *that* doesn't have aliasing problems...) [09:23] fwereade: this is a package with one type with a single method, right? [09:23] rogpeppe, yeah, I think so [09:24] fwereade: in fact... i've just seen the logic for charm/hooks and uniter/hook [09:24] fwereade: move uniter/hook stuff in uniter/state ? [09:24] fwereade: let's go for that [09:25] fwereade, rogpeppe: ok, so finally :) charm/hooks then? [09:25] dimitern: naah. we don't need another state package that's just there for hook info :-) [09:25] rogpeppe, cool :) [09:25] dimitern: yeah. sorry for the swithering. [09:25] :) np [09:26] rogpeppe, dimitern: the suggestion would have been to move uniter.State in there as well, but I'm not sure what that costs usor whether anything blocks it: so, yes, charm/hooks and uniter/hook [09:26] * fwereade does the happy dance [09:27] dimitern: the godoc package graph functionity is cool, isn't it. it's the first time i've used it on juju... [09:29] rogpeppe: it would've been even cooler if you can rearrange the nodes like juju-gui does with machines [09:29] dimitern: definitely [09:29] dimitern: i'm sure gary would accept a patch :-) [09:53] TheMue, I've sent some more thoughts [09:53] TheMue, this might go better, I think, if you do the following: [09:54] TheMue, 1) trivial CL dropping the lxc/network files [09:54] TheMue, 2) CL for just the backend, with tests [09:55] TheMue, 3) CL with the Storage implementation, with tests (as they are today) [09:56] TheMue, 4) CLs with things like the config, the provider, the environ, etc, with tests [09:56] fwereade: Thx for the review [09:57] fwereade: Yep, the real CL for the environ etc has already been intended to follow. This has only been a consolidation. [09:57] fwereade: Maybe already done too much, so I'll split it. [09:58] TheMue, thanks, I'd like that [09:59] fwereade: No problem. [09:59] * TheMue still tends to create too large CLs, shit. *sigh* [09:59] TheMue, I think we definitely need independent tests for the backend though -- and if you're splitting it, it might be a good idea to put it in its own package to prevent the pain of unpicking it later [09:59] TheMue, I offend on that front myself, I know :( [10:00] TheMue, when I manage not to everything goes much smoother though ;) [10:01] fwereade: Yep [10:02] so if I have a map[string]bool as an argument to a func, does it have to be *map... to change it in place? [10:03] dimitern, no, don't think so, a map is already a reference [10:03] fwereade: cheers [10:04] dimitern, http://play.golang.org/p/GEEcsp2KPp [10:06] fwereade: yeah, I keep getting mixed up occasionally with the reference/pointer logic for maps/slices still [10:06] dimitern, I know the feeling, it'll bed in soon enough :) [10:09] rogpeppe, fwereade: here it is https://codereview.appspot.com/7317045 [10:09] dimitern, cheers [10:10] i'll change the description on submit - i couldn't find a way otherwise [10:15] dimitern, you can do lbox propose -edit [10:15] dimitern, looks good though, just a remark or two [10:16] bbiab [10:16] fwereade: ok, 10x [10:30] fwereade: since i'm not aware how to define a const slice, I'll just make a method that returns a copy of the private for both types of hooks [10:35] rogpeppe: ping [10:36] dimitern: pong [10:36] rogpeppe: you're ok with the CL? [10:36] dimitern: looking [10:37] fwereade: i really don't think we need to copy UnitHooks every time it's used [10:37] fwereade: there are plenty of places where we have globals that are immutable by convention [10:38] rogpeppe, how is "immutable by convention" not just an "invitation for bugs"? [10:38] fwereade: there are many possible places for bugs. this one is not easy to trip over. [10:41] fwereade: in my view doing this it would create more garbage for very little gain. [10:44] rogpeppe: i don't see a problem copying a private slice like this [10:45] dimitern: i think it sets a bad convention. it's really ok for a package to expose tables of data directly. [10:45] dimitern: see, for example, the unicode package in the standard library [10:46] rogpeppe, if all your friends jumped off a bridge... ;p [10:46] fwereade: there would be good reason for it... yeah :-) [10:46] fwereade: what do you think can go wrong with slices? somebody changing it silently? we'll catch this is a review [10:49] dimitern, I think your faith in the review process is perhaps a little too solid... bugs can and do make it through review, even obvious ones... but if people really object to this small and cheap nod towards safety/sanity I can't be bothered to fight it :) [10:50] i'm ok either way, I really want to land this, so I can finish my bug fix [10:50] dimitern, then I leave it to your consicience :) [10:51] fwereade: I already did what you suggested [10:52] rogpeppe: is it really that big of a difference? [10:55] dimitern: i think of it as increasing complexity due to unjustified paranoia about side effects. we use these things in precisely one place, in a range statement. making a copy seems total overkill. [10:56] rogpeppe, that sounds like an assertion that we will only ever use these things in one place... [10:56] dimitern: if we think it's justified here, then we will never be allowed an exported data table in any package, and i think that's not great [10:57] fwereade: i think it's easier to guard against undesired mutation than you fear [10:57] * dimitern gets a coffee and sits waiting [10:58] rogpeppe, what is it that's difficult about copying them? [10:58] fwereade: it's more code and more garbage generated for no reason. [10:59] fwereade: i think a table variable is a very elegant and simple thing. i don't think we should disallow ourselves that. [11:00] rogpeppe: are the std lib packages using some special built-in way to define immutable exported slices? [11:00] dimitern: nope [11:01] dimitern: you can change 'em, but that's your own fault if you do. [11:01] rogpeppe: so you can change them and screw up everybody using the package from that time on? [11:01] dimitern: sure [11:01] dimitern: you can change os.Stdin too [11:02] fwereade: can you point out your concerns about changing these exported slices in a bad way? [11:02] dimitern: or image.Black, or os.ErrNotExist [11:03] rogpeppe: the added code is really 6 lines including comments, and since the slices are tiny making a copy doesn't at all sound like an overkill [11:03] rogpeppe, are you arguing that it is *good* to be able to change, say, image.Black? what's the benefit? [11:03] fwereade: i'm arguing that it's good to be able to expose simple static values as variables [11:04] fwereade: and that that's not actually error prone - you have to go out of your way to fuck it up. [11:04] well, if taking go std lib as best applied practices, then it certainly seems to me following the same pattern is clearer and not unexpected behavior [11:04] fwereade: yes, if we had a language with const, we'd probably use that, but we don't, and it works ok. [11:05] dimitern: +1 [11:05] I wish go had const slices [11:06] or a way to mark an exported immutable, but alas.. [11:07] how about a programmatic solution - something like go fix or similar, which is able to detect such things? [11:07] dimitern, like I said, it's between you and your conscience -- I just wish there was a better argument on the other side than "the stdlib does it so it must be ok", with an implied "encapsulation is teh stupids because everybody always considers every ramification of their actions" :( [11:07] going through the code and making sure things marked in some way (eg. comments) are not touched [11:08] fwereade: i certainly see your point [11:08] fwereade: and agree encapsulation is great, but why haven't we done that before (copy exported data) - or this is the first case? [11:09] fwereade: I mean *have we done* [11:09] dimitern, I have not gone through and checked everywhere -- but certainly ISTM to be good hygiene to (eg) copy a struct's internal maps when returning it from a method [11:10] dimitern, I feel it's essentially the same case [11:10] fwereade: "if all of your friends jumped off a bridge": http://xkcd.com/1170/ [11:10] jam, indeed :) [11:10] has a good point that yeah, if everyone I know seems to think that the most rational thing to do is X, they are probably right [11:10] jam: hey :) i thought of that as well [11:10] I had it uppermost in my mind as I typed it, believe it or not [11:11] I probably agree that "immutable by convention" is asking for that one case where someone doesn't know the convention. [11:11] copying a *slice* is trivially cheap, because it is just a reference object, but I haven't looked at the code review. [11:11] jam: hopefully we ourselves *do* know the convention :-) [11:12] jam: i think we were talking about copying the contents of the slice here. [11:12] rogpeppe, that is a hideously short-sighted attitude IMO [11:12] rogpeppe, if we get this right, people will still be working on the codebase N years in the future [11:12] fwereade: i think it's reasonable to consider the std lib as best practice. [11:12] rogpeppe, like go get, huh? [11:13] fwereade: that's not the standard lib, it's a command. [11:13] jam: that's the CL https://codereview.appspot.com/7317045/ if you want to take a look [11:14] rogpeppe, it would still seem to argue that the authors of go can do profoundly short-sighted things [11:14] fwereade: what you're saying is that we should never be allowed *any* global exported variables from any package. [11:15] fwereade: and i'm slightly surprised at your attitude, coming from python where you can change *anything* :-) [11:16] rogpeppe, why do you think I'm so happy doing go? this sort of thing is seductive but IME it screws you up in the not-very-long-term [11:16] fwereade: it's a continuum. [11:16] rogpeppe, and I'm not saying *never* -- but I think there should probably be a good reason *for* doing so, much like using interface{} [11:16] fwereade, rogpeppe: how about a compromise? IsUnitHook(string) bool; IsRelationHook(string) bool (or Kind) ? [11:17] no that won't do actually - i still need to be able to iterate over them.. [11:17] dimitern: yup [11:18] dimitern: BTW i just posted a couple of comments to the CL [11:18] rogpeppe: cheers [11:19] fwereade: honestly, we don't lose sleep about people mutating state.ErrUnauthorized. i don't think we should worry about a global UnitHooks either. [11:19] dimitern, rogpeppe: I first said I was not interested in fighting it almost 30 mins ago... I do not think I'm likely to be convinced that "immutable by convention" is ever a good idea, so please stop trying to convince me that it's somehow *better* than "actually immutable"... but I can also accept that there are much bigger deals [11:20] rogpeppe: you're not exactly right about IsRelation being the opposite of IsUnit, because relation hooks are suffices, and unit hooks are exact matches [11:20] dimitern: it is the opposite in terms of Kind, no? [11:21] ok, another thought - a func taking a callback func and iterating over unit/relation hook kinds? [11:21] rogpeppe, the furthest I'll go is "not a bad enough idea to lose sleep over" [11:21] rogpeppe: no, because IsRelation("foo-relation-changed") is false [11:22] dimitern: isn't it true that for all kinds k defined in charm/hooks, IsRelation(k) == !IsUnit(k) ? [11:22] rogpeppe: in terms of Kind, yes, but with actual strings I have to check for in expand/bundle (file names), it won't work [11:23] dimitern, please just go with whichever you personally prefer [11:24] i'm between the anvil and the hammer here :) I don't want to choose sides of this merely philosophical debate [11:24] dimitern: you don't seem to call IsUnit anywhere. [11:25] rogpeppe: no, I added it for a slightly different logic, now it's useless, so I agree and will remove IsUnit() [11:26] there's a fairly long discussion on golang-nuts about immutables: https://groups.google.com/forum/?fromgroups=#!topic/golang-nuts/BnjG3N77Ico [11:27] to satisfy both views, we can have something like func (kind Kind) RelationHooks() []Kind and the sane for unit hooks [11:28] s/sane/same/ [11:28] fwereade: that's no better tbh [11:28] oops [11:28] dimitern: ^ [11:29] dimitern: go with UnitHooks() []Kind if you like. i'll just sigh from a distance :-) [11:29] rogpeppe: but since we already have methods on Kind, it seems like the right place, no? [11:29] dimitern: it makes it sound like the it's returning a slice that has something to do with the receiving kind. [11:29] s/the// [11:30] rogpeppe: yeah, and if you don't have a kind, you have to do smth. like Kind("").RelationHooks() .. ugly [11:30] dimitern: yup [11:30] rogpeppe: so you agree to have UnitHooks() []Kind ? [11:31] albeit reluctantly i guess.. [11:32] dimitern: *sigh* yes. does that mean i have to make testing.ServerCert into a function now? [11:33] rogpeppe: tyvm; I don't think we're setting a precedent here [11:33] i really do feel it's a slippery slope actually [11:38] fwereade, rogpeppe: final look? https://codereview.appspot.com/7317045/ [11:39] dimitern: that's worse, sorry. it doesn't fix fwereade's issue, and if a function returns a slice, it *really* looks like it's ok to modify. [11:40] rogpeppe: ok, maybe I didn't get what I was supposed to do? [11:40] dimitern: i think you need to do. func RelationHooks() []Kind{a := make([]Kind, len(relationHooks); copy(a, relationHooks); return a} [11:40] rogpeppe: ah, ok - it'll not copy it simply by returning it [11:41] dimitern: indeed - slices are references [11:43] rogpeppe: fixed [11:45] ready to submit then? [11:46] dimitern: LGTM [11:46] rogpeppe: thanks! [12:39] I have this bug fix here - https://codereview.appspot.com/7305096 - anyone care to review? [12:42] fwereade, rogpeppe, jam? ^^ [12:54] dimitern, re dummy's install hook [12:54] dimitern, is there anything currently checking its permissions? [12:54] fwereade: no [12:55] dimitern, best to just fix it then, I think :) [12:55] fwereade: but how? [12:55] dimitern, I think you can just chmod it? doesn't bzr see that as a change? [12:55] fwereade: not sure how to set +x on it, tried chmod +x but bzr didn't pick it up [12:55] dimitern, bah, ok... anyone? [12:58] fwereade: ah, you know, that's not the problem [12:58] dimitern, oh yes? something else does check that? [12:58] fwereade: my bad - I'm not checking before setting them and issuing a warning [12:58] dimitern, I've sent a few comments anyway [12:59] fwereade: dummy/hooks/install was already +, that's why bzr didn't pick it up [12:59] +x [12:59] fwereade: cheers [12:59] dimitern, ha! I completely missed that :/ [13:00] fwereade: what do you mean about the block obfuscating the test? the for loop? [13:00] dimitern, the checks for the test data being the test data -- what relations are there, what it's called, what its rev is [13:01] dimitern, and as I say at the top I'd like those tests most of all if we had some hooks with problems, some without, and some missing [13:01] fwereade: not sure I get your point [13:02] dimitern, sorry, my first point or my second point? [13:02] fwereade: you mean the asserts before the loop are unnecessary? [13:02] dimitern, I think so, I'm certainly open to argument otherwise [13:03] fwereade: well, I agree they don't bring anything more than validating the all-hooks charm has what we need [13:04] fwereade: removing them does not weaken the test i think [13:07] fwereade: not sure about logging the name of the charm beforehand - this will cause assertions on log fail in other cases [13:07] dimitern, grar [13:07] fwereade: maybe instead, log the hooks as "charm/hooks/name"? [13:08] fwereade: and take charm name from meta.Name [13:09] dimitern, probably better not to put it into an apparent path if it doesn't refer to a real one, I think I preferred the original form [13:10] fwereade: without charm name or with? [13:10] dimitern, can we move that bit down, though, to where we do check setExec? [13:10] dimitern, with is fine [13:10] fwereade: sure, it seems the right place [13:10] dimitern, cool, ty [13:11] fwereade: and i'll add additional assets in dir_test to check perms after checking the logged warnings [13:11] dimitern, cheers [13:11] ha! i've worked out why i checking for ErrShutdown after server close is inherently racy. [13:11] obvious really, doh! [13:29] * fwereade cheers at rogpeppe [13:31] rogpeppe, incidentally, https://codereview.appspot.com/7324049/ is back in review [13:33] fwereade: thanks, i'll try to have a look, although i'm making a concerted effort to move forward with the api today. the race (not a bug, just my own stupidity in not understanding the issue) has set me back a bit. [13:34] rogpeppe, no worries, I basically did everything you suggested -- but I decided that it was too much effort messing around with floatifying, so 100 cpu-power == 1 ECU [13:34] fwereade: sgtm [13:34] rogpeppe, cheers -- TheMue or dimitern, if you'd like to take a quick look I'd appreciate it [13:36] fwereade, rogpeppe: PTAL https://codereview.appspot.com/7305096/ - suggestions addressed [13:37] rogpeppe, one thing that is starting to bother me, though... I think the implicit one-environ-per-state will be *much* harder to fix after we release (much like the entity id types) [13:37] rogpeppe, I have a horrible feeling that it's not possible to address it in time though [13:39] fwereade: why do you think it's going to be particularly hard? [13:39] fwereade: (not that i necessarily disagree) [13:40] fwereade: you're thinking about cross-environment juju? [13:40] rogpeppe, because I think all the cross-collection keys will need an e#%d# prepended [13:40] rogpeppe, I'm thinking about multitenanting [13:41] rogpeppe, I know that is not a deliverable, so maybe I should put that on the "meh, fix it later" pile, but a lot of my current stress comes from feeling we have been too quick to do that in the past [13:41] fwereade: i don't think we'd particularly need someone's existing juju environment to upgrade to multitenanting [13:42] rogpeppe, just adding multitenanting should not break an existing one, though [13:42] fwereade: well, even if we do - it's just a major upgrade, we stop the world, fix the database, upgrade the clients, and carry on [13:42] rogpeppe, we have done no work to enable that [13:42] fwereade: indeed - major version upgrades need to be done [13:43] fwereade: but we can upgrade to allow major version upgrades, *then* change to support multi-tenanting [13:43] rogpeppe, I'm not sure that's trivial either... and while we can't do it, we're basically locked to our existing schema, which makes me nervous [13:43] fwereade: it's not trivial, but one of the main points of major version upgrades is to allow us to change db schemas, no? [13:44] rogpeppe, yeah, but *not* changing our schema is even better :) [13:46] fwereade: another possibility is keeping one State per environ, but making it possible to have several States in the same mongo; we could use a collection prefix. [13:46] rogpeppe, indeed, that might actually be smarter, hard to tell at this point [13:47] fwereade: it would certainly give more isolation [13:47] rogpeppe, anyway, I must not distract you from the API, sorry :) [13:47] fwereade: thanks :-) [13:47] * rogpeppe undistracts [13:48] * dimitern lunch [13:59] *sigh* Bad net today. *hmpf* === wedgwood_away is now known as wedgwood [14:20] fwereade, dimitern, jam: this should fix at least one of the common errors we're getting in trunk: https://codereview.appspot.com/7310095/ [14:20] rogpeppe: i'm on it [14:21] i'm a bit worried by this though - i've never seen it; has anyone else other than jam? http://pastebin.ubuntu.com/1654670/ [14:22] rogpeppe: I haven't [14:23] rogpeppe: reviewed, care to reciprocate? https://codereview.appspot.com/7305096/ [14:23] dimitern: ok! [14:25] dimitern: thanks BTW [14:25] rogpeppe: yw [14:31] dimitern, sorry, a couple more comments, looking good though [14:31] fwereade: sure, np [14:33] fwereade: you're right about BundlePath(), I'll fix it [14:34] and the other thing as well, i'll change the logic to handle hooks dir paths better [15:01] rogpeppe, https://plus.google.com/hangouts/_/539f4239bf2fd8f454b789d64cd7307166bc9083# [15:05] mramm, rogpeppe, TheMue: sorry, restarting firefox, brb [15:18] mramm, rogpeppe, TheMue: ffs... restarting whole machine [15:18] fwereade: k [15:22] rogpeppe, screw it, updates seem like my only hope :/ [15:27] dimitern: you've got a review. that was a bit of an unequal swap! [15:27] * fwereade feels lucky [15:27] rogpeppe, mramm, TheMue: heh, are we done? [15:28] rogpeppe: tyvm [15:28] rogpeppe: sorry :) [15:28] fwereade: yea [15:29] mramm, did we come to any conclusions? [15:29] we ended with me saying that I thought that a vendor dir was a mostly effective way of handling dependencies back in the early days of svn and python [15:29] and given that we are still a bit of a similar bleeding edge state with go [15:30] I think it makes sense [15:30] and there was general agreement -- with roger picking up the job of creating an experimental branch with everything "vendored in" to bzr [15:31] mramm, awesome; rogpeppe, thanks very much [15:42] fwereade: one problem with the third-party directory possibility: go test ./... takes a lot longer... [15:42] rogpeppe, ha, I bet :/ [15:44] rogpeppe, incidentally, re printing log messages... making logging sane on the CLI feels like an awful lot of work for very little payoff at this stage [15:45] fwereade: yeah, it was just a passing thought [15:45] rogpeppe, I wanted that, but felt that gating this bug fix on "oh, and fix our logging while you're at it" would be unhelpful :) [15:45] fwereade: yeah, that's why i said "aside" in the comment [15:45] pwd [15:46] rogpeppe, no worries, I wasn't accusing you of doing the same... maybe slightly hoping you had a cunning plan I hadn't thought of, at most :) [15:49] rogpeppe, how much longer, incidentally? [15:49] fwereade: i'll tell you in a mo [15:49] rogpeppe, ouch [15:49] fwereade: no, i haven't started the tests yet [15:49] rogpeppe, ah, phew :) [16:14] fwereade, rogpeppe: I think it should be much better now - https://codereview.appspot.com/7305096/ - I even caught an issue I haven't noticed, once fixing the tests it popped out :) [16:15] dimitern, awesome, I'll take a look -- and would you give https://codereview.appspot.com/7324049/ a once-over please? [16:15] fwereade: sure, i'm on it [16:30] fwereade: reviewed [16:34] dimitern, cheers -- and LGTM for yours :) [16:34] fwereade: tyvm [16:35] rogpeppe: I think I addressed all you comments as well [16:38] fwereade: the thirdparty thing is a time waster. i'm leaving it for now. goyaml tests fail for me against tip :-( [16:39] rogpeppe, heh, that doesn't sound great :( [16:39] fwereade: (which is worrying, but not what i want to be worrying about right now) [16:39] fwereade: i suspect unsafe buggery [16:40] rogpeppe, sounds all too plausible [16:43] dimitern, not sure how space-stripping makes things simpler -- as it is I handle both "k1=v1" "k2=v2" and "k1=v1 k2=v2" cleanly (to fit both expected command line forms) [16:43] dimitern, expand? [16:44] dimitern, also seems like it would make "mem=" constraints very tricky to get right [16:44] fwereade: that was just a thought really [16:44] dimitern, no worries [16:45] fwereade: think of handling extra spaces though [16:45] fwereade: like " k1=v1 " ? [16:46] fwereade: Split(" ") will give you empty entries in that case? [16:46] dimitern, hmm, yeah, maybe I should trim external spaces and ignore empties when split [16:46] dimitern, thanks [16:47] fwereade: yw [16:50] rogpeppe: when you can, please have a final look https://codereview.appspot.com/7305096/ [17:18] gotta dash gents, happy weekends all [17:18] fwereade: happy weekend! [17:24] It looks like atlanta is the confirmed city for the sprint [17:25] mramm: cool, dates as well? [17:27] starting on the 4th [17:27] we are still working on finalizing the hotel details [17:27] mramm: great [17:28] mramm: so we should be getting a mail about what flights to pick? [17:36] rogpeppe: ping [17:37] fwereade: Enjoy your weekend. [17:38] dimitern: fly in on sunday out on friday night or saturday [17:38] book it through the agent and you are all good [17:39] mramm: ok, I'll contact the agent next week [17:42] Will do it on Monday too, in on Sunday, out on Friday evening/night. Found a good connection. [18:06] dimitern: pong [18:06] dimitern: (sorry, been in a meeting) [18:07] rogpeppe: sorry, I submitted it already, but if you thing I should've changed something, take a look pls [18:07] dimitern: that's fine. i've been a bit frantic all day. haven't reviewed as much as i could [18:07] rogpeppe: no worries, it's like that some days :) [18:08] rogpeppe: thanks for all reviews [18:08] dimitern: i've been frantic *and* made no significant progress, which is the really annoying thing. bug fixing all the way. [18:10] rogpeppe: sorry, if was bugging you too much actually [18:11] dimitern: np [18:11] but this thing - I wanted to get over with it already - whole week almost just for one bug fix :| [18:11] dimitern: yeah i know. sorry about that. [18:11] dimitern: i'm just trying to avoid being a critical-path-blocker for the juju-gui guys [18:12] rogpeppe: i see, what's up there? [18:12] dimitern: they're going to start implementing command-line-equivalent functionality in the api server [18:12] dimitern: essentially copying stuff from cmd/juju and making it available from state/api [18:12] rogpeppe: they're using the api already? [18:13] dimitern: they've made a connection to it... [18:13] dimitern: yeah [18:13] so no longer juju status polling [18:13] dimitern: no watchers yet. that was my main goal for this week, and i've not got there yet dammit [18:13] rogpeppe: sounds good, but I guess it's hard to integrate initially [18:14] dimitern: we have a bit of a problem too, that i'm just trying to work out the best way to get around [18:14] rogpeppe: i'll follow the development with keen interest, it's coming along nicely so far [18:15] dimitern: which is that quite a few of the command line commands use a juju.Conn (which is a (State, Environ) pair) but we don't have access to the Environ in the api server [18:15] dimitern: so the question is: can we make them do without the Environ? [18:15] rogpeppe: that's by design or not ready yet? [18:16] dimitern: for instance, deploy uses the environment storage to upload the charm [18:16] dimitern: the plan is to store charms in mongodb, but we don't do that yet [18:16] rogpeppe: blobs? is that sane performancewise? [18:17] dimitern: i dunno. is there a particular reason it should be bad? [18:17] dimitern: a charm wouldn't necessarily need to be stored as a single blob [18:17] rogpeppe: well, I don't know about mongo, but doing so in relational dbs is rarely a good idea [18:18] dimitern: it was part of the reason we moved to mongo, so i hope it won't be too bad. [18:18] dimitern: this makes it sound like it's ok: http://blog.mongodb.org/post/183689081/storing-large-objects-and-files-in-mongodb [18:18] rogpeppe: cool [18:19] rogpeppe: sounds way better than using blobs in a table [18:19] dimitern: the mgo package in supports gridfs: http://godoc.org/labix.org/v2/mgo#GridFS [18:20] s/in/even/ [18:20] rogpeppe: and since it's all bson it shouldn't impair performance, and searches will be fast [18:20] dimitern: yeah [18:20] dimitern: although we won't be searching inside charms [18:21] dimitern: so i'm wondering if it might be easier to do the right thing and change our stuff to store charms in mongo than to get a juju.Conn to the api server [18:21] rogpeppe: maybe not now, but who knows [18:21] dimitern: true [18:21] dimitern: although we'd probably just index the metadata [18:21] rogpeppe: yeah [18:22] dimitern: the other place that uses the environ is status [18:22] dimitern: it uses it to find out DNS names of instances. [18:22] rogpeppe: what's the problem getting the api access the environ? [18:23] dimitern: we don't really want to give it that power [18:23] rogpeppe: maybe read-only [18:23] dimitern: and it's awkward because it'll have to watch the environconfig for changes [18:23] rogpeppe: true [18:25] rogpeppe: but then again, there are things in environ, which have to be proxied through the api, i think [18:25] dimitern: what do you mean? [18:25] DNSName for one [18:26] rogpeppe: I mean things you get out of status, how the gui used to get it [18:26] dimitern: yeah [18:26] rogpeppe: but if all of these are already in state, then i guess it'll be sufficient [18:27] dimitern: except they're not [18:27] dimitern: (perhaps they should be though - but that's more work) [18:28] dimitern: ha! there's a chicken/egg problem too [18:28] rogpeppe: the amount of work can be estimated by what they need, should be easy enough to judge [18:28] rogpeppe: they have a working prototype and all [18:28] dimitern: because the environ isn't valid until someone has connected to the state and pushed the secrets [18:29] dimitern: and that's done by connecting to the api server [18:29] rogpeppe: hmm, how did the gui used to get all that? [18:30] dimitern: but we're saying that the api server needs an environ [18:30] rogpeppe: yeah, maybe at first without caring about environ changes [18:30] rogpeppe: it's not changing that much anyway, is it? [18:31] dimitern: yeah, i think if we provided access to the environ, we'd let the api server come up without it, but any requests that need it would fail [18:31] rogpeppe: until? bootstrap? [18:32] dimitern: until a client connects and pushes the environ secrets [18:32] rogpeppe: but isn't the gui also a client like this? [18:32] dimitern: at which point it'll see a valid environ and api requests will start working. [18:32] dimitern: yes - the gui client won't be able to bootstrap [18:33] dimitern: until at least one non-gui client has connected [18:33] rogpeppe: really? why not? [18:33] dimitern: because the gui doesn't have access to the environment secrets. [18:33] rogpeppe: so you cannot pass with the gui only [18:33] dimitern: although... maybe it should [18:33] dimitern: ah no, it can't bootstrap [18:34] rogpeppe: well, if the gui is designed as a full cli replacement, it should be able to bootstrap as well [18:34] dimitern: because it has no way of talking to the environment provider directly in order to start the first machine [18:34] dimitern: unless we provide some sort of proxy that does that [18:34] rogpeppe: i suspect we'll do that, albeit not for 13.04 probably [18:34] dimitern: (which means that the user would be handing over their credentials to us, which they probably won't want to do) [18:35] rogpeppe: hmm, i see your point [18:36] rogpeppe: but having the same functionality from the gui means, we'll have environments.yaml somewhere in the gui [18:37] dimitern: yeah, but web browsers can't talk to amazon APIs AFAIK [18:38] rogpeppe: the backend will do that, but the environ can come from the gui [18:38] dimitern: yeah, but that's the point: do people trust us enough to give their credentials to the backend? [18:39] rogpeppe: and that's not entirely true either - you can have pure js in-browser AWS clients - there're firefox extensions doing that [18:40] dimitern: in which case, maybe it's a viable option. i don't know. it's a reasonable amount of work. [18:40] dimitern: and what about other providers? [18:40] rogpeppe: it surely is [18:41] rogpeppe: other providers - probably can use the same way to connect from the browser [18:42] rogpeppe: ofc, all this is possible, but it'll mean reimplementing a lot of client-based code in JS :) [18:42] dimitern: depends if they provide an API that's sane to access from javascript. tbh if it involves a browser-specific extension, it's probably a no-goer [18:43] rogpeppe: webkit / gecko pretty much rule all that's not IE, so even having extensions is not unheard of [18:44] rogpeppe: for that password manager app I worked in uniblue, we had extensions for chrome, ff and ie, and the ch/ff shared a lot of common code [18:44] but all that is an entirely different direction for speculation :) [18:45] dimitern: i think a proxy charm would be a nicer solution (i.e. given one juju environment, you can bootstrap another) [18:46] rogpeppe: that sounds interesting in more ways than just for gui use [18:46] dimitern: yeah, there are lots of interesting recursive possibilities :-) [18:47] rogpeppe: anyway, interesting times ahead :) [18:47] I'm off then [18:47] dimitern: oh yes [18:47] dimitern: have a great w/e! [18:47] happy weekend all :) [18:47] rogpeppe: 10x, you too [18:54] right, that's me for the week [18:54] have fun all! === wedgwood is now known as wedgwood_away === wedgwood_away is now known as wedgwood