diff --git a/VERSION b/VERSION index 8fdcf386..77fee73a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.9.2 +1.9.3 diff --git a/cmd/init.go b/cmd/init.go index bf705e92..cda99330 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -23,9 +23,9 @@ import ( "iam/pkg/component" "iam/pkg/config" "iam/pkg/database" - "iam/pkg/errorx" "iam/pkg/logging" "iam/pkg/metric" + "iam/pkg/util" ) var globalConfig *config.Config @@ -62,7 +62,7 @@ func initSentry() { log.Info("Sentry is not enabled, will not init it") } - errorx.InitErrorReport(globalConfig.Sentry.Enable) + util.InitErrorReport(globalConfig.Sentry.Enable) } func initMetrics() { diff --git a/docs/docs.go b/docs/docs.go index 4d9298a5..8a811a66 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -1,13 +1,3 @@ -/* - * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-权限中心(BlueKing-IAM) available. - * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. - * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at http://opensource.org/licenses/MIT - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - // GENERATED BY THE COMMAND ABOVE; DO NOT EDIT // This file was generated by swaggo/swag @@ -120,23 +110,27 @@ var doc = `{ "operationId": "api-engine-policies-list", "parameters": [ { - "type": "integer", - "name": "timestamp", + "type": "string", + "example": "1,2,3", + "name": "ids", "in": "query" }, { "type": "integer", - "name": "min_id", + "example": 10001, + "name": "max_id", "in": "query" }, { "type": "integer", - "name": "max_id", + "example": 1, + "name": "min_id", "in": "query" }, { - "type": "string", - "name": "ids", + "type": "integer", + "example": 1592899208, + "name": "timestamp", "in": "query" } ], @@ -193,11 +187,13 @@ var doc = `{ "parameters": [ { "type": "integer", + "example": 1592899208, "name": "begin_updated_at", "in": "query" }, { "type": "integer", + "example": 1592899208, "name": "end_updated_at", "in": "query" } @@ -255,6 +251,7 @@ var doc = `{ "parameters": [ { "type": "integer", + "example": 1592899208, "name": "updated_at", "in": "query" } @@ -337,6 +334,131 @@ var doc = `{ } } }, + "/api/v1/open/departments/{user_id}/groups": { + "get": { + "security": [ + { + "AppCode": [] + }, + { + "AppSecret": [] + } + ], + "description": "get a department's groups", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "open" + ], + "summary": "department groups", + "operationId": "api-open-department-groups-get", + "parameters": [ + { + "type": "string", + "description": "Department ID", + "name": "department_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/util.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/handler.subjectGroupsResponse" + } + } + } + ] + }, + "headers": { + "X-Request-Id": { + "type": "string", + "description": "the request id" + } + } + } + } + } + }, + "/api/v1/open/users/{user_id}/groups": { + "get": { + "security": [ + { + "AppCode": [] + }, + { + "AppSecret": [] + } + ], + "description": "get a user's groups, include the inherit groups from department", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "open" + ], + "summary": "user groups", + "operationId": "api-open-user-groups-get", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "user_id", + "in": "path", + "required": true + }, + { + "type": "boolean", + "description": "get subject's inherit groups from it's departments", + "name": "inherit", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/util.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/handler.subjectGroupsResponse" + } + } + } + ] + }, + "headers": { + "X-Request-Id": { + "type": "string", + "description": "the request id" + } + } + } + } + } + }, "/api/v1/policy/auth": { "post": { "security": [ @@ -1444,22 +1566,26 @@ var doc = `{ }, { "type": "string", + "example": "edit_host", "name": "actionID", "in": "query", "required": true }, { "type": "integer", - "name": "pageSize", + "example": 1, + "name": "page", "in": "query" }, { "type": "integer", - "name": "page", + "example": 100, + "name": "pageSize", "in": "query" }, { "type": "integer", + "example": 1592899208, "name": "timestamp", "in": "query" } @@ -1524,6 +1650,7 @@ var doc = `{ }, { "type": "string", + "example": "1,2,3", "name": "ids", "in": "query", "required": true @@ -2807,6 +2934,10 @@ var doc = `{ }, "handler.appCodeAppSecretSerializer": { "type": "object", + "required": [ + "app_code", + "app_secret" + ], "properties": { "app_code": { "type": "string" @@ -2974,6 +3105,10 @@ var doc = `{ }, "handler.credentialsVerifySerializer": { "type": "object", + "required": [ + "data", + "type" + ], "properties": { "data": { "type": "object", @@ -2996,6 +3131,46 @@ var doc = `{ } } }, + "handler.enginePolicyResponse": { + "type": "object", + "properties": { + "action": { + "type": "object", + "$ref": "#/definitions/handler.policyResponseAction" + }, + "expired_at": { + "type": "integer", + "example": 4102444800 + }, + "expression": { + "type": "object", + "additionalProperties": true + }, + "id": { + "type": "integer", + "example": 100 + }, + "subject": { + "type": "object", + "$ref": "#/definitions/handler.policyResponseSubject" + }, + "system": { + "type": "string", + "example": "bk_cmdb" + }, + "template_id": { + "type": "integer" + }, + "updated_at": { + "type": "integer", + "example": 4102444800 + }, + "version": { + "type": "string", + "example": "1" + } + } + }, "handler.extResource": { "type": "object", "required": [ @@ -3279,6 +3454,7 @@ var doc = `{ "type": "string" }, "environment": { + "description": "NOTE: this field not used!", "type": "string" }, "expired_at": { @@ -3338,39 +3514,18 @@ var doc = `{ "handler.policyListResponse": { "type": "object", "properties": { - "count": { - "type": "integer", - "example": 120 - }, "metadata": { "type": "object", - "$ref": "#/definitions/handler.policyListResponseMetadata" + "$ref": "#/definitions/handler.listPolicySerializer" }, "results": { "type": "array", "items": { - "$ref": "#/definitions/handler.thinPolicyResponse" + "$ref": "#/definitions/handler.enginePolicyResponse" } } } }, - "handler.policyListResponseMetadata": { - "type": "object", - "properties": { - "action": { - "type": "object", - "$ref": "#/definitions/handler.policyResponseAction" - }, - "system": { - "type": "string", - "example": "bk_test" - }, - "timestamp": { - "type": "integer", - "example": 1592899208 - } - } - }, "handler.policyResponseAction": { "type": "object", "properties": { @@ -3508,6 +3663,27 @@ var doc = `{ } } }, + "handler.referenceInstanceSelection": { + "type": "object", + "required": [ + "id", + "system_id" + ], + "properties": { + "id": { + "type": "string", + "example": "host_view" + }, + "ignore_iam_path": { + "type": "boolean", + "example": false + }, + "system_id": { + "type": "string", + "example": "bk_cmdb" + } + } + }, "handler.referenceResourceType": { "type": "object", "required": [ @@ -3525,6 +3701,40 @@ var doc = `{ } } }, + "handler.relatedResourceType": { + "type": "object", + "required": [ + "id", + "system_id" + ], + "properties": { + "id": { + "type": "string", + "example": "host" + }, + "name_alias": { + "type": "string" + }, + "name_alias_en": { + "type": "string" + }, + "related_instance_selections": { + "type": "array", + "items": { + "$ref": "#/definitions/handler.referenceInstanceSelection" + } + }, + "selection_mode": { + "description": "实例选择方式/范围: [\"all\", \"instance\", \"attribute\"]", + "type": "string", + "example": "instance" + }, + "system_id": { + "type": "string", + "example": "bk_cmdb" + } + } + }, "handler.resource": { "type": "object", "required": [ @@ -3648,6 +3858,23 @@ var doc = `{ } } }, + "handler.responseSubject": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "admin" + }, + "name": { + "type": "string", + "example": "Administer" + }, + "type": { + "type": "string", + "example": "user" + } + } + }, "handler.subject": { "type": "object", "required": [ @@ -3663,6 +3890,12 @@ var doc = `{ } } }, + "handler.subjectGroupsResponse": { + "type": "array", + "items": { + "$ref": "#/definitions/handler.responseSubject" + } + }, "handler.subjectTemplateSerializer": { "type": "object", "required": [ @@ -3840,31 +4073,6 @@ var doc = `{ } } }, - "handler.thinPolicyResponse": { - "type": "object", - "properties": { - "expired_at": { - "type": "integer", - "example": 4102444800 - }, - "expression": { - "type": "object", - "additionalProperties": true - }, - "id": { - "type": "integer", - "example": 100 - }, - "subject": { - "type": "object", - "$ref": "#/definitions/handler.policyResponseSubject" - }, - "version": { - "type": "string", - "example": "1" - } - } - }, "handler.tokenResponse": { "type": "object", "properties": { @@ -3887,6 +4095,7 @@ var doc = `{ "type": "string" }, "environment": { + "description": "NOTE: this field not used!", "type": "string" }, "expired_at": { diff --git a/docs/swagger.json b/docs/swagger.json index fb2ee8c0..aea0f1a0 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -93,23 +93,27 @@ "operationId": "api-engine-policies-list", "parameters": [ { - "type": "integer", - "name": "timestamp", + "type": "string", + "example": "1,2,3", + "name": "ids", "in": "query" }, { "type": "integer", - "name": "min_id", + "example": 10001, + "name": "max_id", "in": "query" }, { "type": "integer", - "name": "max_id", + "example": 1, + "name": "min_id", "in": "query" }, { - "type": "string", - "name": "ids", + "type": "integer", + "example": 1592899208, + "name": "timestamp", "in": "query" } ], @@ -166,11 +170,13 @@ "parameters": [ { "type": "integer", + "example": 1592899208, "name": "begin_updated_at", "in": "query" }, { "type": "integer", + "example": 1592899208, "name": "end_updated_at", "in": "query" } @@ -228,6 +234,7 @@ "parameters": [ { "type": "integer", + "example": 1592899208, "name": "updated_at", "in": "query" } @@ -310,6 +317,131 @@ } } }, + "/api/v1/open/departments/{user_id}/groups": { + "get": { + "security": [ + { + "AppCode": [] + }, + { + "AppSecret": [] + } + ], + "description": "get a department's groups", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "open" + ], + "summary": "department groups", + "operationId": "api-open-department-groups-get", + "parameters": [ + { + "type": "string", + "description": "Department ID", + "name": "department_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/util.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/handler.subjectGroupsResponse" + } + } + } + ] + }, + "headers": { + "X-Request-Id": { + "type": "string", + "description": "the request id" + } + } + } + } + } + }, + "/api/v1/open/users/{user_id}/groups": { + "get": { + "security": [ + { + "AppCode": [] + }, + { + "AppSecret": [] + } + ], + "description": "get a user's groups, include the inherit groups from department", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "open" + ], + "summary": "user groups", + "operationId": "api-open-user-groups-get", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "user_id", + "in": "path", + "required": true + }, + { + "type": "boolean", + "description": "get subject's inherit groups from it's departments", + "name": "inherit", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/util.Response" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/handler.subjectGroupsResponse" + } + } + } + ] + }, + "headers": { + "X-Request-Id": { + "type": "string", + "description": "the request id" + } + } + } + } + } + }, "/api/v1/policy/auth": { "post": { "security": [ @@ -1417,22 +1549,26 @@ }, { "type": "string", + "example": "edit_host", "name": "actionID", "in": "query", "required": true }, { "type": "integer", - "name": "pageSize", + "example": 1, + "name": "page", "in": "query" }, { "type": "integer", - "name": "page", + "example": 100, + "name": "pageSize", "in": "query" }, { "type": "integer", + "example": 1592899208, "name": "timestamp", "in": "query" } @@ -1497,6 +1633,7 @@ }, { "type": "string", + "example": "1,2,3", "name": "ids", "in": "query", "required": true @@ -2780,6 +2917,10 @@ }, "handler.appCodeAppSecretSerializer": { "type": "object", + "required": [ + "app_code", + "app_secret" + ], "properties": { "app_code": { "type": "string" @@ -2947,6 +3088,10 @@ }, "handler.credentialsVerifySerializer": { "type": "object", + "required": [ + "data", + "type" + ], "properties": { "data": { "type": "object", @@ -2969,6 +3114,46 @@ } } }, + "handler.enginePolicyResponse": { + "type": "object", + "properties": { + "action": { + "type": "object", + "$ref": "#/definitions/handler.policyResponseAction" + }, + "expired_at": { + "type": "integer", + "example": 4102444800 + }, + "expression": { + "type": "object", + "additionalProperties": true + }, + "id": { + "type": "integer", + "example": 100 + }, + "subject": { + "type": "object", + "$ref": "#/definitions/handler.policyResponseSubject" + }, + "system": { + "type": "string", + "example": "bk_cmdb" + }, + "template_id": { + "type": "integer" + }, + "updated_at": { + "type": "integer", + "example": 4102444800 + }, + "version": { + "type": "string", + "example": "1" + } + } + }, "handler.extResource": { "type": "object", "required": [ @@ -3252,6 +3437,7 @@ "type": "string" }, "environment": { + "description": "NOTE: this field not used!", "type": "string" }, "expired_at": { @@ -3311,39 +3497,18 @@ "handler.policyListResponse": { "type": "object", "properties": { - "count": { - "type": "integer", - "example": 120 - }, "metadata": { "type": "object", - "$ref": "#/definitions/handler.policyListResponseMetadata" + "$ref": "#/definitions/handler.listPolicySerializer" }, "results": { "type": "array", "items": { - "$ref": "#/definitions/handler.thinPolicyResponse" + "$ref": "#/definitions/handler.enginePolicyResponse" } } } }, - "handler.policyListResponseMetadata": { - "type": "object", - "properties": { - "action": { - "type": "object", - "$ref": "#/definitions/handler.policyResponseAction" - }, - "system": { - "type": "string", - "example": "bk_test" - }, - "timestamp": { - "type": "integer", - "example": 1592899208 - } - } - }, "handler.policyResponseAction": { "type": "object", "properties": { @@ -3481,6 +3646,27 @@ } } }, + "handler.referenceInstanceSelection": { + "type": "object", + "required": [ + "id", + "system_id" + ], + "properties": { + "id": { + "type": "string", + "example": "host_view" + }, + "ignore_iam_path": { + "type": "boolean", + "example": false + }, + "system_id": { + "type": "string", + "example": "bk_cmdb" + } + } + }, "handler.referenceResourceType": { "type": "object", "required": [ @@ -3498,6 +3684,40 @@ } } }, + "handler.relatedResourceType": { + "type": "object", + "required": [ + "id", + "system_id" + ], + "properties": { + "id": { + "type": "string", + "example": "host" + }, + "name_alias": { + "type": "string" + }, + "name_alias_en": { + "type": "string" + }, + "related_instance_selections": { + "type": "array", + "items": { + "$ref": "#/definitions/handler.referenceInstanceSelection" + } + }, + "selection_mode": { + "description": "实例选择方式/范围: [\"all\", \"instance\", \"attribute\"]", + "type": "string", + "example": "instance" + }, + "system_id": { + "type": "string", + "example": "bk_cmdb" + } + } + }, "handler.resource": { "type": "object", "required": [ @@ -3621,6 +3841,23 @@ } } }, + "handler.responseSubject": { + "type": "object", + "properties": { + "id": { + "type": "string", + "example": "admin" + }, + "name": { + "type": "string", + "example": "Administer" + }, + "type": { + "type": "string", + "example": "user" + } + } + }, "handler.subject": { "type": "object", "required": [ @@ -3636,6 +3873,12 @@ } } }, + "handler.subjectGroupsResponse": { + "type": "array", + "items": { + "$ref": "#/definitions/handler.responseSubject" + } + }, "handler.subjectTemplateSerializer": { "type": "object", "required": [ @@ -3813,31 +4056,6 @@ } } }, - "handler.thinPolicyResponse": { - "type": "object", - "properties": { - "expired_at": { - "type": "integer", - "example": 4102444800 - }, - "expression": { - "type": "object", - "additionalProperties": true - }, - "id": { - "type": "integer", - "example": 100 - }, - "subject": { - "type": "object", - "$ref": "#/definitions/handler.policyResponseSubject" - }, - "version": { - "type": "string", - "example": "1" - } - } - }, "handler.tokenResponse": { "type": "object", "properties": { @@ -3860,6 +4078,7 @@ "type": "string" }, "environment": { + "description": "NOTE: this field not used!", "type": "string" }, "expired_at": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 017f776e..4e42718d 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -122,6 +122,9 @@ definitions: type: string app_secret: type: string + required: + - app_code + - app_secret type: object handler.authByActionsRequest: properties: @@ -240,6 +243,9 @@ definitions: type: object type: type: string + required: + - data + - type type: object handler.deleteViaID: properties: @@ -249,6 +255,35 @@ definitions: required: - id type: object + handler.enginePolicyResponse: + properties: + action: + $ref: '#/definitions/handler.policyResponseAction' + type: object + expired_at: + example: 4102444800 + type: integer + expression: + additionalProperties: true + type: object + id: + example: 100 + type: integer + subject: + $ref: '#/definitions/handler.policyResponseSubject' + type: object + system: + example: bk_cmdb + type: string + template_id: + type: integer + updated_at: + example: 4102444800 + type: integer + version: + example: "1" + type: string + type: object handler.extResource: properties: ids: @@ -443,6 +478,7 @@ definitions: action_id: type: string environment: + description: 'NOTE: this field not used!' type: string expired_at: type: integer @@ -488,29 +524,14 @@ definitions: type: object handler.policyListResponse: properties: - count: - example: 120 - type: integer metadata: - $ref: '#/definitions/handler.policyListResponseMetadata' + $ref: '#/definitions/handler.listPolicySerializer' type: object results: items: - $ref: '#/definitions/handler.thinPolicyResponse' + $ref: '#/definitions/handler.enginePolicyResponse' type: array type: object - handler.policyListResponseMetadata: - properties: - action: - $ref: '#/definitions/handler.policyResponseAction' - type: object - system: - example: bk_test - type: string - timestamp: - example: 1592899208 - type: integer - type: object handler.policyResponseAction: properties: id: @@ -608,6 +629,21 @@ definitions: example: base_info,resource_types,actions type: string type: object + handler.referenceInstanceSelection: + properties: + id: + example: host_view + type: string + ignore_iam_path: + example: false + type: boolean + system_id: + example: bk_cmdb + type: string + required: + - id + - system_id + type: object handler.referenceResourceType: properties: id: @@ -620,6 +656,30 @@ definitions: - id - system_id type: object + handler.relatedResourceType: + properties: + id: + example: host + type: string + name_alias: + type: string + name_alias_en: + type: string + related_instance_selections: + items: + $ref: '#/definitions/handler.referenceInstanceSelection' + type: array + selection_mode: + description: '实例选择方式/范围: ["all", "instance", "attribute"]' + example: instance + type: string + system_id: + example: bk_cmdb + type: string + required: + - id + - system_id + type: object handler.resource: properties: attribute: @@ -710,6 +770,18 @@ definitions: example: 1 type: integer type: object + handler.responseSubject: + properties: + id: + example: admin + type: string + name: + example: Administer + type: string + type: + example: user + type: string + type: object handler.subject: properties: id: @@ -720,6 +792,10 @@ definitions: - id - type type: object + handler.subjectGroupsResponse: + items: + $ref: '#/definitions/handler.responseSubject' + type: array handler.subjectTemplateSerializer: properties: subject_id: @@ -847,24 +923,6 @@ definitions: $ref: '#/definitions/handler.systemProviderConfig' type: object type: object - handler.thinPolicyResponse: - properties: - expired_at: - example: 4102444800 - type: integer - expression: - additionalProperties: true - type: object - id: - example: 100 - type: integer - subject: - $ref: '#/definitions/handler.policyResponseSubject' - type: object - version: - example: "1" - type: string - type: object handler.tokenResponse: properties: token: @@ -876,6 +934,7 @@ definitions: action_id: type: string environment: + description: 'NOTE: this field not used!' type: string expired_at: type: integer @@ -997,18 +1056,22 @@ paths: description: list policies of ids or between min-max ids operationId: api-engine-policies-list parameters: - - in: query - name: timestamp + - example: 1,2,3 + in: query + name: ids + type: string + - example: 10001 + in: query + name: max_id type: integer - - in: query + - example: 1 + in: query name: min_id type: integer - - in: query - name: max_id + - example: 1592899208 + in: query + name: timestamp type: integer - - in: query - name: ids - type: string produces: - application/json responses: @@ -1038,10 +1101,12 @@ paths: description: list policy pks by condition operationId: api-engine-policy-id-list parameters: - - in: query + - example: 1592899208 + in: query name: begin_updated_at type: integer - - in: query + - example: 1592899208 + in: query name: end_updated_at type: integer produces: @@ -1073,7 +1138,8 @@ paths: description: get max policy id by condition operationId: api-engine-policy-id-max parameters: - - in: query + - example: 1592899208 + in: query name: updated_at type: integer produces: @@ -1126,6 +1192,79 @@ paths: summary: system info tags: - engine + /api/v1/open/departments/{user_id}/groups: + get: + consumes: + - application/json + description: get a department's groups + operationId: api-open-department-groups-get + parameters: + - description: Department ID + in: path + name: department_id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + headers: + X-Request-Id: + description: the request id + type: string + schema: + allOf: + - $ref: '#/definitions/util.Response' + - properties: + data: + $ref: '#/definitions/handler.subjectGroupsResponse' + type: object + security: + - AppCode: [] + - AppSecret: [] + summary: department groups + tags: + - open + /api/v1/open/users/{user_id}/groups: + get: + consumes: + - application/json + description: get a user's groups, include the inherit groups from department + operationId: api-open-user-groups-get + parameters: + - description: User ID + in: path + name: user_id + required: true + type: string + - description: get subject's inherit groups from it's departments + in: query + name: inherit + required: true + type: boolean + produces: + - application/json + responses: + "200": + description: OK + headers: + X-Request-Id: + description: the request id + type: string + schema: + allOf: + - $ref: '#/definitions/util.Response' + - properties: + data: + $ref: '#/definitions/handler.subjectGroupsResponse' + type: object + security: + - AppCode: [] + - AppSecret: [] + summary: user groups + tags: + - open /api/v1/policy/auth: post: consumes: @@ -1804,17 +1943,21 @@ paths: name: system_id required: true type: string - - in: query + - example: edit_host + in: query name: actionID required: true type: string - - in: query - name: pageSize - type: integer - - in: query + - example: 1 + in: query name: page type: integer - - in: query + - example: 100 + in: query + name: pageSize + type: integer + - example: 1592899208 + in: query name: timestamp type: integer produces: @@ -1851,7 +1994,8 @@ paths: name: system_id required: true type: string - - in: query + - example: 1,2,3 + in: query name: ids required: true type: string diff --git a/go.mod b/go.mod index 604a8082..f9c44e3f 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.16 require ( github.com/DATA-DOG/go-sqlmock v1.5.0 + github.com/TencentBlueKing/gopkg v1.0.7 github.com/TencentBlueKing/iam-go-sdk v0.0.5 github.com/agiledragon/gomonkey v2.0.2+incompatible github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 @@ -33,8 +34,8 @@ require ( github.com/lib/pq v1.8.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-isatty v0.0.13 // indirect - github.com/onsi/ginkgo v1.16.4 - github.com/onsi/gomega v1.14.0 + github.com/onsi/ginkgo v1.16.5 + github.com/onsi/gomega v1.17.0 github.com/parnurzeal/gorequest v0.2.16 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/prometheus/client_golang v1.11.0 diff --git a/go.sum b/go.sum index 0dcabfbf..05277db2 100644 --- a/go.sum +++ b/go.sum @@ -59,6 +59,8 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdko github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/TencentBlueKing/gopkg v1.0.7 h1:Kp/Qt8UtVb1LTr6ZSPXWTGS8KlHhWMqH4JZh7Jguu9E= +github.com/TencentBlueKing/gopkg v1.0.7/go.mod h1:oopRfRIMuG0IeZfNR99olKNx7976tYrRM1E1CY2y31M= github.com/TencentBlueKing/iam-go-sdk v0.0.5 h1:hliT6K1cwOFJNkzlGb5uBeAnUkxyBWzof6eD3UQVm0I= github.com/TencentBlueKing/iam-go-sdk v0.0.5/go.mod h1:5Z5YMclIZBokfgT9dTJFUcywygZXY9nhloX8rj+zFEc= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= @@ -511,15 +513,17 @@ github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg= -github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.4/go.mod h1:g/HbgYopi++010VEqkFgJHKC09uJiW9UkXvMUuKHUCQ= github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= -github.com/onsi/gomega v1.14.0 h1:ep6kpPVwmr/nTbklSx2nrLNSIO62DoYAhnPNIMhK8gI= github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= +github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= diff --git a/pkg/abac/pdp/entrance.go b/pkg/abac/pdp/entrance.go index d675eadf..fc92c608 100644 --- a/pkg/abac/pdp/entrance.go +++ b/pkg/abac/pdp/entrance.go @@ -15,13 +15,14 @@ import ( "errors" "fmt" + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/abac/pdp/evaluation" "iam/pkg/abac/pdp/translate" pdptypes "iam/pkg/abac/pdp/types" "iam/pkg/abac/types" "iam/pkg/abac/types/request" "iam/pkg/cacheimpls" - "iam/pkg/errorx" "iam/pkg/logging/debug" ) diff --git a/pkg/abac/pdp/evaluation/evaluation_test.go b/pkg/abac/pdp/evaluation/evaluation_test.go index 06a15449..dbc0a5a4 100644 --- a/pkg/abac/pdp/evaluation/evaluation_test.go +++ b/pkg/abac/pdp/evaluation/evaluation_test.go @@ -11,6 +11,7 @@ package evaluation import ( + "github.com/TencentBlueKing/gopkg/cache/memory" . "github.com/onsi/ginkgo" "github.com/stretchr/testify/assert" @@ -18,7 +19,6 @@ import ( pdptypes "iam/pkg/abac/pdp/types" "iam/pkg/abac/types" "iam/pkg/abac/types/request" - "iam/pkg/cache/memory" "iam/pkg/cacheimpls" ) diff --git a/pkg/abac/pdp/helper.go b/pkg/abac/pdp/helper.go index 2ea16975..8c4f5370 100644 --- a/pkg/abac/pdp/helper.go +++ b/pkg/abac/pdp/helper.go @@ -14,6 +14,8 @@ import ( "database/sql" "errors" + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/abac/pdp/condition" "iam/pkg/abac/pdp/evaluation" pdptypes "iam/pkg/abac/pdp/types" @@ -21,7 +23,6 @@ import ( "iam/pkg/abac/prp" "iam/pkg/abac/types" "iam/pkg/abac/types/request" - "iam/pkg/errorx" "iam/pkg/logging/debug" ) diff --git a/pkg/abac/pdp/remote.go b/pkg/abac/pdp/remote.go index f4c0eca6..2bba949e 100644 --- a/pkg/abac/pdp/remote.go +++ b/pkg/abac/pdp/remote.go @@ -13,13 +13,14 @@ package pdp import ( "strings" + "github.com/TencentBlueKing/gopkg/collection/set" + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/abac/pdp/condition" "iam/pkg/abac/pip" "iam/pkg/abac/types" "iam/pkg/abac/types/request" "iam/pkg/cacheimpls" - "iam/pkg/errorx" - "iam/pkg/util" ) func fillRemoteResourceAttrs(r *request.Request, policies []types.AuthPolicy) (err error) { @@ -96,7 +97,7 @@ func getConditionAttrKeys( ) []string { keyPrefix := resource.System + "." + resource.Type + "." - keySet := util.NewFixedLengthStringSet(len(conditions)) + keySet := set.NewFixedLengthStringSet(len(conditions)) for _, condition := range conditions { for _, key := range condition.GetKeys() { // NOTE: here remove all the prefix: {system}.{type}. diff --git a/pkg/abac/pdp/translate/translate.go b/pkg/abac/pdp/translate/translate.go index 7b97625c..deea0773 100644 --- a/pkg/abac/pdp/translate/translate.go +++ b/pkg/abac/pdp/translate/translate.go @@ -14,11 +14,11 @@ import ( "fmt" "strings" + "github.com/TencentBlueKing/gopkg/errorx" jsoniter "github.com/json-iterator/go" "iam/pkg/abac/pdp/condition" pdptypes "iam/pkg/abac/pdp/types" - "iam/pkg/errorx" ) /* diff --git a/pkg/abac/pip/action.go b/pkg/abac/pip/action.go index ccdc0419..89385fde 100644 --- a/pkg/abac/pip/action.go +++ b/pkg/abac/pip/action.go @@ -11,9 +11,10 @@ package pip import ( + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/abac/types" "iam/pkg/cacheimpls" - "iam/pkg/errorx" ) // ActionPIP ... diff --git a/pkg/abac/pip/resource.go b/pkg/abac/pip/resource.go index ab553eec..a265c3b3 100644 --- a/pkg/abac/pip/resource.go +++ b/pkg/abac/pip/resource.go @@ -11,8 +11,9 @@ package pip import ( + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/cacheimpls" - "iam/pkg/errorx" ) // ResourcePIP ... diff --git a/pkg/abac/pip/subject.go b/pkg/abac/pip/subject.go index a452f5c1..93ebb029 100644 --- a/pkg/abac/pip/subject.go +++ b/pkg/abac/pip/subject.go @@ -11,9 +11,10 @@ package pip import ( + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/abac/types" "iam/pkg/cacheimpls" - "iam/pkg/errorx" svctypes "iam/pkg/service/types" ) diff --git a/pkg/abac/prp/expression/database.go b/pkg/abac/prp/expression/database.go index 75352aa0..fb586fa8 100644 --- a/pkg/abac/prp/expression/database.go +++ b/pkg/abac/prp/expression/database.go @@ -11,9 +11,10 @@ package expression import ( + "github.com/TencentBlueKing/gopkg/collection/set" + "iam/pkg/service" "iam/pkg/service/types" - "iam/pkg/util" ) type databaseRetriever struct { @@ -38,7 +39,7 @@ func (r *databaseRetriever) retrieve(pks []int64) ([]types.AuthExpression, []int } func (r *databaseRetriever) getMissingPKs(fullPKs []int64, expressions []types.AuthExpression) []int64 { - gotPKSet := util.NewFixedLengthInt64Set(len(expressions)) + gotPKSet := set.NewFixedLengthInt64Set(len(expressions)) for _, e := range expressions { gotPKSet.Add(e.PK) } diff --git a/pkg/abac/prp/expression/memory_test.go b/pkg/abac/prp/expression/memory_test.go index 2c9d6649..4b796059 100644 --- a/pkg/abac/prp/expression/memory_test.go +++ b/pkg/abac/prp/expression/memory_test.go @@ -15,6 +15,7 @@ import ( "reflect" "time" + "github.com/TencentBlueKing/gopkg/cache" "github.com/agiledragon/gomonkey" rds "github.com/go-redis/redis/v8" . "github.com/onsi/ginkgo" @@ -22,7 +23,6 @@ import ( "github.com/stretchr/testify/assert" "iam/pkg/abac/prp/common" - "iam/pkg/cache" "iam/pkg/cache/redis" "iam/pkg/cacheimpls" "iam/pkg/service/types" diff --git a/pkg/abac/prp/expression/redis.go b/pkg/abac/prp/expression/redis.go index e1e2fea4..97c41b16 100644 --- a/pkg/abac/prp/expression/redis.go +++ b/pkg/abac/prp/expression/redis.go @@ -14,9 +14,10 @@ import ( "math/rand" "time" + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/conv" log "github.com/sirupsen/logrus" - "iam/pkg/cache" "iam/pkg/cache/redis" "iam/pkg/cacheimpls" "iam/pkg/service/types" @@ -51,7 +52,7 @@ func (r *redisRetriever) retrieve(pks []int64) ([]types.AuthExpression, []int64, emptyExpressionPKs := make([]int64, 0, len(pks)) for exprPK, exprStr := range hitExpressions { var expression types.AuthExpression - err = cacheimpls.ExpressionCache.Unmarshal(util.StringToBytes(exprStr), &expression) + err = cacheimpls.ExpressionCache.Unmarshal(conv.StringToBytes(exprStr), &expression) if err != nil { log.WithError(err).Errorf("[%s] parse string to expression fail expressionPKs=`%+v`", RedisLayer, pks) @@ -142,7 +143,7 @@ func (r *redisRetriever) batchSet(authExpressions map[int64]types.AuthExpression kvs = append(kvs, redis.KV{ Key: key.Key(), - Value: util.BytesToString(exprBytes), + Value: conv.BytesToString(exprBytes), }) } diff --git a/pkg/abac/prp/expression/redis_test.go b/pkg/abac/prp/expression/redis_test.go index 4080fffa..4416c1f8 100644 --- a/pkg/abac/prp/expression/redis_test.go +++ b/pkg/abac/prp/expression/redis_test.go @@ -15,12 +15,12 @@ import ( "reflect" "time" + "github.com/TencentBlueKing/gopkg/cache" "github.com/agiledragon/gomonkey" "github.com/golang/mock/gomock" . "github.com/onsi/ginkgo" "github.com/stretchr/testify/assert" - "iam/pkg/cache" "iam/pkg/cache/redis" "iam/pkg/cacheimpls" "iam/pkg/service/types" diff --git a/pkg/abac/prp/helper.go b/pkg/abac/prp/helper.go index 0777d756..7d9bc3ee 100644 --- a/pkg/abac/prp/helper.go +++ b/pkg/abac/prp/helper.go @@ -13,10 +13,11 @@ package prp import ( "time" + "github.com/TencentBlueKing/gopkg/collection/set" + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/abac/types" "iam/pkg/cacheimpls" - "iam/pkg/errorx" - "iam/pkg/util" ) /* @@ -52,7 +53,7 @@ func getEffectSubjectPKs(subject types.Subject) ([]int64, error) { // 用户继承组织加入的用户组 => 多个部门属于同一个组, 所以需要去重 now := time.Now().Unix() - inheritGroupPKSet := util.NewInt64Set() + inheritGroupPKSet := set.NewInt64Set() if len(deptPKs) > 0 { subjectGroups, newErr := cacheimpls.ListSubjectEffectGroups(deptPKs) if newErr != nil { @@ -70,7 +71,7 @@ func getEffectSubjectPKs(subject types.Subject) ([]int64, error) { // 1. merge `user-groupPKs` and `user-dept-groupPKs` groupPKMaxLen := len(groupPKs) + len(inheritGroupPKs) - groupPKSet := util.NewFixedLengthInt64Set(groupPKMaxLen) + groupPKSet := set.NewFixedLengthInt64Set(groupPKMaxLen) // 用户加入的用户组 groupPKSet.Append(groupPKs...) // 用户继承组织加入的用户组 diff --git a/pkg/abac/prp/policy/database.go b/pkg/abac/prp/policy/database.go index 68e830fe..eff4a0c9 100644 --- a/pkg/abac/prp/policy/database.go +++ b/pkg/abac/prp/policy/database.go @@ -11,9 +11,10 @@ package policy import ( + "github.com/TencentBlueKing/gopkg/collection/set" + "iam/pkg/service" "iam/pkg/service/types" - "iam/pkg/util" ) type databaseRetriever struct { @@ -39,7 +40,7 @@ func (r *databaseRetriever) retrieve(subjectPKs []int64) ([]types.AuthPolicy, [] } func (r *databaseRetriever) getMissingPKs(subjectPKs []int64, policies []types.AuthPolicy) []int64 { - gotSubjectPKSet := util.NewFixedLengthInt64Set(len(policies)) + gotSubjectPKSet := set.NewFixedLengthInt64Set(len(policies)) for _, e := range policies { gotSubjectPKSet.Add(e.SubjectPK) } diff --git a/pkg/abac/prp/policy/memory_test.go b/pkg/abac/prp/policy/memory_test.go index 8a39f0cb..c424d7ef 100644 --- a/pkg/abac/prp/policy/memory_test.go +++ b/pkg/abac/prp/policy/memory_test.go @@ -15,6 +15,7 @@ import ( "reflect" "time" + "github.com/TencentBlueKing/gopkg/cache" "github.com/agiledragon/gomonkey" rds "github.com/go-redis/redis/v8" "github.com/golang/mock/gomock" @@ -23,7 +24,6 @@ import ( "github.com/stretchr/testify/assert" "iam/pkg/abac/prp/common" - "iam/pkg/cache" "iam/pkg/cache/redis" "iam/pkg/cacheimpls" "iam/pkg/service" diff --git a/pkg/abac/prp/policy/redis.go b/pkg/abac/prp/policy/redis.go index 81880d47..d0b6b2f2 100644 --- a/pkg/abac/prp/policy/redis.go +++ b/pkg/abac/prp/policy/redis.go @@ -44,9 +44,10 @@ import ( "strings" "time" + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/conv" log "github.com/sirupsen/logrus" - "iam/pkg/cache" "iam/pkg/cache/redis" "iam/pkg/cacheimpls" "iam/pkg/service/types" @@ -109,7 +110,7 @@ func (r *redisRetriever) retrieve(subjectPKs []int64) ([]types.AuthPolicy, []int noPoliciesSubjectPKs := make([]int64, 0, len(subjectPKs)) for subjectPK, policiesStr := range hitPolicies { var ps []types.AuthPolicy - err = cacheimpls.PolicyCache.Unmarshal(util.StringToBytes(policiesStr), &ps) + err = cacheimpls.PolicyCache.Unmarshal(conv.StringToBytes(policiesStr), &ps) if err != nil { log.WithError(err).Errorf("[%s] parse string to expression fail system=`%s`, actionPK=`%d`, subjectPKs=`%+v`", RedisLayer, r.system, r.actionPK, subjectPKs) @@ -238,7 +239,7 @@ func (r *redisRetriever) batchSet(subjectPKPolicies map[int64][]types.AuthPolicy Key: key.Key(), Field: field, }, - Value: util.BytesToString(policiesBytes), + Value: conv.BytesToString(policiesBytes), }) // collect keys to set expire diff --git a/pkg/abac/prp/policy/redis_test.go b/pkg/abac/prp/policy/redis_test.go index 13e61f1a..35abf54b 100644 --- a/pkg/abac/prp/policy/redis_test.go +++ b/pkg/abac/prp/policy/redis_test.go @@ -15,11 +15,11 @@ import ( "reflect" "time" + "github.com/TencentBlueKing/gopkg/cache" "github.com/agiledragon/gomonkey" . "github.com/onsi/ginkgo" "github.com/stretchr/testify/assert" - "iam/pkg/cache" "iam/pkg/cache/redis" "iam/pkg/cacheimpls" "iam/pkg/service/types" diff --git a/pkg/abac/prp/policy_curd.go b/pkg/abac/prp/policy_curd.go index 0a2aae9d..71006287 100644 --- a/pkg/abac/prp/policy_curd.go +++ b/pkg/abac/prp/policy_curd.go @@ -13,12 +13,13 @@ package prp import ( "errors" + "github.com/TencentBlueKing/gopkg/collection/set" + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/abac/prp/expression" "iam/pkg/abac/prp/policy" "iam/pkg/abac/types" - "iam/pkg/errorx" svctypes "iam/pkg/service/types" - "iam/pkg/util" ) // NOTE: **important** / **重要** @@ -57,7 +58,7 @@ func convertToServicePolicies( func (m *policyManager) querySubjectActionForAlterPolicies( systemID, subjectType, subjectID string, -) (subjectPK int64, actionPKMap map[string]int64, actionPKWithResourceTypeSet *util.Int64Set, err error) { +) (subjectPK int64, actionPKMap map[string]int64, actionPKWithResourceTypeSet *set.Int64Set, err error) { errorWrapf := errorx.NewLayerFunctionErrorWrapf(PRP, "querySubjectActionForAlterPolicies") // 1. 查询 subject subjectPK @@ -85,7 +86,7 @@ func (m *policyManager) querySubjectActionForAlterPolicies( err = errorWrapf(err, "actionService.ListActionResourceTypeIDByActionSystem systemID=`%s` fail", systemID) return } - actionPKWithResourceTypeSet = util.NewInt64Set() + actionPKWithResourceTypeSet = set.NewInt64Set() for _, t := range actionResourceTypes { actionPKWithResourceTypeSet.Add(actionPKMap[t.ActionID]) } @@ -327,7 +328,7 @@ func (m *policyManager) UpdateSubjectPoliciesExpiredAt( return nil } -func (m *policyManager) queryPoliciesSystemSet(policies []svctypes.QueryPolicy) (*util.StringSet, error) { +func (m *policyManager) queryPoliciesSystemSet(policies []svctypes.QueryPolicy) (*set.StringSet, error) { errorWrapf := errorx.NewLayerFunctionErrorWrapf(PRP, "RenewExpiredAtByIDs") actionPKs := make([]int64, 0, len(policies)) @@ -343,7 +344,7 @@ func (m *policyManager) queryPoliciesSystemSet(policies []svctypes.QueryPolicy) } // 3. 得到涉及的系统id - systemSet := util.NewStringSet() + systemSet := set.NewStringSet() for _, ac := range actions { systemSet.Add(ac.System) } diff --git a/pkg/abac/prp/policy_list.go b/pkg/abac/prp/policy_list.go index 48c7254a..b79acfce 100644 --- a/pkg/abac/prp/policy_list.go +++ b/pkg/abac/prp/policy_list.go @@ -13,12 +13,13 @@ package prp import ( "fmt" + "github.com/TencentBlueKing/gopkg/collection/set" + "github.com/TencentBlueKing/gopkg/errorx" log "github.com/sirupsen/logrus" "iam/pkg/abac/prp/expression" "iam/pkg/abac/prp/policy" "iam/pkg/abac/types" - "iam/pkg/errorx" "iam/pkg/logging/debug" "iam/pkg/service" svctypes "iam/pkg/service/types" @@ -168,7 +169,7 @@ func (m *policyManager) ListBySubjectAction( // 4. expressionPK 去重 expressionPKs := make([]int64, 0, len(effectPolicies)) - expressionPKSet := util.NewFixedLengthInt64Set(len(effectPolicies)) + expressionPKSet := set.NewFixedLengthInt64Set(len(effectPolicies)) for _, p := range effectPolicies { if !expressionPKSet.Has(p.ExpressionPK) { expressionPKSet.Add(p.ExpressionPK) @@ -206,7 +207,7 @@ func (m *policyManager) ListBySubjectAction( } // NOTE: any 排在前面的逻辑去掉, 应该在计算或转换的时候处理合并 remove policy with `Any` first - signatureSet := util.NewFixedLengthStringSet(len(effectPolicies)) + signatureSet := set.NewFixedLengthStringSet(len(effectPolicies)) for _, p := range effectPolicies { expression := expressionMap[p.ExpressionPK] if !signatureSet.Has(expression.Signature) { diff --git a/pkg/abac/prp/policy_list_saas.go b/pkg/abac/prp/policy_list_saas.go index d0b7de91..1ae2c698 100644 --- a/pkg/abac/prp/policy_list_saas.go +++ b/pkg/abac/prp/policy_list_saas.go @@ -14,8 +14,9 @@ import ( "database/sql" "errors" + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/abac/types" - "iam/pkg/errorx" svcTypes "iam/pkg/service/types" ) diff --git a/pkg/abac/types/request/request.go b/pkg/abac/types/request/request.go index 20dc150e..3b375975 100644 --- a/pkg/abac/types/request/request.go +++ b/pkg/abac/types/request/request.go @@ -11,8 +11,9 @@ package request import ( + "github.com/TencentBlueKing/gopkg/collection/set" + "iam/pkg/abac/types" - "iam/pkg/util" ) /* @@ -83,8 +84,8 @@ func (r *Request) ValidateActionRemoteResource() bool { // 检查remote资源全覆盖, local资源部分覆盖 resourceTypes, _ := r.Action.Attribute.GetResourceTypes() - remoteTypeSet := util.NewStringSet() - localTypeSet := util.NewStringSet() + remoteTypeSet := set.NewStringSet() + localTypeSet := set.NewStringSet() for _, rt := range resourceTypes { if rt.System == r.System { localTypeSet.Add(rt.Type) @@ -111,9 +112,9 @@ func (r *Request) ValidateActionRemoteResource() bool { return remoteTypeSet.Size() == remoteCount } -func (r *Request) getActionResourceTypeIDSet() *util.StringSet { +func (r *Request) getActionResourceTypeIDSet() *set.StringSet { resourceTypes, _ := r.Action.Attribute.GetResourceTypes() - typeSet := util.NewStringSet() + typeSet := set.NewStringSet() for _, rt := range resourceTypes { key := r.genResourceTypeKey(rt.System, rt.Type) typeSet.Add(key) diff --git a/pkg/api/common/abac.go b/pkg/api/common/abac.go index b612f0ec..8735bf2f 100644 --- a/pkg/api/common/abac.go +++ b/pkg/api/common/abac.go @@ -11,15 +11,16 @@ package common import ( + "github.com/TencentBlueKing/gopkg/collection/set" + "iam/pkg/abac/pip" - "iam/pkg/util" ) // GetActionResourceTypeSet get resource type set for translate expression -func GetActionResourceTypeSet(systemID string, actionID string) (resourceTypeSet *util.StringSet, err error) { +func GetActionResourceTypeSet(systemID string, actionID string) (resourceTypeSet *set.StringSet, err error) { _, actionResourceTypes, err := pip.GetActionDetail(systemID, actionID) - resourceTypeSet = util.NewFixedLengthStringSet(len(actionResourceTypes)) + resourceTypeSet = set.NewFixedLengthStringSet(len(actionResourceTypes)) for _, rt := range actionResourceTypes { key := rt.System + ":" + rt.Type resourceTypeSet.Add(key) diff --git a/pkg/api/common/middleware.go b/pkg/api/common/middleware.go index 3b385b01..117b57ec 100644 --- a/pkg/api/common/middleware.go +++ b/pkg/api/common/middleware.go @@ -13,6 +13,7 @@ package common import ( "fmt" + "github.com/TencentBlueKing/gopkg/collection/set" "github.com/gin-gonic/gin" "iam/pkg/cacheimpls" @@ -68,7 +69,7 @@ func SystemExistsAndClientValid() gin.HandlerFunc { return } - validClients := util.SplitStringToSet(system.Clients, ",") + validClients := set.SplitStringToSet(system.Clients, ",") if !validClients.Has(clientID) { util.UnauthorizedJSONResponse(c, fmt.Sprintf("app(%s) is not allowed to call system (%s) api", clientID, systemID)) diff --git a/pkg/api/common/validation.go b/pkg/api/common/validation.go index bd8e040f..dc4d2aff 100644 --- a/pkg/api/common/validation.go +++ b/pkg/api/common/validation.go @@ -15,6 +15,7 @@ import ( "fmt" "regexp" + "github.com/TencentBlueKing/gopkg/conv" "github.com/gin-gonic/gin/binding" "iam/pkg/util" @@ -24,7 +25,7 @@ import ( // ValidateArray ... func ValidateArray(data interface{}) (bool, string) { - array, err := util.ToSlice(data) + array, err := conv.ToSlice(data) if err != nil { return false, err.Error() } diff --git a/pkg/api/debug/handler/query.go b/pkg/api/debug/handler/query.go index fc8751d0..c14dcd07 100644 --- a/pkg/api/debug/handler/query.go +++ b/pkg/api/debug/handler/query.go @@ -13,6 +13,7 @@ package handler import ( "errors" + "github.com/TencentBlueKing/gopkg/collection/set" "github.com/gin-gonic/gin" "iam/pkg/abac/pdp" @@ -34,7 +35,7 @@ func QueryModel(c *gin.Context) { fields := "base_info,resource_types,actions,action_groups,instance_selections,resource_creator_actions," + "common_actions,feature_shield_rules" - fieldSet := util.SplitStringToSet(fields, ",") + fieldSet := set.SplitStringToSet(fields, ",") modelHandler.BuildSystemInfoQueryResponse(c, systemID, fieldSet) } diff --git a/pkg/api/engine/handler/policy.go b/pkg/api/engine/handler/policy.go index 9296c84f..4629bd98 100644 --- a/pkg/api/engine/handler/policy.go +++ b/pkg/api/engine/handler/policy.go @@ -14,13 +14,13 @@ import ( "errors" "fmt" + "github.com/TencentBlueKing/gopkg/errorx" "github.com/gin-gonic/gin" log "github.com/sirupsen/logrus" "iam/pkg/abac/pdp/translate" "iam/pkg/abac/prp" "iam/pkg/cacheimpls" - "iam/pkg/errorx" "iam/pkg/service" "iam/pkg/service/types" "iam/pkg/util" diff --git a/pkg/api/engine/handler/system.go b/pkg/api/engine/handler/system.go index e4227732..6a2bf4e1 100644 --- a/pkg/api/engine/handler/system.go +++ b/pkg/api/engine/handler/system.go @@ -11,7 +11,8 @@ package handler import ( - "iam/pkg/errorx" + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/service" "iam/pkg/util" diff --git a/pkg/api/model/handler/action.go b/pkg/api/model/handler/action.go index 1ef744d2..48295dce 100644 --- a/pkg/api/model/handler/action.go +++ b/pkg/api/model/handler/action.go @@ -11,11 +11,12 @@ package handler import ( + "github.com/TencentBlueKing/gopkg/collection/set" + "github.com/TencentBlueKing/gopkg/errorx" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" "iam/pkg/cacheimpls" - "iam/pkg/errorx" "iam/pkg/service" svctypes "iam/pkg/service/types" "iam/pkg/util" @@ -328,7 +329,7 @@ func batchDeleteActions(c *gin.Context, systemID string, ids []string) { } // 从同步删除的操作里剔除掉需要异步删除的 - asyncDeletedIDs := util.NewStringSetWithValues(needAsyncDeletedActionIDs) + asyncDeletedIDs := set.NewStringSetWithValues(needAsyncDeletedActionIDs) newIDs := make([]string, 0, len(ids)) for _, id := range ids { if !asyncDeletedIDs.Has(id) { diff --git a/pkg/api/model/handler/action_slz.go b/pkg/api/model/handler/action_slz.go index cf0310ba..669c9180 100644 --- a/pkg/api/model/handler/action_slz.go +++ b/pkg/api/model/handler/action_slz.go @@ -13,6 +13,7 @@ package handler import ( "fmt" + "github.com/TencentBlueKing/gopkg/collection/set" "github.com/gin-gonic/gin/binding" "iam/pkg/api/common" @@ -111,7 +112,7 @@ func validateRelatedInstanceSelections(data []referenceInstanceSelection, action } func validateRelatedResourceTypes(data []relatedResourceType, actionID string) (bool, string) { - resourceTypeID := util.NewStringSet() + resourceTypeID := set.NewStringSet() for index, data := range data { if err := binding.Validator.ValidateStruct(data); err != nil { message := fmt.Sprintf("data of action_id=%s related_resource_types[%d], %s", @@ -176,9 +177,9 @@ func validateAction(body []actionSerializer) (bool, string) { } func validateActionsRepeat(actions []actionSerializer) error { - idSet := util.NewStringSet() - nameSet := util.NewStringSet() - nameEnSet := util.NewStringSet() + idSet := set.NewStringSet() + nameSet := set.NewStringSet() + nameEnSet := set.NewStringSet() for _, ac := range actions { if idSet.Has(ac.ID) { return fmt.Errorf("action id[%s] repeat", ac.ID) diff --git a/pkg/api/model/handler/instance_selection.go b/pkg/api/model/handler/instance_selection.go index 37f65868..28276db8 100644 --- a/pkg/api/model/handler/instance_selection.go +++ b/pkg/api/model/handler/instance_selection.go @@ -13,13 +13,13 @@ package handler import ( "fmt" + "github.com/TencentBlueKing/gopkg/errorx" "github.com/fatih/structs" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" "iam/pkg/api/common" "iam/pkg/cacheimpls" - "iam/pkg/errorx" "iam/pkg/service" svctypes "iam/pkg/service/types" "iam/pkg/util" diff --git a/pkg/api/model/handler/instance_selection_slz.go b/pkg/api/model/handler/instance_selection_slz.go index bbf2b9fc..a1ffd5c0 100644 --- a/pkg/api/model/handler/instance_selection_slz.go +++ b/pkg/api/model/handler/instance_selection_slz.go @@ -13,8 +13,9 @@ package handler import ( "fmt" + "github.com/TencentBlueKing/gopkg/collection/set" + "iam/pkg/api/common" - "iam/pkg/util" ) type instanceSelectionSerializer struct { @@ -61,9 +62,9 @@ func (r *instanceSelectionUpdateSerializer) validate(keys map[string]interface{} } func validateInstanceSelectionsRepeat(instanceSelections []instanceSelectionSerializer) error { - idSet := util.NewStringSet() - nameSet := util.NewStringSet() - nameEnSet := util.NewStringSet() + idSet := set.NewStringSet() + nameSet := set.NewStringSet() + nameEnSet := set.NewStringSet() for _, is := range instanceSelections { if idSet.Has(is.ID) { return fmt.Errorf("instance selection id[%s] repeat", is.ID) diff --git a/pkg/api/model/handler/policy.go b/pkg/api/model/handler/policy.go index 96cb9ff6..5c979f57 100644 --- a/pkg/api/model/handler/policy.go +++ b/pkg/api/model/handler/policy.go @@ -11,12 +11,12 @@ package handler import ( - "iam/pkg/errorx" + "github.com/TencentBlueKing/gopkg/errorx" + "github.com/gin-gonic/gin" + "iam/pkg/service" svctypes "iam/pkg/service/types" "iam/pkg/util" - - "github.com/gin-gonic/gin" ) // DeleteActionPolicies 并非实时删除,而是发起一个删除事件 diff --git a/pkg/api/model/handler/query.go b/pkg/api/model/handler/query.go index ab054f7a..531dfb44 100644 --- a/pkg/api/model/handler/query.go +++ b/pkg/api/model/handler/query.go @@ -11,9 +11,10 @@ package handler import ( + "github.com/TencentBlueKing/gopkg/collection/set" + "github.com/TencentBlueKing/gopkg/errorx" "github.com/gin-gonic/gin" - "iam/pkg/errorx" "iam/pkg/service" "iam/pkg/util" ) @@ -58,14 +59,14 @@ func SystemInfoQuery(c *gin.Context) { fields = "base_info,resource_types,actions,action_groups,instance_selections,resource_creator_actions," + "common_actions,feature_shield_rules" } - fieldSet := util.SplitStringToSet(fields, ",") + fieldSet := set.SplitStringToSet(fields, ",") BuildSystemInfoQueryResponse(c, systemID, fieldSet) } //nolint:gocognit // BuildSystemInfoQueryResponse will only the data requested -func BuildSystemInfoQueryResponse(c *gin.Context, systemID string, fieldSet *util.StringSet) { +func BuildSystemInfoQueryResponse(c *gin.Context, systemID string, fieldSet *set.StringSet) { // make the return data data := gin.H{} diff --git a/pkg/api/model/handler/resource_type.go b/pkg/api/model/handler/resource_type.go index 1e25cca6..0e2c73f1 100644 --- a/pkg/api/model/handler/resource_type.go +++ b/pkg/api/model/handler/resource_type.go @@ -13,13 +13,13 @@ package handler import ( "fmt" + "github.com/TencentBlueKing/gopkg/errorx" "github.com/fatih/structs" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" "iam/pkg/api/common" "iam/pkg/cacheimpls" - "iam/pkg/errorx" "iam/pkg/service" svctypes "iam/pkg/service/types" "iam/pkg/util" diff --git a/pkg/api/model/handler/resource_type_slz.go b/pkg/api/model/handler/resource_type_slz.go index 7819af00..750e10b0 100644 --- a/pkg/api/model/handler/resource_type_slz.go +++ b/pkg/api/model/handler/resource_type_slz.go @@ -13,8 +13,9 @@ package handler import ( "fmt" + "github.com/TencentBlueKing/gopkg/collection/set" + "iam/pkg/api/common" - "iam/pkg/util" ) type resourceProviderConfig struct { @@ -88,9 +89,9 @@ func (r *resourceTypeUpdateSerializer) validate(keys map[string]interface{}) (bo } func validateResourceTypesRepeat(resourceTypes []resourceTypeSerializer) error { - idSet := util.NewStringSet() - nameSet := util.NewStringSet() - nameEnSet := util.NewStringSet() + idSet := set.NewStringSet() + nameSet := set.NewStringSet() + nameEnSet := set.NewStringSet() for _, rt := range resourceTypes { if idSet.Has(rt.ID) { return fmt.Errorf("resource type id[%s] repeat", rt.ID) diff --git a/pkg/api/model/handler/system.go b/pkg/api/model/handler/system.go index 0d28098e..4ef62814 100644 --- a/pkg/api/model/handler/system.go +++ b/pkg/api/model/handler/system.go @@ -11,13 +11,14 @@ package handler import ( + "github.com/TencentBlueKing/gopkg/collection/set" + "github.com/TencentBlueKing/gopkg/errorx" "github.com/fatih/structs" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" "iam/pkg/api/common" "iam/pkg/cacheimpls" - "iam/pkg/errorx" "iam/pkg/service" svctypes "iam/pkg/service/types" "iam/pkg/util" @@ -28,7 +29,7 @@ func defaultValidClients(c *gin.Context, originClients string) string { clients := originClients clientID := util.GetClientID(c) - bodyClients := util.SplitStringToSet(clients, ",") + bodyClients := set.SplitStringToSet(clients, ",") if !bodyClients.Has(clientID) { bodyClients.Add(clientID) clients = bodyClients.ToString(",") @@ -268,7 +269,7 @@ func GetSystemClients(c *gin.Context) { // NOTE: v1版本的api, clients是string, 原因是为了保持和 get system返回结构体中字段一致 - // TODO: v2版, 两个接口都需要升级到 array => Clients: util.SplitStringToSet(system.Clients, ",").ToSlice(), + // TODO: v2版, 两个接口都需要升级到 array => Clients: set.SplitStringToSet(system.Clients, ",").ToSlice(), util.SuccessJSONResponse(c, "ok", systemClientsResponse{ Clients: system.Clients, diff --git a/pkg/api/model/handler/system_config.go b/pkg/api/model/handler/system_config.go index 67db9776..87c5b28f 100644 --- a/pkg/api/model/handler/system_config.go +++ b/pkg/api/model/handler/system_config.go @@ -14,9 +14,10 @@ import ( "errors" "fmt" + "github.com/TencentBlueKing/gopkg/collection/set" + "github.com/TencentBlueKing/gopkg/errorx" "github.com/gin-gonic/gin" - "iam/pkg/errorx" "iam/pkg/service" "iam/pkg/util" ) @@ -50,7 +51,7 @@ func CreateOrUpdateConfigDispatch(c *gin.Context) { systemID := c.Param("system_id") name := c.Param("name") - set := util.SplitStringToSet(AllowConfigNames, ",") + set := set.SplitStringToSet(AllowConfigNames, ",") if !set.Has(name) { util.BadRequestErrorJSONResponse(c, fmt.Sprintf("config `%s` is not supported yet", name)) return @@ -94,7 +95,7 @@ func actionGroupHandler(systemID string, c *gin.Context) { // 一个操作只能属于一个组(挂载到某个位置), 不允许挂载到多个位置, 即 全局uniq actionIDs := getAllFromActionGroupsActionIDs(body) - uniqActionIDs := util.NewStringSetWithValues(actionIDs) + uniqActionIDs := set.NewStringSetWithValues(actionIDs) if len(actionIDs) > uniqActionIDs.Size() { util.BadRequestErrorJSONResponse(c, "one action can only belong to one group") diff --git a/pkg/api/model/handler/token.go b/pkg/api/model/handler/token.go index a037e2c3..afbe5649 100644 --- a/pkg/api/model/handler/token.go +++ b/pkg/api/model/handler/token.go @@ -13,10 +13,10 @@ package handler import ( "errors" + "github.com/TencentBlueKing/gopkg/errorx" "github.com/gin-gonic/gin" "iam/pkg/cacheimpls" - "iam/pkg/errorx" "iam/pkg/util" ) diff --git a/pkg/api/open/handler/policies_get.go b/pkg/api/open/handler/policies_get.go index a2feee1b..ed13a057 100644 --- a/pkg/api/open/handler/policies_get.go +++ b/pkg/api/open/handler/policies_get.go @@ -22,7 +22,7 @@ import ( "iam/pkg/util" ) -// Get godoc +// PolicyGet godoc // @Summary policy get // @Description get a policy // @ID api-open-system-policies-get @@ -36,7 +36,7 @@ import ( // @Security AppCode // @Security AppSecret // @Router /api/v1/systems/{system_id}/policies/{policy_id} [get] -func Get(c *gin.Context) { +func PolicyGet(c *gin.Context) { var pathParams policyGetSerializer if err := c.ShouldBindUri(&pathParams); err != nil { util.BadRequestErrorJSONResponse(c, util.ValidationErrorMessage(err)) diff --git a/pkg/api/open/handler/policies_list.go b/pkg/api/open/handler/policies_list.go index e0189f4b..4d12fb0b 100644 --- a/pkg/api/open/handler/policies_list.go +++ b/pkg/api/open/handler/policies_list.go @@ -15,19 +15,19 @@ import ( "errors" "fmt" + "github.com/TencentBlueKing/gopkg/errorx" "github.com/gin-gonic/gin" log "github.com/sirupsen/logrus" "iam/pkg/abac/pdp/translate" "iam/pkg/abac/prp" "iam/pkg/cacheimpls" - "iam/pkg/errorx" "iam/pkg/service" "iam/pkg/service/types" "iam/pkg/util" ) -// List godoc +// PolicyList godoc // @Summary policy list // @Description list policies of a action // @ID api-open-system-policies-list @@ -41,7 +41,7 @@ import ( // @Security AppCode // @Security AppSecret // @Router /api/v1/systems/{system_id}/policies [get] -func List(c *gin.Context) { +func PolicyList(c *gin.Context) { // TODO: 翻页接口是否有性能问题 / 限制调用并发, 用drl var query listQuerySerializer if err := c.ShouldBindQuery(&query); err != nil { diff --git a/pkg/api/open/handler/policies_list_slz_test.go b/pkg/api/open/handler/policies_list_slz_test.go index 40d2738b..0fd368b8 100644 --- a/pkg/api/open/handler/policies_list_slz_test.go +++ b/pkg/api/open/handler/policies_list_slz_test.go @@ -13,9 +13,9 @@ package handler import ( "testing" - "iam/pkg/util" - "github.com/stretchr/testify/assert" + + "iam/pkg/util" ) func Test_listQuerySerializer_validate(t *testing.T) { diff --git a/pkg/api/open/handler/policies_query_subjects.go b/pkg/api/open/handler/policies_subjects.go similarity index 93% rename from pkg/api/open/handler/policies_query_subjects.go rename to pkg/api/open/handler/policies_subjects.go index de845d30..26bf5497 100644 --- a/pkg/api/open/handler/policies_query_subjects.go +++ b/pkg/api/open/handler/policies_subjects.go @@ -13,16 +13,16 @@ package handler import ( "fmt" + "github.com/TencentBlueKing/gopkg/errorx" "github.com/gin-gonic/gin" log "github.com/sirupsen/logrus" "iam/pkg/cacheimpls" - "iam/pkg/errorx" "iam/pkg/service" "iam/pkg/util" ) -// Subjects godoc +// PoliciesSubjects godoc // @Summary query subjects/获取有权限的用户列表 // @Description system+action+policy_ids => subjects // @ID api-open-system-policies-subjects @@ -36,8 +36,8 @@ import ( // @Security AppCode // @Security AppSecret // @Router /api/v1/systems/{system_id}/policies/-/subjects [get] -func Subjects(c *gin.Context) { - errorWrapf := errorx.NewLayerFunctionErrorWrapf("Handler", "policy_list.Subjects") +func PoliciesSubjects(c *gin.Context) { + errorWrapf := errorx.NewLayerFunctionErrorWrapf("Handler", "policy_list.PoliciesSubjects") // TODO: maybe add cache here; // key is subjectsSerializer md5 => value is []subjects; search from git history LocalPolicySubjectsCache @@ -84,7 +84,7 @@ func Subjects(c *gin.Context) { // if get subject fail, continue if err1 != nil { log.Info(errorWrapf(err1, - "policy_list.Subjects GetSubjectByPK subject_pk=`%d` fail", + "policy_list.PoliciesSubjects GetSubjectByPK subject_pk=`%d` fail", policy.SubjectPK)) continue diff --git a/pkg/api/open/handler/policies_query_subjects_slz.go b/pkg/api/open/handler/policies_subjects_slz.go similarity index 100% rename from pkg/api/open/handler/policies_query_subjects_slz.go rename to pkg/api/open/handler/policies_subjects_slz.go diff --git a/pkg/api/open/handler/subject_groups.go b/pkg/api/open/handler/subject_groups.go new file mode 100644 index 00000000..d39e72bc --- /dev/null +++ b/pkg/api/open/handler/subject_groups.go @@ -0,0 +1,144 @@ +/* + * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-权限中心(BlueKing-IAM) available. + * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. + * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://opensource.org/licenses/MIT + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package handler + +import ( + "database/sql" + "errors" + "strings" + "time" + + "github.com/TencentBlueKing/gopkg/collection/set" + "github.com/TencentBlueKing/gopkg/errorx" + "github.com/gin-gonic/gin" + log "github.com/sirupsen/logrus" + + "iam/pkg/cacheimpls" + "iam/pkg/service/types" + "iam/pkg/util" +) + +// UserGroups godoc +// @Summary user groups +// @Description get a user's groups, include the inherit groups from department +// @ID api-open-user-groups-get +// @Tags open +// @Accept json +// @Produce json +// @Param user_id path string true "User ID" +// @Param inherit query bool true "get subject's inherit groups from it's departments" +// @Success 200 {object} util.Response{data=subjectGroupsResponse} +// @Header 200 {string} X-Request-Id "the request id" +// @Security AppCode +// @Security AppSecret +// @Router /api/v1/open/users/{user_id}/groups [get] +func UserGroups(c *gin.Context) { + subjectID := c.Param("user_id") + + inherit := strings.ToLower(c.Query("inherit")) == "true" + + handleSubjectGroups(c, types.UserType, subjectID, inherit) +} + +// DepartmentGroups godoc +// @Summary department groups +// @Description get a department's groups +// @ID api-open-department-groups-get +// @Tags open +// @Accept json +// @Produce json +// @Param department_id path string true "Department ID" +// @Success 200 {object} util.Response{data=subjectGroupsResponse} +// @Header 200 {string} X-Request-Id "the request id" +// @Security AppCode +// @Security AppSecret +// @Router /api/v1/open/departments/{department_id}/groups [get] +func DepartmentGroups(c *gin.Context) { + subjectID := c.Param("department_id") + + handleSubjectGroups(c, types.DepartmentType, subjectID, false) +} + +func handleSubjectGroups(c *gin.Context, subjectType, subjectID string, inherit bool) { + errorWrapf := errorx.NewLayerFunctionErrorWrapf("Handler", "subject_groups") + + subjectPK, err := cacheimpls.GetLocalSubjectPK(subjectType, subjectID) + if err != nil { + // 不存在的情况, 404 + if errors.Is(err, sql.ErrNoRows) { + util.NotFoundJSONResponse(c, "subject not exist") + return + } + + util.SystemErrorJSONResponse(c, err) + return + } + + // NOTE: group被删除的时候, subjectDetail引用的并不会清理 + subjectDetail, err := cacheimpls.GetSubjectDetail(subjectPK) + if err != nil { + util.SystemErrorJSONResponse(c, err) + return + } + + nowUnix := time.Now().Unix() + + // 1. get the subject's groups + groups := subjectDetail.SubjectGroups + groupPKs := set.NewFixedLengthInt64Set(len(groups)) + for _, group := range groups { + // 仅仅在有效期内才需要 + if group.PolicyExpiredAt > nowUnix { + groupPKs.Add(group.PK) + } + } + + // 2. get the subject-department's groups + deptPKs := subjectDetail.DepartmentPKs + if inherit && len(deptPKs) > 0 { + subjectGroups, newErr := cacheimpls.ListSubjectEffectGroups(deptPKs) + if newErr != nil { + newErr = errorWrapf(newErr, "ListSubjectEffectGroups deptPKs=`%+v` fail", deptPKs) + util.SystemErrorJSONResponse(c, newErr) + return + } + for _, sg := range subjectGroups { + if sg.PolicyExpiredAt > nowUnix { + groupPKs.Add(sg.PK) + } + } + } + + // 3. build the response + data := subjectGroupsResponse{} + for _, pk := range groupPKs.ToSlice() { + // NOTE: 一个group 被删除, 可能 1min 之内, 还会出现在列表中 + subj, err := cacheimpls.GetSubjectByPK(pk) + if err != nil { + // no log + if errors.Is(err, sql.ErrNoRows) { + continue + } + + // get subject fail, continue + log.Info(errorWrapf(err, "subject_groups GetSubjectByPK subject_pk=`%d` fail", pk)) + continue + } + + data = append(data, responseSubject{ + Type: subj.Type, + ID: subj.ID, + Name: subj.Name, + }) + } + + util.SuccessJSONResponse(c, "ok", data) +} diff --git a/pkg/cache/types_test.go b/pkg/api/open/handler/subject_groups_slz.go similarity index 69% rename from pkg/cache/types_test.go rename to pkg/api/open/handler/subject_groups_slz.go index d00a2560..ca9c5975 100644 --- a/pkg/cache/types_test.go +++ b/pkg/api/open/handler/subject_groups_slz.go @@ -8,22 +8,12 @@ * specific language governing permissions and limitations under the License. */ -package cache +package handler -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestInt64Key(t *testing.T) { - k := NewInt64Key(int64(123)) - assert.NotNil(t, k) - assert.Equal(t, "123", k.Key()) +type responseSubject struct { + Type string `json:"type" example:"user"` + ID string `json:"id" example:"admin"` + Name string `json:"name" example:"Administer"` } -func TestStringKey(t *testing.T) { - k := NewStringKey("hello") - assert.NotNil(t, k) - assert.Equal(t, "hello", k.Key()) -} +type subjectGroupsResponse []responseSubject diff --git a/pkg/api/open/router.go b/pkg/api/open/router.go index a4a3c0ee..48f93999 100644 --- a/pkg/api/open/router.go +++ b/pkg/api/open/router.go @@ -17,19 +17,61 @@ import ( "iam/pkg/api/open/handler" ) -// Register the urls: /api/v1/systems -func Register(r *gin.RouterGroup) { +// RegisterLegacySystemAPIs the urls: /api/v1/systems +// NOTE: should not add more apis here, move to /api/v1/open/systems/{system_id}/ +func RegisterLegacySystemAPIs(r *gin.RouterGroup) { policies := r.Group("/:system_id/policies") policies.Use(common.SystemExistsAndClientValid()) { // GET /api/v1/systems/:system/policies?action=x 拉取某个操作的所有策略列表 - policies.GET("", handler.List) + policies.GET("", handler.PolicyList) // GET /api/v1/systems/:system/policies/:policy_id 查询某个策略详情(这个策略必须属于本系统) - policies.GET("/:policy_id", handler.Get) + policies.GET("/:policy_id", handler.PolicyGet) // https://cloud.google.com/apis/design/design_patterns#list_sub-collections // GET /api/v1/systems/:system/policies/-/subjects?ids=1,2,3,4 - policies.GET("/:policy_id/subjects", handler.Subjects) + policies.GET("/-/subjects", handler.PoliciesSubjects) + } +} + +// Register the urls: /api/v1/open +func Register(r *gin.RouterGroup) { + // 1. system scope /api/v1/open/systems + + // 1.1 policies + policies := r.Group("/systems/:system_id/policies") + policies.Use(common.SystemExistsAndClientValid()) + { + // GET /api/v1/open/systems/:system_id/policies?action=x 拉取某个操作的所有策略列表 + policies.GET("", handler.PolicyList) + + // GET /api/v1/open/systems/:system_id/policies/:policy_id 查询某个策略详情(这个策略必须属于本系统) + policies.GET("/:policy_id", handler.PolicyGet) + + // https://cloud.google.com/apis/design/design_patterns#list_sub-collections + // GET /api/v1/open/systems/:system_id/policies/-/subjects?ids=1,2,3,4 + policies.GET("/-/subjects", handler.PoliciesSubjects) + } + + // 2. subjects: users, departments, groups + users := r.Group("/users") + { + // GET /user/123/groups?inherit=true + users.GET("/:user_id/groups", handler.UserGroups) } + + departments := r.Group("/departments") + { + // GET /department/456/groups + departments.GET("/:department_id/groups", handler.DepartmentGroups) + } + + // 3. groups + // groups := r.Group("/groups") + // { + // groups.GET("/:group_id/members", handler.GroupMembers) + // groups.GET("/", handler.Groups) + // groups.GET("/:group_id", handler.GroupGet) + // } } diff --git a/pkg/api/open/router_test.go b/pkg/api/open/router_test.go index c0515431..9f1becb0 100644 --- a/pkg/api/open/router_test.go +++ b/pkg/api/open/router_test.go @@ -23,7 +23,7 @@ func TestRegister(t *testing.T) { r := util.SetupRouter() g := r.Group("/test") - Register(g) + RegisterLegacySystemAPIs(g) assert.NotNil(t, g) } diff --git a/pkg/api/policy/handler/auth.go b/pkg/api/policy/handler/auth.go index 2a37bb99..52b565db 100644 --- a/pkg/api/policy/handler/auth.go +++ b/pkg/api/policy/handler/auth.go @@ -13,6 +13,7 @@ package handler import ( "errors" + "github.com/TencentBlueKing/gopkg/errorx" "github.com/gin-gonic/gin" "iam/pkg/abac/pdp" @@ -21,7 +22,6 @@ import ( "iam/pkg/abac/types" "iam/pkg/abac/types/request" "iam/pkg/cacheimpls" - "iam/pkg/errorx" "iam/pkg/logging/debug" "iam/pkg/util" ) diff --git a/pkg/api/policy/handler/query.go b/pkg/api/policy/handler/query.go index 00532feb..84c6cb0d 100644 --- a/pkg/api/policy/handler/query.go +++ b/pkg/api/policy/handler/query.go @@ -13,12 +13,12 @@ package handler import ( "errors" + "github.com/TencentBlueKing/gopkg/errorx" "github.com/gin-gonic/gin" "iam/pkg/abac/pdp" "iam/pkg/abac/types" "iam/pkg/abac/types/request" - "iam/pkg/errorx" "iam/pkg/logging/debug" "iam/pkg/util" ) diff --git a/pkg/api/policy/handler/types.go b/pkg/api/policy/handler/types.go index e45028c2..08f476c9 100644 --- a/pkg/api/policy/handler/types.go +++ b/pkg/api/policy/handler/types.go @@ -13,8 +13,9 @@ package handler import ( "fmt" + "github.com/TencentBlueKing/gopkg/stringx" + "iam/pkg/api/common" - "iam/pkg/util" ) type subject struct { @@ -36,7 +37,7 @@ type resource struct { // UID ... func (r *resource) UID() string { s := fmt.Sprintf("%s:%s:%s:%v", r.System, r.Type, r.ID, r.Attribute) - return util.GetMD5Hash(s) + return stringx.MD5Hash(s) } // query for ext resources 附加查询的资源实例 diff --git a/pkg/api/policy/handler/util.go b/pkg/api/policy/handler/util.go index 66a54d30..06bdc152 100644 --- a/pkg/api/policy/handler/util.go +++ b/pkg/api/policy/handler/util.go @@ -14,11 +14,12 @@ import ( "fmt" "strings" + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/abac/types" "iam/pkg/abac/types/request" "iam/pkg/cacheimpls" "iam/pkg/config" - "iam/pkg/errorx" svctypes "iam/pkg/service/types" ) diff --git a/pkg/api/policy/handler/util_test.go b/pkg/api/policy/handler/util_test.go index 236553f4..34ecc818 100644 --- a/pkg/api/policy/handler/util_test.go +++ b/pkg/api/policy/handler/util_test.go @@ -15,14 +15,14 @@ import ( "testing" "time" + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/cache/memory" "github.com/agiledragon/gomonkey" . "github.com/onsi/ginkgo" "github.com/stretchr/testify/assert" "iam/pkg/abac/types" "iam/pkg/abac/types/request" - "iam/pkg/cache" - "iam/pkg/cache/memory" "iam/pkg/cacheimpls" "iam/pkg/config" ) diff --git a/pkg/api/web/handler/action.go b/pkg/api/web/handler/action.go index 5a36b5f9..47a44932 100644 --- a/pkg/api/web/handler/action.go +++ b/pkg/api/web/handler/action.go @@ -11,9 +11,10 @@ package handler import ( + "github.com/TencentBlueKing/gopkg/collection/set" + "github.com/TencentBlueKing/gopkg/errorx" "github.com/gin-gonic/gin" - "iam/pkg/errorx" "iam/pkg/service" "iam/pkg/util" ) @@ -64,7 +65,7 @@ func ListAction(c *gin.Context) { return } - set := util.SplitStringToSet(query.Fields, ",") + set := set.SplitStringToSet(query.Fields, ",") actions := make([]map[string]interface{}, 0, len(allActions)) for _, action := range allActions { ac, err := filterFields(set, action) diff --git a/pkg/api/web/handler/instance_selection.go b/pkg/api/web/handler/instance_selection.go index 37eb4e03..3be6162d 100644 --- a/pkg/api/web/handler/instance_selection.go +++ b/pkg/api/web/handler/instance_selection.go @@ -11,9 +11,9 @@ package handler import ( + "github.com/TencentBlueKing/gopkg/errorx" "github.com/gin-gonic/gin" - "iam/pkg/errorx" "iam/pkg/service" "iam/pkg/util" ) diff --git a/pkg/api/web/handler/model_change_event.go b/pkg/api/web/handler/model_change_event.go index 8fffd123..7d512e94 100644 --- a/pkg/api/web/handler/model_change_event.go +++ b/pkg/api/web/handler/model_change_event.go @@ -13,7 +13,8 @@ package handler import ( "github.com/gin-gonic/gin" - "iam/pkg/errorx" + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/service" "iam/pkg/util" ) diff --git a/pkg/api/web/handler/perm_template.go b/pkg/api/web/handler/perm_template.go index 3bc6a2d2..daa274ce 100644 --- a/pkg/api/web/handler/perm_template.go +++ b/pkg/api/web/handler/perm_template.go @@ -11,11 +11,11 @@ package handler import ( + "github.com/TencentBlueKing/gopkg/errorx" "github.com/gin-gonic/gin" "iam/pkg/abac/prp" "iam/pkg/abac/types" - "iam/pkg/errorx" "iam/pkg/util" ) diff --git a/pkg/api/web/handler/policy.go b/pkg/api/web/handler/policy.go index 1d57374a..61079f94 100644 --- a/pkg/api/web/handler/policy.go +++ b/pkg/api/web/handler/policy.go @@ -11,11 +11,11 @@ package handler import ( + "github.com/TencentBlueKing/gopkg/errorx" "github.com/gin-gonic/gin" "iam/pkg/abac/prp" "iam/pkg/abac/types" - "iam/pkg/errorx" "iam/pkg/service" "iam/pkg/util" ) diff --git a/pkg/api/web/handler/resource_type.go b/pkg/api/web/handler/resource_type.go index c4e2a5c7..f3d0da50 100644 --- a/pkg/api/web/handler/resource_type.go +++ b/pkg/api/web/handler/resource_type.go @@ -11,9 +11,9 @@ package handler import ( + "github.com/TencentBlueKing/gopkg/errorx" "github.com/gin-gonic/gin" - "iam/pkg/errorx" "iam/pkg/service" "iam/pkg/util" ) diff --git a/pkg/api/web/handler/resource_type_slz.go b/pkg/api/web/handler/resource_type_slz.go index 4af5a4aa..104de66a 100644 --- a/pkg/api/web/handler/resource_type_slz.go +++ b/pkg/api/web/handler/resource_type_slz.go @@ -13,7 +13,7 @@ package handler import ( "strings" - "iam/pkg/util" + "github.com/TencentBlueKing/gopkg/collection/set" ) const ( @@ -43,6 +43,6 @@ func (s *resourceTypeSerializer) systems() []string { return strings.Split(s.Systems, ",") } -func (s *resourceTypeSerializer) fieldsSet() *util.StringSet { - return util.SplitStringToSet(s.Fields, ",") +func (s *resourceTypeSerializer) fieldsSet() *set.StringSet { + return set.SplitStringToSet(s.Fields, ",") } diff --git a/pkg/api/web/handler/subject.go b/pkg/api/web/handler/subject.go index 436249c1..cb950fa4 100644 --- a/pkg/api/web/handler/subject.go +++ b/pkg/api/web/handler/subject.go @@ -13,6 +13,8 @@ package handler import ( "fmt" + "github.com/TencentBlueKing/gopkg/collection/set" + "github.com/TencentBlueKing/gopkg/errorx" "github.com/gin-gonic/gin" "github.com/jinzhu/copier" log "github.com/sirupsen/logrus" @@ -20,7 +22,6 @@ import ( pl "iam/pkg/abac/prp/policy" "iam/pkg/api/common" "iam/pkg/cacheimpls" - "iam/pkg/errorx" "iam/pkg/service" "iam/pkg/service/types" "iam/pkg/util" @@ -368,7 +369,7 @@ func BatchAddSubjectMembers(c *gin.Context) { types.DepartmentType: 0, } - bodyMembers := util.NewStringSet() // 用于去重 + bodyMembers := set.NewStringSet() // 用于去重 for _, m := range body.Members { key := fmt.Sprintf("%s:%s", m.Type, m.ID) diff --git a/pkg/api/web/handler/system.go b/pkg/api/web/handler/system.go index 62f365c5..093344fd 100644 --- a/pkg/api/web/handler/system.go +++ b/pkg/api/web/handler/system.go @@ -11,9 +11,10 @@ package handler import ( + "github.com/TencentBlueKing/gopkg/collection/set" + "github.com/TencentBlueKing/gopkg/errorx" "github.com/gin-gonic/gin" - "iam/pkg/errorx" "iam/pkg/service" "iam/pkg/util" ) @@ -42,7 +43,7 @@ func ListSystem(c *gin.Context) { return } - set := util.SplitStringToSet(query.Fields, ",") + set := set.SplitStringToSet(query.Fields, ",") systems := make([]map[string]interface{}, 0, len(allSystems)) for _, system := range allSystems { systemInfo, err := filterFields(set, system) @@ -84,7 +85,7 @@ func GetSystem(c *gin.Context) { return } - set := util.SplitStringToSet(query.Fields, ",") + set := set.SplitStringToSet(query.Fields, ",") data, err := filterFields(set, systemInfo) if err != nil { err = errorWrapf(err, "filterFields systemID=`%s`, set=`%+v` fail", systemID, set) diff --git a/pkg/api/web/handler/util.go b/pkg/api/web/handler/util.go index 20937e02..ea13ba40 100644 --- a/pkg/api/web/handler/util.go +++ b/pkg/api/web/handler/util.go @@ -11,9 +11,8 @@ package handler import ( + "github.com/TencentBlueKing/gopkg/collection/set" jsoniter "github.com/json-iterator/go" - - "iam/pkg/util" ) func validateFields(expected, actual string) bool { @@ -22,12 +21,12 @@ func validateFields(expected, actual string) bool { } // 求差集 - s := util.SplitStringToSet(actual, ",").Diff(util.SplitStringToSet(expected, ",")) + s := set.SplitStringToSet(actual, ",").Diff(set.SplitStringToSet(expected, ",")) return s.Size() == 0 } // 过滤预期字段 -func filterFields(set *util.StringSet, obj interface{}) (data map[string]interface{}, err error) { +func filterFields(set *set.StringSet, obj interface{}) (data map[string]interface{}, err error) { // 1. json.Marshal to json var m []byte m, err = jsoniter.Marshal(obj) diff --git a/pkg/api/web/handler/util_test.go b/pkg/api/web/handler/util_test.go index 30f11ae9..a6eabbd3 100644 --- a/pkg/api/web/handler/util_test.go +++ b/pkg/api/web/handler/util_test.go @@ -13,9 +13,8 @@ package handler import ( "testing" + "github.com/TencentBlueKing/gopkg/collection/set" "github.com/stretchr/testify/assert" - - "iam/pkg/util" ) func Test_validateFields(t *testing.T) { @@ -78,7 +77,7 @@ func Test_filterFields(t *testing.T) { ID: "1", Name: "test", } - fields := util.NewStringSetWithValues([]string{"id"}) + fields := set.NewStringSetWithValues([]string{"id"}) // one want1 := map[string]interface{}{"id": "1"} @@ -88,20 +87,20 @@ func Test_filterFields(t *testing.T) { // all want2 := map[string]interface{}{"id": "1", "name": "test"} - fields = util.NewStringSetWithValues([]string{"id", "name"}) + fields = set.NewStringSetWithValues([]string{"id", "name"}) got2, err := filterFields(fields, obj) assert.NoError(t, err) assert.Equal(t, want2, got2) // not exists want3 := map[string]interface{}{} - fields = util.NewStringSetWithValues([]string{"age"}) + fields = set.NewStringSetWithValues([]string{"age"}) got3, err := filterFields(fields, obj) assert.NoError(t, err) assert.Equal(t, want3, got3) // empty set - fields = util.NewStringSetWithValues([]string{}) + fields = set.NewStringSetWithValues([]string{}) got4, err := filterFields(fields, obj) assert.NoError(t, err) assert.Equal(t, want3, got4) diff --git a/pkg/cache/cleaner/init.go b/pkg/cache/cleaner/init.go index 25bfa4a4..872896c1 100644 --- a/pkg/cache/cleaner/init.go +++ b/pkg/cache/cleaner/init.go @@ -13,11 +13,10 @@ package cleaner import ( "context" - "iam/pkg/util" - + "github.com/TencentBlueKing/gopkg/cache" log "github.com/sirupsen/logrus" - "iam/pkg/cache" + "iam/pkg/util" ) // it's a goroutine diff --git a/pkg/cache/memory/backend/memory_test.go b/pkg/cache/memory/backend/memory_test.go deleted file mode 100644 index 7a042b5f..00000000 --- a/pkg/cache/memory/backend/memory_test.go +++ /dev/null @@ -1,43 +0,0 @@ -/* - * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-权限中心(BlueKing-IAM) available. - * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. - * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at http://opensource.org/licenses/MIT - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package backend - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestNewTTLCache(t *testing.T) { - c := newTTLCache(5*time.Second, 10*time.Second) - assert.NotNil(t, c) - - c = newTTLCache(5*time.Second, 0*time.Second) - assert.NotNil(t, c) -} - -func TestMemoryBackend(t *testing.T) { - be := NewMemoryBackend("test", 5*time.Second, nil) - assert.NotNil(t, be) - - _, found := be.Get("not_exists") - assert.False(t, found) - - be.Set("hello", "world", time.Duration(0)) - value, found := be.Get("hello") - assert.True(t, found) - assert.Equal(t, "world", value) - - be.Delete("hello") - _, found = be.Get("hello") - assert.False(t, found) -} diff --git a/pkg/cache/memory/base_cache.go b/pkg/cache/memory/base_cache.go deleted file mode 100644 index 91872612..00000000 --- a/pkg/cache/memory/base_cache.go +++ /dev/null @@ -1,187 +0,0 @@ -/* - * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-权限中心(BlueKing-IAM) available. - * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. - * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at http://opensource.org/licenses/MIT - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package memory - -import ( - "fmt" - "time" - - "golang.org/x/sync/singleflight" - - "iam/pkg/cache" - "iam/pkg/cache/memory/backend" -) - -// EmptyCacheExpiration ... -const EmptyCacheExpiration = 5 * time.Second - -// BaseCache ... -type BaseCache struct { - backend backend.Backend - - disabled bool - retrieveFunc RetrieveFunc - g singleflight.Group -} - -// EmptyCache is a place holder for the missing key -type EmptyCache struct { - err error -} - -// Exists ... -func (c *BaseCache) Exists(key cache.Key) bool { - k := key.Key() - - _, ok := c.backend.Get(k) - return ok -} - -// Get will get the key from cache, if missing, will call the retrieveFunc to get the data, add to cache, then return -func (c *BaseCache) Get(key cache.Key) (interface{}, error) { - // 1. if cache is disabled, fetch and return - if c.disabled { - value, err := c.retrieveFunc(key) - if err != nil { - return nil, err - } - - return value, nil - } - - k := key.Key() - - // 2. get from cache - value, ok := c.backend.Get(k) - if ok { - // if retrieve fail from retrieveFunc - if emptyCache, isEmptyCache := value.(EmptyCache); isEmptyCache { - return nil, emptyCache.err - } - - return value, nil - } - - // 3. if not exists in cache, retrieve it - return c.doRetrieve(key) -} - -func (c *BaseCache) doRetrieve(k cache.Key) (interface{}, error) { - key := k.Key() - - // 3.2 fetch - value, err, _ := c.g.Do(key, func() (interface{}, error) { - return c.retrieveFunc(k) - }) - - if err != nil { - // ! if error, cache it too, make it short enough(5s) - c.backend.Set(key, EmptyCache{err: err}, EmptyCacheExpiration) - return nil, err - } - - // 4. set value to cache, use default expiration - c.backend.Set(key, value, 0) - - return value, nil -} - -// Set ... -func (c *BaseCache) Set(key cache.Key, data interface{}) { - k := key.Key() - c.backend.Set(k, data, 0) -} - -// TODO: 这里需要实现所有类型的 GetXXXX - -// ! if retrieve fail, will return ("", err) for expire time -func (c *BaseCache) GetString(k cache.Key) (string, error) { - value, err := c.Get(k) - if err != nil { - return "", err - } - - v, ok := value.(string) - if !ok { - return "", fmt.Errorf("not a string value. key=%s, value=%v(%T)", k.Key(), value, value) - } - return v, nil -} - -// GetBool ... -func (c *BaseCache) GetBool(k cache.Key) (bool, error) { - value, err := c.Get(k) - if err != nil { - return false, err - } - - v, ok := value.(bool) - if !ok { - return false, fmt.Errorf("not a string value. key=%s, value=%v(%T)", k.Key(), value, value) - } - return v, nil -} - -// GetInt64 ... -func (c *BaseCache) GetInt64(k cache.Key) (int64, error) { - value, err := c.Get(k) - if err != nil { - return 0, err - } - - v, ok := value.(int64) - if !ok { - return 0, fmt.Errorf("not a int64 value. key=%s, value=%v(%T)", k.Key(), value, value) - } - return v, nil -} - -var defaultZeroTime = time.Time{} - -// GetTime ... -func (c *BaseCache) GetTime(k cache.Key) (time.Time, error) { - value, err := c.Get(k) - if err != nil { - return defaultZeroTime, err - } - - v, ok := value.(time.Time) - if !ok { - return defaultZeroTime, fmt.Errorf("not a string value. key=%s, value=%v(%T)", k.Key(), value, value) - } - return v, nil -} - -// Delete ... -func (c *BaseCache) Delete(key cache.Key) error { - k := key.Key() - return c.backend.Delete(k) -} - -// DirectGet will get key from cache, without calling the retrieveFunc -func (c *BaseCache) DirectGet(key cache.Key) (interface{}, bool) { - k := key.Key() - return c.backend.Get(k) -} - -// Disabled ... -func (c *BaseCache) Disabled() bool { - return c.disabled -} - -// NewBaseCache ... -func NewBaseCache(disabled bool, retrieveFunc RetrieveFunc, backend backend.Backend) Cache { - return &BaseCache{ - backend: backend, - disabled: disabled, - retrieveFunc: retrieveFunc, - } -} diff --git a/pkg/cache/memory/base_cache_test.go b/pkg/cache/memory/base_cache_test.go deleted file mode 100644 index fdd3cec5..00000000 --- a/pkg/cache/memory/base_cache_test.go +++ /dev/null @@ -1,223 +0,0 @@ -/* - * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-权限中心(BlueKing-IAM) available. - * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. - * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at http://opensource.org/licenses/MIT - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package memory - -import ( - "errors" - "testing" - "time" - - "iam/pkg/cache" - - "github.com/stretchr/testify/assert" - "golang.org/x/sync/singleflight" - - "iam/pkg/cache/memory/backend" -) - -func retrieveTest(k cache.Key) (interface{}, error) { - kStr := k.Key() - switch kStr { - case "a": - return "1", nil - case "b": - return "2", nil - case "error": - return nil, errors.New("error") - case "bool": - return true, nil - case "time": - return time.Time{}, nil - default: - return "", nil - } -} - -func retrieveError(k cache.Key) (interface{}, error) { - return nil, errors.New("test error") -} - -func TestNewBaseCache(t *testing.T) { - expiration := 5 * time.Minute - - be := backend.NewMemoryBackend("test", expiration, nil) - - c := NewBaseCache(false, retrieveTest, be) - - // Disabled - assert.False(t, c.Disabled()) - - // get from cache - aKey := cache.NewStringKey("a") - x, err := c.Get(aKey) - assert.NoError(t, err) - assert.Equal(t, "1", x.(string)) - - x, err = c.Get(aKey) - assert.NoError(t, err) - assert.Equal(t, "1", x.(string)) - - assert.True(t, c.Exists(aKey)) - - _, ok := c.DirectGet(aKey) - assert.True(t, ok) - - // get string - x, err = c.GetString(aKey) - assert.NoError(t, err) - assert.Equal(t, "1", x) - - // get bool - boolKey := cache.NewStringKey("bool") - x, err = c.GetBool(boolKey) - assert.NoError(t, err) - assert.Equal(t, true, x.(bool)) - - // get time - timeKey := cache.NewStringKey("time") - x, err = c.GetTime(timeKey) - assert.NoError(t, err) - assert.IsType(t, time.Time{}, x) - - // get fail - errorKey := cache.NewStringKey("error") - x, err = c.Get(errorKey) - assert.Error(t, err) - assert.Nil(t, x) - - err1 := err - - // get fail twice - x, err = c.Get(errorKey) - assert.Error(t, err) - assert.Nil(t, x) - - err2 := err - - // the error should be the same - assert.Equal(t, err1, err2) - - x, err = c.GetString(errorKey) - assert.Error(t, err) - assert.Equal(t, "", x) - - // delete - delKey := cache.NewStringKey("a") - x, err = c.Get(delKey) - assert.NoError(t, err) - assert.Equal(t, "1", x.(string)) - - err = c.Delete(delKey) - assert.NoError(t, err) - assert.False(t, c.Exists(delKey)) - - _, ok = c.DirectGet(delKey) - assert.False(t, ok) - - // set - setKey := cache.NewStringKey("s") - c.Set(setKey, "1") - x, err = c.GetString(setKey) - assert.NoError(t, err) - assert.Equal(t, "1", x) - - // x, err = c.Get(delKey) - // assert.Error(t, err) - - // disabled=true - // c = NewCache("test", true, retrieveOK, expiration, cleanupInterval) - c = NewBaseCache(true, retrieveTest, be) - assert.NotNil(t, c) - x, err = c.Get(aKey) - assert.NoError(t, err) - assert.Equal(t, "1", x.(string)) - - _, err = c.GetString(timeKey) - assert.Error(t, err) - - _, err = c.GetBool(aKey) - assert.Error(t, err) - - _, err = c.GetTime(aKey) - assert.Error(t, err) - - // retrieveError - c = NewBaseCache(true, retrieveError, be) - assert.NotNil(t, c) - _, err = c.Get(aKey) - assert.Error(t, err) - - _, err = c.GetString(timeKey) - assert.Error(t, err) - assert.Equal(t, "test error", err.Error()) - - _, err = c.GetBool(aKey) - assert.Error(t, err) - assert.Equal(t, "test error", err.Error()) - - _, err = c.GetTime(aKey) - assert.Error(t, err) - assert.Equal(t, "test error", err.Error()) - - // TODO: mock the backend first Get fail, second Get ok - - // TODO: add emptyCache here -} - -func retrieveBenchmark(k cache.Key) (interface{}, error) { - return "", nil -} - -func BenchmarkRawRetrieve(b *testing.B) { - var keys []cache.StringKey - for i := 0; i < 100000; i++ { - // keys = append(keys, cache.NewStringKey(util.RandString(5))) - keys = append(keys, cache.NewStringKey("aaa")) - } - - b.ResetTimer() - b.ReportAllocs() - - index := 0 - for i := 0; i < b.N; i++ { - key := keys[index] - index++ - if index > 99999 { - index = 0 - } - retrieveBenchmark(key) - } -} - -func BenchmarkSingleFlightRetrieve(b *testing.B) { - var keys []cache.StringKey - for i := 0; i < 100000; i++ { - // keys = append(keys, cache.NewStringKey(util.RandString(5))) - keys = append(keys, cache.NewStringKey("aaa")) - } - - b.ResetTimer() - b.ReportAllocs() - - var g singleflight.Group - index := 0 - for i := 0; i < b.N; i++ { - key := keys[index] - index++ - if index == 99999 { - index = 0 - } - - g.Do(key.Key(), func() (interface{}, error) { - return retrieveBenchmark(key) - }) - } -} diff --git a/pkg/cache/redis/redis.go b/pkg/cache/redis/redis.go index 95bd898b..79b71e37 100644 --- a/pkg/cache/redis/redis.go +++ b/pkg/cache/redis/redis.go @@ -16,13 +16,13 @@ import ( "strconv" "time" + gopkgcache "github.com/TencentBlueKing/gopkg/cache" "github.com/go-redis/cache/v8" "github.com/go-redis/redis/v8" log "github.com/sirupsen/logrus" "github.com/vmihailenco/msgpack/v5" "golang.org/x/sync/singleflight" - iamcache "iam/pkg/cache" "iam/pkg/util" ) @@ -39,7 +39,7 @@ const ( ) // RetrieveFunc ... -type RetrieveFunc func(key iamcache.Key) (interface{}, error) +type RetrieveFunc func(key gopkgcache.Key) (interface{}, error) // Cache is a cache implements type Cache struct { @@ -106,7 +106,7 @@ func (c *Cache) copyTo(source interface{}, dest interface{}) error { } // Set execute `set` -func (c *Cache) Set(key iamcache.Key, value interface{}, duration time.Duration) error { +func (c *Cache) Set(key gopkgcache.Key, value interface{}, duration time.Duration) error { if duration == time.Duration(0) { duration = c.defaultExpiration } @@ -120,13 +120,13 @@ func (c *Cache) Set(key iamcache.Key, value interface{}, duration time.Duration) } // Get execute `get` -func (c *Cache) Get(key iamcache.Key, value interface{}) error { +func (c *Cache) Get(key gopkgcache.Key, value interface{}) error { k := c.genKey(key.Key()) return c.codec.Get(context.TODO(), k, value) } // Exists execute `exists` -func (c *Cache) Exists(key iamcache.Key) bool { +func (c *Cache) Exists(key gopkgcache.Key) bool { k := c.genKey(key.Key()) count, err := c.cli.Exists(context.TODO(), k).Result() @@ -135,7 +135,7 @@ func (c *Cache) Exists(key iamcache.Key) bool { } // GetInto will retrieve the data from cache and unmarshal into the obj -func (c *Cache) GetInto(key iamcache.Key, obj interface{}, retrieveFunc RetrieveFunc) (err error) { +func (c *Cache) GetInto(key gopkgcache.Key, obj interface{}, retrieveFunc RetrieveFunc) (err error) { // 1. get from cache, hit, return err = c.Get(key, obj) if err == nil { @@ -167,7 +167,7 @@ func (c *Cache) GetInto(key iamcache.Key, obj interface{}, retrieveFunc Retrieve } // Delete execute `del` -func (c *Cache) Delete(key iamcache.Key) (err error) { +func (c *Cache) Delete(key gopkgcache.Key) (err error) { k := c.genKey(key.Key()) ctx := context.TODO() @@ -177,7 +177,7 @@ func (c *Cache) Delete(key iamcache.Key) (err error) { } // BatchDelete execute `del` with pipeline -func (c *Cache) BatchDelete(keys []iamcache.Key) error { +func (c *Cache) BatchDelete(keys []gopkgcache.Key) error { newKeys := make([]string, 0, len(keys)) for _, key := range keys { newKeys = append(newKeys, c.genKey(key.Key())) @@ -200,7 +200,7 @@ func (c *Cache) BatchDelete(keys []iamcache.Key) error { } // BatchExpireWithTx execute `expire` with tx pipeline -func (c *Cache) BatchExpireWithTx(keys []iamcache.Key, expiration time.Duration) error { +func (c *Cache) BatchExpireWithTx(keys []gopkgcache.Key, expiration time.Duration) error { pipe := c.cli.TxPipeline() ctx := context.TODO() @@ -220,12 +220,12 @@ type KV struct { } // BatchGet execute `get` with pipeline -func (c *Cache) BatchGet(keys []iamcache.Key) (map[iamcache.Key]string, error) { +func (c *Cache) BatchGet(keys []gopkgcache.Key) (map[gopkgcache.Key]string, error) { pipe := c.cli.Pipeline() ctx := context.TODO() - cmds := map[iamcache.Key]*redis.StringCmd{} + cmds := map[gopkgcache.Key]*redis.StringCmd{} for _, k := range keys { key := c.genKey(k.Key()) cmd := pipe.Get(ctx, key) @@ -240,7 +240,7 @@ func (c *Cache) BatchGet(keys []iamcache.Key) (map[iamcache.Key]string, error) { return nil, err } - values := make(map[iamcache.Key]string, len(cmds)) + values := make(map[gopkgcache.Key]string, len(cmds)) for hkf, cmd := range cmds { // maybe err or key missing // only return the HashKeyField who get value success from redis diff --git a/pkg/cache/redis/redis_test.go b/pkg/cache/redis/redis_test.go index 5e04bbca..a3a0421b 100644 --- a/pkg/cache/redis/redis_test.go +++ b/pkg/cache/redis/redis_test.go @@ -15,11 +15,10 @@ import ( "testing" "time" + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/conv" "github.com/stretchr/testify/assert" "github.com/vmihailenco/msgpack/v5" - - "iam/pkg/cache" - "iam/pkg/util" ) func TestCache_genKey(t *testing.T) { @@ -169,7 +168,7 @@ func TestBatchExpireWithTx(t *testing.T) { // assert.Equal(t, "1", data) // } -//func TestBatchHSetWithTx_and_BatchHGet(t *testing.T) { +// func TestBatchHSetWithTx_and_BatchHGet(t *testing.T) { // c := NewMockCache("test", 5*time.Minute) // // hs := []Hash{ @@ -215,7 +214,7 @@ func TestBatchExpireWithTx(t *testing.T) { // // assert.Contains(t, data, keyField1) // assert.Equal(t, "1", data[keyField1]) -//} +// } func TestBatchSetWithTx_and_BatchGet(t *testing.T) { c := NewMockCache("test", 5*time.Minute) @@ -276,12 +275,12 @@ func TestSetOneAndBatchGet(t *testing.T) { fmt.Println("value", value) var abc Abc - err = c.Unmarshal(util.StringToBytes(value), &abc) + err = c.Unmarshal(conv.StringToBytes(value), &abc) fmt.Println("abc:", abc) assert.NoError(t, err) var def Abc - err = msgpack.Unmarshal(util.StringToBytes(value), &def) + err = msgpack.Unmarshal(conv.StringToBytes(value), &def) fmt.Println("def:", abc) assert.NoError(t, err) }) @@ -302,12 +301,12 @@ func TestSetOneAndBatchGet(t *testing.T) { fmt.Println("value", value) var abc Abc - err = c.Unmarshal(util.StringToBytes(value), &abc) + err = c.Unmarshal(conv.StringToBytes(value), &abc) fmt.Println("abc:", abc) assert.NoError(t, err) var def Abc - err = msgpack.Unmarshal(util.StringToBytes(value), &def) + err = msgpack.Unmarshal(conv.StringToBytes(value), &def) fmt.Println("def:", abc) assert.Error(t, err) }) @@ -336,11 +335,11 @@ func TestBatchSetAndGet(t *testing.T) { kvs := []KV{ { Key: "a", - Value: util.BytesToString(small), + Value: conv.BytesToString(small), }, { Key: "b", - Value: util.BytesToString(huge), + Value: conv.BytesToString(huge), }, } diff --git a/pkg/cacheimpls/action_detail.go b/pkg/cacheimpls/action_detail.go index f76c4fea..dcaf44a9 100644 --- a/pkg/cacheimpls/action_detail.go +++ b/pkg/cacheimpls/action_detail.go @@ -11,8 +11,9 @@ package cacheimpls import ( - "iam/pkg/cache" - "iam/pkg/errorx" + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/service" "iam/pkg/service/types" ) diff --git a/pkg/cacheimpls/action_pk.go b/pkg/cacheimpls/action_pk.go index d2c55a69..5fbd101f 100644 --- a/pkg/cacheimpls/action_pk.go +++ b/pkg/cacheimpls/action_pk.go @@ -11,8 +11,9 @@ package cacheimpls import ( - "iam/pkg/cache" - "iam/pkg/errorx" + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/service" ) diff --git a/pkg/cacheimpls/deleter.go b/pkg/cacheimpls/deleter.go index 6ffe2189..66737906 100644 --- a/pkg/cacheimpls/deleter.go +++ b/pkg/cacheimpls/deleter.go @@ -11,9 +11,8 @@ package cacheimpls import ( + "github.com/TencentBlueKing/gopkg/cache" "go.uber.org/multierr" - - "iam/pkg/cache" ) // NOTE: action diff --git a/pkg/cacheimpls/init.go b/pkg/cacheimpls/init.go index 7027815b..294df534 100644 --- a/pkg/cacheimpls/init.go +++ b/pkg/cacheimpls/init.go @@ -15,12 +15,12 @@ import ( "math/rand" "time" + "github.com/TencentBlueKing/gopkg/cache/memory" + "github.com/TencentBlueKing/gopkg/cache/memory/backend" gocache "github.com/patrickmn/go-cache" log "github.com/sirupsen/logrus" "iam/pkg/cache/cleaner" - "iam/pkg/cache/memory" - "iam/pkg/cache/memory/backend" "iam/pkg/cache/redis" ) @@ -64,7 +64,7 @@ var ( // ErrNotExceptedTypeFromCache ... var ErrNotExceptedTypeFromCache = errors.New("not expected type from cache") -func newRandomDuration(seconds int) backend.RandomExpirationDurationFunc { +func newRandomDuration(seconds int) backend.RandomExtraExpirationDurationFunc { return func() time.Duration { return time.Duration(rand.Intn(seconds*1000)) * time.Millisecond } diff --git a/pkg/cacheimpls/local_action.go b/pkg/cacheimpls/local_action.go index f19f656c..6ced7a10 100644 --- a/pkg/cacheimpls/local_action.go +++ b/pkg/cacheimpls/local_action.go @@ -14,7 +14,8 @@ import ( "errors" "strconv" - "iam/pkg/cache" + "github.com/TencentBlueKing/gopkg/cache" + "iam/pkg/service" "iam/pkg/service/types" ) diff --git a/pkg/cacheimpls/local_action_test.go b/pkg/cacheimpls/local_action_test.go index f5e0f5de..7155f551 100644 --- a/pkg/cacheimpls/local_action_test.go +++ b/pkg/cacheimpls/local_action_test.go @@ -15,10 +15,10 @@ import ( "testing" "time" + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/cache/memory" "github.com/stretchr/testify/assert" - "iam/pkg/cache" - "iam/pkg/cache/memory" svctypes "iam/pkg/service/types" ) diff --git a/pkg/cacheimpls/local_apigw_jwt_client_id.go b/pkg/cacheimpls/local_apigw_jwt_client_id.go index f884ec02..2f8f8e66 100644 --- a/pkg/cacheimpls/local_apigw_jwt_client_id.go +++ b/pkg/cacheimpls/local_apigw_jwt_client_id.go @@ -13,8 +13,8 @@ package cacheimpls import ( "errors" - "iam/pkg/cache" - "iam/pkg/util" + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/stringx" ) // APIGatewayJWTClientIDCacheKey cache key for JWTToken @@ -24,7 +24,7 @@ type APIGatewayJWTClientIDCacheKey struct { // Key ... func (k APIGatewayJWTClientIDCacheKey) Key() string { - return util.GetMD5Hash(k.JWTToken) + return stringx.MD5Hash(k.JWTToken) } func retrieveAPIGatewayJWTClientID(key cache.Key) (interface{}, error) { diff --git a/pkg/cacheimpls/local_apigw_jwt_client_id_test.go b/pkg/cacheimpls/local_apigw_jwt_client_id_test.go index 2a527a60..85c4206b 100644 --- a/pkg/cacheimpls/local_apigw_jwt_client_id_test.go +++ b/pkg/cacheimpls/local_apigw_jwt_client_id_test.go @@ -13,11 +13,10 @@ package cacheimpls import ( "time" + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/cache/memory" . "github.com/onsi/ginkgo" "github.com/stretchr/testify/assert" - - "iam/pkg/cache" - "iam/pkg/cache/memory" ) var _ = Describe("LocalApigwJwtClientId", func() { diff --git a/pkg/cacheimpls/local_app_code_secret.go b/pkg/cacheimpls/local_app_code_secret.go index 372d5b1b..055ef850 100644 --- a/pkg/cacheimpls/local_app_code_secret.go +++ b/pkg/cacheimpls/local_app_code_secret.go @@ -11,10 +11,10 @@ package cacheimpls import ( - "iam/pkg/cache" - "iam/pkg/database/edao" - + "github.com/TencentBlueKing/gopkg/cache" log "github.com/sirupsen/logrus" + + "iam/pkg/database/edao" ) // AppCodeAppSecretCacheKey ... diff --git a/pkg/cacheimpls/local_app_code_secret_test.go b/pkg/cacheimpls/local_app_code_secret_test.go index 6b7b00d4..5434ec36 100644 --- a/pkg/cacheimpls/local_app_code_secret_test.go +++ b/pkg/cacheimpls/local_app_code_secret_test.go @@ -15,9 +15,8 @@ import ( "testing" "time" - "iam/pkg/cache" - "iam/pkg/cache/memory" - + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/cache/memory" "github.com/stretchr/testify/assert" ) diff --git a/pkg/cacheimpls/local_remote_resource_list.go b/pkg/cacheimpls/local_remote_resource_list.go index 25e33e46..dbc9dbb1 100644 --- a/pkg/cacheimpls/local_remote_resource_list.go +++ b/pkg/cacheimpls/local_remote_resource_list.go @@ -15,10 +15,11 @@ import ( "sort" "strings" - "iam/pkg/cache" + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/errorx" + "github.com/TencentBlueKing/gopkg/stringx" + "iam/pkg/component" - "iam/pkg/errorx" - "iam/pkg/util" ) // RemoteResourceListCacheKey ... @@ -34,7 +35,7 @@ type RemoteResourceListCacheKey struct { // Key ... func (k RemoteResourceListCacheKey) Key() string { key := k.System + ":" + k.Type + ":" + k.IDs + ":" + k.Fields - return util.GetMD5Hash(key) + return stringx.MD5Hash(key) } func retrieveRemoteResourceList(k cache.Key) (interface{}, error) { diff --git a/pkg/cacheimpls/local_remote_resource_list_test.go b/pkg/cacheimpls/local_remote_resource_list_test.go index 07bd83ea..a1bbe833 100644 --- a/pkg/cacheimpls/local_remote_resource_list_test.go +++ b/pkg/cacheimpls/local_remote_resource_list_test.go @@ -15,11 +15,10 @@ import ( "testing" "time" + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/cache/memory" + "github.com/TencentBlueKing/gopkg/stringx" "github.com/stretchr/testify/assert" - - "iam/pkg/cache" - "iam/pkg/cache/memory" - "iam/pkg/util" ) func TestRemoteResourceListCacheKey_Key(t *testing.T) { @@ -30,7 +29,7 @@ func TestRemoteResourceListCacheKey_Key(t *testing.T) { Fields: "id;name", } - assert.Equal(t, util.GetMD5Hash("test:host:1,2:id;name"), k.Key()) + assert.Equal(t, stringx.MD5Hash("test:host:1,2:id;name"), k.Key()) } func TestListRemoteResources(t *testing.T) { diff --git a/pkg/cacheimpls/local_subject.go b/pkg/cacheimpls/local_subject.go index af0663a1..ef903def 100644 --- a/pkg/cacheimpls/local_subject.go +++ b/pkg/cacheimpls/local_subject.go @@ -13,7 +13,8 @@ package cacheimpls import ( "errors" - "iam/pkg/cache" + "github.com/TencentBlueKing/gopkg/cache" + "iam/pkg/service" "iam/pkg/service/types" ) diff --git a/pkg/cacheimpls/local_subject_pk.go b/pkg/cacheimpls/local_subject_pk.go index 2b75e624..8af93da8 100644 --- a/pkg/cacheimpls/local_subject_pk.go +++ b/pkg/cacheimpls/local_subject_pk.go @@ -11,8 +11,8 @@ package cacheimpls import ( - "iam/pkg/cache" - "iam/pkg/errorx" + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/errorx" ) func retrieveSubjectPKFromRedis(key cache.Key) (interface{}, error) { diff --git a/pkg/cacheimpls/local_subject_role.go b/pkg/cacheimpls/local_subject_role.go index acabcef0..2244838b 100644 --- a/pkg/cacheimpls/local_subject_role.go +++ b/pkg/cacheimpls/local_subject_role.go @@ -14,8 +14,9 @@ import ( "database/sql" "errors" - "iam/pkg/cache" - "iam/pkg/errorx" + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/service" ) diff --git a/pkg/cacheimpls/local_subject_role_test.go b/pkg/cacheimpls/local_subject_role_test.go index a278cac6..b5c81364 100644 --- a/pkg/cacheimpls/local_subject_role_test.go +++ b/pkg/cacheimpls/local_subject_role_test.go @@ -15,9 +15,8 @@ import ( "testing" "time" - "iam/pkg/cache" - "iam/pkg/cache/memory" - + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/cache/memory" "github.com/stretchr/testify/assert" ) diff --git a/pkg/cacheimpls/local_subject_test.go b/pkg/cacheimpls/local_subject_test.go index 3023348a..d25006d2 100644 --- a/pkg/cacheimpls/local_subject_test.go +++ b/pkg/cacheimpls/local_subject_test.go @@ -15,10 +15,10 @@ import ( "testing" "time" + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/cache/memory" "github.com/stretchr/testify/assert" - "iam/pkg/cache" - "iam/pkg/cache/memory" svctypes "iam/pkg/service/types" ) diff --git a/pkg/cacheimpls/local_system_clients.go b/pkg/cacheimpls/local_system_clients.go index 3cad5a41..34745dff 100644 --- a/pkg/cacheimpls/local_system_clients.go +++ b/pkg/cacheimpls/local_system_clients.go @@ -14,8 +14,9 @@ import ( "errors" "strings" - "iam/pkg/cache" - "iam/pkg/errorx" + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/service" ) diff --git a/pkg/cacheimpls/local_system_clients_test.go b/pkg/cacheimpls/local_system_clients_test.go index a4001484..41f2ced1 100644 --- a/pkg/cacheimpls/local_system_clients_test.go +++ b/pkg/cacheimpls/local_system_clients_test.go @@ -15,9 +15,8 @@ import ( "testing" "time" - "iam/pkg/cache" - "iam/pkg/cache/memory" - + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/cache/memory" "github.com/stretchr/testify/assert" ) diff --git a/pkg/cacheimpls/local_unmarshaled_expression.go b/pkg/cacheimpls/local_unmarshaled_expression.go index 1ce892c1..39c2c0ea 100644 --- a/pkg/cacheimpls/local_unmarshaled_expression.go +++ b/pkg/cacheimpls/local_unmarshaled_expression.go @@ -13,12 +13,12 @@ package cacheimpls import ( "errors" + "github.com/TencentBlueKing/gopkg/cache" "github.com/sirupsen/logrus" "iam/pkg/abac/pdp/condition" "iam/pkg/abac/pdp/translate" "iam/pkg/abac/types" - "iam/pkg/cache" ) // ResourceExpressionCacheKey is the key for a policy expression, signature is unique diff --git a/pkg/cacheimpls/remote_resource.go b/pkg/cacheimpls/remote_resource.go index 421f628a..bfea31e1 100644 --- a/pkg/cacheimpls/remote_resource.go +++ b/pkg/cacheimpls/remote_resource.go @@ -14,9 +14,9 @@ import ( "sort" "strings" - "iam/pkg/cache" - "iam/pkg/errorx" - "iam/pkg/util" + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/errorx" + "github.com/TencentBlueKing/gopkg/stringx" ) // RemoteResourceCacheKey ... @@ -31,7 +31,7 @@ type RemoteResourceCacheKey struct { // Key ... func (k RemoteResourceCacheKey) Key() string { key := k.System + ":" + k.Type + ":" + k.ID + ":" + k.Fields - return util.GetMD5Hash(key) + return stringx.MD5Hash(key) } func retrieveRemoteResource(k cache.Key) (interface{}, error) { diff --git a/pkg/cacheimpls/remote_resource_test.go b/pkg/cacheimpls/remote_resource_test.go index 95c68e78..693fe590 100644 --- a/pkg/cacheimpls/remote_resource_test.go +++ b/pkg/cacheimpls/remote_resource_test.go @@ -14,15 +14,15 @@ import ( "testing" "time" - "iam/pkg/cache" + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/stringx" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "iam/pkg/cache/redis" "iam/pkg/component" "iam/pkg/component/mock" "iam/pkg/service/types" - "iam/pkg/util" - - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/assert" ) func TestRemoteResourceCacheKey_Key(t *testing.T) { @@ -33,7 +33,7 @@ func TestRemoteResourceCacheKey_Key(t *testing.T) { Fields: "id;name", } - assert.Equal(t, util.GetMD5Hash("test:host:1:id;name"), k.Key()) + assert.Equal(t, stringx.MD5Hash("test:host:1:id;name"), k.Key()) } func TestGetCMDBResource(t *testing.T) { diff --git a/pkg/cacheimpls/resource_type.go b/pkg/cacheimpls/resource_type.go index 6e1131c7..3d991c53 100644 --- a/pkg/cacheimpls/resource_type.go +++ b/pkg/cacheimpls/resource_type.go @@ -11,8 +11,9 @@ package cacheimpls import ( - "iam/pkg/cache" - "iam/pkg/errorx" + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/service" "iam/pkg/service/types" ) diff --git a/pkg/cacheimpls/resource_type_test.go b/pkg/cacheimpls/resource_type_test.go index 2dca404b..3e5bc023 100644 --- a/pkg/cacheimpls/resource_type_test.go +++ b/pkg/cacheimpls/resource_type_test.go @@ -14,14 +14,14 @@ import ( "testing" "time" + "github.com/agiledragon/gomonkey" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "iam/pkg/cache/redis" "iam/pkg/service" "iam/pkg/service/mock" svctypes "iam/pkg/service/types" - - "github.com/agiledragon/gomonkey" - "github.com/golang/mock/gomock" - "github.com/stretchr/testify/assert" ) func TestResourceTypeCacheKey_Key(t *testing.T) { diff --git a/pkg/cacheimpls/subject_detail.go b/pkg/cacheimpls/subject_detail.go index 96405129..f39e6940 100644 --- a/pkg/cacheimpls/subject_detail.go +++ b/pkg/cacheimpls/subject_detail.go @@ -11,8 +11,9 @@ package cacheimpls import ( - "iam/pkg/cache" - "iam/pkg/errorx" + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/service" "iam/pkg/service/types" ) diff --git a/pkg/cacheimpls/subject_group.go b/pkg/cacheimpls/subject_group.go index fdffaeb6..4c6d4202 100644 --- a/pkg/cacheimpls/subject_group.go +++ b/pkg/cacheimpls/subject_group.go @@ -11,13 +11,14 @@ package cacheimpls import ( + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/collection/set" + "github.com/TencentBlueKing/gopkg/conv" + "github.com/TencentBlueKing/gopkg/errorx" log "github.com/sirupsen/logrus" - "iam/pkg/cache" - "iam/pkg/errorx" "iam/pkg/service" "iam/pkg/service/types" - "iam/pkg/util" ) func retrieveSubjectGroups(key cache.Key) (interface{}, error) { @@ -97,7 +98,7 @@ func batchGetSubjectGroups(pks []int64) (subjectGroups []types.ThinSubjectGroup, if data, ok := hitCacheResults[key]; ok { // do unmarshal var sg []types.ThinSubjectGroup - err = SubjectGroupCache.Unmarshal(util.StringToBytes(data), &sg) + err = SubjectGroupCache.Unmarshal(conv.StringToBytes(data), &sg) if err != nil { err = errorWrapf(err, "unmarshal text in cache into SubjectGroup fail", "") return @@ -112,7 +113,7 @@ func batchGetSubjectGroups(pks []int64) (subjectGroups []types.ThinSubjectGroup, } func setMissing(notCachedSubjectGroups map[int64][]types.ThinSubjectGroup, missingPKs []int64) { - hasGroupPKs := util.NewFixedLengthInt64Set(len(notCachedSubjectGroups)) + hasGroupPKs := set.NewFixedLengthInt64Set(len(notCachedSubjectGroups)) // 3. set to cache for pk, sgs := range notCachedSubjectGroups { hasGroupPKs.Add(pk) diff --git a/pkg/cacheimpls/subject_group_test.go b/pkg/cacheimpls/subject_group_test.go index 5149059b..b679baa0 100644 --- a/pkg/cacheimpls/subject_group_test.go +++ b/pkg/cacheimpls/subject_group_test.go @@ -15,17 +15,17 @@ import ( "reflect" "time" + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/conv" "github.com/agiledragon/gomonkey" "github.com/golang/mock/gomock" . "github.com/onsi/ginkgo" "github.com/stretchr/testify/assert" - "iam/pkg/cache" "iam/pkg/cache/redis" "iam/pkg/service" "iam/pkg/service/mock" "iam/pkg/service/types" - "iam/pkg/util" ) var _ = Describe("SubjectGroups", func() { @@ -246,7 +246,7 @@ var _ = Describe("SubjectGroups", func() { }) return map[cache.Key]string{ - SubjectPKCacheKey{PK: 2}: util.BytesToString(bs), + SubjectPKCacheKey{PK: 2}: conv.BytesToString(bs), }, nil }) defer patches.Reset() diff --git a/pkg/cacheimpls/subject_pk.go b/pkg/cacheimpls/subject_pk.go index f9e1c6b8..b744960d 100644 --- a/pkg/cacheimpls/subject_pk.go +++ b/pkg/cacheimpls/subject_pk.go @@ -11,8 +11,9 @@ package cacheimpls import ( - "iam/pkg/cache" - "iam/pkg/errorx" + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/service" ) diff --git a/pkg/cacheimpls/subject_pk_test.go b/pkg/cacheimpls/subject_pk_test.go index 1497ae8b..2b5ecc8d 100644 --- a/pkg/cacheimpls/subject_pk_test.go +++ b/pkg/cacheimpls/subject_pk_test.go @@ -14,13 +14,13 @@ import ( "testing" "time" - "iam/pkg/cache/redis" - "iam/pkg/service" - "iam/pkg/service/mock" - "github.com/agiledragon/gomonkey" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" + + "iam/pkg/cache/redis" + "iam/pkg/service" + "iam/pkg/service/mock" ) func TestSubjectIDCacheKey_Key(t *testing.T) { diff --git a/pkg/cacheimpls/system.go b/pkg/cacheimpls/system.go index af0e36e5..b9bd7db1 100644 --- a/pkg/cacheimpls/system.go +++ b/pkg/cacheimpls/system.go @@ -11,8 +11,9 @@ package cacheimpls import ( - "iam/pkg/cache" - "iam/pkg/errorx" + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/service" "iam/pkg/service/types" ) diff --git a/pkg/component/helper.go b/pkg/component/helper.go index 70889c23..655ecf70 100644 --- a/pkg/component/helper.go +++ b/pkg/component/helper.go @@ -11,10 +11,12 @@ package component import ( - "iam/pkg/errorx" + "strings" + + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/service/types" "iam/pkg/util" - "strings" ) // PrepareRequest ... diff --git a/pkg/component/remote_resource.go b/pkg/component/remote_resource.go index 4c2f1287..be517ff2 100644 --- a/pkg/component/remote_resource.go +++ b/pkg/component/remote_resource.go @@ -19,9 +19,8 @@ import ( "regexp" "time" + "github.com/TencentBlueKing/gopkg/errorx" "github.com/parnurzeal/gorequest" - - "iam/pkg/errorx" ) // RemoteResourceTimeout ... diff --git a/pkg/config/init.go b/pkg/config/init.go index ce2b0a4d..38d028f6 100644 --- a/pkg/config/init.go +++ b/pkg/config/init.go @@ -11,24 +11,24 @@ package config import ( - "iam/pkg/util" + "github.com/TencentBlueKing/gopkg/collection/set" ) // SuperAppCodeSet ... var ( - SuperAppCodeSet *util.StringSet - SuperUserSet *util.StringSet - SupportShieldFeaturesSet *util.StringSet + SuperAppCodeSet *set.StringSet + SuperUserSet *set.StringSet + SupportShieldFeaturesSet *set.StringSet ) // InitSuperAppCode ... func InitSuperAppCode(superAppCode string) { - SuperAppCodeSet = util.SplitStringToSet(superAppCode, ",") + SuperAppCodeSet = set.SplitStringToSet(superAppCode, ",") } // InitSuperUser ... func InitSuperUser(users string) { - SuperUserSet = util.SplitStringToSet(users, ",") + SuperUserSet = set.SplitStringToSet(users, ",") if !SuperUserSet.Has("admin") { SuperUserSet.Add("admin") @@ -37,7 +37,7 @@ func InitSuperUser(users string) { // InitSupportShieldFeatures ... func InitSupportShieldFeatures(supportShieldFeatures []string) { - SupportShieldFeaturesSet = util.NewStringSetWithValues(supportShieldFeatures) + SupportShieldFeaturesSet = set.NewStringSetWithValues(supportShieldFeatures) // 默认支持的屏蔽的功能 defaultSupportShieldFeatures := []string{ // 申请功能 diff --git a/pkg/database/dao/subject_relation.go b/pkg/database/dao/subject_relation.go index fa3d27a0..c40b37e3 100644 --- a/pkg/database/dao/subject_relation.go +++ b/pkg/database/dao/subject_relation.go @@ -128,7 +128,6 @@ func (m *subjectRelationManager) ListRelationBySubjectPK(subjectPK int64) (relat // ListEffectThinRelationBySubjectPK ... func (m *subjectRelationManager) ListEffectThinRelationBySubjectPK(subjectPK int64) ( relations []ThinSubjectRelation, err error) { - // 过期时间必须大于当前时间 now := time.Now().Unix() diff --git a/pkg/database/sqlx.go b/pkg/database/sqlx.go index 9bff761d..2f6b2c80 100644 --- a/pkg/database/sqlx.go +++ b/pkg/database/sqlx.go @@ -14,9 +14,8 @@ import ( "context" "time" + "github.com/TencentBlueKing/gopkg/conv" "github.com/jmoiron/sqlx" - - "iam/pkg/util" ) // ============== timer ============== @@ -293,7 +292,7 @@ func sqlxBulkInsertReturnIDWithTx(tx *sqlx.Tx, query string, args interface{}) ( } defer stmt.Close() - argSlice, err := util.ToSlice(args) + argSlice, err := conv.ToSlice(args) // 转换不成功,说明是非数组,则单个条件 if err != nil { return nil, err @@ -323,7 +322,7 @@ func sqlxBulkUpdateWithTx(tx *sqlx.Tx, query string, args interface{}) error { } defer stmt.Close() - argSlice, err := util.ToSlice(args) + argSlice, err := conv.ToSlice(args) // 转换不成功,说明是非数组,则单个条件 if err != nil { return err diff --git a/pkg/database/utils.go b/pkg/database/utils.go index 5dec0331..2c5a7542 100644 --- a/pkg/database/utils.go +++ b/pkg/database/utils.go @@ -17,12 +17,12 @@ import ( "strings" "time" + "github.com/TencentBlueKing/gopkg/stringx" "github.com/jmoiron/sqlx" jsoniter "github.com/json-iterator/go" log "github.com/sirupsen/logrus" "iam/pkg/logging" - "iam/pkg/util" ) const ( @@ -62,7 +62,7 @@ func truncateArgs(args interface{}, length int) string { if err != nil { s = fmt.Sprintf("%v", args) } - return util.TruncateString(s, length) + return stringx.Truncate(s, length) } func isBlank(value reflect.Value) bool { diff --git a/pkg/database/utils_test.go b/pkg/database/utils_test.go index 30ec9b9e..41dae6ff 100644 --- a/pkg/database/utils_test.go +++ b/pkg/database/utils_test.go @@ -15,6 +15,7 @@ import ( "reflect" "testing" + "github.com/TencentBlueKing/gopkg/stringx" jsoniter "github.com/json-iterator/go" . "github.com/onsi/ginkgo" "github.com/stretchr/testify/assert" @@ -131,7 +132,7 @@ func TestParseUpdateStruct(t *testing.T) { func truncateInterface(v interface{}) string { s := fmt.Sprintf("%v", v) - return util.TruncateString(s, 10) + return stringx.Truncate(s, 10) } func truncateInterfaceViaJSON(v interface{}) string { @@ -139,7 +140,7 @@ func truncateInterfaceViaJSON(v interface{}) string { if err != nil { s = fmt.Sprintf("%v", v) } - return util.TruncateString(s, 10) + return stringx.Truncate(s, 10) } func truncateInterfaceViaJSONToBytes(v interface{}) string { s, err := jsoniter.Marshal(v) diff --git a/pkg/errorx/error_test.go b/pkg/errorx/error_test.go deleted file mode 100644 index 8285bd09..00000000 --- a/pkg/errorx/error_test.go +++ /dev/null @@ -1,64 +0,0 @@ -/* - * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-权限中心(BlueKing-IAM) available. - * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. - * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at http://opensource.org/licenses/MIT - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package errorx - -import ( - "errors" - "testing" - - "github.com/stretchr/testify/assert" -) - -type NoIsWrapError struct { - message string - err error -} - -func (e NoIsWrapError) Error() string { - return e.message -} - -func TestIAMError_Is(t *testing.T) { - // err vs iamerror - e1 := errors.New("a") - - e2 := IAMError{ - message: "iam_e2", - err: e1, - } - - assert.False(t, errors.Is(e1, e2)) - assert.True(t, errors.Is(e2, e1)) - - // iamerror vs iamerror - e3 := IAMError{ - message: "iam_e3", - err: e2, - } - assert.True(t, errors.Is(e3, e1)) - assert.True(t, errors.Is(e3, e2)) - - assert.False(t, errors.Is(e2, e3)) - assert.False(t, errors.Is(e2, e3)) - - // noIsWrapError vs iamerror - e4 := NoIsWrapError{ - message: "no_is_wrap", - err: e1, - } - e5 := IAMError{ - message: "iam_e5", - err: e4, - } - - assert.True(t, errors.Is(e5, e4)) - assert.False(t, errors.Is(e4, e5)) -} diff --git a/pkg/middleware/audit.go b/pkg/middleware/audit.go index f4ec10d5..9a7803eb 100644 --- a/pkg/middleware/audit.go +++ b/pkg/middleware/audit.go @@ -14,6 +14,7 @@ import ( "bytes" "time" + "github.com/TencentBlueKing/gopkg/stringx" "github.com/gin-gonic/gin" log "github.com/sirupsen/logrus" @@ -66,13 +67,13 @@ func Audit() gin.HandlerFunc { // always add 1ms, in case the 0ms in log latency := float64(duration/time.Millisecond) + 1 - params := util.TruncateString(c.Request.URL.RawQuery, 1024) + params := stringx.Truncate(c.Request.URL.RawQuery, 1024) fields := log.Fields{ "method": c.Request.Method, "path": c.Request.URL.Path, "params": params, "body": body, - "response_body": util.TruncateString(newWriter.body.String(), 1024), + "response_body": stringx.Truncate(newWriter.body.String(), 1024), "status": c.Writer.Status(), "latency": latency, diff --git a/pkg/middleware/logger.go b/pkg/middleware/logger.go index 61eb12ba..57bef410 100644 --- a/pkg/middleware/logger.go +++ b/pkg/middleware/logger.go @@ -15,6 +15,7 @@ import ( "fmt" "time" + "github.com/TencentBlueKing/gopkg/stringx" "github.com/gin-gonic/gin" "go.uber.org/zap" @@ -79,7 +80,7 @@ func logContextFields(c *gin.Context) []zap.Field { e = "" } - params := util.TruncateString(c.Request.URL.RawQuery, 1024) + params := stringx.Truncate(c.Request.URL.RawQuery, 1024) fields := []zap.Field{ zap.String("method", c.Request.Method), zap.String("path", c.Request.URL.Path), @@ -96,7 +97,7 @@ func logContextFields(c *gin.Context) []zap.Field { if hasError { fields = append(fields, zap.String("response_body", newWriter.body.String())) } else { - fields = append(fields, zap.String("response_body", util.TruncateString(newWriter.body.String(), 1024))) + fields = append(fields, zap.String("response_body", stringx.Truncate(newWriter.body.String(), 1024))) } if hasError && e != nil { diff --git a/pkg/server/router.go b/pkg/server/router.go index e9a69920..2a915322 100644 --- a/pkg/server/router.go +++ b/pkg/server/router.go @@ -61,7 +61,16 @@ func NewRouter(cfg *config.Config) *gin.Engine { policy.Register(policyRouter) // restful apis for open api - openAPIRouter := router.Group("/api/v1/systems") + // 1. legacy apis, will be removed in the future + openLegacyAPIRouter := router.Group("/api/v1/systems") + openLegacyAPIRouter.Use(middleware.Metrics()) + openLegacyAPIRouter.Use(middleware.APILogger()) + openLegacyAPIRouter.Use(middleware.NewClientAuthMiddleware(cfg)) + policyRouter.Use(middleware.NewRateLimitMiddleware(cfg)) + open.RegisterLegacySystemAPIs(openLegacyAPIRouter) + + // 2. open apis + openAPIRouter := router.Group("/api/v1/open") openAPIRouter.Use(middleware.Metrics()) openAPIRouter.Use(middleware.APILogger()) openAPIRouter.Use(middleware.NewClientAuthMiddleware(cfg)) diff --git a/pkg/service/action.go b/pkg/service/action.go index 03ffa5d6..e3220636 100644 --- a/pkg/service/action.go +++ b/pkg/service/action.go @@ -22,12 +22,12 @@ types定义的数据结构的加载层 import ( "database/sql" + "github.com/TencentBlueKing/gopkg/errorx" jsoniter "github.com/json-iterator/go" "iam/pkg/database" "iam/pkg/database/dao" "iam/pkg/database/sdao" - "iam/pkg/errorx" "iam/pkg/service/types" ) diff --git a/pkg/service/action_instance_selection.go b/pkg/service/action_instance_selection.go index 1e322c75..f2340476 100644 --- a/pkg/service/action_instance_selection.go +++ b/pkg/service/action_instance_selection.go @@ -11,7 +11,8 @@ package service import ( - "iam/pkg/errorx" + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/service/types" ) diff --git a/pkg/service/action_resource_type.go b/pkg/service/action_resource_type.go index 268cbba9..18b659d1 100644 --- a/pkg/service/action_resource_type.go +++ b/pkg/service/action_resource_type.go @@ -11,7 +11,8 @@ package service import ( - "iam/pkg/errorx" + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/service/types" ) diff --git a/pkg/service/action_thin.go b/pkg/service/action_thin.go index 6208534a..f4fe4a65 100644 --- a/pkg/service/action_thin.go +++ b/pkg/service/action_thin.go @@ -11,8 +11,9 @@ package service import ( + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/database/dao" - "iam/pkg/errorx" "iam/pkg/service/types" ) diff --git a/pkg/service/engine_policy.go b/pkg/service/engine_policy.go index c98c09d7..6892e62e 100644 --- a/pkg/service/engine_policy.go +++ b/pkg/service/engine_policy.go @@ -13,8 +13,9 @@ package service //go:generate mockgen -source=$GOFILE -destination=./mock/$GOFILE -package=mock import ( + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/database/dao" - "iam/pkg/errorx" "iam/pkg/service/types" ) diff --git a/pkg/service/instance_selection.go b/pkg/service/instance_selection.go index c8a8e212..f3a76ca1 100644 --- a/pkg/service/instance_selection.go +++ b/pkg/service/instance_selection.go @@ -13,11 +13,11 @@ package service //go:generate mockgen -source=$GOFILE -destination=./mock/$GOFILE -package=mock import ( + "github.com/TencentBlueKing/gopkg/errorx" jsoniter "github.com/json-iterator/go" "iam/pkg/database" "iam/pkg/database/sdao" - "iam/pkg/errorx" "iam/pkg/service/types" ) diff --git a/pkg/service/mock/policy.go b/pkg/service/mock/policy.go index 39a923c5..983f9141 100644 --- a/pkg/service/mock/policy.go +++ b/pkg/service/mock/policy.go @@ -5,10 +5,12 @@ package mock import ( + reflect "reflect" + + "github.com/TencentBlueKing/gopkg/collection/set" gomock "github.com/golang/mock/gomock" + types "iam/pkg/service/types" - util "iam/pkg/util" - reflect "reflect" ) // MockPolicyService is a mock of PolicyService interface @@ -124,7 +126,7 @@ func (mr *MockPolicyServiceMockRecorder) UpdateExpiredAt(policies interface{}) * } // AlterCustomPolicies mocks base method -func (m *MockPolicyService) AlterCustomPolicies(subjectPK int64, createPolicies, updatePolicies []types.Policy, deletePolicyIDs []int64, actionPKWithResourceTypeSet *util.Int64Set) (map[int64][]int64, error) { +func (m *MockPolicyService) AlterCustomPolicies(subjectPK int64, createPolicies, updatePolicies []types.Policy, deletePolicyIDs []int64, actionPKWithResourceTypeSet *set.Int64Set) (map[int64][]int64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AlterCustomPolicies", subjectPK, createPolicies, updatePolicies, deletePolicyIDs, actionPKWithResourceTypeSet) ret0, _ := ret[0].(map[int64][]int64) @@ -167,7 +169,7 @@ func (mr *MockPolicyServiceMockRecorder) DeleteByActionPK(actionPK interface{}) } // CreateAndDeleteTemplatePolicies mocks base method -func (m *MockPolicyService) CreateAndDeleteTemplatePolicies(subjectPK, templateID int64, createPolicies []types.Policy, deletePolicyIDs []int64, actionPKWithResourceTypeSet *util.Int64Set) error { +func (m *MockPolicyService) CreateAndDeleteTemplatePolicies(subjectPK, templateID int64, createPolicies []types.Policy, deletePolicyIDs []int64, actionPKWithResourceTypeSet *set.Int64Set) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CreateAndDeleteTemplatePolicies", subjectPK, templateID, createPolicies, deletePolicyIDs, actionPKWithResourceTypeSet) ret0, _ := ret[0].(error) @@ -181,7 +183,7 @@ func (mr *MockPolicyServiceMockRecorder) CreateAndDeleteTemplatePolicies(subject } // UpdateTemplatePolicies mocks base method -func (m *MockPolicyService) UpdateTemplatePolicies(subjectPK int64, policies []types.Policy, actionPKWithResourceTypeSet *util.Int64Set) error { +func (m *MockPolicyService) UpdateTemplatePolicies(subjectPK int64, policies []types.Policy, actionPKWithResourceTypeSet *set.Int64Set) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateTemplatePolicies", subjectPK, policies, actionPKWithResourceTypeSet) ret0, _ := ret[0].(error) diff --git a/pkg/service/model_change_event.go b/pkg/service/model_change_event.go index 4b70709a..5bb6b0eb 100644 --- a/pkg/service/model_change_event.go +++ b/pkg/service/model_change_event.go @@ -11,8 +11,9 @@ package service import ( + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/database/dao" - "iam/pkg/errorx" "iam/pkg/service/types" ) diff --git a/pkg/service/policy.go b/pkg/service/policy.go index 6ef14563..b4b14436 100644 --- a/pkg/service/policy.go +++ b/pkg/service/policy.go @@ -14,13 +14,14 @@ import ( "errors" "time" + "github.com/TencentBlueKing/gopkg/collection/set" + "github.com/TencentBlueKing/gopkg/errorx" + "github.com/TencentBlueKing/gopkg/stringx" "github.com/jmoiron/sqlx" "iam/pkg/database" "iam/pkg/database/dao" - "iam/pkg/errorx" "iam/pkg/service/types" - "iam/pkg/util" ) //go:generate mockgen -source=$GOFILE -destination=./mock/$GOFILE -package=mock @@ -60,15 +61,15 @@ type PolicyService interface { UpdateExpiredAt(policies []types.QueryPolicy) error AlterCustomPolicies(subjectPK int64, createPolicies, updatePolicies []types.Policy, deletePolicyIDs []int64, - actionPKWithResourceTypeSet *util.Int64Set) (map[int64][]int64, error) + actionPKWithResourceTypeSet *set.Int64Set) (map[int64][]int64, error) DeleteByPKs(subjectPK int64, pks []int64) error DeleteByActionPK(actionPK int64) error CreateAndDeleteTemplatePolicies(subjectPK, templateID int64, createPolicies []types.Policy, deletePolicyIDs []int64, - actionPKWithResourceTypeSet *util.Int64Set) error - UpdateTemplatePolicies(subjectPK int64, policies []types.Policy, actionPKWithResourceTypeSet *util.Int64Set) error + actionPKWithResourceTypeSet *set.Int64Set) error + UpdateTemplatePolicies(subjectPK int64, policies []types.Policy, actionPKWithResourceTypeSet *set.Int64Set) error DeleteTemplatePolicies(subjectPK int64, templateID int64) error // for query @@ -230,7 +231,7 @@ func (s *policyService) AlterCustomPolicies( subjectPK int64, createPolicies, updatePolicies []types.Policy, deletePolicyIDs []int64, - actionPKWithResourceTypeSet *util.Int64Set, + actionPKWithResourceTypeSet *set.Int64Set, ) (updatedActionPKExpressionPKs map[int64][]int64, err error) { // 自定义权限每个policy对应一个expression // 创建policy的同时创建expression @@ -252,7 +253,7 @@ func (s *policyService) AlterCustomPolicies( daoCreateExpressions = append(daoCreateExpressions, dao.Expression{ Type: expressionTypeCustom, Expression: p.Expression, - Signature: util.GetMD5Hash(p.Expression), // 计算Hash + Signature: stringx.MD5Hash(p.Expression), // 计算Hash }) daoCreatePolicies = append(daoCreatePolicies, dao.Policy{ @@ -300,7 +301,7 @@ func (s *policyService) AlterCustomPolicies( PK: p.ExpressionPK, Type: expressionTypeCustom, Expression: up.Expression, - Signature: util.GetMD5Hash(up.Expression), + Signature: stringx.MD5Hash(up.Expression), }) // 更新过期时间 @@ -554,13 +555,13 @@ func (s *policyService) HasAnyByActionPK(actionPK int64) (bool, error) { // generateSignatureExpressionPKMap generate signature expressionPK map if expression does not exist create it func (s *policyService) generateSignatureExpressionPKMap( - tx *sqlx.Tx, policies []types.Policy, actionPKWithResourceTypeSet *util.Int64Set, + tx *sqlx.Tx, policies []types.Policy, actionPKWithResourceTypeSet *set.Int64Set, ) (signatureExpressionPKMap map[string]int64, err error) { errorWrapf := errorx.NewLayerFunctionErrorWrapf(PolicySVC, "generateSignatureExpressionPKMap") signatures := make([]string, 0, len(policies)) for i := range policies { - signature := util.GetMD5Hash(policies[i].Expression) + signature := stringx.MD5Hash(policies[i].Expression) signatures = append(signatures, signature) } @@ -572,7 +573,7 @@ func (s *policyService) generateSignatureExpressionPKMap( return nil, err } signatureExpressionPKMap = make(map[string]int64, len(expressions)) - existSignatures := util.NewStringSet() + existSignatures := set.NewStringSet() for _, e := range expressions { signatureExpressionPKMap[e.Signature] = e.PK existSignatures.Add(e.Signature) @@ -620,7 +621,7 @@ func (s *policyService) CreateAndDeleteTemplatePolicies( subjectPK, templateID int64, createPolicies []types.Policy, deletePolicyIDs []int64, - actionPKWithResourceTypeSet *util.Int64Set, + actionPKWithResourceTypeSet *set.Int64Set, ) (err error) { errorWrapf := errorx.NewLayerFunctionErrorWrapf(PolicySVC, "CreateAndDeleteTemplatePolicies") @@ -642,7 +643,7 @@ func (s *policyService) CreateAndDeleteTemplatePolicies( daoCreatePolicies := make([]dao.Policy, 0, len(createPolicies)) for _, p := range createPolicies { - signature := util.GetMD5Hash(p.Expression) + signature := stringx.MD5Hash(p.Expression) // 操作有关联资源类型 if actionPKWithResourceTypeSet.Has(p.ActionPK) { expressionPK, ok := signatureExpressionPKMap[signature] @@ -690,7 +691,7 @@ func (s *policyService) CreateAndDeleteTemplatePolicies( func (s *policyService) UpdateTemplatePolicies( subjectPK int64, policies []types.Policy, - actionPKWithResourceTypeSet *util.Int64Set, + actionPKWithResourceTypeSet *set.Int64Set, ) (err error) { errorWrapf := errorx.NewLayerFunctionErrorWrapf(PolicySVC, "UpdateTemplatePolicies") @@ -747,7 +748,7 @@ func (s *policyService) UpdateTemplatePolicies( continue } - signature := util.GetMD5Hash(p.Expression) + signature := stringx.MD5Hash(p.Expression) daoPolicy.ExpressionPK, ok = signatureExpressionPKMap[signature] if !ok { err = errorWrapf(errPolicy, "generate policy expression error ID=`%d`", p.ID) diff --git a/pkg/service/policy_test.go b/pkg/service/policy_test.go index feb823d2..513e3180 100644 --- a/pkg/service/policy_test.go +++ b/pkg/service/policy_test.go @@ -13,6 +13,7 @@ package service import ( "errors" + "github.com/TencentBlueKing/gopkg/collection/set" "github.com/agiledragon/gomonkey" "github.com/golang/mock/gomock" . "github.com/onsi/ginkgo" @@ -22,7 +23,6 @@ import ( "iam/pkg/database/dao" "iam/pkg/database/dao/mock" "iam/pkg/service/types" - "iam/pkg/util" ) var _ = Describe("PolicyService", func() { @@ -431,14 +431,14 @@ var _ = Describe("PolicyService", func() { }, } - set := util.NewInt64Set() + set := set.NewInt64Set() set.Add(1) set.Add(2) _, err := svc.AlterCustomPolicies(1, createPolicies, updatePolicies, []int64{}, set) assert.NoError(GinkgoT(), err) - //_, err = dbMock.ExpectationsWereMet() + // _, err = dbMock.ExpectationsWereMet() err = dbMock.ExpectationsWereMet() assert.NoError(GinkgoT(), err) }) @@ -687,14 +687,14 @@ var _ = Describe("PolicyService", func() { }, } - set := util.NewInt64Set() + set := set.NewInt64Set() set.Add(1) set.Add(2) err := svc.CreateAndDeleteTemplatePolicies(1, 1, createPolicies, []int64{}, set) assert.NoError(GinkgoT(), err) - //_, err = dbMock.ExpectationsWereMet() + // _, err = dbMock.ExpectationsWereMet() err = dbMock.ExpectationsWereMet() assert.NoError(GinkgoT(), err) }) @@ -803,14 +803,14 @@ var _ = Describe("PolicyService", func() { }, } - set := util.NewInt64Set() + set := set.NewInt64Set() set.Add(1) set.Add(2) err := svc.UpdateTemplatePolicies(1, updatePolicies, set) assert.NoError(GinkgoT(), err) - //_, err = dbMock.ExpectationsWereMet() + // _, err = dbMock.ExpectationsWereMet() err = dbMock.ExpectationsWereMet() assert.NoError(GinkgoT(), err) }) diff --git a/pkg/service/resource_type.go b/pkg/service/resource_type.go index 5a5410fd..ec9a4e95 100644 --- a/pkg/service/resource_type.go +++ b/pkg/service/resource_type.go @@ -13,12 +13,12 @@ package service //go:generate mockgen -source=$GOFILE -destination=./mock/$GOFILE -package=mock import ( + "github.com/TencentBlueKing/gopkg/errorx" jsoniter "github.com/json-iterator/go" "iam/pkg/database" "iam/pkg/database/dao" "iam/pkg/database/sdao" - "iam/pkg/errorx" "iam/pkg/service/types" ) diff --git a/pkg/service/subject.go b/pkg/service/subject.go index 8db8d682..2aa07412 100644 --- a/pkg/service/subject.go +++ b/pkg/service/subject.go @@ -13,9 +13,10 @@ package service //go:generate mockgen -source=$GOFILE -destination=./mock/$GOFILE -package=mock import ( + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/database" "iam/pkg/database/dao" - "iam/pkg/errorx" "iam/pkg/service/types" ) diff --git a/pkg/service/subject_department.go b/pkg/service/subject_department.go index 8699c0ff..5f2ad3e7 100644 --- a/pkg/service/subject_department.go +++ b/pkg/service/subject_department.go @@ -13,8 +13,10 @@ package service import ( "fmt" + "github.com/TencentBlueKing/gopkg/collection/set" + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/database/dao" - "iam/pkg/errorx" "iam/pkg/service/types" "iam/pkg/util" ) @@ -178,7 +180,7 @@ func (l *subjectService) convertSubjectDepartments( errorWrapf := errorx.NewLayerFunctionErrorWrapf(SubjectSVC, "convertSubjectDepartments") subjectIDs := make([]string, 0, len(subjectDepartments)) - departmentIDSet := util.NewStringSet() + departmentIDSet := set.NewStringSet() for _, sd := range subjectDepartments { subjectIDs = append(subjectIDs, sd.SubjectID) for _, did := range sd.DepartmentIDs { diff --git a/pkg/service/subject_group.go b/pkg/service/subject_group.go index d60c40a7..6198c218 100644 --- a/pkg/service/subject_group.go +++ b/pkg/service/subject_group.go @@ -11,10 +11,11 @@ package service import ( + "github.com/TencentBlueKing/gopkg/collection/set" + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/database/dao" - "iam/pkg/errorx" "iam/pkg/service/types" - "iam/pkg/util" ) func convertToSubjectGroup(relation dao.SubjectRelation) types.SubjectGroup { @@ -124,7 +125,7 @@ func (l *subjectService) ListExistSubjectsBeforeExpiredAt( return []types.Subject{}, nil } - idSet := util.NewStringSetWithValues(existGroupIDs) + idSet := set.NewStringSetWithValues(existGroupIDs) existSubjects := make([]types.Subject, 0, len(existGroupIDs)) for _, subject := range subjects { if subject.Type == types.GroupType && idSet.Has(subject.ID) { diff --git a/pkg/service/subject_member.go b/pkg/service/subject_member.go index bc356885..f27151bd 100644 --- a/pkg/service/subject_member.go +++ b/pkg/service/subject_member.go @@ -14,9 +14,10 @@ import ( "errors" "time" + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/database" "iam/pkg/database/dao" - "iam/pkg/errorx" "iam/pkg/service/types" ) diff --git a/pkg/service/subject_role.go b/pkg/service/subject_role.go index f5b2034f..9d9fbe7d 100644 --- a/pkg/service/subject_role.go +++ b/pkg/service/subject_role.go @@ -11,10 +11,11 @@ package service import ( + "github.com/TencentBlueKing/gopkg/collection/set" + "github.com/TencentBlueKing/gopkg/errorx" + "iam/pkg/database/dao" - "iam/pkg/errorx" "iam/pkg/service/types" - "iam/pkg/util" ) // ListSubjectPKByRole ... @@ -49,7 +50,7 @@ func (l *subjectService) BulkCreateSubjectRoles(roleType, system string, subject } // 对比出需要创建subjectPK - oldPKs := util.NewInt64SetWithValues(oldSubjectPKs) + oldPKs := set.NewInt64SetWithValues(oldSubjectPKs) roles := make([]dao.SubjectRole, 0, len(subjectPKs)) diff --git a/pkg/service/system.go b/pkg/service/system.go index bca8d7cf..9d227a21 100644 --- a/pkg/service/system.go +++ b/pkg/service/system.go @@ -13,14 +13,14 @@ package service //go:generate mockgen -source=$GOFILE -destination=./mock/$GOFILE -package=mock import ( + "github.com/TencentBlueKing/gopkg/errorx" + "github.com/TencentBlueKing/gopkg/stringx" jsoniter "github.com/json-iterator/go" "iam/pkg/database" "iam/pkg/database/dao" "iam/pkg/database/sdao" - "iam/pkg/errorx" "iam/pkg/service/types" - "iam/pkg/util" ) // SystemSVC ... @@ -130,7 +130,7 @@ func (l *systemService) Create(system types.System) error { } // NOTE: generate token here - system.ProviderConfig["token"] = util.RandString(32) + system.ProviderConfig["token"] = stringx.Random(32) providerConfig, err := jsoniter.MarshalToString(system.ProviderConfig) if err != nil { diff --git a/pkg/service/system_config.go b/pkg/service/system_config.go index 693f8fbd..a1323379 100644 --- a/pkg/service/system_config.go +++ b/pkg/service/system_config.go @@ -17,10 +17,10 @@ import ( "errors" "fmt" + "github.com/TencentBlueKing/gopkg/errorx" jsoniter "github.com/json-iterator/go" "iam/pkg/database/sdao" - "iam/pkg/errorx" ) // SystemConfigSVC ... diff --git a/pkg/util/bytesconv_test.go b/pkg/util/bytesconv_test.go deleted file mode 100644 index 761df33b..00000000 --- a/pkg/util/bytesconv_test.go +++ /dev/null @@ -1,121 +0,0 @@ -/* - * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-权限中心(BlueKing-IAM) available. - * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. - * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at http://opensource.org/licenses/MIT - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package util_test - -import ( - "math/rand" - "strings" - "testing" - "time" - - . "github.com/onsi/ginkgo" - "github.com/stretchr/testify/assert" - - "iam/pkg/util" -) - -var testString = "Albert Einstein: Logic will get you from A to B. Imagination will take you everywhere." -var testBytes = []byte(testString) - -func rawBytesToStr(b []byte) string { - return string(b) -} - -func rawStrToBytes(s string) []byte { - return []byte(s) -} - -const letterBytesForTest = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" -const ( - letterIdxBits = 6 // 6 bits to represent a letter index - letterIdxMask = 1<= 0; { - if remain == 0 { - cache, remain = src.Int63(), letterIdxMax - } - if idx := int(cache & letterIdxMask); idx < len(letterBytesForTest) { - sb.WriteByte(letterBytesForTest[idx]) - i-- - } - cache >>= letterIdxBits - remain-- - } - - return sb.String() -} - -var _ = Describe("Bytesconv", func() { - - Describe("StringToBytes", func() { - It("a normal string", func() { - b := util.StringToBytes("abc") - assert.Equal(GinkgoT(), []byte("abc"), b) - }) - - It("random generate", func() { - for i := 0; i < 100; i++ { - s := RandStringBytesMaskImprSrcSB(64) - - assert.Equal(GinkgoT(), rawStrToBytes(s), util.StringToBytes(s)) - } - }) - }) - - Describe("BytesToString", func() { - It("a normal bytes", func() { - s := util.BytesToString([]byte("abc")) - assert.Equal(GinkgoT(), "abc", s) - }) - - It("random generate", func() { - data := make([]byte, 1024) - for i := 0; i < 100; i++ { - rand.Read(data) - assert.Equal(GinkgoT(), rawBytesToStr(data), util.BytesToString(data)) - } - }) - }) -}) - -// go test -v -run=none -bench=^BenchmarkBytesConv -benchmem=true - -func BenchmarkBytesConvBytesToStrRaw(b *testing.B) { - for i := 0; i < b.N; i++ { - rawBytesToStr(testBytes) - } -} - -func BenchmarkBytesConvBytesToStr(b *testing.B) { - for i := 0; i < b.N; i++ { - util.BytesToString(testBytes) - } -} - -func BenchmarkBytesConvStrToBytesRaw(b *testing.B) { - for i := 0; i < b.N; i++ { - rawStrToBytes(testString) - } -} - -func BenchmarkBytesConvStrToBytes(b *testing.B) { - for i := 0; i < b.N; i++ { - util.StringToBytes(testString) - } -} diff --git a/pkg/util/conv.go b/pkg/util/conv.go index c4c1d1db..cb8e6c44 100644 --- a/pkg/util/conv.go +++ b/pkg/util/conv.go @@ -11,30 +11,11 @@ package util import ( - "errors" "fmt" - "reflect" "strconv" "strings" ) -// ErrNotArray ... -var ErrNotArray = errors.New("validate array fail, only support array") - -// ToSlice ... -func ToSlice(array interface{}) ([]interface{}, error) { - v := reflect.ValueOf(array) - if v.Kind() != reflect.Slice { - return nil, ErrNotArray - } - l := v.Len() - ret := make([]interface{}, l) - for i := 0; i < l; i++ { - ret[i] = v.Index(i).Interface() - } - return ret, nil -} - // Int64SliceToString ... func Int64SliceToString(s []int64, sep string) string { return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(s)), sep), "[]") diff --git a/pkg/util/conv_test.go b/pkg/util/conv_test.go index e23e0a04..ffe30112 100644 --- a/pkg/util/conv_test.go +++ b/pkg/util/conv_test.go @@ -24,27 +24,6 @@ import ( var _ = Describe("Conv", func() { - Describe("ToSlice", func() { - - intSlice := []int{1} - strSlice := []string{"abc"} - - DescribeTable("ToSlice cases", func(expected int, willError bool, input interface{}) { - data, err := util.ToSlice(input) - - if willError { - assert.Error(GinkgoT(), err) - } else { - assert.NoError(GinkgoT(), err) - assert.Equal(GinkgoT(), expected, len(data)) - } - }, - Entry("not a slice", 0, true, ""), - Entry("a []int{1}", 1, false, intSlice), - Entry("a []string{abc}", 1, false, strSlice), - ) - }) - Describe("Int64SliceToString", func() { DescribeTable("Int64SliceToString cases", func(expected string, input []int64, sep string) { assert.Equal(GinkgoT(), expected, util.Int64SliceToString(input, sep)) diff --git a/pkg/util/error.go b/pkg/util/error.go index 81faa9f7..86fbd1ef 100644 --- a/pkg/util/error.go +++ b/pkg/util/error.go @@ -14,8 +14,6 @@ import ( "time" "github.com/getsentry/sentry-go" - - "iam/pkg/errorx" ) // Error Codes @@ -39,5 +37,7 @@ func ReportToSentry(message string, extra map[string]interface{}) { ev.Level = "error" ev.Timestamp = time.Now() ev.Extra = extra - errorx.ReportEvent(ev) + if sentryOn { + sentry.CaptureEvent(ev) + } } diff --git a/pkg/util/error_test.go b/pkg/util/error_test.go index 9ac93e17..ce1ae6f9 100644 --- a/pkg/util/error_test.go +++ b/pkg/util/error_test.go @@ -3,14 +3,13 @@ package util_test import ( . "github.com/onsi/ginkgo" - "iam/pkg/errorx" "iam/pkg/util" ) var _ = Describe("Error", func() { BeforeEach(func() { - errorx.InitErrorReport(false) + util.InitErrorReport(false) }) Describe("ReportToSentry", func() { diff --git a/pkg/util/hash_test.go b/pkg/util/hash_test.go deleted file mode 100644 index c44bbfc8..00000000 --- a/pkg/util/hash_test.go +++ /dev/null @@ -1,32 +0,0 @@ -/* - * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-权限中心(BlueKing-IAM) available. - * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. - * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at http://opensource.org/licenses/MIT - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package util_test - -import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/ginkgo/extensions/table" - "github.com/stretchr/testify/assert" - - "iam/pkg/util" -) - -var _ = Describe("Hash", func() { - - Describe("GetMD5Hash", func() { - DescribeTable("GetMD5Hash cases", func(expected string, input string) { - assert.Equal(GinkgoT(), expected, util.GetMD5Hash(input)) - }, - Entry("value is empty string", "d41d8cd98f00b204e9800998ecf8427e", ""), - Entry("value is 'test'", "098f6bcd4621d373cade4e832627b4f6", "test"), - ) - }) - -}) diff --git a/pkg/errorx/init.go b/pkg/util/init.go similarity index 80% rename from pkg/errorx/init.go rename to pkg/util/init.go index 078dd74b..75737c4c 100644 --- a/pkg/errorx/init.go +++ b/pkg/util/init.go @@ -8,9 +8,7 @@ * specific language governing permissions and limitations under the License. */ -package errorx - -import "github.com/getsentry/sentry-go" +package util var sentryOn bool @@ -18,10 +16,3 @@ var sentryOn bool func InitErrorReport(sentryEnabled bool) { sentryOn = sentryEnabled } - -// ReportEvent will report an event to sentry, if sentry enabled -func ReportEvent(event *sentry.Event) { - if sentryOn { - sentry.CaptureEvent(event) - } -} diff --git a/pkg/util/request.go b/pkg/util/request.go index b972a80c..dda0e571 100644 --- a/pkg/util/request.go +++ b/pkg/util/request.go @@ -17,6 +17,7 @@ import ( "io/ioutil" "net/http" + "github.com/TencentBlueKing/gopkg/conv" "github.com/gin-gonic/gin" ) @@ -69,5 +70,5 @@ func SetError(c *gin.Context, err error) { // BasicAuthAuthorizationHeader ... func BasicAuthAuthorizationHeader(user, password string) string { base := user + ":" + password - return "Basic " + base64.StdEncoding.EncodeToString(StringToBytes(base)) + return "Basic " + base64.StdEncoding.EncodeToString(conv.StringToBytes(base)) } diff --git a/pkg/util/set_test.go b/pkg/util/set_test.go deleted file mode 100644 index adb768ab..00000000 --- a/pkg/util/set_test.go +++ /dev/null @@ -1,208 +0,0 @@ -/* - * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-权限中心(BlueKing-IAM) available. - * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. - * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at http://opensource.org/licenses/MIT - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package util_test - -import ( - . "github.com/onsi/ginkgo" - "github.com/stretchr/testify/assert" - - "iam/pkg/util" -) - -var _ = Describe("Set", func() { - - Describe("StringSet", func() { - - Describe("New", func() { - It("NewStringSet", func() { - s := util.NewStringSet() - - assert.Len(GinkgoT(), s.Data, 0) - assert.Equal(GinkgoT(), 0, s.Size()) - - assert.False(GinkgoT(), s.Has("hello")) - }) - - It("NewStringSetWithValues", func() { - s := util.NewStringSetWithValues([]string{"hello", "world"}) - - assert.Len(GinkgoT(), s.Data, 2) - assert.Equal(GinkgoT(), 2, s.Size()) - - assert.True(GinkgoT(), s.Has("hello")) - }) - - It("NewFixedLengthStringSet", func() { - s := util.NewFixedLengthStringSet(2) - - assert.Len(GinkgoT(), s.Data, 0) - assert.Equal(GinkgoT(), 0, s.Size()) - }) - }) - - Describe("Functions", func() { - var s *util.StringSet - - BeforeEach(func() { - s = util.NewStringSet() - s.Add("hello") - }) - - It("Has", func() { - assert.True(GinkgoT(), s.Has("hello")) - assert.False(GinkgoT(), s.Has("world")) - }) - - It("Add", func() { - s.Add("world") - assert.True(GinkgoT(), s.Has("world")) - }) - - It("Append", func() { - s.Append([]string{"abc", "def"}...) - s.Append([]string{"def", "opq"}...) - - assert.Len(GinkgoT(), s.Data, 4) - assert.Equal(GinkgoT(), 4, s.Size()) - - assert.True(GinkgoT(), s.Has("abc")) - assert.True(GinkgoT(), s.Has("def")) - assert.True(GinkgoT(), s.Has("opq")) - }) - - It("Size", func() { - assert.Equal(GinkgoT(), 1, s.Size()) - }) - - It("ToSlice", func() { - sli1 := s.ToSlice() - assert.Len(GinkgoT(), sli1, 1) - - s.Add("world") - sli2 := s.ToSlice() - assert.Len(GinkgoT(), sli2, 2) - }) - - It("ToString", func() { - s1 := s.ToString(",") - assert.Equal(GinkgoT(), "hello", s1) - - s.Add("world") - s2 := s.ToString(",") - - isEqual := s2 == "hello,world" || s2 == "world,hello" - //assert.Equal(GinkgoT(), "hello,world", s2) - assert.True(GinkgoT(), isEqual) - - }) - - It("Diff", func() { - // s = [hello, world] - s.Add("world") - - // s1 = [world, foo] - s1 := util.NewStringSetWithValues([]string{"world", "foo"}) - - // the diff result - s2 := s.Diff(s1) - - // the result = [hello] - assert.Equal(GinkgoT(), 1, s2.Size()) - assert.True(GinkgoT(), s2.Has("hello")) - }) - - }) - - }) - - Describe("Int64Set", func() { - - var s *util.Int64Set - - BeforeEach(func() { - s = util.NewInt64Set() - }) - - It("NewInt64Set", func() { - //s := util.NewInt64Set() - assert.Len(GinkgoT(), s.Data, 0) - assert.Equal(GinkgoT(), 0, s.Size()) - }) - - It("NewInt64SetWithValues", func() { - s1 := util.NewInt64SetWithValues([]int64{123, 456}) - - assert.Len(GinkgoT(), s1.Data, 2) - assert.Equal(GinkgoT(), 2, s1.Size()) - - assert.True(GinkgoT(), s1.Has(123)) - }) - - It("NewFixedLengthInt64Set", func() { - s1 := util.NewFixedLengthInt64Set(2) - - assert.Len(GinkgoT(), s1.Data, 0) - assert.Equal(GinkgoT(), 0, s1.Size()) - }) - - It("Add one, check size", func() { - s.Add(123) - - assert.Len(GinkgoT(), s.Data, 1) - assert.Equal(GinkgoT(), 1, s.Size()) - }) - - It("Append", func() { - s.Append([]int64{123, 456}...) - s.Append([]int64{456, 789}...) - - assert.Len(GinkgoT(), s.Data, 3) - assert.Equal(GinkgoT(), 3, s.Size()) - - assert.True(GinkgoT(), s.Has(int64(123))) - assert.True(GinkgoT(), s.Has(int64(456))) - assert.True(GinkgoT(), s.Has(int64(789))) - }) - - It("Has 123", func() { - assert.False(GinkgoT(), s.Has(123)) - s.Add(123) - assert.True(GinkgoT(), s.Has(123)) - }) - - It("ToSlice", func() { - s.Add(123) - sli1 := s.ToSlice() - assert.Len(GinkgoT(), sli1, 1) - - s.Add(456) - - sli2 := s.ToSlice() - assert.Len(GinkgoT(), sli2, 2) - }) - - }) - - Describe("SplitStringToSet", func() { - - It("Empty string", func() { - s := util.SplitStringToSet("", ",") - assert.Equal(GinkgoT(), 0, s.Size()) - }) - - It("Normal string a,b,c", func() { - s := util.SplitStringToSet("a,b,c", ",") - assert.Equal(GinkgoT(), 3, s.Size()) - assert.True(GinkgoT(), s.Has("b")) - }) - }) - -}) diff --git a/pkg/util/string.go b/pkg/util/string.go index f7aab372..4019132f 100644 --- a/pkg/util/string.go +++ b/pkg/util/string.go @@ -10,10 +10,6 @@ package util -import ( - "math/rand" -) - // TruncateBytes truncate []byte to specific length func TruncateBytes(content []byte, length int) []byte { if len(content) > length { @@ -27,22 +23,3 @@ func TruncateBytesToString(content []byte, length int) string { s := TruncateBytes(content, length) return string(s) } - -// TruncateString truncate string to specific length -func TruncateString(s string, n int) string { - if n > len(s) { - return s - } - return s[:n] -} - -const letterBytes = "abcdefghijklmnopqrstuvwxyz1234567890" - -// RandString ... -func RandString(n int) string { - b := make([]byte, n) - for i := range b { - b[i] = letterBytes[rand.Intn(len(letterBytes))] - } - return string(b) -} diff --git a/pkg/util/string_test.go b/pkg/util/string_test.go index 897e5930..d2b570cc 100644 --- a/pkg/util/string_test.go +++ b/pkg/util/string_test.go @@ -49,28 +49,6 @@ var _ = Describe("String", func() { ) }) - Describe("TruncateString", func() { - var s = "helloworld" - - DescribeTable("TruncateString cases", func(expected string, truncatedSize int) { - assert.Equal(GinkgoT(), expected, util.TruncateString(s, truncatedSize)) - }, - Entry("truncated size less than real size", "he", 2), - Entry("truncated size equals to real size", s, 10), - Entry("truncated size greater than real size", s, 20), - ) - }) - - Describe("RandString", func() { - DescribeTable("RandString cases", func(length int) { - assert.Equal(GinkgoT(), length, len(util.RandString(length))) - }, - Entry("string length 0", 0), - Entry("string length 1", 10), - Entry("string length 10", 10), - ) - }) - }) func BenchmarkStringSprintf(b *testing.B) { diff --git a/release.md b/release.md index d326c7fa..a2da06ef 100644 --- a/release.md +++ b/release.md @@ -1,3 +1,9 @@ +# 1.9.3 + +- update: replace some lib with https://github.com/TencentBlueKing/gopkg +- move: API /api/v1/systems/ to /api/v1/open/systems/ +- add: API /api/v1/open/users/:user_id/groups + # 1.9.2 - hotfix: condition StringPrefix eval wrong when key is _bk_iam_path_ diff --git a/vendor/github.com/TencentBlueKing/gopkg/LICENSE.txt b/vendor/github.com/TencentBlueKing/gopkg/LICENSE.txt new file mode 100644 index 00000000..619b65cd --- /dev/null +++ b/vendor/github.com/TencentBlueKing/gopkg/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 腾讯蓝鲸 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/TencentBlueKing/gopkg/cache/key.go b/vendor/github.com/TencentBlueKing/gopkg/cache/key.go new file mode 100644 index 00000000..48e13d1d --- /dev/null +++ b/vendor/github.com/TencentBlueKing/gopkg/cache/key.go @@ -0,0 +1,105 @@ +/* + * TencentBlueKing is pleased to support the open source community by making + * 蓝鲸智云-gopkg available. + * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. + * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://opensource.org/licenses/MIT + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package cache + +import "fmt" + +// Key is the type for the key of a cache entry. +// a struct-like object implements the key interface, so it can be used as a key in a cache. +type Key interface { + Key() string +} + +// StringKey is a string key. +type StringKey struct { + key string +} + +// NewStringKey creates a new StringKey +func NewStringKey(key string) StringKey { + return StringKey{ + key: key, + } +} + +// Key returns the key. +func (s StringKey) Key() string { + return s.key +} + +// IntKey is an int key. +type IntKey struct { + key int +} + +// NewIntKey creates a new IntKey +func NewIntKey(key int) IntKey { + return IntKey{ + key: key, + } +} + +// Key returns the key. +func (k IntKey) Key() string { + return fmt.Sprintf("%d", k.key) +} + +// Int64Key is an int64 key. +type Int64Key struct { + key int64 +} + +// NewInt64Key creates a new Int64Key +func NewInt64Key(key int64) Int64Key { + return Int64Key{ + key: key, + } +} + +// Key returns the key. +func (k Int64Key) Key() string { + return fmt.Sprintf("%d", k.key) +} + +// UintKey is an uint key. +type UintKey struct { + key uint +} + +// NewUintKey creates a new UintKey +func NewUintKey(key uint) UintKey { + return UintKey{ + key: key, + } +} + +// Key returns the key. +func (k UintKey) Key() string { + return fmt.Sprintf("%d", k.key) +} + +// Uint64Key is an uint64 key. +type Uint64Key struct { + key uint64 +} + +// NewUint64Key creates a new Uint64Key +func NewUint64Key(key uint64) Uint64Key { + return Uint64Key{ + key: key, + } +} + +// Key returns the key. +func (k Uint64Key) Key() string { + return fmt.Sprintf("%d", k.key) +} diff --git a/pkg/cache/memory/backend/memory.go b/vendor/github.com/TencentBlueKing/gopkg/cache/memory/backend/memory.go similarity index 62% rename from pkg/cache/memory/backend/memory.go rename to vendor/github.com/TencentBlueKing/gopkg/cache/memory/backend/memory.go index 3f0ad5d5..8cf6fe09 100644 --- a/pkg/cache/memory/backend/memory.go +++ b/vendor/github.com/TencentBlueKing/gopkg/cache/memory/backend/memory.go @@ -1,5 +1,6 @@ /* - * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-权限中心(BlueKing-IAM) available. + * TencentBlueKing is pleased to support the open source community by making + * 蓝鲸智云-gopkg available. * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://opensource.org/licenses/MIT @@ -16,12 +17,12 @@ import ( gocache "github.com/patrickmn/go-cache" ) -// DefaultCleanupInterval ... const ( DefaultCleanupInterval = 5 * time.Minute ) -type RandomExpirationDurationFunc func() time.Duration +// RandomExtraExpirationDurationFunc is the type of the function generate extra expiration duration +type RandomExtraExpirationDurationFunc func() time.Duration // NewTTLCache create cache with expiration and cleanup interval, // if cleanupInterval is 0, will use DefaultCleanupInterval @@ -33,56 +34,54 @@ func newTTLCache(expiration time.Duration, cleanupInterval time.Duration) *gocac return gocache.New(expiration, cleanupInterval) } -// MemoryBackend ... +// NewMemoryBackend create memory backend +// - name: the name of the backend +// - expiration: the expiration duration of the cache +// - randomExtraExpirationFunc: the function generate extra expiration duration +func NewMemoryBackend( + name string, + expiration time.Duration, + randomExtraExpirationFunc RandomExtraExpirationDurationFunc, +) *MemoryBackend { + cleanupInterval := expiration + (5 * time.Minute) + + return &MemoryBackend{ + name: name, + cache: newTTLCache(expiration, cleanupInterval), + defaultExpiration: expiration, + randomExtraExpirationFunc: randomExtraExpirationFunc, + } +} + +// MemoryBackend is the backend for memory cache type MemoryBackend struct { name string cache *gocache.Cache - defaultExpiration time.Duration - randomDurationFunc RandomExpirationDurationFunc + defaultExpiration time.Duration + randomExtraExpirationFunc RandomExtraExpirationDurationFunc } -// Set ... +// Set sets value to cache with key and expiration func (c *MemoryBackend) Set(key string, value interface{}, duration time.Duration) { if duration == time.Duration(0) { duration = c.defaultExpiration } - if c.randomDurationFunc != nil { - duration += c.randomDurationFunc() + if c.randomExtraExpirationFunc != nil { + duration += c.randomExtraExpirationFunc() } c.cache.Set(key, value, duration) } -// Get ... +// Get gets value by key from the cache func (c *MemoryBackend) Get(key string) (interface{}, bool) { return c.cache.Get(key) } -// GetInto ... -func (c *MemoryBackend) GetInto(key string, value interface{}) (interface{}, bool) { - return c.cache.Get(key) -} - -// Delete ... +// Delete deletes value by key from the cache func (c *MemoryBackend) Delete(key string) error { c.cache.Delete(key) return nil } - -// NewMemoryBackend ... -func NewMemoryBackend( - name string, - expiration time.Duration, - randomDurationFunc RandomExpirationDurationFunc, -) *MemoryBackend { - cleanupInterval := expiration + (5 * time.Minute) - - return &MemoryBackend{ - name: name, - cache: newTTLCache(expiration, cleanupInterval), - defaultExpiration: expiration, - randomDurationFunc: randomDurationFunc, - } -} diff --git a/pkg/cache/memory/backend/types.go b/vendor/github.com/TencentBlueKing/gopkg/cache/memory/backend/types.go similarity index 87% rename from pkg/cache/memory/backend/types.go rename to vendor/github.com/TencentBlueKing/gopkg/cache/memory/backend/types.go index a6fcea3a..d20c0632 100644 --- a/pkg/cache/memory/backend/types.go +++ b/vendor/github.com/TencentBlueKing/gopkg/cache/memory/backend/types.go @@ -1,5 +1,6 @@ /* - * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-权限中心(BlueKing-IAM) available. + * TencentBlueKing is pleased to support the open source community by making + * 蓝鲸智云-gopkg available. * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://opensource.org/licenses/MIT @@ -12,11 +13,9 @@ package backend import "time" -// Backend ... +// Backend is the interface that wraps the basic cache backend operations. type Backend interface { Set(key string, value interface{}, duration time.Duration) Get(key string) (interface{}, bool) - - // Get(key string, value interface{}) error Delete(key string) error } diff --git a/vendor/github.com/TencentBlueKing/gopkg/cache/memory/base_cache.go b/vendor/github.com/TencentBlueKing/gopkg/cache/memory/base_cache.go new file mode 100644 index 00000000..71006f41 --- /dev/null +++ b/vendor/github.com/TencentBlueKing/gopkg/cache/memory/base_cache.go @@ -0,0 +1,354 @@ +/* + * TencentBlueKing is pleased to support the open source community by making + * 蓝鲸智云-gopkg available. + * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. + * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://opensource.org/licenses/MIT + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package memory + +import ( + "fmt" + "time" + + "golang.org/x/sync/singleflight" + + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/cache/memory/backend" +) + +const EmptyCacheExpiration = 5 * time.Second + +// NewBaseCache creates a new baseCache instance which implements Cache interface. +func NewBaseCache(disabled bool, retrieveFunc RetrieveFunc, backend backend.Backend) Cache { + return &BaseCache{ + backend: backend, + disabled: disabled, + retrieveFunc: retrieveFunc, + } +} + +// BaseCache is a cache which retrieves data from the backend and stores it in the cache. +type BaseCache struct { + backend backend.Backend + + disabled bool + retrieveFunc RetrieveFunc + g singleflight.Group +} + +// EmptyCache is a placeholder for the missing key +type EmptyCache struct { + err error +} + +// Exists returns true if the cache has a value for the given key. +func (c *BaseCache) Exists(key cache.Key) bool { + k := key.Key() + _, ok := c.backend.Get(k) + return ok +} + +// Get will get the key from cache, if missing, will call the retrieveFunc to get the data, add to cache, then return +func (c *BaseCache) Get(key cache.Key) (interface{}, error) { + // 1. if cache is disabled, fetch and return + if c.disabled { + value, err := c.retrieveFunc(key) + if err != nil { + return nil, err + } + return value, nil + } + + k := key.Key() + + // 2. get from cache + value, ok := c.backend.Get(k) + if ok { + // if retrieve fail from retrieveFunc + if emptyCache, isEmptyCache := value.(EmptyCache); isEmptyCache { + return nil, emptyCache.err + } + return value, nil + } + + // 3. if not exists in cache, retrieve it + return c.doRetrieve(key) +} + +// doRetrieve will retrieve the real data from database, redis, apis, etc. +func (c *BaseCache) doRetrieve(k cache.Key) (interface{}, error) { + key := k.Key() + + // 3.2 fetch + value, err, _ := c.g.Do(key, func() (interface{}, error) { + return c.retrieveFunc(k) + }) + + if err != nil { + // ! if error, cache it too, make it short enough(5s) + c.backend.Set(key, EmptyCache{err: err}, EmptyCacheExpiration) + return nil, err + } + + // 4. set value to cache, use default expiration + c.backend.Set(key, value, 0) + + return value, nil +} + +// Set will set key-value into cache. +func (c *BaseCache) Set(key cache.Key, data interface{}) { + k := key.Key() + c.backend.Set(k, data, 0) +} + +// Delete deletes the value from the cache for the given key. +func (c *BaseCache) Delete(key cache.Key) error { + k := key.Key() + return c.backend.Delete(k) +} + +// DirectGet will get key from cache, without calling the retrieveFunc +func (c *BaseCache) DirectGet(key cache.Key) (interface{}, bool) { + k := key.Key() + return c.backend.Get(k) +} + +// Disabled returns true if the cache is disabled. +func (c *BaseCache) Disabled() bool { + return c.disabled +} + +// TODO: 这里需要实现所有类型的 GetXXXX + +// GetString returns a string representation of the value for the given key. +// will error if the type is not a string. +func (c *BaseCache) GetString(k cache.Key) (string, error) { + value, err := c.Get(k) + if err != nil { + return "", err + } + + v, ok := value.(string) + if !ok { + return "", fmt.Errorf("not a string value. key=%s, value=%v(%T)", k.Key(), value, value) + } + return v, nil +} + +// GetBool returns a bool representation of the value for the given key. +// will error if the type is not a bool. +func (c *BaseCache) GetBool(k cache.Key) (bool, error) { + value, err := c.Get(k) + if err != nil { + return false, err + } + + v, ok := value.(bool) + if !ok { + return false, fmt.Errorf("not a string value. key=%s, value=%v(%T)", k.Key(), value, value) + } + return v, nil +} + +var defaultZeroTime = time.Time{} + +// GetTime returns a time representation of the value for the given key. +// will error if the type is not an time.Time. +func (c *BaseCache) GetTime(k cache.Key) (time.Time, error) { + value, err := c.Get(k) + if err != nil { + return defaultZeroTime, err + } + + v, ok := value.(time.Time) + if !ok { + return defaultZeroTime, fmt.Errorf("not a string value. key=%s, value=%v(%T)", k.Key(), value, value) + } + return v, nil +} + +// GetInt returns an int representation of the value for the given key. +// will error if the type is not an int. +func (c *BaseCache) GetInt(k cache.Key) (int, error) { + value, err := c.Get(k) + if err != nil { + return 0, err + } + + v, ok := value.(int) + if !ok { + return 0, fmt.Errorf("not a int value. key=%s, value=%v(%T)", k.Key(), value, value) + } + return v, nil +} + +// GetInt8 returns an int8 representation of the value for the given key. +// will error if the type is not an int8. +func (c *BaseCache) GetInt8(k cache.Key) (int8, error) { + value, err := c.Get(k) + if err != nil { + return 0, err + } + + v, ok := value.(int8) + if !ok { + return 0, fmt.Errorf("not a int8 value. key=%s, value=%v(%T)", k.Key(), value, value) + } + return v, nil +} + +// GetInt16 returns an int16 representation of the value for the given key. +// will error if the type is not an int16. +func (c *BaseCache) GetInt16(k cache.Key) (int16, error) { + value, err := c.Get(k) + if err != nil { + return 0, err + } + + v, ok := value.(int16) + if !ok { + return 0, fmt.Errorf("not a int16 value. key=%s, value=%v(%T)", k.Key(), value, value) + } + return v, nil +} + +// GetInt32 returns an int32 representation of the value for the given key. +// will error if the type is not an int32. +func (c *BaseCache) GetInt32(k cache.Key) (int32, error) { + value, err := c.Get(k) + if err != nil { + return 0, err + } + + v, ok := value.(int32) + if !ok { + return 0, fmt.Errorf("not a int32 value. key=%s, value=%v(%T)", k.Key(), value, value) + } + return v, nil +} + +// GetInt64 returns an int64 representation of the value for the given key. +// will error if the type is not an int64. +func (c *BaseCache) GetInt64(k cache.Key) (int64, error) { + value, err := c.Get(k) + if err != nil { + return 0, err + } + + v, ok := value.(int64) + if !ok { + return 0, fmt.Errorf("not a int64 value. key=%s, value=%v(%T)", k.Key(), value, value) + } + return v, nil +} + +// GetUint returns an uint representation of the value for the given key. +// will error if the type is not an uint. +func (c *BaseCache) GetUint(k cache.Key) (uint, error) { + value, err := c.Get(k) + if err != nil { + return 0, err + } + + v, ok := value.(uint) + if !ok { + return 0, fmt.Errorf("not a uint value. key=%s, value=%v(%T)", k.Key(), value, value) + } + return v, nil +} + +// GetUint8 returns an uint8 representation of the value for the given key. +// will error if the type is not an uint8. +func (c *BaseCache) GetUint8(k cache.Key) (uint8, error) { + value, err := c.Get(k) + if err != nil { + return 0, err + } + + v, ok := value.(uint8) + if !ok { + return 0, fmt.Errorf("not a uint8 value. key=%s, value=%v(%T)", k.Key(), value, value) + } + return v, nil +} + +// GetUint16 returns an uint16 representation of the value for the given key. +// will error if the type is not an uint16. +func (c *BaseCache) GetUint16(k cache.Key) (uint16, error) { + value, err := c.Get(k) + if err != nil { + return 0, err + } + + v, ok := value.(uint16) + if !ok { + return 0, fmt.Errorf("not a uint16 value. key=%s, value=%v(%T)", k.Key(), value, value) + } + return v, nil +} + +// GetUint32 returns an uint32 representation of the value for the given key. +// will error if the type is not an uint32. +func (c *BaseCache) GetUint32(k cache.Key) (uint32, error) { + value, err := c.Get(k) + if err != nil { + return 0, err + } + + v, ok := value.(uint32) + if !ok { + return 0, fmt.Errorf("not a uint32 value. key=%s, value=%v(%T)", k.Key(), value, value) + } + return v, nil +} + +// GetUint64 returns an uint64 representation of the value for the given key. +// will error if the type is not an uint64. +func (c *BaseCache) GetUint64(k cache.Key) (uint64, error) { + value, err := c.Get(k) + if err != nil { + return 0, err + } + + v, ok := value.(uint64) + if !ok { + return 0, fmt.Errorf("not a uint64 value. key=%s, value=%v(%T)", k.Key(), value, value) + } + return v, nil +} + +// GetFloat32 returns a float32 representation of the value for the given key. +// will error if the type is not a float32. +func (c *BaseCache) GetFloat32(k cache.Key) (float32, error) { + value, err := c.Get(k) + if err != nil { + return 0, err + } + + v, ok := value.(float32) + if !ok { + return 0, fmt.Errorf("not a float32 value. key=%s, value=%v(%T)", k.Key(), value, value) + } + return v, nil +} + +// GetFloat64 returns a float64 representation of the value for the given key. +// will error if the type is not a float64. +func (c *BaseCache) GetFloat64(k cache.Key) (float64, error) { + value, err := c.Get(k) + if err != nil { + return 0, err + } + + v, ok := value.(float64) + if !ok { + return 0, fmt.Errorf("not a float64 value. key=%s, value=%v(%T)", k.Key(), value, value) + } + return v, nil +} diff --git a/pkg/cache/memory/cache.go b/vendor/github.com/TencentBlueKing/gopkg/cache/memory/cache.go similarity index 62% rename from pkg/cache/memory/cache.go rename to vendor/github.com/TencentBlueKing/gopkg/cache/memory/cache.go index 1f7f24e1..baeab992 100644 --- a/pkg/cache/memory/cache.go +++ b/vendor/github.com/TencentBlueKing/gopkg/cache/memory/cache.go @@ -1,5 +1,6 @@ /* - * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-权限中心(BlueKing-IAM) available. + * TencentBlueKing is pleased to support the open source community by making + * 蓝鲸智云-gopkg available. * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://opensource.org/licenses/MIT @@ -13,16 +14,23 @@ package memory import ( "time" - "iam/pkg/cache/memory/backend" + "github.com/TencentBlueKing/gopkg/cache/memory/backend" ) -// NewCache create a memory cache -func NewCache(name string, disabled bool, +// NewCache creates a new cache object. +// - name: the cache name. +// - disabled: whether the cache is disabled. +// - retrieveFunc: the function to retrieve the real data. +// - expiration: the expiration time. +// - randomExtraExpirationFunc: the function to generate a random duration, used to add extra expiration for each key. +func NewCache( + name string, + disabled bool, retrieveFunc RetrieveFunc, expiration time.Duration, - randomDurationFunc backend.RandomExpirationDurationFunc, + randomExtraExpirationFunc backend.RandomExtraExpirationDurationFunc, ) Cache { - be := backend.NewMemoryBackend(name, expiration, randomDurationFunc) + be := backend.NewMemoryBackend(name, expiration, randomExtraExpirationFunc) return NewBaseCache(disabled, retrieveFunc, be) } diff --git a/vendor/github.com/TencentBlueKing/gopkg/cache/memory/readme.md b/vendor/github.com/TencentBlueKing/gopkg/cache/memory/readme.md new file mode 100644 index 00000000..db72700f --- /dev/null +++ b/vendor/github.com/TencentBlueKing/gopkg/cache/memory/readme.md @@ -0,0 +1,137 @@ +# cache + +cache the data in memory, auto to fetch the data if missing. + +- based on via [patrickmn/go-cache](https://github.com/patrickmn/go-cache) +- retrieveFunc will be called if the key not in cache +- TTL required +- cache the missing key for 5s, avoid `cache penetration` +- use [singleflight ](https://godoc.org/golang.org/x/sync/singleflight) to avoid `cache breakdown` +- cache in memory, no need to worry about `cache avalanche` +- support go version 1.11+ + +## usage + +#### use string key + +```go +package main + +import ( + "fmt" + "time" + + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/cache/memory" +) + +// 1. impl the reterive func +func RetrieveOK(k cache.Key) (interface{}, error) { + arg := k.(memory.StringKey) + fmt.Println("arg: ", arg) + // you can use the arg to fetch data from database or http request + // username, err := GetFromDatabase(arg) + // if err != nil { + // return nil, err + // } + return "ok", nil +} + +func main() { + // 2. new a cache + c := memory.NewCache( + "example", + false, + RetrieveOK, + 5*time.Minute, + nil) + + // 4. use it + k := memory.NewStringKey("hello") + + data, err := c.Get(k) + fmt.Println("err == nil: ", err == nil) + fmt.Println("data from cache: ", data) +} +``` + +#### use your own key + + +```go +package main + +import ( + "fmt" + "time" + + "github.com/TencentBlueKing/gopkg/cache" + "github.com/TencentBlueKing/gopkg/cache/memory" +) + +// 1. impl the key interface, Key() string +type ExampleKey struct { + Field1 string + Field2 int64 +} + +func (k ExampleKey) Key() string { + return fmt.Sprintf("%s:%d", k.Field1, k.Field2) +} + +// 2. impl the reterive func +func RetrieveExample(inKey cache.Key) (interface{}, error) { + k := inKey.(ExampleKey) + fmt.Println("ExampleKey Field1 and Field2 value:", k.Field1, k.Field2) + // data, err := GetFromDatabase(k.Field1, k.Field2) + // if err != nil { + // return nil, err + // } + return "world", nil +} + +func main() { + // 3. new a cache + c := memory.NewCache( + "example", + false, + RetrieveExample, + 5*time.Minute, + nil) + + // 4. use it + k := ExampleKey{ + Field1: "hello", + Field2: 42, + } + + data, err := c.Get(k) + fmt.Println("err == nil: ", err == nil) + fmt.Println("data from cache: ", data) + + dataStr, err := c.GetString(k) + fmt.Println("err == nil: ", err == nil) + fmt.Printf("data type is %T, value is %s\n", dataStr, dataStr) +} +``` + +#### add a random expired duration + +打散过期时间, 防止并发较高的场景下, 同时失效发起retrieve导致后端压力过大 + + +```go +func newRandomDuration(seconds int) backend.RandomExpirationDurationFunc { + return func() time.Duration { + return time.Duration(rand.Intn(seconds*1000)) * time.Millisecond + } +} + +LocalSubjectCache = memory.NewCache( + "local_subject", + disabled, + retrieveSubject, + 1*time.Minute, + newRandomDuration(30), +) +``` diff --git a/pkg/cache/memory/types.go b/vendor/github.com/TencentBlueKing/gopkg/cache/memory/types.go similarity index 62% rename from pkg/cache/memory/types.go rename to vendor/github.com/TencentBlueKing/gopkg/cache/memory/types.go index 4656a3fd..328c8338 100644 --- a/pkg/cache/memory/types.go +++ b/vendor/github.com/TencentBlueKing/gopkg/cache/memory/types.go @@ -1,5 +1,6 @@ /* - * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-权限中心(BlueKing-IAM) available. + * TencentBlueKing is pleased to support the open source community by making + * 蓝鲸智云-gopkg available. * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://opensource.org/licenses/MIT @@ -13,21 +14,33 @@ package memory import ( "time" - "iam/pkg/cache" + "github.com/TencentBlueKing/gopkg/cache" ) -// RetrieveFunc ... +// RetrieveFunc is the type of the retrieve function. +// it retrieves the value from database, redis, apis, etc. type RetrieveFunc func(key cache.Key) (interface{}, error) -// Cache ... +// Cache is the interface for the cache. type Cache interface { Get(key cache.Key) (interface{}, error) Set(key cache.Key, data interface{}) GetString(key cache.Key) (string, error) GetBool(key cache.Key) (bool, error) - GetTime(key cache.Key) (time.Time, error) + GetInt(key cache.Key) (int, error) + GetInt8(key cache.Key) (int8, error) + GetInt16(key cache.Key) (int16, error) + GetInt32(key cache.Key) (int32, error) GetInt64(key cache.Key) (int64, error) + GetUint(key cache.Key) (uint, error) + GetUint8(key cache.Key) (uint8, error) + GetUint16(key cache.Key) (uint16, error) + GetUint32(key cache.Key) (uint32, error) + GetUint64(key cache.Key) (uint64, error) + GetFloat32(key cache.Key) (float32, error) + GetFloat64(key cache.Key) (float64, error) + GetTime(key cache.Key) (time.Time, error) Delete(key cache.Key) error Exists(key cache.Key) bool diff --git a/vendor/github.com/TencentBlueKing/gopkg/collection/set/int64.go b/vendor/github.com/TencentBlueKing/gopkg/collection/set/int64.go new file mode 100644 index 00000000..2c01348c --- /dev/null +++ b/vendor/github.com/TencentBlueKing/gopkg/collection/set/int64.go @@ -0,0 +1,74 @@ +/* + * TencentBlueKing is pleased to support the open source community by making + * 蓝鲸智云-gopkg available. + * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. + * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://opensource.org/licenses/MIT + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package set + +// Int64Set is a set of int64 +type Int64Set struct { + Data map[int64]struct{} +} + +// Has return true if set contains the key +func (s *Int64Set) Has(key int64) bool { + _, ok := s.Data[key] + return ok +} + +// Add a key into set +func (s *Int64Set) Add(key int64) { + s.Data[key] = struct{}{} +} + +// Append append keys into set +func (s *Int64Set) Append(keys ...int64) { + for _, key := range keys { + s.Data[key] = struct{}{} + } +} + +// Size return the size of set +func (s *Int64Set) Size() int { + return len(s.Data) +} + +// ToSlice return key slice +func (s *Int64Set) ToSlice() []int64 { + l := make([]int64, 0, len(s.Data)) + for k := range s.Data { + l = append(l, k) + } + return l +} + +// NewInt64Set make a int64 set +func NewInt64Set() *Int64Set { + return &Int64Set{ + Data: map[int64]struct{}{}, + } +} + +// NewInt64SetWithValues make a int64 set with values +func NewInt64SetWithValues(keys []int64) *Int64Set { + set := &Int64Set{ + Data: map[int64]struct{}{}, + } + for _, key := range keys { + set.Add(key) + } + return set +} + +// NewFixedLengthInt64Set make a int64 set with fixed length +func NewFixedLengthInt64Set(length int) *Int64Set { + return &Int64Set{ + Data: make(map[int64]struct{}, length), + } +} diff --git a/vendor/github.com/TencentBlueKing/gopkg/collection/set/readme.md b/vendor/github.com/TencentBlueKing/gopkg/collection/set/readme.md new file mode 100644 index 00000000..907836f9 --- /dev/null +++ b/vendor/github.com/TencentBlueKing/gopkg/collection/set/readme.md @@ -0,0 +1,45 @@ +# collection/set + +`set`底层使用`map`实现, 封装了set类操作 + +目前支持类型: + +- string +- int64 + +注意, 目前未使用泛型, 所以没有统一的interface + + +## Usage + +### String Set + +```go +import "github.com/TencentBlueKing/gopkg/collection/set" + +s := set.NewStringSet() +// s := set.NewStringSetWithValues([]string{"hello", "world"}) +// s := set.NewFixedLengthStringSet(2) +// s := set.SplitStringToSet("a,b,c", ",") +s.Add("abc") +s.Has("abc") +s.Append([]string{"abc", "def"}...) +s.Size() +sli1 := s.ToSlice() +s1 := s.ToString(",") +``` + +### Int64 Set + +```go +import "github.com/TencentBlueKing/gopkg/collection/set" + +s := set.NewInt64Set() +// s := set.NewInt64SetWithValues([]int64{123, 456}) +// s := set.NewFixedLengthInt64Set(2) +s.Add(123) +s.Has(123) +s.Append([]int64{123, 456}...) +s.Size() +sli1 := s.ToSlice() +``` diff --git a/pkg/util/set.go b/vendor/github.com/TencentBlueKing/gopkg/collection/set/string.go similarity index 61% rename from pkg/util/set.go rename to vendor/github.com/TencentBlueKing/gopkg/collection/set/string.go index 76254e3b..78dd84b0 100644 --- a/pkg/util/set.go +++ b/vendor/github.com/TencentBlueKing/gopkg/collection/set/string.go @@ -1,5 +1,6 @@ /* - * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-权限中心(BlueKing-IAM) available. + * TencentBlueKing is pleased to support the open source community by making + * 蓝鲸智云-gopkg available. * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://opensource.org/licenses/MIT @@ -8,64 +9,39 @@ * specific language governing permissions and limitations under the License. */ -package util +package set import "strings" -// StringSet ... +// StringSet is a set of string type StringSet struct { Data map[string]struct{} } -// NewStringSet ... -func NewStringSet() *StringSet { - return &StringSet{ - Data: map[string]struct{}{}, - } -} - -// NewStringSetWithValues ... -func NewStringSetWithValues(keys []string) *StringSet { - set := &StringSet{ - Data: map[string]struct{}{}, - } - for _, key := range keys { - set.Add(key) - } - return set -} - -// NewFixedLengthStringSet ... -func NewFixedLengthStringSet(length int) *StringSet { - return &StringSet{ - Data: make(map[string]struct{}, length), - } -} - -// Has ... +// Has return true if set contains the key func (s *StringSet) Has(key string) bool { _, ok := s.Data[key] return ok } -// Add ... +// Add a key into set func (s *StringSet) Add(key string) { s.Data[key] = struct{}{} } -// Append ... +// Append append keys into set func (s *StringSet) Append(keys ...string) { for _, key := range keys { s.Data[key] = struct{}{} } } -// Size ... +// Size return the size of set func (s *StringSet) Size() int { return len(s.Data) } -// ToSlice ... +// ToSlice return key slice func (s *StringSet) ToSlice() []string { l := make([]string, 0, len(s.Data)) for k := range s.Data { @@ -74,13 +50,13 @@ func (s *StringSet) ToSlice() []string { return l } -// ToString ... +// ToString join the string with sep func (s *StringSet) ToString(sep string) string { l := s.ToSlice() return strings.Join(l, sep) } -// Diff 求差集 +// Diff will return the difference of two set func (s *StringSet) Diff(b *StringSet) *StringSet { diffSet := NewStringSet() @@ -92,36 +68,18 @@ func (s *StringSet) Diff(b *StringSet) *StringSet { return diffSet } -// SplitStringToSet ... -func SplitStringToSet(s string, sep string) *StringSet { - if s == "" { - return &StringSet{Data: map[string]struct{}{}} - } - data := map[string]struct{}{} - keys := strings.Split(s, sep) - for _, key := range keys { - data[key] = struct{}{} - } - return &StringSet{Data: data} -} - -// Int64Set ... -type Int64Set struct { - Data map[int64]struct{} -} - -// NewInt64Set ... -func NewInt64Set() *Int64Set { - return &Int64Set{ - Data: map[int64]struct{}{}, +// NewStringSet make a string set +func NewStringSet() *StringSet { + return &StringSet{ + Data: map[string]struct{}{}, } } -// NewInt64SetWithValues ... -func NewInt64SetWithValues(keys []int64) *Int64Set { - set := &Int64Set{ - Data: map[int64]struct{}{}, +// NewStringSetWithValues make a string set with values +func NewStringSetWithValues(keys []string) *StringSet { + set := &StringSet{ + Data: map[string]struct{}{}, } for _, key := range keys { set.Add(key) @@ -129,41 +87,23 @@ func NewInt64SetWithValues(keys []int64) *Int64Set { return set } -// NewFixedLengthInt64Set ... -func NewFixedLengthInt64Set(length int) *Int64Set { - return &Int64Set{ - Data: make(map[int64]struct{}, length), +// NewFixedLengthStringSet make a string set with fixed length +func NewFixedLengthStringSet(length int) *StringSet { + return &StringSet{ + Data: make(map[string]struct{}, length), } } -// Has ... -func (s *Int64Set) Has(key int64) bool { - _, ok := s.Data[key] - return ok -} - -// Add ... -func (s *Int64Set) Add(key int64) { - s.Data[key] = struct{}{} -} - -// Append ... -func (s *Int64Set) Append(keys ...int64) { - for _, key := range keys { - s.Data[key] = struct{}{} +// SplitStringToSet make a string set by split a string into parts +func SplitStringToSet(s string, sep string) *StringSet { + if s == "" { + return &StringSet{Data: map[string]struct{}{}} } -} - -// Size ... -func (s *Int64Set) Size() int { - return len(s.Data) -} -// ToSlice ... -func (s *Int64Set) ToSlice() []int64 { - l := make([]int64, 0, len(s.Data)) - for k := range s.Data { - l = append(l, k) + data := map[string]struct{}{} + keys := strings.Split(s, sep) + for _, key := range keys { + data[key] = struct{}{} } - return l + return &StringSet{Data: data} } diff --git a/pkg/cache/types.go b/vendor/github.com/TencentBlueKing/gopkg/conv/int64.go similarity index 54% rename from pkg/cache/types.go rename to vendor/github.com/TencentBlueKing/gopkg/conv/int64.go index 4cdaaea4..b7d50b3f 100644 --- a/pkg/cache/types.go +++ b/vendor/github.com/TencentBlueKing/gopkg/conv/int64.go @@ -1,5 +1,6 @@ /* - * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-权限中心(BlueKing-IAM) available. + * TencentBlueKing is pleased to support the open source community by making + * 蓝鲸智云-gopkg available. * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://opensource.org/licenses/MIT @@ -8,47 +9,31 @@ * specific language governing permissions and limitations under the License. */ -package cache +package conv import ( + "fmt" "strconv" ) -// Key ... -type Key interface { - Key() string -} - -// StringKey ... -type StringKey struct { - key string -} - -// NewStringKey ... -func NewStringKey(key string) StringKey { - return StringKey{ - key: key, +// ToInt64 casts a interface to an int64 +func ToInt64(i interface{}) (int64, error) { + switch s := i.(type) { + case int: + return int64(s), nil + case int64: + return s, nil + case string: + v, err := strconv.ParseInt(s, 0, 64) + if err == nil { + return v, nil + } + return 0, fmt.Errorf("unable to cast %#v to int64, %w", i, err) + case float64: + return int64(s), nil + case nil: + return 0, nil + default: + return 0, fmt.Errorf("unable to cast %#v to int64, unsupported type", i) } } - -// Key ... -func (s StringKey) Key() string { - return s.key -} - -// Int64Key ... -type Int64Key struct { - key int64 -} - -// NewInt64Key ... -func NewInt64Key(key int64) Int64Key { - return Int64Key{ - key: key, - } -} - -// Key ... -func (k Int64Key) Key() string { - return strconv.FormatInt(k.key, 10) -} diff --git a/vendor/github.com/TencentBlueKing/gopkg/conv/readme.md b/vendor/github.com/TencentBlueKing/gopkg/conv/readme.md new file mode 100644 index 00000000..27e28b04 --- /dev/null +++ b/vendor/github.com/TencentBlueKing/gopkg/conv/readme.md @@ -0,0 +1,22 @@ +# conv + +`conv` 实现了一些类型转换函数 + + +## Usage + +### string - bytes + +```go +import "github.com/TencentBlueKing/gopkg/conv" + +conv.StringToBytes("hello world") +conv.BytesToString([]byte("hello world")) +conv.ToString(123) + +conv.ToInt64("123") + +var i interface{} +i = []int{123} +conv.ToSlice(i) +``` diff --git a/pkg/util/bytesconv.go b/vendor/github.com/TencentBlueKing/gopkg/conv/slice.go similarity index 55% rename from pkg/util/bytesconv.go rename to vendor/github.com/TencentBlueKing/gopkg/conv/slice.go index 94c52c7d..9ec760c3 100644 --- a/pkg/util/bytesconv.go +++ b/vendor/github.com/TencentBlueKing/gopkg/conv/slice.go @@ -1,5 +1,6 @@ /* - * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-权限中心(BlueKing-IAM) available. + * TencentBlueKing is pleased to support the open source community by making + * 蓝鲸智云-gopkg available. * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://opensource.org/licenses/MIT @@ -8,24 +9,26 @@ * specific language governing permissions and limitations under the License. */ -package util +package conv import ( + "errors" "reflect" - "unsafe" ) -// StringToBytes converts string to byte slice without a memory allocation. -func StringToBytes(s string) (b []byte) { - // nolint:govet - sh := *(*reflect.StringHeader)(unsafe.Pointer(&s)) - // nolint:govet - bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) - bh.Data, bh.Len, bh.Cap = sh.Data, sh.Len, sh.Len - return b -} +var ErrNotArray = errors.New("only support array") -// BytesToString converts byte slice to string without a memory allocation. -func BytesToString(b []byte) string { - return *(*string)(unsafe.Pointer(&b)) +// ToSlice conv an array-interface to []interface{} +// will error if the type is not slice +func ToSlice(array interface{}) ([]interface{}, error) { + v := reflect.ValueOf(array) + if v.Kind() != reflect.Slice { + return nil, ErrNotArray + } + l := v.Len() + ret := make([]interface{}, l) + for i := 0; i < l; i++ { + ret[i] = v.Index(i).Interface() + } + return ret, nil } diff --git a/vendor/github.com/TencentBlueKing/gopkg/conv/string.go b/vendor/github.com/TencentBlueKing/gopkg/conv/string.go new file mode 100644 index 00000000..e1ff070d --- /dev/null +++ b/vendor/github.com/TencentBlueKing/gopkg/conv/string.go @@ -0,0 +1,78 @@ +/* + * TencentBlueKing is pleased to support the open source community by making + * 蓝鲸智云-gopkg available. + * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. + * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://opensource.org/licenses/MIT + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package conv + +import ( + "fmt" + "reflect" + "strconv" + "unsafe" +) + +// StringToBytes converts string to byte slice without a memory allocation. +func StringToBytes(s string) (b []byte) { + // nolint:govet + sh := *(*reflect.StringHeader)(unsafe.Pointer(&s)) + // nolint:govet + bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + bh.Data, bh.Len, bh.Cap = sh.Data, sh.Len, sh.Len + return b +} + +// BytesToString converts byte slice to string without a memory allocation. +func BytesToString(b []byte) string { + return *(*string)(unsafe.Pointer(&b)) +} + +// ToString casts a interface to a string. +func ToString(i interface{}) string { + switch s := i.(type) { + case string: + return s + case bool: + return strconv.FormatBool(s) + case float64: + return strconv.FormatFloat(i.(float64), 'f', -1, 64) + case float32: + return strconv.FormatFloat(float64(i.(float32)), 'f', -1, 64) + case int: + return strconv.FormatInt(int64(i.(int)), 10) + case int8: + return strconv.FormatInt(int64(i.(int8)), 10) + case int16: + return strconv.FormatInt(int64(i.(int16)), 10) + case int32: + return strconv.FormatInt(int64(i.(int32)), 10) + case int64: + return strconv.FormatInt(i.(int64), 10) + case uint: + return strconv.FormatUint(uint64(i.(uint)), 10) + case uint8: + return strconv.FormatUint(uint64(i.(uint8)), 10) + case uint16: + return strconv.FormatUint(uint64(i.(uint16)), 10) + case uint32: + return strconv.FormatUint(uint64(i.(uint32)), 10) + case uint64: + return strconv.FormatUint(i.(uint64), 10) + case []byte: + return string(s) + case nil: + return "" + case error: + return s.Error() + case fmt.Stringer: + return s.String() + default: + return fmt.Sprint(i) + } +} diff --git a/pkg/errorx/error.go b/vendor/github.com/TencentBlueKing/gopkg/errorx/errorx.go similarity index 57% rename from pkg/errorx/error.go rename to vendor/github.com/TencentBlueKing/gopkg/errorx/errorx.go index 7906c55f..eaa67def 100644 --- a/pkg/errorx/error.go +++ b/vendor/github.com/TencentBlueKing/gopkg/errorx/errorx.go @@ -1,5 +1,6 @@ /* - * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-权限中心(BlueKing-IAM) available. + * TencentBlueKing is pleased to support the open source community by making + * 蓝鲸智云-gopkg available. * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://opensource.org/licenses/MIT @@ -10,69 +11,70 @@ package errorx -import ( - "errors" - "fmt" -) +/* +Package `errorx` implements a custom error, wrap/wrapf with detail formatted message -// IAMError is a wrapped struct for err -type IAMError struct { - message string - err error -} +The usage: -// Error show the error message -func (e IAMError) Error() string { - return e.message -} +1. raw wrap -// Is check if the error is target -func (e IAMError) Is(target error) bool { - if target == nil || e.err == nil { - return e.err == target - } + import "github.com/TencentBlueKing/gopkg/errorx" - return errors.Is(e.err, target) -} + cnt, err := l.relationManager.GetMemberCount(_type, id) + if err != nil { + return errorx.Wrapf(err, "ServiceLayer", "GetMemberCount", + "relationManager.GetMemberCount _type=`%s`, id=`%s` fail", _type, id) + } -// Unwrap will unwrap the wrapped error -func (e *IAMError) Unwrap() error { - u, ok := e.err.(interface { - Unwrap() error - }) - if !ok { - return e.err - } +2. in func with multiple returns - return u.Unwrap() -} + import "github.com/TencentBlueKing/gopkg/errorx" + + // create a func with layer name and function name + errorWrapf := errorx.NewLayerFunctionErrorWrapf("ServiceLayer", "BulkDeleteSubjectMember") + + if err != nil { + return errorWrapf(err, "relationManager.UpdateExpiredAt relations=`%+v` fail", relations) + } + + // ... + + if err != nil { + return errorWrapf(err, "relationManager.DoSomething relations=`%+v` fail", relations) + } +*/ + +import ( + "errors" + "fmt" +) +// make the message for error wrap func makeMessage(err error, layer, function, msg string) string { var message string - var e IAMError + var e Errorx if errors.As(err, &e) { message = fmt.Sprintf("[%s:%s] %s => %s", layer, function, msg, err.Error()) } else { - // jsoniter.marshal/unmarshal error not print, others? message = fmt.Sprintf("[%s:%s] %s => [Raw:Error] %v", layer, function, msg, err.Error()) } return message } -// Wrap will wrap the error with layer, function and message +// Wrap the error with message func Wrap(err error, layer string, function string, message string) error { if err == nil { return nil } - return IAMError{ + return Errorx{ message: makeMessage(err, layer, function, message), err: err, } } -// Wrapf will wrap the error with layer, function, and format the message with args +// Wrapf the error with formatted message, shortcut for func Wrapf(err error, layer string, function string, format string, args ...interface{}) error { if err == nil { return nil @@ -80,26 +82,26 @@ func Wrapf(err error, layer string, function string, format string, args ...inte msg := fmt.Sprintf(format, args...) - return IAMError{ + return Errorx{ message: makeMessage(err, layer, function, msg), err: err, } } -// WrapFuncWithLayerFunction is a type alias for Wrap func +// WrapFuncWithLayerFunction define the func of wrapError for partial specific layer name and function name type WrapFuncWithLayerFunction func(err error, message string) error -// WrapfFuncWithLayerFunction is a type alias for Wrapf func +// WrapfFuncWithLayerFunction define the func of wrapfError for partial specific layer name and function name type WrapfFuncWithLayerFunction func(err error, format string, args ...interface{}) error -// NewLayerFunctionErrorWrap will create a Wrap func with specific layer and function +// NewLayerFunctionErrorWrap create the wrapError func with specific layer and func func NewLayerFunctionErrorWrap(layer string, function string) WrapFuncWithLayerFunction { return func(err error, message string) error { return Wrap(err, layer, function, message) } } -// NewLayerFunctionErrorWrapf will create a Wrapf func with specific layer and function +// NewLayerFunctionErrorWrapf create the wrapfError func with specific layer and func func NewLayerFunctionErrorWrapf(layer string, function string) WrapfFuncWithLayerFunction { return func(err error, format string, args ...interface{}) error { return Wrapf(err, layer, function, format, args...) diff --git a/vendor/github.com/TencentBlueKing/gopkg/errorx/readme.md b/vendor/github.com/TencentBlueKing/gopkg/errorx/readme.md new file mode 100644 index 00000000..5cd92f40 --- /dev/null +++ b/vendor/github.com/TencentBlueKing/gopkg/errorx/readme.md @@ -0,0 +1,47 @@ +# errorx + +`errorx`实现了一个自定义error, 可以进行错误详情wrap. + +封装的`Wrap/Wrapf`存在两个参数: `layer`层级, `function`函数名, 可以方便地定位报错调用链的层级及函数. + +最终效果: + +``` +system error[request_id=0838c4090cfa4f4f9dceea5fd8c7b029]: [Handler:validateSystemSuperUser] impls.ListSubjectRoleSystemID subjectType=`user`, subjectID=`test1` fail%!(EXTRA string=test1) => [Cache:GetSubjectRole] SubjectRoleCache.Get subjectType=`user`, subjectID=`test1` fail => [Cache:GetSubjectPK] SubjectPKCache.Get _type=`user`, id=`test1` fail => [SubjectSVC:GetPK] GetPK _type=`user`, id=`test1` fail => [Raw:Error] sql: no rows in result set +``` + + +## Usage + +### 1. 直接wrap + + +```go +import "github.com/TencentBlueKing/gopkg/errorx" + +cnt, err := l.relationManager.GetMemberCount(_type, id) +if err != nil { + return errorx.Wrapf(err, "ServiceLayer", "GetMemberCount", + "relationManager.GetMemberCount _type=`%s`, id=`%s` fail", _type, id) +} +``` + +### 2. 函数中存在多个return + + +```go +import "github.com/TencentBlueKing/gopkg/errorx" + +// create a func with layer name and function name +errorWrapf := errorx.NewLayerFunctionErrorWrapf("ServiceLayer", "BulkDeleteSubjectMember") + +if err != nil { + return errorWrapf(err, "relationManager.UpdateExpiredAt relations=`%+v` fail", relations) +} + +// ... + +if err != nil { + return errorWrapf(err, "relationManager.DoSomething relations=`%+v` fail", relations) +} +``` diff --git a/vendor/github.com/TencentBlueKing/gopkg/errorx/types.go b/vendor/github.com/TencentBlueKing/gopkg/errorx/types.go new file mode 100644 index 00000000..bfe89221 --- /dev/null +++ b/vendor/github.com/TencentBlueKing/gopkg/errorx/types.go @@ -0,0 +1,50 @@ +/* + * TencentBlueKing is pleased to support the open source community by making + * 蓝鲸智云-gopkg available. + * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. + * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://opensource.org/licenses/MIT + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package errorx + +import ( + "errors" +) + +// Errorx is a struct for wrap raw err with message +type Errorx struct { + message string + err error +} + +// Error return the error message +func (e Errorx) Error() string { + return e.message +} + +// Is reports whether any error in err's chain matches target. +func (e Errorx) Is(target error) bool { + if target == nil || e.err == nil { + return e.err == target + } + + return errors.Is(e.err, target) +} + +// Unwrap returns the result of calling the Unwrap method on err, if err's +// type contains an Unwrap method returning error. +// Otherwise, Unwrap returns nil. +func (e *Errorx) Unwrap() error { + u, ok := e.err.(interface { + Unwrap() error + }) + if !ok { + return e.err + } + + return u.Unwrap() +} diff --git a/pkg/util/hash.go b/vendor/github.com/TencentBlueKing/gopkg/stringx/hash.go similarity index 85% rename from pkg/util/hash.go rename to vendor/github.com/TencentBlueKing/gopkg/stringx/hash.go index e64ea883..41af29a1 100644 --- a/pkg/util/hash.go +++ b/vendor/github.com/TencentBlueKing/gopkg/stringx/hash.go @@ -1,5 +1,6 @@ /* - * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-权限中心(BlueKing-IAM) available. + * TencentBlueKing is pleased to support the open source community by making + * 蓝鲸智云-gopkg available. * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://opensource.org/licenses/MIT @@ -8,15 +9,15 @@ * specific language governing permissions and limitations under the License. */ -package util +package stringx import ( "crypto/md5" "encoding/hex" ) -// GetMD5Hash ... -func GetMD5Hash(text string) string { +// MD5Hash calculate the md5 of string +func MD5Hash(text string) string { hash := md5.Sum([]byte(text)) return hex.EncodeToString(hash[:]) } diff --git a/vendor/github.com/TencentBlueKing/gopkg/stringx/readme.md b/vendor/github.com/TencentBlueKing/gopkg/stringx/readme.md new file mode 100644 index 00000000..13599a9f --- /dev/null +++ b/vendor/github.com/TencentBlueKing/gopkg/stringx/readme.md @@ -0,0 +1,17 @@ +# stringx + +`stringx`包含一些字符串常用函数 + +## Usage + + +```go +import "github.com/TencentBlueKing/gopkg/stringx" + +text := "hello world" + +stringx.MD5Hash(text) +stringx.Truncate(text, 10) +stringx.Random(10) +``` + diff --git a/pkg/cache/memory/cache_test.go b/vendor/github.com/TencentBlueKing/gopkg/stringx/stringx.go similarity index 59% rename from pkg/cache/memory/cache_test.go rename to vendor/github.com/TencentBlueKing/gopkg/stringx/stringx.go index 7e993bfd..a5f66d31 100644 --- a/pkg/cache/memory/cache_test.go +++ b/vendor/github.com/TencentBlueKing/gopkg/stringx/stringx.go @@ -1,5 +1,6 @@ /* - * TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-权限中心(BlueKing-IAM) available. + * TencentBlueKing is pleased to support the open source community by making + * 蓝鲸智云-gopkg available. * Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved. * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://opensource.org/licenses/MIT @@ -8,24 +9,26 @@ * specific language governing permissions and limitations under the License. */ -package memory +package stringx -import ( - "testing" - "time" +import "math/rand" - "iam/pkg/cache" - - "github.com/stretchr/testify/assert" -) - -func retrieveOK(k cache.Key) (interface{}, error) { - return "ok", nil +// Truncate string to specific length +func Truncate(s string, n int) string { + if n > len(s) { + return s + } + return s[:n] } -func TestNewCache(t *testing.T) { - expiration := 5 * time.Minute - c := NewCache("test", false, retrieveOK, expiration, nil) - assert.NotNil(t, c) +const letterBytes = "abcdefghijklmnopqrstuvwxyz1234567890" + +// Random generate a random string with fixed length. [a-z0-9] +func Random(n int) string { + b := make([]byte, n) + for i := range b { + b[i] = letterBytes[rand.Intn(len(letterBytes))] + } + return string(b) } diff --git a/vendor/github.com/onsi/ginkgo/CHANGELOG.md b/vendor/github.com/onsi/ginkgo/CHANGELOG.md index 494abdbf..a26bc530 100644 --- a/vendor/github.com/onsi/ginkgo/CHANGELOG.md +++ b/vendor/github.com/onsi/ginkgo/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.16.5 + +Ginkgo 2.0 now has a Release Candidate. 1.16.5 advertises the existence of the RC. +1.16.5 deprecates GinkgoParallelNode in favor of GinkgoParallelProcess + +You can silence the RC advertisement by setting an `ACK_GINKG_RC=true` environment variable or creating a file in your home directory called `.ack-ginkgo-rc` + ## 1.16.4 ### Fixes diff --git a/vendor/github.com/onsi/ginkgo/README.md b/vendor/github.com/onsi/ginkgo/README.md index 05321e6e..a25ca5e0 100644 --- a/vendor/github.com/onsi/ginkgo/README.md +++ b/vendor/github.com/onsi/ginkgo/README.md @@ -1,23 +1,18 @@ ![Ginkgo: A Go BDD Testing Framework](https://onsi.github.io/ginkgo/images/ginkgo.png) -[![Build Status](https://travis-ci.org/onsi/ginkgo.svg?branch=master)](https://travis-ci.org/onsi/ginkgo) [![test](https://github.com/onsi/ginkgo/workflows/test/badge.svg?branch=master)](https://github.com/onsi/ginkgo/actions?query=workflow%3Atest+branch%3Amaster) Jump to the [docs](https://onsi.github.io/ginkgo/) | [中文文档](https://ke-chain.github.io/ginkgodoc) to learn more. To start rolling your Ginkgo tests *now* [keep reading](#set-me-up)! If you have a question, comment, bug report, feature request, etc. please open a GitHub issue, or visit the [Ginkgo Slack channel](https://app.slack.com/client/T029RQSE6/CQQ50BBNW). -# Ginkgo 2.0 is coming soon! +# Ginkgo 2.0 Release Candidate is available! -An effort is underway to develop and deliver Ginkgo 2.0. The work is happening in the [v2](https://github.com/onsi/ginkgo/tree/v2) branch and a changelog and migration guide is being maintained on that branch [here](https://github.com/onsi/ginkgo/blob/v2/docs/MIGRATING_TO_V2.md). Issue [#711](https://github.com/onsi/ginkgo/issues/711) is the central place for discussion and links to the original [proposal doc](https://docs.google.com/document/d/1h28ZknXRsTLPNNiOjdHIO-F2toCzq4xoZDXbfYaBdoQ/edit#). +An effort is underway to develop and deliver Ginkgo 2.0. The work is happening in the [ver2](https://github.com/onsi/ginkgo/tree/ver2) branch and a changelog and migration guide is being maintained on that branch [here](https://github.com/onsi/ginkgo/blob/ver2/docs/MIGRATING_TO_V2.md). Issue [#711](https://github.com/onsi/ginkgo/issues/711) is the central place for discussion. -As described in the [changelog](https://github.com/onsi/ginkgo/blob/v2/docs/MIGRATING_TO_V2.md) and [proposal](https://docs.google.com/document/d/1h28ZknXRsTLPNNiOjdHIO-F2toCzq4xoZDXbfYaBdoQ/edit#), Ginkgo 2.0 will clean up the Ginkgo codebase, deprecate and remove some v1 functionality, and add several new much-requested features. To help users get ready for the migration, Ginkgo v1 has started emitting deprecation warnings for features that will no longer be supported with links to documentation for how to migrate away from these features. If you have concerns or comments please chime in on [#711](https://github.com/onsi/ginkgo/issues/711). +As described in the [changelog](https://github.com/onsi/ginkgo/blob/ver2/docs/MIGRATING_TO_V2.md) and [proposal](https://docs.google.com/document/d/1h28ZknXRsTLPNNiOjdHIO-F2toCzq4xoZDXbfYaBdoQ/edit#), Ginkgo 2.0 will clean up the Ginkgo codebase, deprecate and remove some v1 functionality, and add several new much-requested features. To help users get ready for the migration, Ginkgo v1 has started emitting deprecation warnings for features that will no longer be supported with links to documentation for how to migrate away from these features. If you have concerns or comments please chime in on [#711](https://github.com/onsi/ginkgo/issues/711). -The current timeline for completion of 2.0 looks like: - -- Early April 2021: first public release of 2.0, deprecation warnings land in v1. -- May 2021: first beta/rc of 2.0 with most new functionality in place. -- June/July 2021: 2.0 ships and fully replaces the 1.x codebase on master. +Please start exploring and using the V2 release! To get started follow the [Using the Release Candidate](https://github.com/onsi/ginkgo/blob/ver2/docs/MIGRATING_TO_V2.md#using-the-beta) directions in the migration guide. ## TLDR Ginkgo builds on Go's `testing` package, allowing expressive [Behavior-Driven Development](https://en.wikipedia.org/wiki/Behavior-driven_development) ("BDD") style tests. diff --git a/vendor/github.com/onsi/ginkgo/config/config.go b/vendor/github.com/onsi/ginkgo/config/config.go index 5f3f4396..3130c778 100644 --- a/vendor/github.com/onsi/ginkgo/config/config.go +++ b/vendor/github.com/onsi/ginkgo/config/config.go @@ -20,7 +20,7 @@ import ( "fmt" ) -const VERSION = "1.16.4" +const VERSION = "1.16.5" type GinkgoConfigType struct { RandomSeed int64 diff --git a/vendor/github.com/onsi/ginkgo/ginkgo_dsl.go b/vendor/github.com/onsi/ginkgo/ginkgo_dsl.go index 4a6e1e1e..ccd7685e 100644 --- a/vendor/github.com/onsi/ginkgo/ginkgo_dsl.go +++ b/vendor/github.com/onsi/ginkgo/ginkgo_dsl.go @@ -73,9 +73,15 @@ func GinkgoRandomSeed() int64 { return config.GinkgoConfig.RandomSeed } -//GinkgoParallelNode returns the parallel node number for the current ginkgo process -//The node number is 1-indexed +//GinkgoParallelNode is deprecated, use GinkgoParallelProcess instead func GinkgoParallelNode() int { + deprecationTracker.TrackDeprecation(types.Deprecations.ParallelNode(), codelocation.New(1)) + return GinkgoParallelProcess() +} + +//GinkgoParallelProcess returns the parallel process number for the current ginkgo process +//The process number is 1-indexed +func GinkgoParallelProcess() int { return config.GinkgoConfig.ParallelNode } @@ -109,6 +115,7 @@ func GinkgoT(optionalOffset ...int) GinkgoTInterface { //in the testing package's T. type GinkgoTInterface interface { Cleanup(func()) + Setenv(key, value string) Error(args ...interface{}) Errorf(format string, args ...interface{}) Fail() diff --git a/vendor/github.com/onsi/ginkgo/go.mod b/vendor/github.com/onsi/ginkgo/go.mod index 86a5a97b..17114432 100644 --- a/vendor/github.com/onsi/ginkgo/go.mod +++ b/vendor/github.com/onsi/ginkgo/go.mod @@ -1,6 +1,6 @@ module github.com/onsi/ginkgo -go 1.15 +go 1.16 require ( github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 diff --git a/vendor/github.com/onsi/ginkgo/internal/testingtproxy/testing_t_proxy.go b/vendor/github.com/onsi/ginkgo/internal/testingtproxy/testing_t_proxy.go index d7bbb7a9..4dcfaf4c 100644 --- a/vendor/github.com/onsi/ginkgo/internal/testingtproxy/testing_t_proxy.go +++ b/vendor/github.com/onsi/ginkgo/internal/testingtproxy/testing_t_proxy.go @@ -34,6 +34,11 @@ func (t *ginkgoTestingTProxy) Cleanup(func()) { // No-op } +func (t *ginkgoTestingTProxy) Setenv(kev, value string) { + fmt.Println("Setenv is a noop for Ginkgo at the moment but will be implemented in V2") + // No-op until Cleanup is implemented +} + func (t *ginkgoTestingTProxy) Error(args ...interface{}) { t.fail(fmt.Sprintln(args...), t.offset) } diff --git a/vendor/github.com/onsi/ginkgo/types/deprecation_support.go b/vendor/github.com/onsi/ginkgo/types/deprecation_support.go index 305c134b..d5a6658f 100644 --- a/vendor/github.com/onsi/ginkgo/types/deprecation_support.go +++ b/vendor/github.com/onsi/ginkgo/types/deprecation_support.go @@ -52,6 +52,14 @@ func (d deprecations) Measure() Deprecation { } } +func (d deprecations) ParallelNode() Deprecation { + return Deprecation{ + Message: "GinkgoParallelNode is deprecated and will be removed in Ginkgo V2. Please use GinkgoParallelProcess instead.", + DocLink: "renamed-ginkgoparallelnode", + Version: "1.16.5", + } +} + func (d deprecations) Convert() Deprecation { return Deprecation{ Message: "The convert command is deprecated in Ginkgo V2", @@ -99,16 +107,18 @@ func (d *DeprecationTracker) DidTrackDeprecations() bool { } func (d *DeprecationTracker) DeprecationsReport() string { - out := formatter.F("{{light-yellow}}You're using deprecated Ginkgo functionality:{{/}}\n") + out := formatter.F("\n{{light-yellow}}You're using deprecated Ginkgo functionality:{{/}}\n") out += formatter.F("{{light-yellow}}============================================={{/}}\n") - out += formatter.F("Ginkgo 2.0 is under active development and will introduce (a small number of) breaking changes.\n") - out += formatter.F("To learn more, view the migration guide at {{cyan}}{{underline}}https://github.com/onsi/ginkgo/blob/v2/docs/MIGRATING_TO_V2.md{{/}}\n") - out += formatter.F("To comment, chime in at {{cyan}}{{underline}}https://github.com/onsi/ginkgo/issues/711{{/}}\n\n") + out += formatter.F("{{bold}}{{green}}Ginkgo 2.0{{/}} is under active development and will introduce several new features, improvements, and a small handful of breaking changes.\n") + out += formatter.F("A release candidate for 2.0 is now available and 2.0 should GA in Fall 2021. {{bold}}Please give the RC a try and send us feedback!{{/}}\n") + out += formatter.F(" - To learn more, view the migration guide at {{cyan}}{{underline}}https://github.com/onsi/ginkgo/blob/ver2/docs/MIGRATING_TO_V2.md{{/}}\n") + out += formatter.F(" - For instructions on using the Release Candidate visit {{cyan}}{{underline}}https://github.com/onsi/ginkgo/blob/ver2/docs/MIGRATING_TO_V2.md#using-the-beta{{/}}\n") + out += formatter.F(" - To comment, chime in at {{cyan}}{{underline}}https://github.com/onsi/ginkgo/issues/711{{/}}\n\n") for deprecation, locations := range d.deprecations { out += formatter.Fi(1, "{{yellow}}"+deprecation.Message+"{{/}}\n") if deprecation.DocLink != "" { - out += formatter.Fi(1, "{{bold}}Learn more at:{{/}} {{cyan}}{{underline}}https://github.com/onsi/ginkgo/blob/v2/docs/MIGRATING_TO_V2.md#%s{{/}}\n", deprecation.DocLink) + out += formatter.Fi(1, "{{bold}}Learn more at:{{/}} {{cyan}}{{underline}}https://github.com/onsi/ginkgo/blob/ver2/docs/MIGRATING_TO_V2.md#%s{{/}}\n", deprecation.DocLink) } for _, location := range locations { out += formatter.Fi(2, "{{gray}}%s{{/}}\n", location) diff --git a/vendor/github.com/onsi/gomega/CHANGELOG.md b/vendor/github.com/onsi/gomega/CHANGELOG.md index f2181a8c..65c6c1d8 100644 --- a/vendor/github.com/onsi/gomega/CHANGELOG.md +++ b/vendor/github.com/onsi/gomega/CHANGELOG.md @@ -1,3 +1,32 @@ +## 1.17.0 + +### Features +- Add HaveField matcher [3a26311] +- add Error() assertions on the final error value of multi-return values (#480) [2f96943] +- separate out offsets and timeouts (#478) [18a4723] +- fix transformation error reporting (#479) [e001fab] +- allow transform functions to report errors (#472) [bf93408] + +### Fixes +Stop using deprecated ioutil package (#467) [07f405d] + +## 1.16.0 + +### Features +- feat: HaveHTTPStatus multiple expected values (#465) [aa69f1b] +- feat: HaveHTTPHeaderWithValue() matcher (#463) [dd83a96] +- feat: HaveHTTPBody matcher (#462) [504e1f2] +- feat: formatter for HTTP responses (#461) [e5b3157] + +## 1.15.0 + +### Fixes +The previous version (1.14.0) introduced a change to allow `Eventually` and `Consistently` to support functions that make assertions. This was accomplished by overriding the global fail handler when running the callbacks passed to `Eventually/Consistently` in order to capture any resulting errors. Issue #457 uncovered a flaw with this approach: when multiple `Eventually`s are running concurrently they race when overriding the singleton global fail handler. + +1.15.0 resolves this by requiring users who want to make assertions in `Eventually/Consistently` call backs to explicitly pass in a function that takes a `Gomega` as an argument. The passed-in `Gomega` instance can be used to make assertions. Any failures will cause `Eventually` to retry the callback. This cleaner interface avoids the issue of swapping out globals but comes at the cost of changing the contract introduced in v1.14.0. As such 1.15.0 introduces a breaking change with respect to 1.14.0 - however we expect that adoption of this feature in 1.14.0 remains limited. + +In addition, 1.15.0 cleans up some of Gomega's internals. Most users shouldn't notice any differences stemming from the refactoring that was made. + ## 1.14.0 ### Features diff --git a/vendor/github.com/onsi/gomega/env.go b/vendor/github.com/onsi/gomega/env.go deleted file mode 100644 index 62fd885a..00000000 --- a/vendor/github.com/onsi/gomega/env.go +++ /dev/null @@ -1,40 +0,0 @@ -package gomega - -import ( - "os" - - "github.com/onsi/gomega/internal/defaults" -) - -const ( - ConsistentlyDurationEnvVarName = "GOMEGA_DEFAULT_CONSISTENTLY_DURATION" - ConsistentlyPollingIntervalEnvVarName = "GOMEGA_DEFAULT_CONSISTENTLY_POLLING_INTERVAL" - EventuallyTimeoutEnvVarName = "GOMEGA_DEFAULT_EVENTUALLY_TIMEOUT" - EventuallyPollingIntervalEnvVarName = "GOMEGA_DEFAULT_EVENTUALLY_POLLING_INTERVAL" -) - -func init() { - defaults.SetDurationFromEnv( - os.Getenv, - SetDefaultConsistentlyDuration, - ConsistentlyDurationEnvVarName, - ) - - defaults.SetDurationFromEnv( - os.Getenv, - SetDefaultConsistentlyPollingInterval, - ConsistentlyPollingIntervalEnvVarName, - ) - - defaults.SetDurationFromEnv( - os.Getenv, - SetDefaultEventuallyTimeout, - EventuallyTimeoutEnvVarName, - ) - - defaults.SetDurationFromEnv( - os.Getenv, - SetDefaultEventuallyPollingInterval, - EventuallyPollingIntervalEnvVarName, - ) -} diff --git a/vendor/github.com/onsi/gomega/go.mod b/vendor/github.com/onsi/gomega/go.mod index 62b8f396..7fea4ac0 100644 --- a/vendor/github.com/onsi/gomega/go.mod +++ b/vendor/github.com/onsi/gomega/go.mod @@ -1,6 +1,6 @@ module github.com/onsi/gomega -go 1.14 +go 1.16 require ( github.com/golang/protobuf v1.5.2 diff --git a/vendor/github.com/onsi/gomega/go.sum b/vendor/github.com/onsi/gomega/go.sum index 177d5e87..56f1b44e 100644 --- a/vendor/github.com/onsi/gomega/go.sum +++ b/vendor/github.com/onsi/gomega/go.sum @@ -1,4 +1,5 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= @@ -20,6 +21,7 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= @@ -30,13 +32,19 @@ github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -47,6 +55,7 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG0 golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -60,6 +69,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -85,6 +95,7 @@ google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/l google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= diff --git a/vendor/github.com/onsi/gomega/gomega_dsl.go b/vendor/github.com/onsi/gomega/gomega_dsl.go index 67f6e45c..f5156fd4 100644 --- a/vendor/github.com/onsi/gomega/gomega_dsl.go +++ b/vendor/github.com/onsi/gomega/gomega_dsl.go @@ -16,78 +16,92 @@ package gomega import ( "errors" "fmt" - "reflect" "time" - "github.com/onsi/gomega/internal/assertion" - "github.com/onsi/gomega/internal/asyncassertion" - "github.com/onsi/gomega/internal/testingtsupport" + "github.com/onsi/gomega/internal" "github.com/onsi/gomega/types" ) -const GOMEGA_VERSION = "1.14.0" +const GOMEGA_VERSION = "1.17.0" -const nilFailHandlerPanic = `You are trying to make an assertion, but Gomega's fail handler is nil. +const nilGomegaPanic = `You are trying to make an assertion, but haven't registered Gomega's fail handler. If you're using Ginkgo then you probably forgot to put your assertion in an It(). Alternatively, you may have forgotten to register a fail handler with RegisterFailHandler() or RegisterTestingT(). Depending on your vendoring solution you may be inadvertently importing gomega and subpackages (e.g. ghhtp, gexec,...) from different locations. ` -var globalFailWrapper *types.GomegaFailWrapper - -var defaultEventuallyTimeout = time.Second -var defaultEventuallyPollingInterval = 10 * time.Millisecond -var defaultConsistentlyDuration = 100 * time.Millisecond -var defaultConsistentlyPollingInterval = 10 * time.Millisecond - -// RegisterFailHandler connects Ginkgo to Gomega. When a matcher fails -// the fail handler passed into RegisterFailHandler is called. -func RegisterFailHandler(handler types.GomegaFailHandler) { - RegisterFailHandlerWithT(testingtsupport.EmptyTWithHelper{}, handler) -} +// Gomega describes the essential Gomega DSL. This interface allows libraries +// to abstract between the standard package-level function implementations +// and alternatives like *WithT. +// +// The types in the top-level DSL have gotten a bit messy due to earlier depracations that avoid stuttering +// and due to an accidental use of a concrete type (*WithT) in an earlier release. +// +// As of 1.15 both the WithT and Ginkgo variants of Gomega are implemented by the same underlying object +// however one (the Ginkgo variant) is exported as an interface (types.Gomega) whereas the other (the withT variant) +// is shared as a concrete type (*WithT, which is aliased to *internal.Gomega). 1.15 did not clean this mess up to ensure +// that declarations of *WithT in existing code are not broken by the upgrade to 1.15. +type Gomega = types.Gomega -// RegisterFailHandlerWithT ensures that the given types.TWithHelper and fail handler -// are used globally. -func RegisterFailHandlerWithT(t types.TWithHelper, handler types.GomegaFailHandler) { - if handler == nil { - globalFailWrapper = nil - return - } +// DefaultGomega supplies the standard package-level implementation +var Default = Gomega(internal.NewGomega(internal.FetchDefaultDurationBundle())) - globalFailWrapper = &types.GomegaFailWrapper{ - Fail: handler, - TWithHelper: t, - } +// NewGomega returns an instance of Gomega wired into the passed-in fail handler. +// You generally don't need to use this when using Ginkgo - RegisterFailHandler will wire up the global gomega +// However creating a NewGomega with a custom fail handler can be useful in contexts where you want to use Gomega's +// rich ecosystem of matchers without causing a test to fail. For example, to aggregate a series of potential failures +// or for use in a non-test setting. +func NewGomega(fail types.GomegaFailHandler) Gomega { + return internal.NewGomega(Default.(*internal.Gomega).DurationBundle).ConfigureWithFailHandler(fail) } -// RegisterTestingT connects Gomega to Golang's XUnit style -// Testing.T tests. It is now deprecated and you should use NewWithT() instead. +// WithT wraps a *testing.T and provides `Expect`, `Eventually`, and `Consistently` methods. This allows you to leverage +// Gomega's rich ecosystem of matchers in standard `testing` test suites. // -// Legacy Documentation: +// Use `NewWithT` to instantiate a `WithT` // -// You'll need to call this at the top of each XUnit style test: +// As of 1.15 both the WithT and Ginkgo variants of Gomega are implemented by the same underlying object +// however one (the Ginkgo variant) is exported as an interface (types.Gomega) whereas the other (the withT variant) +// is shared as a concrete type (*WithT, which is aliased to *internal.Gomega). 1.15 did not clean this mess up to ensure +// that declarations of *WithT in existing code are not broken by the upgrade to 1.15. +type WithT = internal.Gomega + +// GomegaWithT is deprecated in favor of gomega.WithT, which does not stutter. +type GomegaWithT = WithT + +// NewWithT takes a *testing.T and returngs a `gomega.WithT` allowing you to use `Expect`, `Eventually`, and `Consistently` along with +// Gomega's rich ecosystem of matchers in standard `testing` test suits. // // func TestFarmHasCow(t *testing.T) { -// RegisterTestingT(t) +// g := gomega.NewWithT(t) // // f := farm.New([]string{"Cow", "Horse"}) -// Expect(f.HasCow()).To(BeTrue(), "Farm should have cow") -// } -// -// Note that this *testing.T is registered *globally* by Gomega (this is why you don't have to -// pass `t` down to the matcher itself). This means that you cannot run the XUnit style tests -// in parallel as the global fail handler cannot point to more than one testing.T at a time. -// -// NewWithT() does not have this limitation -// -// (As an aside: Ginkgo gets around this limitation by running parallel tests in different *processes*). +// g.Expect(f.HasCow()).To(BeTrue(), "Farm should have cow") +// } +func NewWithT(t types.GomegaTestingT) *WithT { + return internal.NewGomega(Default.(*internal.Gomega).DurationBundle).ConfigureWithT(t) +} + +// NewGomegaWithT is deprecated in favor of gomega.NewWithT, which does not stutter. +var NewGomegaWithT = NewWithT + +// RegisterFailHandler connects Ginkgo to Gomega. When a matcher fails +// the fail handler passed into RegisterFailHandler is called. +func RegisterFailHandler(fail types.GomegaFailHandler) { + Default.(*internal.Gomega).ConfigureWithFailHandler(fail) +} + +// RegisterFailHandlerWithT is deprecated and will be removed in a future release. +// users should use RegisterFailHandler, or RegisterTestingT +func RegisterFailHandlerWithT(_ types.GomegaTestingT, fail types.GomegaFailHandler) { + fmt.Println("RegisterFailHandlerWithT is deprecated. Please use RegisterFailHandler or RegisterTestingT instead.") + Default.(*internal.Gomega).ConfigureWithFailHandler(fail) +} + +// RegisterTestingT connects Gomega to Golang's XUnit style +// Testing.T tests. It is now deprecated and you should use NewWithT() instead to get a fresh instance of Gomega for each test. func RegisterTestingT(t types.GomegaTestingT) { - tWithHelper, hasHelper := t.(types.TWithHelper) - if !hasHelper { - RegisterFailHandler(testingtsupport.BuildTestingTGomegaFailWrapper(t).Fail) - return - } - RegisterFailHandlerWithT(tWithHelper, testingtsupport.BuildTestingTGomegaFailWrapper(t).Fail) + Default.(*internal.Gomega).ConfigureWithT(t) } // InterceptGomegaFailures runs a given callback and returns an array of @@ -98,13 +112,13 @@ func RegisterTestingT(t types.GomegaTestingT) { // This is most useful when testing custom matchers, but can also be used to check // on a value using a Gomega assertion without causing a test failure. func InterceptGomegaFailures(f func()) []string { - originalHandler := globalFailWrapper.Fail + originalHandler := Default.(*internal.Gomega).Fail failures := []string{} - RegisterFailHandler(func(message string, callerSkip ...int) { + Default.(*internal.Gomega).Fail = func(message string, callerSkip ...int) { failures = append(failures, message) - }) + } defer func() { - RegisterFailHandler(originalHandler) + Default.(*internal.Gomega).Fail = originalHandler }() f() return failures @@ -117,14 +131,14 @@ func InterceptGomegaFailures(f func()) []string { // does not register a failure with the FailHandler registered via RegisterFailHandler - it is up // to the user to decide what to do with the returned error func InterceptGomegaFailure(f func()) (err error) { - originalHandler := globalFailWrapper.Fail - RegisterFailHandler(func(message string, callerSkip ...int) { + originalHandler := Default.(*internal.Gomega).Fail + Default.(*internal.Gomega).Fail = func(message string, callerSkip ...int) { err = errors.New(message) panic("stop execution") - }) + } defer func() { - RegisterFailHandler(originalHandler) + Default.(*internal.Gomega).Fail = originalHandler if e := recover(); e != nil { if err == nil { panic(e) @@ -136,6 +150,12 @@ func InterceptGomegaFailure(f func()) (err error) { return err } +func ensureDefaultGomegaIsConfigured() { + if !Default.(*internal.Gomega).IsConfigured() { + panic(nilGomegaPanic) + } +} + // Ω wraps an actual value allowing assertions to be made on it: // Ω("foo").Should(Equal("foo")) // @@ -154,7 +174,8 @@ func InterceptGomegaFailure(f func()) (err error) { // // Ω and Expect are identical func Ω(actual interface{}, extra ...interface{}) Assertion { - return ExpectWithOffset(0, actual, extra...) + ensureDefaultGomegaIsConfigured() + return Default.Ω(actual, extra...) } // Expect wraps an actual value allowing assertions to be made on it: @@ -175,179 +196,183 @@ func Ω(actual interface{}, extra ...interface{}) Assertion { // // Expect and Ω are identical func Expect(actual interface{}, extra ...interface{}) Assertion { - return ExpectWithOffset(0, actual, extra...) + ensureDefaultGomegaIsConfigured() + return Default.Expect(actual, extra...) } // ExpectWithOffset wraps an actual value allowing assertions to be made on it: // ExpectWithOffset(1, "foo").To(Equal("foo")) // // Unlike `Expect` and `Ω`, `ExpectWithOffset` takes an additional integer argument -// that is used to modify the call-stack offset when computing line numbers. +// that is used to modify the call-stack offset when computing line numbers. It is +// the same as `Expect(...).WithOffset`. // // This is most useful in helper functions that make assertions. If you want Gomega's // error message to refer to the calling line in the test (as opposed to the line in the helper function) // set the first argument of `ExpectWithOffset` appropriately. func ExpectWithOffset(offset int, actual interface{}, extra ...interface{}) Assertion { - if globalFailWrapper == nil { - panic(nilFailHandlerPanic) - } - return assertion.New(actual, globalFailWrapper, offset, extra...) + ensureDefaultGomegaIsConfigured() + return Default.ExpectWithOffset(offset, actual, extra...) } -// Eventually wraps an actual value allowing assertions to be made on it. -// The assertion is tried periodically until it passes or a timeout occurs. -// -// Both the timeout and polling interval are configurable as optional arguments: -// The first optional argument is the timeout -// The second optional argument is the polling interval -// -// Both intervals can either be specified as time.Duration, parsable duration strings or as floats/integers. In the -// last case they are interpreted as seconds. -// -// If Eventually is passed an actual that is a function taking no arguments, -// then Eventually will call the function periodically and try the matcher against the function's first return value. -// -// Example: -// -// Eventually(func() int { -// return thingImPolling.Count() -// }).Should(BeNumerically(">=", 17)) -// -// Note that this example could be rewritten: -// -// Eventually(thingImPolling.Count).Should(BeNumerically(">=", 17)) -// -// If the function returns more than one value, then Eventually will pass the first value to the matcher and -// assert that all other values are nil/zero. -// This allows you to pass Eventually a function that returns a value and an error - a common pattern in Go. -// -// For example, consider a method that returns a value and an error: -// func FetchFromDB() (string, error) -// -// Then -// Eventually(FetchFromDB).Should(Equal("hasselhoff")) -// -// Will pass only if the the returned error is nil and the returned string passes the matcher. -// -// Eventually allows you to make assertions in the pased-in function. The function is assumed to have failed and will be retried if any assertion in the function fails. -// For example: -// -// Eventually(func() Widget { -// resp, err := http.Get(url) -// Expect(err).NotTo(HaveOccurred()) -// defer resp.Body.Close() -// Expect(resp.SatusCode).To(Equal(http.StatusOK)) -// var widget Widget -// Expect(json.NewDecoder(resp.Body).Decode(&widget)).To(Succeed()) -// return widget -// }).Should(Equal(expectedWidget)) -// -// will keep trying the passed-in function until all its assertsions pass (i.e. the http request succeeds) _and_ the returned object satisfies the passed-in matcher. -// -// Functions passed to Eventually typically have a return value. However you are allowed to pass in a function with no return value. Eventually assumes such a function -// is making assertions and will turn it into a function that returns an error if any assertion fails, or nil if no assertion fails. This allows you to use the Succeed() matcher -// to express that a complex operation should eventually succeed. For example: -// -// Eventually(func() { -// model, err := db.Find("foo") -// Expect(err).NotTo(HaveOccurred()) -// Expect(model.Reticulated()).To(BeTrue()) -// Expect(model.Save()).To(Succeed()) -// }).Should(Succeed()) -// -// will rerun the function until all its assertions pass. -// -// Eventually's default timeout is 1 second, and its default polling interval is 10ms +/* +Eventually enables making assertions on asynchronous behavior. + +Eventually checks that an assertion *eventually* passes. Eventually blocks when called and attempts an assertion periodically until it passes or a timeout occurs. Both the timeout and polling interval are configurable as optional arguments. +The first optional argument is the timeout (which defaults to 1s), the second is the polling interval (which defaults to 10ms). Both intervals can be specified as time.Duration, parsable duration strings or floats/integers (in which case they are interpreted as seconds). + +Eventually works with any Gomega compatible matcher and supports making assertions against three categories of actual value: + +**Category 1: Making Eventually assertions on values** + +There are several examples of values that can change over time. These can be passed in to Eventually and will be passed to the matcher repeatedly until a match occurs. For example: + + c := make(chan bool) + go DoStuff(c) + Eventually(c, "50ms").Should(BeClosed()) + +will poll the channel repeatedly until it is closed. In this example `Eventually` will block until either the specified timeout of 50ms has elapsed or the channel is closed, whichever comes first. + +Several Gomega libraries allow you to use Eventually in this way. For example, the gomega/gexec package allows you to block until a *gexec.Session exits successfuly via: + + Eventually(session).Should(gexec.Exit(0)) + +And the gomega/gbytes package allows you to monitor a streaming *gbytes.Buffer until a given string is seen: + + Eventually(buffer).Should(gbytes.Say("hello there")) + +In these examples, both `session` and `buffer` are designed to be thread-safe when polled by the `Exit` and `Say` matchers. This is not true in general of most raw values, so while it is tempting to do something like: + + // THIS IS NOT THREAD-SAFE + var s *string + go mutateStringEventually(s) + Eventually(s).Should(Equal("I've changed")) + +this will trigger Go's race detector as the goroutine polling via Eventually will race over the value of s with the goroutine mutating the string. For cases like this you can use channels or introduce your own locking around s by passing Eventually a function. + +**Category 2: Make Eventually assertions on functions** + +Eventually can be passed functions that **take no arguments** and **return at least one value**. When configured this way, Eventually will poll the function repeatedly and pass the first returned value to the matcher. + +For example: + + Eventually(func() int { + return client.FetchCount() + }).Should(BeNumerically(">=", 17)) + + will repeatedly poll client.FetchCount until the BeNumerically matcher is satisfied. (Note that this example could have been written as Eventually(client.FetchCount).Should(BeNumerically(">=", 17))) + +If multple values are returned by the function, Eventually will pass the first value to the matcher and require that all others are zero-valued. This allows you to pass Eventually a function that returns a value and an error - a common patternin Go. + +For example, consider a method that returns a value and an error: + func FetchFromDB() (string, error) + +Then + Eventually(FetchFromDB).Should(Equal("got it")) + +will pass only if and when the returned error is nil *and* the returned string satisfies the matcher. + +It is important to note that the function passed into Eventually is invoked *synchronously* when polled. Eventually does not (in fact, it cannot) kill the function if it takes longer to return than Eventually's configured timeout. You should design your functions with this in mind. + +**Category 3: Making assertions _in_ the function passed into Eventually** + +When testing complex systems it can be valuable to assert that a _set_ of assertions passes Eventually. Eventually supports this by accepting functions that take a single Gomega argument and return zero or more values. + +Here's an example that makes some asssertions and returns a value and error: + + Eventually(func(g Gomega) (Widget, error) { + ids, err := client.FetchIDs() + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(ids).To(ContainElement(1138)) + return client.FetchWidget(1138) + }).Should(Equal(expectedWidget)) + +will pass only if all the assertions in the polled function pass and the return value satisfied the matcher. + +Eventually also supports a special case polling function that takes a single Gomega argument and returns no values. Eventually assumes such a function is making assertions and is designed to work with the Succeed matcher to validate that all assertions have passed. +For example: + + Eventually(func(g Gomega) { + model, err := client.Find(1138) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(model.Reticulate()).To(Succeed()) + g.Expect(model.IsReticulated()).To(BeTrue()) + g.Expect(model.Save()).To(Succeed()) + }).Should(Succeed()) + +will rerun the function until all assertions pass. + +`Eventually` specifying a timeout interval (and an optional polling interval) are +the same as `Eventually(...).WithTimeout` or `Eventually(...).WithTimeout(...).WithPolling`. +*/ func Eventually(actual interface{}, intervals ...interface{}) AsyncAssertion { - return EventuallyWithOffset(0, actual, intervals...) + ensureDefaultGomegaIsConfigured() + return Default.Eventually(actual, intervals...) } // EventuallyWithOffset operates like Eventually but takes an additional // initial argument to indicate an offset in the call stack. This is useful when building helper // functions that contain matchers. To learn more, read about `ExpectWithOffset`. +// +// `EventuallyWithOffset` is the same as `Eventually(...).WithOffset`. +// +// `EventuallyWithOffset` specifying a timeout interval (and an optional polling interval) are +// the same as `Eventually(...).WithOffset(...).WithTimeout` or +// `Eventually(...).WithOffset(...).WithTimeout(...).WithPolling`. func EventuallyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion { - if globalFailWrapper == nil { - panic(nilFailHandlerPanic) - } - timeoutInterval := defaultEventuallyTimeout - pollingInterval := defaultEventuallyPollingInterval - if len(intervals) > 0 { - timeoutInterval = toDuration(intervals[0]) - } - if len(intervals) > 1 { - pollingInterval = toDuration(intervals[1]) - } - return asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, actual, globalFailWrapper, timeoutInterval, pollingInterval, offset) + ensureDefaultGomegaIsConfigured() + return Default.EventuallyWithOffset(offset, actual, intervals...) } -// Consistently wraps an actual value allowing assertions to be made on it. -// The assertion is tried periodically and is required to pass for a period of time. -// -// Both the total time and polling interval are configurable as optional arguments: -// The first optional argument is the duration that Consistently will run for -// The second optional argument is the polling interval -// -// Both intervals can either be specified as time.Duration, parsable duration strings or as floats/integers. In the -// last case they are interpreted as seconds. -// -// If Consistently is passed an actual that is a function taking no arguments. -// -// If the function returns one value, then Consistently will call the function periodically and try the matcher against the function's first return value. -// -// If the function returns more than one value, then Consistently will pass the first value to the matcher and -// assert that all other values are nil/zero. -// This allows you to pass Consistently a function that returns a value and an error - a common pattern in Go. -// -// Like Eventually, Consistently allows you to make assertions in the function. If any assertion fails Consistently will fail. In addition, -// Consistently also allows you to pass in a function with no return value. In this case Consistently can be paired with the Succeed() matcher to assert -// that no assertions in the function fail. -// -// Consistently is useful in cases where you want to assert that something *does not happen* over a period of time. -// For example, you want to assert that a goroutine does *not* send data down a channel. In this case, you could: -// -// Consistently(channel).ShouldNot(Receive()) -// -// Consistently's default duration is 100ms, and its default polling interval is 10ms +/* +Consistently, like Eventually, enables making assertions on asynchronous behavior. + +Consistently blocks when called for a specified duration. During that duration Consistently repeatedly polls its matcher and ensures that it is satisfied. If the matcher is consistently satisfied, then Consistently will pass. Otherwise Consistently will fail. + +Both the total waiting duration and the polling interval are configurable as optional arguments. The first optional arugment is the duration that Consistently will run for (defaults to 100ms), and the second argument is the polling interval (defaults to 10ms). As with Eventually, these intervals can be passed in as time.Duration, parsable duration strings or an integer or float number of seconds. + +Consistently accepts the same three categories of actual as Eventually, check the Eventually docs to learn more. + +Consistently is useful in cases where you want to assert that something *does not happen* for a period of time. For example, you may want to assert that a goroutine does *not* send data down a channel. In this case you could write: + + Consistently(channel, "200ms").ShouldNot(Receive()) + +This will block for 200 milliseconds and repeatedly check the channel and ensure nothing has been received. +*/ func Consistently(actual interface{}, intervals ...interface{}) AsyncAssertion { - return ConsistentlyWithOffset(0, actual, intervals...) + ensureDefaultGomegaIsConfigured() + return Default.Consistently(actual, intervals...) } // ConsistentlyWithOffset operates like Consistently but takes an additional // initial argument to indicate an offset in the call stack. This is useful when building helper // functions that contain matchers. To learn more, read about `ExpectWithOffset`. +// +// `ConsistentlyWithOffset` is the same as `Consistently(...).WithOffset` and +// optional `WithTimeout` and `WithPolling`. func ConsistentlyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion { - if globalFailWrapper == nil { - panic(nilFailHandlerPanic) - } - timeoutInterval := defaultConsistentlyDuration - pollingInterval := defaultConsistentlyPollingInterval - if len(intervals) > 0 { - timeoutInterval = toDuration(intervals[0]) - } - if len(intervals) > 1 { - pollingInterval = toDuration(intervals[1]) - } - return asyncassertion.New(asyncassertion.AsyncAssertionTypeConsistently, actual, globalFailWrapper, timeoutInterval, pollingInterval, offset) + ensureDefaultGomegaIsConfigured() + return Default.ConsistentlyWithOffset(offset, actual, intervals...) } // SetDefaultEventuallyTimeout sets the default timeout duration for Eventually. Eventually will repeatedly poll your condition until it succeeds, or until this timeout elapses. func SetDefaultEventuallyTimeout(t time.Duration) { - defaultEventuallyTimeout = t + Default.SetDefaultEventuallyTimeout(t) } // SetDefaultEventuallyPollingInterval sets the default polling interval for Eventually. func SetDefaultEventuallyPollingInterval(t time.Duration) { - defaultEventuallyPollingInterval = t + Default.SetDefaultEventuallyPollingInterval(t) } // SetDefaultConsistentlyDuration sets the default duration for Consistently. Consistently will verify that your condition is satisfied for this long. func SetDefaultConsistentlyDuration(t time.Duration) { - defaultConsistentlyDuration = t + Default.SetDefaultConsistentlyDuration(t) } // SetDefaultConsistentlyPollingInterval sets the default polling interval for Consistently. func SetDefaultConsistentlyPollingInterval(t time.Duration) { - defaultConsistentlyPollingInterval = t + Default.SetDefaultConsistentlyPollingInterval(t) } // AsyncAssertion is returned by Eventually and Consistently and polls the actual value passed into Eventually against @@ -365,13 +390,10 @@ func SetDefaultConsistentlyPollingInterval(t time.Duration) { // // Eventually(myChannel).Should(Receive(), "Something should have come down the pipe.") // Consistently(myChannel).ShouldNot(Receive(), func() string { return "Nothing should have come down the pipe." }) -type AsyncAssertion interface { - Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool - ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool -} +type AsyncAssertion = types.AsyncAssertion // GomegaAsyncAssertion is deprecated in favor of AsyncAssertion, which does not stutter. -type GomegaAsyncAssertion = AsyncAssertion +type GomegaAsyncAssertion = types.AsyncAssertion // Assertion is returned by Ω and Expect and compares the actual value to the matcher // passed to the Should/ShouldNot and To/ToNot/NotTo methods. @@ -390,149 +412,10 @@ type GomegaAsyncAssertion = AsyncAssertion // Example: // // Ω(farm.HasCow()).Should(BeTrue(), "Farm %v should have a cow", farm) -type Assertion interface { - Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool - ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool - - To(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool - ToNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool - NotTo(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool -} +type Assertion = types.Assertion // GomegaAssertion is deprecated in favor of Assertion, which does not stutter. -type GomegaAssertion = Assertion +type GomegaAssertion = types.Assertion // OmegaMatcher is deprecated in favor of the better-named and better-organized types.GomegaMatcher but sticks around to support existing code that uses it -type OmegaMatcher types.GomegaMatcher - -// WithT wraps a *testing.T and provides `Expect`, `Eventually`, and `Consistently` methods. This allows you to leverage -// Gomega's rich ecosystem of matchers in standard `testing` test suites. -// -// Use `NewWithT` to instantiate a `WithT` -type WithT struct { - failWrapper *types.GomegaFailWrapper -} - -// GomegaWithT is deprecated in favor of gomega.WithT, which does not stutter. -type GomegaWithT = WithT - -// NewWithT takes a *testing.T and returngs a `gomega.WithT` allowing you to use `Expect`, `Eventually`, and `Consistently` along with -// Gomega's rich ecosystem of matchers in standard `testing` test suits. -// -// func TestFarmHasCow(t *testing.T) { -// g := gomega.NewWithT(t) -// -// f := farm.New([]string{"Cow", "Horse"}) -// g.Expect(f.HasCow()).To(BeTrue(), "Farm should have cow") -// } -func NewWithT(t types.GomegaTestingT) *WithT { - return &WithT{ - failWrapper: testingtsupport.BuildTestingTGomegaFailWrapper(t), - } -} - -// NewGomegaWithT is deprecated in favor of gomega.NewWithT, which does not stutter. -func NewGomegaWithT(t types.GomegaTestingT) *GomegaWithT { - return NewWithT(t) -} - -// ExpectWithOffset is used to make assertions. See documentation for ExpectWithOffset. -func (g *WithT) ExpectWithOffset(offset int, actual interface{}, extra ...interface{}) Assertion { - return assertion.New(actual, g.failWrapper, offset, extra...) -} - -// EventuallyWithOffset is used to make asynchronous assertions. See documentation for EventuallyWithOffset. -func (g *WithT) EventuallyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion { - timeoutInterval := defaultEventuallyTimeout - pollingInterval := defaultEventuallyPollingInterval - if len(intervals) > 0 { - timeoutInterval = toDuration(intervals[0]) - } - if len(intervals) > 1 { - pollingInterval = toDuration(intervals[1]) - } - return asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, actual, g.failWrapper, timeoutInterval, pollingInterval, offset) -} - -// ConsistentlyWithOffset is used to make asynchronous assertions. See documentation for ConsistentlyWithOffset. -func (g *WithT) ConsistentlyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion { - timeoutInterval := defaultConsistentlyDuration - pollingInterval := defaultConsistentlyPollingInterval - if len(intervals) > 0 { - timeoutInterval = toDuration(intervals[0]) - } - if len(intervals) > 1 { - pollingInterval = toDuration(intervals[1]) - } - return asyncassertion.New(asyncassertion.AsyncAssertionTypeConsistently, actual, g.failWrapper, timeoutInterval, pollingInterval, offset) -} - -// Expect is used to make assertions. See documentation for Expect. -func (g *WithT) Expect(actual interface{}, extra ...interface{}) Assertion { - return g.ExpectWithOffset(0, actual, extra...) -} - -// Eventually is used to make asynchronous assertions. See documentation for Eventually. -func (g *WithT) Eventually(actual interface{}, intervals ...interface{}) AsyncAssertion { - return g.EventuallyWithOffset(0, actual, intervals...) -} - -// Consistently is used to make asynchronous assertions. See documentation for Consistently. -func (g *WithT) Consistently(actual interface{}, intervals ...interface{}) AsyncAssertion { - return g.ConsistentlyWithOffset(0, actual, intervals...) -} - -func toDuration(input interface{}) time.Duration { - duration, ok := input.(time.Duration) - if ok { - return duration - } - - value := reflect.ValueOf(input) - kind := reflect.TypeOf(input).Kind() - - if reflect.Int <= kind && kind <= reflect.Int64 { - return time.Duration(value.Int()) * time.Second - } else if reflect.Uint <= kind && kind <= reflect.Uint64 { - return time.Duration(value.Uint()) * time.Second - } else if reflect.Float32 <= kind && kind <= reflect.Float64 { - return time.Duration(value.Float() * float64(time.Second)) - } else if reflect.String == kind { - duration, err := time.ParseDuration(value.String()) - if err != nil { - panic(fmt.Sprintf("%#v is not a valid parsable duration string.", input)) - } - return duration - } - - panic(fmt.Sprintf("%v is not a valid interval. Must be time.Duration, parsable duration string or a number.", input)) -} - -// Gomega describes the essential Gomega DSL. This interface allows libraries -// to abstract between the standard package-level function implementations -// and alternatives like *WithT. -type Gomega interface { - Expect(actual interface{}, extra ...interface{}) Assertion - Eventually(actual interface{}, intervals ...interface{}) AsyncAssertion - Consistently(actual interface{}, intervals ...interface{}) AsyncAssertion -} - -type globalFailHandlerGomega struct{} - -// DefaultGomega supplies the standard package-level implementation -var Default Gomega = globalFailHandlerGomega{} - -// Expect is used to make assertions. See documentation for Expect. -func (globalFailHandlerGomega) Expect(actual interface{}, extra ...interface{}) Assertion { - return Expect(actual, extra...) -} - -// Eventually is used to make asynchronous assertions. See documentation for Eventually. -func (globalFailHandlerGomega) Eventually(actual interface{}, extra ...interface{}) AsyncAssertion { - return Eventually(actual, extra...) -} - -// Consistently is used to make asynchronous assertions. See documentation for Consistently. -func (globalFailHandlerGomega) Consistently(actual interface{}, extra ...interface{}) AsyncAssertion { - return Consistently(actual, extra...) -} +type OmegaMatcher = types.GomegaMatcher diff --git a/vendor/github.com/onsi/gomega/internal/assertion.go b/vendor/github.com/onsi/gomega/internal/assertion.go new file mode 100644 index 00000000..b3c26889 --- /dev/null +++ b/vendor/github.com/onsi/gomega/internal/assertion.go @@ -0,0 +1,150 @@ +package internal + +import ( + "fmt" + "reflect" + + "github.com/onsi/gomega/types" +) + +type Assertion struct { + actuals []interface{} // actual value plus all extra values + actualIndex int // value to pass to the matcher + vet vetinari // the vet to call before calling Gomega matcher + offset int + g *Gomega +} + +// ...obligatory discworld reference, as "vetineer" doesn't sound ... quite right. +type vetinari func(assertion *Assertion, optionalDescription ...interface{}) bool + +func NewAssertion(actualInput interface{}, g *Gomega, offset int, extra ...interface{}) *Assertion { + return &Assertion{ + actuals: append([]interface{}{actualInput}, extra...), + actualIndex: 0, + vet: (*Assertion).vetActuals, + offset: offset, + g: g, + } +} + +func (assertion *Assertion) WithOffset(offset int) types.Assertion { + assertion.offset = offset + return assertion +} + +func (assertion *Assertion) Error() types.Assertion { + return &Assertion{ + actuals: assertion.actuals, + actualIndex: len(assertion.actuals) - 1, + vet: (*Assertion).vetError, + offset: assertion.offset, + g: assertion.g, + } +} + +func (assertion *Assertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { + assertion.g.THelper() + return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, true, optionalDescription...) +} + +func (assertion *Assertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { + assertion.g.THelper() + return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, false, optionalDescription...) +} + +func (assertion *Assertion) To(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { + assertion.g.THelper() + return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, true, optionalDescription...) +} + +func (assertion *Assertion) ToNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { + assertion.g.THelper() + return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, false, optionalDescription...) +} + +func (assertion *Assertion) NotTo(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { + assertion.g.THelper() + return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, false, optionalDescription...) +} + +func (assertion *Assertion) buildDescription(optionalDescription ...interface{}) string { + switch len(optionalDescription) { + case 0: + return "" + case 1: + if describe, ok := optionalDescription[0].(func() string); ok { + return describe() + "\n" + } + } + return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n" +} + +func (assertion *Assertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool { + actualInput := assertion.actuals[assertion.actualIndex] + matches, err := matcher.Match(actualInput) + assertion.g.THelper() + if err != nil { + description := assertion.buildDescription(optionalDescription...) + assertion.g.Fail(description+err.Error(), 2+assertion.offset) + return false + } + if matches != desiredMatch { + var message string + if desiredMatch { + message = matcher.FailureMessage(actualInput) + } else { + message = matcher.NegatedFailureMessage(actualInput) + } + description := assertion.buildDescription(optionalDescription...) + assertion.g.Fail(description+message, 2+assertion.offset) + return false + } + + return true +} + +// vetActuals vets the actual values, with the (optional) exception of a +// specific value, such as the first value in case non-error assertions, or the +// last value in case of Error()-based assertions. +func (assertion *Assertion) vetActuals(optionalDescription ...interface{}) bool { + success, message := vetActuals(assertion.actuals, assertion.actualIndex) + if success { + return true + } + + description := assertion.buildDescription(optionalDescription...) + assertion.g.THelper() + assertion.g.Fail(description+message, 2+assertion.offset) + return false +} + +// vetError vets the actual values, except for the final error value, in case +// the final error value is non-zero. Otherwise, it doesn't vet the actual +// values, as these are allowed to take on any values unless there is a non-zero +// error value. +func (assertion *Assertion) vetError(optionalDescription ...interface{}) bool { + if err := assertion.actuals[assertion.actualIndex]; err != nil { + // Go error result idiom: all other actual values must be zero values. + return assertion.vetActuals(optionalDescription...) + } + return true +} + +// vetActuals vets a slice of actual values, optionally skipping a particular +// value slice element, such as the first or last value slice element. +func vetActuals(actuals []interface{}, skipIndex int) (bool, string) { + for i, actual := range actuals { + if i == skipIndex { + continue + } + if actual != nil { + zeroValue := reflect.Zero(reflect.TypeOf(actual)).Interface() + if !reflect.DeepEqual(zeroValue, actual) { + message := fmt.Sprintf("Unexpected non-nil/non-zero argument at index %d:\n\t<%T>: %#v", i, actual, actual) + return false, message + } + } + } + return true, "" +} diff --git a/vendor/github.com/onsi/gomega/internal/assertion/assertion.go b/vendor/github.com/onsi/gomega/internal/assertion/assertion.go deleted file mode 100644 index a248298f..00000000 --- a/vendor/github.com/onsi/gomega/internal/assertion/assertion.go +++ /dev/null @@ -1,109 +0,0 @@ -package assertion - -import ( - "fmt" - "reflect" - - "github.com/onsi/gomega/types" -) - -type Assertion struct { - actualInput interface{} - failWrapper *types.GomegaFailWrapper - offset int - extra []interface{} -} - -func New(actualInput interface{}, failWrapper *types.GomegaFailWrapper, offset int, extra ...interface{}) *Assertion { - return &Assertion{ - actualInput: actualInput, - failWrapper: failWrapper, - offset: offset, - extra: extra, - } -} - -func (assertion *Assertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { - assertion.failWrapper.TWithHelper.Helper() - return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, true, optionalDescription...) -} - -func (assertion *Assertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { - assertion.failWrapper.TWithHelper.Helper() - return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...) -} - -func (assertion *Assertion) To(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { - assertion.failWrapper.TWithHelper.Helper() - return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, true, optionalDescription...) -} - -func (assertion *Assertion) ToNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { - assertion.failWrapper.TWithHelper.Helper() - return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...) -} - -func (assertion *Assertion) NotTo(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { - assertion.failWrapper.TWithHelper.Helper() - return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...) -} - -func (assertion *Assertion) buildDescription(optionalDescription ...interface{}) string { - switch len(optionalDescription) { - case 0: - return "" - case 1: - if describe, ok := optionalDescription[0].(func() string); ok { - return describe() + "\n" - } - } - return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n" -} - -func (assertion *Assertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool { - matches, err := matcher.Match(assertion.actualInput) - assertion.failWrapper.TWithHelper.Helper() - if err != nil { - description := assertion.buildDescription(optionalDescription...) - assertion.failWrapper.Fail(description+err.Error(), 2+assertion.offset) - return false - } - if matches != desiredMatch { - var message string - if desiredMatch { - message = matcher.FailureMessage(assertion.actualInput) - } else { - message = matcher.NegatedFailureMessage(assertion.actualInput) - } - description := assertion.buildDescription(optionalDescription...) - assertion.failWrapper.Fail(description+message, 2+assertion.offset) - return false - } - - return true -} - -func (assertion *Assertion) vetExtras(optionalDescription ...interface{}) bool { - success, message := vetExtras(assertion.extra) - if success { - return true - } - - description := assertion.buildDescription(optionalDescription...) - assertion.failWrapper.TWithHelper.Helper() - assertion.failWrapper.Fail(description+message, 2+assertion.offset) - return false -} - -func vetExtras(extras []interface{}) (bool, string) { - for i, extra := range extras { - if extra != nil { - zeroValue := reflect.Zero(reflect.TypeOf(extra)).Interface() - if !reflect.DeepEqual(zeroValue, extra) { - message := fmt.Sprintf("Unexpected non-nil/non-zero extra argument at index %d:\n\t<%T>: %#v", i+1, extra, extra) - return false, message - } - } - } - return true, "" -} diff --git a/vendor/github.com/onsi/gomega/internal/async_assertion.go b/vendor/github.com/onsi/gomega/internal/async_assertion.go new file mode 100644 index 00000000..99f4ebcf --- /dev/null +++ b/vendor/github.com/onsi/gomega/internal/async_assertion.go @@ -0,0 +1,235 @@ +package internal + +import ( + "errors" + "fmt" + "reflect" + "runtime" + "time" + + "github.com/onsi/gomega/types" +) + +type AsyncAssertionType uint + +const ( + AsyncAssertionTypeEventually AsyncAssertionType = iota + AsyncAssertionTypeConsistently +) + +type AsyncAssertion struct { + asyncType AsyncAssertionType + + actualIsFunc bool + actualValue interface{} + actualFunc func() ([]reflect.Value, error) + + timeoutInterval time.Duration + pollingInterval time.Duration + offset int + g *Gomega +} + +func NewAsyncAssertion(asyncType AsyncAssertionType, actualInput interface{}, g *Gomega, timeoutInterval time.Duration, pollingInterval time.Duration, offset int) *AsyncAssertion { + out := &AsyncAssertion{ + asyncType: asyncType, + timeoutInterval: timeoutInterval, + pollingInterval: pollingInterval, + offset: offset, + g: g, + } + + switch actualType := reflect.TypeOf(actualInput); { + case actualType.Kind() != reflect.Func: + out.actualValue = actualInput + case actualType.NumIn() == 0 && actualType.NumOut() > 0: + out.actualIsFunc = true + out.actualFunc = func() ([]reflect.Value, error) { + return reflect.ValueOf(actualInput).Call([]reflect.Value{}), nil + } + case actualType.NumIn() == 1 && actualType.In(0).Implements(reflect.TypeOf((*types.Gomega)(nil)).Elem()): + out.actualIsFunc = true + out.actualFunc = func() (values []reflect.Value, err error) { + var assertionFailure error + assertionCapturingGomega := NewGomega(g.DurationBundle).ConfigureWithFailHandler(func(message string, callerSkip ...int) { + skip := 0 + if len(callerSkip) > 0 { + skip = callerSkip[0] + } + _, file, line, _ := runtime.Caller(skip + 1) + assertionFailure = fmt.Errorf("Assertion in callback at %s:%d failed:\n%s", file, line, message) + panic("stop execution") + }) + + defer func() { + if actualType.NumOut() == 0 { + if assertionFailure == nil { + values = []reflect.Value{reflect.Zero(reflect.TypeOf((*error)(nil)).Elem())} + } else { + values = []reflect.Value{reflect.ValueOf(assertionFailure)} + } + } else { + err = assertionFailure + } + if e := recover(); e != nil && assertionFailure == nil { + panic(e) + } + }() + + values = reflect.ValueOf(actualInput).Call([]reflect.Value{reflect.ValueOf(assertionCapturingGomega)}) + return + } + default: + msg := fmt.Sprintf("The function passed to Gomega's async assertions should either take no arguments and return values, or take a single Gomega interface that it can use to make assertions within the body of the function. When taking a Gomega interface the function can optionally return values or return nothing. The function you passed takes %d arguments and returns %d values.", actualType.NumIn(), actualType.NumOut()) + g.Fail(msg, offset+4) + } + + return out +} + +func (assertion *AsyncAssertion) WithOffset(offset int) types.AsyncAssertion { + assertion.offset = offset + return assertion +} + +func (assertion *AsyncAssertion) WithTimeout(interval time.Duration) types.AsyncAssertion { + assertion.timeoutInterval = interval + return assertion +} + +func (assertion *AsyncAssertion) WithPolling(interval time.Duration) types.AsyncAssertion { + assertion.pollingInterval = interval + return assertion +} + +func (assertion *AsyncAssertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { + assertion.g.THelper() + return assertion.match(matcher, true, optionalDescription...) +} + +func (assertion *AsyncAssertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { + assertion.g.THelper() + return assertion.match(matcher, false, optionalDescription...) +} + +func (assertion *AsyncAssertion) buildDescription(optionalDescription ...interface{}) string { + switch len(optionalDescription) { + case 0: + return "" + case 1: + if describe, ok := optionalDescription[0].(func() string); ok { + return describe() + "\n" + } + } + return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n" +} + +func (assertion *AsyncAssertion) pollActual() (interface{}, error) { + if !assertion.actualIsFunc { + return assertion.actualValue, nil + } + + values, err := assertion.actualFunc() + if err != nil { + return nil, err + } + extras := []interface{}{nil} + for _, value := range values[1:] { + extras = append(extras, value.Interface()) + } + success, message := vetActuals(extras, 0) + if !success { + return nil, errors.New(message) + } + + return values[0].Interface(), nil +} + +func (assertion *AsyncAssertion) matcherMayChange(matcher types.GomegaMatcher, value interface{}) bool { + if assertion.actualIsFunc { + return true + } + return types.MatchMayChangeInTheFuture(matcher, value) +} + +func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool { + timer := time.Now() + timeout := time.After(assertion.timeoutInterval) + + var matches bool + var err error + mayChange := true + value, err := assertion.pollActual() + if err == nil { + mayChange = assertion.matcherMayChange(matcher, value) + matches, err = matcher.Match(value) + } + + assertion.g.THelper() + + fail := func(preamble string) { + errMsg := "" + message := "" + if err != nil { + errMsg = "Error: " + err.Error() + } else { + if desiredMatch { + message = matcher.FailureMessage(value) + } else { + message = matcher.NegatedFailureMessage(value) + } + } + assertion.g.THelper() + description := assertion.buildDescription(optionalDescription...) + assertion.g.Fail(fmt.Sprintf("%s after %.3fs.\n%s%s%s", preamble, time.Since(timer).Seconds(), description, message, errMsg), 3+assertion.offset) + } + + if assertion.asyncType == AsyncAssertionTypeEventually { + for { + if err == nil && matches == desiredMatch { + return true + } + + if !mayChange { + fail("No future change is possible. Bailing out early") + return false + } + + select { + case <-time.After(assertion.pollingInterval): + value, err = assertion.pollActual() + if err == nil { + mayChange = assertion.matcherMayChange(matcher, value) + matches, err = matcher.Match(value) + } + case <-timeout: + fail("Timed out") + return false + } + } + } else if assertion.asyncType == AsyncAssertionTypeConsistently { + for { + if !(err == nil && matches == desiredMatch) { + fail("Failed") + return false + } + + if !mayChange { + return true + } + + select { + case <-time.After(assertion.pollingInterval): + value, err = assertion.pollActual() + if err == nil { + mayChange = assertion.matcherMayChange(matcher, value) + matches, err = matcher.Match(value) + } + case <-timeout: + return true + } + } + } + + return false +} diff --git a/vendor/github.com/onsi/gomega/internal/asyncassertion/async_assertion.go b/vendor/github.com/onsi/gomega/internal/asyncassertion/async_assertion.go deleted file mode 100644 index 6aa02bc5..00000000 --- a/vendor/github.com/onsi/gomega/internal/asyncassertion/async_assertion.go +++ /dev/null @@ -1,235 +0,0 @@ -// untested sections: 2 - -package asyncassertion - -import ( - "errors" - "fmt" - "reflect" - "runtime" - "time" - - "github.com/onsi/gomega/internal/oraclematcher" - "github.com/onsi/gomega/types" -) - -type AsyncAssertionType uint - -const ( - AsyncAssertionTypeEventually AsyncAssertionType = iota - AsyncAssertionTypeConsistently -) - -type AsyncAssertion struct { - asyncType AsyncAssertionType - actualInput interface{} - timeoutInterval time.Duration - pollingInterval time.Duration - failWrapper *types.GomegaFailWrapper - offset int -} - -func New(asyncType AsyncAssertionType, actualInput interface{}, failWrapper *types.GomegaFailWrapper, timeoutInterval time.Duration, pollingInterval time.Duration, offset int) *AsyncAssertion { - actualType := reflect.TypeOf(actualInput) - if actualType.Kind() == reflect.Func { - if actualType.NumIn() != 0 { - panic("Expected a function with no arguments and zero or more return values.") - } - } - - return &AsyncAssertion{ - asyncType: asyncType, - actualInput: actualInput, - failWrapper: failWrapper, - timeoutInterval: timeoutInterval, - pollingInterval: pollingInterval, - offset: offset, - } -} - -func (assertion *AsyncAssertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { - assertion.failWrapper.TWithHelper.Helper() - return assertion.match(matcher, true, optionalDescription...) -} - -func (assertion *AsyncAssertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { - assertion.failWrapper.TWithHelper.Helper() - return assertion.match(matcher, false, optionalDescription...) -} - -func (assertion *AsyncAssertion) buildDescription(optionalDescription ...interface{}) string { - switch len(optionalDescription) { - case 0: - return "" - case 1: - if describe, ok := optionalDescription[0].(func() string); ok { - return describe() + "\n" - } - } - return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n" -} - -func (assertion *AsyncAssertion) actualInputIsAFunction() bool { - actualType := reflect.TypeOf(assertion.actualInput) - return actualType.Kind() == reflect.Func && actualType.NumIn() == 0 -} - -func (assertion *AsyncAssertion) pollActual() (interface{}, error) { - if !assertion.actualInputIsAFunction() { - return assertion.actualInput, nil - } - var capturedAssertionFailure string - var values []reflect.Value - - numOut := reflect.TypeOf(assertion.actualInput).NumOut() - - func() { - originalHandler := assertion.failWrapper.Fail - assertion.failWrapper.Fail = func(message string, callerSkip ...int) { - skip := 0 - if len(callerSkip) > 0 { - skip = callerSkip[0] - } - _, file, line, _ := runtime.Caller(skip + 1) - capturedAssertionFailure = fmt.Sprintf("Assertion in callback at %s:%d failed:\n%s", file, line, message) - panic("stop execution") - } - - defer func() { - assertion.failWrapper.Fail = originalHandler - if e := recover(); e != nil && capturedAssertionFailure == "" { - panic(e) - } - }() - - values = reflect.ValueOf(assertion.actualInput).Call([]reflect.Value{}) - }() - - if capturedAssertionFailure != "" { - if numOut == 0 { - return errors.New(capturedAssertionFailure), nil - } else { - return nil, errors.New(capturedAssertionFailure) - } - } - - if numOut > 0 { - extras := []interface{}{} - for _, value := range values[1:] { - extras = append(extras, value.Interface()) - } - - success, message := vetExtras(extras) - - if !success { - return nil, errors.New(message) - } - - return values[0].Interface(), nil - } - - return nil, nil -} - -func (assertion *AsyncAssertion) matcherMayChange(matcher types.GomegaMatcher, value interface{}) bool { - if assertion.actualInputIsAFunction() { - return true - } - - return oraclematcher.MatchMayChangeInTheFuture(matcher, value) -} - -func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool { - timer := time.Now() - timeout := time.After(assertion.timeoutInterval) - - var matches bool - var err error - mayChange := true - value, err := assertion.pollActual() - if err == nil { - mayChange = assertion.matcherMayChange(matcher, value) - matches, err = matcher.Match(value) - } - - assertion.failWrapper.TWithHelper.Helper() - - fail := func(preamble string) { - errMsg := "" - message := "" - if err != nil { - errMsg = "Error: " + err.Error() - } else { - if desiredMatch { - message = matcher.FailureMessage(value) - } else { - message = matcher.NegatedFailureMessage(value) - } - } - assertion.failWrapper.TWithHelper.Helper() - description := assertion.buildDescription(optionalDescription...) - assertion.failWrapper.Fail(fmt.Sprintf("%s after %.3fs.\n%s%s%s", preamble, time.Since(timer).Seconds(), description, message, errMsg), 3+assertion.offset) - } - - if assertion.asyncType == AsyncAssertionTypeEventually { - for { - if err == nil && matches == desiredMatch { - return true - } - - if !mayChange { - fail("No future change is possible. Bailing out early") - return false - } - - select { - case <-time.After(assertion.pollingInterval): - value, err = assertion.pollActual() - if err == nil { - mayChange = assertion.matcherMayChange(matcher, value) - matches, err = matcher.Match(value) - } - case <-timeout: - fail("Timed out") - return false - } - } - } else if assertion.asyncType == AsyncAssertionTypeConsistently { - for { - if !(err == nil && matches == desiredMatch) { - fail("Failed") - return false - } - - if !mayChange { - return true - } - - select { - case <-time.After(assertion.pollingInterval): - value, err = assertion.pollActual() - if err == nil { - mayChange = assertion.matcherMayChange(matcher, value) - matches, err = matcher.Match(value) - } - case <-timeout: - return true - } - } - } - - return false -} - -func vetExtras(extras []interface{}) (bool, string) { - for i, extra := range extras { - if extra != nil { - zeroValue := reflect.Zero(reflect.TypeOf(extra)).Interface() - if !reflect.DeepEqual(zeroValue, extra) { - message := fmt.Sprintf("Unexpected non-nil/non-zero extra argument at index %d:\n\t<%T>: %#v", i+1, extra, extra) - return false, message - } - } - } - return true, "" -} diff --git a/vendor/github.com/onsi/gomega/internal/defaults/env.go b/vendor/github.com/onsi/gomega/internal/defaults/env.go deleted file mode 100644 index bc29c63d..00000000 --- a/vendor/github.com/onsi/gomega/internal/defaults/env.go +++ /dev/null @@ -1,22 +0,0 @@ -package defaults - -import ( - "fmt" - "time" -) - -func SetDurationFromEnv(getDurationFromEnv func(string) string, varSetter func(time.Duration), name string) { - durationFromEnv := getDurationFromEnv(name) - - if len(durationFromEnv) == 0 { - return - } - - duration, err := time.ParseDuration(durationFromEnv) - - if err != nil { - panic(fmt.Sprintf("Expected a duration when using %s! Parse error %v", name, err)) - } - - varSetter(duration) -} diff --git a/vendor/github.com/onsi/gomega/internal/duration_bundle.go b/vendor/github.com/onsi/gomega/internal/duration_bundle.go new file mode 100644 index 00000000..af8d989f --- /dev/null +++ b/vendor/github.com/onsi/gomega/internal/duration_bundle.go @@ -0,0 +1,71 @@ +package internal + +import ( + "fmt" + "os" + "reflect" + "time" +) + +type DurationBundle struct { + EventuallyTimeout time.Duration + EventuallyPollingInterval time.Duration + ConsistentlyDuration time.Duration + ConsistentlyPollingInterval time.Duration +} + +const ( + EventuallyTimeoutEnvVarName = "GOMEGA_DEFAULT_EVENTUALLY_TIMEOUT" + EventuallyPollingIntervalEnvVarName = "GOMEGA_DEFAULT_EVENTUALLY_POLLING_INTERVAL" + + ConsistentlyDurationEnvVarName = "GOMEGA_DEFAULT_CONSISTENTLY_DURATION" + ConsistentlyPollingIntervalEnvVarName = "GOMEGA_DEFAULT_CONSISTENTLY_POLLING_INTERVAL" +) + +func FetchDefaultDurationBundle() DurationBundle { + return DurationBundle{ + EventuallyTimeout: durationFromEnv(EventuallyTimeoutEnvVarName, time.Second), + EventuallyPollingInterval: durationFromEnv(EventuallyPollingIntervalEnvVarName, 10*time.Millisecond), + + ConsistentlyDuration: durationFromEnv(ConsistentlyDurationEnvVarName, 100*time.Millisecond), + ConsistentlyPollingInterval: durationFromEnv(ConsistentlyPollingIntervalEnvVarName, 10*time.Millisecond), + } +} + +func durationFromEnv(key string, defaultDuration time.Duration) time.Duration { + value := os.Getenv(key) + if value == "" { + return defaultDuration + } + duration, err := time.ParseDuration(value) + if err != nil { + panic(fmt.Sprintf("Expected a duration when using %s! Parse error %v", key, err)) + } + return duration +} + +func toDuration(input interface{}) time.Duration { + duration, ok := input.(time.Duration) + if ok { + return duration + } + + value := reflect.ValueOf(input) + kind := reflect.TypeOf(input).Kind() + + if reflect.Int <= kind && kind <= reflect.Int64 { + return time.Duration(value.Int()) * time.Second + } else if reflect.Uint <= kind && kind <= reflect.Uint64 { + return time.Duration(value.Uint()) * time.Second + } else if reflect.Float32 <= kind && kind <= reflect.Float64 { + return time.Duration(value.Float() * float64(time.Second)) + } else if reflect.String == kind { + duration, err := time.ParseDuration(value.String()) + if err != nil { + panic(fmt.Sprintf("%#v is not a valid parsable duration string.", input)) + } + return duration + } + + panic(fmt.Sprintf("%v is not a valid interval. Must be time.Duration, parsable duration string or a number.", input)) +} diff --git a/vendor/github.com/onsi/gomega/internal/gomega.go b/vendor/github.com/onsi/gomega/internal/gomega.go new file mode 100644 index 00000000..d26a6748 --- /dev/null +++ b/vendor/github.com/onsi/gomega/internal/gomega.go @@ -0,0 +1,102 @@ +package internal + +import ( + "time" + + "github.com/onsi/gomega/types" +) + +type Gomega struct { + Fail types.GomegaFailHandler + THelper func() + DurationBundle DurationBundle +} + +func NewGomega(bundle DurationBundle) *Gomega { + return &Gomega{ + Fail: nil, + THelper: nil, + DurationBundle: bundle, + } +} + +func (g *Gomega) IsConfigured() bool { + return g.Fail != nil && g.THelper != nil +} + +func (g *Gomega) ConfigureWithFailHandler(fail types.GomegaFailHandler) *Gomega { + g.Fail = fail + g.THelper = func() {} + return g +} + +func (g *Gomega) ConfigureWithT(t types.GomegaTestingT) *Gomega { + g.Fail = func(message string, _ ...int) { + t.Helper() + t.Fatalf("\n%s", message) + } + g.THelper = t.Helper + return g +} + +func (g *Gomega) Ω(actual interface{}, extra ...interface{}) types.Assertion { + return g.ExpectWithOffset(0, actual, extra...) +} + +func (g *Gomega) Expect(actual interface{}, extra ...interface{}) types.Assertion { + return g.ExpectWithOffset(0, actual, extra...) +} + +func (g *Gomega) ExpectWithOffset(offset int, actual interface{}, extra ...interface{}) types.Assertion { + return NewAssertion(actual, g, offset, extra...) +} + +func (g *Gomega) Eventually(actual interface{}, intervals ...interface{}) types.AsyncAssertion { + return g.EventuallyWithOffset(0, actual, intervals...) +} + +func (g *Gomega) EventuallyWithOffset(offset int, actual interface{}, intervals ...interface{}) types.AsyncAssertion { + timeoutInterval := g.DurationBundle.EventuallyTimeout + pollingInterval := g.DurationBundle.EventuallyPollingInterval + if len(intervals) > 0 { + timeoutInterval = toDuration(intervals[0]) + } + if len(intervals) > 1 { + pollingInterval = toDuration(intervals[1]) + } + + return NewAsyncAssertion(AsyncAssertionTypeEventually, actual, g, timeoutInterval, pollingInterval, offset) +} + +func (g *Gomega) Consistently(actual interface{}, intervals ...interface{}) types.AsyncAssertion { + return g.ConsistentlyWithOffset(0, actual, intervals...) +} + +func (g *Gomega) ConsistentlyWithOffset(offset int, actual interface{}, intervals ...interface{}) types.AsyncAssertion { + timeoutInterval := g.DurationBundle.ConsistentlyDuration + pollingInterval := g.DurationBundle.ConsistentlyPollingInterval + if len(intervals) > 0 { + timeoutInterval = toDuration(intervals[0]) + } + if len(intervals) > 1 { + pollingInterval = toDuration(intervals[1]) + } + + return NewAsyncAssertion(AsyncAssertionTypeConsistently, actual, g, timeoutInterval, pollingInterval, offset) +} + +func (g *Gomega) SetDefaultEventuallyTimeout(t time.Duration) { + g.DurationBundle.EventuallyTimeout = t +} + +func (g *Gomega) SetDefaultEventuallyPollingInterval(t time.Duration) { + g.DurationBundle.EventuallyPollingInterval = t +} + +func (g *Gomega) SetDefaultConsistentlyDuration(t time.Duration) { + g.DurationBundle.ConsistentlyDuration = t +} + +func (g *Gomega) SetDefaultConsistentlyPollingInterval(t time.Duration) { + g.DurationBundle.ConsistentlyPollingInterval = t +} diff --git a/vendor/github.com/onsi/gomega/internal/oraclematcher/oracle_matcher.go b/vendor/github.com/onsi/gomega/internal/oraclematcher/oracle_matcher.go deleted file mode 100644 index 66cad88a..00000000 --- a/vendor/github.com/onsi/gomega/internal/oraclematcher/oracle_matcher.go +++ /dev/null @@ -1,25 +0,0 @@ -package oraclematcher - -import "github.com/onsi/gomega/types" - -/* -GomegaMatchers that also match the OracleMatcher interface can convey information about -whether or not their result will change upon future attempts. - -This allows `Eventually` and `Consistently` to short circuit if success becomes impossible. - -For example, a process' exit code can never change. So, gexec's Exit matcher returns `true` -for `MatchMayChangeInTheFuture` until the process exits, at which point it returns `false` forevermore. -*/ -type OracleMatcher interface { - MatchMayChangeInTheFuture(actual interface{}) bool -} - -func MatchMayChangeInTheFuture(matcher types.GomegaMatcher, value interface{}) bool { - oracleMatcher, ok := matcher.(OracleMatcher) - if !ok { - return true - } - - return oracleMatcher.MatchMayChangeInTheFuture(value) -} diff --git a/vendor/github.com/onsi/gomega/internal/testingtsupport/testing_t_support.go b/vendor/github.com/onsi/gomega/internal/testingtsupport/testing_t_support.go deleted file mode 100644 index bb27032f..00000000 --- a/vendor/github.com/onsi/gomega/internal/testingtsupport/testing_t_support.go +++ /dev/null @@ -1,60 +0,0 @@ -package testingtsupport - -import ( - "regexp" - "runtime/debug" - "strings" - - "github.com/onsi/gomega/types" -) - -var StackTracePruneRE = regexp.MustCompile(`\/gomega\/|\/ginkgo\/|\/pkg\/testing\/|\/pkg\/runtime\/`) - -type EmptyTWithHelper struct{} - -func (e EmptyTWithHelper) Helper() {} - -type gomegaTestingT interface { - Fatalf(format string, args ...interface{}) -} - -func BuildTestingTGomegaFailWrapper(t gomegaTestingT) *types.GomegaFailWrapper { - tWithHelper, hasHelper := t.(types.TWithHelper) - if !hasHelper { - tWithHelper = EmptyTWithHelper{} - } - - fail := func(message string, callerSkip ...int) { - if hasHelper { - tWithHelper.Helper() - t.Fatalf("\n%s", message) - } else { - skip := 2 - if len(callerSkip) > 0 { - skip += callerSkip[0] - } - stackTrace := pruneStack(string(debug.Stack()), skip) - t.Fatalf("\n%s\n%s\n", stackTrace, message) - } - } - - return &types.GomegaFailWrapper{ - Fail: fail, - TWithHelper: tWithHelper, - } -} - -func pruneStack(fullStackTrace string, skip int) string { - stack := strings.Split(fullStackTrace, "\n")[1:] - if len(stack) > 2*skip { - stack = stack[2*skip:] - } - prunedStack := []string{} - for i := 0; i < len(stack)/2; i++ { - if !StackTracePruneRE.Match([]byte(stack[i*2])) { - prunedStack = append(prunedStack, stack[i*2]) - prunedStack = append(prunedStack, stack[i*2+1]) - } - } - return strings.Join(prunedStack, "\n") -} diff --git a/vendor/github.com/onsi/gomega/matchers.go b/vendor/github.com/onsi/gomega/matchers.go index 667160ad..e4aacc90 100644 --- a/vendor/github.com/onsi/gomega/matchers.go +++ b/vendor/github.com/onsi/gomega/matchers.go @@ -342,6 +342,34 @@ func HaveKeyWithValue(key interface{}, value interface{}) types.GomegaMatcher { } } +//HaveField succeeds if actual is a struct and the value at the passed in field +//matches the passed in matcher. By default HaveField used Equal() to perform the match, +//however a matcher can be passed in in stead. +// +//The field must be a string that resolves to the name of a field in the struct. Structs can be traversed +//using the '.' delimiter. If the field ends with '()' a method named field is assumed to exist on the struct and is invoked. +//Such methods must take no arguments and return a single value: +// +// type Book struct { +// Title string +// Author Person +// } +// type Person struct { +// FirstName string +// LastName string +// DOB time.Time +// } +// Expect(book).To(HaveField("Title", "Les Miserables")) +// Expect(book).To(HaveField("Title", ContainSubstring("Les")) +// Expect(book).To(HaveField("Person.FirstName", Equal("Victor")) +// Expect(book).To(HaveField("Person.DOB.Year()", BeNumerically("<", 1900)) +func HaveField(field string, expected interface{}) types.GomegaMatcher { + return &matchers.HaveFieldMatcher{ + Field: field, + Expected: expected, + } +} + //BeNumerically performs numerical assertions in a type-agnostic way. //Actual and expected should be numbers, though the specific type of //number is irrelevant (float32, float64, uint8, etc...). @@ -423,10 +451,29 @@ func BeADirectory() types.GomegaMatcher { //Expected must be either an int or a string. // Expect(resp).Should(HaveHTTPStatus(http.StatusOK)) // asserts that resp.StatusCode == 200 // Expect(resp).Should(HaveHTTPStatus("404 Not Found")) // asserts that resp.Status == "404 Not Found" -func HaveHTTPStatus(expected interface{}) types.GomegaMatcher { +// Expect(resp).Should(HaveHTTPStatus(http.StatusOK, http.StatusNoContent)) // asserts that resp.StatusCode == 200 || resp.StatusCode == 204 +func HaveHTTPStatus(expected ...interface{}) types.GomegaMatcher { return &matchers.HaveHTTPStatusMatcher{Expected: expected} } +// HaveHTTPHeaderWithValue succeeds if the header is found and the value matches. +// Actual must be either a *http.Response or *httptest.ResponseRecorder. +// Expected must be a string header name, followed by a header value which +// can be a string, or another matcher. +func HaveHTTPHeaderWithValue(header string, value interface{}) types.GomegaMatcher { + return &matchers.HaveHTTPHeaderWithValueMatcher{ + Header: header, + Value: value, + } +} + +// HaveHTTPBody matches if the body matches. +// Actual must be either a *http.Response or *httptest.ResponseRecorder. +// Expected must be either a string, []byte, or other matcher +func HaveHTTPBody(expected interface{}) types.GomegaMatcher { + return &matchers.HaveHTTPBodyMatcher{Expected: expected} +} + //And succeeds only if all of the given matchers succeed. //The matchers are tried in order, and will fail-fast if one doesn't succeed. // Expect("hi").To(And(HaveLen(2), Equal("hi")) @@ -466,10 +513,15 @@ func Not(matcher types.GomegaMatcher) types.GomegaMatcher { } //WithTransform applies the `transform` to the actual value and matches it against `matcher`. -//The given transform must be a function of one parameter that returns one value. +//The given transform must be either a function of one parameter that returns one value or a +// function of one parameter that returns two values, where the second value must be of the +// error type. // var plus1 = func(i int) int { return i + 1 } // Expect(1).To(WithTransform(plus1, Equal(2)) // +// var failingplus1 = func(i int) (int, error) { return 42, "this does not compute" } +// Expect(1).To(WithTransform(failingplus1, Equal(2))) +// //And(), Or(), Not() and WithTransform() allow matchers to be composed into complex expressions. func WithTransform(transform interface{}, matcher types.GomegaMatcher) types.GomegaMatcher { return matchers.NewWithTransformMatcher(transform, matcher) diff --git a/vendor/github.com/onsi/gomega/matchers/and.go b/vendor/github.com/onsi/gomega/matchers/and.go index d83a2916..6bd826ad 100644 --- a/vendor/github.com/onsi/gomega/matchers/and.go +++ b/vendor/github.com/onsi/gomega/matchers/and.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/onsi/gomega/format" - "github.com/onsi/gomega/internal/oraclematcher" "github.com/onsi/gomega/types" ) @@ -52,12 +51,12 @@ func (m *AndMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { if m.firstFailedMatcher == nil { // so all matchers succeeded.. Any one of them changing would change the result. for _, matcher := range m.Matchers { - if oraclematcher.MatchMayChangeInTheFuture(matcher, actual) { + if types.MatchMayChangeInTheFuture(matcher, actual) { return true } } return false // none of were going to change } // one of the matchers failed.. it must be able to change in order to affect the result - return oraclematcher.MatchMayChangeInTheFuture(m.firstFailedMatcher, actual) + return types.MatchMayChangeInTheFuture(m.firstFailedMatcher, actual) } diff --git a/vendor/github.com/onsi/gomega/matchers/have_field.go b/vendor/github.com/onsi/gomega/matchers/have_field.go new file mode 100644 index 00000000..2f1a9163 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/have_field.go @@ -0,0 +1,80 @@ +package matchers + +import ( + "fmt" + "reflect" + "strings" + + "github.com/onsi/gomega/format" +) + +func extractField(actual interface{}, field string) (interface{}, error) { + fields := strings.SplitN(field, ".", 2) + actualValue := reflect.ValueOf(actual) + + if actualValue.Kind() != reflect.Struct { + return nil, fmt.Errorf("HaveField encountered:\n%s\nWhich is not a struct.", format.Object(actual, 1)) + } + + var extractedValue reflect.Value + + if strings.HasSuffix(fields[0], "()") { + extractedValue = actualValue.MethodByName(strings.TrimSuffix(fields[0], "()")) + if extractedValue == (reflect.Value{}) { + return nil, fmt.Errorf("HaveField could not find method named '%s' in struct of type %T.", fields[0], actual) + } + t := extractedValue.Type() + if t.NumIn() != 0 || t.NumOut() != 1 { + return nil, fmt.Errorf("HaveField found an invalid method named '%s' in struct of type %T.\nMethods must take no arguments and return exactly one value.", fields[0], actual) + } + extractedValue = extractedValue.Call([]reflect.Value{})[0] + } else { + extractedValue = actualValue.FieldByName(fields[0]) + if extractedValue == (reflect.Value{}) { + return nil, fmt.Errorf("HaveField could not find field named '%s' in struct:\n%s", fields[0], format.Object(actual, 1)) + } + } + + if len(fields) == 1 { + return extractedValue.Interface(), nil + } else { + return extractField(extractedValue.Interface(), fields[1]) + } +} + +type HaveFieldMatcher struct { + Field string + Expected interface{} + + extractedField interface{} + expectedMatcher omegaMatcher +} + +func (matcher *HaveFieldMatcher) Match(actual interface{}) (success bool, err error) { + matcher.extractedField, err = extractField(actual, matcher.Field) + if err != nil { + return false, err + } + + var isMatcher bool + matcher.expectedMatcher, isMatcher = matcher.Expected.(omegaMatcher) + if !isMatcher { + matcher.expectedMatcher = &EqualMatcher{Expected: matcher.Expected} + } + + return matcher.expectedMatcher.Match(matcher.extractedField) +} + +func (matcher *HaveFieldMatcher) FailureMessage(actual interface{}) (message string) { + message = fmt.Sprintf("Value for field '%s' failed to satisfy matcher.\n", matcher.Field) + message += matcher.expectedMatcher.FailureMessage(matcher.extractedField) + + return message +} + +func (matcher *HaveFieldMatcher) NegatedFailureMessage(actual interface{}) (message string) { + message = fmt.Sprintf("Value for field '%s' satisfied matcher, but should not have.\n", matcher.Field) + message += matcher.expectedMatcher.NegatedFailureMessage(matcher.extractedField) + + return message +} diff --git a/vendor/github.com/onsi/gomega/matchers/have_http_body_matcher.go b/vendor/github.com/onsi/gomega/matchers/have_http_body_matcher.go new file mode 100644 index 00000000..204b467a --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/have_http_body_matcher.go @@ -0,0 +1,101 @@ +package matchers + +import ( + "fmt" + "io" + "net/http" + "net/http/httptest" + + "github.com/onsi/gomega/format" + "github.com/onsi/gomega/types" +) + +type HaveHTTPBodyMatcher struct { + Expected interface{} + cachedBody []byte +} + +func (matcher *HaveHTTPBodyMatcher) Match(actual interface{}) (bool, error) { + body, err := matcher.body(actual) + if err != nil { + return false, err + } + + switch e := matcher.Expected.(type) { + case string: + return (&EqualMatcher{Expected: e}).Match(string(body)) + case []byte: + return (&EqualMatcher{Expected: e}).Match(body) + case types.GomegaMatcher: + return e.Match(body) + default: + return false, fmt.Errorf("HaveHTTPBody matcher expects string, []byte, or GomegaMatcher. Got:\n%s", format.Object(matcher.Expected, 1)) + } +} + +func (matcher *HaveHTTPBodyMatcher) FailureMessage(actual interface{}) (message string) { + body, err := matcher.body(actual) + if err != nil { + return fmt.Sprintf("failed to read body: %s", err) + } + + switch e := matcher.Expected.(type) { + case string: + return (&EqualMatcher{Expected: e}).FailureMessage(string(body)) + case []byte: + return (&EqualMatcher{Expected: e}).FailureMessage(body) + case types.GomegaMatcher: + return e.FailureMessage(body) + default: + return fmt.Sprintf("HaveHTTPBody matcher expects string, []byte, or GomegaMatcher. Got:\n%s", format.Object(matcher.Expected, 1)) + } +} + +func (matcher *HaveHTTPBodyMatcher) NegatedFailureMessage(actual interface{}) (message string) { + body, err := matcher.body(actual) + if err != nil { + return fmt.Sprintf("failed to read body: %s", err) + } + + switch e := matcher.Expected.(type) { + case string: + return (&EqualMatcher{Expected: e}).NegatedFailureMessage(string(body)) + case []byte: + return (&EqualMatcher{Expected: e}).NegatedFailureMessage(body) + case types.GomegaMatcher: + return e.NegatedFailureMessage(body) + default: + return fmt.Sprintf("HaveHTTPBody matcher expects string, []byte, or GomegaMatcher. Got:\n%s", format.Object(matcher.Expected, 1)) + } +} + +// body returns the body. It is cached because once we read it in Match() +// the Reader is closed and it is not readable again in FailureMessage() +// or NegatedFailureMessage() +func (matcher *HaveHTTPBodyMatcher) body(actual interface{}) ([]byte, error) { + if matcher.cachedBody != nil { + return matcher.cachedBody, nil + } + + body := func(a *http.Response) ([]byte, error) { + if a.Body != nil { + defer a.Body.Close() + var err error + matcher.cachedBody, err = io.ReadAll(a.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + } + return matcher.cachedBody, nil + } + + switch a := actual.(type) { + case *http.Response: + return body(a) + case *httptest.ResponseRecorder: + return body(a.Result()) + default: + return nil, fmt.Errorf("HaveHTTPBody matcher expects *http.Response or *httptest.ResponseRecorder. Got:\n%s", format.Object(actual, 1)) + } + +} diff --git a/vendor/github.com/onsi/gomega/matchers/have_http_header_with_value_matcher.go b/vendor/github.com/onsi/gomega/matchers/have_http_header_with_value_matcher.go new file mode 100644 index 00000000..c256f452 --- /dev/null +++ b/vendor/github.com/onsi/gomega/matchers/have_http_header_with_value_matcher.go @@ -0,0 +1,81 @@ +package matchers + +import ( + "fmt" + "net/http" + "net/http/httptest" + + "github.com/onsi/gomega/format" + "github.com/onsi/gomega/types" +) + +type HaveHTTPHeaderWithValueMatcher struct { + Header string + Value interface{} +} + +func (matcher *HaveHTTPHeaderWithValueMatcher) Match(actual interface{}) (success bool, err error) { + headerValue, err := matcher.extractHeader(actual) + if err != nil { + return false, err + } + + headerMatcher, err := matcher.getSubMatcher() + if err != nil { + return false, err + } + + return headerMatcher.Match(headerValue) +} + +func (matcher *HaveHTTPHeaderWithValueMatcher) FailureMessage(actual interface{}) string { + headerValue, err := matcher.extractHeader(actual) + if err != nil { + panic(err) // protected by Match() + } + + headerMatcher, err := matcher.getSubMatcher() + if err != nil { + panic(err) // protected by Match() + } + + diff := format.IndentString(headerMatcher.FailureMessage(headerValue), 1) + return fmt.Sprintf("HTTP header %q:\n%s", matcher.Header, diff) +} + +func (matcher *HaveHTTPHeaderWithValueMatcher) NegatedFailureMessage(actual interface{}) (message string) { + headerValue, err := matcher.extractHeader(actual) + if err != nil { + panic(err) // protected by Match() + } + + headerMatcher, err := matcher.getSubMatcher() + if err != nil { + panic(err) // protected by Match() + } + + diff := format.IndentString(headerMatcher.NegatedFailureMessage(headerValue), 1) + return fmt.Sprintf("HTTP header %q:\n%s", matcher.Header, diff) +} + +func (matcher *HaveHTTPHeaderWithValueMatcher) getSubMatcher() (types.GomegaMatcher, error) { + switch m := matcher.Value.(type) { + case string: + return &EqualMatcher{Expected: matcher.Value}, nil + case types.GomegaMatcher: + return m, nil + default: + return nil, fmt.Errorf("HaveHTTPHeaderWithValue matcher must be passed a string or a GomegaMatcher. Got:\n%s", format.Object(matcher.Value, 1)) + } +} + +func (matcher *HaveHTTPHeaderWithValueMatcher) extractHeader(actual interface{}) (string, error) { + switch r := actual.(type) { + case *http.Response: + return r.Header.Get(matcher.Header), nil + case *httptest.ResponseRecorder: + return r.Result().Header.Get(matcher.Header), nil + default: + return "", fmt.Errorf("HaveHTTPHeaderWithValue matcher expects *http.Response or *httptest.ResponseRecorder. Got:\n%s", format.Object(actual, 1)) + } +} diff --git a/vendor/github.com/onsi/gomega/matchers/have_http_status_matcher.go b/vendor/github.com/onsi/gomega/matchers/have_http_status_matcher.go index 3ce4800b..85f77642 100644 --- a/vendor/github.com/onsi/gomega/matchers/have_http_status_matcher.go +++ b/vendor/github.com/onsi/gomega/matchers/have_http_status_matcher.go @@ -2,14 +2,17 @@ package matchers import ( "fmt" + "io" "net/http" "net/http/httptest" + "reflect" + "strings" "github.com/onsi/gomega/format" ) type HaveHTTPStatusMatcher struct { - Expected interface{} + Expected []interface{} } func (matcher *HaveHTTPStatusMatcher) Match(actual interface{}) (success bool, err error) { @@ -23,20 +26,71 @@ func (matcher *HaveHTTPStatusMatcher) Match(actual interface{}) (success bool, e return false, fmt.Errorf("HaveHTTPStatus matcher expects *http.Response or *httptest.ResponseRecorder. Got:\n%s", format.Object(actual, 1)) } - switch e := matcher.Expected.(type) { - case int: - return resp.StatusCode == e, nil - case string: - return resp.Status == e, nil + if len(matcher.Expected) == 0 { + return false, fmt.Errorf("HaveHTTPStatus matcher must be passed an int or a string. Got nothing") } - return false, fmt.Errorf("HaveHTTPStatus matcher must be passed an int or a string. Got:\n%s", format.Object(matcher.Expected, 1)) + for _, expected := range matcher.Expected { + switch e := expected.(type) { + case int: + if resp.StatusCode == e { + return true, nil + } + case string: + if resp.Status == e { + return true, nil + } + default: + return false, fmt.Errorf("HaveHTTPStatus matcher must be passed int or string types. Got:\n%s", format.Object(expected, 1)) + } + } + + return false, nil } func (matcher *HaveHTTPStatusMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, "to have HTTP status", matcher.Expected) + return fmt.Sprintf("Expected\n%s\n%s\n%s", formatHttpResponse(actual), "to have HTTP status", matcher.expectedString()) } func (matcher *HaveHTTPStatusMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, "not to have HTTP status", matcher.Expected) + return fmt.Sprintf("Expected\n%s\n%s\n%s", formatHttpResponse(actual), "not to have HTTP status", matcher.expectedString()) +} + +func (matcher *HaveHTTPStatusMatcher) expectedString() string { + var lines []string + for _, expected := range matcher.Expected { + lines = append(lines, format.Object(expected, 1)) + } + return strings.Join(lines, "\n") +} + +func formatHttpResponse(input interface{}) string { + var resp *http.Response + switch r := input.(type) { + case *http.Response: + resp = r + case *httptest.ResponseRecorder: + resp = r.Result() + default: + return "cannot format invalid HTTP response" + } + + body := "" + if resp.Body != nil { + defer resp.Body.Close() + data, err := io.ReadAll(resp.Body) + if err != nil { + data = []byte("") + } + body = format.Object(string(data), 0) + } + + var s strings.Builder + s.WriteString(fmt.Sprintf("%s<%s>: {\n", format.Indent, reflect.TypeOf(input))) + s.WriteString(fmt.Sprintf("%s%sStatus: %s\n", format.Indent, format.Indent, format.Object(resp.Status, 0))) + s.WriteString(fmt.Sprintf("%s%sStatusCode: %s\n", format.Indent, format.Indent, format.Object(resp.StatusCode, 0))) + s.WriteString(fmt.Sprintf("%s%sBody: %s\n", format.Indent, format.Indent, body)) + s.WriteString(fmt.Sprintf("%s}", format.Indent)) + + return s.String() } diff --git a/vendor/github.com/onsi/gomega/matchers/not.go b/vendor/github.com/onsi/gomega/matchers/not.go index 2c91670b..78b71910 100644 --- a/vendor/github.com/onsi/gomega/matchers/not.go +++ b/vendor/github.com/onsi/gomega/matchers/not.go @@ -1,7 +1,6 @@ package matchers import ( - "github.com/onsi/gomega/internal/oraclematcher" "github.com/onsi/gomega/types" ) @@ -26,5 +25,5 @@ func (m *NotMatcher) NegatedFailureMessage(actual interface{}) (message string) } func (m *NotMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { - return oraclematcher.MatchMayChangeInTheFuture(m.Matcher, actual) // just return m.Matcher's value + return types.MatchMayChangeInTheFuture(m.Matcher, actual) // just return m.Matcher's value } diff --git a/vendor/github.com/onsi/gomega/matchers/or.go b/vendor/github.com/onsi/gomega/matchers/or.go index 3bf79980..841ae26a 100644 --- a/vendor/github.com/onsi/gomega/matchers/or.go +++ b/vendor/github.com/onsi/gomega/matchers/or.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/onsi/gomega/format" - "github.com/onsi/gomega/internal/oraclematcher" "github.com/onsi/gomega/types" ) @@ -54,11 +53,11 @@ func (m *OrMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { if m.firstSuccessfulMatcher != nil { // one of the matchers succeeded.. it must be able to change in order to affect the result - return oraclematcher.MatchMayChangeInTheFuture(m.firstSuccessfulMatcher, actual) + return types.MatchMayChangeInTheFuture(m.firstSuccessfulMatcher, actual) } else { // so all matchers failed.. Any one of them changing would change the result. for _, matcher := range m.Matchers { - if oraclematcher.MatchMayChangeInTheFuture(matcher, actual) { + if types.MatchMayChangeInTheFuture(matcher, actual) { return true } } diff --git a/vendor/github.com/onsi/gomega/matchers/with_transform.go b/vendor/github.com/onsi/gomega/matchers/with_transform.go index f3dec910..6f743b1b 100644 --- a/vendor/github.com/onsi/gomega/matchers/with_transform.go +++ b/vendor/github.com/onsi/gomega/matchers/with_transform.go @@ -4,13 +4,12 @@ import ( "fmt" "reflect" - "github.com/onsi/gomega/internal/oraclematcher" "github.com/onsi/gomega/types" ) type WithTransformMatcher struct { // input - Transform interface{} // must be a function of one parameter that returns one value + Transform interface{} // must be a function of one parameter that returns one value and an optional error Matcher types.GomegaMatcher // cached value @@ -20,6 +19,9 @@ type WithTransformMatcher struct { transformedValue interface{} } +// reflect.Type for error +var errorT = reflect.TypeOf((*error)(nil)).Elem() + func NewWithTransformMatcher(transform interface{}, matcher types.GomegaMatcher) *WithTransformMatcher { if transform == nil { panic("transform function cannot be nil") @@ -28,8 +30,10 @@ func NewWithTransformMatcher(transform interface{}, matcher types.GomegaMatcher) if txType.NumIn() != 1 { panic("transform function must have 1 argument") } - if txType.NumOut() != 1 { - panic("transform function must have 1 return value") + if numout := txType.NumOut(); numout != 1 { + if numout != 2 || !txType.Out(1).AssignableTo(errorT) { + panic("transform function must either have 1 return value, or 1 return value plus 1 error value") + } } return &WithTransformMatcher{ @@ -58,6 +62,11 @@ func (m *WithTransformMatcher) Match(actual interface{}) (bool, error) { // call the Transform function with `actual` fn := reflect.ValueOf(m.Transform) result := fn.Call([]reflect.Value{param}) + if len(result) == 2 { + if !result[1].IsNil() { + return false, fmt.Errorf("Transform function failed: %s", result[1].Interface().(error).Error()) + } + } m.transformedValue = result[0].Interface() // expect exactly one value return m.Matcher.Match(m.transformedValue) @@ -77,5 +86,5 @@ func (m *WithTransformMatcher) MatchMayChangeInTheFuture(_ interface{}) bool { // Querying the next matcher is fine if the transformer always will return the same value. // But if the transformer is non-deterministic and returns a different value each time, then there // is no point in querying the next matcher, since it can only comment on the last transformed value. - return oraclematcher.MatchMayChangeInTheFuture(m.Matcher, m.transformedValue) + return types.MatchMayChangeInTheFuture(m.Matcher, m.transformedValue) } diff --git a/vendor/github.com/onsi/gomega/types/types.go b/vendor/github.com/onsi/gomega/types/types.go index ac59a3a5..c315ef06 100644 --- a/vendor/github.com/onsi/gomega/types/types.go +++ b/vendor/github.com/onsi/gomega/types/types.go @@ -1,21 +1,35 @@ package types -type TWithHelper interface { - Helper() -} +import ( + "time" +) type GomegaFailHandler func(message string, callerSkip ...int) -type GomegaFailWrapper struct { - Fail GomegaFailHandler - TWithHelper TWithHelper -} - //A simple *testing.T interface wrapper type GomegaTestingT interface { + Helper() Fatalf(format string, args ...interface{}) } +// Gomega represents an object that can perform synchronous and assynchronous assertions with Gomega matchers +type Gomega interface { + Ω(actual interface{}, extra ...interface{}) Assertion + Expect(actual interface{}, extra ...interface{}) Assertion + ExpectWithOffset(offset int, actual interface{}, extra ...interface{}) Assertion + + Eventually(actual interface{}, intervals ...interface{}) AsyncAssertion + EventuallyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion + + Consistently(actual interface{}, intervals ...interface{}) AsyncAssertion + ConsistentlyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion + + SetDefaultEventuallyTimeout(time.Duration) + SetDefaultEventuallyPollingInterval(time.Duration) + SetDefaultConsistentlyDuration(time.Duration) + SetDefaultConsistentlyPollingInterval(time.Duration) +} + //All Gomega matchers must implement the GomegaMatcher interface // //For details on writing custom matchers, check out: http://onsi.github.io/gomega/#adding-your-own-matchers @@ -24,3 +38,50 @@ type GomegaMatcher interface { FailureMessage(actual interface{}) (message string) NegatedFailureMessage(actual interface{}) (message string) } + +/* +GomegaMatchers that also match the OracleMatcher interface can convey information about +whether or not their result will change upon future attempts. + +This allows `Eventually` and `Consistently` to short circuit if success becomes impossible. + +For example, a process' exit code can never change. So, gexec's Exit matcher returns `true` +for `MatchMayChangeInTheFuture` until the process exits, at which point it returns `false` forevermore. +*/ +type OracleMatcher interface { + MatchMayChangeInTheFuture(actual interface{}) bool +} + +func MatchMayChangeInTheFuture(matcher GomegaMatcher, value interface{}) bool { + oracleMatcher, ok := matcher.(OracleMatcher) + if !ok { + return true + } + + return oracleMatcher.MatchMayChangeInTheFuture(value) +} + +// AsyncAssertions are returned by Eventually and Consistently and enable matchers to be polled repeatedly to ensure +// they are eventually satisfied +type AsyncAssertion interface { + Should(matcher GomegaMatcher, optionalDescription ...interface{}) bool + ShouldNot(matcher GomegaMatcher, optionalDescription ...interface{}) bool + + WithOffset(offset int) AsyncAssertion + WithTimeout(interval time.Duration) AsyncAssertion + WithPolling(interval time.Duration) AsyncAssertion +} + +// Assertions are returned by Ω and Expect and enable assertions against Gomega matchers +type Assertion interface { + Should(matcher GomegaMatcher, optionalDescription ...interface{}) bool + ShouldNot(matcher GomegaMatcher, optionalDescription ...interface{}) bool + + To(matcher GomegaMatcher, optionalDescription ...interface{}) bool + ToNot(matcher GomegaMatcher, optionalDescription ...interface{}) bool + NotTo(matcher GomegaMatcher, optionalDescription ...interface{}) bool + + WithOffset(offset int) Assertion + + Error() Assertion +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 2b9fb08d..65b7b999 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -7,6 +7,15 @@ github.com/KyleBanks/depth github.com/PuerkitoBio/purell # github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 github.com/PuerkitoBio/urlesc +# github.com/TencentBlueKing/gopkg v1.0.7 +## explicit +github.com/TencentBlueKing/gopkg/cache +github.com/TencentBlueKing/gopkg/cache/memory +github.com/TencentBlueKing/gopkg/cache/memory/backend +github.com/TencentBlueKing/gopkg/collection/set +github.com/TencentBlueKing/gopkg/conv +github.com/TencentBlueKing/gopkg/errorx +github.com/TencentBlueKing/gopkg/stringx # github.com/TencentBlueKing/iam-go-sdk v0.0.5 ## explicit github.com/TencentBlueKing/iam-go-sdk/expression/eval @@ -169,7 +178,7 @@ github.com/nxadm/tail/ratelimiter github.com/nxadm/tail/util github.com/nxadm/tail/watch github.com/nxadm/tail/winfile -# github.com/onsi/ginkgo v1.16.4 +# github.com/onsi/ginkgo v1.16.5 ## explicit github.com/onsi/ginkgo github.com/onsi/ginkgo/config @@ -192,15 +201,11 @@ github.com/onsi/ginkgo/reporters/stenographer github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable github.com/onsi/ginkgo/reporters/stenographer/support/go-isatty github.com/onsi/ginkgo/types -# github.com/onsi/gomega v1.14.0 +# github.com/onsi/gomega v1.17.0 ## explicit github.com/onsi/gomega github.com/onsi/gomega/format -github.com/onsi/gomega/internal/assertion -github.com/onsi/gomega/internal/asyncassertion -github.com/onsi/gomega/internal/defaults -github.com/onsi/gomega/internal/oraclematcher -github.com/onsi/gomega/internal/testingtsupport +github.com/onsi/gomega/internal github.com/onsi/gomega/matchers github.com/onsi/gomega/matchers/support/goraph/bipartitegraph github.com/onsi/gomega/matchers/support/goraph/edge