diff --git a/api/plugins.go b/api/plugins.go index d1c3f98..7f6e41c 100644 --- a/api/plugins.go +++ b/api/plugins.go @@ -7,49 +7,39 @@ import ( "time" ) -type PluginParams struct { - Name string `json:"plugin_name,omitempty"` - Enabled bool `json:"enabled,omitempty"` -} - -func (api *API) waitUntilPluginChanged(instanceID int, pluginName string, enabled bool) (map[string]interface{}, error) { - log.Printf("[DEBUG] go-api::plugin::waitUntilPluginChanged instance id: %v, name: %v", instanceID, pluginName) - for { - time.Sleep(10 * time.Second) - response, err := api.ReadPlugin(instanceID, pluginName) - log.Printf("[DEBUG] go-api::plugin::waitUntilPluginChanged response: %v", response) - if err != nil { - return nil, err - } - if response["required"] != nil && response["required"] != false { - return response, nil - } - if response["enabled"] == enabled { - return response, nil - } - } -} - -func (api *API) EnablePlugin(instanceID int, pluginName string) (map[string]interface{}, error) { - failed := make(map[string]interface{}) - params := &PluginParams{Name: pluginName} - log.Printf("[DEBUG] go-api::plugin::enable instance id: %v, params: %v", instanceID, pluginName) - path := fmt.Sprintf("/api/instances/%d/plugins?async=true", instanceID) +// EnablePlugin: enable a plugin on an instance. +func (api *API) EnablePlugin(instanceID int, pluginName string, sleep, timeout int) ( + map[string]interface{}, error) { + + var ( + failed map[string]interface{} + params = make(map[string]interface{}) + path = fmt.Sprintf("/api/instances/%d/plugins?async=true", instanceID) + ) + + params["plugin_name"] = pluginName + log.Printf("[DEBUG] go-api::plugin::enable instance id: %v, params: %v", instanceID, params) response, err := api.sling.New().Post(path).BodyJSON(params).Receive(nil, &failed) if err != nil { return nil, err } - if response.StatusCode != 204 { - return nil, fmt.Errorf("EnablePlugin failed, status: %v, message: %s", response.StatusCode, failed) - } - return api.waitUntilPluginChanged(instanceID, pluginName, true) + switch response.StatusCode { + case 204: + return api.waitUntilPluginChanged(instanceID, pluginName, true, 1, sleep, timeout) + default: + return nil, + fmt.Errorf("enable plugin failed, status: %v, message: %s", response.StatusCode, failed) + } } -func (api *API) ReadPlugin(instanceID int, pluginName string) (map[string]interface{}, error) { +// ReadPlugin: reads a specific plugin from an instance. +func (api *API) ReadPlugin(instanceID int, pluginName string, sleep, timeout int) ( + map[string]interface{}, error) { + log.Printf("[DEBUG] go-api::plugin::read instance id: %v, name: %v", instanceID, pluginName) - data, err := api.ReadPlugins(instanceID) + data, err := api.ListPlugins(instanceID, sleep, timeout) if err != nil { return nil, err } @@ -64,86 +54,144 @@ func (api *API) ReadPlugin(instanceID int, pluginName string) (map[string]interf return nil, nil } -func (api *API) ReadPlugins(instanceID int) ([]map[string]interface{}, error) { - // Initiale values, 5 attempts and 20 second sleep - return api.readPluginsWithRetry(instanceID, 5, 20) +// ListPlugins: list plugins from an instance. +func (api *API) ListPlugins(instanceID, sleep, timeout int) ([]map[string]interface{}, error) { + return api.listPluginsWithRetry(instanceID, 1, sleep, timeout) } -func (api *API) readPluginsWithRetry(instanceID, attempts, sleep int) ([]map[string]interface{}, error) { - var data []map[string]interface{} - failed := make(map[string]interface{}) - log.Printf("[DEBUG] go-api::plugin::readWithRetry instance id: %v", instanceID) - path := fmt.Sprintf("/api/instances/%d/plugins", instanceID) - response, err := api.sling.New().Get(path).Receive(&data, &failed) +// listPluginsWithRetry: list plugins from an instance, with retry if backend is busy. +func (api *API) listPluginsWithRetry(instanceID, attempt, sleep, timeout int) ( + []map[string]interface{}, error) { + + var ( + data []map[string]interface{} + failed map[string]interface{} + path = fmt.Sprintf("/api/instances/%d/plugins", instanceID) + ) + response, err := api.sling.New().Get(path).Receive(&data, &failed) if err != nil { return nil, err + } else if attempt*sleep > timeout { + return nil, fmt.Errorf("read plugins reached timeout of %d seconds", timeout) } - statusCode := response.StatusCode - log.Printf("[DEBUG] go-api::plugins::readWithRetry statusCode: %d", statusCode) - switch { - case statusCode == 400: + switch response.StatusCode { + case 200: + return data, nil + case 400: if strings.Compare(failed["error"].(string), "Timeout talking to backend") == 0 { - if attempts--; attempts > 0 { - log.Printf("[INFO] go-api::plugin::readWithRetry Timeout talking to backend "+ - "attempts left %d and retry in %d seconds", attempts, sleep) - time.Sleep(time.Duration(sleep) * time.Second) - return api.readPluginsWithRetry(instanceID, attempts, 2*sleep) - } else { - return nil, fmt.Errorf("ReadWithRetry failed, status: %v, message: %s", response.StatusCode, failed) - } + log.Printf("[INFO] go-api::plugins::read Timeout talking to backend "+ + "attempt: %d, until timeout: %d", attempt, (timeout - (attempt * sleep))) + attempt++ + time.Sleep(time.Duration(sleep) * time.Second) + return api.listPluginsWithRetry(instanceID, attempt, sleep, timeout) } + return nil, fmt.Errorf("ReadWithRetry failed, status: %v, message: %s", 400, failed) + default: + return nil, + fmt.Errorf("list plugin with retry failed, status: %v, message: %s", + response.StatusCode, failed) } - return data, nil } -func (api *API) UpdatePlugin(instanceID int, params map[string]interface{}) (map[string]interface{}, error) { - failed := make(map[string]interface{}) - pluginParams := &PluginParams{Name: params["name"].(string), Enabled: params["enabled"].(bool)} - log.Printf("[DEBUG] go-api::plugin::update instance ID: %v, params: %v", instanceID, pluginParams) - path := fmt.Sprintf("/api/instances/%d/plugins?async=true", instanceID) - response, err := api.sling.New().Put(path).BodyJSON(pluginParams).Receive(nil, &failed) +// UpdatePlugin: updates a plugin from an instance. +func (api *API) UpdatePlugin(instanceID int, pluginName string, enabled bool, sleep, timeout int) ( + map[string]interface{}, error) { + + var ( + failed map[string]interface{} + params = make(map[string]interface{}) + path = fmt.Sprintf("/api/instances/%d/plugins?async=true", instanceID) + ) + + params["plugin_name"] = pluginName + params["enabled"] = enabled + log.Printf("[DEBUG] go-api::plugin::update instance ID: %v, params: %v", instanceID, params) + response, err := api.sling.New().Put(path).BodyJSON(params).Receive(nil, &failed) if err != nil { return nil, err } - if response.StatusCode != 204 { - return nil, fmt.Errorf("UpdatePlugin failed, status: %v, message: %s", response.StatusCode, failed) - } - return api.waitUntilPluginChanged(instanceID, params["name"].(string), params["enabled"].(bool)) + switch response.StatusCode { + case 204: + return api.waitUntilPluginChanged(instanceID, pluginName, enabled, 1, sleep, timeout) + default: + return nil, + fmt.Errorf("update plugin failed, status: %v, message: %s", response.StatusCode, failed) + } } -func (api *API) DisablePlugin(instanceID int, pluginName string) (map[string]interface{}, error) { - failed := make(map[string]interface{}) - log.Printf("[DEBUG] go-api::plugin::disable instance id: %v, name: %v", instanceID, pluginName) - path := fmt.Sprintf("/api/instances/%d/plugins/%s?async=true", instanceID, pluginName) +// DisablePlugin: disables a plugin from an instance. +func (api *API) DisablePlugin(instanceID int, pluginName string, sleep, timeout int) ( + map[string]interface{}, error) { + + var ( + failed map[string]interface{} + path = fmt.Sprintf("/api/instances/%d/plugins/%s?async=true", instanceID, pluginName) + ) + + log.Printf("[DEBUG] go-api::plugin::disable path: %s", path) response, err := api.sling.New().Delete(path).Receive(nil, &failed) if err != nil { return nil, err } - if response.StatusCode != 204 { - return nil, fmt.Errorf("DisablePlugin failed, status: %v, message: %s", response.StatusCode, failed) - } - return api.waitUntilPluginChanged(instanceID, pluginName, false) + switch response.StatusCode { + case 204: + return api.waitUntilPluginChanged(instanceID, pluginName, false, 1, sleep, timeout) + default: + return nil, fmt.Errorf("disable plugin failed, status: %v, message: %s", + response.StatusCode, failed) + } } -func (api *API) DeletePlugin(instanceID int, pluginName string) error { - failed := make(map[string]interface{}) - log.Printf("[DEBUG] go-api::plugin::delete instance: %v, name: %v", instanceID, pluginName) - path := fmt.Sprintf("/api/instances/%d/plugins/%s?async=true", instanceID, pluginName) - response, err := api.sling.New().Delete(path).Receive(nil, &failed) +// DeletePlugin: deletes a plugin from an instance. +func (api *API) DeletePlugin(instanceID int, pluginName string, sleep, timeout int) error { + var ( + failed map[string]interface{} + path = fmt.Sprintf("/api/instances/%d/plugins/%s?async=true", instanceID, pluginName) + ) + log.Printf("[DEBUG] go-api::plugin::delete path: %s", path) + response, err := api.sling.New().Delete(path).Receive(nil, &failed) if err != nil { return err } - if response.StatusCode != 204 { - return fmt.Errorf("DeletePlugin failed, status: %v, message: %s", response.StatusCode, failed) + + switch response.StatusCode { + case 204: + _, err = api.waitUntilPluginChanged(instanceID, pluginName, false, 1, sleep, timeout) + return err + default: + return fmt.Errorf("delete plugin failed, status: %v, message: %s", + response.StatusCode, failed) } +} - _, err = api.waitUntilPluginChanged(instanceID, pluginName, false) - return err +// waitUntilPluginChanged: wait until plugin changed. +func (api *API) waitUntilPluginChanged(instanceID int, pluginName string, enabled bool, + attempt, sleep, timeout int) (map[string]interface{}, error) { + + for { + time.Sleep(time.Duration(sleep) * time.Second) + if attempt*sleep > timeout { + return nil, fmt.Errorf("wait until plugin changed reached timeout of %d seconds", timeout) + } + + response, err := api.ReadPlugin(instanceID, pluginName, sleep, timeout) + log.Printf("[DEBUG] go-api::plugin::waitUntilPluginChanged response: %v", response) + if err != nil { + return nil, err + } + if response["required"] != nil && response["required"] != false { + return response, nil + } + if response["enabled"] == enabled { + return response, nil + } + attempt++ + } } diff --git a/api/plugins_community.go b/api/plugins_community.go index 18f927b..3e914c4 100644 --- a/api/plugins_community.go +++ b/api/plugins_community.go @@ -7,45 +7,38 @@ import ( "time" ) -func (api *API) waitUntilPluginUninstalled(instanceID int, pluginName string) (map[string]interface{}, error) { - log.Printf("[DEBUG] go-api::plugin_community::waitUntilPluginUninstalled instance id: %v, name: %v", instanceID, pluginName) - time.Sleep(10 * time.Second) - for { - response, err := api.ReadPlugin(instanceID, pluginName) - - if err != nil { - return nil, err - } - if len(response) == 0 { - return response, nil - } - - time.Sleep(10 * time.Second) - } -} - -func (api *API) EnablePluginCommunity(instanceID int, pluginName string) (map[string]interface{}, error) { - failed := make(map[string]interface{}) - params := &PluginParams{Name: pluginName} - log.Printf("[DEBUG] go-api::plugin_community::enable instance ID: %v, name: %v", instanceID, pluginName) - path := fmt.Sprintf("/api/instances/%d/plugins/community?async=true", instanceID) +// InstallPluginCommunity: install a community plugin on an instance. +func (api *API) InstallPluginCommunity(instanceID int, pluginName string, sleep, timeout int) ( + map[string]interface{}, error) { + + var ( + failed map[string]interface{} + params = make(map[string]interface{}) + path = fmt.Sprintf("/api/instances/%d/plugins/community?async=true", instanceID) + ) + + params["plugin_name"] = pluginName + log.Printf("[DEBUG] go-api::plugin_community::enable path: %s", path) response, err := api.sling.New().Post(path).BodyJSON(params).Receive(nil, &failed) - if err != nil { return nil, err } - if response.StatusCode != 204 { - return nil, fmt.Errorf("EnablePluginCommunity failed, status: %v, message: %s", response.StatusCode, failed) - } - return api.waitUntilPluginChanged(instanceID, pluginName, true) + switch response.StatusCode { + case 204: + return api.waitUntilPluginChanged(instanceID, pluginName, true, 1, sleep, timeout) + default: + return nil, fmt.Errorf("install community plugin failed, status: %v, message: %v", + response.StatusCode, failed) + } } -func (api *API) ReadPluginCommunity(instanceID int, pluginName string) (map[string]interface{}, error) { - var data []map[string]interface{} - log.Printf("[DEBUG] go-api::plugin_community::read instance ID: %v, name: %v", instanceID, pluginName) - data, err := api.ReadPluginsCommunity(instanceID) +// ReadPluginCommunity: reads a specific community plugin from an instance. +func (api *API) ReadPluginCommunity(instanceID int, pluginName string, sleep, timeout int) ( + map[string]interface{}, error) { + log.Printf("[DEBUG] go-api::plugin_community::read instance ID: %v, name: %v", instanceID, pluginName) + data, err := api.ListPluginsCommunity(instanceID, sleep, timeout) if err != nil { return nil, err } @@ -60,69 +53,116 @@ func (api *API) ReadPluginCommunity(instanceID int, pluginName string) (map[stri return nil, nil } -func (api *API) ReadPluginsCommunity(instanceID int) ([]map[string]interface{}, error) { - // Initiale values, 5 attempts and 20 second sleep - return api.readPluginsCommunityWithRetry(instanceID, 5, 20) +// ListPluginsCommunity: list all community plugins for an instance. +func (api *API) ListPluginsCommunity(instanceID, sleep, timeout int) ([]map[string]interface{}, error) { + return api.listPluginsCommunityWithRetry(instanceID, 1, sleep, timeout) } -func (api *API) readPluginsCommunityWithRetry(instanceID, attempts, sleep int) ([]map[string]interface{}, error) { - var data []map[string]interface{} - failed := make(map[string]interface{}) - log.Printf("[DEBUG] go-api::plugin_community::readPluginsCommunityWithRetry instance id: %v", instanceID) - path := fmt.Sprintf("/api/instances/%d/plugins/community", instanceID) +// listPluginsCommunityWithRetry: list all community plugins for an instance, +// with retry if the backend is busy. +func (api *API) listPluginsCommunityWithRetry(instanceID, attempt, sleep, timeout int) ( + []map[string]interface{}, error) { + + var ( + data []map[string]interface{} + failed map[string]interface{} + path = fmt.Sprintf("/api/instances/%d/plugins/community", instanceID) + ) + + log.Printf("[DEBUG] go-api::plugin_community::listPluginsCommunityWithRetry path: %s", path) response, err := api.sling.New().Get(path).Receive(&data, &failed) if err != nil { return nil, err + } else if attempt*sleep > timeout { + return nil, fmt.Errorf("read plugins reached timeout of %d seconds", timeout) } statusCode := response.StatusCode - log.Printf("[DEBUG] go-api::plugin_community::readPluginsCommunityWithRetry statusCode: %d", statusCode) + log.Printf("[DEBUG] go-api::plugin_community::listPluginsCommunityWithRetry statusCode: %d", statusCode) switch { case statusCode == 400: if strings.Compare(failed["error"].(string), "Timeout talking to backend") == 0 { - if attempts--; attempts > 0 { - log.Printf("[INFO] go-api::plugin_community::readPluginsCommunityWithRetry Timeout talking to backend "+ - "attempts left %d and retry in %d seconds", attempts, sleep) - time.Sleep(time.Duration(sleep) * time.Second) - return api.readPluginsCommunityWithRetry(instanceID, attempts, 2*sleep) - } else { - return nil, fmt.Errorf("ReadWithRetry failed, status: %v, message: %s", response.StatusCode, failed) - } + log.Printf("[INFO] go-api::plugins-community::read Timeout talking to backend "+ + "attempt: %d, until timeout: %d", attempt, (timeout - (attempt * sleep))) + attempt++ + time.Sleep(time.Duration(sleep) * time.Second) + return api.listPluginsCommunityWithRetry(instanceID, attempt, sleep, timeout) } } return data, nil } -func (api *API) UpdatePluginCommunity(instanceID int, params map[string]interface{}) (map[string]interface{}, error) { - failed := make(map[string]interface{}) - pluginParams := &PluginParams{Name: params["name"].(string), Enabled: params["enabled"].(bool)} - log.Printf("[DEBUG] go-api::plugin_community::update instance ID: %v, params: %v", instanceID, params) - path := fmt.Sprintf("/api/instances/%d/plugins/community?async=true", instanceID) - response, err := api.sling.New().Put(path).BodyJSON(pluginParams).Receive(nil, &failed) +// UpdatePluginCommunity: updates a community plugin from an instance. +func (api *API) UpdatePluginCommunity(instanceID int, pluginName string, enabled bool, + sleep, timeout int) (map[string]interface{}, error) { + var ( + failed map[string]interface{} + params = make(map[string]interface{}) + path = fmt.Sprintf("/api/instances/%d/plugins/community?async=true", instanceID) + ) + + params["plugin_name"] = pluginName + params["enabled"] = enabled + log.Printf("[DEBUG] go-api::plugin_community::update path: %s", path) + response, err := api.sling.New().Put(path).BodyJSON(params).Receive(nil, &failed) if err != nil { return nil, err } - if response.StatusCode != 204 { - return nil, fmt.Errorf("UpdatePluginCommunity failed, status: %v, message: %s", response.StatusCode, failed) - } - return api.waitUntilPluginChanged(instanceID, params["name"].(string), params["enabled"].(bool)) + switch response.StatusCode { + case 204: + return api.waitUntilPluginChanged(instanceID, pluginName, enabled, 1, sleep, timeout) + default: + return nil, fmt.Errorf("UpdatePluginCommunity failed, status: %v, message: %s", + response.StatusCode, failed) + } } -func (api *API) DisablePluginCommunity(instanceID int, pluginName string) (map[string]interface{}, error) { - failed := make(map[string]interface{}) - log.Printf("[DEBUG] go-api::plugin_community::disable instance ID: %v, name: %v", instanceID, pluginName) - path := fmt.Sprintf("/api/instances/%d/plugins/community/%s?async=true", instanceID, pluginName) - response, err := api.sling.New().Delete(path).Receive(nil, &failed) +// UninstallPluginCommunity: uninstall a community plugin from an instance. +func (api *API) UninstallPluginCommunity(instanceID int, pluginName string, sleep, timeout int) ( + map[string]interface{}, error) { + + var ( + failed map[string]interface{} + path = fmt.Sprintf("/api/instances/%d/plugins/community/%s?async=true", instanceID, pluginName) + ) + log.Printf("[DEBUG] go-api::plugin_community::disable path: %s", path) + response, err := api.sling.New().Delete(path).Receive(nil, &failed) if err != nil { return nil, err } - if response.StatusCode != 204 { - return nil, fmt.Errorf("DisablePluginCommunity failed, status: %v, message: %s", response.StatusCode, failed) + + switch response.StatusCode { + case 204: + return api.waitUntilPluginUninstalled(instanceID, pluginName, 1, sleep, timeout) + default: + return nil, fmt.Errorf("DisablePluginCommunity failed, status: %v, message: %s", + response.StatusCode, failed) } +} - return api.waitUntilPluginUninstalled(instanceID, pluginName) +// waitUntilPluginUninstalled: wait until a community plugin been uninstalled. +func (api *API) waitUntilPluginUninstalled(instanceID int, pluginName string, + attempt, sleep, timeout int) (map[string]interface{}, error) { + + log.Printf("[DEBUG] go-api::plugin_community::waitUntilPluginUninstalled instance id: %v, name: %v", + instanceID, pluginName) + for { + time.Sleep(time.Duration(sleep) * time.Second) + if attempt*sleep > timeout { + return nil, fmt.Errorf("wait until plugin uninstalled reached timeout of %d seconds", timeout) + } + + response, err := api.ReadPlugin(instanceID, pluginName, sleep, timeout) + if err != nil { + return nil, err + } + if len(response) == 0 { + return response, nil + } + attempt++ + } }