From 96271257e73ce0ded9479d1359b24f309cd6d562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Brod=C3=A9n?= Date: Thu, 23 Nov 2023 14:10:28 +0100 Subject: [PATCH 1/5] Clean up and make plugins sleep/timeout configurable --- api/plugins.go | 137 +++++++++++++++++++++++++++++++------------------ 1 file changed, 86 insertions(+), 51 deletions(-) diff --git a/api/plugins.go b/api/plugins.go index d1c3f98..12c7790 100644 --- a/api/plugins.go +++ b/api/plugins.go @@ -12,11 +12,12 @@ type PluginParams struct { 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) +// waitUntilPluginChanged wait until plugin changed. +func (api *API) waitUntilPluginChanged(instanceID int, pluginName string, enabled bool, + sleep, timeout int) (map[string]interface{}, error) { + for { - time.Sleep(10 * time.Second) - response, err := api.ReadPlugin(instanceID, pluginName) + response, err := api.ReadPlugin(instanceID, pluginName, sleep, timeout) log.Printf("[DEBUG] go-api::plugin::waitUntilPluginChanged response: %v", response) if err != nil { return nil, err @@ -30,26 +31,36 @@ func (api *API) waitUntilPluginChanged(instanceID int, pluginName string, enable } } -func (api *API) EnablePlugin(instanceID int, pluginName string) (map[string]interface{}, error) { - failed := make(map[string]interface{}) - params := &PluginParams{Name: pluginName} +// EnablePlugin enable a plugin on an instance. +func (api *API) EnablePlugin(instanceID int, pluginName string, sleep, timeout int) ( + map[string]interface{}, error) { + + var ( + failed = make(map[string]interface{}) + params = &PluginParams{Name: pluginName} + path = fmt.Sprintf("/api/instances/%d/plugins?async=true", instanceID) + ) + log.Printf("[DEBUG] go-api::plugin::enable instance id: %v, params: %v", instanceID, pluginName) - path := fmt.Sprintf("/api/instances/%d/plugins?async=true", instanceID) 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 nil, + fmt.Errorf("EnablePlugin failed, status: %v, message: %s", response.StatusCode, failed) } - return api.waitUntilPluginChanged(instanceID, pluginName, true) + return api.waitUntilPluginChanged(instanceID, pluginName, true, sleep, timeout) } -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,61 +75,82 @@ 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("ReadWithRetry 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)} +// UpdatePlugin updates a plugin from an instance. +func (api *API) UpdatePlugin(instanceID int, params map[string]interface{}, sleep, timeout int) ( + map[string]interface{}, error) { + + var ( + failed map[string]interface{} + pluginName = params["name"].(string) + enabled = params["enabled"].(bool) + pluginParams = &PluginParams{Name: pluginName, Enabled: enabled} + path = fmt.Sprintf("/api/instances/%d/plugins?async=true", instanceID) + ) + 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) if err != nil { return nil, err } if response.StatusCode != 204 { - return nil, fmt.Errorf("UpdatePlugin failed, status: %v, message: %s", response.StatusCode, failed) + return nil, + fmt.Errorf("UpdatePlugin failed, status: %v, message: %s", response.StatusCode, failed) } - return api.waitUntilPluginChanged(instanceID, params["name"].(string), params["enabled"].(bool)) + return api.waitUntilPluginChanged(instanceID, pluginName, enabled, sleep, timeout) } -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 { @@ -128,15 +160,18 @@ func (api *API) DisablePlugin(instanceID int, pluginName string) (map[string]int return nil, fmt.Errorf("DisablePlugin failed, status: %v, message: %s", response.StatusCode, failed) } - return api.waitUntilPluginChanged(instanceID, pluginName, false) + return api.waitUntilPluginChanged(instanceID, pluginName, false, sleep, timeout) } -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 } @@ -144,6 +179,6 @@ func (api *API) DeletePlugin(instanceID int, pluginName string) error { return fmt.Errorf("DeletePlugin failed, status: %v, message: %s", response.StatusCode, failed) } - _, err = api.waitUntilPluginChanged(instanceID, pluginName, false) + _, err = api.waitUntilPluginChanged(instanceID, pluginName, false, sleep, timeout) return err } From 83e162341f4d8194442d5e493c7fe868b69a0b30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Brod=C3=A9n?= Date: Thu, 23 Nov 2023 14:10:54 +0100 Subject: [PATCH 2/5] Clean up and make community plugins sleep/timeout configurable --- api/plugins_community.go | 124 ++++++++++++++++++++++++--------------- 1 file changed, 78 insertions(+), 46 deletions(-) diff --git a/api/plugins_community.go b/api/plugins_community.go index 18f927b..c34b630 100644 --- a/api/plugins_community.go +++ b/api/plugins_community.go @@ -7,11 +7,14 @@ 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) +// waitUntilPluginUninstalled wait until a community plugin been uninstalled. +func (api *API) waitUntilPluginUninstalled(instanceID int, pluginName string, sleep, timeout int) ( + map[string]interface{}, error) { + + log.Printf("[DEBUG] go-api::plugin_community::waitUntilPluginUninstalled instance id: %v, name: %v", + instanceID, pluginName) for { - response, err := api.ReadPlugin(instanceID, pluginName) + response, err := api.ReadPlugin(instanceID, pluginName, sleep, timeout) if err != nil { return nil, err @@ -19,32 +22,39 @@ func (api *API) waitUntilPluginUninstalled(instanceID int, pluginName string) (m 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) +// InstallPlugin 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 = &PluginParams{Name: pluginName} + path = fmt.Sprintf("/api/instances/%d/plugins/community?async=true", instanceID) + ) + + 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 nil, + fmt.Errorf("EnablePluginCommunity failed, status: %v, message: %v", response.StatusCode, failed) } - return api.waitUntilPluginChanged(instanceID, pluginName, true) + return api.waitUntilPluginChanged(instanceID, pluginName, true, sleep, timeout) } -func (api *API) ReadPluginCommunity(instanceID int, pluginName string) (map[string]interface{}, error) { - var data []map[string]interface{} +// ReadPlugin 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.ReadPluginsCommunity(instanceID) + data, err := api.ListPluginsCommunity(instanceID, sleep, timeout) if err != nil { return nil, err @@ -60,69 +70,91 @@ 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) +// UpdatePluginCommunity updates a community plugin from an instance. +func (api *API) UpdatePluginCommunity(instanceID int, params map[string]interface{}, sleep, timeout int) ( + map[string]interface{}, error) { + + var ( + failed map[string]interface{} + pluginName = params["name"].(string) + enabled = params["enabled"].(bool) + pluginParams = &PluginParams{Name: pluginName, Enabled: enabled} + path = fmt.Sprintf("/api/instances/%d/plugins/community?async=true", instanceID) + ) + + log.Printf("[DEBUG] go-api::plugin_community::update path: %s", path) response, err := api.sling.New().Put(path).BodyJSON(pluginParams).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 nil, + fmt.Errorf("UpdatePluginCommunity failed, status: %v, message: %s", response.StatusCode, failed) } - return api.waitUntilPluginChanged(instanceID, params["name"].(string), params["enabled"].(bool)) + return api.waitUntilPluginChanged(instanceID, pluginName, enabled, sleep, timeout) } -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) +// 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 = make(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) + return nil, + fmt.Errorf("DisablePluginCommunity failed, status: %v, message: %s", response.StatusCode, failed) } - return api.waitUntilPluginUninstalled(instanceID, pluginName) + return api.waitUntilPluginUninstalled(instanceID, pluginName, sleep, timeout) } From 769f17407148c5676fd4452b9c92721f3880c0c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Brod=C3=A9n?= Date: Fri, 24 Nov 2023 14:39:14 +0100 Subject: [PATCH 3/5] Remove PluginParams struct --- api/plugins.go | 29 +++++++++++++---------------- api/plugins_community.go | 20 +++++++++++--------- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/api/plugins.go b/api/plugins.go index 12c7790..1c49083 100644 --- a/api/plugins.go +++ b/api/plugins.go @@ -7,16 +7,12 @@ import ( "time" ) -type PluginParams struct { - Name string `json:"plugin_name,omitempty"` - Enabled bool `json:"enabled,omitempty"` -} - // waitUntilPluginChanged wait until plugin changed. func (api *API) waitUntilPluginChanged(instanceID int, pluginName string, enabled bool, sleep, timeout int) (map[string]interface{}, error) { for { + time.Sleep(time.Duration(sleep) * time.Second) response, err := api.ReadPlugin(instanceID, pluginName, sleep, timeout) log.Printf("[DEBUG] go-api::plugin::waitUntilPluginChanged response: %v", response) if err != nil { @@ -36,12 +32,13 @@ func (api *API) EnablePlugin(instanceID int, pluginName string, sleep, timeout i map[string]interface{}, error) { var ( - failed = make(map[string]interface{}) - params = &PluginParams{Name: pluginName} + failed map[string]interface{} + params = make(map[string]interface{}) path = fmt.Sprintf("/api/instances/%d/plugins?async=true", instanceID) ) - log.Printf("[DEBUG] go-api::plugin::enable instance id: %v, params: %v", instanceID, pluginName) + 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 { @@ -116,19 +113,19 @@ func (api *API) listPluginsWithRetry(instanceID, attempt, sleep, timeout int) ( } // UpdatePlugin updates a plugin from an instance. -func (api *API) UpdatePlugin(instanceID int, params map[string]interface{}, sleep, timeout int) ( +func (api *API) UpdatePlugin(instanceID int, pluginName string, enabled bool, sleep, timeout int) ( map[string]interface{}, error) { var ( - failed map[string]interface{} - pluginName = params["name"].(string) - enabled = params["enabled"].(bool) - pluginParams = &PluginParams{Name: pluginName, Enabled: enabled} - path = fmt.Sprintf("/api/instances/%d/plugins?async=true", instanceID) + failed map[string]interface{} + params = make(map[string]interface{}) + path = fmt.Sprintf("/api/instances/%d/plugins?async=true", instanceID) ) - log.Printf("[DEBUG] go-api::plugin::update instance ID: %v, params: %v", instanceID, pluginParams) - response, err := api.sling.New().Put(path).BodyJSON(pluginParams).Receive(nil, &failed) + 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 diff --git a/api/plugins_community.go b/api/plugins_community.go index c34b630..e578691 100644 --- a/api/plugins_community.go +++ b/api/plugins_community.go @@ -14,6 +14,7 @@ func (api *API) waitUntilPluginUninstalled(instanceID int, pluginName string, sl log.Printf("[DEBUG] go-api::plugin_community::waitUntilPluginUninstalled instance id: %v, name: %v", instanceID, pluginName) for { + time.Sleep(time.Duration(sleep) * time.Second) response, err := api.ReadPlugin(instanceID, pluginName, sleep, timeout) if err != nil { @@ -31,10 +32,11 @@ func (api *API) InstallPluginCommunity(instanceID int, pluginName string, sleep, var ( failed map[string]interface{} - params = &PluginParams{Name: pluginName} + 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) @@ -111,19 +113,19 @@ func (api *API) listPluginsCommunityWithRetry(instanceID, attempt, sleep, timeou } // UpdatePluginCommunity updates a community plugin from an instance. -func (api *API) UpdatePluginCommunity(instanceID int, params map[string]interface{}, sleep, timeout int) ( +func (api *API) UpdatePluginCommunity(instanceID int, pluginName string, enabled bool, sleep, timeout int) ( map[string]interface{}, error) { var ( - failed map[string]interface{} - pluginName = params["name"].(string) - enabled = params["enabled"].(bool) - pluginParams = &PluginParams{Name: pluginName, Enabled: enabled} - path = fmt.Sprintf("/api/instances/%d/plugins/community?async=true", instanceID) + 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(pluginParams).Receive(nil, &failed) + response, err := api.sling.New().Put(path).BodyJSON(params).Receive(nil, &failed) if err != nil { return nil, err @@ -141,7 +143,7 @@ func (api *API) UninstallPluginCommunity(instanceID int, pluginName string, slee map[string]interface{}, error) { var ( - failed = make(map[string]interface{}) + failed map[string]interface{} path = fmt.Sprintf("/api/instances/%d/plugins/community/%s?async=true", instanceID, pluginName) ) From 1db3c50290d29f73f177b5afdfa68ffcbe0631da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Brod=C3=A9n?= Date: Fri, 24 Nov 2023 15:11:39 +0100 Subject: [PATCH 4/5] Update comment --- api/plugins_community.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/plugins_community.go b/api/plugins_community.go index e578691..6ce8df4 100644 --- a/api/plugins_community.go +++ b/api/plugins_community.go @@ -51,7 +51,7 @@ func (api *API) InstallPluginCommunity(instanceID int, pluginName string, sleep, return api.waitUntilPluginChanged(instanceID, pluginName, true, sleep, timeout) } -// ReadPlugin reads a specific community plugin from an instance. +// ReadPluginCommunity reads a specific community plugin from an instance. func (api *API) ReadPluginCommunity(instanceID int, pluginName string, sleep, timeout int) ( map[string]interface{}, error) { From d520ddfef60115c2f4e701f73053e64a1979a9fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Brod=C3=A9n?= Date: Tue, 12 Dec 2023 09:26:36 +0100 Subject: [PATCH 5/5] Update with switch/case for response status code --- api/plugins.go | 104 ++++++++++++++++++++++----------------- api/plugins_community.go | 96 +++++++++++++++++++----------------- 2 files changed, 111 insertions(+), 89 deletions(-) diff --git a/api/plugins.go b/api/plugins.go index 1c49083..7f6e41c 100644 --- a/api/plugins.go +++ b/api/plugins.go @@ -7,27 +7,7 @@ import ( "time" ) -// waitUntilPluginChanged wait until plugin changed. -func (api *API) waitUntilPluginChanged(instanceID int, pluginName string, enabled bool, - sleep, timeout int) (map[string]interface{}, error) { - - for { - time.Sleep(time.Duration(sleep) * time.Second) - 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 - } - } -} - -// EnablePlugin enable a plugin on an instance. +// EnablePlugin: enable a plugin on an instance. func (api *API) EnablePlugin(instanceID int, pluginName string, sleep, timeout int) ( map[string]interface{}, error) { @@ -44,15 +24,17 @@ func (api *API) EnablePlugin(instanceID int, pluginName string, sleep, timeout i if err != nil { return nil, err } - if response.StatusCode != 204 { + + switch response.StatusCode { + case 204: + return api.waitUntilPluginChanged(instanceID, pluginName, true, 1, sleep, timeout) + default: return nil, - fmt.Errorf("EnablePlugin failed, status: %v, message: %s", response.StatusCode, failed) + fmt.Errorf("enable plugin failed, status: %v, message: %s", response.StatusCode, failed) } - - return api.waitUntilPluginChanged(instanceID, pluginName, true, sleep, timeout) } -// ReadPlugin reads a specific plugin from an instance. +// ReadPlugin: reads a specific plugin from an instance. func (api *API) ReadPlugin(instanceID int, pluginName string, sleep, timeout int) ( map[string]interface{}, error) { @@ -72,12 +54,12 @@ func (api *API) ReadPlugin(instanceID int, pluginName string, sleep, timeout int return nil, nil } -// ListPlugins list plugins from an instance. +// 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) } -// listPluginsWithRetry list plugins from an instance, with retry if backend is busy. +// 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) { @@ -108,11 +90,12 @@ func (api *API) listPluginsWithRetry(instanceID, attempt, sleep, timeout int) ( return nil, fmt.Errorf("ReadWithRetry failed, status: %v, message: %s", 400, failed) default: return nil, - fmt.Errorf("ReadWithRetry failed, status: %v, message: %s", response.StatusCode, failed) + fmt.Errorf("list plugin with retry failed, status: %v, message: %s", + response.StatusCode, failed) } } -// UpdatePlugin updates a plugin from an instance. +// UpdatePlugin: updates a plugin from an instance. func (api *API) UpdatePlugin(instanceID int, pluginName string, enabled bool, sleep, timeout int) ( map[string]interface{}, error) { @@ -130,15 +113,17 @@ func (api *API) UpdatePlugin(instanceID int, pluginName string, enabled bool, sl if err != nil { return nil, err } - if response.StatusCode != 204 { + + switch response.StatusCode { + case 204: + return api.waitUntilPluginChanged(instanceID, pluginName, enabled, 1, sleep, timeout) + default: return nil, - fmt.Errorf("UpdatePlugin failed, status: %v, message: %s", response.StatusCode, failed) + fmt.Errorf("update plugin failed, status: %v, message: %s", response.StatusCode, failed) } - - return api.waitUntilPluginChanged(instanceID, pluginName, enabled, sleep, timeout) } -// DisablePlugin disables a plugin from an instance. +// DisablePlugin: disables a plugin from an instance. func (api *API) DisablePlugin(instanceID int, pluginName string, sleep, timeout int) ( map[string]interface{}, error) { @@ -153,14 +138,17 @@ func (api *API) DisablePlugin(instanceID int, pluginName string, sleep, timeout 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, sleep, timeout) + 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) + } } -// DeletePlugin deletes a plugin from an instance. +// DeletePlugin: deletes a plugin from an instance. func (api *API) DeletePlugin(instanceID int, pluginName string, sleep, timeout int) error { var ( failed map[string]interface{} @@ -172,10 +160,38 @@ func (api *API) DeletePlugin(instanceID int, pluginName string, sleep, timeout i 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) } +} + +// 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) + } - _, err = api.waitUntilPluginChanged(instanceID, pluginName, false, sleep, timeout) - return err + 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 6ce8df4..3e914c4 100644 --- a/api/plugins_community.go +++ b/api/plugins_community.go @@ -7,26 +7,7 @@ import ( "time" ) -// waitUntilPluginUninstalled wait until a community plugin been uninstalled. -func (api *API) waitUntilPluginUninstalled(instanceID int, pluginName string, 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) - response, err := api.ReadPlugin(instanceID, pluginName, sleep, timeout) - - if err != nil { - return nil, err - } - if len(response) == 0 { - return response, nil - } - } -} - -// InstallPlugin install a community plugin on an instance. +// InstallPluginCommunity: install a community plugin on an instance. func (api *API) InstallPluginCommunity(instanceID int, pluginName string, sleep, timeout int) ( map[string]interface{}, error) { @@ -39,25 +20,25 @@ func (api *API) InstallPluginCommunity(instanceID int, pluginName string, sleep, 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: %v", response.StatusCode, failed) - } - return api.waitUntilPluginChanged(instanceID, pluginName, true, sleep, timeout) + 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) + } } -// ReadPluginCommunity reads a specific community plugin from an instance. +// 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 } @@ -72,12 +53,12 @@ func (api *API) ReadPluginCommunity(instanceID int, pluginName string, sleep, ti return nil, nil } -// ListPluginsCommunity list all community plugins for an instance. +// 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) } -// listPluginsCommunityWithRetry list all community plugins for an instance, +// 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) { @@ -112,9 +93,9 @@ func (api *API) listPluginsCommunityWithRetry(instanceID, attempt, sleep, timeou return data, nil } -// 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) { +// 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{} @@ -126,19 +107,20 @@ func (api *API) UpdatePluginCommunity(instanceID int, pluginName string, enabled 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, pluginName, enabled, sleep, timeout) + 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) + } } -// UninstallPluginCommunity uninstall a community plugin from an instance. +// UninstallPluginCommunity: uninstall a community plugin from an instance. func (api *API) UninstallPluginCommunity(instanceID int, pluginName string, sleep, timeout int) ( map[string]interface{}, error) { @@ -149,14 +131,38 @@ func (api *API) UninstallPluginCommunity(instanceID int, pluginName string, slee 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, sleep, timeout) +// 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++ + } }