API for Upgrader

roger peppe rogpeppe at gmail.com
Thu Jun 20 15:34:09 UTC 2013


On 20 June 2013 13:47, John Arbash Meinel <john at arbash-meinel.com> wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Just a rough outline of what William and I just discussed as the API
> needed for Upgrader.
>
> I'm writing this down partially so I don't forget it, and partially to
> get pre-implementation feedback.

Thanks for writing this up. It's very useful.

> Essentially the whole Upgrader API becomes a single function, which is
> "start a watcher to tell me about the desired Agent version for this
> list of agents, and the watcher signals by sending a state.Tools
> struct describing the desired version and a URL to download the
> associated tools".
>
> In rough prototypes:
>
> type WatchAgentToolsParams struct {

s/Params//
(in the params package, the Params suffix is implied)

>   Id string

In code I'm just doing for password setting, shared between the machine
and unit agents, I've used a tag instead of an id here.
That means we can easily write generic code on
the server side that can retrieve the relevant entities
from state without needing to try and decide what looks
like a unit id and what looks like a machine id.

>   Arch string
>   Series string
> }
>
> Upgrader().WatchAgentTools(params []WatchAgentToolsParams) []WatcherResult

s/WatcherResult/ToolsWatcherResult/

> type AgentToolsChange struct {
>   Id string

I'm not sure it's necessary to send the id with every watcher response, as it's
already known by the watcher.

>   Tools state.Tools
> }
>
>
> var change AgentToolsChange
> change, err := <-watcher.Changes()
>
>
>
> The Id being passed can be either for a Machine agent or a Unit agent.
> And both types of agents will use the same API.
>
> The initial implementation on the APIServer side will just use
> state.WatchEnvironConfig(). With these steps:
>
> 1) Initially we can just fire a watch response on any change to
> EnvironConfig. This will trigger a few too many probes, but the
> Upgrader code already has a:
>   if proposed == version.Current.Number {
>     noDelay()
>     break
>   }
>
> So it doesn't create much extra load on the system.
>
> 2) Stage 2 can have the API server track what the current API version
> is, and only fire the watcher when API version changes. That mostly
> just changes the load on the API server, because it doesn't have to
> search for updated tools.
>
> 3) Stage 3 can only tell Machine or Unit agents that there is a new
> API version once it finds there are binaries for it to download. (we
> might do this before 2). This handles the heterogenous series and Arch
> environments when someone doesn't upload all possible tools case.
>
> 4) Stage 4 we can get extra fancy by only telling the Machine that
> there is a new API version to download, and only once the Machine
> reports a new Machine API, do we tell the Unit agents to upgrade.

I think this might be difficult to achieve. In a HA scenario, how
do the various API servers coordinate this information?
If we want to do this, it's probably easier and more
efficient to coordinate locally.

> This helps with the "thundering herds" case. Where someone requests an
> upgrade, and then every agent ever wakes up and tries to download new
> tools. (So if you have a Machine with a Unit and a subordinate Unit,
> you would download the tools 3 times, only to have 2 of them get
> thrown away)
>
> 5) While it doesn't make a lot of sense today to have a Watcher across
> multiple agents, it is possible we will change responsibilities (say
> one upgrader per container).

I'm not sure what you mean by "having a Watcher across multiple agents"
here.

> We pass the Arch and Series to the API Server for this, so we can
> lookup the Tools URL for the agents directly in the API. Eventually
> when Ian's patch lands that sort of information may already be stored
> in the state database, but it isn't there today, and requiring it in
> the API means you can upgrade to 1.11 and still have the upgrader
> function normally.
>
> Should the change itself be an array: eg:
> type AgentTools struct {
>   Id string
>   Tools *state.Tools
> }
>
> type AgentToolsChange []AgentTools
>
> Or should watcher.Changes fire multiple times for multiple units?

I think we should use the existing watcher architecture where
we have one watcher resource for each thing being watched
and the client calls Next on any given watcher to retrieve
the next change. The "bulk" interface simply returns
one watcher for each id in the request.

That's the way the machine watcher that Martin has just landed works,
for example.

  cheers,
    rog.



More information about the Juju-dev mailing list