~abp998/gwacl/subscription

« back to all changes in this revision

Viewing changes to poller.go

  • Committer: Gavin Panella
  • Date: 2013-03-12 17:05:36 UTC
  • mto: (5.3.2 use-dedent)
  • mto: This revision was merged to the branch mainline in revision 8.
  • Revision ID: gavin@gromper.net-20130312170536-b7u1wbzy0tf0bbxa
Reformat with 4 spaces instead of tabs.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// Copyright 2013 Canonical Ltd.  This software is licensed under the
2
 
// GNU Lesser General Public License version 3 (see the file COPYING).
3
 
 
4
 
package gwacl
5
 
 
6
 
import (
7
 
    "fmt"
8
 
    "time"
9
 
)
10
 
 
11
 
// Generic poller interface/methods.
12
 
 
13
 
// A poller exposes two methods to query a remote server and decide when
14
 
// the response given by the server means that the polling is finished.
15
 
type poller interface {
16
 
    poll() (*x509Response, error)
17
 
    isDone(*x509Response, error) (bool, error)
18
 
}
19
 
 
20
 
// performPolling calls the poll() method of the given 'poller' object every
21
 
// 'interval' until poller.isDone() returns true.
22
 
func performPolling(poller poller, interval time.Duration, timeout time.Duration) (*x509Response, error) {
23
 
    timeoutChannel := time.After(timeout)
24
 
    ticker := time.Tick(interval)
25
 
    // Function to do a single poll, checking for timeout. The bool returned
26
 
    // indicates if polling is finished, one way or another.
27
 
    poll := func() (bool, *x509Response, error) {
28
 
        // This may need to tolerate some transient failures, such as network
29
 
        // failures that may go away after a few retries.
30
 
        select {
31
 
        case <-timeoutChannel:
32
 
            return true, nil, fmt.Errorf("polling timed out waiting for an asynchronous operation")
33
 
        default:
34
 
            response, pollerErr := poller.poll()
35
 
            done, err := poller.isDone(response, pollerErr)
36
 
            if err != nil {
37
 
                return true, nil, err
38
 
            }
39
 
            if done {
40
 
                return true, response, nil
41
 
            }
42
 
        }
43
 
        return false, nil, nil
44
 
    }
45
 
    // Do an initial poll.
46
 
    done, response, err := poll()
47
 
    if done {
48
 
        return response, err
49
 
    }
50
 
    // Poll every interval.
51
 
    for _ = range ticker {
52
 
        done, response, err := poll()
53
 
        if done {
54
 
            return response, err
55
 
        }
56
 
    }
57
 
    // This code cannot be reached but Go insists on having a return or a panic
58
 
    // statement at the end of this method.  Sigh.
59
 
    panic("invalid poller state!")
60
 
}
61
 
 
62
 
// Operation poller structs/methods.
63
 
 
64
 
// performOperationPolling calls performPolling on the given arguments and converts
65
 
// the returned object into an *Operation.
66
 
func performOperationPolling(poller poller, interval time.Duration, timeout time.Duration) (*Operation, error) {
67
 
    response, err := performPolling(poller, interval, timeout)
68
 
    if err != nil {
69
 
        return nil, err
70
 
    }
71
 
    operation := Operation{}
72
 
    err = operation.Deserialize(response.Body)
73
 
    return &operation, err
74
 
}
75
 
 
76
 
// operationPoller is an object implementing the poller interface, used to
77
 
// poll the Window Azure server until the operation referenced by the given
78
 
// operationID is completed.
79
 
type operationPoller struct {
80
 
    api         *ManagementAPI
81
 
    operationID string
82
 
}
83
 
 
84
 
var _ poller = &operationPoller{}
85
 
 
86
 
// newOperationPoller returns a poller object associated with the given
87
 
// management API object and the given operationID.  It can track (by polling
88
 
// the server) the status of the operation associated with the provided
89
 
// operationID string.
90
 
func newOperationPoller(api *ManagementAPI, operationID string) poller {
91
 
    return operationPoller{api: api, operationID: operationID}
92
 
}
93
 
 
94
 
// Poll issues a blocking request to microsoft Azure to fetch the information
95
 
// related to the operation associated with the poller.
96
 
// See http://msdn.microsoft.com/en-us/library/windowsazure/ee460783.aspx
97
 
func (poller operationPoller) poll() (*x509Response, error) {
98
 
    URI := "operations/" + poller.operationID
99
 
    return poller.api.session.get(URI, "2009-10-01")
100
 
}
101
 
 
102
 
// IsDone returns true if the given response has a status code indicating
103
 
// success and if the returned XML response corresponds to a valid Operation
104
 
// with a status indicating that the operation is completed.
105
 
func (poller operationPoller) isDone(response *x509Response, pollerError error) (bool, error) {
106
 
    // TODO: Add a timeout so that polling won't continue forever if the
107
 
    // server cannot be reached.
108
 
    if pollerError != nil {
109
 
        return true, pollerError
110
 
    }
111
 
    if response.StatusCode >= 200 && response.StatusCode < 300 {
112
 
        operation := Operation{}
113
 
        err := operation.Deserialize(response.Body)
114
 
        if err != nil {
115
 
            return false, err
116
 
        }
117
 
        status := operation.Status
118
 
        done := (status != "" && status != InProgressOperationStatus)
119
 
        return done, nil
120
 
    }
121
 
    return false, nil
122
 
}