From 6f2af39021fade5f3730516ab15f7eb4102706d5 Mon Sep 17 00:00:00 2001 From: Christian Simon Date: Mon, 1 Aug 2016 22:42:43 +0100 Subject: [PATCH] Updates godep for pkg/volume/flocker Supports additional options for CreateDataset and DeleteDataset for dynamic provisioning. Bugfix for timeouts during CreateDataset --- Godeps/Godeps.json | 8 +- Godeps/LICENSES | 4 +- .../flocker-go/LICENSE | 0 .../flocker-go/README.md | 0 .../clusterhq/flocker-go/circle.yml | 16 +++ .../flocker-go/client.go | 116 +++++++++++++----- .../flocker-go/doc.go | 0 .../flocker-go/util.go | 0 8 files changed, 106 insertions(+), 38 deletions(-) rename vendor/github.com/{ClusterHQ => clusterhq}/flocker-go/LICENSE (100%) rename vendor/github.com/{ClusterHQ => clusterhq}/flocker-go/README.md (100%) create mode 100644 vendor/github.com/clusterhq/flocker-go/circle.yml rename vendor/github.com/{ClusterHQ => clusterhq}/flocker-go/client.go (73%) rename vendor/github.com/{ClusterHQ => clusterhq}/flocker-go/doc.go (100%) rename vendor/github.com/{ClusterHQ => clusterhq}/flocker-go/util.go (100%) diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 3037c3b878a..ae6786c014c 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -65,10 +65,6 @@ "Comment": "v7.0.6-4-g2492d97", "Rev": "2492d97b402e00797833c03ac5fa1c572c7bb29a" }, - { - "ImportPath": "github.com/ClusterHQ/flocker-go", - "Rev": "1c0a791b33bdc01d062b376612aa04e27eed7eb3" - }, { "ImportPath": "github.com/Microsoft/go-winio", "Comment": "v0.1.0", @@ -331,6 +327,10 @@ "Comment": "1.2.0", "Rev": "db0d0650b6496bfe8061ec56a92edd32d8e75c30" }, + { + "ImportPath": "github.com/clusterhq/flocker-go", + "Rev": "2b8b7259d3139c96c4a6871031355808ab3fd3b3" + }, { "ImportPath": "github.com/codegangsta/negroni", "Comment": "v0.1.0-62-g8d75e11", diff --git a/Godeps/LICENSES b/Godeps/LICENSES index 4bc102e8923..108a3121d1e 100644 --- a/Godeps/LICENSES +++ b/Godeps/LICENSES @@ -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 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 limitations under the License. -= vendor/github.com/ClusterHQ/flocker-go/LICENSE d8103d9796cd0e951379d0834edad066 - += vendor/github.com/clusterhq/flocker-go/LICENSE d8103d9796cd0e951379d0834edad066 - ================================================================================ diff --git a/vendor/github.com/ClusterHQ/flocker-go/LICENSE b/vendor/github.com/clusterhq/flocker-go/LICENSE similarity index 100% rename from vendor/github.com/ClusterHQ/flocker-go/LICENSE rename to vendor/github.com/clusterhq/flocker-go/LICENSE diff --git a/vendor/github.com/ClusterHQ/flocker-go/README.md b/vendor/github.com/clusterhq/flocker-go/README.md similarity index 100% rename from vendor/github.com/ClusterHQ/flocker-go/README.md rename to vendor/github.com/clusterhq/flocker-go/README.md diff --git a/vendor/github.com/clusterhq/flocker-go/circle.yml b/vendor/github.com/clusterhq/flocker-go/circle.yml new file mode 100644 index 00000000000..81b56c8f27f --- /dev/null +++ b/vendor/github.com/clusterhq/flocker-go/circle.yml @@ -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/ + diff --git a/vendor/github.com/ClusterHQ/flocker-go/client.go b/vendor/github.com/clusterhq/flocker-go/client.go similarity index 73% rename from vendor/github.com/ClusterHQ/flocker-go/client.go rename to vendor/github.com/clusterhq/flocker-go/client.go index d379a03516d..7cdd71054cb 100644 --- a/vendor/github.com/ClusterHQ/flocker-go/client.go +++ b/vendor/github.com/clusterhq/flocker-go/client.go @@ -34,12 +34,15 @@ var ( // Clientable exposes the needed methods to implement your own Flocker Client. type Clientable interface { - CreateDataset(metaName string) (*DatasetState, error) + CreateDataset(options *CreateDatasetOptions) (*DatasetState, error) + DeleteDataset(datasetID string) error GetDatasetState(datasetID string) (*DatasetState, error) GetDatasetID(metaName string) (datasetID string, err error) GetPrimaryUUID() (primaryUUID string, err error) + ListNodes() (nodes []NodeState, err error) + UpdatePrimaryForDataset(primaryUUID, datasetID string) (*DatasetState, error) } @@ -57,6 +60,8 @@ type Client struct { maximumSize json.Number } +var _ Clientable = &Client{} + // 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) { 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) } +// 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 func (c Client) get(url string) (*http.Response, error) { return c.request("GET", url, nil) @@ -128,6 +138,13 @@ type configurationPayload struct { 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 { Name string `json:"name,omitempty"` } @@ -143,7 +160,7 @@ type datasetStatePayload struct { *DatasetState } -type nodeStatePayload struct { +type NodeState struct { UUID string `json:"uuid"` Host string `json:"host"` } @@ -163,25 +180,54 @@ func (c Client) findIDInConfigurationsPayload(body io.ReadCloser, name string) ( 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 // the given host. func (c Client) GetPrimaryUUID() (uuid string, err error) { - resp, err := c.get(c.getURL("state/nodes")) + states, err := c.ListNodes() if err != nil { return "", err } + + for _, s := range states { + if s.Host == c.clientIP { + return s.UUID, nil + } + } + 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 + } defer resp.Body.Close() - var states []nodeStatePayload - if err = json.NewDecoder(resp.Body).Decode(&states); err == nil { - for _, s := range states { - if s.Host == c.clientIP { - return s.UUID, nil - } - } - return "", errStateNotFound + if resp.StatusCode >= 300 { + return fmt.Errorf("Expected: {1,2}xx deleting the dataset %s, got: %d", datasetID, resp.StatusCode) } - return "", err + + return nil } // 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: 1. Find the Flocker Control Service UUID -2. Try to create the dataset -3. If it already exists an error is returned -4. If it didn't previously exist, wait for it to be ready +2. If it already exists an error is returned +3. 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 // Note: it could be cached, but doing this query we health check it - primary, err := c.GetPrimaryUUID() - if err != nil { - return nil, err + if options.Primary == "" { + options.Primary, err = c.GetPrimaryUUID() + if err != nil { + 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, - }, + if options.MaximumSize == 0 { + options.MaximumSize, _ = c.maximumSize.Int64() } - resp, err := c.post(c.getURL("configuration/datasets"), payload) + resp, err := c.post(c.getURL("configuration/datasets"), options) if err != nil { return nil, err } 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 { return nil, errVolumeAlreadyExists } @@ -254,21 +296,31 @@ func (c Client) CreateDataset(metaName string) (*DatasetState, error) { 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 timeoutChan := time.NewTimer(timeoutWaitingForVolume).C tickChan := time.NewTicker(tickerWaitingForVolume).C 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 } 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 { 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: break } diff --git a/vendor/github.com/ClusterHQ/flocker-go/doc.go b/vendor/github.com/clusterhq/flocker-go/doc.go similarity index 100% rename from vendor/github.com/ClusterHQ/flocker-go/doc.go rename to vendor/github.com/clusterhq/flocker-go/doc.go diff --git a/vendor/github.com/ClusterHQ/flocker-go/util.go b/vendor/github.com/clusterhq/flocker-go/util.go similarity index 100% rename from vendor/github.com/ClusterHQ/flocker-go/util.go rename to vendor/github.com/clusterhq/flocker-go/util.go