[00:50] davecheney: ping [00:52] ack [00:52] niemeyer: ack [00:52] davecheney: Yo [00:52] davecheney: Sorry, I actually decided to just note down in the review.. we can talk after that [00:53] no worries [01:04] davecheney: Please see what you think of https://codereview.appspot.com/6295067 [01:07] davecheney: I'm looking at https://codereview.appspot.com/6307071/diff/3007/environs/jujutest/livetests.go [01:07] davecheney: How come t.Env.AllInstances returns 1 instance, and t.Env.Instances right above returns two? [01:09] niemeyer: because the code above asks for id0 twice [01:09] AllInstances returns a Set of known instances [01:09] davecheney: ROTFL [01:09] maybe I should add a comment there, took me a while to figure that one out, I'm not even sure if it's correct [01:11] reguarding the rety branch [01:11] i'm just going to remove that comment [01:11] you are of course correct that calling p.Stop() will return nil [01:11] so that is a trap for someone who might add that fuctinoality (via a signal handler or something) [01:14] davecheney: I've added a note on https://codereview.appspot.com/6307071/ [01:14] davecheney: There's one assertion there that would be nice, but otherwise please feel free to go ahead an merge it now [01:14] niemeyer: thank you [01:15] davecheney: I'll likely move the branch over the day tomorrow to lp.net/juju-core (no /juju), so the least branches we have flying around the best [01:15] Even though this time it should really not be much trouble [01:15] Since reviews will remain valid and all [01:15] coiol [01:15] cool [01:30] Okay, almost clean queue [01:31] davecheney: I see a LGTM from rog on https://codereview.appspot.com/6294066/ [01:31] davecheney: I'll have a look at it tomorrow so we can get this in [01:31] sure [02:50] Okay, bed time here [02:50] davecheney: Have a good time there [02:50] niemeyer: will do, thanks again [03:30] aujuju: What is the correct way to set config options to a juju service unit with a file? [04:53] aujuju: Can't get juju to deploy, problem with ec2 keypair? [05:57] davecheney: mornin' [05:57] * rogpeppe is a bit hung over. [06:03] rogpeppe: hey, just responding to your follow up now [06:03] will be but a moment [06:04] https://www.youtube.com/watch?v=AJHCsVZGnNY << audio quality is utterly atrocious [07:12] davecheney: oh yeah, i still have to finish watching that. [07:12] davecheney: i learned one new thing: hex.Dumper. [07:43] rogpeppe, btw, while I remember, what's state.Unit.isPrincipal for? [07:44] rogpeppe, doesn't seem to be used; and Unit already has IsPrincipal()... [07:44] fwereade: i didn't think there is state.Unit.isPrincipal... [07:44] fwereade: let me check [07:44] fwereade: nope, i don't see that method [07:45] rogpeppe, field [07:45] rogpeppe, you added it recently [07:45] fwereade: ah! [07:45] fwereade: yes, it's so that when we're watching units for a machine, we can tell which units are principals without having to read the topology again for each one [07:46] fwereade: i made a mistake actually [07:46] fwereade: Unit.IsPrincipal should just return u.isPrincipal [07:47] fwereade: will fix asap [07:47] rogpeppe, yeah, indeed [07:47] rogpeppe, cool, thanks [07:47] fwereade: good catch, thanks [07:47] rogpeppe, wasn't sure if there might have been something else going on [07:47] cheers [07:47] fwereade: nope, just my forgetfulness [07:48] fwereade: BTW did you see my question on #juju? [07:48] ah sorry no [07:48] rogpeppe, I get to #juju rarely, when I run out of other work to do ;) [07:49] fwereade: i was playing around with writing some charms this morning and wondered what was the best way to wait until all of several relations had been joined [07:50] fwereade: like, for instance, if one relation has been joined, can i query the joined status of other relations? [07:51] rogpeppe, honestly I'm a bit unclear about the current intent of the various hook commands [07:51] rogpeppe, not really up on what jimbaker was doing just before last release [07:51] rogpeppe, bu I need to find out in detail soon, so I can implement them ;) [07:53] fwereade: :-) === TheRealMue is now known as TheMue [08:08] rogpeppe, can I run some ideas past you as a sort of crackfulness check please? [08:09] fwereade: please do [08:09] rogpeppe, stop me when you detect the whiff ;) [08:09] * rogpeppe sniffs carefully [08:10] rogpeppe, RelatedService is a good name for ServiceRelation; and its role and scope should be taken from the expected endpoint (of the service it's describing) [08:11] rogpeppe, RelatedService.RelationName(), however, should return the RelationName of the endpoint at *this* end of the relation, because that's how the related service is seen by the "local" service [08:11] fwereade: remind me what ServiceRelation represents again... [08:11] rogpeppe, ie the one on which we called RelatedServices() to get that RelatedService [08:11] rogpeppe, my best guess is that it represents what I just described [08:12] rogpeppe, but there's definitely something hinky about the types in play in the python [08:12] rogpeppe, I'm trying to come up with something that actually fits what we need to do [08:12] i haven't looked at those types. i think i had a mental block at "UnitRelation" which gustavo's renaming has helped with [08:13] rogpeppe, I'm actually starting to think that UnitRelation was a real misstep, that we don't want at all [08:13] rogpeppe, but that's a bit further down the crackfulness list [08:13] fwereade: we're talking about types that exist to hold the settings, right? [08:14] rogpeppe, well, zookeeper holds the settings... and I don't think anybody except the unit agent has any right to be looking directly at those settings any more [08:14] fwereade: because there's one collection of settings for each service for each relation for each unit [08:14] rogpeppe, yes [08:14] rogpeppe, but nobody should be looking at them directly IMO [08:15] fwereade: are these types not there specifically to enable what the unit agent needs to do? [08:15] rogpeppe, because they run the risk of destabilising the space-time continuum by violating causality [08:15] rogpeppe, *kinda* [08:15] fwereade: how is the unit agent going to read and change those settings without an API in state? [08:18] gaaah [08:19] lol [08:19] rogpeppe, did you see any of that, about RelatedGroup? [08:19] fwereade_: nope [08:19] rogpeppe, I'm going to return to my sorta-thread of ideas briefly, I think what I'm saying will make more sense with context [08:19] rogpeppe, next: RelatedGroup is a useful concept that deserves its own (simple) type [08:19] rogpeppe, a RelatedGroup really just wraps a path that's either /relations/rel-id or /relations/rel-id/container-id [08:19] rogpeppe, but it's a lot more convenient to pass around one of those and specify things like group.settingsPath(RoleProvider, unitId) [08:19] rogpeppe, ...thn it is to hand-hack those paths everywhere [08:19] fwereade_: last i saw from you was "*kinda*" BTW [08:19] rogpeppe, cool, I picked the right join point :) [08:20] rogpeppe, so, re RelatedGroup, this is IMO a useful concept that we didn't previously have a name for [08:20] rogpeppe, it's really the set of units that can (transitively) affect one another within a relation [08:21] rogpeppe, in a global relation it's just all units [08:21] rogpeppe, in a container-scoped one there's a RelatedGroup per container [08:21] fwereade_: so what kind of operations are you imagining on a RelatedGroup? [08:22] rogpeppe, .presencePath() and .settingsPath() mainly [08:22] fwereade_: is there anywhere in the current code that would be changed to use it? [08:22] rogpeppe, also perhaps .prepareJoin(role), which ensures that nodes have been created [08:23] rogpeppe, relationUnitWatcher would directly take presencePath and settingsPath which had been calculated from a RelatedGroup [08:23] rogpeppe, and finally, maybe, .Watch(...) [08:24] rogpeppe, but it is true that I have not yet figured out exactly what data the various bits of behaviour are best attached to [08:24] fwereade_: just so i have an idea, what would the type definition look like? [08:24] rogpeppe, it really just wraps a path [08:24] rogpeppe, that's it [08:24] fwereade_: type RelatedGroup string ? [08:25] rogpeppe, it'll need a zk.Conn, or State, or something too [08:25] rogpeppe, this is what I have atm: [08:25] / RelatedGroup represents the set of units within a specific relation that [08:25] / can (transitively) affect one another. For a globally-scoped relation, this [08:25] / includes all units of all services in the relation; for a container-scoped [08:25] / relation, there will be one RelatedGroup per container, containing all [08:25] / units of the related services that are running within that container. [08:25] type RelatedGroup struct { [08:26] zk *zookeeper.Conn [08:26] path string [08:26] } [08:27] rogpeppe, it's not a lot of data, but it draws together a lot of special-casing that would otherwise be smeared across the codebase [08:27] rogpeppe, there are several places in python that act differently depending on relation scope [08:27] fwereade_: could you point to a few examples in the python code, so i have an idea? [08:27] rogpeppe, just a sec [08:32] rogpeppe, oddly enough, mainly in state/relation.py -- there are several `if self._relation_scope == "container"`s [08:32] rogpeppe, there's a _self.container_path which seems kinda redundant but is also related [08:33] rogpeppe, basically everything involving relation unit presence or settings, whether reading or writing, needs to care about this concept (implicitly or explicitly) [08:34] fwereade_: i think i need to be reminded of how relations are laid out in zk [08:34] rogpeppe, I have vocab that will make things easier :) [08:35] rogpeppe, a "group" has (1) a settings subnode, which contains a unit-key-keyed node for every unit in the relation [08:36] rogpeppe, (2) a role subnode for each role in play in the relation, which contains a unit-key-keyed presence node for every unit actively playing that role in the relation [08:36] rogpeppe, sorry, in the *group* [08:36] rogpeppe, a globally coped relation contains just one group, which is the relation node in ZK [08:37] rogpeppe, a container-scoped relation has N container-key-keyed groups within the relation node [08:37] rogpeppe, the group is the unit of... er, influence? [08:38] rogpeppe, I am still slightly scrabbling around for vocabulary; let me know if you come up with improvements ;) [08:38] fwereade_: how about RelationScope ? [08:39] rogpeppe, yeah, except the name is taken and I don't know what to call the thing that already has that name [08:39] rogpeppe, and all the existing methods called RelationScope [08:39] fwereade_: well, we're actually talking about the same thing, right? [08:40] rogpeppe, not really, RelationScope is just "container" or "global" [08:40] rogpeppe, the thing we're talking about is about what units are actually within a given scope of a given relation [08:40] fwereade_: aren't those the two kinds of things you can have in a RelatedGroup? [08:41] fwereade_: it seems to me that the path in RelatedGroup is exactly expressing the scope of the relation [08:41] rogpeppe, I strongly agree that RelatedGroup is, intuitively, a "scope" [08:41] fwereade_: so, can we think of a way to join the two concepts? [08:41] rogpeppe, but I feel somewhat constrained naming-wise [08:41] rogpeppe, I hope so :) [08:42] rogpeppe, one possibility is to rename RelationScope to RelationScopeKind or something [08:42] fwereade_: vague thought: i wonder if a relation could return a RelationScope which looks like your RelatedGroup, but has a Kind method which returns what is currently RelationScope [08:43] ha! [08:43] rogpeppe, then I can [08:43] fwereade_: jinx, kinda [08:43] rogpeppe, the trouble is that I cannot necessarily just go from a relation to a relation scope, without knowing the container I'm talking about (in the case of a container-scoped relation, anyway) [08:44] rogpeppe, also there's no such type as a Relation ATM [08:44] rogpeppe, I'll add one if it seems worthwhile ofc ;) [08:44] fwereade_: there is actually [08:44] fwereade_: see relation.go:/^type Relation [08:45] rogpeppe, the way I currently have to get to a group is RelatedService.group(u *Unit) [08:45] rogpeppe, I deleted it [08:45] fwereade_: ah. [08:45] rogpeppe, it's entirely useless [08:46] rogpeppe, anyway, can we agree that I'm not *obviously* high, yet, although there cretainly are naming issues to be sorted out here? [08:47] fwereade_: i definitely think there's something to what you're saying, but i don't think it's just naming that's at issue. [08:47] rogpeppe, because the next step is to point out that a RelatedUnitsWatcher could be, in terms of vocabulary we've been discussing, a GroupRoleWatcher [08:47] rogpeppe, ah, ok, let's back up then [08:48] fwereade_: or rather, naming has semantic implications too [08:48] rogpeppe, oh hell yes [08:49] rogpeppe, I'm kinda hoping that naming fixes will fall out of the discussion [08:49] fwereade_: not sure about GroupRoleWatcher - that sounds like it's watching for changes in group roles [08:49] rogpeppe, for example with the scope idea that becomes a ScopeRoleWatcher, which feels much closer to what it's really doing [08:49] fwereade_: but it is actually watching for related units, no? [08:50] rogpeppe, yeah, it could indeed just be called a RelatedUnitsWatcher [08:50] rogpeppe, but it feels slightly important to make it clear that it's scoped [08:50] fwereade_: just that RelatedUnitsWatcher watches for related units within a particular scope, no? [08:51] rogpeppe, yeah, it's also reasonable to just ignore that entirely -- pass in a group/scope and not mention it at all in the type name [08:51] rogpeppe, probably a better idea all things considered [08:51] fwereade_: if we create the RelatedUnitsWatcher from a RelationScope, that would be obvious, perhaps [08:52] rogpeppe, yeah [08:53] rogpeppe, ok stepping back again a mo [08:53] fwereade_: type ScopeKind string? [08:54] rogpeppe, yeah, +1 [08:54] rogpeppe, a unit agent has a unit [08:54] rogpeppe, it can find out its service, and fro that get some RelatedServices [08:55] rogpeppe, given a unit and a RelatedService we can figure out the Scope [08:55] func (r *ServiceRelation) Scope(unit *Unit) RelationScope [08:55] rogpeppe, a Scope is useful both for watching related units and for signalling the unit agent's presence within the relation [08:55] rogpeppe, yeah [08:56] for globally scoped relations, unit could be nil [08:56] rogpeppe, doesn't matter I think [08:56] fwereade_: that's true. [08:56] i think [08:57] rogpeppe, I think the question we're asking is "what scope will this unit be in if it joins, or please give me an error of the unit can't join" [08:57] and finally... [08:57] rogpeppe, watching related units, and signalling ones presence in the relation, are profoundly intimately bound up [08:57] rogpeppe, such that we don't want a unit agent ever doing just one of those things [08:58] rogpeppe, by signalling presence, it's advertising that it's also watching and responding to the related units [08:59] sorry, parcel just arrived [08:59] rogpeppe, and I *think* the right high-level thing for this is `func (u*Unit) AgentJoin(s *RelatedService) (*SOMETHING, error)` [08:59] back now though [09:00] rogpeppe, where that SOMETHING is... I dunno, I've been calling it a JoinedService, but that's obviously wrong [09:00] rogpeppe, JoinedScope might be better [09:01] rogpeppe, (it's purpose is (1) to provide access to a RelatedUnitsWatcher's Changes channel, and (2) to maintain a pinger on the approriate path so that SOMETHINGs on other unit agents detect it) [09:01] rogpeppe, and if either of those tasks fail both should fail [09:01] func (u*Unit) AgentJoin(s *RelatedService) (*presence.Pinger, *someWatcherType, error) ? [09:02] rogpeppe, -1 [09:02] rogpeppe, if one of those things dies the other should too [09:02] rogpeppe, if anything that type is a RelationUnit [09:03] rogpeppe, but it's something whose mere *existence* has broad impact on other processes [09:03] rogpeppe, so I'm very reluctant to use that name ;) [09:04] rogpeppe, ScopePresence? JoinedRelation? JoinedScope? JoinedService? [09:04] rogpeppe, Joined is IMO a good word [09:04] fwereade_: i don't mind if we let the unit agent do some work, rather than pushing everything into a state type [09:04] rogpeppe, the unit agent will be doing *plently* of work [09:05] fwereade_: it's very easy for the unit agent to do: defer pinger.Stop() [09:05] rogpeppe, and to react to its death as well? [09:05] rogpeppe, to clean up one if the other dies, and vice versa? [09:05] rogpeppe, sound to me like a job for a type honestly [09:05] fwereade_: yeah, we'll already have a loop with a select in [09:07] rogpeppe, and it will also be simple to express Depart() and Abandon()? [09:07] fwereade_: but i'm sure a type could work well too [09:10] rogpeppe, ok, I think I'll go with ScopeKind string and RelationScope struct {*zk.Conn, string} [09:10] rogpeppe, see what falls out of that [09:10] fwereade_: i think that sounds good. [09:12] rogpeppe, TheMue: since I'm making this change, I wonder how you feel about s/RelationRole/RoleKind/ throughout as well [09:12] rogpeppe, TheMue: for consistency's sake if nothing else [09:13] fwereade_: that sounds good to me too [09:13] rogpeppe, TheMue: (also, for methods/fields currently called RelationRole/RelationScope, name them Role and Scope unless that causes ambiguity?) [09:13] fwereade_: and i'm thinking that the methods on ServiceRelation don't really benefit from having "Relation" prefixes [09:13] jinx [09:13] rogpeppe, RelationName kinda does [09:14] fwereade_: i don't mind ServiceRelation.Name [09:14] rogpeppe, but i want to call that type RelatedService :) [09:14] ah [09:15] rogpeppe, which IMO has more appropriate connotations [09:15] rogpeppe, but then, hmm. [09:15] fwereade_: in which case, yeah, RelationName is better [09:15] rogpeppe, similarly, in that case, RelationScope [09:16] rogpeppe, but Role is something that really is on the service not the relation so that should change [09:16] rogpeppe, it's more things like [09:16] fwereade_: i don't mind Scope on RelatedService actually [09:16] fwereade_: because services don't have scopes [09:16] rogpeppe, true, and the scope is shared by both ends of the relation [09:16] fwereade_: so it's obvious that it's about the Related bit of the name [09:17] rogpeppe, cool [09:17] fwereade_: i vote for a small CL cleaning up these issues, then another one renaming RelationScope to ScopeKind [09:18] rogpeppe, yeah, that sounds sensible [09:18] fwereade_: as they're orthogonal and the latter is more controversial [09:18] rogpeppe, I'll think on it a touch more though [09:18] k [09:19] rogpeppe, most of this morning's conversation is *really* about me figuring what order I can make CLs in to maximise my chances of getting all this stuff in ;) [09:19] fwereade_: ah, but you've now changed *what* you're gonna put in! [09:20] fwereade_: and i, for one, think it's a really nice step forward [09:20] rogpeppe, the types haven't changed, I think, just the names :) [09:20] fwereade_: You know the current Py code best. If there's no conflict with Role and Scope I'm fine with it. [09:20] rogpeppe, (from what I was originally intending, that is; it's definitely changed from the py in some ways ;)) [09:20] fwereade_: having ScopeKind hanging off RelationScope is new, i think [09:22] fwereade_: yeah. but the names make so much difference. when it was "RelatedGroup" i was, like, "where are the things in the group?". but now it's a "RelationScope", i see it as a container and it makes sense to me. [09:23] rogpeppe, yeah, tyvm for helping out with that [09:23] fwereade_: even though the underlying representation might be identical [09:23] rogpeppe, that was what I was looking for, and what I got :) [09:24] fwereade_: np. i would never have got as far as you did! [09:24] rogpeppe, I'm lucky to have undergone the baptism by fire that was the restartable-unit-agent work in the python [09:24] rogpeppe, that was enough that figuring out the real details here was.. possible [09:25] fwereade_: just had a silly naming idea [09:25] rogpeppe, oo, go on [09:26] fwereade_: so we've got these "scopes" right? so when we've got a language implementation, what do we call the actual implementation of scopes? "frames". [09:26] fwereade_: sadly it doesn't really work [09:26] rogpeppe, I don't think it quite does :( [09:26] rogpeppe, I culd call them continuations I suppose, if I really want someone to stab me [09:27] fwereade_: actually, maybe just RelationSettings might be appropriate [09:27] rogpeppe, -1 [09:28] fwereade_: as it's the settings that are scoped [09:28] rogpeppe, (1) they're presence as well (2) the only thing that should actually be reading the settings is the RelatedUnitsWatcher anyway [09:30] fwereade_: yeah [09:30] fwereade_: i'm just slightly concerned that the word "scope" implies something abstract rather than concrete [10:10] moin [10:20] Aram: Moin. Hehe, you adopted the usual greeting here in Northern Germany. [10:29] Lunchtime [11:14] fwereade_: ping [11:15] rogpeppe, pong [11:15] fwereade_: i wonder if you can help me thinking about a good way to test this new package: http://paste.ubuntu.com/1042221/ [11:16] fwereade_: i can mock up start commands like the upstart package does [11:16] fwereade_: but i don't think it provides any useful assurance of anything [11:17] fwereade_: i'm considering a "-root" flag to the test, for tests that need to run as root. [11:17] fwereade_: then we can test it for real. [11:17] rogpeppe, yeah, it's tricky; in the python we had exactly that [11:17] fwereade_: a "-root" flag? [11:17] rogpeppe, and it always felt a little icky but I never saw any other way to really verify it [11:17] rogpeppe, well, a USE_SUDO env var [11:18] rogpeppe, but basically yes [11:18] fwereade_: it's particularly an issue for the LXC-manipulating code [11:18] rogpeppe, yeah, that's the main place we had it for [11:18] fwereade_: ok, that's useful, thanks [11:18] rogpeppe, I think I had it for the upstart stuff in the local provider as well [11:19] fwereade_: i think i'll go for a "-root" flag and require that the user id be root if it's set [11:20] rogpeppe, if you're going to require that, can't you just check for root and explicitly skip the tests that require it if not? [11:20] fwereade_: i thought of that, but i'm not sure i want the tests manipulating my global state without me explictly asking them to do so [11:20] rogpeppe, IMO by running them as root you're explicitly asking them to do so [11:21] fwereade_: sometimes i'm running a root shell without being aware of it... [11:21] fwereade_: but maybe it's ok [11:21] rogpeppe, ah, fair enough [11:21] fwereade_: and it means that i could do sudo go test ./... [11:21] rogpeppe, I *think* I've broken myself of that habit even when I really want to [11:22] * rogpeppe hates sudo [11:22] rather, i hate the requirement that so many things run as root [11:23] rogpeppe, ha, yeah [11:24] rogpeppe, TheMue: I have a thought about topoRelationService [11:24] * rogpeppe is all ears [11:24] rogpeppe, at the moment a topoRelation has Services, which holds topoRelationServices keyed on service key [11:25] rogpeppe, tRS has RelationName and Role fields [11:25] rogpeppe, each taken from the appropriate endpoint of the apropriate service [11:25] rogpeppe, I contend that even here, the relation name should be taken from the opposite endpoint [11:26] rogpeppe, because, in the context of a relation, the important thing about a service is what the *other* end thinking it's called [11:26] fwereade_: what is the relation name BTW? is it a key? [11:26] rogpeppe, *db*-relation-joined [11:27] rogpeppe, *blog*-relation-changed [11:27] rogpeppe, etc [11:27] fwereade_: ok, thought so, just checking [11:27] rogpeppe, it's the name used within a charm to refer to the other end of the relation [11:28] rogpeppe, so it seems stupid for a service to store (1) what it does and (2) what it thinks the other end's called [11:28] rogpeppe, when it could store (1) what it does and (2) what it's called (from the perspective of the rest of the relation) [11:29] rogpeppe, it's non-obvious but I'm becoming convinced that the obvious solution is crack [11:31] * rogpeppe is looking at the code [11:32] TheMue, when you return, I would also appreciate your opinion of the foregoing [11:36] fwereade_: hmm. this stuff is barely used at the moment. how do you anticipate it being used? [11:37] fwereade_: (currently there's just a single call to topology.Relation [11:37] ) [11:37] rogpeppe, well, I have this idea the a RelatedService is {relationKey string, serviceKey string, relater RelationEndpoint} [11:38] fwereade_: what's an "endpoint" again? [11:38] rogpeppe, where "relater" is the endpoint of the service at the "local" end of the relation [11:38] rogpeppe, relation name, interface, role, scope [11:38] ok [11:39] rogpeppe, oh and service name [11:41] fwereade_: am i wrong that the only thing in that endpoint tuple that can't be derived from relationKey is the role? [11:41] rogpeppe, also the name [11:42] fwereade_: that comes from the relationKey and role too, doesn't it [11:42] rogpeppe, which needs to come from the topoRelationService, and which I contend is the wrong way round at the moment [11:42] rogpeppe, but, yes; it's pretty trivial to construct the RelatedService above, given a topology and a relation key [11:42] fwereade_: i'm thinking that a RelatedService is more {relationKey string, role RelationRole} [11:43] rogpeppe, sure, it could be, but the fields I described are the ones that are (1) convenient to access when constructing and (2) used subsequently [11:44] fwereade_: i'm trying to think fundamentals, not optimisation. [11:44] rogpeppe, if we only stored what you suggest it would be doable but a bloody hassle [11:44] fwereade_: really? [11:45] fwereade_: well, of course it'd need to store the topology too [11:45] rogpeppe, rooting around in the topology, looping through relation Services dicts trying to find the data it's looking for [11:45] rogpeppe, I'd really much rather collect all the relevant info than the bare minimum necessary to infer that info [11:45] rogpeppe, (otherwise why have types at all? :p) [11:46] rogpeppe, this would be problematic if relations could change after being created, but the fact of a relation and its endpoints is not something that changes over the lifetime of that relation [11:46] fwereade_: i guess i was trying to get to the bottom of what is *necessary* for a RelatedService [11:47] rogpeppe, it could equally be relationKey, serviceKey [11:48] rogpeppe, we can infer role from service+relation just as we can infer service from relation+role [11:48] fwereade_: ah, that seems more logical, given the name [11:48] fwereade_: ah, except that relationKey isn't visible outside state [11:49] rogpeppe, we don't have to expose it [11:49] ;) [11:50] rogpeppe, I don't think we really want to expose much on a RelatedService at all tbh [11:50] fwereade_: what do we get from RelatedService that we don't get from Scope? [11:50] rogpeppe, I certainly don't need anything public *yet* [11:51] rogpeppe, scope covers both ends [11:51] rogpeppe, relatedservice is about one end only [11:51] fwereade_: i'm wondering about: func (svc *Service) Relation(u *Unit) *Scope [11:51] ah [11:51] rogpeppe, why would I care about that? [11:52] rogpeppe, the only actual use case is for the unit agent to loop over the related services and respond to changes in each [11:52] rogpeppe, for that I just want a list of RelatedServices, each of which describes what I need to know to join a relation with it [11:52] * rogpeppe is starting to see [11:53] * fwereade_ is hugely relieved [11:53] rogpeppe, I've been a bit nervous about all this -- it is definitely different to the python, but I think it is a projection of the same problem into a simpler space [11:53] :-) [11:54] rogpeppe, and it's far from immediately obvious that that's the case ;) [11:54] fwereade_: the zk representation is pretty much the same though, right? [11:55] rogpeppe, I need to make another pass over the topology code, but yes, I think so [11:55] rogpeppe, the zk representation of the actual live relations is identical [11:55] rogpeppe, but I think the code representation of that representation is much nicer like this [11:56] fwereade_: dumb question: why isn't a RelatedService just a Service? [11:56] rogpeppe, I think that it will lead to a much cleaner clearer data flow [11:56] rogpeppe, because a service doesn't in itself have a role/relname/scope [11:56] rogpeppe, it has a whole bunch of relations, only one of which we're interested in at a time [11:57] fwereade_: but you could have something like: func (svc *Service) Join(*Service) error, though, no? [11:57] rogpeppe, I *think* that it's units which have to join services [11:58] fwereade_: oh yeah [11:58] rogpeppe, but I may be missing context... what would that do? [11:58] rogpeppe, at first glance that really looks like a different version of AddRelation(endpoints) [11:59] rogpeppe, but not specific enough to do anything, because there may be more than one valid pair of endpoints between the services [11:59] fwereade_: yeah, i've realised that [11:59] fwereade_: i'm just wondering if there's some way of doing without RelatedService [12:00] rogpeppe, I thought there was at one stage but the code said it wanted it [12:00] rogpeppe, (I understood the code to be saying it wanted it ;)) [12:00] fwereade_: so i'm just trying to think about what the unit agent is doing [12:01] rogpeppe, I'm expecting pseudocode: [12:02] rogpeppe, for rs in self.unit.service.related_services: self.unit.AgentJoin(rs) [12:02] rogpeppe, and therefrom marshal the changes coming out of the JoinedService, or whatever we call it, into actual hook executions [12:02] fwereade_: i was toying with the idea of: func (u *Unit) Join(svc1, svc2 *Service) [12:02] rogpeppe, which is a separate and not entirely trivial thing [12:03] rogpeppe, what are the two services for? [12:03] rogpeppe, surely we can infer one of those from the unit [12:03] ah, we need a relation name too [12:03] rogpeppe, IMO hence the value of RelatedServcei [12:03] fwereade_: func (u *Unit) Join(svc *Service, relationName string) [12:04] rogpeppe, that doesn't uniquely identify the relation [12:04] rogpeppe, I, mysql, might have a bunch of different "db" relations going at the same time [12:04] fwereade_: really? [12:04] rogpeppe, ah but hmm maybe with service it is unique [12:04] fwereade_: i thought each relation had a unique name [12:04] fwereade_: yeah [12:05] rogpeppe, nah, this was something I only recently appreciated [12:05] rogpeppe, as a unit I may be participating in multiple foo relations [12:05] fwereade_: if a service provides a relation, it can be joined by more than one other service? [12:05] rogpeppe, charm authors handle this by looking at RELATION_IDENT in the hook context when running foo-relation-* [12:06] rogpeppe, yeah [12:06] rogpeppe, but those are still multiple relations [12:06] rogpeppe, (relation means different things in different contexts, unhelpfully) [12:06] fwereade_: ah, makes sense, if we want to share db connections etc [12:06] another piece of the puzzle falls into place [12:06] rogpeppe, the prospect of writing a charm that handles all that right makes my head hurt [12:07] rogpeppe, but we do provide all the information required to do so, I think [12:07] ciggie, brb [12:07] fwereade_: k [12:14] rogpeppe, b [12:16] fwereade_: but you can't have more than one of the same relation going on with a single other service, right? [12:17] rogpeppe, apart from that being somewhat crazy, Idon't think there's anything stopping you from doing so [12:17] fwereade_: i'm not sure you *can* do that though [12:17] fwereade_: how would you? [12:18] rogpeppe, I could certainly write a charm that had 2 relations with the same interface and role [12:18] fwereade_: yeah, but they'd have different relation names [12:18] rogpeppe, but on the *other* end on the charm with just one relation, those two others would have the same name [12:19] fwereade_: which is why i'm thinking that this could work ok: func (u *Unit) Join(svc *Service, relationName string) [12:19] fwereade_: where relationName specifies the name at the other end [12:19] rogpeppe, if I'm mysql, I don't care whether your relation name is "db" or "posts" or what [12:20] fwereade_: i'm not quite sure how that's relevant. [12:20] rogpeppe, I, as mysql, am calling you "db", regardless [12:20] fwereade_: ok [12:20] fwereade_: and? [12:21] rogpeppe, why does one end of the relation ever want to know internal name the other end is using for the first end? [12:21] rogpeppe, except to do what you suggest [12:22] rogpeppe, but then how does it get that information without smooshing all the RelatedService construction stuff inline? [12:23] fwereade_: ok, i think i'm *starting* to get there. [12:23] rogpeppe, IMO far easier to have (*Unit)AgentJoin(*RelatedService), which can barf quickly and easily if the unit's service name doesn't match the relater endpoint's service name [12:23] fwereade_: so you call Service.RelatedServices. then for each one of those, you can do Unit.Join(service) [12:23] rogpeppe, yeah, that's the idea [12:24] rogpeppe, RelatedServices is also I think just the right info for status display [12:25] rogpeppe, again we want to know what the service we're looking at thinks the related services are called, not what the related services think the current service is called [12:25] fwereade_: or even RelatedService.Add(*Unit) [12:25] rogpeppe, I don;t think that fits, I think join is exactly the right word [12:25] ok, yeah it works [12:26] 'specially with the usual xxx-relation-joined stuff [12:26] rogpeppe, exactly so [12:26] rogpeppe, we end up with methods like JoinedRelation.Depart(), which will cause -departed hooks to fire at the other end [12:26] rogpeppe, just as the Join causes -joined hooks to fire at the other end [12:27] rogpeppe, I also have a plan to write an Abscond method (ie depart but try to keep it secret for as long as possible) ;) [12:27] lol [12:28] fwereade_: maybe "Relation" is a good spelling for "JoinedRelation" [12:28] fwereade_: given you deleted the Relation type [12:29] rogpeppe, oh yes, I like that [12:29] fwereade_: cool. [12:33] fwereade_: so we're looking at something like this: http://paste.ubuntu.com/1042310/ ? [12:34] rogpeppe, close [12:34] rogpeppe, just a sec [12:36] rogpeppe, more like http://paste.ubuntu.com/1042315/ I think [12:36] rogpeppe, sorry crappy type name on Changes [12:37] rogpeppe, RelatedUnitsChange probably better [12:37] fwereade_: you'll still need Service.WatchRelatedServices though, no? [12:38] rogpeppe, well, yeah, I'll need to pay attention to what relations I'm meant to be part of [12:38] rogpeppe, that feels pretty simple and not really related at this stage [12:38] fwereade_: yeah, so you can watch 'em. [12:38] fwereade_: but i think you'll need an Attach method or something because you need some way of making a Relation without creating the join node [12:38] rogpeppe, so, yeah, I'll need a RelatedServicesWatcher [12:38] rogpeppe, why? [12:38] fwereade_: or maybe not [12:39] fwereade_: yeah, if the node already exists, we do nothing, that works [12:39] rogpeppe, I will probably want some way to get a list of the units involved, for status display [12:39] fwereade_: so unless i've missed something, the only change above is to add Relation.Changes ? [12:39] RelatedService.Units [12:40] rogpeppe, yeah, I think so [12:40] fwereade_: perhaps ScopeUnitChange would be better spelled RelationSettingsChange [12:41] rogpeppe, also includes departs [12:41] fwereade_: hmm [12:41] rogpeppe, I think RelatedUnitsChange to go with RelatedUnitsWatcher [12:41] fwereade_: yeah [12:42] fwereade_: actually, given that there will be two independent watchers for the relation setting and the unit departure, maybe it makes sense to have two channels [12:42] fwereade_: rather than trying to stuff 'em both into the same type [12:43] rogpeppe, under the hood, there may well be [12:43] rogpeppe, I'm expecting RelatedUnitsChange to be {Updates map[string]string, Deletes []string} [12:44] rogpeppe, which I *think* is exactly the format we wnt for a hook scheduler [12:44] rogpeppe, where updates is map[unitKey]settingsData [12:45] rogpeppe, there's an interesting debate to be had on the precise type to use for the settings data [12:45] rogpeppe, but for today it's a derail ;) [12:45] fwereade_: ConfigNode [12:45] rogpeppe, hell no [12:45] mebbe [12:45] no? ok [12:45] rogpeppe, we definitely don;t want any other bastard writing to our settings ;) [12:45] fwereade_: we're in control here, remember? [12:46] fwereade_: convention is ok [12:46] rogpeppe, another reason: we don;t want to see zookeeper state [12:46] rogpeppe, we want to see the state as it was at the time the change was detected [12:46] fwereade_: why would we? ConfigNode has a cache [12:46] fwereade_: but yeah, why not pass along the map[string]interface{} [12:46] rogpeppe, so, plausibly, map[string]map[string]interface{} [12:47] fwereade_: yup [12:47] fwereade_: so where did you want to put the departed watch info? [12:47] rogpeppe, departs will be in Deletes [12:48] rogpeppe, which should maybe even be called Departs because they do map cleanly onto hook executions, in a way that updates don't [12:49] {Updates map[*Unit] string; Deletes []*Unit} ? [12:49] oops [12:49] i mean [12:49] map[*Unit]map[string]interface{} [12:49] except units don't live well in maps, i suppse [12:49] rogpeppe, I'm not really sure why we need the *unit, especially since it might plausibly not exist by the time we try to look at it [12:50] rogpeppe, unit names only I think [12:50] fwereade_: that's true of MachineUnitsWatcher too though [12:50] rogpeppe, but probably the client of MachineUnitsWatcher actually wants to do something with the *Units though [12:51] fwereade_: and the unit agent doesn't? [12:51] rogpeppe, I don't think so [12:52] rogpeppe, again it's trying to present (to the hooks it runs) an immutable facade representing the state at the time it was known to have changed [12:52] fwereade_: BTW a little diversionary sketch i did early this morning: http://paste.ubuntu.com/1042285/ [12:52] rogpeppe, I can't think of any Unit methods I'd want or need to call [12:52] fwereade_: ok, that's cool [12:53] fwereade_: so the strings would be unit names? [12:53] rogpeppe, yeah, that's what the hooks expect to work with expect to see [12:53] rogpeppe, I'm sure I have something kinda similar to that lying around somewhere :) [12:54] fwereade_: i think it should be quite quick to write. [12:54] rogpeppe, yeah, I think I already wrote most of it a while ago, then gotstymied by the lack of relations :) [12:54] fwereade_: the idea is that in every hook you put '#!/bin/sh\ngohook $0' [12:55] fwereade_: how do you mean? [12:55] rogpeppe, ok, I suspect what you wrote doesn't do what I thought it did [12:55] rogpeppe, explain please? [12:56] rogpeppe, (I'm not so sure about that HookKind busines either) [12:56] fwereade_: so you've got one go server that runs; the hooks talk to it with rpc. [12:56] rogpeppe, yes [12:57] fwereade_: when a hook executes, Wait returns a context, which you can then use to do the usual hooky things, until you close it, which terminates the hook execution [12:58] rogpeppe, ah, yes, I misread the stuff at the bottom [12:58] fwereade_: it's a kind of inversion of the usual callback-driven control flow, but makes sense in the go world i think [12:58] rogpeppe, I am in general suspicious of the idea that we should let people run hooks whenever they want to [12:58] fwereade_: they can't [12:58] fwereade_: you can only get a context when a hook is run by juju [12:59] rogpeppe, ok, sorry, who's calling RunHook? [12:59] fwereade_: in every hook you want to register, you have a shell script that does, as above: [12:59] #!/bin/shj [12:59] gohook $0 [12:59] s/shj/sh/ [13:00] otherwise there's no way of knowing what hooks you want to register with juju [13:00] rogpeppe, isn't that hugely complicated compared to "if the file exists, it's a hook"? [13:00] fwereade_: it uses that mechanism [13:01] fwereade_: but all the hooks get actually executed in the same go program context. [13:02] rogpeppe, still not seeing what we get out of that [13:02] fwereade_: i *think* you could write pretty charms with it. [13:02] fwereade_: 'cos i'm not a big fan of callbacks [13:02] fwereade_: but yeah it's just an idea [13:03] rogpeppe, oh, I think I see [13:03] rogpeppe, yeah, that is interesting :) [13:03] rogpeppe, maybe one of those ones to do in our copious free time though ;p [13:04] fwereade_: yeah, definitely. all that copious free time. [13:04] fwereade_: i suspect it'd only be a hundred or so lines of code though. [13:29] niemeyer, heyhey [13:29] niemeyer: Moin [13:30] Hellos! [13:30] * niemeyer waves [13:30] TheMue, did youhave any thought on that stuff (some way) above (by now)? [13:31] fwereade_, certainly feel free to ping me with any questions on the relation id stuff [13:31] TheMue, starting at round about 13:24 [13:32] fwereade_: One moment, will read it quickly. [13:32] jimbaker, cool, thanks -- I'll be getting there before too long I think :) [13:32] fwereade_, also if you have any more thoughts on the merge proposal for format: 2 support that would be great [13:32] TheMue, sorry, it gets pretty wide-ranging [13:32] jimbaker, crap, sorry, I'll take another look sometime today [13:33] TheMue, the bit I'm really concerned about is that IMO we're storing the wrong relation names in the topology [13:33] fwereade_, no worries. thanks for taking a look at this, you had some great comments. i did refactor to use polymorphism so the parallel code paths are all now in one place. lot cleaner code. and a lot more tests. (almost at 2000 for the python version as a whole) [13:34] jimbaker, awesome [13:36] * niemeyer sips some great chimarrĂ£o on that shadowing Friday morning [13:41] niemeyer: hiya! [13:44] fwereade_: i still don't quite see it. is your concern that the topology isn't storing the right info, or that the info is just a bit harder to get to than it should be? [13:45] rogpeppe, well, all the necessary bits are there, but in a misleading configuration [13:45] rogpeppe, we're storing a service's role together with what it thinks the other service is called [13:45] rogpeppe, considering it from the POV of the relation [13:45] rogpeppe, we should be storing name and role together [13:46] fwereade_: "storing a service's role together with what it thinks the other service is called" currently? or as you'd like it to be? [13:46] rogpeppe, that's current [13:46] fwereade_: ah, that seems odd [13:47] rogpeppe, indeed so [13:47] rogpeppe, it's tempting to do what we currently do because we're storing information from a single endpoint together [13:47] fwereade_: Just for info, I didn't forget you, but it's a lot to read. ;) [13:47] TheMue, ofc, np [13:48] fwereade_: actually, i can't see anywhere that topoRelationService.RelationName is set [13:48] TheMue, it's restated succinctly just above though [13:48] rogpeppe, it's in State.AddRelation [13:49] rogpeppe, TheMue: storing the name according to the other end is more work but (1) IMO more correct and (2) also fits better with the RelatedService ideas discussed earlier [13:50] TheMue, if there's anything I can clarify please ask away :) [13:54] fwereade_: there's a one-to-one mapping between endpoints and topoRelationServices. that seems good to me. [13:54] fwereade_: i suppose the problem is i can't envisage the code which the current structure would make awkward [13:55] rogpeppe, the point is much more that it's wrong than that it's awkward [13:55] fwereade_: whether it's wrong depends on how we're gonna use it, no? [13:56] rogpeppe, well, the name is wrong anyway... and calling it NameByWhichWeReferToTheOtherServiceInThisRelationIfItExists is somewhat awkward [13:57] rogpeppe, whereas Name, implying "what other people call us", seems rather saner to me [13:57] rogpeppe, and the fact that it's stored within relation data should make the context clear IMO [13:57] fwereade_: I don't know if I got everything right, but as far as I can follow it sounds reasonable. [13:57] fwereade_: so the RelationName in RelationEndpoint is the name as referred to by whom? [13:58] i see the awkwardness, because every relation has two names [13:59] fwereade_: so RelationName is ambiguous, which i hadn't really appreciated before [13:59] rogpeppe, the very term "relation" is ambiguous [14:00] rogpeppe, do we mean "thing in the 'relations' part of charm metadata", or "connection between two services"? [14:00] fwereade_: because it represents the abstract relation as defined in the metadata, but also ... [14:00] fwereade_: yeah [14:01] fwereade_: maybe if we sort out some terminology for that, then everything will become clearer [14:01] rogpeppe, perhaps, but I fear both meanings are (1) user-facing and (2) entrenched already [14:02] fwereade_: is there anywhere in the current code that we use "relation" to mean "the abstract relation as defined in the metadata" ? [14:02] rogpeppe, I think that's the meaning of RelationEndpoint.RelationName [14:02] rogpeppe: At least in the yet ported code not. [14:03] rogpeppe, an endpoint is a perfectly meaningful construct even when not participating in a relation [14:04] fwereade_: hmm. so it's more PotentialRelationEndpoint than RelationEndpoint. [14:05] rogpeppe, heh, if you like :) [14:05] rogpeppe, I'd say it still exists even if don't connect to it [14:05] fwereade_: i'm trying to contrast with the other kind of endpoint, which actually exists and has an id [14:06] maybe. i'm still fuzzy! [14:06] rogpeppe, sorry, what other kind of endpoint? topoRelationService? [14:06] rogpeppe, I don't really see that as an endpoint [14:06] rogpeppe, although I can certainly see a perspective from which that conception of it makes sense [14:08] rogpeppe, no, can't be, TRSs don't have ids [14:08] rogpeppe, I'm confused [14:08] fwereade_: what id is given to the hook? the relation key? [14:08] * TheMue would like a whiteboard for the discussion. [14:09] rogpeppe, yeah, effectively [14:09] * rogpeppe too [14:09] rogpeppe, IIRC we strip unnecessary 0s [14:09] * rogpeppe would like to see a snapshot of a fully populated zk tree [14:10] * fwereade_ sympathises but doesn't think he has anything like that handy [14:17] fwereade_: Could you mail a kind of outline of how the entities, structure and topology should look like to juju-dev? [14:17] fwereade_: So it's mo simple to think about it twice and make annotations. [14:17] TheMue, this is 100% internal to the topology [14:18] TheMue, wait, except that it affects ServiceRelation.relationName [14:18] fwereade_: Which makes it a bit simpler. ;) [14:18] TheMue, but if yu look at the docs for ServiceRelation it's very clear that it's "from the viewpoint of a participant service" [14:19] TheMue, my point is in essence very simple: that the RelationName stored in a topoRelstionService does not actually refer to that service [14:19] TheMue, and that this is a Bad Thing [14:21] fwereade_, TheMue: i'm writing a quick snapshot of how i think the whole thing will look, with a single wordpress/mysql instance. feel free to edit with me: https://docs.google.com/a/canonical.com/document/d/1z2bIJ097qawOPGtZnHABIqNPVXaQ5ePCRIQVYGfcHsg/edit [14:21] rogpeppe: Thx *click* [14:22] rogpeppe: Need access, request is sent. ;) [14:22] TheMue: you should be able to access it with your canonical account [14:23] rogpeppe: OK, I mostly use my old private one, also for lp. [14:23] huh.. haven't seen this before "Your Amazon EC2 Instance scheduled for retirement " [14:23] email notification [14:23] i think that's for the jujucharms site [14:23] hazmat: poor dear. i hope it gets a pension. [14:24] rogpeppe: I'm in. [14:24] rogpeppe, this ain't no socialist cloud! ;-) [14:24] hazmat: paid for by its earnings over its lifetime, of course :-) [14:25] rogpeppe, balls, I appear to have the wrong @canonical.com google password stored... and resetting it doesn't appear to have sent me anything [14:25] rogpeppe, grar ok they won't send it to me [14:25] rogpeppe, it was unemployed for most of its life sadly, and abused by the state, and burden to it as we ll apparently. i'm going to appeal to amnesty international for it as a political prisoner ;-) [14:25] rogpeppe, any chance we could use an etherpad for now? [14:26] fwereade_: just made it available to all [14:26] fwereade_: etherpad? [14:27] rogpeppe, http://pad.ubuntu.com/ [14:27] rogpeppe, but anyway, I'm in [14:28] rogpeppe, and I want to edit where you're sitting :p [14:33] rogpeppe, that's what I'd like to see in the topology [14:33] fwereade_: can we see what there is *now*. [14:33] rogpeppe, done [14:33] fwereade_: as i'd like to understand where we are before moving from that... [14:34] rogpeppe, can I delete everything to do with units and machines? [14:34] rogpeppe, services and relations are the only bits relevant right now I think [14:34] fwereade_: i'd like to keep the whole picture, for the moment [14:35] fwereade_: Aram will find it useful apart from anything [14:35] rogpeppe, ok, but I think everything outside the topology is a red herring [14:36] rogpeppe, for the purposes of the is-fwereade-on-crack-re-relation-names discussion [14:36] fwereade_: I've been following the conversation, but to be honest I still miss the root [14:36] fwereade_: Nice project name. [14:36] fwereade_: Where's the etherpad? [14:36] fwereade_: i'm sure so. but for the moment, i think this is a useful diversion. [14:37] niemeyer, it's at https://docs.google.com/document/d/1z2bIJ097qawOPGtZnHABIqNPVXaQ5ePCRIQVYGfcHsg/edit [14:37] fwereade_: i've wanted to see something like this for a while, just to sort it all out in my head [14:37] niemeyer, no etherpad, we'd already started on docs [14:37] Cool, looking [14:37] Is there audio or something? [14:37] What are we doing? [14:37] Besides hacking a topology? :) [14:38] In other words, is there a problem statement somewhere? [14:38] niemeyer, all I'm trying to do is convince people that we're storing relation names in the wrong bits of the topology [14:38] niemeyer, rogpeppe is I think usefully diverted [14:38] niemeyer, but not in the direction I want him to be ;p [14:39] niemeyer, I would state the problem as follows: [14:39] fwereade_: Why? Oh, nevermind, go on please.. [14:39] fwereade_: it's ok, i'm looking back at the relations now :-) [14:39] niemeyer, the topoRelationService has "Role" and "RelationName" fields [14:39] fwereade_: Ok [14:40] fwereade_: and is keyed by the service key on the topology [14:40] niemeyer, a topoRelation has "Scope", "Interface" and "Services"; Services contains a serviceKey:topoRelationService map [14:40] fwereade_: Okay, seems to make sense to me [14:40] niemeyer, the RelationName fields in topoRelationService do not refer to the service by which they are keyed [14:41] fwereade_: What? [14:41] niemeyer, I consider this to be misleading at best [14:41] fwereade_: This is a bug for sure [14:41] niemeyer, they refer to the [14:41] niemeyer, it's a subtle one though [14:41] fwereade_: It seems rather blatant to me [14:42] niemeyer, mainly because "relation" means different things depending on whether we're talking about charms alone, or... actual, er, relations... connections-between-services [14:42] fwereade_: If you asked me I'd say that's not the case in the current implementation.. [14:42] niemeyer, from the perspective of a user, we do have the same word meaning two things [14:42] fwereade_: I see them as the same in both contexts [14:43] fwereade_: But even then, I don't see how that's related to what we're discussing [14:43] niemeyer, they are profoundly related [14:43] niemeyer, it's the cause of the problem IMO [14:43] fwereade_: I'll actually see the code right now because I still can't believe the RelationName isn't for the service [14:43] niemeyer, consider a RelationEndpoint [14:43] fwereade_: Okay, with you [14:43] niemeyer, that has a RelationName which matches the relation defined in the charm [14:44] fwereade_: Yes [14:44] niemeyer, however from the perspective of a hook running in a relation, that is effectively the name of the other side of the relation [14:45] fwereade_: Erm, no? [14:45] niemeyer, I run db-relation-joined because I think that someone has joined my db relation [14:45] fwereade_: The local relation name is always the local relation name.. the charm doesn't care about what's the remote relation name [14:45] niemeyer, I am seieng [14:45] fwereade_: Yes, *your* db relation.. local relation name [14:46] fwereade_: Your db relation has a different high-level identification for the remote charm [14:46] fwereade_: and they both don't fight about that [14:48] niemeyer: ok, I am mysql, and I provide "db"; you are wordpress and you require "data" (interfaces, scopes match) [14:48] fwereade_: Cool [14:48] fwereade_: (by the way, I just checked the code, and seems to match my understanding) [14:49] niemeyer, we currently store {mysql-key: {role: provides, name:db}, wp-key: {role: requires, name:data}} [14:49] niemeyer, this representation fits nicely with the charms [14:50] fwereade_: Yes, that sounds correct [14:50] fwereade_: in all senses of that [14:50] niemeyer, but from my perspective as "mysql" I consider myself to be related to a thing called "db" [14:50] fwereade_: Aha! [14:51] fwereade_: That's where the misunderstanding lies [14:51] niemeyer, and I don;t consider that t be *my* name at all [14:51] fwereade_: But it is.. [14:51] fwereade_: You're not related to a thing called "db", and you have that feeling because that relation name is a poor name [14:52] fwereade_: Imagine you are wordpress, and for whatever reason you support two relations with interface "mysql": "cache", and "data" [14:52] niemeyer, ok [14:52] fwereade_: This is the *wordpress* name for the relations.. [14:52] fwereade_: Who is "my cache"? [14:52] yes, and in the context of a relation that's the important thing... isn;t it? [14:53] fwereade_: Well, everything is important [14:53] * TheMue listens carefully [14:53] niemeyer, your "cache" is a conduit to a service somewhere running mysql [14:54] niemeyer, or not? [14:54] fwereade_: Yes, but it's the name you gave to it.. it's your way of calling it [14:54] niemeyer, in the context of a relation, what name is important other than the name used by the other people in a relation? [14:54] fwereade_: It's a local identifier for the *relation* [14:55] fwereade_: Sorry, I missed your intention with that sentence [14:55] niemeyer, if I'm in a relation and looking at the other side of it -- rel.Services[key-of-other-service] -- what name do I want to see? [14:55] niemeyer, the name it think I have, or the name I think it has? [14:56] fwereade_: This is bogus [14:56] fwereade_: The relation that you have at hand is *a relation*, it's not on either side [14:56] niemeyer, wait, if I'm participating in a relation, there's no "other side"? [14:57] fwereade_: If I say relation.Service[serviceKey].RelationName, I want to see the "relation name" of the charm of serviceKey that is participating in this relation [14:57] fwereade_: The *relation that you have at hand*.. that value that you mentioned as "rel" [14:57] fwereade_: This has no side.. it's the storage for the relation data that all the sides will see [14:59] niemeyer: +1. that's a good way of putting it. [14:59] niemeyer, if I am a unit of service-X and and I am looking at a relation between service-X and service-Y, then surely service-Y is the other side..? [14:59] niemeyer, and vice versa? [15:00] fwereade_: Yes, but do you understand what I'm trying to point out? When you have a "rel", that "rel" has no side, because every single side in that relation will get the same data [15:01] fwereade_: This is not "the relation for my side of the service" [15:01] fwereade_: This is "the relation" [15:01] niemeyer, I *think* I understand that just fine [15:01] fwereade_: It's seems like you're looking for a convenient way to navigate. "Hey, relation, I'm X, so who is my Y." [15:01] fwereade_: relation[serviceKey].RelationName has the relation name from serviceKey that is participating [15:01] fwereade_: and that seems correct to me [15:02] niemeyer, I will concede that that is also a valid way of looking at it ;) [15:03] fwereade_: It's not just a valid way.. it's the only way to look at what we have in place [15:03] fwereade_: You might want to represent it in a different way, and that's certainly possible, but I don't see a reason to change it [15:03] fwereade_: Maybe you do? [15:03] smaddock, invite out [15:04] niemeyer, I *think* I do, let me just take a moment to reorder my thoughts [15:04] fwereade_: Sounds good [15:10] fwereade_: As a curiosity, note that we say "juju add-relation service1:rel1 service2:rel2".. that's consistent with how we store it as well. [15:12] niemeyer, ok, when I want to get the relations participated in by a specific service -- the "local" service, for the purposes of discussion -- I get a list of topoRelations out of the topology and I want to turn them into a bunch of instances that encapsulate what the local service needs to know about the various remote services [15:13] fwereade_: The local service needs to know pretty much nothing about the remote relation names being used, I think [15:14] niemeyer, from that perspective, it seems strange that I should get the name of the relation from my own service key, and the role it plays from its service key... but wait, dammit, I can get the name from my own key and trivially derive the role played by inverting my own role [15:14] niemeyer, I'd been thinking about getting the role of the opposite service from the opposite service key [15:14] niemeyer, and having to get the name from my own key, and that seemed insane [15:15] fwereade_: Yeah, I think doing provider => requirer sounds fine :) [15:15] niemeyer, right, I think we can safely forget I said anything :) [15:15] niemeyer, sorry derail [15:16] fwereade_: No worries, I actually find that kind of conversation useful to solidify the concepts we have in place [15:16] me too [15:16] and we've got this doc now as a kind of reference point: https://docs.google.com/a/canonical.com/document/d/1z2bIJ097qawOPGtZnHABIqNPVXaQ5ePCRIQVYGfcHsg/edit [15:16] niemeyer, yeah, the time wasn't wasted, just wish I'd seen the other perspective some hours ago [15:16] which has been helpful for me anyway [15:16] * TheMue appreciates it too, getting a better understanding. [15:25] rogpeppe, slight update to https://codereview.appspot.com/6310046, tRS.RelationName became tRS.Name [15:25] fwereade_: sounds good [15:25] fd [15:25] everyone, I'm going for a quick walk to see whether thinking this through dislodges any more misconceptions, bbiab [15:25] fwereade_: at some point we should add some comments to the fields in the structs [15:26] rogpeppe, yeah, I think I will be doing that for my next trick :) [15:33] * rogpeppe has just realised the google docs is absolutely terrible for editing a document where indentation matters [15:33] s/the/that/ [15:34] rogpeppe: You need a shared online vim. [15:34] TheMue: noooo! [15:35] TheMue: just normal text will do fine. i don't really want to go back to vi key commands... [15:35] TheMue: or monospaced fonts [15:37] rogpeppe: Hehe, even if my favorite editor is a different one I still like the good old vim. [15:38] rogpeppe: It's always fine for admin tasks. [15:38] TheMue: ed FTW! [15:39] rogpeppe: For Go (and other tasks) I like my Sublime Text. highlighting, code completion, templates, fmt on save, good build support and as an editor many nice features. [15:40] rogpeppe: But editors are good for endless discussions. *lol* [15:40] TheMue: i had a challenge from hazmat to try sublime text for a month... if he tries acme for a month! [15:42] rogpeppe: hazmat is using st too? Didn't know that. [15:42] TheMue: no, i think he's an emacs user [15:42] TheMue: but there's no way i'm using emacs :-) [15:42] rogpeppe: Uhh, tried it for some time, but never get warm with it. [15:44] TheMue, actually i'm on emacs user [15:44] TheMue, i'd like to use ST more [15:45] hazmat: Just give it a try. ;) [15:45] TheMue, i have.. its nice.. the go integration looks sweet [15:45] hazmat: Especially when installing GoSublime. [15:46] TheMue, indeed.. my use of ST is hindered by my workflow which has evolved into a terminal session with many open editors split across different tmux windows, all on different branches [15:46] i guess that translates into multiple ST editor windows [15:47] hazmat: Yip. [15:59] Lunch time [17:05] fwereade_, niemeyer, TheMue, Aram: gotta go soon. have a great weekend all. [17:05] rogpeppe, and you, happy weekend [17:06] fwereade_: just started world war Z, BTW. will let you know how i get on. [17:07] rogpeppe, cool; I've got the others of yours with me this w/e so hopefully I'll manage another :) [17:07] fwereade_: can't remember what i left you with now... [17:07] fwereade_: oh yeah, valentine's castle. [17:07] rogpeppe, er, nor can I now (I already read that one, must get sequels...) [17:08] rogpeppe, axiomatic [17:08] rogpeppe, to hold infinity [17:08] fwereade_: oh yeah, awesome shorts [17:08] fwereade_: i think you'll enjoy both [17:08] rogpeppe, you haven't led me wrong yet :) [17:24] rogpeppe: Bye, have a nice weekend. [17:25] rogpeppe: Have a good time there man [17:26] * TheMue leaves now too. [17:27] TheMue: Have a good time too [17:27] niemeyer: Thx, for you, and all here, a nice weekend too. [17:31] niemeyer, re: redundant-relation-prefixes -- my thinking is that Role and Scope invariably refer to aspects of a relation; but Name and Key may need disambiguation, and where they do have been left alone [17:32] niemeyer, and I'm slightly concerned that we're drifting down the python path of VeryLongRedundantNomenclatureNamesThatEndUpObscuringMeaning [17:33] niemeyer, invalid? [17:37] fwereade_: I'm not sure this is the case on that specific instance [17:37] fwereade_: I find more awkward to have relationName string; scope RelationScope [17:38] fwereade_: RelationRole is VeryFarFromSuchALongAndUNpleasantNameInMyOpinion :-) [17:39] fwereade_: I'd be happy to take *all* prefixes off, but we can't, because RelationName is ambiguous with ServiceName in certain contexts [17:40] niemeyer, fair enough, I'd really prefer prefixes only for disambiguation, but it's hardly worth arguing over :) [17:40] fwereade_: I find consistency a relevant factor too [17:41] fwereade_: this hurts my eyes somehow: { relationKey string; scope RelationScope } [17:41] niemeyer, "prefixes only for disambiguation" is imo perfectly consistent, but as I say it's no big deal :) [17:42] niemeyer, I do have another thought I should probably run by you before I propose it [17:42] fwereade_: This is not consistent.. this is a statement [17:42] fwereade_: The code is consistent or not, and in the example above it isn't [17:43] fwereade_: type is { relationKey string; relationName; scope RelationScope; role RelationRole} [17:43] fwereade_: now I have a rel [17:44] niemeyer, as I say, I'm happy to drop it :) [17:44] fwereade_: DO I type rel.role or rel.relationRole? [17:44] fwereade_: Do I type rel.key or rel.relationKey? [17:44] fwereade_: That's what I mean by inconsistency. [17:44] niemeyer, I have no idea, and your position doesn't make it any easier to tell [17:44] fwereade_: I understand, I'm trying to provide reasoning so it doesn't feel like arbitrating a personal opinion [17:45] niemeyer, or should I go and change Unit.key to Unit.unitKey throughout? [17:45] fwereade_: It does.. it's always rel.relation* [17:45] fwereade_: This type, specifically, is named ServiceRelation, not Relation [17:45] fwereade_: If you introduce UnitRelation, then we can have the same convention [17:45] niemeyer, heh, I want to call it RelatedService actually [17:46] fwereade_: Okay, I'll just shut up now. :) [17:48] niemeyer, I'm entirely happy following your own preferred conventions, you don't actually have to convince me they're right every time... you favouring them is a pretty solid heuristic, and if I still don't like them in a few months we can worry about it then ;) [17:49] niemeyer, but if you have a moment... aside from wanting to rename ServiceRelation, which IMO doesn't say enough about what it is or why it exists [17:49] fwereade_: Sounds good.. I will still try to explain why in my opinion it isn't simply arbitrary favoring, though [17:50] niemeyer, that's fine -- but I think we have quite different perspectives on some things, and I can't always be convinced ;) [17:50] fwereade_: in a single type having prefix or not forces people to look it over, and this is obviously bad in my favoritism mechanism [17:51] fwereade_: I won't try to convince you every time, promise : [17:51] :0 [17:51] :) [17:51] fwereade_: I'll still explain, though [17:51] niemeyer, don't worry, I don't think you're just being arbitrary :) [17:51] niemeyer, it's worth it often enough that I'm certainly not going to complain [17:52] single type with { relationKey, relationName, relationScope, relationRole } == Good [17:52] single type with { relationKey, relationName, scope, role } == Bad [17:52] niemeyer, the message I failed to convey above was "yeah, I can see that's a sane perspective, I'm happy to go with it" [17:52] fwereade_: Sounds good, we're in sync [17:52] fwereade_: Thanks for being flexible as well, by the way [17:52] niemeyer, anyway, I'd quite like to introduce a RelationScope type that actually represents a specific scope [17:53] fwereade_: That counts highly [17:53] niemeyer, a pleasure :) [17:53] niemeyer, but this introduces naming issues [17:53] niemeyer, renaming RelationScope will be annoying -- something like ScopeKind could work, but then for consistency's sake I'd want to have RoleKind as well [17:54] fwereade_: Hmm, can you explain further what you mean by "actually representing a specific scope" in that sense? [17:54] niemeyer, the group of units within a relation that can actually affect one another [17:54] niemeyer, either all of them, or the set of them that are all in the same container, depending on scope [17:55] niemeyer, this concept is sprinkled through the python code and I think deserves to be promoted [17:55] niemeyer, even if the type itself is just a ZK conn and a path [17:57] niemeyer, so, on the basis that I don;t really want to rename the Relation* types, how would you feel about UnitScope for that? [17:57] fwereade_: Ah, interesting [17:57] fwereade_: Can you talk me through the use case(s?) we have for that, just so I can picture it? [17:58] niemeyer, most things to do with unit relations touch it in some way [17:59] niemeyer, the settings paths and presence paths, which are both read and written by different unit agents, are consistent relative to the unitscope base path [17:59] fwereade_: Right [17:59] fwereade_: But how do we actually need to handle these details, code wise? [18:00] niemeyer, and being able to pass it around saves us quite a lot of basePath + "/" + string(r.RelationRole) + "/" + u.key [18:01] niemeyer, for example, with that we can pass it into a RelatedUnitsWatcher, and it becomes positively trivial for that to start a unitRelationWatcher by passing in the settings path and presence path directly [18:01] niemeyer, with scope.presencePath(role, unit) and scope.settingsPath(unit) [18:01] niemeyer, the lazy parent node creation can be tidied away in one place [18:02] niemeyer, the *same* UnitScope we pass into the watcher can also be used to determine the paths the unit agent wants to write to [18:02] fwereade_: Is this a RelationScope, rather than UnitScope? [18:03] niemeyer, it's probably only half a dozen cases in total, but it lets us implement almost everything else without worrying about the concept of scope [18:03] niemeyer, I don;t think so, because it only becomes meaningful in the context of a specific unit [18:03] fwereade_: Yeah, I can see how it maps well to the issues we might have [18:03] niemeyer, in a global relation, ok, every unit on either side has the same scope [18:03] niemeyer, in a container relation it's a specific path that depends on the unit [18:04] niemeyer, I'm afraid I am being called to supper :) [18:04] fwereade_: Right, but I'm wondering if we don't simplify code and future use cases by having a representation which is unit-agnostic, if you see what I mean [18:04] niemeyer, hmm, the way I see it we construct it with a unit and never think of it again [18:04] niemeyer, it's just the scope and it does what it should [18:04] fwereade_: That's because I suspect you have a bit of a biased view [18:05] fwereade_: Having to implement that specific use case right now [18:05] fwereade_: But think of a monitor, for example [18:05] fwereade_: Or a debugger [18:05] fwereade_: You're not sitting on a single unit anymore, and still, the concept of a Scope still exists [18:05] niemeyer, it's still a which binds together N units which (transitively) can affect one another [18:06] fwereade_: Exactly, and that is a relation scope [18:06] fwereade_: With N units [18:06] niemeyer, that was the name I originally wanted to give it [18:06] fwereade_: As I understand it, your thinking was to have a relation scope which represents N-1 units [18:06] niemeyer, no: all units that can transitively affect one another [18:07] niemeyer, a unit is absolutely part of its own scope [18:07] niemeyer, fwereade_: new container package for review: https://codereview.appspot.com/6304085 [18:07] fwereade_: Oh, ok [18:07] unfortunately i can't seem to get it off WIP [18:07] fwereade_: Well, sounds like we're aligned then, and just need to bikeshed on names :) [18:07] fwereade_: Supper is calling you :) [18:07] niemeyer: i have this issue: http://paste.ubuntu.com/1042741/ [18:08] niemeyer, cool , cheers -- happy weekend if I decide to be all social tongiht :) [18:09] fwereade_: Thanks, you too :) [18:09] rogpeppe: Hmm [18:11] rogpeppe: Is bzr info and all working successfully on /home/rog/src/go/src/launchpad.net/juju-core/juju/.bzr/cobzr/container-package [18:11] ? [18:26] niemeyer: yeah [18:26] niemeyer: i did accidentally run bzr commit as root once; i suppose that might have done something bad [18:26] rogpeppe: Hmm, quite possibly [18:26] niemeyer: but i did a chown -R rog.rog afterwards to try to clean things up [18:26] rogpeppe: You could try fiddling, but I suggest rebranching [18:27] rogpeppe: bzr branch container-package temp-branch [18:27] rogpeppe: bzr branch -d container-package [18:27] rogpeppe: bzr branch -m temp-branch container-package [18:27] branch -m, yeah [18:30] niemeyer: still failed: http://paste.ubuntu.com/1042771/ [18:30] rogpeppe: Crap [18:30] rogpeppe: See if you have something on ~/.bzr.log [18:31] rogpeppe: "resource not found" is an error coming out of lpad, IIRC [18:31] Yeah, it is [18:31] lpad is looking for something in Launchpad that does not exist [18:32] rogpeppe: Can you please try running with -debug? [18:34] rogpeppe: Also, can you please paste the result of "bzr info" within ~/.bzr/cobzr/container-package [18:38] andrewsmedina: ping [18:44] niemeyer: last part of $HOME/.bzr.log: http://paste.ubuntu.com/1042796/ [18:44] rogpeppe: Ok, that's not the issue [18:44] rogpeppe: bzr info? [18:45] niemeyer: http://paste.ubuntu.com/1042801/ [18:45] rogpeppe: That's probably the issue: /home/rog/src/go/src/launchpad.net/juju-core/juju/.bzr/cobzr/temp [18:45] rogpeppe: I wonder what it was before [18:46] But I'm not sure, to be hoenst [18:46] rogpeppe: The output with -debug? [18:47] niemeyer: http://paste.ubuntu.com/ [18:47] oops [18:47] niemeyer: http://paste.ubuntu.com/1042810/ [18:47] Hah [18:48] Okay, that's clearly wrong [18:48] Let's see where it's getting the information from [18:48] it doesn't seem to be remembering the push branch [18:48] rogpeppe: bzr info within juju-core/juju? [18:49] niemeyer: at the start of that paste [18:50] Very awkward.. why is it not resolving the path [18:51] rogpeppe: Do you have changes in your local lbox? [18:51] niemeyer: nope [18:57] rogpeppe: When I execute locally, it doesn't look up information for branches starting with "." in Launchpad [18:58] rogpeppe: The code seems to back that up [18:58] rogpeppe: I'm a bit puzzled about how it's doing that there [18:59] Ah, I think I see [18:59] Hmmm.. or maybe not [19:13] rogpeppe: Here is the output of mine: http://paste.ubuntu.com/1042850/ [19:14] rogpeppe: Reproducing the same scenario [19:14] with push --remember et al [19:14] rogpeppe: I'm out of ideas really.. the only option is diving in and seeing why it's attempting to load ".bzr/..." from Launchpad [19:14] rogpeppe: It shouldn't ever do that [19:20] niemeyer: oh now, this is weird. i committed a new change, then did bzr push --remember ...; and it said "No new revisions or tags to push" [19:22] rogpeppe: Huh.. things aren't quite alright there [19:23] rogpeppe: I'm not entirely sure of what that push --remember does with a bound branch [19:24] rogpeppe: Theoretically whenever you commit, it's already being pushed onto the bound branch [19:30] niemeyer: i think i stuffed up bzr when i ran it as root :-( [19:56] rogpeppe: It'd be nice to see why lbox is misbehaving, though [19:56] rogpeppe: It's being mislead in a way it shouldn't [19:57] rogpeppe: If we find out, we can make it have a saner behavior in the future