From 54a239327187c0ea0537ddbf4ff331217e26e64f Mon Sep 17 00:00:00 2001 From: Yunkon Kim Date: Wed, 11 Sep 2024 13:23:44 +0900 Subject: [PATCH 1/3] Handle the missing vNet/subnets by the unexpected outside operations * Add RefineVNet() and RefineSubent(), which operates based on information managed by Tumblebug * Add actions (refine, force) on the following APIs - `DELETE /ns/{nsId}/resources/vNet/{vNetId}?action=xxx` - `DELETE /ns/{nsId}/resources/vNet/{vNetId}/subnet/{subnetId}?action=xxx` * Add the API to get subnet - `GET /ns/{nsId}/resources/vNet/{vNetId}/subnet/{subnetId}` * Improve comments in overall vNet/subnet functions and handlers --- src/api/rest/docs/docs.go | 35 +- src/api/rest/docs/swagger.json | 35 +- src/api/rest/docs/swagger.yaml | 124 ++++--- src/api/rest/server/resource/subnet.go | 47 ++- src/api/rest/server/resource/vnet.go | 47 ++- src/core/resource/subnet.go | 388 +++++++++++++++++++--- src/core/resource/vnet.go | 437 ++++++++++++++++++++----- 7 files changed, 886 insertions(+), 227 deletions(-) diff --git a/src/api/rest/docs/docs.go b/src/api/rest/docs/docs.go index aeea23553..9fd1a6901 100644 --- a/src/api/rest/docs/docs.go +++ b/src/api/rest/docs/docs.go @@ -8155,7 +8155,7 @@ const docTemplate = `{ } }, "delete": { - "description": "Delete VNet", + "description": "Delete VNet\n- withsubnets: delete VNet and its subnets\n- refine: delete information of VNet and its subnets if there's no info/resource in Spider/CSP\n- force: delete VNet and its subnets regardless of the status of info/resource in Spider/CSP", "consumes": [ "application/json" ], @@ -8165,7 +8165,7 @@ const docTemplate = `{ "tags": [ "[Infra Resource] Network Management" ], - "summary": "Delete VNet", + "summary": "Delete VNet (supporting actions: withsubnet, refine, force)", "operationId": "DelVNet", "parameters": [ { @@ -8185,12 +8185,13 @@ const docTemplate = `{ }, { "enum": [ - "true", - "false" + "withsubnets", + "refine", + "force" ], "type": "string", - "description": "Delete subnets as well", - "name": "withSubnets", + "description": "Action", + "name": "action", "in": "query" } ], @@ -8212,7 +8213,7 @@ const docTemplate = `{ }, "/ns/{nsId}/resources/vNet/{vNetId}/subnet": { "get": { - "description": "List all subnets (metadata)", + "description": "List all subnets", "consumes": [ "application/json" ], @@ -8222,7 +8223,7 @@ const docTemplate = `{ "tags": [ "[Infra Resource] Network Management" ], - "summary": "List all subnets (metadata)", + "summary": "List all subnets", "operationId": "GetAllSubnet", "parameters": [ { @@ -8325,7 +8326,7 @@ const docTemplate = `{ }, "/ns/{nsId}/resources/vNet/{vNetId}/subnet/{subnetId}": { "get": { - "description": "Get Subnet (metadata)", + "description": "Get Subnet", "consumes": [ "application/json" ], @@ -8335,7 +8336,7 @@ const docTemplate = `{ "tags": [ "[Infra Resource] Network Management" ], - "summary": "Get Subnet (metadata)", + "summary": "Get Subnet", "operationId": "GetSubnet", "parameters": [ { @@ -8383,7 +8384,7 @@ const docTemplate = `{ } }, "delete": { - "description": "Delete Subnet", + "description": "Delete Subnet\n- refine: delete information of subnet if there's no info/resource in Spider/CSP\n- force: delete subnet regardless of the status of info/resource in Spider/CSP", "consumes": [ "application/json" ], @@ -8393,7 +8394,7 @@ const docTemplate = `{ "tags": [ "[Infra Resource] Network Management" ], - "summary": "Delete Subnet", + "summary": "Delete Subnet (supporting actions: refine, force)", "operationId": "DelSubnet", "parameters": [ { @@ -8417,6 +8418,16 @@ const docTemplate = `{ "name": "subnetId", "in": "path", "required": true + }, + { + "enum": [ + "refine", + "force" + ], + "type": "string", + "description": "Action", + "name": "action", + "in": "query" } ], "responses": { diff --git a/src/api/rest/docs/swagger.json b/src/api/rest/docs/swagger.json index 6a2159379..0f5e9f8fd 100644 --- a/src/api/rest/docs/swagger.json +++ b/src/api/rest/docs/swagger.json @@ -8148,7 +8148,7 @@ } }, "delete": { - "description": "Delete VNet", + "description": "Delete VNet\n- withsubnets: delete VNet and its subnets\n- refine: delete information of VNet and its subnets if there's no info/resource in Spider/CSP\n- force: delete VNet and its subnets regardless of the status of info/resource in Spider/CSP", "consumes": [ "application/json" ], @@ -8158,7 +8158,7 @@ "tags": [ "[Infra Resource] Network Management" ], - "summary": "Delete VNet", + "summary": "Delete VNet (supporting actions: withsubnet, refine, force)", "operationId": "DelVNet", "parameters": [ { @@ -8178,12 +8178,13 @@ }, { "enum": [ - "true", - "false" + "withsubnets", + "refine", + "force" ], "type": "string", - "description": "Delete subnets as well", - "name": "withSubnets", + "description": "Action", + "name": "action", "in": "query" } ], @@ -8205,7 +8206,7 @@ }, "/ns/{nsId}/resources/vNet/{vNetId}/subnet": { "get": { - "description": "List all subnets (metadata)", + "description": "List all subnets", "consumes": [ "application/json" ], @@ -8215,7 +8216,7 @@ "tags": [ "[Infra Resource] Network Management" ], - "summary": "List all subnets (metadata)", + "summary": "List all subnets", "operationId": "GetAllSubnet", "parameters": [ { @@ -8318,7 +8319,7 @@ }, "/ns/{nsId}/resources/vNet/{vNetId}/subnet/{subnetId}": { "get": { - "description": "Get Subnet (metadata)", + "description": "Get Subnet", "consumes": [ "application/json" ], @@ -8328,7 +8329,7 @@ "tags": [ "[Infra Resource] Network Management" ], - "summary": "Get Subnet (metadata)", + "summary": "Get Subnet", "operationId": "GetSubnet", "parameters": [ { @@ -8376,7 +8377,7 @@ } }, "delete": { - "description": "Delete Subnet", + "description": "Delete Subnet\n- refine: delete information of subnet if there's no info/resource in Spider/CSP\n- force: delete subnet regardless of the status of info/resource in Spider/CSP", "consumes": [ "application/json" ], @@ -8386,7 +8387,7 @@ "tags": [ "[Infra Resource] Network Management" ], - "summary": "Delete Subnet", + "summary": "Delete Subnet (supporting actions: refine, force)", "operationId": "DelSubnet", "parameters": [ { @@ -8410,6 +8411,16 @@ "name": "subnetId", "in": "path", "required": true + }, + { + "enum": [ + "refine", + "force" + ], + "type": "string", + "description": "Action", + "name": "action", + "in": "query" } ], "responses": { diff --git a/src/api/rest/docs/swagger.yaml b/src/api/rest/docs/swagger.yaml index f43533d60..4a54bbbbe 100644 --- a/src/api/rest/docs/swagger.yaml +++ b/src/api/rest/docs/swagger.yaml @@ -6093,23 +6093,38 @@ paths: x-codegen-request-body-name: vNetReq delete: tags: - - "[Infra Resource] Network Management" - summary: Delete all VNets - description: Delete all VNets - operationId: DelAllVNet + - '[Infra Resource] Network Management' + /ns/{nsId}/resources/vNet/{vNetId}: + delete: + consumes: + - application/json + description: |- + Delete VNet + - withsubnets: delete VNet and its subnets + - refine: delete information of VNet and its subnets if there's no info/resource in Spider/CSP + - force: delete VNet and its subnets regardless of the status of info/resource in Spider/CSP + operationId: DelVNet parameters: - name: nsId in: path description: Namespace ID required: true - schema: - type: string - default: default - - name: match + type: string + - description: VNet ID + in: path + name: vNetId + required: true + type: string + - description: Action + enum: + - withsubnets + - refine + - force in: query - description: Delete resources containing matched ID-substring only - schema: - type: string + name: action + type: string + produces: + - application/json responses: "200": description: OK @@ -6119,12 +6134,9 @@ paths: $ref: '#/components/schemas/model.IdList' "404": description: Not Found - content: - application/json: - schema: - $ref: '#/components/schemas/model.SimpleMsg' - /ns/{nsId}/resources/vNet/{vNetId}: - get: + schema: + $ref: '#/definitions/model.SimpleMsg' + summary: 'Delete VNet (supporting actions: withsubnet, refine, force)' tags: - "[Infra Resource] Network Management" summary: Get VNet @@ -6206,10 +6218,9 @@ paths: $ref: '#/components/schemas/model.SimpleMsg' /ns/{nsId}/resources/vNet/{vNetId}/subnet: get: - tags: - - "[Infra Resource] Network Management" - summary: List all subnets (metadata) - description: List all subnets (metadata) + consumes: + - application/json + description: List all subnets operationId: GetAllSubnet parameters: - name: nsId @@ -6240,11 +6251,9 @@ paths: $ref: '#/components/schemas/model.SimpleMsg' "500": description: Internal Server Error - content: - application/json: - schema: - $ref: '#/components/schemas/model.SimpleMsg' - post: + schema: + $ref: '#/definitions/model.SimpleMsg' + summary: List all subnets tags: - "[Infra Resource] Network Management" summary: Create Subnet @@ -6294,10 +6303,16 @@ paths: /ns/{nsId}/resources/vNet/{vNetId}/subnet/{subnetId}: get: tags: - - "[Infra Resource] Network Management" - summary: Get Subnet (metadata) - description: Get Subnet (metadata) - operationId: GetSubnet + - '[Infra Resource] Network Management' + /ns/{nsId}/resources/vNet/{vNetId}/subnet/{subnetId}: + delete: + consumes: + - application/json + description: |- + Delete Subnet + - refine: delete information of subnet if there's no info/resource in Spider/CSP + - force: delete subnet regardless of the status of info/resource in Spider/CSP + operationId: DelSubnet parameters: - name: nsId in: path @@ -6316,8 +6331,16 @@ paths: in: path description: Subnet ID required: true - schema: - type: string + type: string + - description: Action + enum: + - refine + - force + in: query + name: action + type: string + produces: + - application/json responses: "200": description: OK @@ -6327,22 +6350,16 @@ paths: $ref: '#/components/schemas/model.TbSubnetInfo' "404": description: Not Found - content: - application/json: - schema: - $ref: '#/components/schemas/model.SimpleMsg' - "500": - description: Internal Server Error - content: - application/json: - schema: - $ref: '#/components/schemas/model.SimpleMsg' - delete: + schema: + $ref: '#/definitions/model.SimpleMsg' + summary: 'Delete Subnet (supporting actions: refine, force)' tags: - - "[Infra Resource] Network Management" - summary: Delete Subnet - description: Delete Subnet - operationId: DelSubnet + - '[Infra Resource] Network Management' + get: + consumes: + - application/json + description: Get Subnet + operationId: GetSubnet parameters: - name: nsId in: path @@ -6372,10 +6389,15 @@ paths: $ref: '#/components/schemas/model.SimpleMsg' "404": description: Not Found - content: - application/json: - schema: - $ref: '#/components/schemas/model.SimpleMsg' + schema: + $ref: '#/definitions/model.SimpleMsg' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/model.SimpleMsg' + summary: Get Subnet + tags: + - '[Infra Resource] Network Management' /ns/{nsId}/sharedResource: post: tags: diff --git a/src/api/rest/server/resource/subnet.go b/src/api/rest/server/resource/subnet.go index 29760a9f3..9393c8b10 100644 --- a/src/api/rest/server/resource/subnet.go +++ b/src/api/rest/server/resource/subnet.go @@ -75,8 +75,8 @@ func RestPostSubnet(c echo.Context) error { // RestGetSubnet godoc // @ID GetSubnet -// @Summary Get Subnet (metadata) -// @Description Get Subnet (metadata) +// @Summary Get Subnet +// @Description Get Subnet // @Tags [Infra Resource] Network Management // @Accept json // @Produce json @@ -128,8 +128,8 @@ type RestGetAllSubnetResponse struct { // RestGetListSubnet godoc // @ID GetAllSubnet -// @Summary List all subnets (metadata) -// @Description List all subnets (metadata) +// @Summary List all subnets +// @Description List all subnets // @Tags [Infra Resource] Network Management // @Accept json // @Produce json @@ -199,14 +199,17 @@ type RestGetAllSubnetResponse struct { // RestDelSubnet godoc // @ID DelSubnet -// @Summary Delete Subnet +// @Summary Delete Subnet (supporting actions: refine, force) // @Description Delete Subnet +// @Description - refine: delete information of subnet if there's no info/resource in Spider/CSP +// @Description - force: delete subnet regardless of the status of info/resource in Spider/CSP // @Tags [Infra Resource] Network Management // @Accept json // @Produce json // @Param nsId path string true "Namespace ID" default(default) // @Param vNetId path string true "VNet ID" // @Param subnetId path string true "Subnet ID" +// @Param action query string false "Action" Enums(refine, force) // @Success 200 {object} model.SimpleMsg // @Failure 404 {object} model.SimpleMsg // @Router /ns/{nsId}/resources/vNet/{vNetId}/subnet/{subnetId} [delete] @@ -234,11 +237,35 @@ func RestDelSubnet(c echo.Context) error { return c.JSON(http.StatusBadRequest, model.SimpleMsg{Message: errMsg.Error()}) } - // [Process] - resp, err := resource.DeleteSubnet(nsId, vNetId, subnetId) - if err != nil { - log.Error().Err(err).Msg("") - return c.JSON(http.StatusInternalServerError, model.SimpleMsg{Message: err.Error()}) + paramAction := c.QueryParam("action") + action, vaild := resource.ParseNetworkAction(paramAction) + if !vaild { + errMsg := fmt.Errorf("invalid action (%s)", action) + log.Warn().Err(errMsg).Msgf(errMsg.Error()) + return c.JSON(http.StatusBadRequest, model.SimpleMsg{Message: errMsg.Error()}) + } + + var resp model.SimpleMsg + var err error + switch action { + case resource.ActionNone, resource.ActionForce: + // [Process] + resp, err = resource.DeleteSubnet(nsId, vNetId, subnetId, action.String()) + if err != nil { + log.Error().Err(err).Msg("") + return c.JSON(http.StatusInternalServerError, model.SimpleMsg{Message: err.Error()}) + } + case resource.ActionRefine: + // [Process] + resp, err = resource.RefineSubnet(nsId, vNetId, subnetId) + if err != nil { + log.Error().Err(err).Msg("") + return c.JSON(http.StatusInternalServerError, model.SimpleMsg{Message: err.Error()}) + } + default: + errMsg := fmt.Errorf("invalid action (%s)", action) + log.Warn().Err(errMsg).Msgf(errMsg.Error()) + return c.JSON(http.StatusBadRequest, model.SimpleMsg{Message: errMsg.Error()}) } // [Output] diff --git a/src/api/rest/server/resource/vnet.go b/src/api/rest/server/resource/vnet.go index 5f92b722c..dcd90d29d 100644 --- a/src/api/rest/server/resource/vnet.go +++ b/src/api/rest/server/resource/vnet.go @@ -164,14 +164,17 @@ func RestGetAllVNet(c echo.Context) error { // RestDelVNet godoc // @ID DelVNet -// @Summary Delete VNet +// @Summary Delete VNet (supporting actions: withsubnet, refine, force) // @Description Delete VNet +// @Description - withsubnets: delete VNet and its subnets +// @Description - refine: delete information of VNet and its subnets if there's no info/resource in Spider/CSP +// @Description - force: delete VNet and its subnets regardless of the status of info/resource in Spider/CSP // @Tags [Infra Resource] Network Management // @Accept json // @Produce json // @Param nsId path string true "Namespace ID" default(default) // @Param vNetId path string true "VNet ID" -// @Param withSubnets query string false "Delete subnets as well" Enums(true,false) +// @Param action query string false "Action" Enums(withsubnets,refine,force) // @Success 200 {object} model.SimpleMsg // @Failure 404 {object} model.SimpleMsg // @Router /ns/{nsId}/resources/vNet/{vNetId} [delete] @@ -192,25 +195,41 @@ func RestDelVNet(c echo.Context) error { return c.JSON(http.StatusBadRequest, model.SimpleMsg{Message: errMsg.Error()}) } - withSubnets := c.QueryParam("withSubnets") - if withSubnets != "" && withSubnets != "true" && withSubnets != "false" { - errMsg := fmt.Errorf("invalid option, withSubnets (%s)", withSubnets) + actionParam := c.QueryParam("action") + action, valid := resource.ParseNetworkAction(actionParam) + if !valid { + errMsg := fmt.Errorf("invalid action (%s)", action) log.Warn().Msgf(errMsg.Error()) return c.JSON(http.StatusBadRequest, model.SimpleMsg{Message: errMsg.Error()}) - } - if withSubnets == "" { - withSubnets = "false" + } - // [Process] - content, err := resource.DeleteVNet(nsId, vNetId, withSubnets) - if err != nil { - log.Error().Err(err).Msg("") - return c.JSON(http.StatusInternalServerError, model.SimpleMsg{Message: err.Error()}) + var resp model.SimpleMsg + var err error + + switch action { + case resource.ActionNone, resource.ActionWithSubnets, resource.ActionForce: + // [Process] + resp, err = resource.DeleteVNet(nsId, vNetId, action.String()) + if err != nil { + log.Error().Err(err).Msg("") + return c.JSON(http.StatusInternalServerError, model.SimpleMsg{Message: err.Error()}) + } + case resource.ActionRefine: + // [Process] + resp, err = resource.RefineVNet(nsId, vNetId) + if err != nil { + log.Error().Err(err).Msg("") + return c.JSON(http.StatusInternalServerError, model.SimpleMsg{Message: err.Error()}) + } + default: + errMsg := fmt.Errorf("invalid action (%s)", action) + log.Warn().Msgf(errMsg.Error()) + return c.JSON(http.StatusBadRequest, model.SimpleMsg{Message: errMsg.Error()}) } // [Output] - return c.JSON(http.StatusOK, content) + return c.JSON(http.StatusOK, resp) } // RestDelAllVNet godoc diff --git a/src/core/resource/subnet.go b/src/core/resource/subnet.go index 23ef2d85e..6772f419c 100644 --- a/src/core/resource/subnet.go +++ b/src/core/resource/subnet.go @@ -199,12 +199,10 @@ func CreateSubnet(nsId string, vNetId string, subnetReq *model.TbSubnetReq) (mod var vNetInfo model.TbVNetInfo var subnetInfo model.TbSubnetInfo var err error = nil - subnetInfo.Id = subnetReq.Name - subnetInfo.Name = subnetReq.Name - // Set the resource type - parentResourceType := model.StrVNet - resourceType := model.StrSubnet + /* + * Validate the input parameters + */ // Validate the input parameters err = common.CheckString(nsId) @@ -228,6 +226,13 @@ func CreateSubnet(nsId string, vNetId string, subnetReq *model.TbSubnetReq) (mod return emptyRet, err } + subnetInfo.Id = subnetReq.Name + subnetInfo.Name = subnetReq.Name + + // Set the resource type + parentResourceType := model.StrVNet + resourceType := model.StrSubnet + // Check if the subnet already exists exists, err := CheckChildResource(nsId, resourceType, vNetId, subnetInfo.Id) if exists { @@ -278,6 +283,10 @@ func CreateSubnet(nsId string, vNetId string, subnetReq *model.TbSubnetReq) (mod subnetInfo.CspVNetId = vNetInfo.CspResourceId subnetInfo.CspVNetName = vNetInfo.CspResourceName + /* + * Create a subnet + */ + // Set status to 'Configuring' subnetInfo.Status = string(NetworkOnConfiguring) // Save the status @@ -309,7 +318,7 @@ func CreateSubnet(nsId string, vNetId string, subnetReq *model.TbSubnetReq) (mod // API to create a subnet url := fmt.Sprintf("%s/vpc/%s/subnet", model.SpiderRestUrl, vNetInfo.CspResourceName) - // Defer function to ensure cleanup object + // Clean up the object when something goes wrong defer func() { // Only if this operation fails, the subnet will be deleted if err != nil && subnetInfo.Status == string(NetworkOnConfiguring) { @@ -362,11 +371,9 @@ func CreateSubnet(nsId string, vNetId string, subnetReq *model.TbSubnetReq) (mod } } - // Set status to 'Available' + // [Set and store status] subnetInfo.Status = string(NetworkAvailable) - log.Debug().Msgf("subnetInfo: %+v", subnetInfo) - // Save subnet object into the key-value store subnetObj, err := json.Marshal(subnetInfo) if err != nil { @@ -435,14 +442,15 @@ func CreateSubnet(nsId string, vNetId string, subnetReq *model.TbSubnetReq) (mod // GetSubnet func GetSubnet(nsId string, vNetId string, subnetId string) (model.TbSubnetInfo, error) { + log.Info().Msg("GetSubnet") // subnet objects var emptyRet model.TbSubnetInfo var subnetInfo model.TbSubnetInfo - // Set the resource type - // parentResourceType := model.StrVNet - resourceType := model.StrSubnet + /* + * Validate the input parameters + */ // Validate the input parameters err := common.CheckString(nsId) @@ -461,10 +469,36 @@ func GetSubnet(nsId string, vNetId string, subnetId string) (model.TbSubnetInfo, return emptyRet, err } - // Todo: Change the below code when Spider provides the API to get the subnet info from CSP + /* + * Get the subnet info + */ + + // Set the resource type + // parentResourceType := model.StrVNet + resourceType := model.StrSubnet + // Set a key for the subnet object + // vNetKey := common.GenResourceKey(nsId, parentResourceType, vNetId) subnetKey := common.GenChildResourceKey(nsId, resourceType, vNetId, subnetId) + // // Read the saved vNet info + // vNetKv, err := kvstore.GetKv(vNetKey) + // if err != nil { + // log.Error().Err(err).Msg("") + // return emptyRet, err + // } + // if vNetKv == (kvstore.KeyValue{}) { + // err := fmt.Errorf("does not exist, vNet: %s", vNetId) + // log.Error().Err(err).Msg("") + // return emptyRet, err + // } + // // vNet object + // err = json.Unmarshal([]byte(vNetKv.Value), &vNetInfo) + // if err != nil { + // log.Error().Err(err).Msg("") + // return emptyRet, err + // } + // Read the stored subnet info subnetKeyValue, err := kvstore.GetKv(subnetKey) if err != nil { @@ -476,6 +510,7 @@ func GetSubnet(nsId string, vNetId string, subnetId string) (model.TbSubnetInfo, log.Error().Err(err).Msg("") return emptyRet, err } + // subnet object err = json.Unmarshal([]byte(subnetKeyValue.Value), &subnetInfo) if err != nil { @@ -483,19 +518,59 @@ func GetSubnet(nsId string, vNetId string, subnetId string) (model.TbSubnetInfo, return emptyRet, err } + // [Via Spider] Get a subnet + client := resty.New() + method := "GET" + + // API to get a subnet + url := fmt.Sprintf("%s/vpc/%s/subnet/%s", model.SpiderRestUrl, subnetInfo.CspVNetName, subnetInfo.CspResourceName) + queryParams := "?ConnectionName=" + subnetInfo.ConnectionName + url += queryParams + + spReqt := common.NoBody + var spResp spiderSubnetInfo + + err = common.ExecuteHttpRequest( + client, + method, + url, + nil, + common.SetUseBody(spReqt), + &spReqt, + &spResp, + common.MediumDuration, + ) + + if err != nil { + log.Error().Err(err).Msg("") + return emptyRet, err + } + + // Set the subnet object with the response from the Spider + subnetInfo.CspResourceId = spResp.IId.SystemId + subnetInfo.CspResourceName = spResp.IId.NameId + subnetInfo.IPv4_CIDR = spResp.IPv4_CIDR + subnetInfo.Zone = spResp.Zone + subnetInfo.KeyValueList = spResp.KeyValueList + // TODO: restore the tag list later + // subnetInfo.TagList = spResp.TagList + + // TODO: Check if it's required or not to save the subnet object + return subnetInfo, nil } // ListSubnet func ListSubnet(nsId string, vNetId string) ([]model.TbSubnetInfo, error) { + log.Info().Msg("ListSubnet") // subnet objects var emptyRet []model.TbSubnetInfo var subnetInfoList []model.TbSubnetInfo - // Set the resource type - // parentResourceType := model.StrVNet - resourceType := model.StrSubnet + /* + * Validate the input parameters + */ // Validate the input parameters err := common.CheckString(nsId) @@ -509,41 +584,33 @@ func ListSubnet(nsId string, vNetId string) ([]model.TbSubnetInfo, error) { return emptyRet, err } - // Set a vNetKey for the vNet object - vNetKey := common.GenResourceKey(nsId, resourceType, vNetId) - subnetPrefixKey := vNetKey + "/subnet" - // Read the stored subnets - subnetsKv, err := kvstore.GetKvList(subnetPrefixKey) + /* + * Get the subnet info list + */ + + // Use the GetVNet function to get the subnets info + vNetInfo, err := GetVNet(nsId, vNetId) if err != nil { log.Error().Err(err).Msg("") return emptyRet, err } - log.Debug().Msgf("subnetsKv: %+v", subnetsKv) - // subnet object - for _, kv := range subnetsKv { - var subnetInfo model.TbSubnetInfo - err = json.Unmarshal([]byte(kv.Value), &subnetInfo) - if err != nil { - log.Error().Err(err).Msg("") - return emptyRet, err - } - subnetInfoList = append(subnetInfoList, subnetInfo) - } + subnetInfoList = append(subnetInfoList, vNetInfo.SubnetInfoList...) return subnetInfoList, nil } // DeleteSubnet deletes and returns the result -func DeleteSubnet(nsId string, vNetId string, subnetId string) (model.SimpleMsg, error) { +func DeleteSubnet(nsId string, vNetId string, subnetId string, actionParam string) (model.SimpleMsg, error) { + log.Info().Msg("DeleteSubnet") // subnet objects var emptyRet model.SimpleMsg var ret model.SimpleMsg - // Set the resource type - parentResourceType := model.StrVNet - resourceType := model.StrSubnet + /* + * Validate the input parameters + */ // Validate the input parameters err := common.CheckString(nsId) @@ -562,6 +629,19 @@ func DeleteSubnet(nsId string, vNetId string, subnetId string) (model.SimpleMsg, return emptyRet, err } + action, vaild := ParseNetworkAction(actionParam) + + // Validate options: withSubnets + if !vaild { + errMsg := fmt.Errorf("invalid action (%s)", action) + log.Warn().Msgf(errMsg.Error()) + return emptyRet, errMsg + } + + // Set the resource type + parentResourceType := model.StrVNet + resourceType := model.StrSubnet + // Set a key for the subnet object vNetKey := common.GenResourceKey(nsId, parentResourceType, vNetId) subnetKey := common.GenChildResourceKey(nsId, resourceType, vNetId, subnetId) @@ -606,12 +686,16 @@ func DeleteSubnet(nsId string, vNetId string, subnetId string) (model.SimpleMsg, // Todo: Check if the subnet is being used by any resouces, such as virtual machines, gateways, etc. // Check if the vNet has subnets or not - if subnetInfo.Status == string(NetworkInUse) { + if action == ActionNone && subnetInfo.Status == string(NetworkInUse) { err := fmt.Errorf("the subnet (%s) is in-use, may have any resources", subnetId) log.Error().Err(err).Msg("") return emptyRet, err } + /* + * Delete the subnet + */ + // Set status to 'Deleting' subnetInfo.Status = string(NetworkOnDeleting) // Save the status @@ -632,6 +716,11 @@ func DeleteSubnet(nsId string, vNetId string, subnetId string) (model.SimpleMsg, // API to delete a subnet url := fmt.Sprintf("%s/vpc/%s/subnet/%s", model.SpiderRestUrl, subnetInfo.CspVNetName, subnetInfo.CspResourceName) + queryParams := "" + if action == ActionForce { + queryParams = "?force=true" + } + url += queryParams var spResp spiderBooleanInfoResp @@ -659,7 +748,7 @@ func DeleteSubnet(nsId string, vNetId string, subnetId string) (model.SimpleMsg, return emptyRet, err } if !ok { - err := fmt.Errorf("failed to delete the subnet (%s)", subnetId) + err := fmt.Errorf("failed to delete the subnet (%s)", subnetInfo.Id) log.Error().Err(err).Msg("") return emptyRet, err } @@ -711,22 +800,195 @@ func DeleteSubnet(nsId string, vNetId string, subnetId string) (model.SimpleMsg, return ret, nil } -func RegisterSubnet(nsId string, vNetId string, subnetReq *model.TbRegisterSubnetReq) (model.TbSubnetInfo, error) { +func RefineSubnet(nsId string, vNetId string, subnetId string) (model.SimpleMsg, error) { + log.Info().Msg("RefineSubnet") + + /* + * [NOTE] + * "Refine" operates based on information managed by Tumblebug. + * Based on this information, it checks whether there is information/resource in Spider/CSP. + * It removes the information managed by Tumblebug if there's no information/resource. + */ // subnet objects - var emptyRet model.TbSubnetInfo + var emptyRet model.SimpleMsg + var ret model.SimpleMsg var vNetInfo model.TbVNetInfo var subnetInfo model.TbSubnetInfo - var err error = nil - - // Set the subnet object - subnetInfo.Id = subnetReq.Name - subnetInfo.Name = subnetReq.Name // Set the resource type parentResourceType := model.StrVNet resourceType := model.StrSubnet - subnetInfo.ResourceType = resourceType + + /* + * Validate the input parameters + */ + + // Validate the input parameters + err := common.CheckString(nsId) + if err != nil { + log.Error().Err(err).Msg("") + return emptyRet, err + } + err = common.CheckString(vNetId) + if err != nil { + log.Error().Err(err).Msg("") + return emptyRet, err + } + err = common.CheckString(subnetId) + if err != nil { + log.Error().Err(err).Msg("") + return emptyRet, err + } + + // Set a key for the subnet object + vNetKey := common.GenResourceKey(nsId, parentResourceType, vNetId) + subnetKey := common.GenChildResourceKey(nsId, resourceType, vNetId, subnetId) + + // Read the saved vNet info + vNetKv, err := kvstore.GetKv(vNetKey) + if err != nil { + log.Error().Err(err).Msg("") + return emptyRet, err + } + if vNetKv == (kvstore.KeyValue{}) { + err := fmt.Errorf("does not exist, vNet: %s", vNetId) + log.Error().Err(err).Msg("") + return emptyRet, err + } + // vNet object + err = json.Unmarshal([]byte(vNetKv.Value), &vNetInfo) + if err != nil { + log.Error().Err(err).Msg("") + return emptyRet, err + } + + // Read the stored subnet info + subnetKeyValue, err := kvstore.GetKv(subnetKey) + if err != nil { + log.Error().Err(err).Msg("") + return emptyRet, err + } + if subnetKeyValue == (kvstore.KeyValue{}) { + err := fmt.Errorf("does not exist, subnet: %s", subnetId) + log.Error().Err(err).Msg("") + return emptyRet, err + } + + // subnet object + err = json.Unmarshal([]byte(subnetKeyValue.Value), &subnetInfo) + if err != nil { + log.Error().Err(err).Msg("") + return emptyRet, err + } + + /* + * Check and refine the subnet info + */ + + // [Via Spider] Get the subnet + client := resty.New() + method := "GET" + + // API to get a subnet + url := fmt.Sprintf("%s/vpc/%s/subnet/%s", model.SpiderRestUrl, subnetInfo.CspVNetName, subnetInfo.CspResourceName) + queryParams := "?ConnectionName=" + subnetInfo.ConnectionName + url += queryParams + + spReqt := common.NoBody + var spResp spiderSubnetInfo + + err = common.ExecuteHttpRequest( + client, + method, + url, + nil, + common.SetUseBody(spReqt), + &spReqt, + &spResp, + common.MediumDuration, + ) + + // if err != nil { + // log.Error().Err(err).Msg("") + // return emptyRet, err + // } + if err == nil { + // [Output] + err := fmt.Errorf("may not be refined, subnet info (id: %s) exists", subnetId) + log.Warn().Err(err).Msg("") + ret.Message = err.Error() + return ret, err + } + + /* + * Delete the subnet info in case of the subnet does not exist + */ + + // Delete the saved the subnet info + err = kvstore.Delete(subnetKey) + if err != nil { + log.Warn().Err(err).Msg("") + // return emptyRet, err + } + + // Update the vNet info + for i, s := range vNetInfo.SubnetInfoList { + if s.Id == subnetId { + vNetInfo.SubnetInfoList = append(vNetInfo.SubnetInfoList[:i], vNetInfo.SubnetInfoList[i+1:]...) + break + } + } + if len(vNetInfo.SubnetInfoList) == 0 { + vNetInfo.Status = string(NetworkAvailable) + } + + // Save the updated vNet info + val, err := json.Marshal(vNetInfo) + if err != nil { + log.Warn().Err(err).Msg("") + // return emptyRet, err + } + err = kvstore.Put(vNetKey, string(val)) + if err != nil { + log.Warn().Err(err).Msg("") + // return emptyRet, err + } + + err = label.DeleteLabelObject(model.StrSubnet, subnetInfo.Uid) + if err != nil { + log.Warn().Err(err).Msg("") + } + + // Get and check the subnet info still exists or not + subnetKv, err := kvstore.GetKv(subnetKey) + if err != nil { + log.Warn().Err(err).Msg("") + } + if subnetKv != (kvstore.KeyValue{}) { + err := fmt.Errorf("fail to refine the subnet info (id: %s)", subnetId) + ret.Message = err.Error() + return ret, err + } + + // [Output] the message + ret.Message = fmt.Sprintf("the subnet info (%s) has been refined", subnetId) + + return ret, nil +} + +func RegisterSubnet(nsId string, vNetId string, subnetReq *model.TbRegisterSubnetReq) (model.TbSubnetInfo, error) { + log.Info().Msg("RegisterSubnet") + + // subnet objects + var emptyRet model.TbSubnetInfo + var vNetInfo model.TbVNetInfo + var subnetInfo model.TbSubnetInfo + var err error = nil + + /* + * Validate the input parameters + */ // Validate the input parameters err = common.CheckString(nsId) @@ -750,6 +1012,15 @@ func RegisterSubnet(nsId string, vNetId string, subnetReq *model.TbRegisterSubne return emptyRet, err } + // Set the subnet object + subnetInfo.Id = subnetReq.Name + subnetInfo.Name = subnetReq.Name + + // Set the resource type + parentResourceType := model.StrVNet + resourceType := model.StrSubnet + subnetInfo.ResourceType = resourceType + // Check if the subnet already exists exists, err := CheckChildResource(nsId, resourceType, vNetId, subnetInfo.Id) if exists { @@ -792,6 +1063,10 @@ func RegisterSubnet(nsId string, vNetId string, subnetReq *model.TbRegisterSubne subnetInfo.CspVNetId = vNetInfo.CspResourceId subnetInfo.CspVNetName = vNetInfo.CspResourceName + /* + * Register a subnet + */ + // Set status to 'Registering' subnetInfo.Status = string(NetworkOnRegistering) // Save the status @@ -942,14 +1217,15 @@ func RegisterSubnet(nsId string, vNetId string, subnetReq *model.TbRegisterSubne // DeregisterSubnet deregister subnet and returns the result func DeregisterSubnet(nsId string, vNetId string, subnetId string) (model.SimpleMsg, error) { + log.Info().Msg("DeregisterSubnet") // subnet objects var emptyRet model.SimpleMsg var ret model.SimpleMsg - // Set the resource type - parentResourceType := model.StrVNet - resourceType := model.StrSubnet + /* + * Validate the input parameters + */ // Validate the input parameters err := common.CheckString(nsId) @@ -968,6 +1244,10 @@ func DeregisterSubnet(nsId string, vNetId string, subnetId string) (model.Simple return emptyRet, err } + // Set the resource type + parentResourceType := model.StrVNet + resourceType := model.StrSubnet + // Set a key for the subnet object vNetKey := common.GenResourceKey(nsId, parentResourceType, vNetId) subnetKey := common.GenChildResourceKey(nsId, resourceType, vNetId, subnetId) @@ -1018,6 +1298,10 @@ func DeregisterSubnet(nsId string, vNetId string, subnetId string) (model.Simple return emptyRet, err } + /* + * Deregister the subnet + */ + // Set status to 'Derigistering' subnetInfo.Status = string(NetworkOnDeregistering) // Save the status @@ -1038,7 +1322,7 @@ func DeregisterSubnet(nsId string, vNetId string, subnetId string) (model.Simple spReqt.ReqInfo.VPCName = subnetInfo.CspVNetName // API to deregister subnet - url := fmt.Sprintf("%s/regsubne/%s", model.SpiderRestUrl, subnetInfo.CspResourceName) + url := fmt.Sprintf("%s/regsubnet/%s", model.SpiderRestUrl, subnetInfo.CspResourceName) var spResp spiderBooleanInfoResp @@ -1120,8 +1404,8 @@ func DeregisterSubnet(nsId string, vNetId string, subnetId string) (model.Simple } /* -The following functions are used for Designing VNets -*/ + * The following functions are used for Designing VNets + */ // GetFirstNZones returns the first N zones of the given connection func GetFirstNZones(connectionName string, firstN int) ([]string, int, error) { diff --git a/src/core/resource/vnet.go b/src/core/resource/vnet.go index 3b543a37f..e81b1cab0 100644 --- a/src/core/resource/vnet.go +++ b/src/core/resource/vnet.go @@ -41,12 +41,13 @@ const ( NetworkOnReading NetworkStatus = "Reading" // The network information is being read. NetworkOnUpdating NetworkStatus = "Updating" // The network is being updated. NetworkOnDeleting NetworkStatus = "Deleting" // The network is being deleted. + // NetworkOnRefinining NetworkStatus = "Refining" // The network is being refined. // Register/deregister operations NetworkOnRegistering NetworkStatus = "Registering" // The network is being registered. NetworkOnDeregistering NetworkStatus = "Dergistering" // The network is being registered. - // Available status + // NetworkAvailable status NetworkAvailable NetworkStatus = "Available" // The network is fully created and ready for use. // In Use status @@ -55,7 +56,7 @@ const ( // Unknwon status NetworkUnknown NetworkStatus = "Unknown" // The network status is unknown. - // Error Handling + // NetworkError Handling NetworkError NetworkStatus = "Error" // An error occurred during a CRUD operation. NetworkErrorOnConfiguring NetworkStatus = "ErrorOnConfiguring" // An error occurred during the configuring operation. NetworkErrorOnReading NetworkStatus = "ErrorOnReading" // An error occurred during the reading operation. @@ -64,6 +65,55 @@ const ( NetworkErrorOnRegistering NetworkStatus = "ErrorOnRegistering" // An error occurred during the registering operation. ) +type NetworkAction string + +const ( + ActionNone NetworkAction = "" + ActionRefine NetworkAction = "refine" + ActionForce NetworkAction = "force" + ActionWithSubnets NetworkAction = "withsubnets" + // add additional actions here +) + +var ( + stringToNetworkAction = map[string]NetworkAction{ + "": ActionNone, + "refine": ActionRefine, + "force": ActionForce, + "withsubnets": ActionWithSubnets, + } + + actionsToDeleteSubnet = map[NetworkAction]bool{ + ActionRefine: true, + ActionForce: true, + // add additional actions here + } + + actionsToDeleteVNet = map[NetworkAction]bool{ + ActionRefine: true, + ActionForce: true, + ActionWithSubnets: true, + // add additional actions here + } +) + +func ParseNetworkAction(s string) (NetworkAction, bool) { + action, ok := stringToNetworkAction[strings.ToLower(s)] + return action, ok +} + +func (na NetworkAction) String() string { + return string(na) +} + +func (na NetworkAction) IsValidToDeleteSubnet() bool { + return actionsToDeleteSubnet[na] +} + +func (na NetworkAction) IsValidToDeleteVNet() bool { + return actionsToDeleteVNet[na] +} + // TbVNetReqStructLevelValidation is a function to validate 'TbVNetReq' object. func TbVNetReqStructLevelValidation(sl validator.StructLevel) { @@ -286,9 +336,9 @@ func CreateVNet(nsId string, vNetReq *model.TbVNetReq) (model.TbVNetInfo, error) var vNetInfo model.TbVNetInfo var err error = nil - // Set the resource type - resourceType := model.StrVNet - childResourceType := model.StrSubnet + /* + * Validate the input parameters + */ // Validate the input parameters err = common.CheckString(nsId) @@ -306,7 +356,11 @@ func CreateVNet(nsId string, vNetReq *model.TbVNetReq) (model.TbVNetInfo, error) return emptyRet, err } - // Set the vNet object + // Set the resource type + resourceType := model.StrVNet + childResourceType := model.StrSubnet + + // Set the vNet object in advance uid := common.GenUid() vNetInfo.ResourceType = resourceType vNetInfo.Name = vNetReq.Name @@ -317,21 +371,6 @@ func CreateVNet(nsId string, vNetReq *model.TbVNetReq) (model.TbVNetInfo, error) // todo: restore the tag list later // vNetInfo.TagList = vNetReq.TagList - // Set a vNetKey for the vNet object - vNetKey := common.GenResourceKey(nsId, resourceType, vNetInfo.Id) - // Check if the vNet already exists or not - exists, err := CheckResource(nsId, resourceType, vNetInfo.Id) - if exists { - log.Error().Err(err).Msg("") - err := fmt.Errorf("already exists, vNet: %s", vNetInfo.Id) - return emptyRet, err - } - if err != nil { - log.Error().Err(err).Msg("") - err := fmt.Errorf("failed to check if the vNet (%s) exists or not", vNetInfo.Id) - return emptyRet, err - } - // Note: Set subnetInfoList in vNetInfo in advance // since each subnet uid must be consistent for _, subnetInfo := range vNetReq.SubnetInfoList { @@ -349,10 +388,27 @@ func CreateVNet(nsId string, vNetReq *model.TbVNetReq) (model.TbVNetInfo, error) log.Debug().Msgf("vNetInfo: %+v", vNetInfo) - // [Set status] - vNetInfo.Status = string(NetworkOnConfiguring) + // Set a vNetKey for the vNet object + vNetKey := common.GenResourceKey(nsId, resourceType, vNetInfo.Id) + // Check if the vNet already exists or not + exists, err := CheckResource(nsId, resourceType, vNetInfo.Id) + if exists { + log.Error().Err(err).Msg("") + err := fmt.Errorf("already exists, vNet: %s", vNetInfo.Id) + return emptyRet, err + } + if err != nil { + log.Error().Err(err).Msg("") + err := fmt.Errorf("failed to check if the vNet (%s) exists or not", vNetInfo.Id) + return emptyRet, err + } - // Save the current operation status and the vNet object + /* + * Create vNet with at least one subnet + */ + + // [Set and store status] + vNetInfo.Status = string(NetworkOnConfiguring) val, err := json.Marshal(vNetInfo) if err != nil { log.Error().Err(err).Msg("") @@ -364,7 +420,7 @@ func CreateVNet(nsId string, vNetReq *model.TbVNetReq) (model.TbVNetInfo, error) return emptyRet, err } - // Set request body to create a vNet and subnets + // [Via Spider] Create a vNet and subnets spReqt := spiderCreateVPCRequest{} spReqt.ConnectionName = vNetReq.ConnectionName spReqt.ReqInfo.Name = vNetInfo.Uid @@ -391,7 +447,7 @@ func CreateVNet(nsId string, vNetReq *model.TbVNetReq) (model.TbVNetInfo, error) // API to create a vNet url := fmt.Sprintf("%s/vpc", model.SpiderRestUrl) - // Defer function to ensure cleanup object + // Cleanup object when something goes wrong defer func() { // Only if this operation fails, the vNet will be deleted if err != nil && vNetInfo.Status == string(NetworkOnConfiguring) { @@ -470,7 +526,7 @@ func CreateVNet(nsId string, vNetReq *model.TbVNetReq) (model.TbVNetInfo, error) } } - // [Set status] + // [Set and store status] if len(vNetInfo.SubnetInfoList) == 0 { vNetInfo.Status = string(NetworkAvailable) } else if len(vNetInfo.SubnetInfoList) > 0 { @@ -482,7 +538,7 @@ func CreateVNet(nsId string, vNetReq *model.TbVNetReq) (model.TbVNetInfo, error) log.Debug().Msgf("vNetInfo: %+v", vNetInfo) - // Save vNet object into the key-value store + // Store vNet object into the key-value store value, err := json.Marshal(vNetInfo) if err != nil { log.Error().Err(err).Msg("") @@ -494,7 +550,7 @@ func CreateVNet(nsId string, vNetReq *model.TbVNetReq) (model.TbVNetInfo, error) return emptyRet, err } - // Save subnet objects into the key-value store + // Store subnet objects into the key-value store for _, subnetInfo := range vNetInfo.SubnetInfoList { // Set a subnetKey for the subnet object subnetKey := common.GenChildResourceKey(nsId, childResourceType, vNetInfo.Id, subnetInfo.Id) @@ -504,7 +560,7 @@ func CreateVNet(nsId string, vNetReq *model.TbVNetReq) (model.TbVNetInfo, error) return emptyRet, err } - // Save the subnet object into the key-value store + // Store the subnet object into the key-value store err = kvstore.Put(subnetKey, string(value)) if err != nil { log.Error().Err(err).Msg("") @@ -533,7 +589,6 @@ func CreateVNet(nsId string, vNetReq *model.TbVNetReq) (model.TbVNetInfo, error) log.Error().Err(err).Msg("") return emptyRet, err } - } // Check if the vNet info is stored @@ -584,10 +639,9 @@ func GetVNet(nsId string, vNetId string) (model.TbVNetInfo, error) { var emptyRet model.TbVNetInfo var vNetInfo model.TbVNetInfo - // Set the resource type - resourceType := model.StrVNet - // Set a vNetKey for the vNet object - vNetKey := common.GenResourceKey(nsId, resourceType, vNetId) + /* + * Validate the input parameters + */ // Check the input parameters err := common.CheckString(nsId) @@ -601,6 +655,11 @@ func GetVNet(nsId string, vNetId string) (model.TbVNetInfo, error) { return emptyRet, err } + // Set the resource type + resourceType := model.StrVNet + // Set a vNetKey for the vNet object + vNetKey := common.GenResourceKey(nsId, resourceType, vNetId) + // Read the stored vNet info keyValue, err := kvstore.GetKv(vNetKey) if err != nil { @@ -620,7 +679,11 @@ func GetVNet(nsId string, vNetId string) (model.TbVNetInfo, error) { return emptyRet, err } - // Get a vNet and subnets + /* + * Get vNet info + */ + + // [Via Spider] Get a vNet and subnets client := resty.New() method := "GET" spReqt := common.NoBody @@ -655,32 +718,33 @@ func GetVNet(nsId string, vNetId string) (model.TbVNetInfo, error) { // todo: restore the tag list later // vNetInfo.TagList = spResp.TagList - // Save the current operation status and the vNet object - val, err := json.Marshal(vNetInfo) - if err != nil { - log.Error().Err(err).Msg("") - return emptyRet, err - } + // TODO: Check if it's required or not to save the vNet object + // val, err := json.Marshal(vNetInfo) + // if err != nil { + // log.Error().Err(err).Msg("") + // return emptyRet, err + // } - err = kvstore.Put(vNetKey, string(val)) - if err != nil { - log.Error().Err(err).Msg("") - return emptyRet, err - } + // err = kvstore.Put(vNetKey, string(val)) + // if err != nil { + // log.Error().Err(err).Msg("") + // return emptyRet, err + // } return vNetInfo, nil } // DeleteVNet accepts vNet creation request, creates and returns an TB vNet object -func DeleteVNet(nsId string, vNetId string, withSubnets string) (model.SimpleMsg, error) { +func DeleteVNet(nsId string, vNetId string, actionParam string) (model.SimpleMsg, error) { log.Info().Msg("DeleteVNet") // vNet object var emptyRet model.SimpleMsg var ret model.SimpleMsg - // Set the resource type - resourceType := model.StrVNet + /* + * Validate the input parameters + */ // Check the input parameters err := common.CheckString(nsId) @@ -694,15 +758,15 @@ func DeleteVNet(nsId string, vNetId string, withSubnets string) (model.SimpleMsg return emptyRet, err } - // Validate options: withSubnets - if withSubnets != "" && withSubnets != "true" && withSubnets != "false" { - errMsg := fmt.Errorf("invalid option, withSubnets (%s)", withSubnets) + action, valid := ParseNetworkAction(actionParam) + if !valid { + errMsg := fmt.Errorf("invalid action (%s)", action) log.Warn().Msgf(errMsg.Error()) return emptyRet, errMsg } - if withSubnets == "" { - withSubnets = "false" - } + + // Set the resource type + resourceType := model.StrVNet // Set a vNetKey for the vNet object vNetKey := common.GenResourceKey(nsId, resourceType, vNetId) @@ -714,13 +778,31 @@ func DeleteVNet(nsId string, vNetId string, withSubnets string) (model.SimpleMsg } log.Debug().Msgf("subnetsKv: %+v", subnetsKv) - if withSubnets == "false" && len(subnetsKv) > 0 { + // normal case: action == "" + if action == ActionNone && len(subnetsKv) > 0 { err := fmt.Errorf("the vNet (%s) is in-use, may have subnets", vNetId) log.Error().Err(err).Msg("") return emptyRet, err } - // Delete the subnets associated with the vNet + // Set the subnet delete action + subnetDelAction := ActionNone + switch action { + case ActionNone, ActionWithSubnets: + subnetDelAction = ActionNone + case ActionForce: + subnetDelAction = ActionForce + default: + err := fmt.Errorf("invalid action (%s)", action) + log.Warn().Msgf(err.Error()) + return emptyRet, err + } + + /* + * Delete the vNet + */ + + // First, delete the subnets associated with the vNet for _, kv := range subnetsKv { subnet := model.TbSubnetInfo{} err = json.Unmarshal([]byte(kv.Value), &subnet) @@ -728,14 +810,14 @@ func DeleteVNet(nsId string, vNetId string, withSubnets string) (model.SimpleMsg log.Error().Err(err).Msg("") return emptyRet, err } - _, err := DeleteSubnet(nsId, vNetId, subnet.Id) + _, err := DeleteSubnet(nsId, vNetId, subnet.Id, subnetDelAction.String()) if err != nil { log.Error().Err(err).Msg("") return emptyRet, err } } - // Read the stored vNet info + // Read the stored vNet info, which includes the updated subnets vNetKv, err := kvstore.GetKv(vNetKey) if err != nil { log.Error().Err(err).Msg("") @@ -755,9 +837,9 @@ func DeleteVNet(nsId string, vNetId string, withSubnets string) (model.SimpleMsg return emptyRet, err } - // Set status to 'Deleting' + // [Set and store status] vNetInfo.Status = string(NetworkOnDeleting) - // Save the status + // Store the status val, err := json.Marshal(vNetInfo) if err != nil { log.Error().Err(err).Msg("") @@ -775,6 +857,11 @@ func DeleteVNet(nsId string, vNetId string, withSubnets string) (model.SimpleMsg // API to delete a vNet url := fmt.Sprintf("%s/vpc/%s", model.SpiderRestUrl, vNetInfo.CspResourceName) + queryParam := "" + if action == ActionForce { + queryParam = "?force=true" + } + url += queryParam var spResp spiderBooleanInfoResp @@ -831,6 +918,190 @@ func DeleteVNet(nsId string, vNetId string, withSubnets string) (model.SimpleMsg return ret, nil } +func RefineVNet(nsId string, vNetId string) (model.SimpleMsg, error) { + log.Info().Msg("RefineVNet") + + /* + * [NOTE] + * "Refine" operates based on information managed by Tumblebug. + * Based on this information, it checks whether there is information/resource in Spider/CSP. + * It removes the information managed by Tumblebug if there's no information/resource. + */ + + // vNet object + var emptyRet model.SimpleMsg + var ret model.SimpleMsg + var vNetInfo model.TbVNetInfo + + // Set the resource type + resourceType := model.StrVNet + + /* + * Validate the input parameters + */ + + // Check the input parameters + err := common.CheckString(nsId) + if err != nil { + log.Error().Err(err).Msg("") + return emptyRet, err + } + err = common.CheckString(vNetId) + if err != nil { + log.Error().Err(err).Msg("") + return emptyRet, err + } + + // Set a vNetKey for the vNet object + vNetKey := common.GenResourceKey(nsId, resourceType, vNetId) + + // Read the stored vNet info + keyValue, err := kvstore.GetKv(vNetKey) + if err != nil { + log.Error().Err(err).Msg("") + return emptyRet, err + } + + if keyValue == (kvstore.KeyValue{}) { + err := fmt.Errorf("does not exist, vNet: %s", vNetId) + log.Error().Err(err).Msg("") + return emptyRet, err + } + + err = json.Unmarshal([]byte(keyValue.Value), &vNetInfo) + if err != nil { + log.Error().Err(err).Msg("") + return emptyRet, err + } + + /* + * Check and refine the info of vNet and associated subnets + */ + + // [Via Spider] Get a vNet + client := resty.New() + method := "GET" + spReqt := common.NoBody + var spResp spiderVPCInfo + + // API to get a vNet + url := fmt.Sprintf("%s/vpc/%s", model.SpiderRestUrl, vNetInfo.CspResourceName) + queryParams := "?ConnectionName=" + vNetInfo.ConnectionName + url += queryParams + + err = common.ExecuteHttpRequest( + client, + method, + url, + nil, + common.SetUseBody(spReqt), + &spReqt, + &spResp, + common.MediumDuration, + ) + + // if err != nil { + // log.Error().Err(err).Msg("") + // return emptyRet, err + // } + + if err == nil { + err = fmt.Errorf("may not be refined, vNet info (id: %s) exists", vNetId) + log.Warn().Err(err).Msg("") + log.Info().Msgf("try to refine subnets once") + + // Read the stored subnets + subnetKvList, err2 := kvstore.GetKvList(vNetKey + "/subnet") + if err2 != nil { + log.Warn().Err(err2).Msg("") + return emptyRet, err2 + } + + for _, subnetKv := range subnetKvList { + subnetInfo := model.TbSubnetInfo{} + err2 = json.Unmarshal([]byte(subnetKv.Value), &subnetInfo) + if err2 != nil { + log.Warn().Err(err2).Msg("") + // return emptyRet, err + } + log.Debug().Msgf("subnetInfo: %+v", subnetInfo) + + _, err2 := RefineSubnet(nsId, vNetId, subnetInfo.Id) + if err2 != nil { + log.Warn().Err(err2).Msg("") + // return emptyRet, err + } + } + + // [Output] + ret.Message = err.Error() + return ret, err + } + + /* + * In case of the VPC info/resource does not exist in Spider/CSP + * delete the information of vNet and subnets from the key-value stores + */ + + // Delete subnet objects from the key-value store + // Read the stored subnets + subnetKvList, err := kvstore.GetKvList(vNetKey + "/subnet") + if err != nil { + log.Error().Err(err).Msg("") + return emptyRet, err + } + + for _, subnetKv := range subnetKvList { + // Save the subnet object into the key-value store + err = kvstore.Delete(subnetKv.Key) + if err != nil { + log.Warn().Err(err).Msg("") + // return emptyRet, err + } + + err = label.RemoveLabel(model.StrSubnet, vNetInfo.Uid, subnetKv.Key) + if err != nil { + log.Warn().Err(err).Msg("") + // return emptyRet, err + } + } + + // Delete the saved the vNet info + err = kvstore.Delete(vNetKey) + if err != nil { + log.Warn().Err(err).Msg("") + // return emptyRet, err + } + + // Remove label info using RemoveLabel + // labels := map[string]string{ + // "sys.manager": model.StrManager, + // "namespace": nsId, + // } + err = label.RemoveLabel(model.StrVNet, vNetInfo.Uid, vNetKey) + if err != nil { + log.Warn().Err(err).Msg("") + // return emptyRet, err + } + + // Get and check the subnet info still exists or not + vNetKv, err := kvstore.GetKv(vNetKey) + if err != nil { + log.Warn().Err(err).Msg("") + // return emptyRet, err + } + if vNetKv != (kvstore.KeyValue{}) { + err := fmt.Errorf("fail to refine the vNet info (%s)", vNetKv) + ret.Message = err.Error() + return ret, err + } + + // [Output] the message + ret.Message = fmt.Sprintf("the vNet info (%s) has been refined", vNetId) + + return ret, nil +} + // RegisterVNet accepts vNet registration request, register and returns an TB vNet object func RegisterVNet(nsId string, vNetRegisterReq *model.TbRegisterVNetReq) (model.TbVNetInfo, error) { log.Info().Msg("RegisterVNet") @@ -840,9 +1111,9 @@ func RegisterVNet(nsId string, vNetRegisterReq *model.TbRegisterVNetReq) (model. var vNetInfo model.TbVNetInfo var err error = nil - // Set the resource type - resourceType := model.StrVNet - childResourceType := model.StrSubnet + /* + * Validate the input parameters + */ // Validate the input parameters err = common.CheckString(nsId) @@ -860,6 +1131,10 @@ func RegisterVNet(nsId string, vNetRegisterReq *model.TbRegisterVNetReq) (model. return emptyRet, err } + // Set the resource type + resourceType := model.StrVNet + childResourceType := model.StrSubnet + // Set the vNet object uid := common.GenUid() vNetInfo.ResourceType = resourceType @@ -882,9 +1157,12 @@ func RegisterVNet(nsId string, vNetRegisterReq *model.TbRegisterVNetReq) (model. return emptyRet, err } - // [Set status] - vNetInfo.Status = string(NetworkOnRegistering) + /* + * Register vNet in the CSP, which has not been created by Tumblebug + */ + // [Set and store status] + vNetInfo.Status = string(NetworkOnRegistering) // Save the current operation status and the vNet object val, err := json.Marshal(vNetInfo) if err != nil { @@ -896,7 +1174,7 @@ func RegisterVNet(nsId string, vNetRegisterReq *model.TbRegisterVNetReq) (model. return emptyRet, err } - // Register a vNet that has already been created externally + // [Via Spider] Register vNet and subnets var spReqt = spiderVPCRegisterRequest{} spReqt.ConnectionName = vNetRegisterReq.ConnectionName spReqt.ReqInfo.Name = vNetInfo.Uid @@ -918,7 +1196,7 @@ func RegisterVNet(nsId string, vNetRegisterReq *model.TbRegisterVNetReq) (model. spReqt = spiderVPCRegisterRequest{} } - // Defer function to ensure cleanup object + // Clean up the vNet object when something goes wrong defer func() { // Only if this operation fails, the vNet will be deleted if err != nil && vNetInfo.Status == string(NetworkOnRegistering) { @@ -1043,9 +1321,8 @@ func RegisterVNet(nsId string, vNetRegisterReq *model.TbRegisterVNetReq) (model. log.Debug().Msgf("vNetInfo: %+v", vNetInfo) - // [Set status] + // [Set and store status] vNetInfo.Status = string(NetworkAvailable) - // Put vNet object into the key-value store value, err := json.Marshal(vNetInfo) if err != nil { @@ -1109,8 +1386,9 @@ func DeregisterVNet(nsId string, vNetId string, withSubnets string) (model.Simpl var emptyRet model.SimpleMsg var ret model.SimpleMsg - // Set the resource type - resourceType := model.StrVNet + /* + * Validate the input parameters + */ // Check the input parameters err := common.CheckString(nsId) @@ -1124,6 +1402,9 @@ func DeregisterVNet(nsId string, vNetId string, withSubnets string) (model.Simpl return emptyRet, err } + // Set the resource type + resourceType := model.StrVNet + // Validate options: withSubnets if withSubnets != "" && withSubnets != "true" && withSubnets != "false" { errMsg := fmt.Errorf("invalid option, withSubnets (%s)", withSubnets) @@ -1150,6 +1431,10 @@ func DeregisterVNet(nsId string, vNetId string, withSubnets string) (model.Simpl return emptyRet, err } + /* + * Deregister the vNet + */ + // Delete the subnets associated with the vNet for _, kv := range subnetsKv { subnet := model.TbSubnetInfo{} @@ -1158,7 +1443,7 @@ func DeregisterVNet(nsId string, vNetId string, withSubnets string) (model.Simpl log.Error().Err(err).Msg("") return emptyRet, err } - _, err := DeleteSubnet(nsId, vNetId, subnet.Id) + _, err := DeregisterSubnet(nsId, vNetId, subnet.Id) if err != nil { log.Error().Err(err).Msg("") return emptyRet, err @@ -1262,8 +1547,8 @@ func DeregisterVNet(nsId string, vNetId string, withSubnets string) (model.Simpl } /* -The following functions are used for Designing VNets -*/ + * The following functions are used for Designing VNets + */ // DesignVNets accepts a VNet design request, designs and returns a VNet design response func DesignVNets(reqt *model.VNetDesignRequest) (model.VNetDesignResponse, error) { From 44a1e8e760870e628f278d473a3be90c82c9018e Mon Sep 17 00:00:00 2001 From: Yunkon Kim Date: Fri, 4 Oct 2024 18:04:35 +0900 Subject: [PATCH 2/3] Resolve a minor conflict related to `swagger.yaml` --- src/api/rest/docs/swagger.yaml | 150 ++++++++++++++++----------------- 1 file changed, 72 insertions(+), 78 deletions(-) diff --git a/src/api/rest/docs/swagger.yaml b/src/api/rest/docs/swagger.yaml index 4a54bbbbe..530121c1b 100644 --- a/src/api/rest/docs/swagger.yaml +++ b/src/api/rest/docs/swagger.yaml @@ -6093,38 +6093,23 @@ paths: x-codegen-request-body-name: vNetReq delete: tags: - - '[Infra Resource] Network Management' - /ns/{nsId}/resources/vNet/{vNetId}: - delete: - consumes: - - application/json - description: |- - Delete VNet - - withsubnets: delete VNet and its subnets - - refine: delete information of VNet and its subnets if there's no info/resource in Spider/CSP - - force: delete VNet and its subnets regardless of the status of info/resource in Spider/CSP - operationId: DelVNet + - "[Infra Resource] Network Management" + summary: Delete all VNets + description: Delete all VNets + operationId: DelAllVNet parameters: - name: nsId in: path description: Namespace ID required: true - type: string - - description: VNet ID - in: path - name: vNetId - required: true - type: string - - description: Action - enum: - - withsubnets - - refine - - force + schema: + type: string + default: default + - name: match in: query - name: action - type: string - produces: - - application/json + description: Delete resources containing matched ID-substring only + schema: + type: string responses: "200": description: OK @@ -6134,9 +6119,12 @@ paths: $ref: '#/components/schemas/model.IdList' "404": description: Not Found - schema: - $ref: '#/definitions/model.SimpleMsg' - summary: 'Delete VNet (supporting actions: withsubnet, refine, force)' + content: + application/json: + schema: + $ref: '#/components/schemas/model.SimpleMsg' + /ns/{nsId}/resources/vNet/{vNetId}: + get: tags: - "[Infra Resource] Network Management" summary: Get VNet @@ -6178,8 +6166,12 @@ paths: delete: tags: - "[Infra Resource] Network Management" - summary: Delete VNet - description: Delete VNet + summary: "Delete VNet (supporting actions: withsubnet, refine, force)" + description: |- + Delete VNet + - withsubnets: delete VNet and its subnets + - refine: delete information of VNet and its subnets if there's no info/resource in Spider/CSP + - force: delete VNet and its subnets regardless of the status of info/resource in Spider/CSP operationId: DelVNet parameters: - name: nsId @@ -6195,14 +6187,15 @@ paths: required: true schema: type: string - - name: withSubnets + - name: action in: query - description: Delete subnets as well + description: Action schema: type: string enum: - - "true" - - "false" + - withsubnets + - refine + - force responses: "200": description: OK @@ -6218,8 +6211,9 @@ paths: $ref: '#/components/schemas/model.SimpleMsg' /ns/{nsId}/resources/vNet/{vNetId}/subnet: get: - consumes: - - application/json + tags: + - "[Infra Resource] Network Management" + summary: List all subnets description: List all subnets operationId: GetAllSubnet parameters: @@ -6251,9 +6245,11 @@ paths: $ref: '#/components/schemas/model.SimpleMsg' "500": description: Internal Server Error - schema: - $ref: '#/definitions/model.SimpleMsg' - summary: List all subnets + content: + application/json: + schema: + $ref: '#/components/schemas/model.SimpleMsg' + post: tags: - "[Infra Resource] Network Management" summary: Create Subnet @@ -6303,16 +6299,10 @@ paths: /ns/{nsId}/resources/vNet/{vNetId}/subnet/{subnetId}: get: tags: - - '[Infra Resource] Network Management' - /ns/{nsId}/resources/vNet/{vNetId}/subnet/{subnetId}: - delete: - consumes: - - application/json - description: |- - Delete Subnet - - refine: delete information of subnet if there's no info/resource in Spider/CSP - - force: delete subnet regardless of the status of info/resource in Spider/CSP - operationId: DelSubnet + - "[Infra Resource] Network Management" + summary: Get Subnet + description: Get Subnet + operationId: GetSubnet parameters: - name: nsId in: path @@ -6331,16 +6321,8 @@ paths: in: path description: Subnet ID required: true - type: string - - description: Action - enum: - - refine - - force - in: query - name: action - type: string - produces: - - application/json + schema: + type: string responses: "200": description: OK @@ -6350,16 +6332,25 @@ paths: $ref: '#/components/schemas/model.TbSubnetInfo' "404": description: Not Found - schema: - $ref: '#/definitions/model.SimpleMsg' - summary: 'Delete Subnet (supporting actions: refine, force)' + content: + application/json: + schema: + $ref: '#/components/schemas/model.SimpleMsg' + "500": + description: Internal Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/model.SimpleMsg' + delete: tags: - - '[Infra Resource] Network Management' - get: - consumes: - - application/json - description: Get Subnet - operationId: GetSubnet + - "[Infra Resource] Network Management" + summary: "Delete Subnet (supporting actions: refine, force)" + description: |- + Delete Subnet + - refine: delete information of subnet if there's no info/resource in Spider/CSP + - force: delete subnet regardless of the status of info/resource in Spider/CSP + operationId: DelSubnet parameters: - name: nsId in: path @@ -6380,6 +6371,14 @@ paths: required: true schema: type: string + - name: action + in: query + description: Action + schema: + type: string + enum: + - refine + - force responses: "200": description: OK @@ -6389,15 +6388,10 @@ paths: $ref: '#/components/schemas/model.SimpleMsg' "404": description: Not Found - schema: - $ref: '#/definitions/model.SimpleMsg' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/model.SimpleMsg' - summary: Get Subnet - tags: - - '[Infra Resource] Network Management' + content: + application/json: + schema: + $ref: '#/components/schemas/model.SimpleMsg' /ns/{nsId}/sharedResource: post: tags: From f85a1268a9772afc5152e5c7fc4d1db37b954525 Mon Sep 17 00:00:00 2001 From: Yunkon Kim Date: Fri, 4 Oct 2024 19:27:18 +0900 Subject: [PATCH 3/3] Upgrade Spider version in `docker-compose.yaml` --- docker-compose.yaml | 2 +- go.mod | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index a2831f8ac..525aaffa7 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -111,7 +111,7 @@ services: # CB-Spider cb-spider: - image: cloudbaristaorg/cb-spider:0.9.3 + image: cloudbaristaorg/cb-spider:0.9.6 container_name: cb-spider # build: # context: ../cb-spider diff --git a/go.mod b/go.mod index f1ff91dfb..de01b7575 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,6 @@ require ( github.com/rs/zerolog v1.32.0 github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.18.2 - github.com/stretchr/testify v1.9.0 github.com/swaggo/echo-swagger v1.4.1 github.com/swaggo/swag v1.16.3 github.com/tidwall/gjson v1.17.1 @@ -33,7 +32,6 @@ require ( github.com/KyleBanks/depth v1.2.1 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/ghodss/yaml v1.0.0 // indirect @@ -71,7 +69,6 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.1.1 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect