~juju-qa/ubuntu/xenial/juju/xenial-2.0-beta3

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/resource/workers/charmstorepoller.go

  • Committer: Martin Packman
  • Date: 2016-03-30 19:31:08 UTC
  • mfrom: (1.1.41)
  • Revision ID: martin.packman@canonical.com-20160330193108-h9iz3ak334uk0z5r
Merge new upstream source 2.0~beta3

Show diffs side-by-side

added added

removed removed

Lines of Context:
4
4
package workers
5
5
 
6
6
import (
7
 
        "io"
8
7
        "time"
9
8
 
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"
14
12
 
15
 
        "github.com/juju/juju/worker"
 
13
        "github.com/juju/juju/charmstore"
16
14
)
17
15
 
18
 
const charmStorePollPeriod = 24 * time.Hour
19
 
 
20
 
// Service exposes the functionality of the Juju entity needed here.
21
 
type Service interface {
22
 
        // ID identifies the service in the model.
23
 
        ID() names.ServiceTag
24
 
 
25
 
        // CharmURL identifies the service's charm.
26
 
        CharmURL() *charm.URL
27
 
}
28
 
 
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)
33
 
 
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
37
21
}
38
22
 
39
 
// CharmStoreClient exposes the functionality of the charm store
40
 
// needed here.
41
 
type CharmStoreClient interface {
42
 
        io.Closer
43
 
 
44
 
        // ListResources returns the resources info for each identified charm.
45
 
        ListResources([]*charm.URL) ([][]charmresource.Resource, error)
46
 
}
47
 
 
48
 
// CharmStorePoller provides the functionality to poll the charm store
49
 
// for changes in resources in the Juju model.
50
 
type CharmStorePoller struct {
51
 
        CharmStorePollerDeps
52
 
 
53
 
        // Period is the time between poll attempts.
54
 
        Period time.Duration
55
 
}
56
 
 
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{
61
 
                DataStore: st,
62
 
                newClient: newClient,
63
 
        }
64
 
        return &CharmStorePoller{
65
 
                CharmStorePollerDeps: deps,
66
 
                Period:               charmStorePollPeriod,
67
 
        }
68
 
}
69
 
 
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
73
 
        // returning it?
74
 
        return csp.NewPeriodicWorker(csp.Do, csp.Period)
75
 
}
76
 
 
77
 
func shouldStop(stop <-chan struct{}) bool {
78
 
        select {
79
 
        case <-stop:
80
 
                return true
81
 
        default:
82
 
                return false
83
 
        }
84
 
}
85
 
 
86
 
// Do performs a single polling iteration.
87
 
func (csp CharmStorePoller) Do(stop <-chan struct{}) error {
88
 
        services, err := csp.ListAllServices()
89
 
        if err != nil {
90
 
                return errors.Trace(err)
91
 
        }
92
 
        if shouldStop(stop) {
93
 
                return nil
94
 
        }
95
 
 
96
 
        var cURLs []*charm.URL
97
 
        for _, service := range services {
98
 
                cURL := service.CharmURL()
99
 
                if cURL == nil {
100
 
                        continue
101
 
                }
102
 
                cURLs = append(cURLs, cURL)
103
 
        }
104
 
        if shouldStop(stop) {
105
 
                return nil
106
 
        }
107
 
 
108
 
        chResources, err := csp.ListCharmStoreResources(cURLs)
109
 
        if err != nil {
110
 
                return errors.Trace(err)
111
 
        }
112
 
 
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) {
119
 
                        return nil
120
 
                }
121
 
 
122
 
                serviceID := service.ID().Id()
123
 
                if err := csp.SetCharmStoreResources(serviceID, chResources[i], lastPolled); err != nil {
124
 
                        return errors.Trace(err)
125
 
                }
126
 
        }
127
 
 
 
23
// LatestCharmHandler implements apiserver/charmrevisionupdater.LatestCharmHandler.
 
24
type LatestCharmHandler struct {
 
25
        store DataStore
 
26
}
 
27
 
 
28
// NewLatestCharmHandler returns a LatestCharmHandler that uses the
 
29
// given data store.
 
30
func NewLatestCharmHandler(store DataStore) *LatestCharmHandler {
 
31
        return &LatestCharmHandler{
 
32
                store: store,
 
33
        }
 
34
}
 
35
 
 
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)
 
41
        }
128
42
        return nil
129
43
}
130
 
 
131
 
// CharmStorePollerDeps exposes the external dependencies of a charm
132
 
// store poller.
133
 
type CharmStorePollerDeps interface {
134
 
        DataStore
135
 
 
136
 
        // NewPeriodicWorker returns a new periodic worker.
137
 
        NewPeriodicWorker(func(stop <-chan struct{}) error, time.Duration) worker.Worker
138
 
 
139
 
        // ListCharmStoreResources returns the resources from the charm
140
 
        // store for each of the identified charms.
141
 
        ListCharmStoreResources([]*charm.URL) ([][]charmresource.Resource, error)
142
 
}
143
 
 
144
 
type csPollerDeps struct {
145
 
        DataStore
146
 
        newClient func() (CharmStoreClient, error)
147
 
}
148
 
 
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)
152
 
}
153
 
 
154
 
// ListCharmStoreResources implements CharmStorePollerDeps.
155
 
func (deps csPollerDeps) ListCharmStoreResources(cURLs []*charm.URL) ([][]charmresource.Resource, error) {
156
 
        client, err := deps.newClient()
157
 
        if err != nil {
158
 
                return nil, errors.Trace(err)
159
 
        }
160
 
        defer client.Close()
161
 
 
162
 
        chResources, err := client.ListResources(cURLs)
163
 
        if err != nil {
164
 
                return nil, errors.Trace(err)
165
 
        }
166
 
        return chResources, nil
167
 
}