10
9
"github.com/juju/errors"
11
10
"github.com/juju/names"
12
"gopkg.in/juju/charm.v6-unstable"
13
11
charmresource "gopkg.in/juju/charm.v6-unstable/resource"
15
"github.com/juju/juju/worker"
13
"github.com/juju/juju/charmstore"
18
const charmStorePollPeriod = 24 * time.Hour
20
// Service exposes the functionality of the Juju entity needed here.
21
type Service interface {
22
// ID identifies the service in the model.
25
// CharmURL identifies the service's charm.
29
16
// DataStore exposes the functionality of Juju state needed here.
30
17
type DataStore interface {
31
// ListAllServices returns all the services in the model.
32
ListAllServices() ([]Service, error)
34
18
// SetCharmStoreResources sets the "polled from the charm store"
35
19
// resources for the service to the provided values.
36
20
SetCharmStoreResources(serviceID string, info []charmresource.Resource, lastPolled time.Time) error
39
// CharmStoreClient exposes the functionality of the charm store
41
type CharmStoreClient interface {
44
// ListResources returns the resources info for each identified charm.
45
ListResources([]*charm.URL) ([][]charmresource.Resource, error)
48
// CharmStorePoller provides the functionality to poll the charm store
49
// for changes in resources in the Juju model.
50
type CharmStorePoller struct {
53
// Period is the time between poll attempts.
57
// NewCharmStorePoller returns a charm store poller that uses the
58
// provided data store.
59
func NewCharmStorePoller(st DataStore, newClient func() (CharmStoreClient, error)) *CharmStorePoller {
60
deps := &csPollerDeps{
64
return &CharmStorePoller{
65
CharmStorePollerDeps: deps,
66
Period: charmStorePollPeriod,
70
// NewWorker returns a new periodic worker for the poller.
71
func (csp CharmStorePoller) NewWorker() worker.Worker {
72
// TODO(ericsnow) Wrap Do() in a retry? Log the error instead of
74
return csp.NewPeriodicWorker(csp.Do, csp.Period)
77
func shouldStop(stop <-chan struct{}) bool {
86
// Do performs a single polling iteration.
87
func (csp CharmStorePoller) Do(stop <-chan struct{}) error {
88
services, err := csp.ListAllServices()
90
return errors.Trace(err)
96
var cURLs []*charm.URL
97
for _, service := range services {
98
cURL := service.CharmURL()
102
cURLs = append(cURLs, cURL)
104
if shouldStop(stop) {
108
chResources, err := csp.ListCharmStoreResources(cURLs)
110
return errors.Trace(err)
113
lastPolled := time.Now().UTC()
114
// Note: since we used "services" to compose the list of charm URL
115
// s passed to the charm store client, there is a one-to-one
116
// correspondence between "services" and "chResources".
117
for i, service := range services {
118
if shouldStop(stop) {
122
serviceID := service.ID().Id()
123
if err := csp.SetCharmStoreResources(serviceID, chResources[i], lastPolled); err != nil {
124
return errors.Trace(err)
23
// LatestCharmHandler implements apiserver/charmrevisionupdater.LatestCharmHandler.
24
type LatestCharmHandler struct {
28
// NewLatestCharmHandler returns a LatestCharmHandler that uses the
30
func NewLatestCharmHandler(store DataStore) *LatestCharmHandler {
31
return &LatestCharmHandler{
36
// HandleLatest implements apiserver/charmrevisionupdater.LatestCharmHandler
37
// by storing the charm's resources in state.
38
func (handler LatestCharmHandler) HandleLatest(serviceID names.ServiceTag, info charmstore.CharmInfo) error {
39
if err := handler.store.SetCharmStoreResources(serviceID.Id(), info.LatestResources, info.Timestamp); err != nil {
40
return errors.Trace(err)
131
// CharmStorePollerDeps exposes the external dependencies of a charm
133
type CharmStorePollerDeps interface {
136
// NewPeriodicWorker returns a new periodic worker.
137
NewPeriodicWorker(func(stop <-chan struct{}) error, time.Duration) worker.Worker
139
// ListCharmStoreResources returns the resources from the charm
140
// store for each of the identified charms.
141
ListCharmStoreResources([]*charm.URL) ([][]charmresource.Resource, error)
144
type csPollerDeps struct {
146
newClient func() (CharmStoreClient, error)
149
// NewPeriodicWorker implements CharmStorePollerDeps.
150
func (csPollerDeps) NewPeriodicWorker(call func(stop <-chan struct{}) error, period time.Duration) worker.Worker {
151
return worker.NewPeriodicWorker(call, period, worker.NewTimer)
154
// ListCharmStoreResources implements CharmStorePollerDeps.
155
func (deps csPollerDeps) ListCharmStoreResources(cURLs []*charm.URL) ([][]charmresource.Resource, error) {
156
client, err := deps.newClient()
158
return nil, errors.Trace(err)
162
chResources, err := client.ListResources(cURLs)
164
return nil, errors.Trace(err)
166
return chResources, nil