Updates godep for pkg/volume/flocker

Supports additional options for CreateDataset and DeleteDataset for dynamic provisioning. Bugfix for timeouts during CreateDataset
This commit is contained in:
Christian Simon 2016-08-01 22:42:43 +01:00
parent 1c11047ffb
commit 6f2af39021
8 changed files with 106 additions and 38 deletions

8
Godeps/Godeps.json generated
View File

@ -65,10 +65,6 @@
"Comment": "v7.0.6-4-g2492d97", "Comment": "v7.0.6-4-g2492d97",
"Rev": "2492d97b402e00797833c03ac5fa1c572c7bb29a" "Rev": "2492d97b402e00797833c03ac5fa1c572c7bb29a"
}, },
{
"ImportPath": "github.com/ClusterHQ/flocker-go",
"Rev": "1c0a791b33bdc01d062b376612aa04e27eed7eb3"
},
{ {
"ImportPath": "github.com/Microsoft/go-winio", "ImportPath": "github.com/Microsoft/go-winio",
"Comment": "v0.1.0", "Comment": "v0.1.0",
@ -331,6 +327,10 @@
"Comment": "1.2.0", "Comment": "1.2.0",
"Rev": "db0d0650b6496bfe8061ec56a92edd32d8e75c30" "Rev": "db0d0650b6496bfe8061ec56a92edd32d8e75c30"
}, },
{
"ImportPath": "github.com/clusterhq/flocker-go",
"Rev": "2b8b7259d3139c96c4a6871031355808ab3fd3b3"
},
{ {
"ImportPath": "github.com/codegangsta/negroni", "ImportPath": "github.com/codegangsta/negroni",
"Comment": "v0.1.0-62-g8d75e11", "Comment": "v0.1.0-62-g8d75e11",

4
Godeps/LICENSES generated
View File

@ -9231,7 +9231,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================================================ ================================================================================
= vendor/github.com/ClusterHQ/flocker-go licensed under: = = vendor/github.com/clusterhq/flocker-go licensed under: =
Apache License Apache License
Version 2.0, January 2004 Version 2.0, January 2004
@ -9424,7 +9424,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
= vendor/github.com/ClusterHQ/flocker-go/LICENSE d8103d9796cd0e951379d0834edad066 - = vendor/github.com/clusterhq/flocker-go/LICENSE d8103d9796cd0e951379d0834edad066 -
================================================================================ ================================================================================

16
vendor/github.com/clusterhq/flocker-go/circle.yml generated vendored Normal file
View File

@ -0,0 +1,16 @@
machine:
timezone:
America/Los_Angeles
# Output the test output to circle.
test:
pre:
- go get -u github.com/jstemmer/go-junit-report
override:
- go test -coverprofile=coverage.out -v -race ./... > test.out
- cat test.out | go-junit-report > report.xml
- go tool cover -func=coverage.out
post:
- mv test.out $CIRCLE_ARTIFACTS/
- mv report.xml $CIRCLE_TEST_REPORTS/

View File

@ -34,12 +34,15 @@ var (
// Clientable exposes the needed methods to implement your own Flocker Client. // Clientable exposes the needed methods to implement your own Flocker Client.
type Clientable interface { type Clientable interface {
CreateDataset(metaName string) (*DatasetState, error) CreateDataset(options *CreateDatasetOptions) (*DatasetState, error)
DeleteDataset(datasetID string) error
GetDatasetState(datasetID string) (*DatasetState, error) GetDatasetState(datasetID string) (*DatasetState, error)
GetDatasetID(metaName string) (datasetID string, err error) GetDatasetID(metaName string) (datasetID string, err error)
GetPrimaryUUID() (primaryUUID string, err error) GetPrimaryUUID() (primaryUUID string, err error)
ListNodes() (nodes []NodeState, err error)
UpdatePrimaryForDataset(primaryUUID, datasetID string) (*DatasetState, error) UpdatePrimaryForDataset(primaryUUID, datasetID string) (*DatasetState, error)
} }
@ -57,6 +60,8 @@ type Client struct {
maximumSize json.Number maximumSize json.Number
} }
var _ Clientable = &Client{}
// NewClient creates a wrapper over http.Client to communicate with the flocker control service. // NewClient creates a wrapper over http.Client to communicate with the flocker control service.
func NewClient(host string, port int, clientIP string, caCertPath, keyPath, certPath string) (*Client, error) { func NewClient(host string, port int, clientIP string, caCertPath, keyPath, certPath string) (*Client, error) {
client, err := newTLSClient(caCertPath, keyPath, certPath) client, err := newTLSClient(caCertPath, keyPath, certPath)
@ -110,6 +115,11 @@ func (c Client) post(url string, payload interface{}) (*http.Response, error) {
return c.request("POST", url, payload) return c.request("POST", url, payload)
} }
// delete performs a delete request with the indicated payload
func (c Client) delete(url string, payload interface{}) (*http.Response, error) {
return c.request("DELETE", url, payload)
}
// get performs a get request // get performs a get request
func (c Client) get(url string) (*http.Response, error) { func (c Client) get(url string) (*http.Response, error) {
return c.request("GET", url, nil) return c.request("GET", url, nil)
@ -128,6 +138,13 @@ type configurationPayload struct {
Metadata metadataPayload `json:"metadata,omitempty"` Metadata metadataPayload `json:"metadata,omitempty"`
} }
type CreateDatasetOptions struct {
Primary string `json:"primary"`
DatasetID string `json:"dataset_id,omitempty"`
MaximumSize int64 `json:"maximum_size,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
}
type metadataPayload struct { type metadataPayload struct {
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
} }
@ -143,7 +160,7 @@ type datasetStatePayload struct {
*DatasetState *DatasetState
} }
type nodeStatePayload struct { type NodeState struct {
UUID string `json:"uuid"` UUID string `json:"uuid"`
Host string `json:"host"` Host string `json:"host"`
} }
@ -163,25 +180,54 @@ func (c Client) findIDInConfigurationsPayload(body io.ReadCloser, name string) (
return "", err return "", err
} }
// ListNodes returns a list of dataset agent nodes from Flocker Control Service
func (c *Client) ListNodes() (nodes []NodeState, err error) {
resp, err := c.get(c.getURL("state/nodes"))
if err != nil {
return []NodeState{}, err
}
defer resp.Body.Close()
if resp.StatusCode >= 300 {
return []NodeState{}, fmt.Errorf("Expected: {1,2}xx listing nodes, got: %d", resp.StatusCode)
}
err = json.NewDecoder(resp.Body).Decode(&nodes)
if err != nil {
return []NodeState{}, err
}
return nodes, err
}
// GetPrimaryUUID returns the UUID of the primary Flocker Control Service for // GetPrimaryUUID returns the UUID of the primary Flocker Control Service for
// the given host. // the given host.
func (c Client) GetPrimaryUUID() (uuid string, err error) { func (c Client) GetPrimaryUUID() (uuid string, err error) {
resp, err := c.get(c.getURL("state/nodes")) states, err := c.ListNodes()
if err != nil { if err != nil {
return "", err return "", err
} }
defer resp.Body.Close()
var states []nodeStatePayload
if err = json.NewDecoder(resp.Body).Decode(&states); err == nil {
for _, s := range states { for _, s := range states {
if s.Host == c.clientIP { if s.Host == c.clientIP {
return s.UUID, nil return s.UUID, nil
} }
} }
return "", errStateNotFound return "", fmt.Errorf("No node found with IP '%s', available nodes %+v", c.clientIP, states)
}
// DeleteDataset performs a delete request to the given datasetID
func (c *Client) DeleteDataset(datasetID string) error {
url := c.getURL(fmt.Sprintf("configuration/datasets/%s", datasetID))
resp, err := c.delete(url, nil)
if err != nil {
return err
} }
return "", err defer resp.Body.Close()
if resp.StatusCode >= 300 {
return fmt.Errorf("Expected: {1,2}xx deleting the dataset %s, got: %d", datasetID, resp.StatusCode)
}
return nil
} }
// GetDatasetState performs a get request to get the state of the given datasetID, if // GetDatasetState performs a get request to get the state of the given datasetID, if
@ -213,34 +259,30 @@ returns the dataset id.
This process is a little bit complex but follows this flow: This process is a little bit complex but follows this flow:
1. Find the Flocker Control Service UUID 1. Find the Flocker Control Service UUID
2. Try to create the dataset 2. If it already exists an error is returned
3. If it already exists an error is returned 3. If it didn't previously exist, wait for it to be ready
4. If it didn't previously exist, wait for it to be ready
*/ */
func (c Client) CreateDataset(metaName string) (*DatasetState, error) { func (c *Client) CreateDataset(options *CreateDatasetOptions) (datasetState *DatasetState, err error) {
// 1) Find the primary Flocker UUID // 1) Find the primary Flocker UUID
// Note: it could be cached, but doing this query we health check it // Note: it could be cached, but doing this query we health check it
primary, err := c.GetPrimaryUUID() if options.Primary == "" {
options.Primary, err = c.GetPrimaryUUID()
if err != nil { if err != nil {
return nil, err return nil, err
} }
// 2) Try to create the dataset in the given Primary
payload := configurationPayload{
Primary: primary,
MaximumSize: json.Number(c.maximumSize),
Metadata: metadataPayload{
Name: metaName,
},
} }
resp, err := c.post(c.getURL("configuration/datasets"), payload) if options.MaximumSize == 0 {
options.MaximumSize, _ = c.maximumSize.Int64()
}
resp, err := c.post(c.getURL("configuration/datasets"), options)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
// 3) Return if the dataset was previously created // 2) Return if the dataset was previously created
if resp.StatusCode == http.StatusConflict { if resp.StatusCode == http.StatusConflict {
return nil, errVolumeAlreadyExists return nil, errVolumeAlreadyExists
} }
@ -254,21 +296,31 @@ func (c Client) CreateDataset(metaName string) (*DatasetState, error) {
return nil, err return nil, err
} }
// 4) Wait until the dataset is ready for usage. In case it never gets // 3) Wait until the dataset is ready for usage. In case it never gets
// ready there is a timeoutChan that will return an error // ready there is a timeoutChan that will return an error
timeoutChan := time.NewTimer(timeoutWaitingForVolume).C timeoutChan := time.NewTimer(timeoutWaitingForVolume).C
tickChan := time.NewTicker(tickerWaitingForVolume).C tickChan := time.NewTicker(tickerWaitingForVolume).C
for { for {
if s, err := c.GetDatasetState(p.DatasetID); err == nil { var strErrDel string
s, err := c.GetDatasetState(p.DatasetID)
if err == nil {
return s, nil return s, nil
} else if err != errStateNotFound { } else if err != errStateNotFound {
return nil, err errDel := c.DeleteDataset(p.DatasetID)
if errDel != nil {
strErrDel = fmt.Sprintf(", deletion of dataset failed with %s", errDel)
}
return nil, fmt.Errorf("Flocker API error during dataset creation (datasetID %s): %s%s", p.DatasetID, err, strErrDel)
} }
select { select {
case <-timeoutChan: case <-timeoutChan:
return nil, err errDel := c.DeleteDataset(p.DatasetID)
if errDel != nil {
strErrDel = fmt.Sprintf(", deletion of dataset failed with %s", errDel)
}
return nil, fmt.Errorf("Flocker API timeout during dataset creation (datasetID %s): %s%s", p.DatasetID, err, strErrDel)
case <-tickChan: case <-tickChan:
break break
} }