Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added task support for install license API #1165

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib-utilities/common/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ const (
CreateRemoteAccountService = "CreateRemoteAccountService"
UpdateRemoteAccountService = "UpdateRemoteAccountService"
DeleteRemoteAccountService = "DeleteRemoteAccountService"

InstallLicenseService = "InstallLicenseService"
// constants for log
SessionToken = "sessiontoken"
SessionUserID = "sessionuserid"
Expand Down
73 changes: 66 additions & 7 deletions svc-licenses/lcommon/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,20 @@ import (
"fmt"
"io/ioutil"
"net/http"
"runtime"
"strconv"
"strings"
"time"

"github.com/ODIM-Project/ODIM/lib-persistence-manager/persistencemgr"
"github.com/ODIM-Project/ODIM/lib-utilities/common"
"github.com/ODIM-Project/ODIM/lib-utilities/config"
"github.com/ODIM-Project/ODIM/lib-utilities/errors"
"github.com/ODIM-Project/ODIM/lib-utilities/logs"
l "github.com/ODIM-Project/ODIM/lib-utilities/logs"
taskproto "github.com/ODIM-Project/ODIM/lib-utilities/proto/task"
"github.com/ODIM-Project/ODIM/lib-utilities/response"
"github.com/ODIM-Project/ODIM/lib-utilities/services"
"github.com/ODIM-Project/ODIM/svc-licenses/model"
)

Expand All @@ -37,6 +42,13 @@ var (
ConfigFilePath string
)

// PluginTaskInfo hold the task information from plugin
type PluginTaskInfo struct {
Location string
PluginIP string
PluginServerName string
}

// GetAllKeysFromTable fetches all keys in a given table
func GetAllKeysFromTable(table string, dbtype persistencemgr.DbType) ([]string, error) {
conn, err := persistencemgr.GetDBConnection(dbtype)
Expand Down Expand Up @@ -112,8 +124,11 @@ func GetPluginData(pluginID string) (*model.Plugin, *errors.Error) {
}

// ContactPlugin is commons which handles the request and response of Contact Plugin usage
func ContactPlugin(ctx context.Context, req model.PluginContactRequest, errorMessage string) ([]byte, string, model.ResponseStatus, error) {
func ContactPlugin(ctx context.Context, req model.PluginContactRequest,
errorMessage string) ([]byte, string, PluginTaskInfo, model.ResponseStatus, error) {

var resp model.ResponseStatus
var pluginTaskInfo PluginTaskInfo
var err error
pluginResponse, err := callPlugin(ctx, req)
if err != nil {
Expand All @@ -125,41 +140,52 @@ func ContactPlugin(ctx context.Context, req model.PluginContactRequest, errorMes
resp.StatusCode = http.StatusServiceUnavailable
resp.StatusMessage = response.CouldNotEstablishConnection
resp.MsgArgs = []interface{}{"https://" + req.Plugin.IP + ":" + req.Plugin.Port + req.OID}
return nil, "", resp, fmt.Errorf(errorMessage)
return nil, "", pluginTaskInfo, resp, fmt.Errorf(errorMessage)
}
}
defer pluginResponse.Body.Close()

body, err := ioutil.ReadAll(pluginResponse.Body)
if err != nil {
errorMessage := "error while trying to read response body: " + err.Error()
resp.StatusCode = http.StatusInternalServerError
resp.StatusMessage = errors.InternalError
l.LogWithFields(ctx).Warn(errorMessage)
return nil, "", resp, fmt.Errorf(errorMessage)
return nil, "", pluginTaskInfo, resp, fmt.Errorf(errorMessage)
}

if pluginResponse.StatusCode != http.StatusCreated && pluginResponse.StatusCode != http.StatusOK {
if pluginResponse.StatusCode == http.StatusAccepted {
pluginTaskInfo.Location = pluginResponse.Header.Get("Location")
pluginTaskInfo.PluginIP = pluginResponse.Header.Get(common.XForwardedFor)
}

if pluginResponse.StatusCode != http.StatusCreated &&
pluginResponse.StatusCode != http.StatusOK &&
pluginResponse.StatusCode != http.StatusAccepted {
if pluginResponse.StatusCode == http.StatusUnauthorized {
errorMessage += "error: invalid resource username/password"
resp.StatusCode = int32(pluginResponse.StatusCode)
resp.StatusMessage = response.ResourceAtURIUnauthorized
resp.MsgArgs = []interface{}{"https://" + req.Plugin.IP + ":" + req.Plugin.Port + req.OID}
l.LogWithFields(ctx).Warn(errorMessage)
return nil, "", resp, fmt.Errorf(errorMessage)
return nil, "", pluginTaskInfo, resp, fmt.Errorf(errorMessage)
}
errorMessage += string(body)
resp.StatusCode = int32(pluginResponse.StatusCode)
resp.StatusMessage = response.InternalError
l.LogWithFields(ctx).Warn(errorMessage)
return body, "", resp, fmt.Errorf(errorMessage)
return body, "", pluginTaskInfo, resp, fmt.Errorf(errorMessage)
}

resp.StatusCode = int32(pluginResponse.StatusCode)
resp.StatusMessage = response.Success

data := string(body)
//replacing the resposne with north bound translation URL
for key, value := range config.Data.URLTranslation.NorthBoundURL {
data = strings.Replace(data, key, value, -1)
}
return []byte(data), pluginResponse.Header.Get("X-Auth-Token"), resp, nil
return []byte(data), pluginResponse.Header.Get("X-Auth-Token"), pluginTaskInfo, resp, nil
}

// getPluginStatus checks the status of given plugin in configured interval
Expand Down Expand Up @@ -215,6 +241,7 @@ func GenericSave(ctx context.Context, body []byte, table string, key string) err
return nil
}

// GetIDsFromURI will return the manager ID from server URI
func GetIDsFromURI(uri string) (string, string, error) {
lastChar := uri[len(uri)-1:]
if lastChar == "/" {
Expand All @@ -228,6 +255,7 @@ func GetIDsFromURI(uri string) (string, string, error) {
return ids[0], ids[1], nil
}

// TrackConfigFileChanges monitors the config changes using fsnotfiy
func TrackConfigFileChanges(errChan chan error) {
eventChan := make(chan interface{})
format := config.Data.LogFormat
Expand All @@ -250,3 +278,34 @@ func TrackConfigFileChanges(errChan chan error) {
}
}
}

// UpdateTask update the task with the given data
func UpdateTask(ctx context.Context, taskData common.TaskData) error {
var res map[string]interface{}
if err := json.Unmarshal([]byte(taskData.TaskRequest), &res); err != nil {
l.Log.Error(err)
}
reqStr := logs.MaskRequestBody(res)

respBody, _ := json.Marshal(taskData.Response.Body)
payLoad := &taskproto.Payload{
HTTPHeaders: taskData.Response.Header,
HTTPOperation: taskData.HTTPMethod,
JSONBody: reqStr,
StatusCode: taskData.Response.StatusCode,
TargetURI: taskData.TargetURI,
ResponseBody: respBody,
}

err := services.UpdateTask(ctx, taskData.TaskID, taskData.TaskState, taskData.TaskStatus, taskData.PercentComplete, payLoad, time.Now())
if err != nil && (err.Error() == common.Cancelling) {
// We cant do anything here as the task has done it work completely, we cant reverse it.
//Unless if we can do opposite/reverse action for delete server which is add server.
services.UpdateTask(ctx, taskData.TaskID, common.Cancelled, taskData.TaskStatus, taskData.PercentComplete, payLoad, time.Now())
if taskData.PercentComplete == 0 {
return fmt.Errorf("error while starting the task: %v", err)
}
runtime.Goexit()
}
return nil
}
30 changes: 25 additions & 5 deletions svc-licenses/licenses/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,17 @@ type ExternalInterface struct {

// External struct holds the function pointers all outboud services
type External struct {
ContactClient func(context.Context, string, string, string, string, interface{}, map[string]string) (*http.Response, error)
Auth func(string, []string, []string) (response.RPC, error)
DevicePassword func([]byte) ([]byte, error)
GetPluginData func(string) (*model.Plugin, *errors.Error)
ContactPlugin func(context.Context, model.PluginContactRequest, string) ([]byte, string, model.ResponseStatus, error)
ContactClient func(context.Context, string, string, string, string, interface{}, map[string]string) (*http.Response, error)
Auth func(string, []string, []string) (response.RPC, error)
DevicePassword func([]byte) ([]byte, error)
GetPluginData func(string) (*model.Plugin, *errors.Error)
ContactPlugin func(context.Context, model.PluginContactRequest, string) ([]byte, string,
lcommon.PluginTaskInfo, model.ResponseStatus, error)
GetTarget func(string) (*model.Target, *errors.Error)
GetSessionUserName func(string) (string, error)
CreateTask func(ctx context.Context, sessionUserName string) (string, error)
CreateChildTask func(context.Context, string, string) (string, error)
UpdateTask func(context.Context, common.TaskData) error
GenericSave func(context.Context, []byte, string, string) error
}

Expand All @@ -63,6 +67,9 @@ func GetExternalInterface() *ExternalInterface {
ContactPlugin: lcommon.ContactPlugin,
GetTarget: lcommon.GetTarget,
GetSessionUserName: services.GetSessionUserName,
CreateTask: services.CreateTask,
CreateChildTask: services.CreateChildTask,
UpdateTask: lcommon.UpdateTask,
GenericSave: lcommon.GenericSave,
},
DB: DB{
Expand All @@ -71,3 +78,16 @@ func GetExternalInterface() *ExternalInterface {
},
}
}

func fillTaskData(taskID, targetURI, request string, resp response.RPC, taskState string, taskStatus string, percentComplete int32, httpMethod string) common.TaskData {
return common.TaskData{
TaskID: taskID,
TargetURI: targetURI,
TaskRequest: request,
Response: resp,
TaskState: taskState,
TaskStatus: taskStatus,
PercentComplete: percentComplete,
HTTPMethod: httpMethod,
}
}
44 changes: 35 additions & 9 deletions svc-licenses/licenses/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"github.com/ODIM-Project/ODIM/lib-utilities/common"
"github.com/ODIM-Project/ODIM/lib-utilities/errors"
"github.com/ODIM-Project/ODIM/lib-utilities/response"
lcommon "github.com/ODIM-Project/ODIM/svc-licenses/lcommon"
"github.com/ODIM-Project/ODIM/svc-licenses/model"
)

Expand All @@ -50,13 +51,16 @@ func TestGetExternalInterface(t *testing.T) {
func mockGetExternalInterface() *ExternalInterface {
return &ExternalInterface{
External: External{
Auth: mockIsAuthorized,
ContactClient: mockContactClient,
GetTarget: mockGetTarget,
GetPluginData: mockGetPluginData,
ContactPlugin: mockContactPlugin,
DevicePassword: stubDevicePassword,
GenericSave: stubGenericSave,
Auth: mockIsAuthorized,
ContactClient: mockContactClient,
GetTarget: mockGetTarget,
GetPluginData: mockGetPluginData,
ContactPlugin: mockContactPlugin,
DevicePassword: stubDevicePassword,
CreateTask: mockCreateTask,
CreateChildTask: mockCreateChildTask,
UpdateTask: mockUpdateTask,
GenericSave: stubGenericSave,
},
DB: DB{
GetAllKeysFromTable: mockGetAllKeysFromTable,
Expand All @@ -65,10 +69,32 @@ func mockGetExternalInterface() *ExternalInterface {
}
}

func mockContactPlugin(ctx context.Context, req model.PluginContactRequest, errorMessage string) ([]byte, string, model.ResponseStatus, error) {
func mockCreateTask(ctx context.Context, sessionID string) (string, error) {
return "task12345", nil
}

func mockCreateChildTask(ctx context.Context, sessionID, taskID string) (string, error) {
switch taskID {
case "taskWithoutChild":
return "", fmt.Errorf("subtask cannot created")
case "subTaskWithSlash":
return "someSubTaskID/", nil
default:
return "someSubTaskID", nil
}
}

func mockUpdateTask(ctx context.Context, task common.TaskData) error {
if task.TaskID == "invalid" {
return fmt.Errorf("task with this ID not found")
}
return nil
}

func mockContactPlugin(ctx context.Context, req model.PluginContactRequest, errorMessage string) ([]byte, string, lcommon.PluginTaskInfo, model.ResponseStatus, error) {
var responseStatus model.ResponseStatus

return []byte(`{"Attributes":"sample"}`), "token", responseStatus, nil
return []byte(`{"Attributes":"sample"}`), "token", lcommon.PluginTaskInfo{}, responseStatus, nil
}

func stubGenericSave(ctx context.Context, reqBody []byte, table string, uuid string) error {
Expand Down
Loading