Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

External controller updater worker #7924

Merged
merged 1 commit into from
Oct 18, 2017

Conversation

axw
Copy link
Contributor

@axw axw commented Oct 11, 2017

Description of change

We introduce a new worker that watches for
addition and removal of external controller
entries, and then watches for changes to
each one's API addresses. When the addresses
change, we update the external controller
details in the local controller's database.

To support this worker, two new facades are
introduced: ExternalControllerUpdater, and
CrossController. The former is an internal
controller facade, used by the worker to
watch, fetch and store the local knowledge
of external controllers. The latter is an
anonymous controller facade that is used by
other controllers to watch and fetch the
controller's API connection info.

There's a drive-by fix to stop importing
apiserver/authentication in workers. Workers
should never be using backend code directly,
so we move the AnonymousUsername constant
to the api package.

QA steps

  1. juju bootstrap localhost l1 && juju deploy wordpress
  2. juju bootstrap localhost l2 && juju deploy mysql && juju offer mysql:db
  3. juju switch l1
  4. juju consume l2:admin/default.mysql
  5. juju relate wordpress mysql
  6. juju enable-ha -c l2
    watch l1's debug log for changes to external controller info

Documentation changes

None.

Bug reference

None.

return nil, errors.Trace(err)
}
info := results.Results[0].Info
return &crossmodel.ControllerInfo{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we now have a controller alias - let's populate that so we can use it when logging errors etc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

state/watcher.go Outdated
w.watcher.WatchCollection(externalControllersC, in)
defer w.watcher.UnwatchCollection(externalControllersC, in)

// Get the initial documents in the collection.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this chuck to get the initial docs should probably be in an initial() func like is done for other watchers (for consistency)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}
}

func (c *Client) ControllerInfo(controllerUUID string) (*crossmodel.ControllerInfo, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given there's several of these now duplicated, is it time to introduce an api/common struct and embed in several api clients?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, because

the one I added to apiserver/crossmodelrelations shouldn't be there, I added it and forgot to drop it
this one and the one in crosscontroller aren't duplicates, they have different signatures

@axw axw force-pushed the cross-model-externalcontrollerupdater branch 2 times, most recently from 1b8678d to 4709bf1 Compare October 12, 2017 09:47
@@ -186,6 +188,16 @@ func Manifolds(config ManifoldsConfig) dependency.Manifolds {
externalUpdateProxyFunc = lxd.ConfigureLXDProxies
}

newExternalControllerWatcherClient := func(apiInfo *api.Info) (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to be consistent with how the remoterelations worker is set up. That worker's manifold has a func to get the controller connection and then there's a shim.go in the worker package that constructs the client - so we pass in the connection rather than the facade client. I guess the use cases might be subtly different enough that maybe it doesn't make sense to consolidate on a single approach.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea was to avoid a dependency on the specific API facade, and have that passed in by the code configuring/constructing the manifold. My approach means that this package can just require an interface, and not be concerned at all with how it's fulfilled.


var logger = loggo.GetLogger("juju.worker.externalcontrollerupdater")

type ExternalControllerUpdaterClient interface {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better not to group these 3 methods on a single interface? For a given external controller, we need to watch for changes and then ask for changes when the watcher fires. And then we need to take those changes and update the local controller. So Watch/GetInfo belong on one interface, and SetInfo() on another interface.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does that buy? In practice they're all implemented by the same API, and conceptually they belong together as they're all dealing with the same source of information. i.e. we're watching, fetching and updating the local controller's knowledge of external controllers.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same API but different targets. Just as Go uses small interfaces specific to a particular purpose, and then composes as needed into broader interfaces, I think we should do that here too (expect there's no need for the broader interface, just the individual ones). The facade connected to the local controller will just implement SetExternalControllerInfo(), and the one connected to the remote controller will implement the other two.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, that's not how it works.

All of the methods in this interface are dealing with the local controller.

  • WatchExternalControllers watches for addition and removal of external controller records in the local controller
  • ExternalControllerInfo asks the local controller for its current information about an external controller (so we can make an API connection)
  • ExternalControllerInfo sets new external controller information in the local controller

The ExternalControllerWatcherClient interface a couple of types below is the one that communicates with the external controller. It has two methods:

  • WatchControllerInfo watches the external controller's API info for changes
  • ControllerInfo gets the external controller's current API info

externalControllerInfo: externalControllers.ExternalControllerInfo,
setExternalControllerInfo: externalControllers.SetExternalControllerInfo,
newExternalControllerWatcherClient: newExternalControllerWatcherClient,
runner: worker.NewRunner(worker.RunnerParams{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we also need to add the runner to the catacomb of the main worker so it and all the subworkers are stopped when the main worker dies?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is, see the Init in catacomb.Plan below.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, so it is.

@axw axw force-pushed the cross-model-externalcontrollerupdater branch 7 times, most recently from 6ee9a6a to 25d3be8 Compare October 16, 2017 04:28
@axw axw changed the title WIP: external controller updater worker External controller updater worker Oct 16, 2017
@axw axw force-pushed the cross-model-externalcontrollerupdater branch from 25d3be8 to f98cb6b Compare October 16, 2017 06:28
Copy link
Member

@wallyworld wallyworld left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you

}
logger.Infof("starting watcher for external controller %q", tag.Id())
watchers.Add(tag)
w.runner.StartWorker(tag.Id(), func() (worker.Worker, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error from StartWorker should not be ignored.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@@ -0,0 +1,209 @@
// Copyright 2016 Canonical Ltd.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2017

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

return errors.Annotate(err, "caching external controller info")
}
logger.Infof("new controller info for controller %q: %v", w.tag.Id(), info)
if err := worker.Stop(nw); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also remove nw from the catacomb? Otherwise we are just adding reference to the catacomb each time we create the client again and get a new watcher?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the catacomb will remove the worker from its management when the worker stops

We introduce a new worker that watches for
addition and removal of external controller
entries, and then watches for changes to
each one's API addresses. When the addresses
change, we update the external controller
details in the local controller's database.

To support this worker, two new facades are
introduced: ExternalControllerUpdater, and
CrossController. The former is an internal
controller facade, used by the worker to
watch, fetch and store the local knowledge
of external controllers. The latter is an
anonymous controller facade that is used by
other controllers to watch and fetch the
controller's API connection info.

There's a drive-by fix to stop importing
apiserver/authentication in workers. Workers
should never be using backend code directly,
so we move the AnonymousUsername constant
to the api package.
@axw axw force-pushed the cross-model-externalcontrollerupdater branch from f98cb6b to 5a26cd3 Compare October 18, 2017 02:01
@axw
Copy link
Contributor Author

axw commented Oct 18, 2017

$$merge$$

@jujubot
Copy link
Collaborator

jujubot commented Oct 18, 2017

Status: merge request accepted. Url: http://ci.jujucharms.com/job/github-merge-juju

@jujubot jujubot merged commit bad2bef into juju:develop Oct 18, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants